asciidoctor-pdf 2.2.0 → 2.3.0
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 +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
|