asciidoctor-pdf 2.2.0 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +30 -0
- data/README.adoc +1 -1
- data/data/themes/base-theme.yml +2 -2
- data/data/themes/default-theme.yml +7 -30
- data/lib/asciidoctor/pdf/converter.rb +92 -49
- data/lib/asciidoctor/pdf/ext/prawn/document/column_box.rb +4 -0
- data/lib/asciidoctor/pdf/ext/prawn/formatted_text/arranger.rb +11 -0
- data/lib/asciidoctor/pdf/ext/prawn/formatted_text/line_wrap.rb +42 -0
- data/lib/asciidoctor/pdf/ext/prawn.rb +1 -0
- data/lib/asciidoctor/pdf/formatted_text/transform.rb +5 -2
- data/lib/asciidoctor/pdf/sanitizer.rb +7 -3
- data/lib/asciidoctor/pdf/theme_loader.rb +10 -14
- data/lib/asciidoctor/pdf/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fa858819d28baa5f07261f05d2ac3694e571683f3c99b9cbf80b11bd7d79c4b3
|
4
|
+
data.tar.gz: 243f4f2a35fcd2d3a2d6c9449499c3097804865abe137058dd55975970fbded9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 64571af8d39650e26245fd746675c8af1b3b98ee0b8b6d2de705acaf93849bbc5ed51ae36543ac06105e102b79ed698a24676ca76d63c91213399bd029b58e85
|
7
|
+
data.tar.gz: f446904f582d0d423c9ea918a23bff541ec65be94e0be7b94a07e1e190a6e66ee4aeb2c7ad403c61773708feb30adb2949e2822a56f7f0c3bd1b291191225c48
|
data/CHANGELOG.adoc
CHANGED
@@ -5,6 +5,36 @@
|
|
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.3.0 (2022-08-16) - @mojavelinux
|
9
|
+
|
10
|
+
Enhancements::
|
11
|
+
|
12
|
+
* place footnotes directly below last block of content if `footnotes-margin-top` theme key is 0 (#2291)
|
13
|
+
* allow page / column break to be forced using `always` option (e.g., `[%always]`) (#2300)
|
14
|
+
* insert column break instead of page break in multi-column layout if `column` role is specified on page break macro (#2293)
|
15
|
+
* use relative font size for big and small roles (#2307)
|
16
|
+
* use default-for-print theme by default if media is `print` or `prepress` (#2306)
|
17
|
+
* support text alignment roles on all styled paragraphs
|
18
|
+
* support text alignment roles on verse block
|
19
|
+
|
20
|
+
Bug Fixes::
|
21
|
+
|
22
|
+
* only indent text that starts at left margin (i.e., when text align is left or justify) (#2298)
|
23
|
+
* apply text transform and formatting when checking height of heading for orphan prevention
|
24
|
+
* apply text transform and formatting when computing height of background for caption
|
25
|
+
* honor theme settings (`prose-margin-inner` and `prose-text-indent-inner`) for inner paragraphs in abstract
|
26
|
+
* prevent footnote label from being split across lines (#2297)
|
27
|
+
* keep footnote label with preceding text if adjacent (#2297)
|
28
|
+
* strip formatting added to source block by custom subs when syntax highlighter is enabled (#2086)
|
29
|
+
|
30
|
+
Compliance::
|
31
|
+
|
32
|
+
* remove support for deprecated `spread` role on table
|
33
|
+
|
34
|
+
=== Details
|
35
|
+
|
36
|
+
{url-repo}/releases/tag/v2.3.0[git tag] | {url-repo}/compare/v2.2.0\...v2.3.0[full diff]
|
37
|
+
|
8
38
|
== 2.2.0 (2022-07-22) - @mojavelinux
|
9
39
|
|
10
40
|
Enhancements::
|
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.
|
3
|
+
v2.3.0, 2022-08-16
|
4
4
|
// Settings:
|
5
5
|
:experimental:
|
6
6
|
:idprefix:
|
data/data/themes/base-theme.yml
CHANGED
@@ -17,8 +17,8 @@ base_border_color: '000000'
|
|
17
17
|
role_lead_font_size: 13.5
|
18
18
|
role_line-through_text_decoration: line-through
|
19
19
|
role_underline_text_decoration: underline
|
20
|
-
role_big_font_size:
|
21
|
-
role_small_font_size:
|
20
|
+
role_big_font_size: 1.2em
|
21
|
+
role_small_font_size: 0.8em
|
22
22
|
role_subtitle_font_size: 0.8em
|
23
23
|
button_content: '[%s]'
|
24
24
|
button_font_family: Courier
|
@@ -23,28 +23,14 @@ page:
|
|
23
23
|
size: A4
|
24
24
|
base:
|
25
25
|
text_align: justify
|
26
|
-
# color as hex string (leading # is optional)
|
27
26
|
font_color: 333333
|
28
|
-
# color as RGB array
|
29
|
-
#font_color: [51, 51, 51]
|
30
|
-
# color as CMYK array (approximated)
|
31
|
-
#font_color: [0, 0, 0, 0.92]
|
32
|
-
#font_color: [0, 0, 0, 92%]
|
33
27
|
font_family: Noto Serif
|
34
|
-
# choose one of these font_size/line_height_length combinations
|
35
|
-
#font_size: 14
|
36
|
-
#line_height_length: 20
|
37
|
-
#font_size: 11.25
|
38
|
-
#line_height_length: 18
|
39
|
-
#font_size: 11.2
|
40
|
-
#line_height_length: 16
|
41
28
|
font_size: 10.5
|
42
|
-
#line_height_length
|
43
|
-
# correct line height for Noto Serif metrics
|
29
|
+
# line_height_length is really just a vertical spacing variable; it's not actually the height of a line
|
44
30
|
line_height_length: 12
|
45
|
-
#
|
46
|
-
#
|
47
|
-
line_height: $base_line_height_length /
|
31
|
+
# The Noto font family has a built-in line height of 1.36
|
32
|
+
# With this line_height, a line of text will occupy a height of 15.78pt
|
33
|
+
line_height: $base_line_height_length / 10.5
|
48
34
|
font_size_large: round($base_font_size * 1.25)
|
49
35
|
font_size_small: round($base_font_size * 0.85)
|
50
36
|
font_size_min: $base_font_size * 0.75
|
@@ -60,16 +46,13 @@ role:
|
|
60
46
|
underline:
|
61
47
|
text_decoration: underline
|
62
48
|
big:
|
63
|
-
font_size:
|
49
|
+
font_size: 1.2em
|
64
50
|
small:
|
65
|
-
font_size:
|
51
|
+
font_size: 0.8em
|
66
52
|
subtitle:
|
67
53
|
font_color: 999999
|
68
54
|
font_size: 0.8em
|
69
55
|
font_style: normal_italic
|
70
|
-
# FIXME vertical_rhythm is weird; we should think in terms of ems
|
71
|
-
#vertical_rhythm: $base_line_height_length * 2 / 3
|
72
|
-
# correct line height for Noto Serif metrics (comes with built-in line height)
|
73
56
|
vertical_rhythm: $base_line_height_length
|
74
57
|
horizontal_rhythm: $base_line_height_length
|
75
58
|
link:
|
@@ -107,8 +90,7 @@ heading:
|
|
107
90
|
h4_font_size: $base_font_size_large
|
108
91
|
h5_font_size: $base_font_size
|
109
92
|
h6_font_size: $base_font_size_small
|
110
|
-
#
|
111
|
-
# correct line height for Noto Serif metrics (comes with built-in line height)
|
93
|
+
# rely on built-in line height in Noto
|
112
94
|
line_height: 1
|
113
95
|
margin_top: $vertical_rhythm * 0.4
|
114
96
|
margin_bottom: $vertical_rhythm * 0.9
|
@@ -156,11 +138,6 @@ admonition:
|
|
156
138
|
column_rule_color: $base_border_color
|
157
139
|
column_rule_width: $base_border_width
|
158
140
|
padding: [$vertical_rhythm / 3.0, $horizontal_rhythm, $vertical_rhythm / 3.0, $horizontal_rhythm]
|
159
|
-
#icon:
|
160
|
-
# tip:
|
161
|
-
# name: far-lightbulb
|
162
|
-
# stroke_color: 111111
|
163
|
-
# size: 24
|
164
141
|
label:
|
165
142
|
text_transform: uppercase
|
166
143
|
font_style: bold
|
@@ -46,8 +46,9 @@ module Asciidoctor
|
|
46
46
|
tip: { name: 'far-lightbulb', stroke_color: '111111', size: 24 },
|
47
47
|
warning: { name: 'fas-exclamation-triangle', stroke_color: 'BF6900', size: 24 },
|
48
48
|
}
|
49
|
-
TextAlignmentNames =
|
50
|
-
|
49
|
+
TextAlignmentNames = { 'justify' => true, 'left' => true, 'center' => true, 'right' => true }
|
50
|
+
IndentableTextAlignments = { justify: true, left: true }
|
51
|
+
TextAlignmentRoles = { 'text-justify' => true, 'text-left' => true, 'text-center' => true, 'text-right' => true }
|
51
52
|
TextDecorationStyleTable = { 'underline' => :underline, 'line-through' => :strikethrough }
|
52
53
|
FontKerningTable = { 'normal' => true, 'none' => false }
|
53
54
|
BlockFloatNames = %w(left right)
|
@@ -102,6 +103,7 @@ module Asciidoctor
|
|
102
103
|
'filled' => (?\u2776..?\u277f).to_a + (?\u24eb..?\u24f4).to_a,
|
103
104
|
}
|
104
105
|
TypographicQuotes = %w(“ ” ‘ ’)
|
106
|
+
InlineFormatSniffRx = /[<&]/
|
105
107
|
SimpleAttributeRefRx = /(?<!\\)\{\w+(?:-\w+)*\}/
|
106
108
|
MeasurementRxt = '\\d+(?:\\.\\d+)?(?:in|cm|mm|p[txc])?'
|
107
109
|
MeasurementPartsRx = /^(\d+(?:\.\d+)?)(in|mm|cm|p[txc])?$/
|
@@ -394,7 +396,7 @@ module Asciidoctor
|
|
394
396
|
@font_scale = 1
|
395
397
|
@font_color = theme.base_font_color
|
396
398
|
@text_decoration_width = theme.base_text_decoration_width
|
397
|
-
@base_text_align = (text_align = doc.attr 'text-align') &&
|
399
|
+
@base_text_align = (text_align = doc.attr 'text-align') && TextAlignmentNames[text_align] ? text_align : theme.base_text_align
|
398
400
|
@base_line_height = theme.base_line_height
|
399
401
|
@cjk_line_breaks = doc.attr? 'scripts', 'cjk'
|
400
402
|
if (hyphen_lang = (doc.attr 'hyphens') || ((doc.attr_unspecified? 'hyphens') ? @theme.base_hyphens : nil)) &&
|
@@ -533,8 +535,10 @@ module Asciidoctor
|
|
533
535
|
elsif (theme_name = doc.attr 'pdf-theme')
|
534
536
|
theme = ThemeLoader.load_theme theme_name, (user_themesdir = (doc.attr 'pdf-themesdir')&.sub '{docdir}', (doc.attr 'docdir'))
|
535
537
|
@themesdir = theme.__dir__
|
536
|
-
|
538
|
+
elsif (doc.attr 'media', 'screen') == 'screen'
|
537
539
|
@themesdir = (theme = ThemeLoader.load_theme).__dir__
|
540
|
+
else
|
541
|
+
@themesdir = (theme = ThemeLoader.load_theme 'default-for-print').__dir__
|
538
542
|
end
|
539
543
|
prepare_theme theme
|
540
544
|
rescue
|
@@ -577,6 +581,7 @@ module Asciidoctor
|
|
577
581
|
theme.code_linenum_font_color ||= '999999'
|
578
582
|
theme.callout_list_margin_top_after_code ||= 0
|
579
583
|
theme.role_unresolved_font_color ||= 'FF0000'
|
584
|
+
theme.footnotes_margin_top ||= 'auto'
|
580
585
|
theme.footnotes_item_spacing ||= 0
|
581
586
|
theme.index_columns ||= 2
|
582
587
|
theme.index_column_gap ||= theme.base_font_size
|
@@ -741,22 +746,16 @@ module Asciidoctor
|
|
741
746
|
pagenums = consolidate_ranges term.dests.map {|dest| dest[:page] }.uniq
|
742
747
|
end
|
743
748
|
pagenums.each do |pagenum|
|
744
|
-
|
745
|
-
|
746
|
-
if ::String === pagenum
|
747
|
-
term_fragments[-1] = prev_fragment.merge text: %(#{prev_fragment[:text]}, #{pagenum})
|
748
|
-
next
|
749
|
-
else
|
750
|
-
term_fragments[-1] = prev_fragment.merge text: %(#{prev_fragment[:text]}, )
|
751
|
-
end
|
749
|
+
if ::String === pagenum
|
750
|
+
term_fragments << ({ text: %(, #{pagenum}) })
|
752
751
|
else
|
753
|
-
term_fragments <<
|
752
|
+
term_fragments << { text: ', ' }
|
753
|
+
term_fragments << pagenum
|
754
754
|
end
|
755
|
-
term_fragments << (::String === pagenum ? { text: pagenum } : pagenum)
|
756
755
|
end
|
757
756
|
end
|
758
757
|
subterm_indent = @theme.description_list_description_indent
|
759
|
-
typeset_formatted_text term_fragments, (calc_line_metrics @base_line_height), align: :left, color: @font_color, hanging_indent: subterm_indent * 2
|
758
|
+
typeset_formatted_text term_fragments, (calc_line_metrics @base_line_height), align: :left, color: @font_color, hanging_indent: subterm_indent * 2, consolidate: true
|
760
759
|
indent subterm_indent do
|
761
760
|
term.subterms.each do |subterm|
|
762
761
|
convert_index_term subterm, pagenum_sequence_style
|
@@ -782,10 +781,7 @@ module Asciidoctor
|
|
782
781
|
ink_prose node.title, align: (@theme.abstract_title_text_align || @base_text_align).to_sym, margin_top: @theme.heading_margin_top, margin_bottom: @theme.heading_margin_bottom, line_height: (@theme.heading_line_height || @theme.base_line_height)
|
783
782
|
end if node.title?
|
784
783
|
theme_font :abstract do
|
785
|
-
prose_opts = { align: (@theme.abstract_text_align || @base_text_align).to_sym, hyphenate: true }
|
786
|
-
if (text_indent = @theme.prose_text_indent) > 0
|
787
|
-
prose_opts[:indent_paragraphs] = text_indent
|
788
|
-
end
|
784
|
+
prose_opts = { align: (@theme.abstract_text_align || @base_text_align).to_sym, hyphenate: true, margin_bottom: 0 }
|
789
785
|
# FIXME: allow theme to control more first line options
|
790
786
|
if (line1_font_style = @theme.abstract_first_line_font_style&.to_sym) && line1_font_style != font_style
|
791
787
|
case line1_font_style
|
@@ -806,14 +802,11 @@ module Asciidoctor
|
|
806
802
|
prose_opts[:first_line_options] = first_line_options if first_line_options
|
807
803
|
# FIXME: make this cleaner!!
|
808
804
|
if node.blocks?
|
809
|
-
last_block = node.last_child
|
810
805
|
node.blocks.each do |child|
|
811
806
|
if child.context == :paragraph
|
812
807
|
child.document.playback_attributes child.attributes
|
813
|
-
|
814
|
-
ink_prose child.content, ((text_align = resolve_text_align_from_role child.roles) ? (prose_opts.merge align: text_align) : prose_opts.dup)
|
808
|
+
convert_paragraph child, prose_opts.dup
|
815
809
|
prose_opts.delete :first_line_options
|
816
|
-
prose_opts.delete :margin_bottom
|
817
810
|
else
|
818
811
|
# FIXME: this could do strange things if the wrong kind of content shows up
|
819
812
|
child.convert
|
@@ -823,7 +816,10 @@ module Asciidoctor
|
|
823
816
|
if (text_align = resolve_text_align_from_role node.roles)
|
824
817
|
prose_opts[:align] = text_align
|
825
818
|
end
|
826
|
-
|
819
|
+
if IndentableTextAlignments[prose_opts[:align]] && (text_indent = @theme.prose_text_indent) > 0
|
820
|
+
prose_opts[:indent_paragraphs] = text_indent
|
821
|
+
end
|
822
|
+
ink_prose string, prose_opts
|
827
823
|
end
|
828
824
|
end
|
829
825
|
end
|
@@ -832,16 +828,19 @@ module Asciidoctor
|
|
832
828
|
theme_margin :block, :bottom, (next_enclosed_block node)
|
833
829
|
end
|
834
830
|
|
835
|
-
def convert_paragraph node
|
831
|
+
def convert_paragraph node, opts = nil
|
836
832
|
add_dest_for_block node if node.id
|
837
833
|
|
838
|
-
prose_opts = { margin_bottom: 0, hyphenate: true }
|
834
|
+
prose_opts = opts || { margin_bottom: 0, hyphenate: true }
|
839
835
|
if (text_align = resolve_text_align_from_role (roles = node.roles), query_theme: true, remove_predefined: true)
|
840
836
|
prose_opts[:align] = text_align
|
837
|
+
else
|
838
|
+
text_align = @base_text_align.to_sym
|
841
839
|
end
|
842
840
|
role_keys = roles.map {|role| %(role_#{role}) } unless roles.empty?
|
843
|
-
if
|
844
|
-
((text_indent = @theme.
|
841
|
+
if IndentableTextAlignments[text_align] &&
|
842
|
+
((text_indent = @theme.prose_text_indent) > 0 ||
|
843
|
+
((text_indent = @theme.prose_text_indent_inner) > 0 && node.previous_sibling&.context == :paragraph))
|
845
844
|
prose_opts[:indent_paragraphs] = text_indent
|
846
845
|
end
|
847
846
|
if (bottom_gutter = @bottom_gutters[-1][node])
|
@@ -1046,7 +1045,7 @@ module Asciidoctor
|
|
1046
1045
|
else
|
1047
1046
|
highlighter = nil
|
1048
1047
|
end
|
1049
|
-
|
1048
|
+
saved_subs = (subs = node.subs).dup
|
1050
1049
|
callouts_enabled = subs.include? :callouts
|
1051
1050
|
highlight_idx = subs.index :highlight
|
1052
1051
|
# NOTE: scratch? here only applies if listing block is nested inside another block
|
@@ -1056,16 +1055,33 @@ module Asciidoctor
|
|
1056
1055
|
# switch the :highlight sub back to :specialcharacters
|
1057
1056
|
subs[highlight_idx] = :specialcharacters
|
1058
1057
|
else
|
1059
|
-
|
1058
|
+
saved_subs = nil
|
1060
1059
|
end
|
1061
1060
|
source_string = guard_indentation node.content
|
1062
1061
|
elsif highlight_idx
|
1063
1062
|
# NOTE: the source highlighter logic below handles the highlight and callouts subs
|
1064
|
-
|
1065
|
-
|
1066
|
-
|
1063
|
+
if (subs - [:highlight, :callouts]).empty?
|
1064
|
+
subs.clear
|
1065
|
+
# NOTE: indentation guards will be added by the source highlighter logic
|
1066
|
+
source_string = expand_tabs node.content
|
1067
|
+
else
|
1068
|
+
if callouts_enabled
|
1069
|
+
saved_lines = node.lines.dup
|
1070
|
+
subs.delete :callouts
|
1071
|
+
prev_subs = subs.dup
|
1072
|
+
subs.clear
|
1073
|
+
source_string, conum_mapping = extract_conums node.content
|
1074
|
+
node.lines.replace (source_string.split LF)
|
1075
|
+
subs.replace prev_subs
|
1076
|
+
callouts_enabled = false
|
1077
|
+
end
|
1078
|
+
subs[highlight_idx] = :specialcharacters
|
1079
|
+
# NOTE: indentation guards will be added by the source highlighter logic
|
1080
|
+
source_string = expand_tabs unescape_xml (sanitize node.content, compact: false)
|
1081
|
+
node.lines.replace saved_lines if saved_lines
|
1082
|
+
end
|
1067
1083
|
else
|
1068
|
-
highlighter =
|
1084
|
+
highlighter = saved_subs = nil
|
1069
1085
|
source_string = guard_indentation node.content
|
1070
1086
|
end
|
1071
1087
|
else
|
@@ -1157,7 +1173,7 @@ module Asciidoctor
|
|
1157
1173
|
# NOTE: only format if we detect a need (callouts or inline formatting)
|
1158
1174
|
source_chunks = (XMLMarkupRx.match? source_string) ? (text_formatter.format source_string) : [text: source_string]
|
1159
1175
|
end
|
1160
|
-
node.subs.replace
|
1176
|
+
node.subs.replace saved_subs if saved_subs
|
1161
1177
|
adjusted_font_size = ((node.option? 'autofit') || (node.document.attr? 'autofit-option')) ? (compute_autofit_font_size source_chunks, :code) : nil
|
1162
1178
|
end
|
1163
1179
|
|
@@ -1281,7 +1297,7 @@ module Asciidoctor
|
|
1281
1297
|
content = guard_indentation node.content
|
1282
1298
|
ink_prose content,
|
1283
1299
|
normalize: false,
|
1284
|
-
align: :left,
|
1300
|
+
align: (resolve_text_align_from_role node.roles) || :left,
|
1285
1301
|
hyphenate: true,
|
1286
1302
|
margin_bottom: 0,
|
1287
1303
|
bottom_gutter: (attribution ? nil : @bottom_gutters[-1][node])
|
@@ -1682,7 +1698,7 @@ module Asciidoctor
|
|
1682
1698
|
alignment = float_to.to_sym
|
1683
1699
|
elsif (alignment = node.attr 'align')
|
1684
1700
|
alignment = (BlockAlignmentNames.include? alignment) ? alignment.to_sym : :left
|
1685
|
-
elsif !(alignment = node.roles.reverse.find {|
|
1701
|
+
elsif !(alignment = node.roles.reverse.find {|role| BlockAlignmentNames.include? role }&.to_sym)
|
1686
1702
|
alignment = @theme.image_align&.to_sym || :left
|
1687
1703
|
end
|
1688
1704
|
end
|
@@ -1916,14 +1932,16 @@ module Asciidoctor
|
|
1916
1932
|
page_layout = nil
|
1917
1933
|
end
|
1918
1934
|
|
1919
|
-
if at_page_top?
|
1935
|
+
if at_page_top? && !(node.option? 'always')
|
1920
1936
|
if page_layout && page_layout != page.layout && page.empty?
|
1921
1937
|
delete_current_page
|
1922
1938
|
advance_page layout: page_layout, margin: @page_margin[page_layout][page_side nil, @folio_placement[:inverted]]
|
1923
1939
|
end
|
1924
1940
|
elsif page_layout
|
1941
|
+
bounds.current_column = bounds.last_column if ColumnBox === bounds && !(node.has_role? 'column')
|
1925
1942
|
advance_page layout: page_layout, margin: @page_margin[page_layout][page_side nil, @folio_placement[:inverted]]
|
1926
1943
|
else
|
1944
|
+
bounds.current_column = bounds.last_column if ColumnBox === bounds && !(node.has_role? 'column')
|
1927
1945
|
advance_page
|
1928
1946
|
end
|
1929
1947
|
end
|
@@ -2196,7 +2214,7 @@ module Asciidoctor
|
|
2196
2214
|
|
2197
2215
|
if node.option? 'autowidth'
|
2198
2216
|
table_width = (node.attr? 'width') ? bounds.width * ((node.attr 'tablepcwidth') / 100.0) :
|
2199
|
-
(((node.has_role? 'stretch')
|
2217
|
+
(((node.has_role? 'stretch')) ? bounds.width : nil)
|
2200
2218
|
column_widths = []
|
2201
2219
|
else
|
2202
2220
|
table_width = bounds.width * ((node.attr 'tablepcwidth') / 100.0)
|
@@ -2362,6 +2380,9 @@ module Asciidoctor
|
|
2362
2380
|
node.content
|
2363
2381
|
elsif node.content_model != :compound && (string = node.content)
|
2364
2382
|
prose_opts = opts.merge hyphenate: true, margin_bottom: 0
|
2383
|
+
if (text_align = resolve_text_align_from_role node.roles)
|
2384
|
+
prose_opts[:align] = text_align
|
2385
|
+
end
|
2365
2386
|
if (bottom_gutter = @bottom_gutters[-1][node])
|
2366
2387
|
prose_opts[:bottom_gutter] = bottom_gutter
|
2367
2388
|
end
|
@@ -2477,9 +2498,9 @@ module Asciidoctor
|
|
2477
2498
|
else
|
2478
2499
|
label = index
|
2479
2500
|
end
|
2480
|
-
%(
|
2501
|
+
%(<sup class="wj">#{anchor}[<a anchor="_footnotedef_#{index}">#{label}</a>]</sup>)
|
2481
2502
|
elsif node.type == :xref
|
2482
|
-
%(<sup><font color="#{theme.role_unresolved_font_color}">[#{node.text}]</font></sup>)
|
2503
|
+
%(<sup class="wj"><font color="#{theme.role_unresolved_font_color}">[#{node.text}]</font></sup>)
|
2483
2504
|
else
|
2484
2505
|
log :warn, %(unknown footnote type: #{node.type.inspect})
|
2485
2506
|
nil
|
@@ -2829,8 +2850,7 @@ module Asciidoctor
|
|
2829
2850
|
theme_font :heading, level: (hlevel = opts[:level]) do
|
2830
2851
|
h_padding_t, h_padding_r, h_padding_b, h_padding_l = expand_padding_value @theme[%(heading_h#{hlevel}_padding)]
|
2831
2852
|
h_fits = indent h_padding_l, h_padding_r do
|
2832
|
-
|
2833
|
-
heading_h = (height_of_typeset_text title) +
|
2853
|
+
heading_h = (height_of_typeset_text title, inline_format: true, text_transform: @text_transform) +
|
2834
2854
|
(@theme[%(heading_h#{hlevel}_margin_top)] || @theme.heading_margin_top) +
|
2835
2855
|
(@theme[%(heading_h#{hlevel}_margin_bottom)] || @theme.heading_margin_bottom) + h_padding_t + h_padding_b
|
2836
2856
|
heading_h += min_height_after if min_height_after && (node.context == :section ? node.blocks? : !node.last_child?)
|
@@ -3002,8 +3022,16 @@ module Asciidoctor
|
|
3002
3022
|
end
|
3003
3023
|
|
3004
3024
|
def height_of_typeset_text string, opts = {}
|
3025
|
+
if (transform = opts[:text_transform])
|
3026
|
+
string = transform_text string, transform
|
3027
|
+
end
|
3028
|
+
if (inline_format = opts[:inline_format]) && (InlineFormatSniffRx.match? string)
|
3029
|
+
fragments = parse_text string, inline_format: inline_format
|
3030
|
+
else
|
3031
|
+
fragments = [{ text: string }]
|
3032
|
+
end
|
3005
3033
|
line_metrics = (calc_line_metrics opts[:line_height] || @base_line_height)
|
3006
|
-
(
|
3034
|
+
(height_of_formatted fragments, leading: line_metrics.leading, final_gap: line_metrics.final_gap) + line_metrics.padding_top + (opts[:single_line] ? 0 : line_metrics.padding_bottom)
|
3007
3035
|
end
|
3008
3036
|
|
3009
3037
|
# Render the caption in the current document. If the dry_run option is true, return the height.
|
@@ -3095,7 +3123,7 @@ module Asciidoctor
|
|
3095
3123
|
opts = opts.merge inherited
|
3096
3124
|
end
|
3097
3125
|
unless scratch? || !(bg_color = @theme[%(#{category_caption}_background_color)] || @theme.caption_background_color)
|
3098
|
-
caption_height = height_of_typeset_text string
|
3126
|
+
caption_height = height_of_typeset_text string, inline_format: true, text_transform: @text_transform
|
3099
3127
|
fill_at = [bounds.left, cursor]
|
3100
3128
|
fill_at[1] -= (margin[:top] || 0) unless at_page_top?
|
3101
3129
|
float { bounding_box(fill_at, width: container_width, height: caption_height) { fill_bounds bg_color } }
|
@@ -3156,9 +3184,9 @@ module Asciidoctor
|
|
3156
3184
|
def ink_footnotes node
|
3157
3185
|
return if (fns = (doc = node.document).footnotes - @rendered_footnotes).empty?
|
3158
3186
|
theme_margin :block, :bottom if node.context == :document || node == node.document.last_child
|
3159
|
-
theme_margin :footnotes, :top
|
3187
|
+
theme_margin :footnotes, :top unless (valign_bottom = @theme.footnotes_margin_top == 'auto')
|
3160
3188
|
with_dry_run do |extent|
|
3161
|
-
if (single_page_height = extent&.single_page_height) && (delta = cursor - single_page_height - 0.0001) > 0
|
3189
|
+
if valign_bottom && (single_page_height = extent&.single_page_height) && (delta = cursor - single_page_height - 0.0001) > 0
|
3162
3190
|
move_down delta
|
3163
3191
|
end
|
3164
3192
|
theme_font :footnotes do
|
@@ -4294,8 +4322,8 @@ module Asciidoctor
|
|
4294
4322
|
end
|
4295
4323
|
|
4296
4324
|
def resolve_text_align_from_role roles, query_theme: false, remove_predefined: false
|
4297
|
-
if (align_role = roles.reverse.find {|
|
4298
|
-
roles.replace roles - TextAlignmentRoles if remove_predefined
|
4325
|
+
if (align_role = roles.reverse.find {|role| TextAlignmentRoles[role] })
|
4326
|
+
roles.replace roles - TextAlignmentRoles.keys if remove_predefined
|
4299
4327
|
(align_role.slice 5, align_role.length).to_sym
|
4300
4328
|
elsif query_theme
|
4301
4329
|
roles.reverse.each do |role|
|
@@ -4537,6 +4565,7 @@ module Asciidoctor
|
|
4537
4565
|
# QUESTION: combine with typeset_text?
|
4538
4566
|
def typeset_formatted_text fragments, line_metrics, opts = {}
|
4539
4567
|
opts = { leading: line_metrics.leading, initial_gap: line_metrics.padding_top, final_gap: line_metrics.final_gap }.merge opts
|
4568
|
+
fragments = consolidate_fragments fragments if opts.delete :consolidate
|
4540
4569
|
if (hanging_indent = (opts.delete :hanging_indent) || 0) > 0
|
4541
4570
|
indent hanging_indent do
|
4542
4571
|
formatted_text fragments, (opts.merge indent_paragraphs: -hanging_indent)
|
@@ -4746,6 +4775,20 @@ module Asciidoctor
|
|
4746
4775
|
end
|
4747
4776
|
end
|
4748
4777
|
|
4778
|
+
def consolidate_fragments fragments
|
4779
|
+
return fragments unless fragments.size > 1
|
4780
|
+
accum = []
|
4781
|
+
prev_fragment = nil
|
4782
|
+
fragments.each do |fragment|
|
4783
|
+
if prev_fragment && fragment == (prev_fragment.merge text: (fragment_text = fragment[:text]))
|
4784
|
+
prev_fragment[:text] += fragment_text
|
4785
|
+
else
|
4786
|
+
accum << (prev_fragment = fragment)
|
4787
|
+
end
|
4788
|
+
end
|
4789
|
+
accum
|
4790
|
+
end
|
4791
|
+
|
4749
4792
|
def conum_glyph number
|
4750
4793
|
@conum_glyphs[number - 1]
|
4751
4794
|
end
|
@@ -26,6 +26,17 @@ Prawn::Text::Formatted::Arranger.prepend (Module.new do
|
|
26
26
|
(string = super) == @dummy_text ? (string.extend Prawn::Text::NoopLstripBang) : string
|
27
27
|
end
|
28
28
|
|
29
|
+
def preview_joined_string
|
30
|
+
if (next_unconsumed = @unconsumed[0] || {})[:wj] && !(@consumed[-1] || [])[:wj]
|
31
|
+
idx = 0
|
32
|
+
str = '' if (str = next_unconsumed[:text]) == @dummy_text
|
33
|
+
while (next_unconsumed = @unconsumed[idx += 1] || {})[:wj] && (next_string = next_unconsumed[:text])
|
34
|
+
str += next_string unless next_string == @dummy_text
|
35
|
+
end
|
36
|
+
str unless str == ''
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
29
40
|
def apply_font_size size, styles
|
30
41
|
if (subscript? styles) || (superscript? styles)
|
31
42
|
size ||= @document.font_size
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Prawn::Text::Formatted::LineWrap.prepend (Module.new do
|
4
|
+
def add_fragment_to_line fragment
|
5
|
+
case fragment
|
6
|
+
when ''
|
7
|
+
true
|
8
|
+
when ?\n
|
9
|
+
@newline_encountered = true
|
10
|
+
false
|
11
|
+
else
|
12
|
+
if (joined_string = @arranger.preview_joined_string)
|
13
|
+
joined_string_width = @document.width_of (tokenize joined_string)[0], kerning: @kerning
|
14
|
+
else
|
15
|
+
joined_string_width = 0
|
16
|
+
end
|
17
|
+
last_idx = (segments = tokenize fragment).length - 1
|
18
|
+
segments.each_with_index do |segment, idx|
|
19
|
+
if segment == (zero_width_space segment.encoding)
|
20
|
+
segment_width = effective_segment_width = 0
|
21
|
+
else
|
22
|
+
segment_width = effective_segment_width = @document.width_of segment, kerning: @kerning
|
23
|
+
effective_segment_width += joined_string_width if idx === last_idx
|
24
|
+
end
|
25
|
+
if @accumulated_width + effective_segment_width <= @width
|
26
|
+
@accumulated_width += segment_width
|
27
|
+
if segment[-1] == (shy = soft_hyphen segment.encoding)
|
28
|
+
@accumulated_width -= (@document.width_of shy, kerning: @kerning)
|
29
|
+
end
|
30
|
+
@fragment_output += segment
|
31
|
+
else
|
32
|
+
@line_contains_more_than_one_word = false if @accumulated_width == 0 && @line_contains_more_than_one_word
|
33
|
+
end_of_the_line_reached segment
|
34
|
+
fragment_finished fragment
|
35
|
+
return false
|
36
|
+
end
|
37
|
+
end
|
38
|
+
fragment_finished fragment
|
39
|
+
true
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end)
|
@@ -13,5 +13,6 @@ require_relative 'prawn/formatted_text/arranger'
|
|
13
13
|
require_relative 'prawn/formatted_text/box'
|
14
14
|
require_relative 'prawn/formatted_text/fragment'
|
15
15
|
require_relative 'prawn/formatted_text/indented_paragraph_wrap'
|
16
|
+
require_relative 'prawn/formatted_text/line_wrap'
|
16
17
|
require_relative 'prawn/formatted_text/protect_bottom_gutter'
|
17
18
|
require_relative 'prawn/extensions'
|
@@ -315,10 +315,12 @@ module Asciidoctor
|
|
315
315
|
end) : value
|
316
316
|
elsif (value = attrs[:id])
|
317
317
|
# NOTE: text is null character, which is used as placeholder text so Prawn doesn't drop fragment
|
318
|
-
|
318
|
+
new_fragment = { name: value, callback: [InlineDestinationMarker] }
|
319
|
+
new_fragment[:wj] = true if fragment[:wj]
|
319
320
|
if (type = attrs[:type])
|
320
|
-
|
321
|
+
new_fragment[:type] = type.to_sym
|
321
322
|
end
|
323
|
+
fragment = new_fragment
|
322
324
|
visible = nil
|
323
325
|
end
|
324
326
|
end
|
@@ -357,6 +359,7 @@ module Asciidoctor
|
|
357
359
|
end
|
358
360
|
# TODO: we could limit to select tags, but doesn't seem to really affect performance
|
359
361
|
attrs[:class].split.each do |class_name|
|
362
|
+
fragment[:wj] = true if class_name == 'wj'
|
360
363
|
next unless @theme_settings.key? class_name
|
361
364
|
update_fragment fragment, @theme_settings[class_name]
|
362
365
|
# NOTE: defer assignment of callback since we must look at combined styles of element and role
|
@@ -8,7 +8,7 @@ module Asciidoctor
|
|
8
8
|
'>' => '>',
|
9
9
|
'&' => '&',
|
10
10
|
}
|
11
|
-
XMLSpecialCharsRx =
|
11
|
+
XMLSpecialCharsRx = /&(?:[lg]t|amp);/
|
12
12
|
InverseXMLSpecialChars = XMLSpecialChars.invert
|
13
13
|
InverseXMLSpecialCharsRx = /[#{InverseXMLSpecialChars.keys.join}]/
|
14
14
|
(BuiltInNamedEntities = {
|
@@ -28,16 +28,20 @@ module Asciidoctor
|
|
28
28
|
#
|
29
29
|
# FIXME: move to a module so we can mix it in elsewhere
|
30
30
|
# FIXME: add option to control escaping entities, or a filter mechanism in general
|
31
|
-
def sanitize string
|
31
|
+
def sanitize string, compact: true
|
32
32
|
string = string.gsub SanitizeXMLRx, '' if string.include? '<'
|
33
33
|
string = string.gsub(CharRefRx) { $1 ? BuiltInNamedEntities[$1] : ([$2 ? $2.to_i : ($3.to_i 16)].pack 'U1') } if string.include? '&'
|
34
|
-
string.strip.tr_s ' ', ' '
|
34
|
+
compact ? (string.strip.tr_s ' ', ' ') : string
|
35
35
|
end
|
36
36
|
|
37
37
|
def escape_xml string
|
38
38
|
string.gsub InverseXMLSpecialCharsRx, InverseXMLSpecialChars
|
39
39
|
end
|
40
40
|
|
41
|
+
def unescape_xml string
|
42
|
+
string.gsub XMLSpecialCharsRx, XMLSpecialChars
|
43
|
+
end
|
44
|
+
|
41
45
|
def escape_amp string
|
42
46
|
string.gsub UnescapedAmpersandRx, '&'
|
43
47
|
end
|
@@ -160,7 +160,7 @@ module Asciidoctor
|
|
160
160
|
data[key] = {}.tap do |accum|
|
161
161
|
val.each do |key2, val2|
|
162
162
|
key2 = key2.tr '-', '_' if key2.include? '-'
|
163
|
-
accum[key2.to_sym] = (key2.end_with? '_color') ? (to_color evaluate val2, data) : (evaluate val2, data)
|
163
|
+
accum[key2.to_sym] = (key2.end_with? '_color') ? (to_color evaluate val2, data, math: false) : (evaluate val2, data)
|
164
164
|
end
|
165
165
|
end if val
|
166
166
|
elsif ::Hash === val
|
@@ -173,20 +173,18 @@ module Asciidoctor
|
|
173
173
|
end
|
174
174
|
elsif (rekey = DeprecatedKeys[key]) ||
|
175
175
|
((key.start_with? 'role_') && (key.end_with? '_align') && (rekey = key.sub RoleAlignKeyRx, '_text_align'))
|
176
|
-
data[rekey] = evaluate val, data
|
176
|
+
data[rekey] = evaluate val, data, math: false
|
177
177
|
elsif PaddingBottomHackKeys.include? key
|
178
178
|
val = evaluate val, data
|
179
179
|
# normalize padding hacks for themes designed before the converter had smart margins
|
180
180
|
val[2] = val[0] if ::Array === val && val[0].to_f >= 0 && val[2].to_f <= 0
|
181
181
|
data[key] = val
|
182
|
-
# QUESTION: do we really need to evaluate_math in this case?
|
183
182
|
elsif key.end_with? '_color'
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
data[key] = val.map {|it| to_color evaluate it, data }
|
183
|
+
# assume table_grid_color is a single color unless the value is a 2-element array for backwards compatibility
|
184
|
+
if key == 'table_border_color' ? ::Array === val : (key == 'table_grid_color' && ::Array === val && val.size == 2)
|
185
|
+
data[key] = val.map {|it| to_color evaluate it, data, math: false }
|
188
186
|
else
|
189
|
-
data[key] = to_color evaluate val, data
|
187
|
+
data[key] = to_color evaluate val, data, math: false
|
190
188
|
end
|
191
189
|
elsif key.end_with? '_content'
|
192
190
|
data[key] = (expand_vars val.to_s, data).to_s
|
@@ -196,12 +194,12 @@ module Asciidoctor
|
|
196
194
|
data
|
197
195
|
end
|
198
196
|
|
199
|
-
def evaluate expr, vars
|
197
|
+
def evaluate expr, vars, math: true
|
200
198
|
case expr
|
201
199
|
when ::String
|
202
|
-
evaluate_math expand_vars expr, vars
|
200
|
+
math ? (evaluate_math expand_vars expr, vars) : (expand_vars expr, vars)
|
203
201
|
when ::Array
|
204
|
-
expr.map {|e| evaluate e, vars }
|
202
|
+
expr.map {|e| evaluate e, vars, math: math }
|
205
203
|
else
|
206
204
|
expr
|
207
205
|
end
|
@@ -233,7 +231,6 @@ module Asciidoctor
|
|
233
231
|
def evaluate_math expr
|
234
232
|
return expr if !(::String === expr) || ColorValue === expr
|
235
233
|
# resolve measurement values (e.g., 0.5in => 36)
|
236
|
-
# QUESTION: should we round the value? perhaps leave that to the precision functions
|
237
234
|
# NOTE: leave % as a string; handled by converter for now
|
238
235
|
original, expr = expr, (resolve_measurement_values expr)
|
239
236
|
while true
|
@@ -257,8 +254,7 @@ module Asciidoctor
|
|
257
254
|
end
|
258
255
|
end
|
259
256
|
if (expr.end_with? ')') && expr =~ PrecisionFuncRx
|
260
|
-
op = $1
|
261
|
-
offset = op.length + 1
|
257
|
+
offset = (op = $1).length + 1
|
262
258
|
expr = expr[offset...-1].to_f.send op.to_sym
|
263
259
|
end
|
264
260
|
if expr == original
|
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.
|
4
|
+
version: 2.3.0
|
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-
|
12
|
+
date: 2022-08-16 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: asciidoctor
|
@@ -280,6 +280,7 @@ files:
|
|
280
280
|
- lib/asciidoctor/pdf/ext/prawn/formatted_text/box.rb
|
281
281
|
- lib/asciidoctor/pdf/ext/prawn/formatted_text/fragment.rb
|
282
282
|
- lib/asciidoctor/pdf/ext/prawn/formatted_text/indented_paragraph_wrap.rb
|
283
|
+
- lib/asciidoctor/pdf/ext/prawn/formatted_text/line_wrap.rb
|
283
284
|
- lib/asciidoctor/pdf/ext/prawn/formatted_text/protect_bottom_gutter.rb
|
284
285
|
- lib/asciidoctor/pdf/ext/prawn/images.rb
|
285
286
|
- lib/asciidoctor/pdf/ext/pygments.rb
|