asciidoctor-pdf 2.0.5 → 2.0.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3c80f2acfd251ff6721a173c153551989ca3372a3a51ddbf628176f24dd84f5f
4
- data.tar.gz: 19c675f8b088711366559a831dcea41e2008a242dc4e19a321f7e40df50e1acf
3
+ metadata.gz: cd47554913fab2e3f8002491a92c430e0dbfe48b9177705470ffa7c64155289b
4
+ data.tar.gz: 580c0c9e6a46e5614927f42c4ad309e303dd69681bf10ddb674241cd930e6b31
5
5
  SHA512:
6
- metadata.gz: 994846a17ff30b567e29a7a14f63c8d74348ac1644a4bd9ce1d85e687736c8f0a241a5f1fa91a84c52b3ecb9c66ef68701162bdf53fd7e5733c4e6df7b19f81b
7
- data.tar.gz: 3889ba7c0cee283800c613a6d8d5e2c34b29d1cccb3d68a5d83c09c9ff300e1550c6cf28ffdc5b3b3873a35f14b6ad2b90a75f6e97431d0667c6c4b62a1e5a79
6
+ metadata.gz: 326e87435e664dde2e944b878d2c5c9a5b71cfd285da78115331a8cd14e20f2aaa0cb2b81779bc99ca8642df004ef03f60386a971e9176eb03d17755a9db951b
7
+ data.tar.gz: 7d7cd662e4bbe5df6d10c057e2f00a1a1c800f0e5a3e4f7731c94b657b0bf6ec51599e88d42dbd806a1e4bf991e54282c4efb820b0d39912ed34a74d25f89ff4
data/CHANGELOG.adoc CHANGED
@@ -5,6 +5,56 @@
5
5
  This document provides a high-level view of the changes to the {project-name} by release.
6
6
  For a detailed view of what has changed, refer to the {url-repo}/commits/main[commit history] on GitHub.
7
7
 
8
+ == 2.0.8 (2022-06-08) - @mojavelinux
9
+
10
+ Improvements::
11
+
12
+ * encapsulate logic to adjust column box inside float and dry run
13
+ * automatically set height on column box if not specified; update value automatically when reflowing margins
14
+
15
+ Bug Fixes::
16
+
17
+ * correctly compute value of to cursor on extent when column box starts below top of page (#2230)
18
+ * fix crash in `ColumnBox#move_past_bottom` when `:reflow_margins` option is not set
19
+ * fix x position of SVG when advanced to next column of column box
20
+ * `at_page_top?` should consider top of column box to be top of page
21
+ * prevent SVG image taller than column from being advanced to next column
22
+ * don't push section that follows index section in article to new page if last page of index does not extend to bottom of page
23
+
24
+ === Details
25
+
26
+ {url-repo}/releases/tag/v2.0.8[git tag] | {url-repo}/compare/v2.0.7\...v2.0.8[full diff]
27
+
28
+ == 2.0.7 (2022-06-03) - @mojavelinux
29
+
30
+ Improvements::
31
+
32
+ * don't recommend prawn-gmagick if PNG or JPG is corrupt or incomplete
33
+ * add helper method to determine when to recommend the prawn-gmagick gem
34
+
35
+ Bug Fixes::
36
+
37
+ * fix crash when doctitle or section title with automatic ID contains inline image without explicit width (#2228)
38
+ * use prawn-gmagick, if available, to read raster image referenced by SVG (#2223)
39
+ * allow image path in SVG to refer to any location within Asciidoctor jail (no restriction if safe mode is unsafe) (#1941)
40
+
41
+ === Details
42
+
43
+ {url-repo}/releases/tag/v2.0.7[git tag] | {url-repo}/compare/v2.0.6\...v2.0.7[full diff]
44
+
45
+ == 2.0.6 (2022-05-30) - @mojavelinux
46
+
47
+ Bug Fixes::
48
+
49
+ * indent content of collapsible block and apply bottom margin to match style of HTML output (#2219)
50
+ * patch prawn-gmagick to reread bit depth of a PNG image if it extracts the wrong value (#2216)
51
+ * interpret width of SVG correctly when width is defined in file using px units (#2215)
52
+ * don't crash if inline role defines border width but not border color
53
+
54
+ === Details
55
+
56
+ {url-repo}/releases/tag/v2.0.6[git tag] | {url-repo}/compare/v2.0.5\...v2.0.6[full diff]
57
+
8
58
  == 2.0.5 (2022-05-26) - @mojavelinux
9
59
 
10
60
  Bug Fixes::
data/README.adoc CHANGED
@@ -1,6 +1,6 @@
1
1
  = Asciidoctor PDF: A native PDF converter for AsciiDoc
2
2
  Dan Allen <https://github.com/mojavelinux[@mojavelinux]>; Sarah White <https://github.com/graphitefriction[@graphitefriction]>
3
- v2.0.5, 2022-05-26
3
+ v2.0.8, 2022-06-08
4
4
  // Settings:
5
5
  :experimental:
6
6
  :idprefix:
@@ -24,7 +24,7 @@ endif::[]
24
24
  :project-handle: asciidoctor-pdf
25
25
  // Variables:
26
26
  :release-line: 2.0.x
27
- :release-version: 2.0.5
27
+ :release-version: 2.0.8
28
28
  // URLs:
29
29
  :url-gem: https://rubygems.org/gems/asciidoctor-pdf
30
30
  :url-project: https://github.com/asciidoctor/asciidoctor-pdf
@@ -62,7 +62,7 @@ Asciidoctor PDF converts an AsciiDoc document directly to a PDF document.
62
62
  The style and layout of the PDF are controlled by a dedicated theme file.
63
63
  To the degree possible, Asciidoctor PDF supports all the features of AsciiDoc that are supported by Asciidoctor.
64
64
  It also provides {url-project-docs}/features/[PDF-specific features].
65
- However, there are {url-project-docs}/features/#limitations[certain limits] imposed by the PDF format and the PDF library this extension uses.
65
+ However, there are {url-project-docs}/features/#limitations[certain limitations] imposed by the PDF format and the PDF library this extension uses.
66
66
 
67
67
  Asciidoctor PDF uses the Prawn gem and Prawn's extensions, such as prawn-svg and prawn-table, to generate a PDF document.
68
68
  {url-prawn}[Prawn] is a general purpose PDF generator for Ruby that features high-level APIs for common needs like setting up the page and inserting images and low-level APIs for positioning and rendering text and graphics.
@@ -182,7 +182,7 @@ Asciidoctor creates the output file in the same directory as the input file by d
182
182
  Open the [.path]_basic-example.pdf_ file with a PDF viewer to see the result.
183
183
 
184
184
  .Example PDF document rendered in a PDF viewer
185
- image::docs/modules/ROOT/images/basic-example-pdf-screenshot.png[Screenshot of PDF document,960,540,pdfwidth=100%]
185
+ image::docs/modules/ROOT/images/basic-example-pdf-screenshot.png[Screenshot of PDF document,960,pdfwidth=100%]
186
186
 
187
187
  For more information about how to use Asciidoctor PDF and PDF-specific AsciiDoc syntax, see the {url-project-docs}/[Asciidoctor PDF documentation].
188
188
 
@@ -6,9 +6,6 @@ require_relative 'pdfmark'
6
6
  require_relative 'roman_numeral'
7
7
  require_relative 'section_info_by_page'
8
8
 
9
- autoload :StringIO, 'stringio'
10
- autoload :Tempfile, 'tempfile'
11
-
12
9
  module Asciidoctor
13
10
  module PDF
14
11
  class Converter < ::Prawn::Document
@@ -23,6 +20,8 @@ module Asciidoctor
23
20
 
24
21
  attr_reader :cache_uri
25
22
 
23
+ attr_reader :jail_dir
24
+
26
25
  attr_accessor :font_color
27
26
 
28
27
  attr_accessor :font_scale
@@ -153,7 +152,7 @@ module Asciidoctor
153
152
  log :warn, %(missing convert handler for #{name} node in #{@backend} backend)
154
153
  end
155
154
  # NOTE: inline node handlers generate HTML-like strings; all other handlers write directly to the PDF object
156
- ::Asciidoctor::Inline === node ? result : self
155
+ node.inline? ? result : self
157
156
  end
158
157
 
159
158
  def convert_document doc
@@ -163,7 +162,6 @@ module Asciidoctor
163
162
  doc.attributes['outline'] = '' unless (doc.attribute_locked? 'outline') || ((doc.instance_variable_get :@attributes_modified).include? 'outline')
164
163
  doc.attributes['outline-title'] = '' unless (doc.attribute_locked? 'outline-title') || ((doc.instance_variable_get :@attributes_modified).include? 'outline-title')
165
164
  doc.attributes['pagenums'] = '' unless (doc.attribute_locked? 'pagenums') || ((doc.instance_variable_get :@attributes_modified).include? 'pagenums')
166
- #assign_missing_section_ids doc
167
165
 
168
166
  on_page_create(&(method :init_page))
169
167
 
@@ -339,7 +337,11 @@ module Asciidoctor
339
337
  #@page_opts = { size: pdf_opts[:page_size], layout: pdf_opts[:page_layout] }
340
338
  ((::Prawn::Document.instance_method :initialize).bind self).call pdf_opts
341
339
  renderer.min_version (@pdf_version = PDFVersions[doc.attr 'pdf-version'])
342
- @media = doc.attr 'media', 'screen'
340
+ @tmp_files ||= {}
341
+ @allow_uri_read = doc.attr? 'allow-uri-read'
342
+ @cache_uri = doc.attr? 'cache-uri'
343
+ @jail_dir = doc.safe < ::Asciidoctor::SafeMode::SAFE ? nil : doc.base_dir
344
+ @media ||= doc.attr 'media', 'screen'
343
345
  @page_margin_by_side = { recto: (page_margin_recto = page_margin), verso: (page_margin_verso = page_margin), cover: page_margin }
344
346
  case doc.attr 'pdf-folio-placement', (@media == 'prepress' ? 'physical' : 'virtual')
345
347
  when 'physical'
@@ -364,13 +366,6 @@ module Asciidoctor
364
366
  else
365
367
  @ppbook = nil
366
368
  end
367
- # QUESTION: should ThemeLoader handle registering fonts instead?
368
- register_fonts theme.font_catalog, ((doc.attr 'pdf-fontsdir')&.sub '{docdir}', (doc.attr 'docdir')) || 'GEM_FONTS_DIR'
369
- default_kerning theme.base_font_kerning != 'none'
370
- @fallback_fonts = Array theme.font_fallbacks
371
- @allow_uri_read = doc.attr? 'allow-uri-read'
372
- @cache_uri = doc.attr? 'cache-uri'
373
- @tmp_files = {}
374
369
  if (bg_image = resolve_background_image doc, theme, 'page-background-image')&.first
375
370
  @page_bg_image = { verso: bg_image, recto: bg_image }
376
371
  else
@@ -383,6 +378,10 @@ module Asciidoctor
383
378
  @page_bg_image[:recto] = bg_image[0] && bg_image
384
379
  end
385
380
  @page_bg_color = resolve_theme_color :page_background_color, 'FFFFFF'
381
+ # QUESTION: should ThemeLoader handle registering fonts instead?
382
+ register_fonts theme.font_catalog, ((doc.attr 'pdf-fontsdir')&.sub '{docdir}', (doc.attr 'docdir')) || 'GEM_FONTS_DIR'
383
+ default_kerning theme.base_font_kerning != 'none'
384
+ @fallback_fonts = Array theme.font_fallbacks
386
385
  @root_font_size = theme.base_font_size
387
386
  @font_scale = 1
388
387
  @font_color = theme.base_font_color
@@ -703,6 +702,7 @@ module Asciidoctor
703
702
  def convert_index_section node
704
703
  space_needed_for_category = @theme.description_list_term_spacing + (2 * (height_of_typeset_text 'A'))
705
704
  pagenum_sequence_style = node.document.attr 'index-pagenum-sequence-style'
705
+ end_cursor = nil
706
706
  column_box [0, cursor], columns: @theme.index_columns, width: bounds.width, reflow_margins: true, spacer: @theme.index_column_gap do
707
707
  @index.categories.each do |category|
708
708
  bounds.move_past_bottom if space_needed_for_category > cursor
@@ -714,7 +714,10 @@ module Asciidoctor
714
714
  category.terms.each {|term| convert_index_list_item term, pagenum_sequence_style }
715
715
  @theme.prose_margin_bottom > cursor ? bounds.move_past_bottom : (move_down @theme.prose_margin_bottom)
716
716
  end
717
+ end_cursor = cursor if bounds.current_column == 0
717
718
  end
719
+ # Q: could we move this logic into column_box?
720
+ move_cursor_to end_cursor if end_cursor
718
721
  nil
719
722
  end
720
723
 
@@ -939,7 +942,7 @@ module Asciidoctor
939
942
  height: label_height,
940
943
  fallback_font_name: fallback_svg_font_name,
941
944
  enable_web_requests: allow_uri_read ? (method :load_open_uri).to_proc : false,
942
- enable_file_requests_with_root: (::File.dirname icon_path),
945
+ enable_file_requests_with_root: { base: (::File.dirname icon_path), root: @jail_dir },
943
946
  cache_images: cache_uri
944
947
  svg_obj.resize height: label_height if svg_obj.document.sizing.output_height > label_height
945
948
  svg_obj.draw
@@ -1167,8 +1170,26 @@ module Asciidoctor
1167
1170
  alias convert_literal convert_code
1168
1171
  alias convert_listing_or_literal convert_code
1169
1172
 
1173
+ def convert_collapsible node
1174
+ id = node.id
1175
+ title = (collapsible_marker = %(\u25bc )) + (node.title? ? node.title : 'Details')
1176
+ indent_by = theme_font(:caption) { rendered_width_of_string collapsible_marker }
1177
+ if !at_page_top? && (id || (node.option? 'unbreakable'))
1178
+ arrange_block node do
1179
+ add_dest_for_block node if id
1180
+ tare_first_page_content_stream { ink_caption title }
1181
+ indent(indent_by) { traverse node }
1182
+ end
1183
+ else
1184
+ add_dest_for_block node if id
1185
+ tare_first_page_content_stream { ink_caption title }
1186
+ indent(indent_by) { traverse node }
1187
+ end
1188
+ theme_margin :block, :bottom, (next_enclosed_block node)
1189
+ end
1190
+
1170
1191
  def convert_example node
1171
- return convert_open node if node.option? 'collapsible'
1192
+ return convert_collapsible node if node.option? 'collapsible'
1172
1193
  caption_bottom = @theme.example_caption_end&.to_sym == :bottom
1173
1194
  arrange_block node do |extent|
1174
1195
  add_dest_for_block node if node.id
@@ -1193,14 +1214,12 @@ module Asciidoctor
1193
1214
  if !at_page_top? && (has_title || id || (node.option? 'unbreakable'))
1194
1215
  arrange_block node do
1195
1216
  add_dest_for_block node if id
1196
- tare_first_page_content_stream do
1197
- node.context == :example ? (ink_caption %(\u25bc #{node.title})) : (ink_caption node, labeled: false)
1198
- end if has_title
1217
+ tare_first_page_content_stream { ink_caption node, labeled: false } if has_title
1199
1218
  traverse node
1200
1219
  end
1201
1220
  else
1202
1221
  add_dest_for_block node if id
1203
- node.context == :example ? (ink_caption %(\u25bc #{node.title})) : (ink_caption node, labeled: false) if has_title
1222
+ ink_caption node, labeled: false if has_title
1204
1223
  traverse node
1205
1224
  end
1206
1225
  end
@@ -1718,7 +1737,7 @@ module Asciidoctor
1718
1737
  file_request_root = false
1719
1738
  else
1720
1739
  svg_data = ::File.read image_path, mode: 'r:UTF-8'
1721
- file_request_root = ::File.dirname image_path
1740
+ file_request_root = { base: (::File.dirname image_path), root: @jail_dir }
1722
1741
  end
1723
1742
  svg_obj = ::Prawn::SVG::Interface.new svg_data, self,
1724
1743
  position: alignment,
@@ -1736,6 +1755,7 @@ module Asciidoctor
1736
1755
  if (rendered_h = svg_size.output_height) > (available_h = cursor - caption_h)
1737
1756
  unless pinned || at_page_top?
1738
1757
  advance_page
1758
+ (svg_obj.options[:at] = svg_obj.position)[0] += bounds.left if ColumnBox === bounds
1739
1759
  available_h = cursor - caption_h
1740
1760
  end
1741
1761
  rendered_w = (svg_obj.resize height: (rendered_h = available_h)).output_width if rendered_h > available_h
@@ -1798,7 +1818,7 @@ module Asciidoctor
1798
1818
  end
1799
1819
  rescue => e
1800
1820
  raise if ::StopIteration === e
1801
- on_image_error :exception, node, target, (opts.merge align: alignment, message: %(could not embed image: #{image_path}; #{e.message}#{::Prawn::Errors::UnsupportedImageType === e && !(defined? ::GMagick::Image) ? '; install prawn-gmagick gem to add support' : ''}))
1821
+ on_image_error :exception, node, target, (opts.merge align: alignment, message: %(could not embed image: #{image_path}; #{e.message}#{(recommend_prawn_gmagick? e, image_format) ? %(; install prawn-gmagick gem to add support for #{image_format&.upcase || 'unknown'} image format) : ''}))
1802
1822
  end
1803
1823
  end
1804
1824
 
@@ -2492,14 +2512,19 @@ module Asciidoctor
2492
2512
  # NOTE: an image with a data URI is handled using a temporary file
2493
2513
  elsif (image_path = resolve_image_path node, target, image_format)
2494
2514
  if ::File.readable? image_path
2515
+ class_attr = (role = node.role) ? %( class="#{role}") : ''
2516
+ fit_attr = (fit = node.attr 'fit') ? %( fit="#{fit}") : ''
2495
2517
  if (width = resolve_explicit_width node.attributes)
2496
- width += (intrinsic_image_dimensions image_path, image_format)[:width].to_s if node.parent.context == :table_cell && ::String === width && (width.end_with? '%')
2518
+ if node.parent.context == :table_cell && ::String === width && (width.end_with? '%')
2519
+ width += (intrinsic_image_dimensions image_path, image_format)[:width].to_s
2520
+ end
2521
+ width_attr = %( width="#{width}")
2522
+ elsif state # check that converter is initialized
2523
+ width_attr = %( width="#{(intrinsic_image_dimensions image_path, image_format)[:width]}")
2497
2524
  else
2498
- width = (intrinsic_image_dimensions image_path, image_format)[:width]
2525
+ width_attr = ' width="auto"' # defer operation until arranger runs
2499
2526
  end
2500
- class_attr = (role = node.role) ? %( class="#{role}") : ''
2501
- fit_attr = (fit = node.attr 'fit') ? %( fit="#{fit}") : ''
2502
- img = %(<img src="#{image_path}" format="#{image_format}" alt="#{encode_quotes node.attr 'alt'}" width="#{width}"#{class_attr}#{fit_attr}>)
2527
+ img = %(<img src="#{image_path}" format="#{image_format}" alt="#{encode_quotes node.attr 'alt'}"#{width_attr}#{class_attr}#{fit_attr}>)
2503
2528
  else
2504
2529
  log :warn, %(image to embed not found or not readable: #{image_path})
2505
2530
  img = %([#{node.attr 'alt'}&#93;)
@@ -3001,7 +3026,7 @@ module Asciidoctor
3001
3026
  end
3002
3027
  end
3003
3028
  end
3004
- theme_font_cascade [:caption, category_caption] do
3029
+ theme_font_cascade ['caption', category_caption] do
3005
3030
  if ((opts.delete :end) || (opts.delete :side) || :top) == :top
3006
3031
  margin = { top: caption_margin_outside, bottom: caption_margin_inside }
3007
3032
  else
@@ -3825,6 +3850,27 @@ module Asciidoctor
3825
3850
  end
3826
3851
  end
3827
3852
 
3853
+ # Retrieve the intrinsic image dimensions for the specified path in pt.
3854
+ #
3855
+ # Returns a Hash containing :width and :height keys that map to the image's
3856
+ # intrinsic width and height values (in pt).
3857
+ def intrinsic_image_dimensions path, format
3858
+ if format == 'svg'
3859
+ # NOTE: prawn-svg automatically converts intrinsic width and height to pt
3860
+ img_obj = ::Prawn::SVG::Interface.new (::File.read path, mode: 'r:UTF-8'), self, {}
3861
+ img_size = img_obj.document.sizing
3862
+ { width: img_size.output_width, height: img_size.output_height }
3863
+ else
3864
+ # NOTE: build_image_object caches image data previously loaded
3865
+ # NOTE: build_image_object computes intrinsic width and height in px
3866
+ _, img_size = ::File.open(path, 'rb') {|fd| build_image_object fd }
3867
+ { width: (to_pt img_size.width, :px), height: (to_pt img_size.height, :px) }
3868
+ end
3869
+ rescue
3870
+ # NOTE: image can't be read, so it won't be used anyway
3871
+ { width: 0, height: 0 }
3872
+ end
3873
+
3828
3874
  # Sends the specified message to the log unless this method is called from the scratch document
3829
3875
  def log severity, message = nil, &block
3830
3876
  logger.send severity, message, &block unless scratch?
@@ -4033,7 +4079,7 @@ module Asciidoctor
4033
4079
  def resolve_image_options image_path, image_format, image_attrs, opts = {}
4034
4080
  if image_format == 'svg'
4035
4081
  image_opts = {
4036
- enable_file_requests_with_root: (::File.dirname image_path),
4082
+ enable_file_requests_with_root: { base: (::File.dirname image_path), root: @jail_dir },
4037
4083
  enable_web_requests: allow_uri_read ? (method :load_open_uri).to_proc : false,
4038
4084
  cache_images: cache_uri,
4039
4085
  fallback_font_name: fallback_svg_font_name,
@@ -4120,6 +4166,7 @@ module Asciidoctor
4120
4166
  else
4121
4167
  imagesdir = relative_to
4122
4168
  end
4169
+ @tmp_files ||= {}
4123
4170
  # NOTE: base64 logic currently used for inline images
4124
4171
  if ::Base64 === image_path
4125
4172
  return @tmp_files[image_path] if @tmp_files.key? image_path
@@ -4346,7 +4393,7 @@ module Asciidoctor
4346
4393
  end
4347
4394
 
4348
4395
  def theme_font_cascade categories, &block
4349
- if ::Array === (category = (categories = categories.dup).shift)
4396
+ if ::Array === (category = (categories = categories.uniq).shift)
4350
4397
  category, opts = category
4351
4398
  else
4352
4399
  opts = {}
@@ -1,20 +1,35 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  Prawn::Document::ColumnBox.prepend (Module.new do
4
- def absolute_bottom
5
- stretchy? ? @parent.absolute_bottom : super
6
- end
4
+ attr_accessor :current_column
7
5
 
8
6
  def move_past_bottom
9
7
  (doc = @document).y = @y
10
8
  return if (@current_column = (@current_column + 1) % @columns) > 0
11
- @y = (par = @parent).absolute_top if @reflow_margins
9
+ par = @parent
10
+ if (reset_y = @reflow_margins) && (reset_y == true || reset_y > doc.page_number)
11
+ @y = par.absolute_top
12
+ @height = par.height unless stretchy?
13
+ end
12
14
  initial_margins = doc.page.margins
13
15
  par.move_past_bottom
14
16
  if doc.page.margins != initial_margins
15
- doc.bounds = self.class.new doc, par, (margin_box = doc.margin_box).absolute_top_left,
16
- columns: @columns, reflow_margins: true, spacer: @spacer, width: margin_box.width
17
+ doc.bounds = self.class.new doc, par, [(margin_box = doc.margin_box).absolute_left, @y],
18
+ columns: @columns, reflow_margins: @reflow_margins, spacer: @spacer, width: margin_box.width, height: @height
19
+ end
20
+ nil
21
+ end
22
+
23
+ # Rearranges the column box into a single column, where the original columns are in a single file. Used
24
+ # for the purpose of computing the extent of content in a scratch document.
25
+ def single_file
26
+ if @reflow_margins && @parent.absolute_top > @y && @columns > @current_column + 1
27
+ # defer reflow margins until all columns on current page have been exhausted
28
+ @reflow_margins = @document.page_number + (@columns - @current_column)
17
29
  end
30
+ @width = bare_column_width
31
+ @columns = 1
32
+ @current_column = 0
18
33
  nil
19
34
  end
20
35
  end)
@@ -251,16 +251,17 @@ module Asciidoctor
251
251
  # Returns whether the cursor is at the top of the page (i.e., margin box).
252
252
  #
253
253
  def at_page_top?
254
- @y == @margin_box.absolute_top
254
+ @y == (ColumnBox === bounds ? bounds : @margin_box).absolute_top
255
255
  end
256
256
 
257
257
  # Prevents at_page_top? from returning true while yielding to the specified block.
258
258
  #
259
259
  def conceal_page_top
260
- margin_box.instance_variable_set :@y, (old_top = margin_box.absolute_top) + 0.0001
260
+ old_top = (outer_bounds = ColumnBox === bounds ? bounds : @margin_box).absolute_top
261
+ outer_bounds.instance_variable_set :@y, old_top + 0.0001
261
262
  yield
262
263
  ensure
263
- margin_box.instance_variable_set :@y, old_top
264
+ outer_bounds.instance_variable_set :@y, old_top
264
265
  end
265
266
 
266
267
  # Returns whether the current page is the last page in the document.
@@ -560,11 +561,11 @@ module Asciidoctor
560
561
  def float
561
562
  original_page_number = page_number
562
563
  original_y = y
563
- original_column = bounds.instance_variable_get :@current_column if ColumnBox === bounds
564
+ original_column = bounds.current_column if ColumnBox === bounds
564
565
  yield
565
566
  go_to_page original_page_number unless page_number == original_page_number
566
567
  self.y = original_y
567
- bounds.instance_variable_set :@current_column, original_column if original_column
568
+ bounds.current_column = original_column if original_column
568
569
  end
569
570
 
570
571
  # Short-circuits the call to the built-in move_up operation
@@ -721,6 +722,12 @@ module Asciidoctor
721
722
  end
722
723
  end
723
724
 
725
+ # Wraps the column_box method and automatically sets the height unless the :height option is specified.
726
+ def column_box point, options, &block
727
+ options[:height] = cursor unless options.key? :height
728
+ super
729
+ end
730
+
724
731
  # A flowing version of bounding_box. If the content runs to another page, the cursor starts at
725
732
  # the top of the page instead of from the original cursor position. Similar to span, except
726
733
  # the :position option is limited to a numeric value and additional options are passed through
@@ -1146,10 +1153,7 @@ module Asciidoctor
1146
1153
  scratch_pdf.bounds = bounds.dup.tap do |bounds_copy|
1147
1154
  bounds_copy.instance_variable_set :@document, scratch_pdf
1148
1155
  bounds_copy.instance_variable_set :@parent, saved_bounds
1149
- if ColumnBox === bounds_copy
1150
- bounds_copy.instance_variable_set :@width, bounds_copy.bare_column_width
1151
- bounds_copy.instance_variable_set :@current_column, (bounds_copy.instance_variable_set :@columns, 1) - 1
1152
- end
1156
+ bounds_copy.single_file if ColumnBox === bounds_copy
1153
1157
  end
1154
1158
  scratch_pdf.move_cursor_to cursor unless (scratch_start_at_top = keep_together || pages_advanced > 0 || at_page_top?)
1155
1159
  scratch_start_cursor = scratch_pdf.cursor
@@ -33,25 +33,8 @@ module Asciidoctor
33
33
  end
34
34
  end
35
35
 
36
- # Retrieve the intrinsic image dimensions for the specified path in pt.
37
- #
38
- # Returns a Hash containing :width and :height keys that map to the image's
39
- # intrinsic width and height values (in pt).
40
- def intrinsic_image_dimensions path, format
41
- if format == 'svg'
42
- # NOTE: prawn-svg computes intrinsic width and height in pt
43
- img_obj = ::Prawn::SVG::Interface.new (::File.read path, mode: 'r:UTF-8'), self, {}
44
- img_size = img_obj.document.sizing
45
- { width: img_size.output_width, height: img_size.output_height }
46
- else
47
- # NOTE: build_image_object caches image data previously loaded
48
- # NOTE: build_image_object computes intrinsic width and height in px
49
- _, img_size = ::File.open(path, 'rb') {|fd| build_image_object fd }
50
- { width: (to_pt img_size.width, :px), height: (to_pt img_size.height, :px) }
51
- end
52
- rescue
53
- # NOTE: image cannot be read, so it won't be used anyway
54
- { width: 0, height: 0 }
36
+ def recommend_prawn_gmagick? err, image_format
37
+ ::Prawn::Errors::UnsupportedImageType === err && !(defined? ::GMagick::Image) && ((err.message.include? 'PNG') || (%w(jpg png).none? image_format))
55
38
  end
56
39
  end
57
40
 
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require 'prawn/gmagick'
5
+ rescue LoadError # rubocop:disable Lint/SuppressedException
6
+ end unless defined? GMagick::Image
7
+
8
+ Gmagick.prepend (Module.new do
9
+ def initialize image_blob
10
+ super
11
+ # apply patch for https://github.com/packetmonkey/prawn-gmagick/issues/19
12
+ if bits != 8 && (GMagick::Image.format image_blob) == 'PNG'
13
+ (io = StringIO.new image_blob).read 8
14
+ chunk_size = io.read 4
15
+ self.bits = ((io.read chunk_size.unpack1 'N').unpack 'NNC')[-1] if (io.read 4) == 'IHDR'
16
+ end
17
+ end
18
+ end) if defined? GMagick::Image
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ Prawn::SVG::Calculators::DocumentSizing.prepend (Module.new do
4
+ def initialize *_args
5
+ super
6
+ @document_width = @document_width.to_f * 0.75 if @document_width&.end_with? 'px'
7
+ @document_height = @document_height.to_f * 0.75 if @document_height&.end_with? 'px'
8
+ end
9
+ end)
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ Prawn::SVG::Elements::Image.prepend (Module.new do
4
+ def image_dimensions data
5
+ unless (handler = find_image_handler data)
6
+ raise ::Prawn::SVG::Elements::Base::SkipElementError, 'Unsupported image type supplied to image tag'
7
+ end
8
+ image = handler.new data
9
+ [image.width.to_f, image.height.to_f]
10
+ end
11
+
12
+ def find_image_handler data
13
+ Prawn.image_handler.find data rescue nil
14
+ end
15
+ end)
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ Prawn::SVG::Loaders::File.prepend (Module.new do
4
+ attr_reader :jail_path
5
+
6
+ def initialize root_path
7
+ if Hash === root_path
8
+ @jail_path = root_path[:root]
9
+ root_path = root_path[:base]
10
+ super
11
+ else
12
+ super
13
+ @jail_path = self.root_path
14
+ end
15
+ end
16
+
17
+ def assert_valid_path! path
18
+ if jail_path && !(path.start_with? %(#{jail_path}#{File::SEPARATOR}))
19
+ raise Prawn::SVG::UrlLoader::Error, %(file path points to location outside of jail #{jail_path})
20
+ end
21
+ end
22
+ end)
@@ -1,7 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'prawn-svg'
4
+ require_relative 'prawn-svg/calculators/document_sizing'
5
+ require_relative 'prawn-svg/elements/image'
4
6
  require_relative 'prawn-svg/loaders/data'
7
+ require_relative 'prawn-svg/loaders/file'
5
8
  require_relative 'prawn-svg/loaders/web'
6
9
  require_relative 'prawn-svg/url_loader'
7
10
  # NOTE: disable system fonts since they're non-portable
@@ -1,5 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # NOTE: patch float precision constant so prawn-table does not fail to arrange cells that span columns (see #1835)
4
+ Prawn.send :remove_const, :FLOAT_PRECISION
5
+ Prawn::FLOAT_PRECISION = 1e-3
6
+
3
7
  # the following are organized under the Asciidoctor::Prawn namespace
4
8
  require_relative 'prawn/document/column_box'
5
9
  require_relative 'prawn/font_metric_cache'
@@ -4,5 +4,6 @@ require_relative 'ext/core'
4
4
  require_relative 'ext/asciidoctor'
5
5
  require_relative 'ext/pdf-core'
6
6
  require_relative 'ext/prawn'
7
+ require_relative 'ext/prawn-gmagick'
7
8
  require_relative 'ext/prawn-svg'
8
9
  require_relative 'ext/prawn-table'
@@ -48,10 +48,11 @@ module Asciidoctor::PDF::FormattedText
48
48
  drop = scratch
49
49
  end
50
50
  image_path = fragment[:image_path]
51
- image_w = fragment[:image_width] || '100%'
52
-
53
- # NOTE: intrinsic width is stored behind % symbol
54
- if (pctidx = image_w.index '%') && pctidx + 1 < image_w.length
51
+ image_format = fragment[:image_format]
52
+ if (image_w = fragment[:image_width] || '100%') == 'auto'
53
+ image_w = nil # use intrinsic width
54
+ elsif (pctidx = image_w.index '%') && pctidx + 1 < image_w.length
55
+ # NOTE: intrinsic width is stored behind % symbol
55
56
  pct = (image_w.slice 0, pctidx).to_f / 100
56
57
  intrinsic_w = (image_w.slice pctidx + 1, image_w.length).to_f
57
58
  image_w = [available_w, pct * intrinsic_w].min
@@ -62,26 +63,33 @@ module Asciidoctor::PDF::FormattedText
62
63
  max_image_h = fragment[:image_fit] == 'line' ? [available_h, doc.font.height].min : available_h
63
64
 
64
65
  # TODO: make helper method to calculate width and height of image
65
- if fragment[:image_format] == 'svg'
66
+ if image_format == 'svg'
66
67
  svg_obj = ::Prawn::SVG::Interface.new ::File.read(image_path, mode: 'r:UTF-8'), doc,
67
68
  at: doc.bounds.top_left,
68
69
  width: image_w,
69
70
  fallback_font_name: doc.fallback_svg_font_name,
70
71
  enable_web_requests: doc.allow_uri_read ? (doc.method :load_open_uri).to_proc : false,
71
- enable_file_requests_with_root: (::File.dirname image_path),
72
+ enable_file_requests_with_root: { base: (::File.dirname image_path), root: doc.jail_dir },
72
73
  cache_images: doc.cache_uri
73
74
  svg_size = svg_obj.document.sizing
74
75
  # NOTE: the best we can do is make the image fit within full height of bounds
75
76
  if (image_h = svg_size.output_height) > max_image_h
76
77
  image_w = (svg_obj.resize height: (image_h = max_image_h)).output_width
77
- else
78
+ elsif image_w
78
79
  image_w = svg_size.output_width
80
+ else
81
+ fragment[:image_width] = (image_w = svg_size.output_width).to_s
82
+ image_w = available_w if image_w > available_w
79
83
  end
80
84
  fragment[:image_obj] = svg_obj
81
85
  else
82
86
  # TODO: cache image info based on path (Prawn caches based on SHA1 of content)
83
87
  # NOTE: image_obj is constrained to image_width by renderer
84
88
  image_obj, image_info = ::File.open(image_path, 'rb') {|fd| doc.build_image_object fd }
89
+ unless image_w
90
+ fragment[:image_width] = (image_w = to_pt image_info.width, :px).to_s
91
+ image_w = available_w if image_w > available_w
92
+ end
85
93
  if (image_h = image_w * (image_info.height.fdiv image_info.width)) > max_image_h
86
94
  # NOTE: the best we can do is make the image fit within full height of bounds
87
95
  image_w = (image_h = max_image_h) * (image_info.width.fdiv image_info.height)
@@ -117,7 +125,7 @@ module Asciidoctor::PDF::FormattedText
117
125
  fragment[:image_width] = fragment[:width] = image_w
118
126
  fragment[:image_height] = image_h
119
127
  rescue
120
- logger.warn %(could not embed image: #{image_path}; #{$!.message}#{::Prawn::Errors::UnsupportedImageType === $! && !(defined? ::GMagick::Image) ? '; install prawn-gmagick gem to add support' : ''}) unless scratch
128
+ logger.warn %(could not embed image: #{image_path}; #{$!.message}#{(doc.recommend_prawn_gmagick? $!, image_format) ? %(; install prawn-gmagick gem to add support for #{image_format&.upcase || 'unknown'} image format) : ''}) unless scratch
121
129
  drop = true # delegate to cleanup logic in ensure block
122
130
  ensure
123
131
  # NOTE: skip rendering image in scratch document or if image can't be loaded
@@ -36,8 +36,7 @@ module Asciidoctor::PDF::FormattedText
36
36
  end
37
37
  pdf.fill_color prev_fill_color
38
38
  end
39
- if (border_width = data[:border_width])
40
- border_color = data[:border_color]
39
+ if (border_width = data[:border_width]) && (border_color = data[:border_color])
41
40
  prev_stroke_color = pdf.stroke_color
42
41
  prev_line_width = pdf.line_width
43
42
  pdf.stroke_color border_color
@@ -69,7 +69,7 @@ module Asciidoctor
69
69
  # NOTE: base theme is loaded "as is" (no post-processing)
70
70
  def self.load_base_theme
71
71
  ::File.open BaseThemePath, mode: 'r:UTF-8' do |io|
72
- (::OpenStruct.new ::YAML.safe_load io, aliases: true, filename: BaseThemePath).tap {|theme| theme.__dir__ = ThemesDir }
72
+ (::OpenStruct.new ::YAML.safe_load io, filename: BaseThemePath).tap {|theme| theme.__dir__ = ThemesDir }
73
73
  end
74
74
  end
75
75
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Asciidoctor
4
4
  module PDF
5
- VERSION = '2.0.5'
5
+ VERSION = '2.0.8'
6
6
  end
7
7
  end
@@ -1,18 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ autoload :Set, 'set'
4
+ autoload :StringIO, 'stringio'
5
+ autoload :Tempfile, 'tempfile'
6
+ require 'time' unless defined? Time.parse
3
7
  require_relative 'pdf/version'
4
8
  require 'asciidoctor'
5
9
  require 'prawn'
6
- # NOTE: patch float precision constant so prawn-table does not fail to arrange cells that span columns (see #1835)
7
- Prawn.send :remove_const, :FLOAT_PRECISION
8
- Prawn::FLOAT_PRECISION = 1e-3
9
10
  require 'prawn/templates'
10
- begin
11
- require 'prawn/gmagick'
12
- rescue LoadError # rubocop:disable Lint/SuppressedException
13
- end unless defined? GMagick::Image
14
- autoload :Set, 'set'
15
- require 'time' unless defined? Time.parse
16
11
  require_relative 'pdf/measurements'
17
12
  require_relative 'pdf/sanitizer'
18
13
  require_relative 'pdf/text_transformer'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: asciidoctor-pdf
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.5
4
+ version: 2.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dan Allen
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2022-05-26 00:00:00.000000000 Z
12
+ date: 2022-06-08 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: asciidoctor
@@ -258,8 +258,12 @@ files:
258
258
  - lib/asciidoctor/pdf/ext/core/string.rb
259
259
  - lib/asciidoctor/pdf/ext/pdf-core.rb
260
260
  - lib/asciidoctor/pdf/ext/pdf-core/page.rb
261
+ - lib/asciidoctor/pdf/ext/prawn-gmagick.rb
261
262
  - lib/asciidoctor/pdf/ext/prawn-svg.rb
263
+ - lib/asciidoctor/pdf/ext/prawn-svg/calculators/document_sizing.rb
264
+ - lib/asciidoctor/pdf/ext/prawn-svg/elements/image.rb
262
265
  - lib/asciidoctor/pdf/ext/prawn-svg/loaders/data.rb
266
+ - lib/asciidoctor/pdf/ext/prawn-svg/loaders/file.rb
263
267
  - lib/asciidoctor/pdf/ext/prawn-svg/loaders/web.rb
264
268
  - lib/asciidoctor/pdf/ext/prawn-svg/url_loader.rb
265
269
  - lib/asciidoctor/pdf/ext/prawn-table.rb