asciidoctor-pdf 2.0.0.alpha.2 → 2.0.0.beta.2
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 +185 -83
- data/README.adoc +31 -126
- data/data/fonts/ABOUT-mplus1p-subset +1 -0
- data/data/fonts/ABOUT-notosans-subset +1 -0
- data/data/fonts/ABOUT-notoserif-subset +1 -0
- data/data/fonts/mplus1mn-bold-subset.ttf +0 -0
- data/data/fonts/mplus1mn-bold_italic-subset.ttf +0 -0
- data/data/fonts/mplus1mn-italic-subset.ttf +0 -0
- data/data/fonts/mplus1mn-regular-subset.ttf +0 -0
- data/data/fonts/mplus1p-regular-fallback.ttf +0 -0
- data/data/fonts/notosans-bold-subset.ttf +0 -0
- data/data/fonts/notosans-bold_italic-subset.ttf +0 -0
- data/data/fonts/notosans-italic-subset.ttf +0 -0
- data/data/fonts/notosans-regular-subset.ttf +0 -0
- data/data/fonts/notoserif-bold-subset.ttf +0 -0
- data/data/fonts/notoserif-bold_italic-subset.ttf +0 -0
- data/data/fonts/notoserif-italic-subset.ttf +0 -0
- data/data/fonts/notoserif-regular-subset.ttf +0 -0
- data/data/themes/base-theme.yml +10 -20
- data/data/themes/default-for-print-theme.yml +4 -4
- data/data/themes/default-for-print-with-fallback-font-theme.yml +2 -3
- data/data/themes/default-for-print-with-font-fallbacks-theme.yml +3 -0
- data/data/themes/{sans-with-fallback-font-theme.yml → default-sans-theme.yml} +1 -1
- data/data/themes/default-sans-with-font-fallbacks-theme.yml +3 -0
- data/data/themes/default-theme.yml +5 -4
- data/data/themes/default-with-fallback-font-theme.yml +2 -9
- data/data/themes/default-with-font-fallbacks-theme.yml +9 -0
- data/docs/theming-guide.adoc +5 -6050
- data/lib/asciidoctor/pdf/converter.rb +505 -294
- data/lib/asciidoctor/pdf/ext/pdf-core/page.rb +8 -0
- data/lib/asciidoctor/pdf/ext/prawn/document/column_box.rb +16 -0
- data/lib/asciidoctor/pdf/ext/prawn/extensions.rb +110 -58
- data/lib/asciidoctor/pdf/ext/prawn/formatted_text/box.rb +14 -0
- data/lib/asciidoctor/pdf/ext/prawn/formatted_text/indented_paragraph_wrap.rb +39 -0
- data/lib/asciidoctor/pdf/ext/prawn-table/cell/asciidoc.rb +3 -10
- data/lib/asciidoctor/pdf/ext/prawn.rb +2 -0
- data/lib/asciidoctor/pdf/formatted_text/source_wrap.rb +7 -2
- data/lib/asciidoctor/pdf/nopngmagick.rb +3 -0
- data/lib/asciidoctor/pdf/optimizer.rb +12 -5
- data/lib/asciidoctor/pdf/text_transformer.rb +14 -0
- data/lib/asciidoctor/pdf/theme_loader.rb +9 -3
- data/lib/asciidoctor/pdf/version.rb +1 -1
- metadata +9 -3
@@ -52,6 +52,7 @@ module Asciidoctor
|
|
52
52
|
TextAlignmentRoles = %w(text-justify text-left text-center text-right)
|
53
53
|
TextDecorationStyleTable = { 'underline' => :underline, 'line-through' => :strikethrough }
|
54
54
|
FontKerningTable = { 'normal' => true, 'none' => false }
|
55
|
+
BlockFloatNames = %w(left right)
|
55
56
|
BlockAlignmentNames = %w(left center right)
|
56
57
|
(AlignmentTable = { '<' => :left, '=' => :center, '>' => :right }).default = :left
|
57
58
|
ColumnPositions = [:left, :center, :right]
|
@@ -139,6 +140,7 @@ module Asciidoctor
|
|
139
140
|
# NOTE: enabling data-uri forces Asciidoctor Diagram to produce absolute image paths
|
140
141
|
doc.attributes['data-uri'] = (doc.instance_variable_get :@attribute_overrides)['data-uri'] = ''
|
141
142
|
end
|
143
|
+
@label = :primary
|
142
144
|
@initial_instance_variables = [:@initial_instance_variables] + instance_variables
|
143
145
|
end
|
144
146
|
|
@@ -168,7 +170,7 @@ module Asciidoctor
|
|
168
170
|
if (bottom_gutter = @bottom_gutters[-1][node])
|
169
171
|
prose_opts[:bottom_gutter] = bottom_gutter
|
170
172
|
end
|
171
|
-
|
173
|
+
ink_prose string, prose_opts
|
172
174
|
end
|
173
175
|
ensure
|
174
176
|
node.document.instance_variable_set :@converter, prev_converter if prev_converter
|
@@ -181,8 +183,9 @@ module Asciidoctor
|
|
181
183
|
def convert_document doc
|
182
184
|
doc.promote_preface_block
|
183
185
|
init_pdf doc
|
184
|
-
# set default value for outline,
|
186
|
+
# set default value for outline, outline-title, and pagenums attributes if not otherwise set
|
185
187
|
doc.attributes['outline'] = '' unless (doc.attribute_locked? 'outline') || ((doc.instance_variable_get :@attributes_modified).include? 'outline')
|
188
|
+
doc.attributes['outline-title'] = '' unless (doc.attribute_locked? 'outline-title') || ((doc.instance_variable_get :@attributes_modified).include? 'outline-title')
|
186
189
|
doc.attributes['pagenums'] = '' unless (doc.attribute_locked? 'pagenums') || ((doc.instance_variable_get :@attributes_modified).include? 'pagenums')
|
187
190
|
#assign_missing_section_ids doc
|
188
191
|
|
@@ -190,22 +193,22 @@ module Asciidoctor
|
|
190
193
|
|
191
194
|
marked_page_number = page_number
|
192
195
|
# NOTE: a new page will already be started (page_number = 2) if the front cover image is a PDF
|
193
|
-
|
196
|
+
ink_cover_page doc, :front
|
194
197
|
has_front_cover = page_number > marked_page_number
|
195
|
-
has_title_page =
|
198
|
+
has_title_page = ink_title_page doc if (title_page_on = doc.doctype == 'book' || (doc.attr? 'title-page'))
|
196
199
|
|
197
200
|
@page_margin_by_side[:cover] = @page_margin_by_side[:recto] if @media == 'prepress' && page_number == 0
|
198
201
|
|
199
202
|
start_new_page unless page&.empty? # rubocop:disable Lint/SafeNavigationWithEmpty
|
200
203
|
|
201
204
|
# NOTE: the base font must be set before any content is written to the main or scratch document
|
202
|
-
# this method is called inside
|
205
|
+
# this method is called inside ink_title_page if the title page is active
|
203
206
|
font @theme.base_font_family, size: @root_font_size, style: @theme.base_font_style unless has_title_page
|
204
207
|
|
205
208
|
unless title_page_on
|
206
209
|
body_start_page_number = page_number
|
207
210
|
theme_font :heading, level: 1 do
|
208
|
-
|
211
|
+
ink_general_heading doc, doc.doctitle, align: (@theme.heading_h1_text_align&.to_sym || :center), level: 1, role: :doctitle
|
209
212
|
end if doc.header? && !doc.notitle
|
210
213
|
end
|
211
214
|
|
@@ -216,7 +219,7 @@ module Asciidoctor
|
|
216
219
|
if (insert_toc = (doc.attr? 'toc') && !((toc_placement = doc.attr 'toc-placement') == 'macro' || toc_placement == 'preamble') && doc.sections?)
|
217
220
|
start_new_page if @ppbook && verso_page?
|
218
221
|
add_dest_for_block doc, id: 'toc', y: (at_page_top? ? page_height : nil)
|
219
|
-
allocate_toc doc, toc_num_levels, cursor, title_page_on
|
222
|
+
@toc_extent = allocate_toc doc, toc_num_levels, cursor, title_page_on
|
220
223
|
else
|
221
224
|
@toc_extent = nil
|
222
225
|
end
|
@@ -301,26 +304,26 @@ module Asciidoctor
|
|
301
304
|
traverse doc
|
302
305
|
|
303
306
|
# NOTE: for a book, these are leftover footnotes; for an article this is everything
|
304
|
-
outdent_section {
|
307
|
+
outdent_section { ink_footnotes doc }
|
305
308
|
|
306
|
-
if @toc_extent
|
309
|
+
if (toc_extent = @toc_extent)
|
307
310
|
if title_page_on && !insert_toc
|
308
|
-
num_front_matter_pages[0] =
|
309
|
-
num_front_matter_pages[1] =
|
311
|
+
num_front_matter_pages[0] = toc_extent.to.page if @theme.running_content_start_at == 'after-toc'
|
312
|
+
num_front_matter_pages[1] = toc_extent.to.page if @theme.page_numbering_start_at == 'after-toc'
|
310
313
|
end
|
311
|
-
toc_page_nums =
|
314
|
+
toc_page_nums = ink_toc doc, toc_num_levels, toc_extent.from.page, toc_extent.from.cursor, num_front_matter_pages[1]
|
312
315
|
else
|
313
316
|
toc_page_nums = []
|
314
317
|
end
|
315
318
|
|
316
319
|
# NOTE: delete orphaned page (a page was created but there was no additional content)
|
317
320
|
# QUESTION: should we delete page if document is empty? (leaving no pages?)
|
318
|
-
|
321
|
+
delete_current_page if page_count > 1 && page.empty?
|
319
322
|
end
|
320
323
|
|
321
324
|
unless page_count < body_start_page_number
|
322
|
-
|
323
|
-
|
325
|
+
ink_running_content :header, doc, num_front_matter_pages, body_start_page_number unless doc.noheader || @theme.header_height.to_f == 0 # rubocop:disable Lint/FloatComparison
|
326
|
+
ink_running_content :footer, doc, num_front_matter_pages, body_start_page_number unless doc.nofooter || @theme.footer_height.to_f == 0 # rubocop:disable Lint/FloatComparison
|
324
327
|
end
|
325
328
|
|
326
329
|
add_outline doc, (doc.attr 'outlinelevels', toc_num_levels), toc_page_nums, num_front_matter_pages[1], has_front_cover
|
@@ -337,7 +340,7 @@ module Asciidoctor
|
|
337
340
|
catalog.data[:ViewerPreferences] = { DisplayDocTitle: true }
|
338
341
|
|
339
342
|
stamp_foreground_image doc, has_front_cover
|
340
|
-
|
343
|
+
ink_cover_page doc, :back
|
341
344
|
add_dest_for_top doc
|
342
345
|
nil
|
343
346
|
end
|
@@ -461,7 +464,6 @@ module Asciidoctor
|
|
461
464
|
end
|
462
465
|
|
463
466
|
def prepare_theme theme
|
464
|
-
theme.base_border_width || 0
|
465
467
|
theme.base_font_color ||= '000000'
|
466
468
|
theme.base_font_size ||= 12
|
467
469
|
theme.base_font_style = theme.base_font_style&.to_sym || :normal
|
@@ -478,12 +480,16 @@ module Asciidoctor
|
|
478
480
|
theme.list_item_spacing ||= 0
|
479
481
|
theme.description_list_term_spacing ||= 0
|
480
482
|
theme.description_list_description_indent ||= 0
|
483
|
+
theme.table_border_color ||= (theme.base_border_color || '000000')
|
484
|
+
theme.table_border_width ||= 0.5
|
485
|
+
theme.thematic_break_border_color ||= (theme.base_border_color || '000000')
|
481
486
|
theme.image_border_width ||= 0
|
482
487
|
theme.code_linenum_font_color ||= '999999'
|
483
488
|
theme.callout_list_margin_top_after_code ||= 0
|
484
489
|
theme.role_unresolved_font_color ||= 'FF0000'
|
485
|
-
theme.index_columns ||= 2
|
486
490
|
theme.footnotes_item_spacing ||= 0
|
491
|
+
theme.index_columns ||= 2
|
492
|
+
theme.index_column_gap ||= theme.base_font_size
|
487
493
|
theme.kbd_separator ||= '+'
|
488
494
|
theme.title_page_authors_delimiter ||= ', '
|
489
495
|
theme.title_page_revision_delimiter ||= ', '
|
@@ -661,24 +667,24 @@ module Asciidoctor
|
|
661
667
|
title, _, subtitle = title.rpartition sep
|
662
668
|
title = %(#{title}\n<em class="subtitle">#{subtitle}</em>)
|
663
669
|
end
|
664
|
-
hlevel = sect.level
|
670
|
+
hlevel = sect.level.next
|
665
671
|
align = (@theme[%(heading_h#{hlevel}_text_align)] || @theme.heading_text_align || @base_text_align).to_sym
|
666
672
|
chapterlike = !(part = sectname == 'part') && (sectname == 'chapter' || (sect.document.doctype == 'book' && sect.level == 1))
|
667
673
|
hidden = sect.option? 'notitle'
|
668
674
|
hopts = { align: align, level: hlevel, part: part, chapterlike: chapterlike, outdent: !(part || chapterlike) }
|
669
675
|
if part
|
670
676
|
unless @theme.heading_part_break_before == 'auto'
|
671
|
-
|
677
|
+
started_new = true
|
672
678
|
start_new_part sect
|
673
679
|
end
|
674
680
|
elsif chapterlike
|
675
681
|
if @theme.heading_chapter_break_before != 'auto' ||
|
676
682
|
(@theme.heading_part_break_after == 'always' && sect == sect.parent.sections[0])
|
677
|
-
|
683
|
+
started_new = true
|
678
684
|
start_new_chapter sect
|
679
685
|
end
|
680
686
|
end
|
681
|
-
|
687
|
+
arrange_heading sect, title, hopts unless hidden || started_new || at_page_top? || !sect.blocks?
|
682
688
|
# QUESTION: should we store pdf-page-start, pdf-anchor & pdf-destination in internal map?
|
683
689
|
sect.set_attr 'pdf-page-start', (start_pgnum = page_number)
|
684
690
|
# QUESTION: should we just assign the section this generated id?
|
@@ -687,11 +693,11 @@ module Asciidoctor
|
|
687
693
|
add_dest_for_block sect, id: sect_anchor, y: (at_page_top? ? page_height : nil)
|
688
694
|
theme_font :heading, level: hlevel do
|
689
695
|
if part
|
690
|
-
|
696
|
+
ink_part_title sect, title, hopts
|
691
697
|
elsif chapterlike
|
692
|
-
|
698
|
+
ink_chapter_title sect, title, hopts
|
693
699
|
else
|
694
|
-
|
700
|
+
ink_general_heading sect, title, hopts
|
695
701
|
end
|
696
702
|
end unless hidden
|
697
703
|
|
@@ -700,7 +706,7 @@ module Asciidoctor
|
|
700
706
|
else
|
701
707
|
traverse sect
|
702
708
|
end
|
703
|
-
outdent_section {
|
709
|
+
outdent_section { ink_footnotes sect } if chapterlike
|
704
710
|
sect.set_attr 'pdf-page-end', page_number
|
705
711
|
end
|
706
712
|
|
@@ -721,7 +727,7 @@ module Asciidoctor
|
|
721
727
|
end
|
722
728
|
|
723
729
|
# QUESTION: if a footnote ref appears in a separate chapter, should the footnote def be duplicated?
|
724
|
-
def
|
730
|
+
def ink_footnotes node
|
725
731
|
return if (fns = (doc = node.document).footnotes - @rendered_footnotes).empty?
|
726
732
|
theme_margin :block, :bottom if node.context == :document || node == node.document.blocks[-1]
|
727
733
|
theme_margin :footnotes, :top
|
@@ -730,7 +736,7 @@ module Asciidoctor
|
|
730
736
|
move_down delta
|
731
737
|
end
|
732
738
|
theme_font :footnotes do
|
733
|
-
(title = doc.attr 'footnotes-title') && (
|
739
|
+
(title = doc.attr 'footnotes-title') && (ink_caption title, category: :footnotes)
|
734
740
|
item_spacing = @theme.footnotes_item_spacing
|
735
741
|
index_offset = @rendered_footnotes.length
|
736
742
|
sect_xreftext = node.context == :section && (node.xreftext node.document.attr 'xrefstyle')
|
@@ -740,7 +746,7 @@ module Asciidoctor
|
|
740
746
|
fn.singleton_class.send :attr_accessor, :label unless fn.respond_to? :label=
|
741
747
|
fn.label = %(#{label} - #{sect_xreftext})
|
742
748
|
end
|
743
|
-
|
749
|
+
ink_prose %(<a id="_footnotedef_#{index}">#{DummyText}</a>[<a anchor="_footnoteref_#{index}">#{label}</a>] #{fn.text}), margin_bottom: item_spacing, hyphenate: true
|
744
750
|
end
|
745
751
|
@rendered_footnotes += fns if extent
|
746
752
|
end
|
@@ -749,14 +755,17 @@ module Asciidoctor
|
|
749
755
|
end
|
750
756
|
|
751
757
|
def convert_floating_title node
|
752
|
-
|
758
|
+
title = node.title
|
753
759
|
hlevel = node.level.next
|
754
|
-
unless (align =
|
760
|
+
unless (align = resolve_text_align_from_role node.roles)
|
755
761
|
align = (@theme[%(heading_h#{hlevel}_text_align)] || @theme.heading_text_align || @base_text_align).to_sym
|
756
762
|
end
|
763
|
+
hopts = { align: align, level: hlevel, outdent: (parent = node.parent).context == :section }
|
764
|
+
arrange_heading node, title, hopts unless at_page_top? || node == parent.blocks[-1]
|
765
|
+
add_dest_for_block node if node.id
|
757
766
|
# QUESTION: should we decouple styles from section titles?
|
758
767
|
theme_font :heading, level: hlevel do
|
759
|
-
|
768
|
+
ink_general_heading node, title, hopts
|
760
769
|
end
|
761
770
|
end
|
762
771
|
|
@@ -765,7 +774,7 @@ module Asciidoctor
|
|
765
774
|
outdent_section do
|
766
775
|
pad_box @theme.abstract_padding do
|
767
776
|
theme_font :abstract_title do
|
768
|
-
|
777
|
+
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)
|
769
778
|
end if node.title?
|
770
779
|
theme_font :abstract do
|
771
780
|
prose_opts = { align: (@theme.abstract_text_align || @base_text_align).to_sym, hyphenate: true }
|
@@ -774,11 +783,21 @@ module Asciidoctor
|
|
774
783
|
end
|
775
784
|
# FIXME: allow theme to control more first line options
|
776
785
|
if (line1_font_style = @theme.abstract_first_line_font_style&.to_sym) && line1_font_style != font_style
|
777
|
-
|
786
|
+
case line1_font_style
|
787
|
+
when :normal
|
788
|
+
first_line_options = { styles: [] }
|
789
|
+
when :normal_italic
|
790
|
+
first_line_options = { styles: [:italic] }
|
791
|
+
else
|
792
|
+
first_line_options = { styles: [font_style, line1_font_style] }
|
793
|
+
end
|
778
794
|
end
|
779
795
|
if (line1_font_color = @theme.abstract_first_line_font_color)
|
780
796
|
(first_line_options ||= {})[:color] = line1_font_color
|
781
797
|
end
|
798
|
+
if (line1_text_transform = @theme.abstract_first_line_text_transform)
|
799
|
+
(first_line_options ||= {})[:text_transform] = line1_text_transform
|
800
|
+
end
|
782
801
|
prose_opts[:first_line_options] = first_line_options if first_line_options
|
783
802
|
# FIXME: make this cleaner!!
|
784
803
|
if node.blocks?
|
@@ -787,7 +806,7 @@ module Asciidoctor
|
|
787
806
|
if child.context == :paragraph
|
788
807
|
child.document.playback_attributes child.attributes
|
789
808
|
prose_opts[:margin_bottom] = 0 if child == last_block
|
790
|
-
|
809
|
+
ink_prose child.content, ((align = resolve_text_align_from_role child.roles) ? (prose_opts.merge align: align) : prose_opts.dup)
|
791
810
|
prose_opts.delete :first_line_options
|
792
811
|
prose_opts.delete :margin_bottom
|
793
812
|
else
|
@@ -796,10 +815,10 @@ module Asciidoctor
|
|
796
815
|
end
|
797
816
|
end
|
798
817
|
elsif node.content_model != :compound && (string = node.content)
|
799
|
-
if (align =
|
818
|
+
if (align = resolve_text_align_from_role node.roles)
|
800
819
|
prose_opts[:align] = align
|
801
820
|
end
|
802
|
-
|
821
|
+
ink_prose string, (prose_opts.merge margin_bottom: 0)
|
803
822
|
end
|
804
823
|
end
|
805
824
|
end
|
@@ -820,39 +839,39 @@ module Asciidoctor
|
|
820
839
|
|
821
840
|
def convert_paragraph node
|
822
841
|
add_dest_for_block node if node.id
|
842
|
+
|
823
843
|
prose_opts = { margin_bottom: 0, hyphenate: true }
|
824
|
-
if (align =
|
844
|
+
if (align = resolve_text_align_from_role (roles = node.roles), query_theme: true, remove_predefined: true)
|
825
845
|
prose_opts[:align] = align
|
826
|
-
roles -= TextAlignmentRoles
|
827
846
|
end
|
828
|
-
|
847
|
+
role_keys = roles.map {|role| %(role_#{role}).to_sym } unless roles.empty?
|
829
848
|
if (text_indent = @theme.prose_text_indent) > 0 ||
|
830
849
|
((text_indent = @theme.prose_text_indent_inner) > 0 &&
|
831
850
|
(self_idx = (siblings = node.parent.blocks).index node) > 0 && siblings[self_idx - 1].context == :paragraph)
|
832
851
|
prose_opts[:indent_paragraphs] = text_indent
|
833
852
|
end
|
834
|
-
|
835
|
-
# TODO: check if we're within one line of the bottom of the page
|
836
|
-
# and advance to the next page if so (similar to logic for section titles)
|
837
|
-
inscribe_caption node, labeled: false if node.title?
|
838
|
-
|
839
853
|
if (bottom_gutter = @bottom_gutters[-1][node])
|
840
854
|
prose_opts[:bottom_gutter] = bottom_gutter
|
841
855
|
end
|
842
856
|
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
857
|
+
block_next = next_enclosed_block node
|
858
|
+
|
859
|
+
insert_margin_bottom = proc do
|
860
|
+
if (margin_inner_val = @theme.prose_margin_inner) && block_next&.context == :paragraph
|
861
|
+
margin_bottom margin_inner_val
|
862
|
+
else
|
863
|
+
theme_margin :prose, :bottom, block_next
|
848
864
|
end
|
849
865
|
end
|
850
866
|
|
851
|
-
|
852
|
-
|
853
|
-
margin_bottom margin_inner_val
|
867
|
+
if (float_box = (@float_box ||= nil))
|
868
|
+
ink_paragraph_in_float_box node, float_box, prose_opts, role_keys, block_next, insert_margin_bottom
|
854
869
|
else
|
855
|
-
|
870
|
+
# TODO: check if we're within one line of the bottom of the page
|
871
|
+
# and advance to the next page if so (similar to logic for section titles)
|
872
|
+
ink_caption node, labeled: false if node.title?
|
873
|
+
role_keys ? theme_font_cascade(role_keys) { ink_prose node.content, prose_opts } : (ink_prose node.content, prose_opts)
|
874
|
+
insert_margin_bottom.call
|
856
875
|
end
|
857
876
|
end
|
858
877
|
|
@@ -892,20 +911,16 @@ module Asciidoctor
|
|
892
911
|
label_width = label_min_width if label_min_width && label_min_width > label_width
|
893
912
|
end
|
894
913
|
end
|
895
|
-
|
896
|
-
|
897
|
-
end
|
898
|
-
unless ::Array === (lpad = @theme.admonition_label_padding || cpad)
|
899
|
-
lpad = ::Array.new 4, lpad
|
900
|
-
end
|
914
|
+
cpad = expand_padding_value @theme.admonition_padding
|
915
|
+
lpad = (lpad = @theme.admonition_label_padding) ? (expand_padding_value lpad) : cpad
|
901
916
|
arrange_block node do |extent|
|
902
917
|
add_dest_for_block node if node.id
|
903
918
|
theme_fill_and_stroke_block :admonition, extent if extent
|
904
919
|
pad_box [0, cpad[1], 0, lpad[3]] do
|
905
920
|
if extent
|
906
921
|
label_height = extent.single_page_height || cursor
|
907
|
-
if (
|
908
|
-
(
|
922
|
+
if (rule_width = @theme.admonition_column_rule_width || 0) > 0 &&
|
923
|
+
(rule_color = @theme.admonition_column_rule_color || @theme.base_border_color)
|
909
924
|
rule_style = @theme.admonition_column_rule_style&.to_sym || :solid
|
910
925
|
float do
|
911
926
|
extent.each_page do |first_page, last_page|
|
@@ -996,7 +1011,7 @@ module Asciidoctor
|
|
996
1011
|
end
|
997
1012
|
end
|
998
1013
|
@text_transform = nil # already applied to label
|
999
|
-
|
1014
|
+
ink_prose label_text,
|
1000
1015
|
align: label_align,
|
1001
1016
|
valign: label_valign,
|
1002
1017
|
line_height: 1,
|
@@ -1010,7 +1025,7 @@ module Asciidoctor
|
|
1010
1025
|
end
|
1011
1026
|
end
|
1012
1027
|
pad_box [cpad[0], 0, cpad[2], label_width + lpad[1] + cpad[3]], node do
|
1013
|
-
|
1028
|
+
ink_caption node, category: :admonition, labeled: false if node.title?
|
1014
1029
|
theme_font :admonition do
|
1015
1030
|
traverse node
|
1016
1031
|
end
|
@@ -1044,13 +1059,13 @@ module Asciidoctor
|
|
1044
1059
|
arrange_block node do
|
1045
1060
|
add_dest_for_block node if id
|
1046
1061
|
tare_first_page_content_stream do
|
1047
|
-
node.context == :example ? (
|
1062
|
+
node.context == :example ? (ink_caption %(\u25bc #{node.title})) : (ink_caption node, labeled: false)
|
1048
1063
|
end if has_title
|
1049
1064
|
traverse node
|
1050
1065
|
end
|
1051
1066
|
else
|
1052
1067
|
add_dest_for_block node if id
|
1053
|
-
node.context == :example ? (
|
1068
|
+
node.context == :example ? (ink_caption %(\u25bc #{node.title})) : (ink_caption node, labeled: false) if has_title
|
1054
1069
|
traverse node
|
1055
1070
|
end
|
1056
1071
|
end
|
@@ -1059,7 +1074,7 @@ module Asciidoctor
|
|
1059
1074
|
category = node.context == :quote ? :quote : :verse
|
1060
1075
|
# NOTE: b_width and b_left_width are mutually exclusive
|
1061
1076
|
if (b_left_width = @theme[%(#{category}_border_left_width)]) && b_left_width > 0
|
1062
|
-
b_color = @theme[%(#{category}_border_color)]
|
1077
|
+
b_color = @theme[%(#{category}_border_color)] || @theme.base_border_color
|
1063
1078
|
else
|
1064
1079
|
b_left_width = nil
|
1065
1080
|
b_width = nil if (b_width = @theme[%(#{category}_border_width)]) == 0
|
@@ -1094,7 +1109,7 @@ module Asciidoctor
|
|
1094
1109
|
traverse node
|
1095
1110
|
else # :verse
|
1096
1111
|
content = guard_indentation node.content
|
1097
|
-
|
1112
|
+
ink_prose content,
|
1098
1113
|
normalize: false,
|
1099
1114
|
align: :left,
|
1100
1115
|
hyphenate: true,
|
@@ -1106,7 +1121,7 @@ module Asciidoctor
|
|
1106
1121
|
margin_bottom @theme.block_margin_bottom
|
1107
1122
|
theme_font %(#{category}_cite) do
|
1108
1123
|
attribution_parts = citetitle ? [attribution, citetitle] : [attribution]
|
1109
|
-
|
1124
|
+
ink_prose %(#{EmDash} #{attribution_parts.join ', '}), align: :left, normalize: false, margin_bottom: 0
|
1110
1125
|
end
|
1111
1126
|
end
|
1112
1127
|
end
|
@@ -1122,9 +1137,11 @@ module Asciidoctor
|
|
1122
1137
|
add_dest_for_block node if node.id
|
1123
1138
|
theme_fill_and_stroke_block :sidebar, extent if extent
|
1124
1139
|
pad_box @theme.sidebar_padding, node do
|
1125
|
-
|
1126
|
-
|
1127
|
-
|
1140
|
+
tare_first_page_content_stream do
|
1141
|
+
theme_font :sidebar_title do
|
1142
|
+
# QUESTION: should we allow margins of sidebar title to be customized?
|
1143
|
+
ink_prose node.title, align: (@theme.sidebar_title_text_align || @theme.heading_text_align || @base_text_align).to_sym, margin_bottom: @theme.heading_margin_bottom, line_height: (@theme.heading_line_height || @theme.base_line_height)
|
1144
|
+
end
|
1128
1145
|
end if node.title?
|
1129
1146
|
theme_font :sidebar do
|
1130
1147
|
traverse node
|
@@ -1135,13 +1152,15 @@ module Asciidoctor
|
|
1135
1152
|
end
|
1136
1153
|
|
1137
1154
|
def convert_colist node
|
1138
|
-
|
1155
|
+
unless at_page_top? || (self_idx = (siblings = node.parent.blocks).index node) == 0 || !([:listing, :literal].include? siblings[self_idx - 1].context)
|
1156
|
+
margin_top @theme.callout_list_margin_top_after_code
|
1157
|
+
end
|
1139
1158
|
add_dest_for_block node if node.id
|
1140
1159
|
@list_numerals << 1
|
1141
1160
|
last_item = node.items[-1]
|
1142
1161
|
item_spacing = @theme.callout_list_item_spacing || @theme.list_item_spacing
|
1143
1162
|
item_opts = { margin_bottom: item_spacing, normalize_line_height: true }
|
1144
|
-
if (item_align = (
|
1163
|
+
if (item_align = (resolve_text_align_from_role node.roles) || @theme.list_text_align&.to_sym)
|
1145
1164
|
item_opts[:align] = item_align
|
1146
1165
|
end
|
1147
1166
|
theme_font :callout_list do
|
@@ -1163,7 +1182,7 @@ module Asciidoctor
|
|
1163
1182
|
marker_width = rendered_width_of_string %(#{marker = conum_glyph index}x)
|
1164
1183
|
float do
|
1165
1184
|
bounding_box [0, cursor], width: marker_width do
|
1166
|
-
|
1185
|
+
ink_prose marker, align: :center, inline_format: false, margin: 0
|
1167
1186
|
end
|
1168
1187
|
end
|
1169
1188
|
end
|
@@ -1247,7 +1266,7 @@ module Asciidoctor
|
|
1247
1266
|
max_term_width += (term_padding[1] + term_padding[3])
|
1248
1267
|
term_column_width = [max_term_width, bounds.width * 0.5].min
|
1249
1268
|
table table_data, position: :left, cell_style: { border_width: 0 }, column_widths: [term_column_width] do
|
1250
|
-
@pdf.
|
1269
|
+
@pdf.ink_table_caption node if node.title?
|
1251
1270
|
end
|
1252
1271
|
theme_margin :prose, :bottom, (next_enclosed_block actual_node) #unless actual_node.nested?
|
1253
1272
|
when 'qanda'
|
@@ -1257,7 +1276,7 @@ module Asciidoctor
|
|
1257
1276
|
else
|
1258
1277
|
# TODO: check if we're within one line of the bottom of the page
|
1259
1278
|
# and advance to the next page if so (similar to logic for section titles)
|
1260
|
-
|
1279
|
+
ink_caption node, category: :description_list, labeled: false if node.title?
|
1261
1280
|
|
1262
1281
|
term_spacing = @theme.description_list_term_spacing
|
1263
1282
|
term_height = theme_font(:description_list_term) { height_of_typeset_text 'A' }
|
@@ -1269,8 +1288,8 @@ module Asciidoctor
|
|
1269
1288
|
term_font_styles = nil
|
1270
1289
|
end
|
1271
1290
|
terms.each_with_index do |term, idx|
|
1272
|
-
# QUESTION: should we pass down styles in other calls to
|
1273
|
-
|
1291
|
+
# QUESTION: should we pass down styles in other calls to ink_prose
|
1292
|
+
ink_prose term.text, margin_top: (idx > 0 ? term_spacing : 0), margin_bottom: 0, align: :left, normalize_line_height: true, styles: term_font_styles
|
1274
1293
|
end
|
1275
1294
|
end
|
1276
1295
|
indent @theme.description_list_description_indent do
|
@@ -1357,10 +1376,10 @@ module Asciidoctor
|
|
1357
1376
|
def convert_list node
|
1358
1377
|
# TODO: check if we're within one line of the bottom of the page
|
1359
1378
|
# and advance to the next page if so (similar to logic for section titles)
|
1360
|
-
|
1379
|
+
ink_caption node, category: :list, labeled: false if node.title?
|
1361
1380
|
|
1362
1381
|
opts = {}
|
1363
|
-
if (align =
|
1382
|
+
if (align = resolve_text_align_from_role node.roles)
|
1364
1383
|
opts[:align] = align
|
1365
1384
|
elsif node.style == 'bibliography'
|
1366
1385
|
opts[:align] = :left
|
@@ -1454,7 +1473,7 @@ module Asciidoctor
|
|
1454
1473
|
float do
|
1455
1474
|
advance_page if @media == 'prepress' && cursor < marker_height
|
1456
1475
|
flow_bounding_box position: start_position, width: marker_width do
|
1457
|
-
|
1476
|
+
ink_prose marker,
|
1458
1477
|
align: :right,
|
1459
1478
|
character_spacing: -0.5,
|
1460
1479
|
color: marker_style[:font_color],
|
@@ -1482,16 +1501,16 @@ module Asciidoctor
|
|
1482
1501
|
def traverse_list_item node, list_type, opts = {}
|
1483
1502
|
if list_type == :dlist # qanda
|
1484
1503
|
terms, desc = node
|
1485
|
-
terms.each {|term|
|
1504
|
+
terms.each {|term| ink_prose %(<em>#{term.text}</em>), (opts.merge margin_bottom: @theme.description_list_term_spacing) }
|
1486
1505
|
if desc
|
1487
|
-
|
1506
|
+
ink_prose desc.text, (opts.merge hyphenate: true) if desc.text?
|
1488
1507
|
traverse desc
|
1489
1508
|
end
|
1490
1509
|
else
|
1491
1510
|
if (primary_text = node.text).nil_or_empty?
|
1492
|
-
|
1511
|
+
ink_prose DummyText, opts unless node.blocks?
|
1493
1512
|
else
|
1494
|
-
|
1513
|
+
ink_prose primary_text, (opts.merge hyphenate: true)
|
1495
1514
|
end
|
1496
1515
|
traverse node
|
1497
1516
|
end
|
@@ -1553,22 +1572,29 @@ module Asciidoctor
|
|
1553
1572
|
|
1554
1573
|
return on_image_error :missing, node, target, opts unless image_path
|
1555
1574
|
|
1556
|
-
|
1557
|
-
|
1558
|
-
|
1575
|
+
if (float_to = node.attr 'float') && ((BlockFloatNames.include? float_to) ? float_to : (float_to = nil))
|
1576
|
+
alignment = float_to.to_sym
|
1577
|
+
elsif (alignment = node.attr 'align')
|
1578
|
+
alignment = (BlockAlignmentNames.include? alignment) ? alignment.to_sym : :left
|
1579
|
+
else
|
1580
|
+
alignment = (resolve_text_align_from_role node.roles) || @theme.image_align&.to_sym || :left
|
1581
|
+
end
|
1559
1582
|
# TODO: support cover (aka canvas) image layout using "canvas" (or "cover") role
|
1560
1583
|
width = resolve_explicit_width node.attributes, bounds_width: (available_w = bounds.width), support_vw: true, use_fallback: true, constrain_to_bounds: true
|
1561
1584
|
# TODO: add `to_pt page_width` method to ViewportWidth type
|
1562
1585
|
width = (width.to_f / 100) * page_width if ViewportWidth === width
|
1563
1586
|
|
1587
|
+
caption_end = @theme.image_caption_end&.to_sym || :bottom
|
1588
|
+
caption_max_width = @theme.image_caption_max_width
|
1589
|
+
caption_max_width = 'fit-content' if float_to && !(caption_max_width&.start_with? 'fit-content')
|
1564
1590
|
# NOTE: if width is not set explicitly and max-width is fit-content, caption height may not be accurate
|
1565
|
-
caption_h = node.title? ? (
|
1591
|
+
caption_h = node.title? ? (ink_caption node, category: :image, end: caption_end, block_align: alignment, block_width: width, max_width: caption_max_width, dry_run: true, force_top_margin: caption_end == :bottom) : 0
|
1566
1592
|
|
1567
1593
|
align_to_page = node.option? 'align-to-page'
|
1568
1594
|
pinned = opts[:pinned]
|
1569
1595
|
|
1570
1596
|
begin
|
1571
|
-
rendered_w = nil
|
1597
|
+
rendered_h = rendered_w = nil
|
1572
1598
|
span_page_width_if align_to_page do
|
1573
1599
|
if image_format == 'svg'
|
1574
1600
|
if ::Base64 === image_path
|
@@ -1598,14 +1624,14 @@ module Asciidoctor
|
|
1598
1624
|
end
|
1599
1625
|
rendered_w = (svg_obj.resize height: (rendered_h = available_h)).output_width if rendered_h > available_h
|
1600
1626
|
end
|
1601
|
-
image_y = y
|
1602
|
-
image_cursor = cursor
|
1603
1627
|
add_dest_for_block node if node.id
|
1604
1628
|
# NOTE: workaround to fix Prawn not adding fill and stroke commands on page that only has an image;
|
1605
1629
|
# breakage occurs when running content (stamps) are added to page
|
1606
1630
|
update_colors if graphic_state.color_space.empty?
|
1607
|
-
|
1608
|
-
|
1631
|
+
ink_caption node, category: :image, end: :top, block_align: alignment, block_width: rendered_w, max_width: caption_max_width if caption_end == :top && node.title?
|
1632
|
+
image_y = y
|
1633
|
+
image_cursor = cursor
|
1634
|
+
svg_obj.draw # NOTE: cursor advances automatically
|
1609
1635
|
svg_obj.document.warnings.each do |img_warning|
|
1610
1636
|
log :warn, %(problem encountered in image: #{image_path}; #{img_warning})
|
1611
1637
|
end unless scratch?
|
@@ -1629,12 +1655,13 @@ module Asciidoctor
|
|
1629
1655
|
end
|
1630
1656
|
rendered_w, rendered_h = image_info.calc_image_dimensions height: available_h if rendered_h > available_h
|
1631
1657
|
end
|
1632
|
-
image_y = y
|
1633
|
-
image_cursor = cursor
|
1634
1658
|
add_dest_for_block node if node.id
|
1635
1659
|
# NOTE: workaround to fix Prawn not adding fill and stroke commands on page that only has an image;
|
1636
1660
|
# breakage occurs when running content (stamps) are added to page
|
1637
1661
|
update_colors if graphic_state.color_space.empty?
|
1662
|
+
ink_caption node, category: :image, end: :top, block_align: alignment, block_width: rendered_w, max_width: caption_max_width if caption_end == :top && node.title?
|
1663
|
+
image_y = y
|
1664
|
+
image_cursor = cursor
|
1638
1665
|
# NOTE: specify both width and height to avoid recalculation
|
1639
1666
|
embed_image image_obj, image_info, width: rendered_w, height: rendered_h, position: alignment
|
1640
1667
|
draw_image_border image_cursor, rendered_w, rendered_h, alignment unless node.role? && (node.has_role? 'noborder')
|
@@ -1645,16 +1672,40 @@ module Asciidoctor
|
|
1645
1672
|
move_down rendered_h if y == image_y
|
1646
1673
|
end
|
1647
1674
|
end
|
1648
|
-
|
1649
|
-
|
1675
|
+
ink_caption node, category: :image, end: :bottom, block_align: alignment, block_width: rendered_w, max_width: caption_max_width if caption_end == :bottom && node.title?
|
1676
|
+
if !pinned && (block_next = next_enclosed_block node)
|
1677
|
+
if float_to && (supports_float_wrapping? block_next) && rendered_w < bounds.width
|
1678
|
+
init_float_box node, rendered_w, rendered_h + caption_h, float_to
|
1679
|
+
else
|
1680
|
+
theme_margin :block, :bottom, block_next
|
1681
|
+
end
|
1682
|
+
end
|
1650
1683
|
rescue => e
|
1651
1684
|
raise if ::StopIteration === e
|
1652
1685
|
on_image_error :exception, node, target, (opts.merge message: %(could not embed image: #{image_path}; #{e.message}#{::Prawn::Errors::UnsupportedImageType === e && !(defined? ::GMagick::Image) ? '; install prawn-gmagick gem to add support' : ''}))
|
1653
1686
|
end
|
1654
1687
|
end
|
1655
1688
|
|
1689
|
+
def supports_float_wrapping? node
|
1690
|
+
node.context == :paragraph
|
1691
|
+
end
|
1692
|
+
|
1693
|
+
def init_float_box _node, block_width, block_height, float_to
|
1694
|
+
gap = ::Array === (gap = @theme.image_float_gap) ? gap.dup : [gap, gap]
|
1695
|
+
float_w = block_width + (gap[0] ||= 12)
|
1696
|
+
float_h = block_height + (gap[1] ||= 6)
|
1697
|
+
box_l = bounds.left + (float_to == 'right' ? 0 : float_w)
|
1698
|
+
box_t = cursor + block_height
|
1699
|
+
box_w = bounds.width - float_w
|
1700
|
+
box_r = box_l + box_w
|
1701
|
+
box_h = [box_t, float_h].min
|
1702
|
+
box_b = box_t - box_h
|
1703
|
+
move_cursor_to box_t
|
1704
|
+
@float_box = { page: page_number, top: box_t, right: box_r, bottom: box_b, left: box_l, width: box_w, height: box_h, gap: gap }
|
1705
|
+
end
|
1706
|
+
|
1656
1707
|
def draw_image_border top, w, h, alignment
|
1657
|
-
if @theme.image_border_width
|
1708
|
+
if (Array @theme.image_border_width).any? {|it| it&.> 0 } && (@theme.image_border_color || @theme.base_border_color)
|
1658
1709
|
if (@theme.image_border_fit || 'content') == 'auto'
|
1659
1710
|
bb_width = bounds.width
|
1660
1711
|
elsif alignment == :center
|
@@ -1684,10 +1735,10 @@ module Asciidoctor
|
|
1684
1735
|
theme_font :image_alt do
|
1685
1736
|
alignment = (alignment = node.attr 'align') ?
|
1686
1737
|
((BlockAlignmentNames.include? alignment) ? alignment.to_sym : :left) :
|
1687
|
-
(
|
1688
|
-
|
1738
|
+
(resolve_text_align_from_role node.roles) || (@theme.image_align&.to_sym || :left)
|
1739
|
+
ink_prose alt_text_template % alt_text_vars, align: alignment, margin: 0, normalize: false, single_line: true
|
1689
1740
|
end
|
1690
|
-
|
1741
|
+
ink_caption node, category: :image, end: :bottom if node.title?
|
1691
1742
|
theme_margin :block, :bottom, (next_enclosed_block node) unless opts[:pinned]
|
1692
1743
|
nil
|
1693
1744
|
end
|
@@ -1696,8 +1747,8 @@ module Asciidoctor
|
|
1696
1747
|
add_dest_for_block node if node.id
|
1697
1748
|
audio_path = node.media_uri node.attr 'target'
|
1698
1749
|
play_symbol = (node.document.attr? 'icons', 'font') ? %(<font name="fas">#{(icon_font_data 'fas').unicode 'play'}</font>) : RightPointer
|
1699
|
-
|
1700
|
-
|
1750
|
+
ink_prose %(#{play_symbol}#{NoBreakSpace}<a href="#{audio_path}">#{audio_path}</a> <em>(audio)</em>), normalize: false, margin: 0, single_line: true
|
1751
|
+
ink_caption node, labeled: false, end: :bottom if node.title?
|
1701
1752
|
theme_margin :block, :bottom, (next_enclosed_block node)
|
1702
1753
|
end
|
1703
1754
|
|
@@ -1724,8 +1775,8 @@ module Asciidoctor
|
|
1724
1775
|
if poster.nil_or_empty?
|
1725
1776
|
add_dest_for_block node if node.id
|
1726
1777
|
play_symbol = (node.document.attr? 'icons', 'font') ? %(<font name="fas">#{(icon_font_data 'fas').unicode 'play'}</font>) : RightPointer
|
1727
|
-
|
1728
|
-
|
1778
|
+
ink_prose %(#{play_symbol}#{NoBreakSpace}<a href="#{video_path}">#{video_path}</a> <em>(#{type})</em>), normalize: false, margin: 0, single_line: true
|
1779
|
+
ink_caption node, labeled: false, end: :bottom if node.title?
|
1729
1780
|
theme_margin :block, :bottom, (next_enclosed_block node)
|
1730
1781
|
else
|
1731
1782
|
original_attributes = node.attributes.dup
|
@@ -1739,7 +1790,7 @@ module Asciidoctor
|
|
1739
1790
|
end
|
1740
1791
|
|
1741
1792
|
# QUESTION: can we avoid arranging fragments multiple times (conums & autofit) by eagerly preparing arranger?
|
1742
|
-
def
|
1793
|
+
def convert_code node
|
1743
1794
|
extensions = []
|
1744
1795
|
source_chunks = bg_color_override = font_color_override = adjusted_font_size = nil
|
1745
1796
|
theme_font :code do
|
@@ -1886,17 +1937,31 @@ module Asciidoctor
|
|
1886
1937
|
theme_margin :block, :bottom, (next_enclosed_block node)
|
1887
1938
|
end
|
1888
1939
|
|
1889
|
-
alias convert_listing
|
1890
|
-
alias convert_literal
|
1940
|
+
alias convert_listing convert_code
|
1941
|
+
alias convert_literal convert_code
|
1942
|
+
alias convert_listing_or_literal convert_code
|
1891
1943
|
|
1892
1944
|
def convert_pass node
|
1893
|
-
|
1894
|
-
|
1895
|
-
|
1896
|
-
|
1945
|
+
theme_font :code do
|
1946
|
+
typeset_formatted_text [text: (guard_indentation node.content), color: @theme.base_font_color], (calc_line_metrics @base_line_height)
|
1947
|
+
end
|
1948
|
+
theme_margin :block, :bottom, (next_enclosed_block node)
|
1897
1949
|
end
|
1898
1950
|
|
1899
|
-
|
1951
|
+
def convert_stem node
|
1952
|
+
arrange_block node do |extent|
|
1953
|
+
add_dest_for_block node if node.id
|
1954
|
+
tare_first_page_content_stream { theme_fill_and_stroke_block :code, extent, caption_node: node }
|
1955
|
+
pad_box @theme.code_padding, node do
|
1956
|
+
theme_font :code do
|
1957
|
+
typeset_formatted_text [text: (guard_indentation node.content), color: @font_color],
|
1958
|
+
(calc_line_metrics @base_line_height),
|
1959
|
+
bottom_gutter: @bottom_gutters[-1][node]
|
1960
|
+
end
|
1961
|
+
end
|
1962
|
+
end
|
1963
|
+
theme_margin :block, :bottom, (next_enclosed_block node)
|
1964
|
+
end
|
1900
1965
|
|
1901
1966
|
# Extract callout marks from string, indexed by 0-based line number
|
1902
1967
|
# Return an Array with the processed string as the first argument
|
@@ -2024,6 +2089,7 @@ module Asciidoctor
|
|
2024
2089
|
|
2025
2090
|
base_header_cell_data = nil
|
2026
2091
|
header_cell_line_metrics = nil
|
2092
|
+
body_cell_padding = expand_padding_value theme.table_cell_padding
|
2027
2093
|
|
2028
2094
|
table_data = []
|
2029
2095
|
theme_font :table do
|
@@ -2038,7 +2104,7 @@ module Asciidoctor
|
|
2038
2104
|
table_header_size = head_rows.size
|
2039
2105
|
head_font_info = font_info
|
2040
2106
|
head_line_metrics = calc_line_metrics theme.table_head_line_height || theme.table_cell_line_height || @base_line_height
|
2041
|
-
head_cell_padding =
|
2107
|
+
head_cell_padding = ((head_cell_padding = theme.table_head_cell_padding) ? (expand_padding_value head_cell_padding) : body_cell_padding).dup
|
2042
2108
|
head_cell_padding[0] += head_line_metrics.padding_top
|
2043
2109
|
head_cell_padding[2] += head_line_metrics.padding_bottom
|
2044
2110
|
# QUESTION: why doesn't text transform inherit from table?
|
@@ -2077,7 +2143,6 @@ module Asciidoctor
|
|
2077
2143
|
text_color: @font_color,
|
2078
2144
|
}
|
2079
2145
|
body_cell_line_metrics = calc_line_metrics (theme.table_cell_line_height || @base_line_height)
|
2080
|
-
body_cell_padding = expand_padding_value theme.table_cell_padding
|
2081
2146
|
(body_rows + node.rows[:foot]).each do |row|
|
2082
2147
|
table_data << (row.map do |cell|
|
2083
2148
|
cell_data = base_cell_data.merge \
|
@@ -2144,7 +2209,7 @@ module Asciidoctor
|
|
2144
2209
|
end
|
2145
2210
|
# NOTE: line metrics get applied when AsciiDoc content is converted
|
2146
2211
|
cell_line_metrics = nil
|
2147
|
-
asciidoc_cell = ::Prawn::Table::Cell::AsciiDoc.new self, (cell_data.merge content: cell.inner_document, padding: body_cell_padding
|
2212
|
+
asciidoc_cell = ::Prawn::Table::Cell::AsciiDoc.new self, (cell_data.merge content: cell.inner_document, padding: body_cell_padding)
|
2148
2213
|
cell_data = { content: asciidoc_cell }
|
2149
2214
|
end
|
2150
2215
|
if cell_line_metrics
|
@@ -2190,17 +2255,17 @@ module Asciidoctor
|
|
2190
2255
|
|
2191
2256
|
rect_side_names = [:top, :right, :bottom, :left]
|
2192
2257
|
grid_axis_names = [:rows, :cols]
|
2193
|
-
border_color = (rect_side_names.zip expand_rect_values theme.table_border_color,
|
2258
|
+
border_color = (rect_side_names.zip expand_rect_values theme.table_border_color, 'transparent').to_h
|
2194
2259
|
border_style = (rect_side_names.zip (expand_rect_values theme.table_border_style, :solid).map(&:to_sym)).to_h
|
2195
2260
|
border_width = (rect_side_names.zip expand_rect_values theme.table_border_width, 0).to_h
|
2196
2261
|
grid_color = (grid_axis_names.zip expand_grid_values (theme.table_grid_color || [border_color[:top], border_color[:left]]), 'transparent').to_h
|
2197
2262
|
grid_style = (grid_axis_names.zip (expand_grid_values (theme.table_grid_style || [border_style[:top], border_style[:left]]), :solid).map(&:to_sym)).to_h
|
2198
|
-
grid_width = (grid_axis_names.zip expand_grid_values theme.table_grid_width, 0).to_h
|
2263
|
+
grid_width = (grid_axis_names.zip expand_grid_values (theme.table_grid_width || [border_width[:top], border_width[:left]]), 0).to_h
|
2199
2264
|
|
2200
2265
|
if table_header_size
|
2201
2266
|
head_border_bottom_color = theme.table_head_border_bottom_color || grid_color[:rows]
|
2202
2267
|
head_border_bottom_style = theme.table_head_border_bottom_style&.to_sym || grid_style[:rows]
|
2203
|
-
head_border_bottom_width = theme.table_head_border_bottom_width || grid_width[:rows]
|
2268
|
+
head_border_bottom_width = theme.table_head_border_bottom_width || (grid_width[:rows] * 2.5)
|
2204
2269
|
end
|
2205
2270
|
|
2206
2271
|
case (grid = node.attr 'grid', 'all', 'table-grid')
|
@@ -2241,7 +2306,7 @@ module Asciidoctor
|
|
2241
2306
|
alignment = theme.table_align&.to_sym || :left
|
2242
2307
|
end
|
2243
2308
|
|
2244
|
-
|
2309
|
+
caption_end = theme.table_caption_end&.to_sym || :top
|
2245
2310
|
caption_max_width = theme.table_caption_max_width || 'fit-content'
|
2246
2311
|
|
2247
2312
|
table_settings = {
|
@@ -2270,7 +2335,7 @@ module Asciidoctor
|
|
2270
2335
|
table table_data, table_settings do
|
2271
2336
|
# NOTE: call width to capture resolved table width
|
2272
2337
|
table_width = width
|
2273
|
-
@pdf.
|
2338
|
+
@pdf.ink_table_caption node, alignment, table_width, caption_max_width if node.title? && caption_end == :top
|
2274
2339
|
# NOTE: align using padding instead of bounding_box as prawn-table does
|
2275
2340
|
# using a bounding_box across pages mangles the margin box of subsequent pages
|
2276
2341
|
if alignment != :left && table_width != (this_bounds = @pdf.bounds).width
|
@@ -2343,20 +2408,23 @@ module Asciidoctor
|
|
2343
2408
|
bounds.subtract_left_padding left_padding
|
2344
2409
|
bounds.subtract_right_padding right_padding if right_padding
|
2345
2410
|
end
|
2346
|
-
|
2411
|
+
ink_table_caption node, alignment, table_width, caption_max_width, caption_end if node.title? && caption_end == :bottom
|
2347
2412
|
theme_margin :block, :bottom, (next_enclosed_block node)
|
2348
2413
|
rescue ::Prawn::Errors::CannotFit
|
2349
2414
|
log :error, (message_with_context 'cannot fit contents of table cell into specified column width', source_location: node.source_location)
|
2350
2415
|
end
|
2351
2416
|
|
2352
2417
|
def convert_thematic_break node
|
2353
|
-
|
2354
|
-
|
2355
|
-
|
2418
|
+
pad_box @theme.thematic_break_padding || [@theme.thematic_break_margin_top, 0] do
|
2419
|
+
stroke_horizontal_rule @theme.thematic_break_border_color,
|
2420
|
+
line_width: @theme.thematic_break_border_width,
|
2421
|
+
line_style: (@theme.thematic_break_border_style&.to_sym || :solid)
|
2422
|
+
end
|
2423
|
+
conceal_page_top { theme_margin :block, :bottom, (next_enclosed_block node) }
|
2356
2424
|
end
|
2357
2425
|
|
2358
2426
|
def convert_toc node, opts = {}
|
2359
|
-
# NOTE: only allow document to have a single toc
|
2427
|
+
# NOTE: only allow document to have a single managed toc
|
2360
2428
|
return if @toc_extent
|
2361
2429
|
is_macro = (placement = opts[:placement] || 'macro') == 'macro'
|
2362
2430
|
if ((doc = node.document).attr? 'toc-placement', placement) && (doc.attr? 'toc') && doc.sections?
|
@@ -2365,11 +2433,11 @@ module Asciidoctor
|
|
2365
2433
|
start_new_page if @ppbook && verso_page? && !(is_macro && (node.option? 'nonfacing'))
|
2366
2434
|
end
|
2367
2435
|
add_dest_for_block node, id: (node.id || 'toc') if is_macro
|
2368
|
-
allocate_toc doc, (doc.attr 'toclevels', 2).to_i, cursor, (title_page_on = is_book || (doc.attr? 'title-page'))
|
2369
|
-
@index.start_page_number =
|
2436
|
+
toc_extent = @toc_extent = allocate_toc doc, (doc.attr 'toclevels', 2).to_i, cursor, (title_page_on = is_book || (doc.attr? 'title-page'))
|
2437
|
+
@index.start_page_number = toc_extent.to.page + 1 if title_page_on && @theme.page_numbering_start_at == 'after-toc'
|
2370
2438
|
if is_macro
|
2371
|
-
@disable_running_content[:header] +=
|
2372
|
-
@disable_running_content[:footer] +=
|
2439
|
+
@disable_running_content[:header] += toc_extent.page_range if node.option? 'noheader'
|
2440
|
+
@disable_running_content[:footer] += toc_extent.page_range if node.option? 'nofooter'
|
2373
2441
|
end
|
2374
2442
|
end
|
2375
2443
|
nil
|
@@ -2387,7 +2455,7 @@ module Asciidoctor
|
|
2387
2455
|
|
2388
2456
|
if at_page_top?
|
2389
2457
|
if page_layout && page_layout != page.layout && page.empty?
|
2390
|
-
|
2458
|
+
delete_current_page
|
2391
2459
|
advance_page layout: page_layout
|
2392
2460
|
end
|
2393
2461
|
elsif page_layout
|
@@ -2397,48 +2465,50 @@ module Asciidoctor
|
|
2397
2465
|
end
|
2398
2466
|
end
|
2399
2467
|
|
2400
|
-
def convert_index_section
|
2468
|
+
def convert_index_section node
|
2401
2469
|
space_needed_for_category = @theme.description_list_term_spacing + (2 * (height_of_typeset_text 'A'))
|
2402
|
-
|
2403
|
-
|
2404
|
-
super(*args)
|
2405
|
-
@document.bounds = @parent = @document.margin_box if @current_column == 0 && @reflow_margins
|
2406
|
-
end
|
2470
|
+
pagenum_sequence_style = node.document.attr 'index-pagenum-sequence-style'
|
2471
|
+
column_box [0, cursor], columns: @theme.index_columns, width: bounds.width, reflow_margins: true, spacer: @theme.index_column_gap do
|
2407
2472
|
@index.categories.each do |category|
|
2408
|
-
|
2409
|
-
|
2410
|
-
inscribe_prose category.name,
|
2473
|
+
bounds.move_past_bottom if space_needed_for_category > cursor
|
2474
|
+
ink_prose category.name,
|
2411
2475
|
align: :left,
|
2412
2476
|
inline_format: false,
|
2413
2477
|
margin_bottom: @theme.description_list_term_spacing,
|
2414
|
-
style: @theme.description_list_term_font_style
|
2415
|
-
category.terms.each {|term| convert_index_list_item term }
|
2416
|
-
|
2417
|
-
if @theme.prose_margin_bottom > y - reference_bounds.absolute_bottom
|
2418
|
-
bounds.move_past_bottom
|
2419
|
-
else
|
2420
|
-
move_down @theme.prose_margin_bottom
|
2421
|
-
end
|
2478
|
+
style: @theme.description_list_term_font_style&.to_sym
|
2479
|
+
category.terms.each {|term| convert_index_list_item term, pagenum_sequence_style }
|
2480
|
+
@theme.prose_margin_bottom > cursor ? bounds.move_past_bottom : (move_down @theme.prose_margin_bottom)
|
2422
2481
|
end
|
2423
2482
|
end
|
2424
2483
|
nil
|
2425
2484
|
end
|
2426
2485
|
|
2427
|
-
def convert_index_list_item term
|
2486
|
+
def convert_index_list_item term, pagenum_sequence_style = nil
|
2428
2487
|
text = escape_xml term.name
|
2429
2488
|
unless term.container?
|
2430
2489
|
if @media == 'screen'
|
2431
|
-
|
2490
|
+
case pagenum_sequence_style
|
2491
|
+
when 'page'
|
2492
|
+
pagenums = term.dests.uniq {|dest| dest[:page] }.map {|dest| %(<a anchor="#{dest[:anchor]}">#{dest[:page]}</a>) }
|
2493
|
+
when 'range'
|
2494
|
+
first_anchor_per_page = term.dests.each_with_object({}) {|dest, accum| accum[dest[:page]] ||= dest[:anchor] }
|
2495
|
+
pagenums = (consolidate_ranges first_anchor_per_page.keys).map do |range|
|
2496
|
+
anchor = first_anchor_per_page[(range.include? '-') ? (range.partition '-')[0] : range]
|
2497
|
+
%(<a anchor="#{anchor}">#{range}</a>)
|
2498
|
+
end
|
2499
|
+
else # term
|
2500
|
+
pagenums = term.dests.map {|dest| %(<a anchor="#{dest[:anchor]}">#{dest[:page]}</a>) }
|
2501
|
+
end
|
2432
2502
|
else
|
2433
|
-
pagenums = consolidate_ranges term.dests.
|
2503
|
+
pagenums = consolidate_ranges term.dests.map {|dest| dest[:page] }.uniq
|
2434
2504
|
end
|
2435
2505
|
text = %(#{text}, #{pagenums.join ', '})
|
2436
2506
|
end
|
2437
2507
|
subterm_indent = @theme.description_list_description_indent
|
2438
|
-
|
2508
|
+
ink_prose text, align: :left, margin: 0, hanging_indent: subterm_indent * 2
|
2439
2509
|
indent subterm_indent do
|
2440
2510
|
term.subterms.each do |subterm|
|
2441
|
-
convert_index_list_item subterm
|
2511
|
+
convert_index_list_item subterm, pagenum_sequence_style
|
2442
2512
|
end
|
2443
2513
|
end unless term.leaf?
|
2444
2514
|
end
|
@@ -2723,7 +2793,7 @@ module Asciidoctor
|
|
2723
2793
|
end
|
2724
2794
|
|
2725
2795
|
# Returns a Boolean indicating whether the title page was created
|
2726
|
-
def
|
2796
|
+
def ink_title_page doc
|
2727
2797
|
return unless doc.header? && !doc.notitle && @theme.title_page != false
|
2728
2798
|
|
2729
2799
|
# NOTE: a new page may have already been started at this point, so decide what to do with it
|
@@ -2799,7 +2869,7 @@ module Asciidoctor
|
|
2799
2869
|
move_down @theme.title_page_title_margin_top || 0
|
2800
2870
|
indent (@theme.title_page_title_margin_left || 0), (@theme.title_page_title_margin_right || 0) do
|
2801
2871
|
theme_font :title_page_title do
|
2802
|
-
|
2872
|
+
ink_prose doctitle.main, align: title_align, margin: 0
|
2803
2873
|
end
|
2804
2874
|
end
|
2805
2875
|
move_down @theme.title_page_title_margin_bottom || 0
|
@@ -2808,7 +2878,7 @@ module Asciidoctor
|
|
2808
2878
|
move_down @theme.title_page_subtitle_margin_top || 0
|
2809
2879
|
indent (@theme.title_page_subtitle_margin_left || 0), (@theme.title_page_subtitle_margin_right || 0) do
|
2810
2880
|
theme_font :title_page_subtitle do
|
2811
|
-
|
2881
|
+
ink_prose subtitle, align: title_align, margin: 0
|
2812
2882
|
end
|
2813
2883
|
end
|
2814
2884
|
move_down @theme.title_page_subtitle_margin_bottom || 0
|
@@ -2833,7 +2903,7 @@ module Asciidoctor
|
|
2833
2903
|
end
|
2834
2904
|
end.join @theme.title_page_authors_delimiter
|
2835
2905
|
theme_font :title_page_authors do
|
2836
|
-
|
2906
|
+
ink_prose authors, align: title_align, margin: 0, normalize: true
|
2837
2907
|
end
|
2838
2908
|
end
|
2839
2909
|
move_down @theme.title_page_authors_margin_bottom || 0
|
@@ -2846,18 +2916,18 @@ module Asciidoctor
|
|
2846
2916
|
end
|
2847
2917
|
indent (@theme.title_page_revision_margin_left || 0), (@theme.title_page_revision_margin_right || 0) do
|
2848
2918
|
theme_font :title_page_revision do
|
2849
|
-
|
2919
|
+
ink_prose revision_text, align: title_align, margin: 0, normalize: false
|
2850
2920
|
end
|
2851
2921
|
end
|
2852
2922
|
move_down @theme.title_page_revision_margin_bottom || 0
|
2853
2923
|
end
|
2854
2924
|
end
|
2855
2925
|
|
2856
|
-
|
2926
|
+
ink_prose DummyText, margin: 0, line_height: 1, normalize: false if page.empty?
|
2857
2927
|
true
|
2858
2928
|
end
|
2859
2929
|
|
2860
|
-
def
|
2930
|
+
def ink_cover_page doc, face
|
2861
2931
|
image_path, image_opts = resolve_background_image doc, @theme, %(#{face}-cover-image), theme_key: %(cover_#{face}_image).to_sym, symbolic_paths: ['', '~']
|
2862
2932
|
if image_path
|
2863
2933
|
if image_path.empty?
|
@@ -2903,92 +2973,178 @@ module Asciidoctor
|
|
2903
2973
|
|
2904
2974
|
def start_new_chapter chapter
|
2905
2975
|
start_new_page unless at_page_top?
|
2906
|
-
# TODO: must call update_colors before advancing to next page if start_new_page is called in
|
2976
|
+
# TODO: must call update_colors before advancing to next page if start_new_page is called in ink_chapter_title
|
2907
2977
|
start_new_page if @ppbook && verso_page? && !(chapter.option? 'nonfacing')
|
2908
2978
|
end
|
2909
2979
|
|
2910
2980
|
alias start_new_part start_new_chapter
|
2911
2981
|
|
2912
|
-
|
2913
|
-
|
2982
|
+
# Position the cursor for where to ink the specified section title or discrete heading node.
|
2983
|
+
#
|
2984
|
+
# This method computes whether there is enough room on the page to prevent the specified node
|
2985
|
+
# from being orphaned. If there is not enough room, the method will advance the cursor to
|
2986
|
+
# the next page. This method is not called if the cursor is already at the top of the page or
|
2987
|
+
# whether this node has no node that follows it in document order.
|
2988
|
+
def arrange_heading node, title, opts
|
2989
|
+
if node.option? 'breakable'
|
2914
2990
|
orphaned = nil
|
2915
2991
|
dry_run single_page: true do
|
2916
2992
|
start_page = page
|
2917
2993
|
theme_font :heading, level: opts[:level] do
|
2918
2994
|
if opts[:part]
|
2919
|
-
|
2995
|
+
ink_part_title node, title, opts
|
2920
2996
|
elsif opts[:chapterlike]
|
2921
|
-
|
2997
|
+
ink_chapter_title node, title, opts
|
2922
2998
|
else
|
2923
|
-
|
2999
|
+
ink_general_heading node, title, opts
|
2924
3000
|
end
|
2925
3001
|
end
|
2926
3002
|
if page == start_page
|
2927
3003
|
page.tare_content_stream
|
2928
|
-
orphaned = stop_if_first_page_empty
|
3004
|
+
orphaned = stop_if_first_page_empty do
|
3005
|
+
node.context == :section ? (traverse node) : (convert (siblings = node.parent.blocks)[(siblings.index node) + 1])
|
3006
|
+
end
|
2929
3007
|
end
|
2930
3008
|
end
|
2931
3009
|
start_new_page if orphaned
|
2932
3010
|
else
|
2933
3011
|
theme_font :heading, level: (hlevel = opts[:level]) do
|
2934
|
-
|
2935
|
-
|
2936
|
-
|
2937
|
-
|
2938
|
-
|
2939
|
-
|
2940
|
-
|
3012
|
+
h_padding_t, h_padding_r, h_padding_b, h_padding_l = expand_padding_value @theme[%(heading_h#{hlevel}_padding)]
|
3013
|
+
h_fits = indent h_padding_l, h_padding_r do
|
3014
|
+
# FIXME: this height doesn't account for impact of text transform or inline formatting
|
3015
|
+
heading_h = (height_of_typeset_text title) +
|
3016
|
+
(@theme[%(heading_h#{hlevel}_margin_top)] || @theme.heading_margin_top) +
|
3017
|
+
(@theme[%(heading_h#{hlevel}_margin_bottom)] || @theme.heading_margin_bottom) + h_padding_t + h_padding_b
|
3018
|
+
if (min_height_after = @theme.heading_min_height_after) &&
|
3019
|
+
(node.context == :section ? node.blocks? : node != node.parent.blocks[-1])
|
3020
|
+
heading_h += min_height_after
|
3021
|
+
end
|
3022
|
+
cursor >= heading_h
|
3023
|
+
end
|
3024
|
+
start_new_page unless h_fits
|
2941
3025
|
end
|
2942
3026
|
end
|
2943
3027
|
nil
|
2944
3028
|
end
|
2945
3029
|
|
2946
|
-
def
|
2947
|
-
|
3030
|
+
def ink_chapter_title node, title, opts = {}
|
3031
|
+
ink_general_heading node, title, (opts.merge outdent: true)
|
2948
3032
|
end
|
2949
3033
|
|
2950
|
-
alias
|
3034
|
+
alias ink_part_title ink_chapter_title
|
2951
3035
|
|
2952
|
-
def
|
2953
|
-
|
3036
|
+
def ink_general_heading _node, title, opts = {}
|
3037
|
+
ink_heading title, opts
|
2954
3038
|
end
|
2955
3039
|
|
2956
|
-
# NOTE:
|
2957
|
-
def
|
2958
|
-
|
3040
|
+
# NOTE: ink_heading doesn't set the theme font because it's used for various types of headings
|
3041
|
+
def ink_heading string, opts = {}
|
3042
|
+
if (h_level = opts[:level])
|
3043
|
+
h_category = %(heading_h#{h_level})
|
3044
|
+
end
|
2959
3045
|
unless (top_margin = (margin = (opts.delete :margin)) || (opts.delete :margin_top))
|
2960
3046
|
if at_page_top?
|
2961
|
-
if
|
3047
|
+
if h_category && (top_margin = @theme[%(#{h_category}_margin_page_top)] || @theme.heading_margin_page_top) > 0
|
2962
3048
|
move_down top_margin
|
2963
3049
|
end
|
2964
3050
|
top_margin = 0
|
2965
3051
|
else
|
2966
|
-
top_margin = (
|
3052
|
+
top_margin = (h_category ? @theme[%(#{h_category}_margin_top)] : nil) || @theme.heading_margin_top
|
2967
3053
|
end
|
2968
3054
|
end
|
2969
|
-
bot_margin = margin || (opts.delete :margin_bottom) || (
|
3055
|
+
bot_margin = margin || (opts.delete :margin_bottom) || (h_category ? @theme[%(#{h_category}_margin_bottom)] : nil) || @theme.heading_margin_bottom
|
2970
3056
|
if (transform = resolve_text_transform opts)
|
2971
3057
|
string = transform_text string, transform
|
2972
3058
|
end
|
2973
3059
|
outdent_section opts.delete :outdent do
|
2974
3060
|
margin_top top_margin
|
2975
|
-
|
2976
|
-
|
2977
|
-
|
2978
|
-
|
2979
|
-
|
3061
|
+
start_cursor = cursor
|
3062
|
+
start_page_number = page_number
|
3063
|
+
pad_box h_category ? @theme[%(#{h_category}_padding)] : nil do
|
3064
|
+
# QUESTION: should we move inherited styles to typeset_text?
|
3065
|
+
if (inherited = apply_text_decoration font_styles, :heading, h_level).empty?
|
3066
|
+
inline_format_opts = true
|
3067
|
+
else
|
3068
|
+
inline_format_opts = [{ inherited: inherited }]
|
3069
|
+
end
|
3070
|
+
typeset_text string, (calc_line_metrics (opts.delete :line_height) || @base_line_height), {
|
3071
|
+
color: @font_color,
|
3072
|
+
inline_format: inline_format_opts,
|
3073
|
+
align: @base_text_align.to_sym,
|
3074
|
+
}.merge(opts)
|
3075
|
+
end
|
3076
|
+
if h_category && @theme[%(#{h_category}_border_width)] &&
|
3077
|
+
(@theme[%(#{h_category}_border_color)] || @theme.base_border_color) && page_number == start_page_number
|
3078
|
+
float do
|
3079
|
+
bounding_box [bounds.left, start_cursor], width: bounds.width, height: start_cursor - cursor do
|
3080
|
+
theme_fill_and_stroke_bounds h_category
|
3081
|
+
end
|
3082
|
+
end
|
2980
3083
|
end
|
2981
|
-
typeset_text string, (calc_line_metrics (opts.delete :line_height) || @base_line_height), {
|
2982
|
-
color: @font_color,
|
2983
|
-
inline_format: inline_format_opts,
|
2984
|
-
align: @base_text_align.to_sym,
|
2985
|
-
}.merge(opts)
|
2986
3084
|
margin_bottom bot_margin
|
2987
3085
|
end
|
2988
3086
|
end
|
2989
3087
|
|
3088
|
+
# private
|
3089
|
+
def ink_paragraph_in_float_box node, float_box, prose_opts, role_keys, block_next, insert_margin_bottom
|
3090
|
+
@float_box = para_font_descender = para_font_size = end_cursor = nil
|
3091
|
+
if role_keys
|
3092
|
+
line_metrics = theme_font_cascade role_keys do
|
3093
|
+
para_font_descender = font.descender
|
3094
|
+
para_font_size = font_size
|
3095
|
+
calc_line_metrics @base_line_height
|
3096
|
+
end
|
3097
|
+
else
|
3098
|
+
para_font_descender = font.descender
|
3099
|
+
para_font_size = font_size
|
3100
|
+
line_metrics = calc_line_metrics @base_line_height
|
3101
|
+
end
|
3102
|
+
# allocate the space of at least one empty line below block
|
3103
|
+
line_height_length = line_metrics.height + line_metrics.leading + line_metrics.padding_top
|
3104
|
+
start_page_number = float_box[:page]
|
3105
|
+
start_cursor = cursor
|
3106
|
+
block_bottom = (float_box_bottom = float_box[:bottom]) + float_box[:gap][1]
|
3107
|
+
# use :at to incorporate padding top from line metrics
|
3108
|
+
# use :final_gap to incorporate padding bottom from line metrics
|
3109
|
+
# use :draw_text_callback to track end cursor (requires applying :final_gap to result manually)
|
3110
|
+
prose_opts.update \
|
3111
|
+
at: [float_box[:left], start_cursor - line_metrics.padding_top],
|
3112
|
+
width: float_box[:width],
|
3113
|
+
height: [cursor, float_box[:height] - (float_box[:top] - start_cursor) + line_height_length].min,
|
3114
|
+
final_gap: para_font_descender + line_metrics.padding_bottom,
|
3115
|
+
draw_text_callback: (proc do |text, opts|
|
3116
|
+
draw_text! text, opts
|
3117
|
+
end_cursor = opts[:at][1] # does not include :final_gap value
|
3118
|
+
end)
|
3119
|
+
overflow_text = role_keys ?
|
3120
|
+
theme_font_cascade(role_keys) { ink_prose node.content, prose_opts } :
|
3121
|
+
(ink_prose node.content, prose_opts)
|
3122
|
+
move_cursor_to end_cursor -= prose_opts[:final_gap] if end_cursor # ink_prose with :height does not move cursor
|
3123
|
+
if overflow_text.empty?
|
3124
|
+
if block_next && (supports_float_wrapping? block_next)
|
3125
|
+
insert_margin_bottom.call
|
3126
|
+
@float_box = float_box if page_number == start_page_number && cursor > start_cursor - prose_opts[:height]
|
3127
|
+
elsif end_cursor > block_bottom
|
3128
|
+
move_cursor_to block_bottom
|
3129
|
+
theme_margin :block, :bottom, block_next
|
3130
|
+
else
|
3131
|
+
insert_margin_bottom.call
|
3132
|
+
end
|
3133
|
+
else
|
3134
|
+
overflow_prose_opts = { align: prose_opts[:align] || @base_text_align.to_sym }
|
3135
|
+
unless end_cursor
|
3136
|
+
overflow_prose_opts[:indent_paragraphs] = prose_opts[:indent_paragraphs]
|
3137
|
+
move_cursor_to float_box_bottom if start_cursor > float_box_bottom
|
3138
|
+
end
|
3139
|
+
role_keys ?
|
3140
|
+
theme_font_cascade(role_keys) { typeset_formatted_text overflow_text, line_metrics, overflow_prose_opts } :
|
3141
|
+
(typeset_formatted_text overflow_text, line_metrics, overflow_prose_opts)
|
3142
|
+
insert_margin_bottom.call
|
3143
|
+
end
|
3144
|
+
end
|
3145
|
+
|
2990
3146
|
# NOTE: inline_format is true by default
|
2991
|
-
def
|
3147
|
+
def ink_prose string, opts = {}
|
2992
3148
|
top_margin = (margin = (opts.delete :margin)) || (opts.delete :margin_top) || 0
|
2993
3149
|
bot_margin = margin || (opts.delete :margin_bottom) || @theme.prose_margin_bottom
|
2994
3150
|
if (transform = resolve_text_transform opts)
|
@@ -3009,12 +3165,13 @@ module Asciidoctor
|
|
3009
3165
|
text_decoration_width: (opts.delete :text_decoration_width),
|
3010
3166
|
}.compact
|
3011
3167
|
end
|
3012
|
-
typeset_text string, (calc_line_metrics (opts.delete :line_height) || @base_line_height), {
|
3168
|
+
result = typeset_text string, (calc_line_metrics (opts.delete :line_height) || @base_line_height), {
|
3013
3169
|
color: @font_color,
|
3014
3170
|
inline_format: [inline_format_opts],
|
3015
3171
|
align: @base_text_align.to_sym,
|
3016
3172
|
}.merge(opts)
|
3017
3173
|
margin_bottom bot_margin
|
3174
|
+
result
|
3018
3175
|
end
|
3019
3176
|
|
3020
3177
|
def generate_manname_section node
|
@@ -3035,13 +3192,14 @@ module Asciidoctor
|
|
3035
3192
|
# The subject argument can either be a String or an AbstractNode. If
|
3036
3193
|
# subject is an AbstractNode, only call this method if the node has a
|
3037
3194
|
# title (i.e., subject.title? returns true).
|
3038
|
-
def
|
3195
|
+
def ink_caption subject, opts = {}
|
3039
3196
|
if opts.delete :dry_run
|
3040
|
-
force_top_margin = !at_page_top? if (force_top_margin = opts.delete :force_top_margin).nil?
|
3041
3197
|
return (dry_run keep_together: true, single_page: :enforce do
|
3042
|
-
|
3043
|
-
|
3044
|
-
|
3198
|
+
if opts.delete :force_top_margin
|
3199
|
+
conceal_page_top { ink_caption subject, opts }
|
3200
|
+
else
|
3201
|
+
ink_caption subject, opts
|
3202
|
+
end
|
3045
3203
|
end).single_page_height
|
3046
3204
|
end
|
3047
3205
|
if ::Asciidoctor::AbstractBlock === subject
|
@@ -3069,11 +3227,22 @@ module Asciidoctor
|
|
3069
3227
|
if (max_width = opts.delete :max_width) && max_width != 'none'
|
3070
3228
|
if ::String === max_width
|
3071
3229
|
if max_width.start_with? 'fit-content'
|
3072
|
-
|
3073
|
-
|
3074
|
-
|
3075
|
-
|
3230
|
+
block_width ||= container_width
|
3231
|
+
unless max_width.end_with? 't', '()'
|
3232
|
+
max_width = block_width * (max_width.slice 12, max_width.length - 1).to_f / 100.0
|
3233
|
+
if (caption_width_delta = block_width - max_width) > 0
|
3234
|
+
case align
|
3235
|
+
when :right
|
3236
|
+
indent_by[0] += caption_width_delta
|
3237
|
+
when :center
|
3238
|
+
indent_by[0] += caption_width_delta * 0.5
|
3239
|
+
indent_by[1] += caption_width_delta * 0.5
|
3240
|
+
else # :left, nil
|
3241
|
+
indent_by[1] += caption_width_delta
|
3242
|
+
end
|
3243
|
+
end
|
3076
3244
|
end
|
3245
|
+
max_width = block_width
|
3077
3246
|
elsif max_width.end_with? '%'
|
3078
3247
|
max_width = [max_width.to_f / 100 * bounds.width, bounds.width].min
|
3079
3248
|
block_align = align
|
@@ -3088,16 +3257,17 @@ module Asciidoctor
|
|
3088
3257
|
if (remainder = container_width - max_width) > 0
|
3089
3258
|
case block_align
|
3090
3259
|
when :right
|
3091
|
-
indent_by
|
3260
|
+
indent_by[0] += remainder
|
3092
3261
|
when :center
|
3093
|
-
indent_by
|
3262
|
+
indent_by[0] += remainder * 0.5
|
3263
|
+
indent_by[1] += remainder * 0.5
|
3094
3264
|
else # :left, nil
|
3095
|
-
indent_by
|
3265
|
+
indent_by[1] += remainder
|
3096
3266
|
end
|
3097
3267
|
end
|
3098
3268
|
end
|
3099
3269
|
theme_font_cascade [:caption, category_caption] do
|
3100
|
-
if ((opts.delete :side) || :top) == :top
|
3270
|
+
if ((opts.delete :end) || (opts.delete :side) || :top) == :top
|
3101
3271
|
margin = { top: caption_margin_outside, bottom: caption_margin_inside }
|
3102
3272
|
else
|
3103
3273
|
margin = { top: caption_margin_inside, bottom: caption_margin_outside }
|
@@ -3111,7 +3281,7 @@ module Asciidoctor
|
|
3111
3281
|
float { bounding_box(fill_at, width: container_width, height: caption_height) { fill_bounds bg_color } }
|
3112
3282
|
end
|
3113
3283
|
indent(*indent_by) do
|
3114
|
-
|
3284
|
+
ink_prose string, ({
|
3115
3285
|
margin_top: margin[:top],
|
3116
3286
|
margin_bottom: margin[:bottom],
|
3117
3287
|
align: text_align,
|
@@ -3125,16 +3295,22 @@ module Asciidoctor
|
|
3125
3295
|
end
|
3126
3296
|
|
3127
3297
|
# Render the caption for a table and return the height of the rendered content
|
3128
|
-
def
|
3129
|
-
|
3298
|
+
def ink_table_caption node, table_alignment = :left, table_width = nil, max_width = nil, end_ = :top
|
3299
|
+
ink_caption node, category: :table, end: end_, block_align: table_alignment, block_width: table_width, max_width: max_width
|
3130
3300
|
end
|
3131
3301
|
|
3132
3302
|
def allocate_toc doc, toc_num_levels, toc_start_cursor, title_page_on
|
3133
|
-
|
3303
|
+
toc_start_page_number = page_number
|
3304
|
+
to_page = nil
|
3134
3305
|
extent = dry_run onto: self do
|
3135
|
-
|
3306
|
+
to_page = (ink_toc doc, toc_num_levels, toc_start_page_number, toc_start_cursor).end
|
3136
3307
|
margin_bottom @theme.block_margin_bottom unless title_page_on
|
3137
3308
|
end
|
3309
|
+
# NOTE: patch for custom converters that allocate extra TOC pages without actually creating them
|
3310
|
+
if to_page > extent.to.page
|
3311
|
+
extent.to.page = to_page
|
3312
|
+
extent.to.cursor = bounds.height
|
3313
|
+
end
|
3138
3314
|
# NOTE: reserve pages for the toc; leaves cursor on page after last page in toc
|
3139
3315
|
if title_page_on
|
3140
3316
|
extent.each_page { start_new_page }
|
@@ -3142,18 +3318,22 @@ module Asciidoctor
|
|
3142
3318
|
extent.each_page {|first_page| start_new_page unless first_page }
|
3143
3319
|
move_cursor_to extent.to.cursor
|
3144
3320
|
end
|
3145
|
-
|
3321
|
+
extent
|
3322
|
+
end
|
3323
|
+
|
3324
|
+
def get_entries_for_toc node
|
3325
|
+
node.sections
|
3146
3326
|
end
|
3147
3327
|
|
3148
3328
|
# NOTE: num_front_matter_pages not used during a dry run
|
3149
|
-
def
|
3329
|
+
def ink_toc doc, num_levels, toc_page_number, start_cursor, num_front_matter_pages = 0
|
3150
3330
|
go_to_page toc_page_number unless (page_number == toc_page_number) || scratch?
|
3151
3331
|
start_page_number = page_number
|
3152
3332
|
move_cursor_to start_cursor
|
3153
3333
|
unless (toc_title = doc.attr 'toc-title').nil_or_empty?
|
3154
3334
|
theme_font_cascade [[:heading, level: 2], :toc_title] do
|
3155
3335
|
toc_title_align = (@theme.toc_title_text_align || @theme.heading_h2_text_align || @theme.heading_text_align || @base_text_align).to_sym
|
3156
|
-
|
3336
|
+
ink_general_heading doc, toc_title, align: toc_title_align, level: 2, outdent: true, role: :toctitle
|
3157
3337
|
end
|
3158
3338
|
end
|
3159
3339
|
# QUESTION: should we skip this whole method if num_levels < 0?
|
@@ -3178,7 +3358,7 @@ module Asciidoctor
|
|
3178
3358
|
}
|
3179
3359
|
end
|
3180
3360
|
theme_margin :toc, :top
|
3181
|
-
|
3361
|
+
ink_toc_level (get_entries_for_toc doc), num_levels, dot_leader, num_front_matter_pages
|
3182
3362
|
end
|
3183
3363
|
# NOTE: range must be calculated relative to toc_page_number; absolute page number in scratch document is arbitrary
|
3184
3364
|
toc_page_numbers = (toc_page_number..(toc_page_number + (page_number - start_page_number)))
|
@@ -3186,38 +3366,48 @@ module Asciidoctor
|
|
3186
3366
|
toc_page_numbers
|
3187
3367
|
end
|
3188
3368
|
|
3189
|
-
def
|
3369
|
+
def ink_toc_level entries, num_levels, dot_leader, num_front_matter_pages
|
3190
3370
|
# NOTE: font options aren't always reliable, so store size separately
|
3191
3371
|
toc_font_info = theme_font :toc do
|
3192
3372
|
{ font: font, size: @font_size }
|
3193
3373
|
end
|
3194
3374
|
hanging_indent = @theme.toc_hanging_indent
|
3195
|
-
|
3196
|
-
next if (
|
3197
|
-
theme_font :toc, level:
|
3198
|
-
|
3199
|
-
|
3375
|
+
entries.each do |entry|
|
3376
|
+
next if (num_levels_for_entry = (entry.attr 'toclevels', num_levels).to_i) < (entry_level = entry.level + 1).pred
|
3377
|
+
theme_font :toc, level: entry_level do
|
3378
|
+
next unless (entry_anchor = (entry.attr 'pdf-anchor') || entry.id)
|
3379
|
+
entry_title = entry.context == :section ? entry.numbered_title : (entry.title? ? entry.title : (entry.xreftext 'basic'))
|
3380
|
+
next if entry_title.empty?
|
3381
|
+
entry_title = transform_text entry_title, @text_transform if @text_transform
|
3200
3382
|
pgnum_label_placeholder_width = rendered_width_of_string '0' * @toc_max_pagenum_digits
|
3201
|
-
# NOTE: only write
|
3383
|
+
# NOTE: only write title (excluding dots and page number) if this is a dry run
|
3202
3384
|
if scratch?
|
3203
3385
|
indent 0, pgnum_label_placeholder_width do
|
3204
3386
|
# NOTE: must wrap title in empty anchor element in case links are styled with different font family / size
|
3205
|
-
|
3387
|
+
ink_prose entry_title, anchor: true, normalize: false, hanging_indent: hanging_indent, normalize_line_height: true, margin: 0
|
3206
3388
|
end
|
3207
3389
|
else
|
3208
|
-
physical_pgnum =
|
3209
|
-
|
3210
|
-
|
3390
|
+
if !(physical_pgnum = entry.attr 'pdf-page-start') &&
|
3391
|
+
(target_page_ref = (get_dest entry_anchor)&.first) &&
|
3392
|
+
(target_page_idx = state.pages.index {|candidate| candidate.dictionary == target_page_ref })
|
3393
|
+
physical_pgnum = target_page_idx + 1
|
3394
|
+
end
|
3395
|
+
if physical_pgnum
|
3396
|
+
virtual_pgnum = physical_pgnum - num_front_matter_pages
|
3397
|
+
pgnum_label = (virtual_pgnum < 1 ? (RomanNumeral.new physical_pgnum, :lower) : virtual_pgnum).to_s
|
3398
|
+
else
|
3399
|
+
pgnum_label = '?'
|
3400
|
+
end
|
3211
3401
|
start_page_number = page_number
|
3212
3402
|
start_cursor = cursor
|
3213
3403
|
start_dots = nil
|
3214
|
-
|
3404
|
+
entry_title_inherited = (apply_text_decoration ::Set.new, :toc, entry_level).merge anchor: entry_anchor, color: @font_color
|
3215
3405
|
# NOTE: use text formatter to add anchor overlay to avoid using inline format with synthetic anchor tag
|
3216
|
-
|
3406
|
+
entry_title_fragments = text_formatter.format entry_title, inherited: entry_title_inherited
|
3217
3407
|
line_metrics = calc_line_metrics @base_line_height
|
3218
3408
|
indent 0, pgnum_label_placeholder_width do
|
3219
|
-
(
|
3220
|
-
typeset_formatted_text
|
3409
|
+
(entry_title_fragments[-1][:callback] ||= []) << (last_fragment_pos = ::Asciidoctor::PDF::FormattedText::FragmentPositionRenderer.new)
|
3410
|
+
typeset_formatted_text entry_title_fragments, line_metrics, hanging_indent: hanging_indent, normalize_line_height: true
|
3221
3411
|
start_dots = last_fragment_pos.right + hanging_indent
|
3222
3412
|
last_fragment_cursor = last_fragment_pos.top + line_metrics.padding_top
|
3223
3413
|
start_cursor = last_fragment_cursor if last_fragment_pos.page_number > start_page_number || (start_cursor - last_fragment_cursor) > line_metrics.height
|
@@ -3225,7 +3415,7 @@ module Asciidoctor
|
|
3225
3415
|
end_cursor = cursor
|
3226
3416
|
move_cursor_to start_cursor
|
3227
3417
|
# NOTE: we're guaranteed to be on the same page as the final line of the entry
|
3228
|
-
if dot_leader[:width] > 0 && (dot_leader[:levels].include?
|
3418
|
+
if dot_leader[:width] > 0 && (dot_leader[:levels].include? entry_level.pred)
|
3229
3419
|
pgnum_label_width = rendered_width_of_string pgnum_label
|
3230
3420
|
pgnum_label_font_settings = { color: @font_color, font: font_family, size: @font_size, styles: font_styles }
|
3231
3421
|
save_font do
|
@@ -3237,18 +3427,18 @@ module Asciidoctor
|
|
3237
3427
|
typeset_formatted_text [
|
3238
3428
|
{ text: dot_leader[:text] * num_dots, color: dot_leader[:font_color] },
|
3239
3429
|
dot_leader[:spacer],
|
3240
|
-
({ text: pgnum_label, anchor:
|
3430
|
+
({ text: pgnum_label, anchor: entry_anchor }.merge pgnum_label_font_settings),
|
3241
3431
|
], line_metrics, align: :right
|
3242
3432
|
end
|
3243
3433
|
else
|
3244
|
-
typeset_formatted_text [{ text: pgnum_label, color: @font_color, anchor:
|
3434
|
+
typeset_formatted_text [{ text: pgnum_label, color: @font_color, anchor: entry_anchor }], line_metrics, align: :right
|
3245
3435
|
end
|
3246
3436
|
move_cursor_to end_cursor
|
3247
3437
|
end
|
3248
3438
|
end
|
3249
3439
|
indent @theme.toc_indent do
|
3250
|
-
|
3251
|
-
end if
|
3440
|
+
ink_toc_level (get_entries_for_toc entry), num_levels_for_entry, dot_leader, num_front_matter_pages
|
3441
|
+
end if num_levels_for_entry >= entry_level
|
3252
3442
|
end
|
3253
3443
|
end
|
3254
3444
|
|
@@ -3275,15 +3465,15 @@ module Asciidoctor
|
|
3275
3465
|
icon_data
|
3276
3466
|
end
|
3277
3467
|
|
3278
|
-
# TODO: delegate to
|
3279
|
-
def
|
3468
|
+
# TODO: delegate to ink_page_header and ink_page_footer per page
|
3469
|
+
def ink_running_content periphery, doc, skip = [1, 1], body_start_page_number = 1
|
3280
3470
|
skip_pages, skip_pagenums = skip
|
3281
3471
|
# NOTE: find and advance to first non-imported content page to use as model page
|
3282
|
-
return unless (
|
3283
|
-
|
3472
|
+
return unless (content_start_page_number = state.pages[skip_pages..-1].index {|it| !it.imported_page? })
|
3473
|
+
content_start_page_number += (skip_pages + 1)
|
3284
3474
|
num_pages = page_count
|
3285
3475
|
prev_page_number = page_number
|
3286
|
-
go_to_page
|
3476
|
+
go_to_page content_start_page_number
|
3287
3477
|
|
3288
3478
|
# FIXME: probably need to treat doctypes differently
|
3289
3479
|
is_book = doc.doctype == 'book'
|
@@ -3379,7 +3569,7 @@ module Asciidoctor
|
|
3379
3569
|
pagenums_enabled = doc.attr? 'pagenums'
|
3380
3570
|
periphery_layout_cache = {}
|
3381
3571
|
# NOTE: this block is invoked during PDF generation, after #write -> #render_file and thus after #convert_document
|
3382
|
-
repeat (
|
3572
|
+
repeat (content_start_page_number..num_pages), dynamic: true do
|
3383
3573
|
pgnum = page_number
|
3384
3574
|
# NOTE: don't write on pages which are imported / inserts (otherwise we can get a corrupt PDF)
|
3385
3575
|
next if page.imported_page? || (disable_on_pages.include? pgnum)
|
@@ -3416,7 +3606,7 @@ module Asciidoctor
|
|
3416
3606
|
theme_font periphery do
|
3417
3607
|
canvas do
|
3418
3608
|
bounding_box [trim_styles[:content_left][side], trim_styles[:top][side]], width: trim_styles[:content_width][side], height: trim_styles[:height] do
|
3419
|
-
if (trim_column_rule_width = trim_styles[:column_rule_width]) > 0
|
3609
|
+
if trim_styles[:column_rule_color] && (trim_column_rule_width = trim_styles[:column_rule_width]) > 0
|
3420
3610
|
trim_column_rule_spacing = trim_styles[:column_rule_spacing]
|
3421
3611
|
else
|
3422
3612
|
trim_column_rule_width = nil
|
@@ -3468,8 +3658,9 @@ module Asciidoctor
|
|
3468
3658
|
content = apply_subs_discretely doc, content, drop_lines_with_unresolved_attributes: true, imagesdir: @themesdir
|
3469
3659
|
content = transform_text content, @text_transform if @text_transform
|
3470
3660
|
end
|
3471
|
-
formatted_text_box (parse_text content,
|
3661
|
+
formatted_text_box (parse_text content, inline_format: [normalize: true]),
|
3472
3662
|
at: [left, bounds.top - trim_styles[:padding][side][0] - trim_styles[:content_offset] + ((Array trim_styles[:valign])[0] == :center ? font.descender * 0.5 : 0)],
|
3663
|
+
color: @font_color,
|
3473
3664
|
width: colwidth,
|
3474
3665
|
height: trim_styles[:prose_content_height][side],
|
3475
3666
|
align: colspec[:align],
|
@@ -3501,7 +3692,7 @@ module Asciidoctor
|
|
3501
3692
|
trim_content_margin_recto = @theme[%(#{periphery}_recto_content_margin)] || @theme[%(#{periphery}_content_margin)] || [0, 'inherit', 0, 'inherit']
|
3502
3693
|
trim_content_margin_recto = (expand_margin_value trim_content_margin_recto).map.with_index {|v, i| i.odd? && v == 'inherit' ? page_margin_recto[i] - trim_margin_recto[i] : v.to_f }
|
3503
3694
|
if (trim_padding_recto = @theme[%(#{periphery}_recto_padding)] || @theme[%(#{periphery}_padding)])
|
3504
|
-
trim_padding_recto = (
|
3695
|
+
trim_padding_recto = (expand_padding_value trim_padding_recto).map.with_index {|v, i| v + trim_content_margin_recto[i] }
|
3505
3696
|
else
|
3506
3697
|
trim_padding_recto = trim_content_margin_recto
|
3507
3698
|
end
|
@@ -3511,7 +3702,7 @@ module Asciidoctor
|
|
3511
3702
|
trim_content_margin_verso = @theme[%(#{periphery}_verso_content_margin)] || @theme[%(#{periphery}_content_margin)] || [0, 'inherit', 0, 'inherit']
|
3512
3703
|
trim_content_margin_verso = (expand_margin_value trim_content_margin_verso).map.with_index {|v, i| i.odd? && v == 'inherit' ? page_margin_verso[i] - trim_margin_verso[i] : v.to_f }
|
3513
3704
|
if (trim_padding_verso = @theme[%(#{periphery}_verso_padding)] || @theme[%(#{periphery}_padding)])
|
3514
|
-
trim_padding_verso = (
|
3705
|
+
trim_padding_verso = (expand_padding_value trim_padding_verso).map.with_index {|v, i| v + trim_content_margin_verso[i] }
|
3515
3706
|
else
|
3516
3707
|
trim_padding_verso = trim_content_margin_verso
|
3517
3708
|
end
|
@@ -3524,12 +3715,12 @@ module Asciidoctor
|
|
3524
3715
|
# NOTE: we've already verified this property is set
|
3525
3716
|
height: (trim_height = @theme[%(#{periphery}_height)]),
|
3526
3717
|
bg_color: (resolve_theme_color %(#{periphery}_background_color).to_sym),
|
3527
|
-
|
3718
|
+
border_width: (trim_border_width = @theme[%(#{periphery}_border_width)] || 0),
|
3719
|
+
border_color: trim_border_width > 0 ? (resolve_theme_color %(#{periphery}_border_color).to_sym, @theme.base_border_color) : nil,
|
3528
3720
|
border_style: (@theme[%(#{periphery}_border_style)]&.to_sym || :solid),
|
3529
|
-
|
3530
|
-
column_rule_color:
|
3721
|
+
column_rule_width: (trim_column_rule_width = @theme[%(#{periphery}_column_rule_width)] || 0),
|
3722
|
+
column_rule_color: trim_column_rule_width > 0 ? (resolve_theme_color %(#{periphery}_column_rule_color).to_sym) : nil,
|
3531
3723
|
column_rule_style: (@theme[%(#{periphery}_column_rule_style)]&.to_sym || :solid),
|
3532
|
-
column_rule_width: (trim_column_rule_color ? @theme[%(#{periphery}_column_rule_width)] || 0 : 0),
|
3533
3724
|
column_rule_spacing: (@theme[%(#{periphery}_column_rule_spacing)] || 0),
|
3534
3725
|
valign: valign_offset ? [valign, valign_offset] : valign,
|
3535
3726
|
img_valign: @theme[%(#{periphery}_image_vertical_align)],
|
@@ -3697,8 +3888,9 @@ module Asciidoctor
|
|
3697
3888
|
outline.define do
|
3698
3889
|
initial_pagenum = has_front_cover ? 2 : 1
|
3699
3890
|
# FIXME: use sanitize: :plain_text on Document#doctitle once available
|
3700
|
-
if document.page_count >= initial_pagenum && (
|
3701
|
-
|
3891
|
+
if document.page_count >= initial_pagenum && (outline_title = doc.attr 'outline-title') &&
|
3892
|
+
(outline_title.empty? ? (outline_title = document.resolve_doctitle doc) : outline_title)
|
3893
|
+
page title: (document.sanitize outline_title), destination: (document.dest_top initial_pagenum)
|
3702
3894
|
end
|
3703
3895
|
# QUESTION: is there any way to get add_outline_level to invoke in the context of the outline?
|
3704
3896
|
document.add_outline_level self, doc.sections, num_levels, expand_levels
|
@@ -3760,7 +3952,14 @@ module Asciidoctor
|
|
3760
3952
|
pdf_doc.render_file target
|
3761
3953
|
# QUESTION: restore attributes first?
|
3762
3954
|
@pdfmark&.generate_file target
|
3763
|
-
(
|
3955
|
+
if (quality = @optimize)
|
3956
|
+
if quality.include? ','
|
3957
|
+
quality, compliance = quality.split ',', 2
|
3958
|
+
elsif quality.include? '/'
|
3959
|
+
quality, compliance = nil, quality
|
3960
|
+
end
|
3961
|
+
(Optimizer.new quality, pdf_doc.min_version, compliance).optimize_file target
|
3962
|
+
end
|
3764
3963
|
to_file = true
|
3765
3964
|
end
|
3766
3965
|
if !ENV['KEEP_ARTIFACTS']
|
@@ -3812,7 +4011,7 @@ module Asciidoctor
|
|
3812
4011
|
end
|
3813
4012
|
|
3814
4013
|
def resolve_text_transform key, use_fallback = true
|
3815
|
-
if (transform = ::Hash === key ? (key.delete :text_transform) : @theme[key
|
4014
|
+
if (transform = ::Hash === key ? (key.delete :text_transform) : @theme[key])
|
3816
4015
|
transform == 'none' ? nil : transform
|
3817
4016
|
elsif use_fallback
|
3818
4017
|
@text_transform
|
@@ -3821,9 +4020,9 @@ module Asciidoctor
|
|
3821
4020
|
|
3822
4021
|
# QUESTION: should we pass a category as an argument?
|
3823
4022
|
# QUESTION: should we make this a method on the theme ostruct? (e.g., @theme.resolve_color key, fallback)
|
3824
|
-
def resolve_theme_color key, fallback_color = nil
|
3825
|
-
if (color = @theme[key
|
3826
|
-
color
|
4023
|
+
def resolve_theme_color key, fallback_color = nil, transparent_color = fallback_color
|
4024
|
+
if (color = @theme[key])
|
4025
|
+
color == 'transparent' ? transparent_color : color
|
3827
4026
|
else
|
3828
4027
|
fallback_color
|
3829
4028
|
end
|
@@ -3834,7 +4033,7 @@ module Asciidoctor
|
|
3834
4033
|
end
|
3835
4034
|
|
3836
4035
|
def theme_fill_and_stroke_bounds category, opts = {}
|
3837
|
-
fill_and_stroke_bounds opts[:background_color], @theme[%(#{category}_border_color)],
|
4036
|
+
fill_and_stroke_bounds opts[:background_color], @theme[%(#{category}_border_color)] || @theme.base_border_color,
|
3838
4037
|
line_width: @theme[%(#{category}_border_width)],
|
3839
4038
|
line_style: (@theme[%(#{category}_border_style)]&.to_sym || :solid),
|
3840
4039
|
radius: @theme[%(#{category}_border_radius)]
|
@@ -3843,7 +4042,7 @@ module Asciidoctor
|
|
3843
4042
|
def theme_fill_and_stroke_block category, extent, opts = {}
|
3844
4043
|
node_with_caption = nil unless (node_with_caption = opts[:caption_node])&.title?
|
3845
4044
|
unless extent
|
3846
|
-
|
4045
|
+
ink_caption node_with_caption, category: category if node_with_caption
|
3847
4046
|
return
|
3848
4047
|
end
|
3849
4048
|
if (b_width = (opts.key? :border_width) ? opts[:border_width] : @theme[%(#{category}_border_width)])
|
@@ -3857,14 +4056,12 @@ module Asciidoctor
|
|
3857
4056
|
bg_color = nil
|
3858
4057
|
end
|
3859
4058
|
unless b_width || bg_color
|
3860
|
-
|
4059
|
+
ink_caption node_with_caption, category: category if node_with_caption
|
3861
4060
|
return
|
3862
4061
|
end
|
3863
|
-
|
3864
|
-
b_color = @page_bg_color
|
3865
|
-
end
|
4062
|
+
b_color = resolve_theme_color %(#{category}_border_color).to_sym, @theme.base_border_color, @page_bg_color
|
3866
4063
|
b_radius ||= (@theme[%(#{category}_border_radius)] || 0) + (b_width || 0)
|
3867
|
-
if b_width
|
4064
|
+
if b_width
|
3868
4065
|
if b_color == @page_bg_color # let page background cut into block background
|
3869
4066
|
b_gap_color, b_shift = @page_bg_color, (b_width * 0.5)
|
3870
4067
|
elsif (b_gap_color = bg_color) && b_gap_color != b_color
|
@@ -3875,7 +4072,7 @@ module Asciidoctor
|
|
3875
4072
|
else # let page background cut into block background; guarantees b_width is set
|
3876
4073
|
b_shift, b_gap_color = (b_width ||= 0.5) * 0.5, @page_bg_color
|
3877
4074
|
end
|
3878
|
-
|
4075
|
+
ink_caption node_with_caption, category: category if node_with_caption
|
3879
4076
|
extent.from.page += 1 unless extent.from.page == page_number # sanity check
|
3880
4077
|
float do
|
3881
4078
|
extent.each_page do |first_page, last_page|
|
@@ -4012,9 +4209,7 @@ module Asciidoctor
|
|
4012
4209
|
# NOTE: it also removes zero-width spaces
|
4013
4210
|
arranger.finalize_line
|
4014
4211
|
actual_width = width_of_fragments arranger.fragments
|
4015
|
-
|
4016
|
-
padding = ::Array.new 4, padding
|
4017
|
-
end
|
4212
|
+
padding = expand_padding_value @theme[%(#{category}_padding)]
|
4018
4213
|
if actual_width > (available_width = bounds.width - padding[3].to_f - padding[1].to_f)
|
4019
4214
|
adjusted_font_size = ((available_width * font_size).to_f / actual_width).truncate 4
|
4020
4215
|
if (min = @theme[%(#{category}_font_size_min)] || @theme.base_font_size_min) && adjusted_font_size < min
|
@@ -4088,9 +4283,10 @@ module Asciidoctor
|
|
4088
4283
|
|
4089
4284
|
# TODO: document me, esp the first line formatting functionality
|
4090
4285
|
def typeset_text string, line_metrics, opts = {}
|
4091
|
-
move_down line_metrics.padding_top
|
4092
4286
|
opts = { leading: line_metrics.leading, final_gap: line_metrics.final_gap }.merge opts
|
4093
4287
|
string = string.gsub CjkLineBreakRx, ZeroWidthSpace if @cjk_line_breaks
|
4288
|
+
return text_box string, opts if opts[:height]
|
4289
|
+
move_down line_metrics.padding_top
|
4094
4290
|
if (hanging_indent = (opts.delete :hanging_indent) || 0) > 0
|
4095
4291
|
indent hanging_indent do
|
4096
4292
|
text string, (opts.merge indent_paragraphs: -hanging_indent)
|
@@ -4254,10 +4450,11 @@ module Asciidoctor
|
|
4254
4450
|
end
|
4255
4451
|
end
|
4256
4452
|
|
4257
|
-
def
|
4453
|
+
def resolve_text_align_from_role roles, query_theme: false, remove_predefined: false
|
4258
4454
|
if (align_role = roles.reverse.find {|r| TextAlignmentRoles.include? r })
|
4455
|
+
roles.replace roles - TextAlignmentRoles if remove_predefined
|
4259
4456
|
(align_role.slice 5, align_role.length).to_sym
|
4260
|
-
elsif
|
4457
|
+
elsif query_theme
|
4261
4458
|
roles.reverse.each do |role|
|
4262
4459
|
if (align = @theme[%(role_#{role}_text_align)])
|
4263
4460
|
return align.to_sym
|
@@ -4267,6 +4464,9 @@ module Asciidoctor
|
|
4267
4464
|
end
|
4268
4465
|
end
|
4269
4466
|
|
4467
|
+
# Deprecated
|
4468
|
+
alias resolve_alignment_from_role resolve_text_align_from_role
|
4469
|
+
|
4270
4470
|
# QUESTION: is this method still necessary?
|
4271
4471
|
def resolve_imagesdir doc
|
4272
4472
|
if (imagesdir = doc.attr 'imagesdir').nil_or_empty? || (imagesdir = imagesdir.chomp '/') == '.'
|
@@ -4596,24 +4796,35 @@ module Asciidoctor
|
|
4596
4796
|
end
|
4597
4797
|
|
4598
4798
|
# Deprecated method names
|
4599
|
-
alias layout_footnotes
|
4600
|
-
alias layout_title_page
|
4601
|
-
alias layout_cover_page
|
4602
|
-
alias layout_chapter_title
|
4603
|
-
alias layout_part_title
|
4604
|
-
alias layout_general_heading
|
4605
|
-
alias layout_heading
|
4606
|
-
alias layout_prose
|
4607
|
-
alias layout_caption
|
4608
|
-
alias layout_table_caption
|
4609
|
-
alias layout_toc
|
4610
|
-
alias layout_toc_level
|
4611
|
-
alias layout_running_content
|
4612
|
-
|
4613
|
-
|
4614
|
-
|
4615
|
-
|
4616
|
-
|
4799
|
+
alias layout_footnotes ink_footnotes
|
4800
|
+
alias layout_title_page ink_title_page
|
4801
|
+
alias layout_cover_page ink_cover_page
|
4802
|
+
alias layout_chapter_title ink_chapter_title
|
4803
|
+
alias layout_part_title ink_part_title
|
4804
|
+
alias layout_general_heading ink_general_heading
|
4805
|
+
alias layout_heading ink_heading
|
4806
|
+
alias layout_prose ink_prose
|
4807
|
+
alias layout_caption ink_caption
|
4808
|
+
alias layout_table_caption ink_table_caption
|
4809
|
+
alias layout_toc ink_toc
|
4810
|
+
alias layout_toc_level ink_toc_level
|
4811
|
+
alias layout_running_content ink_running_content
|
4812
|
+
|
4813
|
+
# intercepts "class CustomPDFConverter < (Asciidoctor::Converter.for 'pdf')"
|
4814
|
+
def self.method_added method_sym
|
4815
|
+
if (method_name = method_sym.to_s).start_with? 'layout_'
|
4816
|
+
alias_method %(ink_#{method_name.slice 7, method_name.length}).to_sym, method_sym
|
4817
|
+
elsif method_name == 'convert_listing_or_literal' || method_name == 'convert_code'
|
4818
|
+
alias_method :convert_listing, method_sym
|
4819
|
+
alias_method :convert_literal, method_sym
|
4820
|
+
end
|
4821
|
+
end
|
4822
|
+
|
4823
|
+
# intercepts "(Asciidoctor::Converter.for 'pdf').prepend CustomConverterExtensions"
|
4824
|
+
def self.prepend *mods
|
4825
|
+
super
|
4826
|
+
mods.each {|mod| (mod.instance_methods false).each {|method| method_added method } }
|
4827
|
+
self
|
4617
4828
|
end
|
4618
4829
|
|
4619
4830
|
private
|