asciidoctor-pdf 2.0.0 → 2.0.3

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: 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