asciidoctor-pdf 2.0.0.alpha.2 → 2.0.0.beta.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|