Module: Asciidoctor::PDF::FormattedText::InlineImageArranger
- Includes:
- Logging, Measurements
- Defined in:
- lib/asciidoctor/pdf/formatted_text/inline_image_arranger.rb
Constant Summary collapse
- PlaceholderChar =
::Asciidoctor::Prawn::Extensions::PlaceholderChar
Constants included from Measurements
Measurements::InsetMeasurementValueRx, Measurements::MeasurementValueHintRx, Measurements::MeasurementValueRx
Instance Method Summary collapse
-
#arrange_images(fragments) ⇒ Object
Iterates over the fragments that represent inline images and prepares the image data to be embedded into the document.
- #wrap(fragments) ⇒ Object
Methods included from Measurements
#resolve_measurement_values, #str_to_pt, #to_pt
Instance Method Details
#arrange_images(fragments) ⇒ Object
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.
When this method is called, the cursor is positioned at start of where this group of fragments will be written (offset by the leading padding).
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.
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/asciidoctor/pdf/formatted_text/inline_image_arranger.rb', line 36 def arrange_images fragments doc = @document return if (raw_image_fragments = fragments.select {|f| (f.key? :image_path) && !(f.key? :image_obj) }).empty? scratch = doc.scratch? available_w = available_width available_h = doc.bounds.height last_fragment = {} raw_image_fragments.each do |fragment| if fragment[:object_id] == last_fragment[:object_id] fragments.delete fragment next else drop = scratch end image_path = fragment[:image_path] image_format = fragment[:image_format] if (image_w = fragment[:image_width] || '100%') == 'auto' image_w = nil # use intrinsic width elsif image_w.start_with? 'auto*' image_scale = (image_w.slice 5, image_w.length).to_f image_w = nil # use intrinsic width elsif (pctidx = image_w.index '%') && pctidx + 1 < image_w.length # NOTE: intrinsic width is stored behind % symbol pct = (image_w.slice 0, pctidx).to_f / 100 intrinsic_w = (image_w.slice pctidx + 1, image_w.length).to_f image_w = [available_w, pct * intrinsic_w].min else image_w = [available_w, pctidx ? (image_w.to_f / 100 * available_w) : image_w.to_f].min end if (fit = fragment[:image_fit]) === 'line' max_image_h = [available_h, doc.font.height].min elsif fit == 'none' max_image_h = doc.margin_box.height elsif doc.bounds.instance_variable_get :@table_cell max_image_h = doc.bounds.parent.height else max_image_h = available_h end # TODO: make helper method to calculate width and height of image if image_format == 'svg' svg_obj = ::Prawn::SVG::Interface.new ::File.read(image_path, mode: 'r:UTF-8'), doc, at: doc.bounds.top_left, width: image_w, fallback_font_name: doc.fallback_svg_font_name, enable_web_requests: doc.allow_uri_read ? (doc.method :load_open_uri).to_proc : false, enable_file_requests_with_root: { base: (::File.dirname image_path), root: doc.jail_dir }, cache_images: doc.cache_uri svg_size = svg_obj.document.sizing # NOTE: the best we can do is make the image fit within full height of bounds if (image_h = svg_size.output_height) > max_image_h image_w = (svg_obj.resize height: (image_h = max_image_h)).output_width elsif image_w image_w = svg_size.output_width else fragment[:image_width] = (image_w = svg_size.output_width).to_s image_w *= image_scale if image_scale image_w = available_w if image_w > available_w end fragment[:image_obj] = svg_obj else # TODO: cache image info based on path (Prawn caches based on SHA1 of content) # NOTE: image_obj is constrained to image_width by renderer image_obj, image_info = ::File.open(image_path, 'rb') {|fd| doc.build_image_object fd } unless image_w fragment[:image_width] = (image_w = to_pt image_info.width, :px).to_s image_w *= image_scale if image_scale image_w = available_w if image_w > available_w end if (image_h = image_w * (image_info.height.fdiv image_info.width)) > max_image_h # NOTE: the best we can do is make the image fit within full height of bounds image_w = (image_h = max_image_h) * (image_info.width.fdiv image_info.height) end fragment[:image_obj] = image_obj fragment[:image_info] = image_info end doc.save_font do # NOTE: if image height exceeds line height by more than 1.5x, increase the line height # FIXME: we could really use a nicer API from Prawn here; this is an ugly hack if (f_height = image_h) > (line_font = doc.font).height * 1.5 # align with descender (equivalent to vertical-align: bottom in CSS) fragment[:ascender] = f_height - (fragment[:descender] = line_font.descender) if f_height == available_h fragment[:ascender] -= (doc.calc_line_metrics (doc.instance_variable_get :@base_line_height), line_font, doc.font_size).padding_top fragment[:full_height] = true end doc.font_size (fragment[:size] = f_height * (doc.font_size / line_font.height)) # align with baseline (roughly equivalent to vertical-align: baseline in CSS) #fragment[:ascender] = f_height #fragment[:descender] = 0 #doc.font_size(fragment[:size] = (f_height + line_font.descender) * (doc.font_size / line_font.height)) fragment[:line_height_increased] = true end end # NOTE: we can't rely on the fragment width because the line wrap mechanism ignores it; # it only considers the text (string) and character spacing, rebuilding the string several times fragment[:text] = PlaceholderChar fragment[:actual_character_spacing] = doc.character_spacing fragment[:character_spacing] = image_w fragment[:image_width] = fragment[:width] = image_w fragment[:image_height] = image_h rescue logger.warn %(could not embed image: #{image_path}; #{$!.}#{(doc.recommend_prawn_gmagick? $!, image_format) ? %(; install prawn-gmagick gem to add support for #{image_format&.upcase || 'unknown'} image format) : ''}) unless scratch drop = true # delegate to cleanup logic in ensure block ensure # NOTE: skip rendering image in scratch document or if image can't be loaded if drop fragment.delete :callback fragment.delete :image_info # NOTE: retain key to indicate we've visited fragment already fragment[:image_obj] = nil end last_fragment = fragment end end |
#wrap(fragments) ⇒ Object
10 11 12 13 |
# File 'lib/asciidoctor/pdf/formatted_text/inline_image_arranger.rb', line 10 def wrap fragments arrange_images fragments super end |