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 +4 -4
- data/CHANGELOG.adoc +39 -0
- data/README.adoc +3 -10
- data/lib/asciidoctor/pdf/converter.rb +115 -93
- data/lib/asciidoctor/pdf/ext/prawn/extensions.rb +16 -6
- data/lib/asciidoctor/pdf/ext/prawn/formatted_text/protect_bottom_gutter.rb +8 -5
- data/lib/asciidoctor/pdf/ext/prawn-table.rb +11 -0
- data/lib/asciidoctor/pdf/formatted_text/inline_image_arranger.rb +5 -1
- data/lib/asciidoctor/pdf/formatted_text/source_wrap.rb +9 -3
- data/lib/asciidoctor/pdf/formatted_text/transform.rb +21 -17
- data/lib/asciidoctor/pdf/theme_loader.rb +7 -7
- data/lib/asciidoctor/pdf/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b41dc215a6b942da9191850d15cf3e68224f1b633147b2df34a5e1e88756f797
|
4
|
+
data.tar.gz: 929dd071b2dc29b6d6f4500efb87e1ffa251c9413ae1e8076a14285bca51a600
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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.
|
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
|
-
|
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
|
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 (
|
415
|
-
|
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.
|
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})
|
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.
|
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)
|
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
|
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.
|
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 =
|
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')
|
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 <<
|
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
|
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 =
|
3265
|
-
|
3266
|
-
|
3267
|
-
|
3268
|
-
|
3269
|
-
|
3270
|
-
|
3271
|
-
|
3272
|
-
|
3273
|
-
|
3274
|
-
|
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
|
3285
|
-
|
3286
|
-
|
3287
|
-
|
3288
|
-
|
3289
|
-
|
3290
|
-
|
3291
|
-
|
3292
|
-
|
3293
|
-
|
3294
|
-
|
3295
|
-
|
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 =
|
3301
|
-
|
3302
|
-
|
3303
|
-
|
3304
|
-
|
3305
|
-
|
3306
|
-
|
3307
|
-
|
3308
|
-
|
3309
|
-
|
3310
|
-
|
3311
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
3874
|
-
|
3875
|
-
|
3876
|
-
|
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
|
-
|
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 (
|
4401
|
-
|
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
|
-
|
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
|
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 =
|
4934
|
-
|
4935
|
-
|
4936
|
-
|
4937
|
-
|
4938
|
-
|
4939
|
-
|
4940
|
-
|
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.
|
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]) &&
|
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
|
-
|
439
|
+
remaining_fragments = box.render
|
440
440
|
@no_text_printed = box.nothing_printed?
|
441
441
|
@all_text_printed = box.everything_printed?
|
442
|
-
|
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
|
-
|
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?
|
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
|
-
|
6
|
-
|
7
|
-
|
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.
|
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
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
(
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
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] =
|
280
|
-
|
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).
|
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] =
|
163
|
-
|
164
|
-
|
165
|
-
|
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)
|
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.
|
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-
|
12
|
+
date: 2022-05-25 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: asciidoctor
|