asciidoctor-pdf 2.0.0 → 2.0.3

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: 027a0c91049b52843552b50087dc100ecaf0143bf2d342c2d3e5a8d37bd8fcda
4
- data.tar.gz: 1723e9c0ab2eab7f2f25dd31e6085e31103724c27eff2e3b9a1fe818d64161af
3
+ metadata.gz: b41dc215a6b942da9191850d15cf3e68224f1b633147b2df34a5e1e88756f797
4
+ data.tar.gz: 929dd071b2dc29b6d6f4500efb87e1ffa251c9413ae1e8076a14285bca51a600
5
5
  SHA512:
6
- metadata.gz: d563c7b4def055b2732563910f0abc318d09e566423592a008535730480363a52f36f0d3bd27ed431490d3cf80bc855c8e98dc271556b8b27ea63ebd1a7b284d
7
- data.tar.gz: 82834fe8af002bc265e09880d8ef11e25e6e9943c1bf6fb4359f655364a15b49e8b71134adbe0fb3db567759944915faabe1cf5720a558751c3fcb53266c1c3f
6
+ metadata.gz: 17d8c003b47e99e0e7156027783ff1fa1e8298767b82de209f40a523e40d6d1f35f7263c1b673bb686082a474a219c9fda26e3f2894852f51374dd554844aae2
7
+ data.tar.gz: 9711e5816aa9c9893ddaf139c717fb125d285b5788d42ebac4473660148f66126b1eccc1f389d781e4fc13ccb02010f9a909fa69580b7239765b907a160a4a3b
data/CHANGELOG.adoc CHANGED
@@ -5,6 +5,45 @@
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.3 (2022-05-25) - @mojavelinux
9
+
10
+ Improvements::
11
+
12
+ * compute the optimize settings in init_pdf and store as Hash instead of after writing the PDF file
13
+
14
+ Bug Fixes::
15
+
16
+ * adjust TrimBox to fit inside of BleedBox when using optimizer and compliance is PDF/X (#2203)
17
+ * set height of resized image to available height to avoid float precision error when scaling down image to fit page (#2205)
18
+ * prevent content on title page from overrunning the bounds of a single page and warn; restriction applies to `ink_title_page`
19
+
20
+ === Details
21
+
22
+ {url-repo}/releases/tag/v2.0.3[git tag] | {url-repo}/compare/v2.0.2\...v2.0.3[full diff]
23
+
24
+ == 2.0.2 (2022-05-22) - @mojavelinux
25
+
26
+ Bug Fixes::
27
+
28
+ * use specified column widths to avoid bugs in column width calculation when using colspans (#1368)
29
+ * advance table to next page if rowspan in first row does not fit in space remaining on current page (#403)
30
+
31
+ === Details
32
+
33
+ {url-repo}/releases/tag/v2.0.2[git tag] | {url-repo}/compare/v2.0.1\...v2.0.2[full diff]
34
+
35
+ == 2.0.1 (2022-05-21) - @mojavelinux
36
+
37
+ Bug Fixes::
38
+
39
+ * scale inline image to fit within available height of page, accounting for the top padding of the line height and the bottom gutter (#2193)
40
+ * short-circuit formatted text routine and log error if fragments in first line cannot fit on an empty page
41
+ * break and wrap long contiguous text in source block when linenums is enabled (#2198)
42
+
43
+ === Details
44
+
45
+ {url-repo}/releases/tag/v2.0.1[git tag] | {url-repo}/compare/v2.0.0\...v2.0.1[full diff]
46
+
8
47
  == 2.0.0 (2022-05-18) - @mojavelinux
9
48
 
10
49
  Improvements::
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.0, 2022-05-18
3
+ v2.0.3, 2022-05-25
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.0
27
+ :release-version: 2.0.3
28
28
  // URLs:
29
29
  :url-gem: https://rubygems.org/gems/asciidoctor-pdf
30
30
  :url-project: https://github.com/asciidoctor/asciidoctor-pdf
@@ -97,14 +97,7 @@ Pass the name of the gem to the `gem install` command as follows:
97
97
  Installing Asciidoctor PDF will install a number of other gems mentioned in these docs, including asciidoctor, prawn, prawn-svg, prawn-table, prawn-icon, and ttfunk.
98
98
  For the most part, the versions of these dependencies are locked to the version of Asciidoctor PDF.
99
99
 
100
- If you're using Ruby 3.1 or better, you must also install the matrix gem until Prawn 2.5.0 or better is released.
101
-
102
- $ gem install matrix
103
-
104
- The matrix gem used to be bundled in the Ruby distribution, but was split out starting in Ruby 3.1.
105
- This requirement will be lifted once Prawn declares it as a runtime dependency.
106
-
107
- For further installation information, see {url-project-docs}/install/[the installation documentation].
100
+ For further installation information about installing Asciidoctor PDF, see {url-project-docs}/install/[the installation documentation].
108
101
  For troubleshooting help, see {url-project-docs}/install/#installation-troubleshooting[Installation troubleshooting].
109
102
 
110
103
  === Install a prerelease or development version
@@ -152,7 +152,7 @@ module Asciidoctor
152
152
  # TODO: delegate to convert_method_missing
153
153
  log :warn, %(missing convert handler for #{name} node in #{@backend} backend)
154
154
  end
155
- # NOTE: inline nodes generate pseudo-HTML strings; the remainder write directly to PDF object
155
+ # NOTE: inline node handlers generate HTML-like strings; all other handlers write directly to the PDF object
156
156
  ::Asciidoctor::Inline === node ? result : self
157
157
  end
158
158
 
@@ -174,7 +174,9 @@ module Asciidoctor
174
174
  if (has_title_page = (title_page_on = doc.doctype == 'book' || (doc.attr? 'title-page')) && (start_title_page doc))
175
175
  # NOTE: the base font must be set before any content is written to the main or scratch document
176
176
  font @theme.base_font_family, size: @root_font_size, style: @theme.base_font_style
177
- ink_title_page doc
177
+ if perform_on_single_page { ink_title_page doc }
178
+ log :warn, 'the title page contents has been truncated to prevent it from overrunning the bounds of a single page'
179
+ end
178
180
  start_new_page
179
181
  else
180
182
  @page_margin_by_side[:cover] = @page_margin_by_side[:recto] if @media == 'prepress' && page_number == 0
@@ -321,6 +323,7 @@ module Asciidoctor
321
323
  stamp_foreground_image doc, has_front_cover
322
324
  ink_cover_page doc, :back
323
325
  add_dest_for_top doc
326
+ state.pages.each {|it| fit_trim_box it } if (@optimize&.[] :compliance)&.start_with? 'PDF/X'
324
327
  nil
325
328
  end
326
329
 
@@ -411,8 +414,14 @@ module Asciidoctor
411
414
  # NOTE: we have to init Pdfmark class here while we have reference to the doc
412
415
  @pdfmark = (doc.attr? 'pdfmark') ? (Pdfmark.new doc) : nil
413
416
  # NOTE: defer instantiating optimizer until we know min pdf version
414
- if (@optimize = doc.attr 'optimize')
415
- @optimize = nil unless (defined? ::Asciidoctor::PDF::Optimizer) || !(Helpers.require_library OptimizerRequirePath, 'rghost', :warn).nil? # rubocop:disable Style/SoleNestedConditional
417
+ if (optimize = doc.attr 'optimize') &&
418
+ ((defined? ::Asciidoctor::PDF::Optimizer) || !(Helpers.require_library OptimizerRequirePath, 'rghost', :warn).nil?)
419
+ @optimize = (optimize.include? ',') ?
420
+ ([:quality, :compliance].zip (optimize.split ',', 2)).to_h :
421
+ ((optimize.include? '/') ? { compliance: optimize } : { quality: optimize })
422
+ fit_trim_box if @optimize[:compliance]&.start_with? 'PDF/X'
423
+ else
424
+ @optimize = nil
416
425
  end
417
426
  allocate_scratch_prototype
418
427
  self
@@ -717,7 +726,7 @@ module Asciidoctor
717
726
  when 'page'
718
727
  pagenums = term.dests.uniq {|dest| dest[:page] }.map {|dest| %(<a anchor="#{dest[:anchor]}">#{dest[:page]}</a>) }
719
728
  when 'range'
720
- first_anchor_per_page = term.dests.each_with_object({}) {|dest, accum| accum[dest[:page]] ||= dest[:anchor] }
729
+ first_anchor_per_page = {}.tap {|accum| term.dests.each {|dest| accum[dest[:page]] ||= dest[:anchor] } }
721
730
  pagenums = (consolidate_ranges first_anchor_per_page.keys).map do |range|
722
731
  anchor = first_anchor_per_page[(range.include? '-') ? (range.partition '-')[0] : range]
723
732
  %(<a anchor="#{anchor}">#{range}</a>)
@@ -814,7 +823,7 @@ module Asciidoctor
814
823
  if (text_align = resolve_text_align_from_role (roles = node.roles), query_theme: true, remove_predefined: true)
815
824
  prose_opts[:align] = text_align
816
825
  end
817
- role_keys = roles.map {|role| %(role_#{role}).to_sym } unless roles.empty?
826
+ role_keys = roles.map {|role| %(role_#{role}) } unless roles.empty?
818
827
  if (text_indent = @theme.prose_text_indent) > 0 ||
819
828
  ((text_indent = @theme.prose_text_indent_inner) > 0 && node.previous_sibling&.context == :paragraph)
820
829
  prose_opts[:indent_paragraphs] = text_indent
@@ -1081,7 +1090,7 @@ module Asciidoctor
1081
1090
  highlight_lines = nil
1082
1091
  else
1083
1092
  pg_highlight_bg_color = pg_block_styles[:highlight_background_color]
1084
- highlight_lines = highlight_lines.map {|linenum| [linenum, pg_highlight_bg_color] }.to_h
1093
+ highlight_lines = {}.tap {|accum| highlight_lines.each {|linenum| accum[linenum] = pg_highlight_bg_color } }
1085
1094
  end
1086
1095
  end
1087
1096
  if (node.option? 'linenums') || (node.attr? 'linenums')
@@ -1110,17 +1119,17 @@ module Asciidoctor
1110
1119
  end
1111
1120
  if (srclang = node.attr 'language')
1112
1121
  if srclang.include? '?'
1113
- if (lexer = ::Rouge::Lexer.find_fancy srclang) && lexer.tag == 'php' && !(node.option? 'mixed') && !((lexer_opts = lexer.options).key? 'start_inline')
1122
+ if (lexer = ::Rouge::Lexer.find_fancy srclang)&.tag == 'php' && !(node.option? 'mixed') && !((lexer_opts = lexer.options).key? 'start_inline')
1114
1123
  lexer = lexer.class.new lexer_opts.merge 'start_inline' => true
1115
1124
  end
1116
- elsif (lexer = ::Rouge::Lexer.find srclang)
1117
- lexer = lexer.new start_inline: true if lexer.tag == 'php' && !(node.option? 'mixed')
1125
+ elsif (lexer = ::Rouge::Lexer.find srclang)&.tag == 'php' && !(node.option? 'mixed')
1126
+ lexer = lexer.new start_inline: true
1118
1127
  end
1119
1128
  end
1120
1129
  lexer ||= ::Rouge::Lexers::PlainText
1121
1130
  source_string, conum_mapping = extract_conums source_string if callouts_enabled
1122
1131
  if (node.attr? 'highlight') && !(hl_lines = (node.resolve_lines_to_highlight source_string, (node.attr 'highlight'))).empty?
1123
- formatter_opts[:highlight_lines] = hl_lines.map {|linenum| [linenum, true] }.to_h
1132
+ formatter_opts[:highlight_lines] = {}.tap {|accum| hl_lines.each {|linenum| accum[linenum] = true } }
1124
1133
  end
1125
1134
  fragments = formatter.format (lexer.lex source_string), formatter_opts rescue [text: source_string]
1126
1135
  source_chunks = conum_mapping ? (restore_conums fragments, conum_mapping) : fragments
@@ -1205,10 +1214,10 @@ module Asciidoctor
1205
1214
  b_left_width = nil
1206
1215
  b_width = nil if (b_width = @theme[%(#{category}_border_width)]) == 0
1207
1216
  end
1208
- if (attribution = (node.attr? 'attribution') && (node.attr 'attribution'))
1217
+ if (attribution = node.attr 'attribution')
1209
1218
  # NOTE: temporary workaround to allow bare & to be used without having to wrap value in single quotes
1210
1219
  attribution = escape_amp attribution if attribution.include? '&'
1211
- if (citetitle = node.attr 'citetitle') && (citetitle.include? '&')
1220
+ if (citetitle = node.attr 'citetitle')&.include? '&'
1212
1221
  citetitle = escape_amp citetitle
1213
1222
  end
1214
1223
  end
@@ -1396,7 +1405,7 @@ module Asciidoctor
1396
1405
  end
1397
1406
  theme_margin :prose, :bottom, (next_enclosed_block actual_node) #unless actual_node.nested?
1398
1407
  when 'qanda'
1399
- @list_numerals << '1'
1408
+ @list_numerals << 1
1400
1409
  convert_list node
1401
1410
  @list_numerals.pop
1402
1411
  else
@@ -1760,7 +1769,7 @@ module Asciidoctor
1760
1769
  advance_page
1761
1770
  available_h = cursor - caption_h
1762
1771
  end
1763
- rendered_w, rendered_h = image_info.calc_image_dimensions height: available_h if rendered_h > available_h
1772
+ rendered_w = (image_info.calc_image_dimensions height: (rendered_h = available_h))[0] if rendered_h > available_h
1764
1773
  end
1765
1774
  add_dest_for_block node if node.id
1766
1775
  # NOTE: workaround to fix Prawn not adding fill and stroke commands on page that only has an image;
@@ -2167,6 +2176,7 @@ module Asciidoctor
2167
2176
 
2168
2177
  left_padding = right_padding = nil
2169
2178
  table table_data, table_settings do
2179
+ @column_widths = column_widths unless column_widths.empty?
2170
2180
  # NOTE: call width to capture resolved table width
2171
2181
  table_width = width
2172
2182
  @pdf.ink_table_caption node, alignment, table_width, caption_max_width if node.title? && caption_end == :top
@@ -3261,64 +3271,70 @@ module Asciidoctor
3261
3271
  end
3262
3272
  end
3263
3273
 
3264
- colspec_dict = PageSides.each_with_object({}) do |side, acc|
3265
- side_trim_content_width = trim_content_width[side]
3266
- if (custom_colspecs = @theme[%(#{periphery}_#{side}_columns)] || @theme[%(#{periphery}_columns)])
3267
- case (colspecs = (custom_colspecs.to_s.tr ',', ' ').split).size
3268
- when 0, 1
3269
- colspecs = { left: '0', center: colspecs[0] || '100', right: '0' }
3270
- when 2
3271
- colspecs = { left: colspecs[0], center: '0', right: colspecs[1] }
3272
- else # 3
3273
- colspecs = { left: colspecs[0], center: colspecs[1], right: colspecs[2] }
3274
- end
3275
- tot_width = 0
3276
- side_colspecs = colspecs.map do |col, spec|
3277
- if (alignment_char = spec.chr).to_i.to_s == alignment_char
3278
- alignment = :left
3279
- rel_width = spec.to_f
3280
- else
3281
- alignment = AlignmentTable[alignment_char]
3282
- rel_width = (spec.slice 1, spec.length).to_f
3274
+ colspec_dict = {}.tap do |acc|
3275
+ PageSides.each do |side|
3276
+ side_trim_content_width = trim_content_width[side]
3277
+ if (custom_colspecs = @theme[%(#{periphery}_#{side}_columns)] || @theme[%(#{periphery}_columns)])
3278
+ case (colspecs = (custom_colspecs.to_s.tr ',', ' ').split).size
3279
+ when 0, 1
3280
+ colspecs = { left: '0', center: colspecs[0] || '100', right: '0' }
3281
+ when 2
3282
+ colspecs = { left: colspecs[0], center: '0', right: colspecs[1] }
3283
+ else # 3
3284
+ colspecs = { left: colspecs[0], center: colspecs[1], right: colspecs[2] }
3283
3285
  end
3284
- tot_width += rel_width
3285
- [col, align: alignment, width: rel_width, x: 0]
3286
- end.to_h
3287
- # QUESTION: should we allow the columns to overlap (capping width at 100%)?
3288
- side_colspecs.each {|_, colspec| colspec[:width] = (colspec[:width] / tot_width) * side_trim_content_width }
3289
- side_colspecs[:right][:x] = (side_colspecs[:center][:x] = side_colspecs[:left][:width]) + side_colspecs[:center][:width]
3290
- acc[side] = side_colspecs
3291
- else
3292
- acc[side] = {
3293
- left: { align: :left, width: side_trim_content_width, x: 0 },
3294
- center: { align: :center, width: side_trim_content_width, x: 0 },
3295
- right: { align: :right, width: side_trim_content_width, x: 0 },
3296
- }
3286
+ tot_width = 0
3287
+ side_colspecs = {}.tap do |accum|
3288
+ colspecs.each do |col, spec|
3289
+ if (alignment_char = spec.chr).to_i.to_s == alignment_char
3290
+ alignment = :left
3291
+ rel_width = spec.to_f
3292
+ else
3293
+ alignment = AlignmentTable[alignment_char]
3294
+ rel_width = (spec.slice 1, spec.length).to_f
3295
+ end
3296
+ tot_width += rel_width
3297
+ accum[col] = { align: alignment, width: rel_width, x: 0 }
3298
+ end
3299
+ end
3300
+ # QUESTION: should we allow the columns to overlap (capping width at 100%)?
3301
+ side_colspecs.each {|_, colspec| colspec[:width] = (colspec[:width] / tot_width) * side_trim_content_width }
3302
+ side_colspecs[:right][:x] = (side_colspecs[:center][:x] = side_colspecs[:left][:width]) + side_colspecs[:center][:width]
3303
+ acc[side] = side_colspecs
3304
+ else
3305
+ acc[side] = {
3306
+ left: { align: :left, width: side_trim_content_width, x: 0 },
3307
+ center: { align: :center, width: side_trim_content_width, x: 0 },
3308
+ right: { align: :right, width: side_trim_content_width, x: 0 },
3309
+ }
3310
+ end
3297
3311
  end
3298
3312
  end
3299
3313
 
3300
- content_dict = PageSides.each_with_object({}) do |side, acc|
3301
- side_content = {}
3302
- ColumnPositions.each do |position|
3303
- next if (val = @theme[%(#{periphery}_#{side}_#{position}_content)]).nil_or_empty?
3304
- val = val.to_s unless ::String === val
3305
- if (val.include? ':') && val =~ ImageAttributeValueRx
3306
- attrlist = $2
3307
- image_attrs = (AttributeList.new attrlist).parse %w(alt width)
3308
- image_path, image_format = ::Asciidoctor::Image.target_and_format $1, image_attrs
3309
- if (image_path = resolve_image_path doc, image_path, image_format, @themesdir) && (::File.readable? image_path)
3310
- image_opts = resolve_image_options image_path, image_format, image_attrs, container_size: [colspec_dict[side][position][:width], trim_content_height[side]]
3311
- side_content[position] = [image_path, image_opts, image_attrs['link']]
3314
+ content_dict = {}.tap do |acc|
3315
+ PageSides.each do |side|
3316
+ side_content = {}
3317
+ ColumnPositions.each do |position|
3318
+ next if (val = @theme[%(#{periphery}_#{side}_#{position}_content)]).nil_or_empty?
3319
+ val = val.to_s unless ::String === val
3320
+ if (val.include? ':') && val =~ ImageAttributeValueRx
3321
+ attrlist = $2
3322
+ image_attrs = (AttributeList.new attrlist).parse %w(alt width)
3323
+ image_path, image_format = ::Asciidoctor::Image.target_and_format $1, image_attrs
3324
+ if (image_path = resolve_image_path doc, image_path, image_format, @themesdir) && (::File.readable? image_path)
3325
+ image_opts = resolve_image_options image_path, image_format, image_attrs, container_size: [colspec_dict[side][position][:width], trim_content_height[side]]
3326
+ side_content[position] = [image_path, image_opts, image_attrs['link']]
3327
+ else
3328
+ # NOTE: allows inline image handler to report invalid reference and replace with alt text
3329
+ side_content[position] = %(image:#{image_path}[#{attrlist}])
3330
+ end
3312
3331
  else
3313
- # NOTE: allows inline image handler to report invalid reference and replace with alt text
3314
- side_content[position] = %(image:#{image_path}[#{attrlist}])
3332
+ side_content[position] = val
3315
3333
  end
3316
- else
3317
- side_content[position] = val
3318
3334
  end
3319
- end
3320
3335
 
3321
- acc[side] = side_content
3336
+ acc[side] = side_content
3337
+ end
3322
3338
  end
3323
3339
 
3324
3340
  if (trim_bg_color = trim_styles[:bg_color]) || trim_bg_image || trim_border_width > 0
@@ -3601,7 +3617,6 @@ module Asciidoctor
3601
3617
  end
3602
3618
  end
3603
3619
 
3604
- # TODO: prevent content from spilling to next page
3605
3620
  theme_font :title_page do
3606
3621
  if (title_top = @theme.title_page_title_top)
3607
3622
  @y = resolve_top title_top
@@ -3866,18 +3881,17 @@ module Asciidoctor
3866
3881
 
3867
3882
  def register_fonts font_catalog, fonts_dir
3868
3883
  return unless font_catalog
3869
- dirs = (fonts_dir.split ValueSeparatorRx, -1).map do |dir|
3870
- dir == 'GEM_FONTS_DIR' || dir.empty? ? ThemeLoader::FontsDir : dir
3871
- end
3884
+ dirs = (fonts_dir.split ValueSeparatorRx, -1).map {|dir| dir == 'GEM_FONTS_DIR' || dir.empty? ? ThemeLoader::FontsDir : dir }
3872
3885
  font_catalog.each do |key, styles|
3873
- styles = styles.each_with_object({}) do |(style, path), accum|
3874
- found = dirs.any? do |dir|
3875
- resolved_font_path = font_path path, dir
3876
- accum[style.to_sym] = resolved_font_path if ::File.readable? resolved_font_path
3886
+ register_font key => ({}.tap do |accum|
3887
+ styles.each do |style, path|
3888
+ found = dirs.any? do |dir|
3889
+ resolved_font_path = font_path path, dir
3890
+ accum[style.to_sym] = resolved_font_path if ::File.readable? resolved_font_path
3891
+ end
3892
+ raise ::Errno::ENOENT, ((File.absolute_path? path) ? %(#{path} not found) : %(#{path} not found in #{fonts_dir.gsub ValueSeparatorRx, ' or '})) unless found
3877
3893
  end
3878
- raise ::Errno::ENOENT, ((File.absolute_path? path) ? %(#{path} not found) : %(#{path} not found in #{fonts_dir.gsub ValueSeparatorRx, ' or '})) unless found
3879
- end
3880
- register_font key => styles
3894
+ end)
3881
3895
  end
3882
3896
  end
3883
3897
 
@@ -4397,13 +4411,8 @@ module Asciidoctor
4397
4411
  pdf_doc.render_file target
4398
4412
  # QUESTION: restore attributes first?
4399
4413
  @pdfmark&.generate_file target
4400
- if (quality = @optimize)
4401
- if quality.include? ','
4402
- quality, compliance = quality.split ',', 2
4403
- elsif quality.include? '/'
4404
- quality, compliance = nil, quality
4405
- end
4406
- (Optimizer.new quality, pdf_doc.min_version, compliance).optimize_file target
4414
+ if (optimize = @optimize)
4415
+ (Optimizer.new optimize[:quality], pdf_doc.min_version, optimize[:compliance]).optimize_file target
4407
4416
  end
4408
4417
  to_file = true
4409
4418
  end
@@ -4578,14 +4587,16 @@ module Asciidoctor
4578
4587
  def consolidate_ranges nums
4579
4588
  if nums.size > 1
4580
4589
  prev = nil
4581
- nums.each_with_object [] do |num, accum|
4590
+ accum = []
4591
+ nums.each do |num|
4582
4592
  if prev && (prev.to_i + 1) == num.to_i
4583
4593
  accum[-1][1] = num
4584
4594
  else
4585
4595
  accum << [num]
4586
4596
  end
4587
4597
  prev = num
4588
- end.map {|range| range.join '-' }
4598
+ end
4599
+ accum.map {|range| range.join '-' }
4589
4600
  else
4590
4601
  nums
4591
4602
  end
@@ -4628,6 +4639,15 @@ module Asciidoctor
4628
4639
  (max_height = bounds.height) < preferred_size ? max_height : preferred_size
4629
4640
  end
4630
4641
 
4642
+ def fit_trim_box page_ = page
4643
+ page_.dictionary.data[:TrimBox].tap do |trim_box|
4644
+ trim_box[0] += 1e-4
4645
+ trim_box[1] += 1e-4
4646
+ trim_box[2] -= 1e-4
4647
+ trim_box[3] -= 1e-4
4648
+ end
4649
+ end
4650
+
4631
4651
  def font_path font_file, fonts_dir
4632
4652
  # resolve relative to built-in font dir unless path is absolute
4633
4653
  ::File.absolute_path font_file, fonts_dir
@@ -4930,14 +4950,16 @@ module Asciidoctor
4930
4950
  result = yield
4931
4951
  else
4932
4952
  email = nil
4933
- original_attrs = AuthorAttributeNames.each_with_object({}) do |(prop_name, attr_name), accum|
4934
- accum[attr_name] = doc.attr attr_name
4935
- if (val = author[prop_name])
4936
- doc.set_attr attr_name, val
4937
- # NOTE: email attribute could be a url
4938
- email = val if prop_name == :email
4939
- else
4940
- doc.remove_attr attr_name
4953
+ original_attrs = {}.tap do |accum|
4954
+ AuthorAttributeNames.each do |prop_name, attr_name|
4955
+ accum[attr_name] = doc.attr attr_name
4956
+ if (val = author[prop_name])
4957
+ doc.set_attr attr_name, val
4958
+ # NOTE: email attribute could be a url
4959
+ email = val if prop_name == :email
4960
+ else
4961
+ doc.remove_attr attr_name
4962
+ end
4941
4963
  end
4942
4964
  end
4943
4965
  doc.set_attr 'url', ((email.include? '@') ? %(mailto:#{email}) : email) if email
@@ -4,7 +4,7 @@ Prawn::Font::AFM.instance_variable_set :@hide_m17n_warning, true
4
4
 
5
5
  require 'prawn/icon'
6
6
 
7
- Prawn::Icon::Compatibility.send :prepend, (::Module.new { def warning *_args; end })
7
+ Prawn::Icon::Compatibility.prepend (::Module.new { def warning *_args; end })
8
8
 
9
9
  module Asciidoctor
10
10
  module Prawn
@@ -431,15 +431,19 @@ module Asciidoctor
431
431
 
432
432
  # NOTE: override built-in fill_formatted_text_box to insert leading before second line when :first_line is true
433
433
  def fill_formatted_text_box text, options
434
- if (initial_gap = options[:initial_gap]) && (first_text = text[0]) && first_text[:from_page] != page_number
434
+ if (initial_gap = options[:initial_gap]) && !text.empty? && text[0][:from_page] != page_number
435
435
  self.y -= initial_gap
436
436
  end
437
437
  merge_text_box_positioning_options options
438
438
  box = ::Prawn::Text::Formatted::Box.new text, options
439
- remaining_text = box.render
439
+ remaining_fragments = box.render
440
440
  @no_text_printed = box.nothing_printed?
441
441
  @all_text_printed = box.everything_printed?
442
- remaining_text[0][:from_page] = page_number unless remaining_text.empty?
442
+ unless remaining_fragments.empty? || (remaining_fragments[0][:from_page] ||= page_number) == page_number
443
+ log :error, %(cannot fit formatted text on page: #{remaining_fragments.map {|it| it[:image_path] || it[:text] }.join})
444
+ page.tare_content_stream
445
+ remaining_fragments = {}
446
+ end
443
447
 
444
448
  if @final_gap || (options[:first_line] && !(@no_text_printed || @all_text_printed))
445
449
  self.y -= box.height + box.line_gap + box.leading
@@ -447,7 +451,7 @@ module Asciidoctor
447
451
  self.y -= box.height
448
452
  end
449
453
 
450
- remaining_text
454
+ remaining_fragments
451
455
  end
452
456
 
453
457
  # NOTE: override built-in draw_indented_formatted_line to set first_line flag
@@ -483,7 +487,13 @@ module Asciidoctor
483
487
  else
484
488
  remaining_fragments = box.render dry_run: true
485
489
  end
486
- remaining_fragments.empty? ? (remaining_fragments = nil) : (remaining_fragments[0][:from_page] = page_number)
490
+ if remaining_fragments.empty?
491
+ remaining_fragments = nil
492
+ elsif (remaining_fragments[0][:from_page] ||= page_number) != page_number
493
+ log :error, %(cannot fit formatted text on page: #{remaining_fragments.map {|it| it[:image_path] || it[:text] }.join})
494
+ page.tare_content_stream
495
+ remaining_fragments = nil
496
+ end
487
497
  if first_line_text_transform
488
498
  # NOTE: applying text transform here could alter the wrapping, so isolate first line and shrink it to fit
489
499
  first_line_text = (box.instance_variable_get :@printed_lines)[0]
@@ -2,12 +2,15 @@
2
2
 
3
3
  module Prawn::Text::Formatted::ProtectBottomGutter
4
4
  def enough_height_for_this_line?
5
- return super unless @arranger.finished?
6
- begin
7
- @height -= @bottom_gutter
5
+ if @arranger.finished? && @arranger.fragments.none? {|it| it.format_state[:full_height] }
6
+ begin
7
+ @height -= @bottom_gutter
8
+ super
9
+ ensure
10
+ @height += @bottom_gutter
11
+ end
12
+ else
8
13
  super
9
- ensure
10
- @height += @bottom_gutter
11
14
  end
12
15
  end
13
16
  end
@@ -1,6 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'prawn/table'
4
+
5
+ Prawn::Table.prepend (Module.new do
6
+ def initial_row_on_initial_page
7
+ return 0 if fits_on_page? @pdf.bounds.height
8
+ height_required = (row (0..number_of_header_rows)).height_with_span
9
+ return -1 if fits_on_page? height_required, true
10
+ @pdf.bounds.move_past_bottom
11
+ 0
12
+ end
13
+ end)
14
+
4
15
  require_relative 'prawn-table/cell'
5
16
  require_relative 'prawn-table/cell/asciidoc'
6
17
  require_relative 'prawn-table/cell/text'
@@ -38,7 +38,7 @@ module Asciidoctor::PDF::FormattedText
38
38
  return if (raw_image_fragments = fragments.select {|f| (f.key? :image_path) && !(f.key? :image_obj) }).empty?
39
39
  scratch = doc.scratch?
40
40
  available_w = available_width
41
- available_h = doc.page.empty? ? doc.cursor : doc.bounds.height
41
+ available_h = doc.bounds.height
42
42
  last_fragment = {}
43
43
  raw_image_fragments.each do |fragment|
44
44
  if fragment[:object_id] == last_fragment[:object_id]
@@ -96,6 +96,10 @@ module Asciidoctor::PDF::FormattedText
96
96
  if (f_height = image_h) > (line_font = doc.font).height * 1.5
97
97
  # align with descender (equivalent to vertical-align: bottom in CSS)
98
98
  fragment[:ascender] = f_height - (fragment[:descender] = line_font.descender)
99
+ if f_height == available_h
100
+ fragment[:ascender] -= (doc.calc_line_metrics (doc.instance_variable_get :@base_line_height), line_font, doc.font_size).padding_top
101
+ fragment[:full_height] = true
102
+ end
99
103
  doc.font_size (fragment[:size] = f_height * (doc.font_size / line_font.height))
100
104
  # align with baseline (roughly equivalent to vertical-align: baseline in CSS)
101
105
  #fragment[:ascender] = f_height
@@ -11,15 +11,15 @@ module Asciidoctor
11
11
  def wrap array
12
12
  return super unless array[0][:linenum] # sanity check
13
13
  initialize_wrap array
14
+ @line_wrap.extend SourceLineWrap
14
15
  highlight_line = stop = nil
15
16
  unconsumed = @arranger.unconsumed
16
17
  until stop
17
18
  if (first_fragment = unconsumed[0])[:linenum]
18
19
  linenum_text = first_fragment[:text]
19
- linenum_spacer ||= { text: (NoBreakSpace.encode linenum_text.encoding) + (' ' * (linenum_text.length - 1)) }
20
+ linenum_spacer ||= { text: (NoBreakSpace.encode linenum_text.encoding) + (' ' * (linenum_text.length - 1)), linenum: :spacer }
20
21
  highlight_line = (second_fragment = unconsumed[1])[:highlight] ? second_fragment.dup : nil
21
- else
22
- # NOTE: a wrapped line
22
+ else # wrapped line
23
23
  first_fragment[:text] = first_fragment[:text].lstrip
24
24
  @arranger.unconsumed.unshift highlight_line if highlight_line
25
25
  @arranger.unconsumed.unshift linenum_spacer.dup
@@ -43,6 +43,12 @@ module Asciidoctor
43
43
  @arranger.unconsumed
44
44
  end
45
45
  end
46
+
47
+ module SourceLineWrap
48
+ def update_line_status_based_on_last_output
49
+ @arranger.current_format_state[:linenum] ? nil : super
50
+ end
51
+ end
46
52
  end
47
53
  end
48
54
  end
@@ -95,22 +95,24 @@ module Asciidoctor
95
95
  styles: (to_styles theme.menu_font_style),
96
96
  }.compact,
97
97
  }
98
- revise_roles = [].to_set
99
- theme.each_pair.each_with_object @theme_settings do |(key, val), accum|
100
- next unless (key = key.to_s).start_with? 'role_'
101
- role, key = (key.slice 5, key.length).split '_', 2
102
- if (prop = ThemeKeyToFragmentProperty[key])
103
- (accum[role] ||= {})[prop] = val
104
- #elsif key == 'font_kerning'
105
- # unless (resolved_val = val == 'none' ? false : (val == 'normal' ? true : nil)).nil?
106
- # (accum[role] ||= {})[:kerning] = resolved_val
107
- # end
108
- elsif key == 'font_style' || key == 'text_decoration'
109
- revise_roles << role
98
+ @theme_settings.tap do |accum|
99
+ revise_roles = [].to_set
100
+ theme.each_pair do |key, val|
101
+ next unless (key = key.to_s).start_with? 'role_'
102
+ role, key = (key.slice 5, key.length).split '_', 2
103
+ if (prop = ThemeKeyToFragmentProperty[key])
104
+ (accum[role] ||= {})[prop] = val
105
+ #elsif key == 'font_kerning'
106
+ # unless (resolved_val = val == 'none' ? false : (val == 'normal' ? true : nil)).nil?
107
+ # (accum[role] ||= {})[:kerning] = resolved_val
108
+ # end
109
+ elsif key == 'font_style' || key == 'text_decoration'
110
+ revise_roles << role
111
+ end
112
+ end
113
+ revise_roles.each do |role|
114
+ (accum[role] ||= {})[:styles] = to_styles theme[%(role_#{role}_font_style)], theme[%(role_#{role}_text_decoration)]
110
115
  end
111
- end
112
- revise_roles.each_with_object @theme_settings do |role, accum|
113
- (accum[role] ||= {})[:styles] = to_styles theme[%(role_#{role}_font_style)], theme[%(role_#{role}_text_decoration)]
114
116
  end
115
117
  @theme_settings['line-through'] = { styles: [:strikethrough].to_set } unless @theme_settings.key? 'line-through'
116
118
  @theme_settings['underline'] = { styles: [:underline].to_set } unless @theme_settings.key? 'underline'
@@ -276,8 +278,10 @@ module Asciidoctor
276
278
  when '#' # hex string (e.g., #FF0000)
277
279
  fragment[:color] = value.length == 7 ? (value.slice 1, 6) : (value.slice 1, 3).each_char.map {|c| c * 2 }.join if HexColorRx.match? value
278
280
  when '[' # CMYK array (e.g., [50, 100, 0, 0])
279
- fragment[:color] = ((((value.slice 1, value.length).chomp ']').split ', ', 4).each_with_object ::Array.new 4, 0).with_index do |(it, accum), idx|
280
- accum[idx] = (ival = it.to_i) == (fval = it.to_f) ? ival : fval
281
+ fragment[:color] = [0, 0, 0, 0].tap do |accum|
282
+ (((value.slice 1, value.length).chomp ']').split ', ', 4).each_with_index do |it, idx|
283
+ accum[idx] = (ival = it.to_i) == (fval = it.to_f) ? ival : fval
284
+ end
281
285
  end
282
286
  else # assume a 6-character hex color (internal only)
283
287
  fragment[:color] = value
@@ -15,9 +15,7 @@ module Asciidoctor
15
15
  BaseThemePath = ::File.join ThemesDir, 'base-theme.yml'
16
16
  BundledThemeNames = (::Dir.children ThemesDir).map {|it| it.slice 0, it.length - 10 }
17
17
  DeprecatedCategoryKeys = { 'blockquote' => 'quote', 'key' => 'kbd', 'literal' => 'codespan', 'outline_list' => 'list' }
18
- DeprecatedKeys = %w(base heading heading_h1 heading_h2 heading_h3 heading_h4 heading_h5 heading_h6 title_page abstract abstract_title admonition_label sidebar_title toc_title).each_with_object({ 'table_caption_side' => 'table_caption_end' }) do |prefix, accum|
19
- accum[%(#{prefix}_align)] = %(#{prefix}_text_align)
20
- end
18
+ DeprecatedKeys = { 'table_caption_side' => 'table_caption_end' }.tap {|accum| %w(base heading heading_h1 heading_h2 heading_h3 heading_h4 heading_h5 heading_h6 title_page abstract abstract_title admonition_label sidebar_title toc_title).each {|prefix| accum[%(#{prefix}_align)] = %(#{prefix}_text_align) } }
21
19
  PaddingBottomHackKeys = %w(example_padding quote_padding sidebar_padding verse_padding)
22
20
 
23
21
  VariableRx = /\$([a-z0-9_-]+)/
@@ -159,10 +157,12 @@ module Asciidoctor
159
157
  elsif key == 'font_fallbacks'
160
158
  data[key] = ::Array === val ? val.map {|name| expand_vars name.to_s, data } : []
161
159
  elsif key.start_with? 'admonition_icon_'
162
- data[key] = val.map do |(key2, val2)|
163
- key2 = key2.tr '-', '_' if key2.include? '-'
164
- [key2.to_sym, (key2.end_with? '_color') ? (to_color evaluate val2, data) : (evaluate val2, data)]
165
- end.to_h if val
160
+ data[key] = {}.tap do |accum|
161
+ val.each do |key2, val2|
162
+ key2 = key2.tr '-', '_' if key2.include? '-'
163
+ accum[key2.to_sym] = (key2.end_with? '_color') ? (to_color evaluate val2, data) : (evaluate val2, data)
164
+ end
165
+ end if val
166
166
  elsif ::Hash === val
167
167
  if (rekey = DeprecatedCategoryKeys[key])
168
168
  logger.warn %(the #{key.tr '_', '-'} theme category is deprecated; use the #{rekey.tr '_', '-'} category instead)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Asciidoctor
4
4
  module PDF
5
- VERSION = '2.0.0'
5
+ VERSION = '2.0.3'
6
6
  end
7
7
  end
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.0
4
+ version: 2.0.3
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-18 00:00:00.000000000 Z
12
+ date: 2022-05-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: asciidoctor