module Asciidoctor::Pdf::FormattedText::InlineImageArranger

Constants

ImagePlaceholderChar

ImagePlaceholderChar = %(u00a0)

PlaceholderWidthCache

Public Class Methods

extended(base) click to toggle source
# File lib/asciidoctor-pdf/formatted_text/inline_image_arranger.rb, line 19
def extended base
  base.class.__send__ :alias_method, :_initial_wrap, :wrap
end

Public Instance Methods

arrange_images(fragments) click to toggle source

Iterates over the fragments that represent inline images and prepares the image data to be embedded into the document.

This method populates the image_width, image_height, image_obj and image_info (PNG only) keys on the fragment. The text is replaced with placeholder text that will be used to reserve enough room in the line to fit the image.

The image height is scaled down to 75% of the specified width (px to pt conversion). If the image height is more than 1.5x the height of the surrounding line of text, the font size and line metrics of the fragment are modified to fit the image in the line.

If this is the scratch document, the image renderer callback is removed so that the image is not embedded.

This method is called each time the set of fragments overflow to another page, so it's necessary to short-circuit if that case is detected.

# File lib/asciidoctor-pdf/formatted_text/inline_image_arranger.rb, line 48
def arrange_images fragments
  doc = @document
  scratch = doc.scratch?
  fragments.select {|f| (f.key? :image_path) && !(f.key? :image_obj) }.each do |fragment|
    begin
      image_path = fragment[:image_path]

      if (image_w = fragment[:image_width])
        image_w *= 0.75
      end

      # TODO make helper method to calculate width and height of image
      if fragment[:image_type] == 'svg'
        svg_obj = ::Prawn::Svg::Interface.new (::IO.read image_path), doc, at: doc.bounds.top_left, width: image_w, fallback_font_name: doc.default_svg_font
        if image_w
          fragment[:image_width] = svg_obj.document.sizing.output_width
          fragment[:image_height] = svg_obj.document.sizing.output_height
        else
          fragment[:image_width] = svg_obj.document.sizing.output_width * 0.75
          fragment[:image_height] = svg_obj.document.sizing.output_height * 0.75
        end
        fragment[:image_obj] = svg_obj
      else
        # TODO cache image info based on path (Prawn cached based on SHA1 of content)
        image_obj, image_info = doc.build_image_object image_path
        if image_w
          fragment[:image_width], fragment[:image_height] = image_info.calc_image_dimensions width: image_w
        else
          fragment[:image_width] = image_info.width * 0.75
          fragment[:image_height] = image_info.height * 0.75
        end
        fragment[:image_obj] = image_obj
        fragment[:image_info] = image_info
      end

      spacer_w = nil
      doc.fragment_font fragment do
        # NOTE if image height exceeds line height by more than 1.5x, increase the line height
        # HACK we could really use a nicer API from Prawn here; this is an ugly hack
        if (f_height = fragment[:image_height]) > ((line_font = doc.font).height * 1.5)
          fragment[:ascender] = f_height
          fragment[:descender] = line_font.descender
          doc.font_size(fragment[:size] = f_height * (doc.font_size / line_font.height))
          fragment[:line_height_increased] = true
        end

        unless (spacer_w = PlaceholderWidthCache[f_info = doc.font_info])
          spacer_w = PlaceholderWidthCache[f_info] = doc.width_of ImagePlaceholderChar
        end
      end

      # NOTE make room for the image by repeating the image placeholder character
      # TODO could use character spacing as an alternative to repeating characters
      # HACK we could use a nicer API from Prawn here to reserve width in a line
      fragment[:text] = ImagePlaceholderChar * (fragment[:image_width] / spacer_w).ceil
      #fragment[:width] = fragment[:image_width]
    ensure
      # NOTE skip rendering image in scratch document
      if scratch
        fragment.delete :callback
        fragment.delete :image_obj
        fragment.delete :image_info
        # NOTE in main document, tmp image path is unlinked by renderer
        ::File.unlink image_path if fragment[:image_tmp]
      end
    end
  end
end
wrap(fragments) click to toggle source
Calls superclass method
# File lib/asciidoctor-pdf/formatted_text/inline_image_arranger.rb, line 13
def wrap fragments
  arrange_images fragments
  super
end