asciidoctor-pdf 2.2.0 → 2.3.1
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 +51 -0
- data/README.adoc +4 -4
- data/asciidoctor-pdf.gemspec +1 -1
- data/data/themes/base-theme.yml +2 -2
- data/data/themes/default-theme.yml +7 -30
- data/lib/asciidoctor/pdf/converter.rb +102 -59
- data/lib/asciidoctor/pdf/ext/asciidoctor/section.rb +7 -4
- data/lib/asciidoctor/pdf/ext/prawn/document/column_box.rb +4 -0
- data/lib/asciidoctor/pdf/ext/prawn/extensions.rb +6 -15
- 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 +9 -4
- data/lib/asciidoctor/pdf/optimizer.rb +1 -0
- data/lib/asciidoctor/pdf/sanitizer.rb +7 -3
- data/lib/asciidoctor/pdf/theme_loader.rb +11 -14
- data/lib/asciidoctor/pdf/version.rb +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9d88e98bb4e7e4afe22f1911c28813edbfe65af757b2cfb5899c754aa811ac12
|
|
4
|
+
data.tar.gz: 931e8ad9378ec457be0566cae2a43d5dddce06d5f821251bf842fc429b689a08
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2b530895e85531f879ad2bf1b8672281e0bc4af53cba666817d5d01349022fc0beaffe758b9def65e82db79f7e86c6bed9fcb70688e65ada7ecb993b5597f748
|
|
7
|
+
data.tar.gz: d97b859cccfb0ff54e65f4ccae7fc4a604c3ccb33a2727d08e2a523896ec045777999397ee506959173b404d13d2b253456943ef93b390f192ed831f4418a170
|
data/CHANGELOG.adoc
CHANGED
|
@@ -5,6 +5,57 @@
|
|
|
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.1 (2022-09-17) - @mojavelinux
|
|
9
|
+
|
|
10
|
+
Improvements::
|
|
11
|
+
|
|
12
|
+
* provide a fallback value for `base-font-size` when loading theme (#2343)
|
|
13
|
+
|
|
14
|
+
Bug Fixes::
|
|
15
|
+
|
|
16
|
+
* fix crash when smallcaps text transform is applied to a phrase (#2339)
|
|
17
|
+
* don't add chapter signifier if `chapter-signifier` is unset (#2328)
|
|
18
|
+
* don't add part signifier if `part-signifier` is unset (#2328)
|
|
19
|
+
* don't include bottom margin when computing heading height if `heading-min-height-after` theme key is empty (#2326)
|
|
20
|
+
* draw border on heading (section title or discrete heading) if it is advanced to next page (#2322)
|
|
21
|
+
* arrange heading even if section is empty
|
|
22
|
+
* ensure `heading-min-height-after` theme key is ignored if section is empty or discrete heading is last child
|
|
23
|
+
* don't force justify first line of abstract if it ends with a hard break
|
|
24
|
+
|
|
25
|
+
=== Details
|
|
26
|
+
|
|
27
|
+
{url-repo}/releases/tag/v2.3.1[git tag] | {url-repo}/compare/v2.3.0\...v2.3.1[full diff]
|
|
28
|
+
|
|
29
|
+
== 2.3.0 (2022-08-16) - @mojavelinux
|
|
30
|
+
|
|
31
|
+
Enhancements::
|
|
32
|
+
|
|
33
|
+
* place footnotes directly below last block of content if `footnotes-margin-top` theme key is 0 (#2291)
|
|
34
|
+
* allow page / column break to be forced using `always` option (e.g., `[%always]`) (#2300)
|
|
35
|
+
* insert column break instead of page break in multi-column layout if `column` role is specified on page break macro (#2293)
|
|
36
|
+
* use relative font size for big and small roles (#2307)
|
|
37
|
+
* use default-for-print theme by default if media is `print` or `prepress` (#2306)
|
|
38
|
+
* support text alignment roles on all styled paragraphs
|
|
39
|
+
* support text alignment roles on verse block
|
|
40
|
+
|
|
41
|
+
Bug Fixes::
|
|
42
|
+
|
|
43
|
+
* only indent text that starts at left margin (i.e., when text align is left or justify) (#2298)
|
|
44
|
+
* apply text transform and formatting when checking height of heading for orphan prevention
|
|
45
|
+
* apply text transform and formatting when computing height of background for caption
|
|
46
|
+
* honor theme settings (`prose-margin-inner` and `prose-text-indent-inner`) for inner paragraphs in abstract
|
|
47
|
+
* prevent footnote label from being split across lines (#2297)
|
|
48
|
+
* keep footnote label with preceding text if adjacent (#2297)
|
|
49
|
+
* strip formatting added to source block by custom subs when syntax highlighter is enabled (#2086)
|
|
50
|
+
|
|
51
|
+
Compliance::
|
|
52
|
+
|
|
53
|
+
* remove support for deprecated `spread` role on table
|
|
54
|
+
|
|
55
|
+
=== Details
|
|
56
|
+
|
|
57
|
+
{url-repo}/releases/tag/v2.3.0[git tag] | {url-repo}/compare/v2.2.0\...v2.3.0[full diff]
|
|
58
|
+
|
|
8
59
|
== 2.2.0 (2022-07-22) - @mojavelinux
|
|
9
60
|
|
|
10
61
|
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.1, 2022-09-17
|
|
4
4
|
// Settings:
|
|
5
5
|
:experimental:
|
|
6
6
|
:idprefix:
|
|
@@ -37,9 +37,9 @@ image:{url-project-repo}/workflows/CI/badge.svg[Build Status (GitHub Actions),li
|
|
|
37
37
|
image:https://img.shields.io/gem/v/asciidoctor-pdf.svg[Latest Release, link={url-gem}]
|
|
38
38
|
endif::[]
|
|
39
39
|
|
|
40
|
-
Asciidoctor PDF is a native PDF converter for AsciiDoc that
|
|
41
|
-
It bypasses the
|
|
42
|
-
Instead, you
|
|
40
|
+
Asciidoctor PDF is a native PDF converter for AsciiDoc that serves the `pdf` backend.
|
|
41
|
+
It bypasses the step of generating an intermediary format such as DocBook, Apache FO, or LaTeX in order to produce PDF.
|
|
42
|
+
Instead, you use Asciidoctor PDF to convert your documents directly from AsciiDoc to PDF with Asciidoctor.
|
|
43
43
|
The aim of this library is to take the pain out of creating PDF documents from AsciiDoc.
|
|
44
44
|
|
|
45
45
|
[NOTE]
|
data/asciidoctor-pdf.gemspec
CHANGED
|
@@ -8,7 +8,7 @@ Gem::Specification.new do |s|
|
|
|
8
8
|
s.name = 'asciidoctor-pdf'
|
|
9
9
|
s.version = Asciidoctor::PDF::VERSION
|
|
10
10
|
s.summary = 'Converts AsciiDoc documents to PDF using Asciidoctor and Prawn'
|
|
11
|
-
s.description = 'An
|
|
11
|
+
s.description = 'An add-on converter for Asciidoctor that converts AsciiDoc documents to PDF using the Prawn PDF generation library.'
|
|
12
12
|
s.authors = ['Dan Allen', 'Sarah White']
|
|
13
13
|
s.email = 'dan@opendevise.com'
|
|
14
14
|
s.homepage = 'https://asciidoctor.org/docs/asciidoctor-pdf'
|
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
|
|
@@ -625,7 +630,6 @@ module Asciidoctor
|
|
|
625
630
|
sect.remove
|
|
626
631
|
return
|
|
627
632
|
end
|
|
628
|
-
|
|
629
633
|
title = sect.numbered_title formal: true
|
|
630
634
|
sep = (sect.attr 'separator') || (sect.document.attr 'title-separator') || ''
|
|
631
635
|
if !sep.empty? && (title.include? (sep = %(#{sep} )))
|
|
@@ -650,7 +654,7 @@ module Asciidoctor
|
|
|
650
654
|
start_new_chapter sect
|
|
651
655
|
end
|
|
652
656
|
end
|
|
653
|
-
arrange_heading sect, title, hopts unless hidden || started_new || at_page_top?
|
|
657
|
+
arrange_heading sect, title, hopts unless hidden || started_new || at_page_top?
|
|
654
658
|
# QUESTION: should we store pdf-page-start, pdf-anchor & pdf-destination in internal map?
|
|
655
659
|
sect.set_attr 'pdf-page-start', (start_pgnum = page_number)
|
|
656
660
|
# QUESTION: should we just assign the section this generated id?
|
|
@@ -741,22 +745,16 @@ module Asciidoctor
|
|
|
741
745
|
pagenums = consolidate_ranges term.dests.map {|dest| dest[:page] }.uniq
|
|
742
746
|
end
|
|
743
747
|
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
|
|
748
|
+
if ::String === pagenum
|
|
749
|
+
term_fragments << ({ text: %(, #{pagenum}) })
|
|
752
750
|
else
|
|
753
|
-
term_fragments <<
|
|
751
|
+
term_fragments << { text: ', ' }
|
|
752
|
+
term_fragments << pagenum
|
|
754
753
|
end
|
|
755
|
-
term_fragments << (::String === pagenum ? { text: pagenum } : pagenum)
|
|
756
754
|
end
|
|
757
755
|
end
|
|
758
756
|
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
|
|
757
|
+
typeset_formatted_text term_fragments, (calc_line_metrics @base_line_height), align: :left, color: @font_color, hanging_indent: subterm_indent * 2, consolidate: true
|
|
760
758
|
indent subterm_indent do
|
|
761
759
|
term.subterms.each do |subterm|
|
|
762
760
|
convert_index_term subterm, pagenum_sequence_style
|
|
@@ -782,10 +780,7 @@ module Asciidoctor
|
|
|
782
780
|
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
781
|
end if node.title?
|
|
784
782
|
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
|
|
783
|
+
prose_opts = { align: (@theme.abstract_text_align || @base_text_align).to_sym, hyphenate: true, margin_bottom: 0 }
|
|
789
784
|
# FIXME: allow theme to control more first line options
|
|
790
785
|
if (line1_font_style = @theme.abstract_first_line_font_style&.to_sym) && line1_font_style != font_style
|
|
791
786
|
case line1_font_style
|
|
@@ -806,14 +801,11 @@ module Asciidoctor
|
|
|
806
801
|
prose_opts[:first_line_options] = first_line_options if first_line_options
|
|
807
802
|
# FIXME: make this cleaner!!
|
|
808
803
|
if node.blocks?
|
|
809
|
-
last_block = node.last_child
|
|
810
804
|
node.blocks.each do |child|
|
|
811
805
|
if child.context == :paragraph
|
|
812
806
|
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)
|
|
807
|
+
convert_paragraph child, prose_opts.dup
|
|
815
808
|
prose_opts.delete :first_line_options
|
|
816
|
-
prose_opts.delete :margin_bottom
|
|
817
809
|
else
|
|
818
810
|
# FIXME: this could do strange things if the wrong kind of content shows up
|
|
819
811
|
child.convert
|
|
@@ -823,7 +815,10 @@ module Asciidoctor
|
|
|
823
815
|
if (text_align = resolve_text_align_from_role node.roles)
|
|
824
816
|
prose_opts[:align] = text_align
|
|
825
817
|
end
|
|
826
|
-
|
|
818
|
+
if IndentableTextAlignments[prose_opts[:align]] && (text_indent = @theme.prose_text_indent) > 0
|
|
819
|
+
prose_opts[:indent_paragraphs] = text_indent
|
|
820
|
+
end
|
|
821
|
+
ink_prose string, prose_opts
|
|
827
822
|
end
|
|
828
823
|
end
|
|
829
824
|
end
|
|
@@ -832,16 +827,19 @@ module Asciidoctor
|
|
|
832
827
|
theme_margin :block, :bottom, (next_enclosed_block node)
|
|
833
828
|
end
|
|
834
829
|
|
|
835
|
-
def convert_paragraph node
|
|
830
|
+
def convert_paragraph node, opts = nil
|
|
836
831
|
add_dest_for_block node if node.id
|
|
837
832
|
|
|
838
|
-
prose_opts = { margin_bottom: 0, hyphenate: true }
|
|
833
|
+
prose_opts = opts || { margin_bottom: 0, hyphenate: true }
|
|
839
834
|
if (text_align = resolve_text_align_from_role (roles = node.roles), query_theme: true, remove_predefined: true)
|
|
840
835
|
prose_opts[:align] = text_align
|
|
836
|
+
else
|
|
837
|
+
text_align = @base_text_align.to_sym
|
|
841
838
|
end
|
|
842
839
|
role_keys = roles.map {|role| %(role_#{role}) } unless roles.empty?
|
|
843
|
-
if
|
|
844
|
-
((text_indent = @theme.
|
|
840
|
+
if IndentableTextAlignments[text_align] &&
|
|
841
|
+
((text_indent = @theme.prose_text_indent) > 0 ||
|
|
842
|
+
((text_indent = @theme.prose_text_indent_inner) > 0 && node.previous_sibling&.context == :paragraph))
|
|
845
843
|
prose_opts[:indent_paragraphs] = text_indent
|
|
846
844
|
end
|
|
847
845
|
if (bottom_gutter = @bottom_gutters[-1][node])
|
|
@@ -882,8 +880,7 @@ module Asciidoctor
|
|
|
882
880
|
if (doc = node.document).attr? 'icons'
|
|
883
881
|
if !(has_icon = node.attr? 'icon') && (doc.attr 'icons') == 'font'
|
|
884
882
|
icons = 'font'
|
|
885
|
-
|
|
886
|
-
icon_data = admonition_icon_data label_text
|
|
883
|
+
icon_data = admonition_icon_data type.to_sym
|
|
887
884
|
icon_size = icon_data[:size] || 24
|
|
888
885
|
label_width = label_min_width || (icon_size * 1.5)
|
|
889
886
|
elsif (icon_path = has_icon || !(icon_path = (@theme[%(admonition_icon_#{type})] || {})[:image]) ?
|
|
@@ -1046,7 +1043,7 @@ module Asciidoctor
|
|
|
1046
1043
|
else
|
|
1047
1044
|
highlighter = nil
|
|
1048
1045
|
end
|
|
1049
|
-
|
|
1046
|
+
saved_subs = (subs = node.subs).dup
|
|
1050
1047
|
callouts_enabled = subs.include? :callouts
|
|
1051
1048
|
highlight_idx = subs.index :highlight
|
|
1052
1049
|
# NOTE: scratch? here only applies if listing block is nested inside another block
|
|
@@ -1056,16 +1053,33 @@ module Asciidoctor
|
|
|
1056
1053
|
# switch the :highlight sub back to :specialcharacters
|
|
1057
1054
|
subs[highlight_idx] = :specialcharacters
|
|
1058
1055
|
else
|
|
1059
|
-
|
|
1056
|
+
saved_subs = nil
|
|
1060
1057
|
end
|
|
1061
1058
|
source_string = guard_indentation node.content
|
|
1062
1059
|
elsif highlight_idx
|
|
1063
1060
|
# NOTE: the source highlighter logic below handles the highlight and callouts subs
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1061
|
+
if (subs - [:highlight, :callouts]).empty?
|
|
1062
|
+
subs.clear
|
|
1063
|
+
# NOTE: indentation guards will be added by the source highlighter logic
|
|
1064
|
+
source_string = expand_tabs node.content
|
|
1065
|
+
else
|
|
1066
|
+
if callouts_enabled
|
|
1067
|
+
saved_lines = node.lines.dup
|
|
1068
|
+
subs.delete :callouts
|
|
1069
|
+
prev_subs = subs.dup
|
|
1070
|
+
subs.clear
|
|
1071
|
+
source_string, conum_mapping = extract_conums node.content
|
|
1072
|
+
node.lines.replace (source_string.split LF)
|
|
1073
|
+
subs.replace prev_subs
|
|
1074
|
+
callouts_enabled = false
|
|
1075
|
+
end
|
|
1076
|
+
subs[highlight_idx] = :specialcharacters
|
|
1077
|
+
# NOTE: indentation guards will be added by the source highlighter logic
|
|
1078
|
+
source_string = expand_tabs unescape_xml (sanitize node.content, compact: false)
|
|
1079
|
+
node.lines.replace saved_lines if saved_lines
|
|
1080
|
+
end
|
|
1067
1081
|
else
|
|
1068
|
-
highlighter =
|
|
1082
|
+
highlighter = saved_subs = nil
|
|
1069
1083
|
source_string = guard_indentation node.content
|
|
1070
1084
|
end
|
|
1071
1085
|
else
|
|
@@ -1157,7 +1171,7 @@ module Asciidoctor
|
|
|
1157
1171
|
# NOTE: only format if we detect a need (callouts or inline formatting)
|
|
1158
1172
|
source_chunks = (XMLMarkupRx.match? source_string) ? (text_formatter.format source_string) : [text: source_string]
|
|
1159
1173
|
end
|
|
1160
|
-
node.subs.replace
|
|
1174
|
+
node.subs.replace saved_subs if saved_subs
|
|
1161
1175
|
adjusted_font_size = ((node.option? 'autofit') || (node.document.attr? 'autofit-option')) ? (compute_autofit_font_size source_chunks, :code) : nil
|
|
1162
1176
|
end
|
|
1163
1177
|
|
|
@@ -1281,7 +1295,7 @@ module Asciidoctor
|
|
|
1281
1295
|
content = guard_indentation node.content
|
|
1282
1296
|
ink_prose content,
|
|
1283
1297
|
normalize: false,
|
|
1284
|
-
align: :left,
|
|
1298
|
+
align: (resolve_text_align_from_role node.roles) || :left,
|
|
1285
1299
|
hyphenate: true,
|
|
1286
1300
|
margin_bottom: 0,
|
|
1287
1301
|
bottom_gutter: (attribution ? nil : @bottom_gutters[-1][node])
|
|
@@ -1682,7 +1696,7 @@ module Asciidoctor
|
|
|
1682
1696
|
alignment = float_to.to_sym
|
|
1683
1697
|
elsif (alignment = node.attr 'align')
|
|
1684
1698
|
alignment = (BlockAlignmentNames.include? alignment) ? alignment.to_sym : :left
|
|
1685
|
-
elsif !(alignment = node.roles.reverse.find {|
|
|
1699
|
+
elsif !(alignment = node.roles.reverse.find {|role| BlockAlignmentNames.include? role }&.to_sym)
|
|
1686
1700
|
alignment = @theme.image_align&.to_sym || :left
|
|
1687
1701
|
end
|
|
1688
1702
|
end
|
|
@@ -1916,14 +1930,16 @@ module Asciidoctor
|
|
|
1916
1930
|
page_layout = nil
|
|
1917
1931
|
end
|
|
1918
1932
|
|
|
1919
|
-
if at_page_top?
|
|
1933
|
+
if at_page_top? && !(node.option? 'always')
|
|
1920
1934
|
if page_layout && page_layout != page.layout && page.empty?
|
|
1921
1935
|
delete_current_page
|
|
1922
1936
|
advance_page layout: page_layout, margin: @page_margin[page_layout][page_side nil, @folio_placement[:inverted]]
|
|
1923
1937
|
end
|
|
1924
1938
|
elsif page_layout
|
|
1939
|
+
bounds.current_column = bounds.last_column if ColumnBox === bounds && !(node.has_role? 'column')
|
|
1925
1940
|
advance_page layout: page_layout, margin: @page_margin[page_layout][page_side nil, @folio_placement[:inverted]]
|
|
1926
1941
|
else
|
|
1942
|
+
bounds.current_column = bounds.last_column if ColumnBox === bounds && !(node.has_role? 'column')
|
|
1927
1943
|
advance_page
|
|
1928
1944
|
end
|
|
1929
1945
|
end
|
|
@@ -2196,7 +2212,7 @@ module Asciidoctor
|
|
|
2196
2212
|
|
|
2197
2213
|
if node.option? 'autowidth'
|
|
2198
2214
|
table_width = (node.attr? 'width') ? bounds.width * ((node.attr 'tablepcwidth') / 100.0) :
|
|
2199
|
-
(((node.has_role? 'stretch')
|
|
2215
|
+
(((node.has_role? 'stretch')) ? bounds.width : nil)
|
|
2200
2216
|
column_widths = []
|
|
2201
2217
|
else
|
|
2202
2218
|
table_width = bounds.width * ((node.attr 'tablepcwidth') / 100.0)
|
|
@@ -2362,6 +2378,9 @@ module Asciidoctor
|
|
|
2362
2378
|
node.content
|
|
2363
2379
|
elsif node.content_model != :compound && (string = node.content)
|
|
2364
2380
|
prose_opts = opts.merge hyphenate: true, margin_bottom: 0
|
|
2381
|
+
if (text_align = resolve_text_align_from_role node.roles)
|
|
2382
|
+
prose_opts[:align] = text_align
|
|
2383
|
+
end
|
|
2365
2384
|
if (bottom_gutter = @bottom_gutters[-1][node])
|
|
2366
2385
|
prose_opts[:bottom_gutter] = bottom_gutter
|
|
2367
2386
|
end
|
|
@@ -2477,9 +2496,9 @@ module Asciidoctor
|
|
|
2477
2496
|
else
|
|
2478
2497
|
label = index
|
|
2479
2498
|
end
|
|
2480
|
-
%(
|
|
2499
|
+
%(<sup class="wj">#{anchor}[<a anchor="_footnotedef_#{index}">#{label}</a>]</sup>)
|
|
2481
2500
|
elsif node.type == :xref
|
|
2482
|
-
%(<sup><font color="#{theme.role_unresolved_font_color}">[#{node.text}]</font></sup>)
|
|
2501
|
+
%(<sup class="wj"><font color="#{theme.role_unresolved_font_color}">[#{node.text}]</font></sup>)
|
|
2483
2502
|
else
|
|
2484
2503
|
log :warn, %(unknown footnote type: #{node.type.inspect})
|
|
2485
2504
|
nil
|
|
@@ -2827,14 +2846,15 @@ module Asciidoctor
|
|
|
2827
2846
|
advance_page if orphaned
|
|
2828
2847
|
else
|
|
2829
2848
|
theme_font :heading, level: (hlevel = opts[:level]) do
|
|
2849
|
+
if (space_below = ::Numeric === min_height_after ? min_height_after : 0) > 0 && (node.context == :section ? node.blocks? : !node.last_child?)
|
|
2850
|
+
space_below += @theme[%(heading_h#{hlevel}_margin_bottom)] || @theme.heading_margin_bottom
|
|
2851
|
+
else
|
|
2852
|
+
space_below = 0
|
|
2853
|
+
end
|
|
2830
2854
|
h_padding_t, h_padding_r, h_padding_b, h_padding_l = expand_padding_value @theme[%(heading_h#{hlevel}_padding)]
|
|
2831
2855
|
h_fits = indent h_padding_l, h_padding_r do
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
(@theme[%(heading_h#{hlevel}_margin_top)] || @theme.heading_margin_top) +
|
|
2835
|
-
(@theme[%(heading_h#{hlevel}_margin_bottom)] || @theme.heading_margin_bottom) + h_padding_t + h_padding_b
|
|
2836
|
-
heading_h += min_height_after if min_height_after && (node.context == :section ? node.blocks? : !node.last_child?)
|
|
2837
|
-
cursor >= heading_h
|
|
2856
|
+
cursor >= (height_of_typeset_text title, inline_format: true, text_transform: @text_transform) +
|
|
2857
|
+
h_padding_t + h_padding_b + (@theme[%(heading_h#{hlevel}_margin_top)] || @theme.heading_margin_top) + space_below
|
|
2838
2858
|
end
|
|
2839
2859
|
advance_page unless h_fits
|
|
2840
2860
|
end
|
|
@@ -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
|
|
@@ -3220,8 +3248,8 @@ module Asciidoctor
|
|
|
3220
3248
|
align: @base_text_align.to_sym,
|
|
3221
3249
|
}.merge(opts)
|
|
3222
3250
|
end
|
|
3223
|
-
if h_category && @theme[%(#{h_category}_border_width)] &&
|
|
3224
|
-
|
|
3251
|
+
if h_category && @theme[%(#{h_category}_border_width)] && (@theme[%(#{h_category}_border_color)] || @theme.base_border_color)
|
|
3252
|
+
start_cursor = bounds.top unless page_number == start_page_number
|
|
3225
3253
|
float do
|
|
3226
3254
|
bounding_box [bounds.left, start_cursor], width: bounds.width, height: start_cursor - cursor do
|
|
3227
3255
|
theme_fill_and_stroke_bounds h_category
|
|
@@ -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
|
|
@@ -4,16 +4,19 @@ class Asciidoctor::Section
|
|
|
4
4
|
def numbered_title opts = {}
|
|
5
5
|
@cached_numbered_title ||= nil
|
|
6
6
|
unless @cached_numbered_title
|
|
7
|
-
|
|
7
|
+
doc = @document
|
|
8
|
+
if @numbered && !@caption && (slevel = @level) <= (doc.attr 'sectnumlevels', 3).to_i
|
|
8
9
|
@is_numbered = true
|
|
9
|
-
if
|
|
10
|
+
if doc.doctype == 'book'
|
|
10
11
|
case slevel
|
|
11
12
|
when 0
|
|
12
13
|
@cached_numbered_title = %(#{sectnum nil, ':'} #{title})
|
|
13
|
-
|
|
14
|
+
signifier = doc.attributes['part-signifier'] || ((doc.attr_unspecified? 'part-signifier') ? 'Part' : '')
|
|
15
|
+
@cached_formal_numbered_title = %(#{signifier}#{signifier.empty? ? '' : ' '}#{@cached_numbered_title})
|
|
14
16
|
when 1
|
|
15
17
|
@cached_numbered_title = %(#{sectnum} #{title})
|
|
16
|
-
|
|
18
|
+
signifier = doc.attributes['chapter-signifier'] || ((doc.attr_unspecified? 'chapter-signifier') ? 'Chapter' : '')
|
|
19
|
+
@cached_formal_numbered_title = %(#{signifier}#{signifier.empty? ? '' : ' '}#{@cached_numbered_title})
|
|
17
20
|
else
|
|
18
21
|
@cached_formal_numbered_title = @cached_numbered_title = %(#{sectnum} #{title})
|
|
19
22
|
end
|
|
@@ -500,22 +500,13 @@ module Asciidoctor
|
|
|
500
500
|
end
|
|
501
501
|
if first_line_text_transform
|
|
502
502
|
# NOTE: applying text transform here could alter the wrapping, so isolate first line and shrink it to fit
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
original_fragments, fragments = fragments, []
|
|
506
|
-
original_fragments.reduce '' do |traced, fragment|
|
|
507
|
-
fragments << fragment
|
|
508
|
-
# NOTE: we could just do a length comparison here
|
|
509
|
-
if (traced += fragment[:text]).start_with? first_line_text
|
|
510
|
-
fragment[:text] = fragment[:text][0...-(traced.length - first_line_text.length)]
|
|
511
|
-
break
|
|
512
|
-
end
|
|
513
|
-
traced
|
|
514
|
-
end
|
|
515
|
-
end
|
|
516
|
-
fragments.each {|fragment| fragment[:text] = transform_text fragment[:text], first_line_text_transform }
|
|
503
|
+
first_line_fragments = (box.instance_variable_get :@arranger).consumed
|
|
504
|
+
fragments = first_line_fragments.map {|fragment| fragment.merge text: (transform_text fragment[:text], first_line_text_transform) }
|
|
517
505
|
first_line_options[:overflow] = :shrink_to_fit
|
|
518
|
-
|
|
506
|
+
if remaining_fragments
|
|
507
|
+
@final_gap = true
|
|
508
|
+
first_line_options[:force_justify] = true if first_line_options[:align] == :justify && first_line_fragments[-1][:text] != ?\n
|
|
509
|
+
end
|
|
519
510
|
end
|
|
520
511
|
if text_indent
|
|
521
512
|
indent(text_indent) { fill_formatted_text_box fragments, first_line_options }
|
|
@@ -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'
|
|
@@ -176,8 +176,10 @@ module Asciidoctor
|
|
|
176
176
|
end
|
|
177
177
|
if (text_transform = fragment.delete :text_transform)
|
|
178
178
|
text = (text_chunks = extract_text pcdata).join
|
|
179
|
-
|
|
180
|
-
restore_text pcdata, text_chunks.each_with_object
|
|
179
|
+
chars = (StringIO.new transform_text text, text_transform).each_char
|
|
180
|
+
restore_text pcdata, (text_chunks.each_with_object [] do |chunk, accum|
|
|
181
|
+
accum << chunk.length.times.map { chars.next }.join
|
|
182
|
+
end)
|
|
181
183
|
end
|
|
182
184
|
# NOTE: decorate child fragments with inherited properties from this element
|
|
183
185
|
apply pcdata, fragments, fragment
|
|
@@ -315,10 +317,12 @@ module Asciidoctor
|
|
|
315
317
|
end) : value
|
|
316
318
|
elsif (value = attrs[:id])
|
|
317
319
|
# NOTE: text is null character, which is used as placeholder text so Prawn doesn't drop fragment
|
|
318
|
-
|
|
320
|
+
new_fragment = { name: value, callback: [InlineDestinationMarker] }
|
|
321
|
+
new_fragment[:wj] = true if fragment[:wj]
|
|
319
322
|
if (type = attrs[:type])
|
|
320
|
-
|
|
323
|
+
new_fragment[:type] = type.to_sym
|
|
321
324
|
end
|
|
325
|
+
fragment = new_fragment
|
|
322
326
|
visible = nil
|
|
323
327
|
end
|
|
324
328
|
end
|
|
@@ -357,6 +361,7 @@ module Asciidoctor
|
|
|
357
361
|
end
|
|
358
362
|
# TODO: we could limit to select tags, but doesn't seem to really affect performance
|
|
359
363
|
attrs[:class].split.each do |class_name|
|
|
364
|
+
fragment[:wj] = true if class_name == 'wj'
|
|
360
365
|
next unless @theme_settings.key? class_name
|
|
361
366
|
update_fragment fragment, @theme_settings[class_name]
|
|
362
367
|
# 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
|
|
@@ -83,6 +83,7 @@ module Asciidoctor
|
|
|
83
83
|
theme_data.base_text_align ||= 'left'
|
|
84
84
|
theme_data.base_line_height ||= 1
|
|
85
85
|
theme_data.base_font_color ||= '000000'
|
|
86
|
+
theme_data.base_font_size ||= 12
|
|
86
87
|
theme_data.code_font_family ||= (theme_data.codespan_font_family || 'Courier')
|
|
87
88
|
theme_data.conum_font_family ||= (theme_data.codespan_font_family || 'Courier')
|
|
88
89
|
if (heading_font_family = theme_data.heading_font_family)
|
|
@@ -160,7 +161,7 @@ module Asciidoctor
|
|
|
160
161
|
data[key] = {}.tap do |accum|
|
|
161
162
|
val.each do |key2, val2|
|
|
162
163
|
key2 = key2.tr '-', '_' if key2.include? '-'
|
|
163
|
-
accum[key2.to_sym] = (key2.end_with? '_color') ? (to_color evaluate val2, data) : (evaluate val2, data)
|
|
164
|
+
accum[key2.to_sym] = (key2.end_with? '_color') ? (to_color evaluate val2, data, math: false) : (evaluate val2, data)
|
|
164
165
|
end
|
|
165
166
|
end if val
|
|
166
167
|
elsif ::Hash === val
|
|
@@ -173,20 +174,18 @@ module Asciidoctor
|
|
|
173
174
|
end
|
|
174
175
|
elsif (rekey = DeprecatedKeys[key]) ||
|
|
175
176
|
((key.start_with? 'role_') && (key.end_with? '_align') && (rekey = key.sub RoleAlignKeyRx, '_text_align'))
|
|
176
|
-
data[rekey] = evaluate val, data
|
|
177
|
+
data[rekey] = evaluate val, data, math: false
|
|
177
178
|
elsif PaddingBottomHackKeys.include? key
|
|
178
179
|
val = evaluate val, data
|
|
179
180
|
# normalize padding hacks for themes designed before the converter had smart margins
|
|
180
181
|
val[2] = val[0] if ::Array === val && val[0].to_f >= 0 && val[2].to_f <= 0
|
|
181
182
|
data[key] = val
|
|
182
|
-
# QUESTION: do we really need to evaluate_math in this case?
|
|
183
183
|
elsif key.end_with? '_color'
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
data[key] = val.map {|it| to_color evaluate it, data }
|
|
184
|
+
# assume table_grid_color is a single color unless the value is a 2-element array for backwards compatibility
|
|
185
|
+
if key == 'table_border_color' ? ::Array === val : (key == 'table_grid_color' && ::Array === val && val.size == 2)
|
|
186
|
+
data[key] = val.map {|it| to_color evaluate it, data, math: false }
|
|
188
187
|
else
|
|
189
|
-
data[key] = to_color evaluate val, data
|
|
188
|
+
data[key] = to_color evaluate val, data, math: false
|
|
190
189
|
end
|
|
191
190
|
elsif key.end_with? '_content'
|
|
192
191
|
data[key] = (expand_vars val.to_s, data).to_s
|
|
@@ -196,12 +195,12 @@ module Asciidoctor
|
|
|
196
195
|
data
|
|
197
196
|
end
|
|
198
197
|
|
|
199
|
-
def evaluate expr, vars
|
|
198
|
+
def evaluate expr, vars, math: true
|
|
200
199
|
case expr
|
|
201
200
|
when ::String
|
|
202
|
-
evaluate_math expand_vars expr, vars
|
|
201
|
+
math ? (evaluate_math expand_vars expr, vars) : (expand_vars expr, vars)
|
|
203
202
|
when ::Array
|
|
204
|
-
expr.map {|e| evaluate e, vars }
|
|
203
|
+
expr.map {|e| evaluate e, vars, math: math }
|
|
205
204
|
else
|
|
206
205
|
expr
|
|
207
206
|
end
|
|
@@ -233,7 +232,6 @@ module Asciidoctor
|
|
|
233
232
|
def evaluate_math expr
|
|
234
233
|
return expr if !(::String === expr) || ColorValue === expr
|
|
235
234
|
# resolve measurement values (e.g., 0.5in => 36)
|
|
236
|
-
# QUESTION: should we round the value? perhaps leave that to the precision functions
|
|
237
235
|
# NOTE: leave % as a string; handled by converter for now
|
|
238
236
|
original, expr = expr, (resolve_measurement_values expr)
|
|
239
237
|
while true
|
|
@@ -257,8 +255,7 @@ module Asciidoctor
|
|
|
257
255
|
end
|
|
258
256
|
end
|
|
259
257
|
if (expr.end_with? ')') && expr =~ PrecisionFuncRx
|
|
260
|
-
op = $1
|
|
261
|
-
offset = op.length + 1
|
|
258
|
+
offset = (op = $1).length + 1
|
|
262
259
|
expr = expr[offset...-1].to_f.send op.to_sym
|
|
263
260
|
end
|
|
264
261
|
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.1
|
|
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-09-17 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: asciidoctor
|
|
@@ -193,8 +193,8 @@ dependencies:
|
|
|
193
193
|
- - "~>"
|
|
194
194
|
- !ruby/object:Gem::Version
|
|
195
195
|
version: 1.4.0
|
|
196
|
-
description: An
|
|
197
|
-
using the Prawn PDF library.
|
|
196
|
+
description: An add-on converter for Asciidoctor that converts AsciiDoc documents
|
|
197
|
+
to PDF using the Prawn PDF generation library.
|
|
198
198
|
email: dan@opendevise.com
|
|
199
199
|
executables:
|
|
200
200
|
- asciidoctor-pdf
|
|
@@ -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
|