module Thinreports::Generator::PDF::Graphics

Constants

BASE_LINE_WIDTH
STROKE_DASH

Public Instance Methods

base64image(base64_data, x, y, w, h) click to toggle source

@param [String] base64_data @param [Numeric, Strng] x @param [Numeric, Strng] y @param [Numeric, Strng] w @param [Numeric, Strng] h

# File lib/thinreports/generator/pdf/document/graphics/image.rb, line 26
def base64image(base64_data, x, y, w, h)
  image_data = Base64.decode64(base64_data)
  image_id = Digest::MD5.hexdigest(base64_data)
  image_path = create_temp_imagefile(image_id, image_data)

  image(image_path, x, y, w, h)
end
build_fill_styles(styles) click to toggle source

@param [Hash] styles @return [Hash, nil]

# File lib/thinreports/generator/pdf/document/graphics/basic.rb, line 126
def build_fill_styles(styles)
  color = styles[:fill]
  return nil unless color && color != 'none'

  { color: parse_color(color) }
end
build_graphic_attributes(style, &block) click to toggle source

@param [Hash] style @yield [attrs] @yieldparam [Hash] attrs @return [Hash]

# File lib/thinreports/generator/pdf/document/graphics/attributes.rb, line 11
def build_graphic_attributes(style, &block)
  graphic_attributes = {
    stroke: style['border-color'],
    stroke_width: style['border-width'],
    stroke_type: style['border-style'],
    fill: style['fill-color']
  }
  block.call(graphic_attributes) if block_given?
  graphic_attributes
end
build_stroke_styles(styles) click to toggle source

@param [Hash] styles @return [Hash, nil]

# File lib/thinreports/generator/pdf/document/graphics/basic.rb, line 111
def build_stroke_styles(styles)
  color = styles[:stroke]
  width = styles[:stroke_width]
  return nil unless color && color != 'none'
  return nil unless width && width != 0

  {
    color: parse_color(color),
    width: s2f(width),
    dash: STROKE_DASH[styles[:stroke_type].to_sym]
  }
end
build_text_attributes(style, &block) click to toggle source

@param [Hash] style @yield [attrs] @yieldparam [Hash] attrs @return [Hash]

# File lib/thinreports/generator/pdf/document/graphics/attributes.rb, line 26
def build_text_attributes(style, &block)
  text_attributes = {
    font: font_family(style['font-family']),
    size: style['font-size'],
    color: style['color'],
    align: text_align(style['text-align']),
    valign: text_valign(style['vertical-align']),
    styles: font_styles(style['font-style']),
    letter_spacing: letter_spacing(style['letter-spacing']),
    line_height: line_height(style['line-height']),
    overflow: text_overflow(style['overflow']),
    word_wrap: word_wrap(style['word-wrap'])
  }
  block.call(text_attributes) if block_given?
  text_attributes
end
clean_temp_images() click to toggle source
# File lib/thinreports/generator/pdf/document/graphics/image.rb, line 72
def clean_temp_images
  temp_image_registry.each_value do |image_path|
    File.delete(image_path) if File.exist?(image_path)
  end
end
create_temp_imagefile(image_id, image_data) click to toggle source

@param [String] image_id @param [String] image_data @return [String] Path to imagefile

# File lib/thinreports/generator/pdf/document/graphics/image.rb, line 85
def create_temp_imagefile(image_id, image_data)
  temp_image_registry[image_id] ||= begin
    file = Tempfile.new('temp-image')
    file.binmode
    file.write(image_data)
    file.open
    file
  end
  temp_image_registry[image_id].path
end
ellipse(x, y, rx, ry, attrs = {}) click to toggle source

@param [Numeric, String] x center-x @param [Numeric, String] y center-y @param [Numeric, String] rx @param [Numeric, String] ry @param [Hash] attrs ({}) @option attrs [String] :stroke @option attrs [Numeric, String] :stroke_width @option attrs [Array<Integer, String>] :stroke_dash @option attrs [“solid”, “dashed”, “dotted”] :stroke_type @option attrs [String] :fill

# File lib/thinreports/generator/pdf/document/graphics/basic.rb, line 59
def ellipse(x, y, rx, ry, attrs = {})
  rx, ry = s2f(rx, ry)

  with_graphic_styles(attrs) do
    pdf.ellipse(pos(x, y), rx, ry)
  end
end
font_family(font_names) click to toggle source

@param [Array<String>] font_names @return [String]

# File lib/thinreports/generator/pdf/document/graphics/attributes.rb, line 45
def font_family(font_names)
  font_name = font_names.first
  default_family_if_missing(font_name)
end
font_styles(styles) click to toggle source

@param [Array<String>] styles @return [Array<Symbol>]

# File lib/thinreports/generator/pdf/document/graphics/attributes.rb, line 52
def font_styles(styles)
  styles.map do |font_style|
    case font_style
    when 'bold' then :bold
    when 'italic' then :italic
    when 'underline' then :underline
    when 'linethrough' then :strikethrough
    end
  end
end
image(filename_or_io, x, y, w, h) click to toggle source

@param [String, IO] filename_or_io @param [Numeric, Strng] x @param [Numeric, Strng] y @param [Numeric, Strng] w @param [Numeric, Strng] h

# File lib/thinreports/generator/pdf/document/graphics/image.rb, line 16
def image(filename_or_io, x, y, w, h)
  w, h = s2f(w, h)
  pdf.image(filename_or_io, at: pos(x, y), width: w, height: h)
end
image_box(filename_or_io, x, y, w, h, options = {}) click to toggle source

@param [String, IO] filename_or_io @param [Numeric, Strng] x @param [Numeric, Strng] y @param [Numeric, Strng] w @param [Numeric, Strng] h @param [Hash] options @option options [:left, :center, :right] :position_x (:left) @option options [:top, :center, :bottom] :position_y (:top) @option options [Numeric] :offset_x @option options [Numeric] :offset_y

# File lib/thinreports/generator/pdf/document/graphics/image.rb, line 44
def image_box(filename_or_io, x, y, w, h, options = {})
  w, h = s2f(w, h)

  computed_position = pos(
    x + (options[:offset_x] || 0),
    y + (options[:offset_y] || 0)
  )
  pdf.bounding_box(computed_position, width: w, height: h) do
    pdf.image(
      filename_or_io,
      position: options[:position_x] || :left,
      vposition: options[:position_y] || :top,
      auto_fit: [w, h]
    )
  end
end
image_dimensions(filename_or_io, x, y, w, h, options = {}) click to toggle source
# File lib/thinreports/generator/pdf/document/graphics/image.rb, line 61
def image_dimensions(filename_or_io, x, y, w, h, options = {})
  w, h = s2f(w, h)
  # XXX: Calling @private method
  _pdf_obj, info = pdf.build_image_object(filename_or_io)
  info.calc_image_dimensions(
    position: options[:position_x] || :left,
    vposition: options[:position_y] || :top,
    auto_fit: [w, h]
  )
end
image_position_x(position) click to toggle source

@param [“left”, “center”, “right”, “”] position @return [:left, :center, :right]

# File lib/thinreports/generator/pdf/document/graphics/attributes.rb, line 123
def image_position_x(position)
  case position
  when 'left' then :left
  when 'center' then :center
  when 'right' then :right
  when '' then :left
  else :left
  end
end
image_position_y(position) click to toggle source

@param [“top”, “middle”, “bottom”, “”] position @return [:left, :center, :right]

# File lib/thinreports/generator/pdf/document/graphics/attributes.rb, line 135
def image_position_y(position)
  case position
  when 'top' then :top
  when 'middle' then :center
  when 'bottom' then :bottom
  when '' then :top
  else :top
  end
end
letter_spacing(spacing) click to toggle source

@param [Float, “”, nil] spacing @return [Float, nil]

# File lib/thinreports/generator/pdf/document/graphics/attributes.rb, line 65
def letter_spacing(spacing)
  blank_value?(spacing) ? nil : spacing
end
line(x1, y1, x2, y2, attrs = {}) click to toggle source

@param [Numeric, String] x1 @param [Numeric, String] y1 @param [Numeric, String] x2 @param [Numeric, String] y2 @param [Hash] attrs ({}) @option attrs [String] :stroke @option attrs [Numeric, String] :stroke_width @option attrs [“solid”, “dashed”, “dotted”] :stroke_type

# File lib/thinreports/generator/pdf/document/graphics/basic.rb, line 20
def line(x1, y1, x2, y2, attrs = {})
  with_graphic_styles(attrs) do
    pdf.line(pos(x1, y1), pos(x2, y2))
  end
end
line_height(height) click to toggle source

@param [Float, “”, nil] height @return [Float, nil]

# File lib/thinreports/generator/pdf/document/graphics/attributes.rb, line 117
def line_height(height)
  blank_value?(height) ? nil : height
end
rect(x, y, w, h, attrs = {}) click to toggle source

@param [Numeric, String] x @param [Numeric, String] y @param [Numeric, String] w width @param [Numeric, String] h height @param [Hash] attrs ({}) @option attrs [Integer, String] :radius @option attrs [String] :stroke @option attrs [Numeric, String] :stroke_width @option attrs [“solid”, “dashed”, “dotted”] :stroke_type @option attrs [String] :fill

# File lib/thinreports/generator/pdf/document/graphics/basic.rb, line 36
def rect(x, y, w, h, attrs = {})
  w, h = s2f(w, h)
  radius = s2f(attrs[:radius])

  with_graphic_styles(attrs) do
    if radius && !radius.zero?
      pdf.rounded_rectangle(pos(x, y), w, h, radius)
    else
      pdf.rectangle(pos(x, y), w, h)
    end
  end
end
temp_image_registry() click to toggle source
# File lib/thinreports/generator/pdf/document/graphics/image.rb, line 78
def temp_image_registry
  @temp_image_registry ||= {}
end
text(content, x, y, w, h, attrs = {}) click to toggle source

@see text_box

# File lib/thinreports/generator/pdf/document/graphics/text.rb, line 57
def text(content, x, y, w, h, attrs = {})
  # Set the :overflow property to :shirink_to_fit.
  text_box(content, x, y, w, h, { overflow: :shirink_to_fit }.merge(attrs))
end
text_align(align) click to toggle source

@param [“left”, “center”, “right”, “”] align @return [:left, :center, :right]

# File lib/thinreports/generator/pdf/document/graphics/attributes.rb, line 71
def text_align(align)
  case align
  when 'left' then :left
  when 'center' then :center
  when 'right' then :right
  when '' then :left
  else :left
  end
end
text_box(content, x, y, w, h, attrs = {}, &block) click to toggle source

@param [String] content @param [Numeric, String] x @param [Numeric, String] y @param [Numeric, String] w @param [Numeric, String] h @param [Hash] attrs ({}) @option attrs [String] :font @option attrs [Numeric, String] :size @option attrs [String] :color @option attrs [Array<:bold, :italic, :underline, :strikethrough>]

:styles (nil)

@option attrs [:left, :center, :right] :align (:left) @option attrs [:top, :center, :bottom] :valign (:top) @option attrs [Numeric, String] :line_height The total height of an text line. @option attrs [Numeric, String] :letter_spacing @option attrs [Boolean] :single (false) @option attrs [:trancate, :shrink_to_fit, :expand] :overflow (:trancate) @option attrs [:none, :break_word] :word_wrap (:none)

# File lib/thinreports/generator/pdf/document/graphics/text.rb, line 25
def text_box(content, x, y, w, h, attrs = {}, &block)
  w, h = s2f(w, h)

  box_attrs = text_box_attrs(
    x, y, w, h,
    single: attrs.delete(:single),
    overflow: attrs[:overflow]
  )

  # Do not break by word unless :word_wrap is :break_word
  content = text_without_line_wrap(content) if attrs[:word_wrap] == :none

  with_text_styles(attrs) do |built_attrs, font_styles|
    if block
      block.call [{ text: content, styles: font_styles }],
        built_attrs.merge(box_attrs)
    else
      pdf.formatted_text_box(
        [{ text: content, styles: font_styles }],
        built_attrs.merge(box_attrs)
      )
    end
  end
rescue Prawn::Errors::CannotFit
  # Nothing to do.
  #
  # When the area is too small compared
  # with the content and the style of the text.
  #   (See prawn/core/text/formatted/line_wrap.rb#L185)
end
text_overflow(overflow) click to toggle source

@param [“truncate”, “fit”, “expand”, “”, nil] overflow @return [:truncate, :shrink_to_fit, :expand]

# File lib/thinreports/generator/pdf/document/graphics/attributes.rb, line 95
def text_overflow(overflow)
  case overflow
  when 'truncate' then :truncate
  when 'fit' then :shrink_to_fit
  when 'expand' then :expand
  when '' then :truncate
  else :truncate
  end
end
text_valign(valign) click to toggle source

@param [“top”, “middle”, “bottom”, “”, nil] valign @return [:top, :center, :bottom]

# File lib/thinreports/generator/pdf/document/graphics/attributes.rb, line 83
def text_valign(valign)
  case valign
  when 'top' then :top
  when 'middle' then :center
  when 'bottom' then :bottom
  when '' then :top
  else :top
  end
end
with_graphic_styles(attrs, &block) click to toggle source

@param [Hash] attrs

# File lib/thinreports/generator/pdf/document/graphics/basic.rb, line 68
def with_graphic_styles(attrs, &block)
  stroke = build_stroke_styles(attrs)
  fill = build_fill_styles(attrs)

  # Do not draw if no colors given.
  return unless fill || stroke

  save_graphics_state

  # Apply stroke-dashed.
  if stroke && stroke[:dash]
    length, space = stroke[:dash]
    pdf.dash(length, space: space)
  end

  # Draw with fill and stroke.
  if fill && stroke
    pdf.fill_and_stroke do
      line_width(stroke[:width])
      pdf.fill_color(fill[:color])
      pdf.stroke_color(stroke[:color])
      block.call
    end
  # Draw only with fill.
  elsif fill
    pdf.fill do
      pdf.fill_color(fill[:color])
      block.call
    end
  # Draw only with stroke.
  elsif stroke
    pdf.stroke do
      line_width(stroke[:width])
      pdf.stroke_color(stroke[:color])
      block.call
    end
  end

  restore_graphics_state
end
word_wrap(word_wrap) click to toggle source

@param [“break-word”, “none”, “”, nil] word_wrap @return [:break_word, :none]

# File lib/thinreports/generator/pdf/document/graphics/attributes.rb, line 107
def word_wrap(word_wrap)
  case word_wrap
  when 'break-word' then :break_word
  when 'none' then :none
  else :none
  end
end

Private Instance Methods

line_width(width) click to toggle source

@param [Numeric] width

# File lib/thinreports/generator/pdf/document/graphics.rb, line 17
def line_width(width)
  pdf.line_width(width * BASE_LINE_WIDTH)
end
restore_graphics_state() click to toggle source

Delegate to Prawn::Document#restore_graphic_state @see Prawn::Document#restore_graphics_state

# File lib/thinreports/generator/pdf/document/graphics.rb, line 29
def restore_graphics_state
  pdf.restore_graphics_state
end
save_graphics_state() click to toggle source

Delegate to Prawn::Document#save_graphic_state @see Prawn::Document#save_graphics_state

# File lib/thinreports/generator/pdf/document/graphics.rb, line 23
def save_graphics_state
  pdf.save_graphics_state
end
setup_custom_graphic_states() click to toggle source

Change the default graphic states defined by Prawn.

# File lib/thinreports/generator/pdf/document/graphics.rb, line 12
def setup_custom_graphic_states
  pdf.line_width(BASE_LINE_WIDTH)
end
text_box_attrs(x, y, w, h, states = {}) click to toggle source

@param x (see text_box) @param y (see text_box) @param w (see text_box) @param h (see text_box) @param [Hash] states @option states [Boolean] :single @option states [Symbold] :overflow @return [Hash]

# File lib/thinreports/generator/pdf/document/graphics/text.rb, line 72
def text_box_attrs(x, y, w, h, states = {})
  attrs = {
    at: pos(x, y),
    width: s2f(w)
  }
  if states[:single]
    states[:overflow] != :expand ? attrs.merge(single_line: true) : attrs
  else
    attrs.merge(height: s2f(h))
  end
end
text_line_leading(line_height, font) click to toggle source

@param [Numeric] line_height @param [Hash] font @option font [String] :name Name of font. @option font [Numeric] :size Size of font. @return [Numeric]

# File lib/thinreports/generator/pdf/document/graphics/text.rb, line 127
def text_line_leading(line_height, font)
  line_height - pdf.font(font[:name], size: font[:size]).height
end
text_without_line_wrap(content) click to toggle source

@param [String] content @return [String]

# File lib/thinreports/generator/pdf/document/graphics/text.rb, line 133
def text_without_line_wrap(content)
  content.gsub(/ /, Prawn::Text::NBSP)
end
with_font_styles(attrs, font, &block) click to toggle source

@param [Hash] attrs @param [Hash] font @option font [String] :color @option font [Numeric] :size @option font [String] :name @yield [attributes, styles] @yieldparam [Hash] modified_attrs @yieldparam [Array] styles

# File lib/thinreports/generator/pdf/document/graphics/text.rb, line 145
def with_font_styles(attrs, font, &block)
  # Building font styles.
  styles = attrs.delete(:styles)

  if styles
    manual, styles = styles.partition do |style|
      %i[bold italic].include?(style) && !font_has_style?(font[:name], style)
    end
  end

  # Emulate bold style.
  if manual && manual.include?(:bold)
    pdf.stroke_color(font[:color])
    pdf.line_width(font[:size] * 0.025)

    # Change rendering mode to :fill_stroke.
    attrs[:mode] = :fill_stroke
  end

  # Emulate italic style.
  if manual && manual.include?(:italic)
    # FIXME
    # pdf.transformation_matrix(1, 0, 0.26, 1, 0, 0)
  end

  pdf.font(font[:name], size: font[:size]) do
    pdf.fill_color(font[:color])
    block.call(attrs, styles || [])
  end
end
with_text_styles(attrs, &block) click to toggle source

@param attrs (see text) @yield [built_attrs, font_styles] @yieldparam [Hash] built_attrs The finalized attributes. @yieldparam [Array] font_styles The finalized styles.

# File lib/thinreports/generator/pdf/document/graphics/text.rb, line 88
def with_text_styles(attrs, &block)
  # When no color is given, do not draw.
  return unless attrs.key?(:color) && attrs[:color] != 'none'

  save_graphics_state

  fontinfo = {
    name: attrs.delete(:font).to_s,
    color: parse_color(attrs.delete(:color)),
    size: s2f(attrs.delete(:size))
  }

  # Add the specified value to :leading option.
  line_height = attrs.delete(:line_height)
  if line_height
    attrs[:leading] = text_line_leading(
      s2f(line_height),
      name: fontinfo[:name],
      size: fontinfo[:size]
    )
  end

  # Set the :character_spacing option.
  spacing = attrs.delete(:letter_spacing)
  attrs[:character_spacing] = s2f(spacing) if spacing

  # Or... with_font_styles(attrs, fontinfo, &block)
  with_font_styles(attrs, fontinfo) do |modified_attrs, styles|
    block.call(modified_attrs, styles)
  end

  restore_graphics_state
end