asciidoctor-pdf 2.0.5 → 2.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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