asciidoctor-pdf 2.0.0.alpha.1 → 2.0.0.beta.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +94 -1
- data/README.adoc +32 -14
- 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 +11 -20
- data/data/themes/default-theme.yml +8 -7
- data/docs/theming-guide.adoc +11 -10
- data/lib/asciidoctor/pdf/converter.rb +445 -305
- data/lib/asciidoctor/pdf/ext/asciidoctor/document.rb +4 -0
- data/lib/asciidoctor/pdf/ext/pdf-core/page.rb +8 -0
- data/lib/asciidoctor/pdf/ext/prawn/extensions.rb +117 -38
- data/lib/asciidoctor/pdf/ext/prawn/formatted_text/box.rb +5 -0
- data/lib/asciidoctor/pdf/ext/prawn/formatted_text/protect_bottom_gutter.rb +13 -0
- data/lib/asciidoctor/pdf/ext/prawn/images.rb +6 -2
- data/lib/asciidoctor/pdf/ext/prawn-table/cell/asciidoc.rb +1 -1
- data/lib/asciidoctor/pdf/ext/prawn.rb +1 -0
- data/lib/asciidoctor/pdf/formatted_text/transform.rb +7 -2
- data/lib/asciidoctor/pdf/nogmagick.rb +6 -0
- 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 +19 -3
- data/lib/asciidoctor/pdf/version.rb +1 -1
- metadata +5 -2
@@ -139,6 +139,7 @@ module Asciidoctor
|
|
139
139
|
# NOTE: enabling data-uri forces Asciidoctor Diagram to produce absolute image paths
|
140
140
|
doc.attributes['data-uri'] = (doc.instance_variable_get :@attribute_overrides)['data-uri'] = ''
|
141
141
|
end
|
142
|
+
@label = :primary
|
142
143
|
@initial_instance_variables = [:@initial_instance_variables] + instance_variables
|
143
144
|
end
|
144
145
|
|
@@ -164,8 +165,11 @@ module Asciidoctor
|
|
164
165
|
if node.blocks?
|
165
166
|
node.content
|
166
167
|
elsif node.content_model != :compound && (string = node.content)
|
167
|
-
|
168
|
-
|
168
|
+
prose_opts = opts.merge hyphenate: true, margin_bottom: 0
|
169
|
+
if (bottom_gutter = @bottom_gutters[-1][node])
|
170
|
+
prose_opts[:bottom_gutter] = bottom_gutter
|
171
|
+
end
|
172
|
+
ink_prose string, prose_opts
|
169
173
|
end
|
170
174
|
ensure
|
171
175
|
node.document.instance_variable_set :@converter, prev_converter if prev_converter
|
@@ -178,8 +182,9 @@ module Asciidoctor
|
|
178
182
|
def convert_document doc
|
179
183
|
doc.promote_preface_block
|
180
184
|
init_pdf doc
|
181
|
-
# set default value for outline,
|
185
|
+
# set default value for outline, outline-title, and pagenums attributes if not otherwise set
|
182
186
|
doc.attributes['outline'] = '' unless (doc.attribute_locked? 'outline') || ((doc.instance_variable_get :@attributes_modified).include? 'outline')
|
187
|
+
doc.attributes['outline-title'] = '' unless (doc.attribute_locked? 'outline-title') || ((doc.instance_variable_get :@attributes_modified).include? 'outline-title')
|
183
188
|
doc.attributes['pagenums'] = '' unless (doc.attribute_locked? 'pagenums') || ((doc.instance_variable_get :@attributes_modified).include? 'pagenums')
|
184
189
|
#assign_missing_section_ids doc
|
185
190
|
|
@@ -187,25 +192,22 @@ module Asciidoctor
|
|
187
192
|
|
188
193
|
marked_page_number = page_number
|
189
194
|
# NOTE: a new page will already be started (page_number = 2) if the front cover image is a PDF
|
190
|
-
|
195
|
+
ink_cover_page doc, :front
|
191
196
|
has_front_cover = page_number > marked_page_number
|
192
|
-
|
193
|
-
if (use_title_page = doc.doctype == 'book' || (doc.attr? 'title-page'))
|
194
|
-
layout_title_page doc
|
195
|
-
has_title_page = page_number == (has_front_cover ? 2 : 1)
|
196
|
-
end
|
197
|
+
has_title_page = ink_title_page doc if (title_page_on = doc.doctype == 'book' || (doc.attr? 'title-page'))
|
197
198
|
|
198
199
|
@page_margin_by_side[:cover] = @page_margin_by_side[:recto] if @media == 'prepress' && page_number == 0
|
199
200
|
|
200
201
|
start_new_page unless page&.empty? # rubocop:disable Lint/SafeNavigationWithEmpty
|
201
202
|
|
202
|
-
# NOTE: font must be set before content is written to the main or scratch document
|
203
|
+
# NOTE: the base font must be set before any content is written to the main or scratch document
|
204
|
+
# this method is called inside ink_title_page if the title page is active
|
203
205
|
font @theme.base_font_family, size: @root_font_size, style: @theme.base_font_style unless has_title_page
|
204
206
|
|
205
|
-
unless
|
207
|
+
unless title_page_on
|
206
208
|
body_start_page_number = page_number
|
207
209
|
theme_font :heading, level: 1 do
|
208
|
-
|
210
|
+
ink_general_heading doc, doc.doctitle, align: (@theme.heading_h1_text_align&.to_sym || :center), level: 1, role: :doctitle
|
209
211
|
end if doc.header? && !doc.notitle
|
210
212
|
end
|
211
213
|
|
@@ -216,14 +218,14 @@ module Asciidoctor
|
|
216
218
|
if (insert_toc = (doc.attr? 'toc') && !((toc_placement = doc.attr 'toc-placement') == 'macro' || toc_placement == 'preamble') && doc.sections?)
|
217
219
|
start_new_page if @ppbook && verso_page?
|
218
220
|
add_dest_for_block doc, id: 'toc', y: (at_page_top? ? page_height : nil)
|
219
|
-
allocate_toc doc, toc_num_levels, cursor,
|
221
|
+
@toc_extent = allocate_toc doc, toc_num_levels, cursor, title_page_on
|
220
222
|
else
|
221
223
|
@toc_extent = nil
|
222
224
|
end
|
223
225
|
|
224
226
|
start_new_page if @ppbook && verso_page? && !(((next_block = doc.blocks[0])&.context == :preamble ? next_block.blocks[0] : next_block)&.option? 'nonfacing')
|
225
227
|
|
226
|
-
if
|
228
|
+
if title_page_on
|
227
229
|
zero_page_offset = has_front_cover ? 1 : 0
|
228
230
|
first_page_offset = has_title_page ? zero_page_offset.next : zero_page_offset
|
229
231
|
body_offset = (body_start_page_number = page_number) - 1
|
@@ -301,26 +303,26 @@ module Asciidoctor
|
|
301
303
|
traverse doc
|
302
304
|
|
303
305
|
# NOTE: for a book, these are leftover footnotes; for an article this is everything
|
304
|
-
outdent_section {
|
306
|
+
outdent_section { ink_footnotes doc }
|
305
307
|
|
306
|
-
if @toc_extent
|
307
|
-
if
|
308
|
-
num_front_matter_pages[0] =
|
309
|
-
num_front_matter_pages[1] =
|
308
|
+
if (toc_extent = @toc_extent)
|
309
|
+
if title_page_on && !insert_toc
|
310
|
+
num_front_matter_pages[0] = toc_extent.to.page if @theme.running_content_start_at == 'after-toc'
|
311
|
+
num_front_matter_pages[1] = toc_extent.to.page if @theme.page_numbering_start_at == 'after-toc'
|
310
312
|
end
|
311
|
-
toc_page_nums =
|
313
|
+
toc_page_nums = ink_toc doc, toc_num_levels, toc_extent.from.page, toc_extent.from.cursor, num_front_matter_pages[1]
|
312
314
|
else
|
313
315
|
toc_page_nums = []
|
314
316
|
end
|
315
317
|
|
316
318
|
# NOTE: delete orphaned page (a page was created but there was no additional content)
|
317
319
|
# QUESTION: should we delete page if document is empty? (leaving no pages?)
|
318
|
-
|
320
|
+
delete_current_page if page_count > 1 && page.empty?
|
319
321
|
end
|
320
322
|
|
321
323
|
unless page_count < body_start_page_number
|
322
|
-
|
323
|
-
|
324
|
+
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
|
325
|
+
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
326
|
end
|
325
327
|
|
326
328
|
add_outline doc, (doc.attr 'outlinelevels', toc_num_levels), toc_page_nums, num_front_matter_pages[1], has_front_cover
|
@@ -337,7 +339,7 @@ module Asciidoctor
|
|
337
339
|
catalog.data[:ViewerPreferences] = { DisplayDocTitle: true }
|
338
340
|
|
339
341
|
stamp_foreground_image doc, has_front_cover
|
340
|
-
|
342
|
+
ink_cover_page doc, :back
|
341
343
|
add_dest_for_top doc
|
342
344
|
nil
|
343
345
|
end
|
@@ -402,7 +404,7 @@ module Asciidoctor
|
|
402
404
|
@font_scale = 1
|
403
405
|
@font_color = theme.base_font_color
|
404
406
|
@text_decoration_width = theme.base_text_decoration_width
|
405
|
-
@
|
407
|
+
@base_text_align = (align = doc.attr 'text-align') && (TextAlignmentNames.include? align) ? align : theme.base_text_align
|
406
408
|
@base_line_height = theme.base_line_height
|
407
409
|
@cjk_line_breaks = doc.attr? 'scripts', 'cjk'
|
408
410
|
if (hyphen_lang = doc.attr 'hyphens') &&
|
@@ -415,6 +417,7 @@ module Asciidoctor
|
|
415
417
|
@text_transform = nil
|
416
418
|
@list_numerals = []
|
417
419
|
@list_bullets = []
|
420
|
+
@bottom_gutters = [{}]
|
418
421
|
@rendered_footnotes = []
|
419
422
|
@conum_glyphs = ConumSets[@theme.conum_glyphs || 'circled'] || (@theme.conum_glyphs.split ',').map do |r|
|
420
423
|
from, to = r.lstrip.split '-', 2
|
@@ -460,7 +463,6 @@ module Asciidoctor
|
|
460
463
|
end
|
461
464
|
|
462
465
|
def prepare_theme theme
|
463
|
-
theme.base_border_width || 0
|
464
466
|
theme.base_font_color ||= '000000'
|
465
467
|
theme.base_font_size ||= 12
|
466
468
|
theme.base_font_style = theme.base_font_style&.to_sym || :normal
|
@@ -477,6 +479,9 @@ module Asciidoctor
|
|
477
479
|
theme.list_item_spacing ||= 0
|
478
480
|
theme.description_list_term_spacing ||= 0
|
479
481
|
theme.description_list_description_indent ||= 0
|
482
|
+
theme.table_border_color ||= (theme.base_border_color || '000000')
|
483
|
+
theme.table_border_width ||= 0.5
|
484
|
+
theme.thematic_break_border_color ||= (theme.base_border_color || '000000')
|
480
485
|
theme.image_border_width ||= 0
|
481
486
|
theme.code_linenum_font_color ||= '999999'
|
482
487
|
theme.callout_list_margin_top_after_code ||= 0
|
@@ -661,22 +666,23 @@ module Asciidoctor
|
|
661
666
|
title = %(#{title}\n<em class="subtitle">#{subtitle}</em>)
|
662
667
|
end
|
663
668
|
hlevel = sect.level + 1
|
664
|
-
align = (@theme[%(heading_h#{hlevel}
|
669
|
+
align = (@theme[%(heading_h#{hlevel}_text_align)] || @theme.heading_text_align || @base_text_align).to_sym
|
665
670
|
chapterlike = !(part = sectname == 'part') && (sectname == 'chapter' || (sect.document.doctype == 'book' && sect.level == 1))
|
666
|
-
|
671
|
+
hidden = sect.option? 'notitle'
|
672
|
+
hopts = { align: align, level: hlevel, part: part, chapterlike: chapterlike, outdent: !(part || chapterlike) }
|
667
673
|
if part
|
668
674
|
unless @theme.heading_part_break_before == 'auto'
|
669
675
|
start_new = true
|
670
|
-
|
676
|
+
start_new_part sect
|
671
677
|
end
|
672
678
|
elsif chapterlike
|
673
679
|
if @theme.heading_chapter_break_before != 'auto' ||
|
674
680
|
(@theme.heading_part_break_after == 'always' && sect == sect.parent.sections[0])
|
675
681
|
start_new = true
|
676
|
-
|
682
|
+
start_new_chapter sect
|
677
683
|
end
|
678
684
|
end
|
679
|
-
arrange_section sect, title, hopts unless start_new || at_page_top?
|
685
|
+
arrange_section sect, title, hopts unless hidden || start_new || at_page_top?
|
680
686
|
# QUESTION: should we store pdf-page-start, pdf-anchor & pdf-destination in internal map?
|
681
687
|
sect.set_attr 'pdf-page-start', (start_pgnum = page_number)
|
682
688
|
# QUESTION: should we just assign the section this generated id?
|
@@ -685,20 +691,20 @@ module Asciidoctor
|
|
685
691
|
add_dest_for_block sect, id: sect_anchor, y: (at_page_top? ? page_height : nil)
|
686
692
|
theme_font :heading, level: hlevel do
|
687
693
|
if part
|
688
|
-
|
694
|
+
ink_part_title sect, title, hopts
|
689
695
|
elsif chapterlike
|
690
|
-
|
696
|
+
ink_chapter_title sect, title, hopts
|
691
697
|
else
|
692
|
-
|
698
|
+
ink_general_heading sect, title, hopts
|
693
699
|
end
|
694
|
-
end
|
700
|
+
end unless hidden
|
695
701
|
|
696
702
|
if index_section
|
697
703
|
outdent_section { convert_index_section sect }
|
698
704
|
else
|
699
705
|
traverse sect
|
700
706
|
end
|
701
|
-
outdent_section {
|
707
|
+
outdent_section { ink_footnotes sect } if chapterlike
|
702
708
|
sect.set_attr 'pdf-page-end', page_number
|
703
709
|
end
|
704
710
|
|
@@ -719,7 +725,7 @@ module Asciidoctor
|
|
719
725
|
end
|
720
726
|
|
721
727
|
# QUESTION: if a footnote ref appears in a separate chapter, should the footnote def be duplicated?
|
722
|
-
def
|
728
|
+
def ink_footnotes node
|
723
729
|
return if (fns = (doc = node.document).footnotes - @rendered_footnotes).empty?
|
724
730
|
theme_margin :block, :bottom if node.context == :document || node == node.document.blocks[-1]
|
725
731
|
theme_margin :footnotes, :top
|
@@ -728,7 +734,7 @@ module Asciidoctor
|
|
728
734
|
move_down delta
|
729
735
|
end
|
730
736
|
theme_font :footnotes do
|
731
|
-
(title = doc.attr 'footnotes-title') && (
|
737
|
+
(title = doc.attr 'footnotes-title') && (ink_caption title, category: :footnotes)
|
732
738
|
item_spacing = @theme.footnotes_item_spacing
|
733
739
|
index_offset = @rendered_footnotes.length
|
734
740
|
sect_xreftext = node.context == :section && (node.xreftext node.document.attr 'xrefstyle')
|
@@ -738,7 +744,7 @@ module Asciidoctor
|
|
738
744
|
fn.singleton_class.send :attr_accessor, :label unless fn.respond_to? :label=
|
739
745
|
fn.label = %(#{label} - #{sect_xreftext})
|
740
746
|
end
|
741
|
-
|
747
|
+
ink_prose %(<a id="_footnotedef_#{index}">#{DummyText}</a>[<a anchor="_footnoteref_#{index}">#{label}</a>] #{fn.text}), margin_bottom: item_spacing, hyphenate: true
|
742
748
|
end
|
743
749
|
@rendered_footnotes += fns if extent
|
744
750
|
end
|
@@ -750,11 +756,11 @@ module Asciidoctor
|
|
750
756
|
add_dest_for_block node if node.id
|
751
757
|
hlevel = node.level.next
|
752
758
|
unless (align = resolve_alignment_from_role node.roles)
|
753
|
-
align = (@theme[%(heading_h#{hlevel}
|
759
|
+
align = (@theme[%(heading_h#{hlevel}_text_align)] || @theme.heading_text_align || @base_text_align).to_sym
|
754
760
|
end
|
755
761
|
# QUESTION: should we decouple styles from section titles?
|
756
762
|
theme_font :heading, level: hlevel do
|
757
|
-
|
763
|
+
ink_general_heading node, node.title, align: align, level: hlevel, outdent: (node.parent.context == :section)
|
758
764
|
end
|
759
765
|
end
|
760
766
|
|
@@ -763,10 +769,10 @@ module Asciidoctor
|
|
763
769
|
outdent_section do
|
764
770
|
pad_box @theme.abstract_padding do
|
765
771
|
theme_font :abstract_title do
|
766
|
-
|
772
|
+
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)
|
767
773
|
end if node.title?
|
768
774
|
theme_font :abstract do
|
769
|
-
prose_opts = { align: (@theme.
|
775
|
+
prose_opts = { align: (@theme.abstract_text_align || @base_text_align).to_sym, hyphenate: true }
|
770
776
|
if (text_indent = @theme.prose_text_indent) > 0
|
771
777
|
prose_opts[:indent_paragraphs] = text_indent
|
772
778
|
end
|
@@ -785,7 +791,7 @@ module Asciidoctor
|
|
785
791
|
if child.context == :paragraph
|
786
792
|
child.document.playback_attributes child.attributes
|
787
793
|
prose_opts[:margin_bottom] = 0 if child == last_block
|
788
|
-
|
794
|
+
ink_prose child.content, ((align = resolve_alignment_from_role child.roles) ? (prose_opts.merge align: align) : prose_opts.dup)
|
789
795
|
prose_opts.delete :first_line_options
|
790
796
|
prose_opts.delete :margin_bottom
|
791
797
|
else
|
@@ -797,7 +803,7 @@ module Asciidoctor
|
|
797
803
|
if (align = resolve_alignment_from_role node.roles)
|
798
804
|
prose_opts[:align] = align
|
799
805
|
end
|
800
|
-
|
806
|
+
ink_prose string, (prose_opts.merge margin_bottom: 0)
|
801
807
|
end
|
802
808
|
end
|
803
809
|
end
|
@@ -809,7 +815,7 @@ module Asciidoctor
|
|
809
815
|
def convert_preamble node
|
810
816
|
# FIXME: core should not be promoting paragraph to preamble if there are no sections
|
811
817
|
if node.blocks? && (first_block = node.blocks[0]).context == :paragraph && node.document.sections? && !first_block.role?
|
812
|
-
first_block.
|
818
|
+
first_block.role = 'lead'
|
813
819
|
end
|
814
820
|
traverse node
|
815
821
|
theme_margin :block, :bottom, (next_enclosed_block node)
|
@@ -832,13 +838,17 @@ module Asciidoctor
|
|
832
838
|
|
833
839
|
# TODO: check if we're within one line of the bottom of the page
|
834
840
|
# and advance to the next page if so (similar to logic for section titles)
|
835
|
-
|
841
|
+
ink_caption node, labeled: false if node.title?
|
842
|
+
|
843
|
+
if (bottom_gutter = @bottom_gutters[-1][node])
|
844
|
+
prose_opts[:bottom_gutter] = bottom_gutter
|
845
|
+
end
|
836
846
|
|
837
847
|
if roles.empty?
|
838
|
-
|
848
|
+
ink_prose node.content, prose_opts
|
839
849
|
else
|
840
850
|
theme_font_cascade (roles.map {|role| %(role_#{role}).to_sym }) do
|
841
|
-
|
851
|
+
ink_prose node.content, prose_opts
|
842
852
|
end
|
843
853
|
end
|
844
854
|
|
@@ -852,7 +862,7 @@ module Asciidoctor
|
|
852
862
|
|
853
863
|
def convert_admonition node
|
854
864
|
type = node.attr 'name'
|
855
|
-
label_align = @theme.
|
865
|
+
label_align = @theme.admonition_label_text_align&.to_sym || :center
|
856
866
|
# TODO: allow vertical_align to be a number
|
857
867
|
if (label_valign = @theme.admonition_label_vertical_align&.to_sym || :middle) == :middle
|
858
868
|
label_valign = :center
|
@@ -861,18 +871,21 @@ module Asciidoctor
|
|
861
871
|
label_min_width = label_min_width.to_f
|
862
872
|
end
|
863
873
|
if (doc = node.document).attr? 'icons'
|
864
|
-
if (
|
874
|
+
if !(has_icon = node.attr? 'icon') && (doc.attr 'icons') == 'font'
|
865
875
|
icons = 'font'
|
866
876
|
label_text = type.to_sym
|
867
877
|
icon_data = admonition_icon_data label_text
|
868
878
|
icon_size = icon_data[:size] || 24
|
869
879
|
label_width = label_min_width || (icon_size * 1.5)
|
870
|
-
elsif (icon_path =
|
880
|
+
elsif (icon_path = has_icon || !(icon_path = (@theme[%(admonition_icon_#{type})] || {})[:image]) ?
|
881
|
+
(resolve_icon_image_path node, type) :
|
882
|
+
(ThemeLoader.resolve_theme_asset (apply_subs_discretely doc, icon_path, subs: [:attributes]), @themesdir)) &&
|
883
|
+
(::File.readable? icon_path)
|
871
884
|
icons = true
|
872
885
|
# TODO: introduce @theme.admonition_image_width? or use size key from admonition_icon_<name>?
|
873
886
|
label_width = label_min_width || 36.0
|
874
887
|
else
|
875
|
-
log :warn, %(admonition icon not found or not readable: #{icon_path || (resolve_icon_image_path node, type, false)})
|
888
|
+
log :warn, %(admonition icon image#{has_icon ? '' : ' for ' + type.upcase} not found or not readable: #{icon_path || (resolve_icon_image_path node, type, false)})
|
876
889
|
end
|
877
890
|
end
|
878
891
|
unless icons
|
@@ -883,20 +896,16 @@ module Asciidoctor
|
|
883
896
|
label_width = label_min_width if label_min_width && label_min_width > label_width
|
884
897
|
end
|
885
898
|
end
|
886
|
-
|
887
|
-
|
888
|
-
end
|
889
|
-
unless ::Array === (lpad = @theme.admonition_label_padding || cpad)
|
890
|
-
lpad = ::Array.new 4, lpad
|
891
|
-
end
|
899
|
+
cpad = expand_padding_value @theme.admonition_padding
|
900
|
+
lpad = (lpad = @theme.admonition_label_padding) ? (expand_padding_value lpad) : cpad
|
892
901
|
arrange_block node do |extent|
|
893
902
|
add_dest_for_block node if node.id
|
894
903
|
theme_fill_and_stroke_block :admonition, extent if extent
|
895
904
|
pad_box [0, cpad[1], 0, lpad[3]] do
|
896
905
|
if extent
|
897
906
|
label_height = extent.single_page_height || cursor
|
898
|
-
if (
|
899
|
-
(
|
907
|
+
if (rule_width = @theme.admonition_column_rule_width || 0) > 0 &&
|
908
|
+
(rule_color = @theme.admonition_column_rule_color || @theme.base_border_color)
|
900
909
|
rule_style = @theme.admonition_column_rule_style&.to_sym || :solid
|
901
910
|
float do
|
902
911
|
extent.each_page do |first_page, last_page|
|
@@ -945,7 +954,7 @@ module Asciidoctor
|
|
945
954
|
log :warn, %(problem encountered in image: #{icon_path}; #{icon_warning})
|
946
955
|
end unless scratch?
|
947
956
|
rescue
|
948
|
-
log :warn, %(could not embed admonition icon: #{icon_path}; #{$!.message})
|
957
|
+
log :warn, %(could not embed admonition icon image: #{icon_path}; #{$!.message})
|
949
958
|
icons = nil
|
950
959
|
end
|
951
960
|
else
|
@@ -959,7 +968,7 @@ module Asciidoctor
|
|
959
968
|
end
|
960
969
|
embed_image image_obj, image_info, width: icon_width, position: label_align, vposition: label_valign
|
961
970
|
rescue
|
962
|
-
log :warn, %(could not embed admonition icon: #{icon_path}; #{$!.message})
|
971
|
+
log :warn, %(could not embed admonition icon image: #{icon_path}; #{$!.message})
|
963
972
|
icons = nil
|
964
973
|
end
|
965
974
|
end
|
@@ -987,7 +996,7 @@ module Asciidoctor
|
|
987
996
|
end
|
988
997
|
end
|
989
998
|
@text_transform = nil # already applied to label
|
990
|
-
|
999
|
+
ink_prose label_text,
|
991
1000
|
align: label_align,
|
992
1001
|
valign: label_valign,
|
993
1002
|
line_height: 1,
|
@@ -1000,8 +1009,8 @@ module Asciidoctor
|
|
1000
1009
|
end
|
1001
1010
|
end
|
1002
1011
|
end
|
1003
|
-
pad_box [cpad[0], 0, cpad[2], label_width + lpad[1] + cpad[3]] do
|
1004
|
-
|
1012
|
+
pad_box [cpad[0], 0, cpad[2], label_width + lpad[1] + cpad[3]], node do
|
1013
|
+
ink_caption node, category: :admonition, labeled: false if node.title?
|
1005
1014
|
theme_font :admonition do
|
1006
1015
|
traverse node
|
1007
1016
|
end
|
@@ -1018,7 +1027,7 @@ module Asciidoctor
|
|
1018
1027
|
tare_first_page_content_stream do
|
1019
1028
|
theme_fill_and_stroke_block :example, extent, caption_node: node
|
1020
1029
|
end
|
1021
|
-
pad_box @theme.example_padding do
|
1030
|
+
pad_box @theme.example_padding, node do
|
1022
1031
|
theme_font :example do
|
1023
1032
|
traverse node
|
1024
1033
|
end
|
@@ -1035,13 +1044,13 @@ module Asciidoctor
|
|
1035
1044
|
arrange_block node do
|
1036
1045
|
add_dest_for_block node if id
|
1037
1046
|
tare_first_page_content_stream do
|
1038
|
-
node.context == :example ? (
|
1047
|
+
node.context == :example ? (ink_caption %(\u25bc #{node.title})) : (ink_caption node, labeled: false)
|
1039
1048
|
end if has_title
|
1040
1049
|
traverse node
|
1041
1050
|
end
|
1042
1051
|
else
|
1043
1052
|
add_dest_for_block node if id
|
1044
|
-
node.context == :example ? (
|
1053
|
+
node.context == :example ? (ink_caption %(\u25bc #{node.title})) : (ink_caption node, labeled: false) if has_title
|
1045
1054
|
traverse node
|
1046
1055
|
end
|
1047
1056
|
end
|
@@ -1050,11 +1059,18 @@ module Asciidoctor
|
|
1050
1059
|
category = node.context == :quote ? :quote : :verse
|
1051
1060
|
# NOTE: b_width and b_left_width are mutually exclusive
|
1052
1061
|
if (b_left_width = @theme[%(#{category}_border_left_width)]) && b_left_width > 0
|
1053
|
-
b_color = @theme[%(#{category}_border_color)]
|
1062
|
+
b_color = @theme[%(#{category}_border_color)] || @theme.base_border_color
|
1054
1063
|
else
|
1055
1064
|
b_left_width = nil
|
1056
1065
|
b_width = nil if (b_width = @theme[%(#{category}_border_width)]) == 0
|
1057
1066
|
end
|
1067
|
+
if (attribution = (node.attr? 'attribution') && (node.attr 'attribution'))
|
1068
|
+
# NOTE: temporary workaround to allow bare & to be used without having to wrap value in single quotes
|
1069
|
+
attribution = escape_amp attribution if attribution.include? '&'
|
1070
|
+
if (citetitle = node.attr 'citetitle') && (citetitle.include? '&')
|
1071
|
+
citetitle = escape_amp citetitle
|
1072
|
+
end
|
1073
|
+
end
|
1058
1074
|
arrange_block node do |extent|
|
1059
1075
|
add_dest_for_block node if node.id
|
1060
1076
|
tare_first_page_content_stream do
|
@@ -1072,27 +1088,25 @@ module Asciidoctor
|
|
1072
1088
|
end
|
1073
1089
|
end
|
1074
1090
|
end
|
1075
|
-
pad_box @theme[%(#{category}_padding)] do
|
1091
|
+
pad_box @theme[%(#{category}_padding)], (attribution ? nil : node) do
|
1076
1092
|
theme_font category do
|
1077
1093
|
if category == :quote
|
1078
1094
|
traverse node
|
1079
1095
|
else # :verse
|
1080
1096
|
content = guard_indentation node.content
|
1081
|
-
|
1097
|
+
ink_prose content,
|
1098
|
+
normalize: false,
|
1099
|
+
align: :left,
|
1100
|
+
hyphenate: true,
|
1101
|
+
margin_bottom: 0,
|
1102
|
+
bottom_gutter: (attribution ? nil : @bottom_gutters[-1][node])
|
1082
1103
|
end
|
1083
1104
|
end
|
1084
|
-
if
|
1105
|
+
if attribution
|
1085
1106
|
margin_bottom @theme.block_margin_bottom
|
1086
1107
|
theme_font %(#{category}_cite) do
|
1087
|
-
|
1088
|
-
|
1089
|
-
attribution = escape_amp attribution if attribution.include? '&'
|
1090
|
-
attribution_parts = [attribution]
|
1091
|
-
if (citetitle = node.attr 'citetitle')
|
1092
|
-
citetitle = escape_amp citetitle if citetitle.include? '&'
|
1093
|
-
attribution_parts << citetitle
|
1094
|
-
end
|
1095
|
-
layout_prose %(#{EmDash} #{attribution_parts.join ', '}), align: :left, normalize: false, margin_bottom: 0
|
1108
|
+
attribution_parts = citetitle ? [attribution, citetitle] : [attribution]
|
1109
|
+
ink_prose %(#{EmDash} #{attribution_parts.join ', '}), align: :left, normalize: false, margin_bottom: 0
|
1096
1110
|
end
|
1097
1111
|
end
|
1098
1112
|
end
|
@@ -1107,10 +1121,10 @@ module Asciidoctor
|
|
1107
1121
|
arrange_block node do |extent|
|
1108
1122
|
add_dest_for_block node if node.id
|
1109
1123
|
theme_fill_and_stroke_block :sidebar, extent if extent
|
1110
|
-
pad_box @theme.sidebar_padding do
|
1124
|
+
pad_box @theme.sidebar_padding, node do
|
1111
1125
|
theme_font :sidebar_title do
|
1112
1126
|
# QUESTION: should we allow margins of sidebar title to be customized?
|
1113
|
-
|
1127
|
+
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)
|
1114
1128
|
end if node.title?
|
1115
1129
|
theme_font :sidebar do
|
1116
1130
|
traverse node
|
@@ -1149,7 +1163,7 @@ module Asciidoctor
|
|
1149
1163
|
marker_width = rendered_width_of_string %(#{marker = conum_glyph index}x)
|
1150
1164
|
float do
|
1151
1165
|
bounding_box [0, cursor], width: marker_width do
|
1152
|
-
|
1166
|
+
ink_prose marker, align: :center, inline_format: false, margin: 0
|
1153
1167
|
end
|
1154
1168
|
end
|
1155
1169
|
end
|
@@ -1233,7 +1247,7 @@ module Asciidoctor
|
|
1233
1247
|
max_term_width += (term_padding[1] + term_padding[3])
|
1234
1248
|
term_column_width = [max_term_width, bounds.width * 0.5].min
|
1235
1249
|
table table_data, position: :left, cell_style: { border_width: 0 }, column_widths: [term_column_width] do
|
1236
|
-
@pdf.
|
1250
|
+
@pdf.ink_table_caption node if node.title?
|
1237
1251
|
end
|
1238
1252
|
theme_margin :prose, :bottom, (next_enclosed_block actual_node) #unless actual_node.nested?
|
1239
1253
|
when 'qanda'
|
@@ -1243,7 +1257,7 @@ module Asciidoctor
|
|
1243
1257
|
else
|
1244
1258
|
# TODO: check if we're within one line of the bottom of the page
|
1245
1259
|
# and advance to the next page if so (similar to logic for section titles)
|
1246
|
-
|
1260
|
+
ink_caption node, category: :description_list, labeled: false if node.title?
|
1247
1261
|
|
1248
1262
|
term_spacing = @theme.description_list_term_spacing
|
1249
1263
|
term_height = theme_font(:description_list_term) { height_of_typeset_text 'A' }
|
@@ -1255,8 +1269,8 @@ module Asciidoctor
|
|
1255
1269
|
term_font_styles = nil
|
1256
1270
|
end
|
1257
1271
|
terms.each_with_index do |term, idx|
|
1258
|
-
# QUESTION: should we pass down styles in other calls to
|
1259
|
-
|
1272
|
+
# QUESTION: should we pass down styles in other calls to ink_prose
|
1273
|
+
ink_prose term.text, margin_top: (idx > 0 ? term_spacing : 0), margin_bottom: 0, align: :left, normalize_line_height: true, styles: term_font_styles
|
1260
1274
|
end
|
1261
1275
|
end
|
1262
1276
|
indent @theme.description_list_description_indent do
|
@@ -1343,7 +1357,7 @@ module Asciidoctor
|
|
1343
1357
|
def convert_list node
|
1344
1358
|
# TODO: check if we're within one line of the bottom of the page
|
1345
1359
|
# and advance to the next page if so (similar to logic for section titles)
|
1346
|
-
|
1360
|
+
ink_caption node, category: :list, labeled: false if node.title?
|
1347
1361
|
|
1348
1362
|
opts = {}
|
1349
1363
|
if (align = resolve_alignment_from_role node.roles)
|
@@ -1440,7 +1454,7 @@ module Asciidoctor
|
|
1440
1454
|
float do
|
1441
1455
|
advance_page if @media == 'prepress' && cursor < marker_height
|
1442
1456
|
flow_bounding_box position: start_position, width: marker_width do
|
1443
|
-
|
1457
|
+
ink_prose marker,
|
1444
1458
|
align: :right,
|
1445
1459
|
character_spacing: -0.5,
|
1446
1460
|
color: marker_style[:font_color],
|
@@ -1468,16 +1482,16 @@ module Asciidoctor
|
|
1468
1482
|
def traverse_list_item node, list_type, opts = {}
|
1469
1483
|
if list_type == :dlist # qanda
|
1470
1484
|
terms, desc = node
|
1471
|
-
terms.each {|term|
|
1485
|
+
terms.each {|term| ink_prose %(<em>#{term.text}</em>), (opts.merge margin_bottom: @theme.description_list_term_spacing) }
|
1472
1486
|
if desc
|
1473
|
-
|
1487
|
+
ink_prose desc.text, (opts.merge hyphenate: true) if desc.text?
|
1474
1488
|
traverse desc
|
1475
1489
|
end
|
1476
1490
|
else
|
1477
1491
|
if (primary_text = node.text).nil_or_empty?
|
1478
|
-
|
1492
|
+
ink_prose DummyText, opts unless node.blocks?
|
1479
1493
|
else
|
1480
|
-
|
1494
|
+
ink_prose primary_text, (opts.merge hyphenate: true)
|
1481
1495
|
end
|
1482
1496
|
traverse node
|
1483
1497
|
end
|
@@ -1498,24 +1512,33 @@ module Asciidoctor
|
|
1498
1512
|
elsif (image_path = resolve_image_path node, target, image_format, (opts.fetch :relative_to_imagesdir, true))
|
1499
1513
|
if image_format == 'pdf'
|
1500
1514
|
if ::File.readable? image_path
|
1501
|
-
if (
|
1515
|
+
if (replace = page.empty?) && ((parent = node.parent).attr? 'pdf-page-start', page_number) && (parent.attr? 'pdf-anchor')
|
1516
|
+
replace_parent = parent
|
1517
|
+
end
|
1518
|
+
if (id = node.id) || replace_parent
|
1502
1519
|
add_dest_block = proc do
|
1503
|
-
|
1504
|
-
|
1520
|
+
node_dest = dest_top
|
1521
|
+
if id
|
1522
|
+
node.set_attr 'pdf-destination', node_dest
|
1523
|
+
add_dest id, node_dest
|
1524
|
+
end
|
1525
|
+
if replace_parent
|
1526
|
+
replace_parent.set_attr 'pdf-destination', node_dest
|
1527
|
+
add_dest (replace_parent.attr 'pdf-anchor'), node_dest
|
1528
|
+
end
|
1505
1529
|
end
|
1506
1530
|
end
|
1507
1531
|
# NOTE: import_page automatically advances to next page afterwards
|
1508
|
-
# QUESTION: should we add destination to top of imported page?
|
1509
1532
|
if (pgnums = node.attr 'pages')
|
1510
1533
|
(resolve_pagenums pgnums).each_with_index do |pgnum, idx|
|
1511
1534
|
if idx == 0
|
1512
|
-
import_page image_path, page: pgnum, replace:
|
1535
|
+
import_page image_path, page: pgnum, replace: replace, &add_dest_block
|
1513
1536
|
else
|
1514
1537
|
import_page image_path, page: pgnum, replace: true
|
1515
1538
|
end
|
1516
1539
|
end
|
1517
1540
|
else
|
1518
|
-
import_page image_path, page: [(node.attr 'page', nil, 1).to_i, 1].max, replace:
|
1541
|
+
import_page image_path, page: [(node.attr 'page', nil, 1).to_i, 1].max, replace: replace, &add_dest_block
|
1519
1542
|
end
|
1520
1543
|
return
|
1521
1544
|
else
|
@@ -1532,14 +1555,16 @@ module Asciidoctor
|
|
1532
1555
|
|
1533
1556
|
alignment = (alignment = node.attr 'align') ?
|
1534
1557
|
((BlockAlignmentNames.include? alignment) ? alignment.to_sym : :left) :
|
1535
|
-
(resolve_alignment_from_role node.roles) ||
|
1558
|
+
(resolve_alignment_from_role node.roles) || @theme.image_align&.to_sym || :left
|
1536
1559
|
# TODO: support cover (aka canvas) image layout using "canvas" (or "cover") role
|
1537
1560
|
width = resolve_explicit_width node.attributes, bounds_width: (available_w = bounds.width), support_vw: true, use_fallback: true, constrain_to_bounds: true
|
1538
1561
|
# TODO: add `to_pt page_width` method to ViewportWidth type
|
1539
1562
|
width = (width.to_f / 100) * page_width if ViewportWidth === width
|
1540
1563
|
|
1564
|
+
caption_end = @theme.image_caption_end&.to_sym || :bottom
|
1565
|
+
caption_max_width = @theme.image_caption_max_width
|
1541
1566
|
# NOTE: if width is not set explicitly and max-width is fit-content, caption height may not be accurate
|
1542
|
-
caption_h = node.title? ? (
|
1567
|
+
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
|
1543
1568
|
|
1544
1569
|
align_to_page = node.option? 'align-to-page'
|
1545
1570
|
pinned = opts[:pinned]
|
@@ -1575,14 +1600,14 @@ module Asciidoctor
|
|
1575
1600
|
end
|
1576
1601
|
rendered_w = (svg_obj.resize height: (rendered_h = available_h)).output_width if rendered_h > available_h
|
1577
1602
|
end
|
1578
|
-
image_y = y
|
1579
|
-
image_cursor = cursor
|
1580
1603
|
add_dest_for_block node if node.id
|
1581
1604
|
# NOTE: workaround to fix Prawn not adding fill and stroke commands on page that only has an image;
|
1582
1605
|
# breakage occurs when running content (stamps) are added to page
|
1583
1606
|
update_colors if graphic_state.color_space.empty?
|
1584
|
-
|
1585
|
-
|
1607
|
+
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?
|
1608
|
+
image_y = y
|
1609
|
+
image_cursor = cursor
|
1610
|
+
svg_obj.draw # NOTE: cursor advances automatically
|
1586
1611
|
svg_obj.document.warnings.each do |img_warning|
|
1587
1612
|
log :warn, %(problem encountered in image: #{image_path}; #{img_warning})
|
1588
1613
|
end unless scratch?
|
@@ -1606,12 +1631,13 @@ module Asciidoctor
|
|
1606
1631
|
end
|
1607
1632
|
rendered_w, rendered_h = image_info.calc_image_dimensions height: available_h if rendered_h > available_h
|
1608
1633
|
end
|
1609
|
-
image_y = y
|
1610
|
-
image_cursor = cursor
|
1611
1634
|
add_dest_for_block node if node.id
|
1612
1635
|
# NOTE: workaround to fix Prawn not adding fill and stroke commands on page that only has an image;
|
1613
1636
|
# breakage occurs when running content (stamps) are added to page
|
1614
1637
|
update_colors if graphic_state.color_space.empty?
|
1638
|
+
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?
|
1639
|
+
image_y = y
|
1640
|
+
image_cursor = cursor
|
1615
1641
|
# NOTE: specify both width and height to avoid recalculation
|
1616
1642
|
embed_image image_obj, image_info, width: rendered_w, height: rendered_h, position: alignment
|
1617
1643
|
draw_image_border image_cursor, rendered_w, rendered_h, alignment unless node.role? && (node.has_role? 'noborder')
|
@@ -1622,7 +1648,7 @@ module Asciidoctor
|
|
1622
1648
|
move_down rendered_h if y == image_y
|
1623
1649
|
end
|
1624
1650
|
end
|
1625
|
-
|
1651
|
+
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?
|
1626
1652
|
theme_margin :block, :bottom, (next_enclosed_block node) unless pinned
|
1627
1653
|
rescue => e
|
1628
1654
|
raise if ::StopIteration === e
|
@@ -1631,7 +1657,7 @@ module Asciidoctor
|
|
1631
1657
|
end
|
1632
1658
|
|
1633
1659
|
def draw_image_border top, w, h, alignment
|
1634
|
-
if @theme.image_border_width
|
1660
|
+
if (Array @theme.image_border_width).any? {|it| it&.> 0 } && (@theme.image_border_color || @theme.base_border_color)
|
1635
1661
|
if (@theme.image_border_fit || 'content') == 'auto'
|
1636
1662
|
bb_width = bounds.width
|
1637
1663
|
elsif alignment == :center
|
@@ -1662,9 +1688,9 @@ module Asciidoctor
|
|
1662
1688
|
alignment = (alignment = node.attr 'align') ?
|
1663
1689
|
((BlockAlignmentNames.include? alignment) ? alignment.to_sym : :left) :
|
1664
1690
|
(resolve_alignment_from_role node.roles) || (@theme.image_align&.to_sym || :left)
|
1665
|
-
|
1691
|
+
ink_prose alt_text_template % alt_text_vars, align: alignment, margin: 0, normalize: false, single_line: true
|
1666
1692
|
end
|
1667
|
-
|
1693
|
+
ink_caption node, category: :image, end: :bottom if node.title?
|
1668
1694
|
theme_margin :block, :bottom, (next_enclosed_block node) unless opts[:pinned]
|
1669
1695
|
nil
|
1670
1696
|
end
|
@@ -1673,8 +1699,8 @@ module Asciidoctor
|
|
1673
1699
|
add_dest_for_block node if node.id
|
1674
1700
|
audio_path = node.media_uri node.attr 'target'
|
1675
1701
|
play_symbol = (node.document.attr? 'icons', 'font') ? %(<font name="fas">#{(icon_font_data 'fas').unicode 'play'}</font>) : RightPointer
|
1676
|
-
|
1677
|
-
|
1702
|
+
ink_prose %(#{play_symbol}#{NoBreakSpace}<a href="#{audio_path}">#{audio_path}</a> <em>(audio)</em>), normalize: false, margin: 0, single_line: true
|
1703
|
+
ink_caption node, labeled: false, end: :bottom if node.title?
|
1678
1704
|
theme_margin :block, :bottom, (next_enclosed_block node)
|
1679
1705
|
end
|
1680
1706
|
|
@@ -1701,8 +1727,8 @@ module Asciidoctor
|
|
1701
1727
|
if poster.nil_or_empty?
|
1702
1728
|
add_dest_for_block node if node.id
|
1703
1729
|
play_symbol = (node.document.attr? 'icons', 'font') ? %(<font name="fas">#{(icon_font_data 'fas').unicode 'play'}</font>) : RightPointer
|
1704
|
-
|
1705
|
-
|
1730
|
+
ink_prose %(#{play_symbol}#{NoBreakSpace}<a href="#{video_path}">#{video_path}</a> <em>(#{type})</em>), normalize: false, margin: 0, single_line: true
|
1731
|
+
ink_caption node, labeled: false, end: :bottom if node.title?
|
1706
1732
|
theme_margin :block, :bottom, (next_enclosed_block node)
|
1707
1733
|
else
|
1708
1734
|
original_attributes = node.attributes.dup
|
@@ -1717,7 +1743,8 @@ module Asciidoctor
|
|
1717
1743
|
|
1718
1744
|
# QUESTION: can we avoid arranging fragments multiple times (conums & autofit) by eagerly preparing arranger?
|
1719
1745
|
def convert_listing_or_literal node
|
1720
|
-
|
1746
|
+
extensions = []
|
1747
|
+
source_chunks = bg_color_override = font_color_override = adjusted_font_size = nil
|
1721
1748
|
theme_font :code do
|
1722
1749
|
# HACK: disable built-in syntax highlighter; must be done before calling node.content!
|
1723
1750
|
if node.style == 'source' && (highlighter = (syntax_hl = node.document.syntax_highlighter)&.highlight? && syntax_hl.name)
|
@@ -1797,7 +1824,7 @@ module Asciidoctor
|
|
1797
1824
|
if (node.option? 'linenums') || (node.attr? 'linenums')
|
1798
1825
|
linenums = (node.attr 'start', 1).to_i
|
1799
1826
|
postprocess = true
|
1800
|
-
|
1827
|
+
extensions << FormattedText::SourceWrap
|
1801
1828
|
elsif conum_mapping || highlight_lines
|
1802
1829
|
postprocess = true
|
1803
1830
|
end
|
@@ -1814,7 +1841,7 @@ module Asciidoctor
|
|
1814
1841
|
else
|
1815
1842
|
if (node.option? 'linenums') || (node.attr? 'linenums')
|
1816
1843
|
formatter_opts = { line_numbers: true, start_line: (node.attr 'start', 1).to_i }
|
1817
|
-
|
1844
|
+
extensions << FormattedText::SourceWrap
|
1818
1845
|
else
|
1819
1846
|
formatter_opts = {}
|
1820
1847
|
end
|
@@ -1848,14 +1875,13 @@ module Asciidoctor
|
|
1848
1875
|
tare_first_page_content_stream do
|
1849
1876
|
theme_fill_and_stroke_block :code, extent, background_color: bg_color_override, caption_node: node
|
1850
1877
|
end
|
1851
|
-
pad_box @theme.code_padding do
|
1878
|
+
pad_box @theme.code_padding, node do
|
1852
1879
|
theme_font :code do
|
1853
|
-
::Prawn::Text::Formatted::Box.extensions << wrap_ext if wrap_ext
|
1854
1880
|
typeset_formatted_text source_chunks, (calc_line_metrics @base_line_height),
|
1855
1881
|
color: (font_color_override || @theme.code_font_color || @font_color),
|
1856
|
-
size: adjusted_font_size
|
1857
|
-
|
1858
|
-
|
1882
|
+
size: adjusted_font_size,
|
1883
|
+
bottom_gutter: @bottom_gutters[-1][node],
|
1884
|
+
extensions: extensions.empty? ? nil : extensions
|
1859
1885
|
end
|
1860
1886
|
end
|
1861
1887
|
end
|
@@ -2001,6 +2027,7 @@ module Asciidoctor
|
|
2001
2027
|
|
2002
2028
|
base_header_cell_data = nil
|
2003
2029
|
header_cell_line_metrics = nil
|
2030
|
+
body_cell_padding = expand_padding_value theme.table_cell_padding
|
2004
2031
|
|
2005
2032
|
table_data = []
|
2006
2033
|
theme_font :table do
|
@@ -2015,7 +2042,7 @@ module Asciidoctor
|
|
2015
2042
|
table_header_size = head_rows.size
|
2016
2043
|
head_font_info = font_info
|
2017
2044
|
head_line_metrics = calc_line_metrics theme.table_head_line_height || theme.table_cell_line_height || @base_line_height
|
2018
|
-
head_cell_padding =
|
2045
|
+
head_cell_padding = ((head_cell_padding = theme.table_head_cell_padding) ? (expand_padding_value head_cell_padding) : body_cell_padding).dup
|
2019
2046
|
head_cell_padding[0] += head_line_metrics.padding_top
|
2020
2047
|
head_cell_padding[2] += head_line_metrics.padding_bottom
|
2021
2048
|
# QUESTION: why doesn't text transform inherit from table?
|
@@ -2054,7 +2081,6 @@ module Asciidoctor
|
|
2054
2081
|
text_color: @font_color,
|
2055
2082
|
}
|
2056
2083
|
body_cell_line_metrics = calc_line_metrics (theme.table_cell_line_height || @base_line_height)
|
2057
|
-
body_cell_padding = expand_padding_value theme.table_cell_padding
|
2058
2084
|
(body_rows + node.rows[:foot]).each do |row|
|
2059
2085
|
table_data << (row.map do |cell|
|
2060
2086
|
cell_data = base_cell_data.merge \
|
@@ -2121,7 +2147,7 @@ module Asciidoctor
|
|
2121
2147
|
end
|
2122
2148
|
# NOTE: line metrics get applied when AsciiDoc content is converted
|
2123
2149
|
cell_line_metrics = nil
|
2124
|
-
asciidoc_cell = ::Prawn::Table::Cell::AsciiDoc.new self, (cell_data.merge content: cell.inner_document, padding: body_cell_padding
|
2150
|
+
asciidoc_cell = ::Prawn::Table::Cell::AsciiDoc.new self, (cell_data.merge content: cell.inner_document, padding: body_cell_padding)
|
2125
2151
|
cell_data = { content: asciidoc_cell }
|
2126
2152
|
end
|
2127
2153
|
if cell_line_metrics
|
@@ -2159,45 +2185,36 @@ module Asciidoctor
|
|
2159
2185
|
end
|
2160
2186
|
end
|
2161
2187
|
|
2162
|
-
# NOTE: Prawn
|
2188
|
+
# NOTE: Prawn crashes if table data is empty, so ensure there's at least one row
|
2163
2189
|
if table_data.empty?
|
2164
2190
|
log(:warn) { message_with_context 'no rows found in table', source_location: node.source_location }
|
2165
2191
|
table_data << ::Array.new([node.columns.size, 1].max) { { content: '' } }
|
2166
2192
|
end
|
2167
2193
|
|
2168
|
-
|
2169
|
-
|
2170
|
-
|
2171
|
-
|
2172
|
-
|
2173
|
-
|
2174
|
-
|
2175
|
-
|
2176
|
-
end
|
2177
|
-
[:top, :bottom, :left, :right].each {|edge| border_width[edge] = table_border_width }
|
2194
|
+
rect_side_names = [:top, :right, :bottom, :left]
|
2195
|
+
grid_axis_names = [:rows, :cols]
|
2196
|
+
border_color = (rect_side_names.zip expand_rect_values theme.table_border_color, 'transparent').to_h
|
2197
|
+
border_style = (rect_side_names.zip (expand_rect_values theme.table_border_style, :solid).map(&:to_sym)).to_h
|
2198
|
+
border_width = (rect_side_names.zip expand_rect_values theme.table_border_width, 0).to_h
|
2199
|
+
grid_color = (grid_axis_names.zip expand_grid_values (theme.table_grid_color || [border_color[:top], border_color[:left]]), 'transparent').to_h
|
2200
|
+
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
|
2201
|
+
grid_width = (grid_axis_names.zip expand_grid_values (theme.table_grid_width || [border_width[:top], border_width[:left]]), 0).to_h
|
2178
2202
|
|
2179
|
-
|
2180
|
-
|
2181
|
-
|
2182
|
-
|
2183
|
-
table_grid_style = [table_grid_style.to_sym]
|
2184
|
-
end
|
2185
|
-
if ::Array === (table_grid_width = theme.table_grid_width || theme.table_border_width)
|
2186
|
-
border_width[:rows] = table_grid_width[0]
|
2187
|
-
border_width[:cols] = table_grid_width[1]
|
2188
|
-
else
|
2189
|
-
[:cols, :rows].each {|edge| border_width[edge] = table_grid_width }
|
2203
|
+
if table_header_size
|
2204
|
+
head_border_bottom_color = theme.table_head_border_bottom_color || grid_color[:rows]
|
2205
|
+
head_border_bottom_style = theme.table_head_border_bottom_style&.to_sym || grid_style[:rows]
|
2206
|
+
head_border_bottom_width = theme.table_head_border_bottom_width || (grid_width[:rows] * 2.5)
|
2190
2207
|
end
|
2191
2208
|
|
2192
2209
|
case (grid = node.attr 'grid', 'all', 'table-grid')
|
2193
2210
|
when 'all'
|
2194
2211
|
# keep inner borders
|
2195
2212
|
when 'cols'
|
2196
|
-
|
2213
|
+
grid_width[:rows] = 0
|
2197
2214
|
when 'rows'
|
2198
|
-
|
2215
|
+
grid_width[:cols] = 0
|
2199
2216
|
else # none
|
2200
|
-
|
2217
|
+
grid_width[:rows] = grid_width[:cols] = 0
|
2201
2218
|
end
|
2202
2219
|
|
2203
2220
|
case (frame = node.attr 'frame', 'all', 'table-frame')
|
@@ -2227,20 +2244,15 @@ module Asciidoctor
|
|
2227
2244
|
alignment = theme.table_align&.to_sym || :left
|
2228
2245
|
end
|
2229
2246
|
|
2230
|
-
|
2247
|
+
caption_end = theme.table_caption_end&.to_sym || :top
|
2231
2248
|
caption_max_width = theme.table_caption_max_width || 'fit-content'
|
2232
2249
|
|
2233
2250
|
table_settings = {
|
2234
2251
|
header: table_header_size,
|
2235
2252
|
# NOTE: position is handled by this method
|
2236
2253
|
position: :left,
|
2237
|
-
|
2238
|
-
|
2239
|
-
border_color: table_grid_color,
|
2240
|
-
border_lines: table_grid_style,
|
2241
|
-
# NOTE: the border width is set later
|
2242
|
-
border_width: 0,
|
2243
|
-
},
|
2254
|
+
# NOTE: the border color, style, and width of the outer frame is set in the table callback block
|
2255
|
+
cell_style: { border_color: grid_color.values, border_lines: grid_style.values, border_width: grid_width.values },
|
2244
2256
|
width: table_width,
|
2245
2257
|
column_widths: column_widths,
|
2246
2258
|
}
|
@@ -2261,7 +2273,7 @@ module Asciidoctor
|
|
2261
2273
|
table table_data, table_settings do
|
2262
2274
|
# NOTE: call width to capture resolved table width
|
2263
2275
|
table_width = width
|
2264
|
-
@pdf.
|
2276
|
+
@pdf.ink_table_caption node, alignment, table_width, caption_max_width if node.title? && caption_end == :top
|
2265
2277
|
# NOTE: align using padding instead of bounding_box as prawn-table does
|
2266
2278
|
# using a bounding_box across pages mangles the margin box of subsequent pages
|
2267
2279
|
if alignment != :left && table_width != (this_bounds = @pdf.bounds).width
|
@@ -2282,7 +2294,7 @@ module Asciidoctor
|
|
2282
2294
|
end if table_header_size
|
2283
2295
|
else
|
2284
2296
|
# apply the grid setting first across all cells
|
2285
|
-
cells.border_width = [
|
2297
|
+
cells.border_width = [grid_width[:rows], grid_width[:cols], grid_width[:rows], grid_width[:cols]]
|
2286
2298
|
|
2287
2299
|
if table_header_size
|
2288
2300
|
(rows table_header_size - 1).tap do |r|
|
@@ -2299,19 +2311,19 @@ module Asciidoctor
|
|
2299
2311
|
|
2300
2312
|
# top edge of table
|
2301
2313
|
(rows 0).tap do |r|
|
2302
|
-
r.border_top_color, r.border_top_line, r.border_top_width =
|
2314
|
+
r.border_top_color, r.border_top_line, r.border_top_width = border_color[:top], border_style[:top], border_width[:top]
|
2303
2315
|
end
|
2304
2316
|
# right edge of table
|
2305
2317
|
(columns num_cols - 1).tap do |r|
|
2306
|
-
r.border_right_color, r.border_right_line, r.border_right_width =
|
2318
|
+
r.border_right_color, r.border_right_line, r.border_right_width = border_color[:right], border_style[:right], border_width[:right]
|
2307
2319
|
end
|
2308
2320
|
# bottom edge of table
|
2309
2321
|
(rows num_rows - 1).tap do |r|
|
2310
|
-
r.border_bottom_color, r.border_bottom_line, r.border_bottom_width =
|
2322
|
+
r.border_bottom_color, r.border_bottom_line, r.border_bottom_width = border_color[:bottom], border_style[:bottom], border_width[:bottom]
|
2311
2323
|
end
|
2312
2324
|
# left edge of table
|
2313
2325
|
(columns 0).tap do |r|
|
2314
|
-
r.border_left_color, r.border_left_line, r.border_left_width =
|
2326
|
+
r.border_left_color, r.border_left_line, r.border_left_width = border_color[:left], border_style[:left], border_width[:left]
|
2315
2327
|
end
|
2316
2328
|
end
|
2317
2329
|
|
@@ -2334,7 +2346,7 @@ module Asciidoctor
|
|
2334
2346
|
bounds.subtract_left_padding left_padding
|
2335
2347
|
bounds.subtract_right_padding right_padding if right_padding
|
2336
2348
|
end
|
2337
|
-
|
2349
|
+
ink_table_caption node, alignment, table_width, caption_max_width, caption_end if node.title? && caption_end == :bottom
|
2338
2350
|
theme_margin :block, :bottom, (next_enclosed_block node)
|
2339
2351
|
rescue ::Prawn::Errors::CannotFit
|
2340
2352
|
log :error, (message_with_context 'cannot fit contents of table cell into specified column width', source_location: node.source_location)
|
@@ -2342,12 +2354,14 @@ module Asciidoctor
|
|
2342
2354
|
|
2343
2355
|
def convert_thematic_break node
|
2344
2356
|
theme_margin :thematic_break, :top
|
2345
|
-
stroke_horizontal_rule @theme.thematic_break_border_color,
|
2357
|
+
stroke_horizontal_rule @theme.thematic_break_border_color,
|
2358
|
+
line_width: @theme.thematic_break_border_width,
|
2359
|
+
line_style: (@theme.thematic_break_border_style&.to_sym || :solid)
|
2346
2360
|
theme_margin :thematic_break, ((block_next = next_enclosed_block node) ? :bottom : :top), block_next || true
|
2347
2361
|
end
|
2348
2362
|
|
2349
2363
|
def convert_toc node, opts = {}
|
2350
|
-
# NOTE: only allow document to have a single toc
|
2364
|
+
# NOTE: only allow document to have a single managed toc
|
2351
2365
|
return if @toc_extent
|
2352
2366
|
is_macro = (placement = opts[:placement] || 'macro') == 'macro'
|
2353
2367
|
if ((doc = node.document).attr? 'toc-placement', placement) && (doc.attr? 'toc') && doc.sections?
|
@@ -2356,11 +2370,11 @@ module Asciidoctor
|
|
2356
2370
|
start_new_page if @ppbook && verso_page? && !(is_macro && (node.option? 'nonfacing'))
|
2357
2371
|
end
|
2358
2372
|
add_dest_for_block node, id: (node.id || 'toc') if is_macro
|
2359
|
-
allocate_toc doc, (doc.attr 'toclevels', 2).to_i, cursor, (
|
2360
|
-
@index.start_page_number =
|
2373
|
+
toc_extent = @toc_extent = allocate_toc doc, (doc.attr 'toclevels', 2).to_i, cursor, (title_page_on = is_book || (doc.attr? 'title-page'))
|
2374
|
+
@index.start_page_number = toc_extent.to.page + 1 if title_page_on && @theme.page_numbering_start_at == 'after-toc'
|
2361
2375
|
if is_macro
|
2362
|
-
@disable_running_content[:header] +=
|
2363
|
-
@disable_running_content[:footer] +=
|
2376
|
+
@disable_running_content[:header] += toc_extent.page_range if node.option? 'noheader'
|
2377
|
+
@disable_running_content[:footer] += toc_extent.page_range if node.option? 'nofooter'
|
2364
2378
|
end
|
2365
2379
|
end
|
2366
2380
|
nil
|
@@ -2378,7 +2392,7 @@ module Asciidoctor
|
|
2378
2392
|
|
2379
2393
|
if at_page_top?
|
2380
2394
|
if page_layout && page_layout != page.layout && page.empty?
|
2381
|
-
|
2395
|
+
delete_current_page
|
2382
2396
|
advance_page layout: page_layout
|
2383
2397
|
end
|
2384
2398
|
elsif page_layout
|
@@ -2388,8 +2402,9 @@ module Asciidoctor
|
|
2388
2402
|
end
|
2389
2403
|
end
|
2390
2404
|
|
2391
|
-
def convert_index_section
|
2405
|
+
def convert_index_section node
|
2392
2406
|
space_needed_for_category = @theme.description_list_term_spacing + (2 * (height_of_typeset_text 'A'))
|
2407
|
+
pagenum_sequence_style = node.document.attr 'index-pagenum-sequence-style'
|
2393
2408
|
column_box [0, cursor], columns: @theme.index_columns, width: bounds.width, reflow_margins: true do
|
2394
2409
|
def @bounding_box.move_past_bottom *args # rubocop:disable Lint/NestedMethodDefinition
|
2395
2410
|
super(*args)
|
@@ -2398,12 +2413,12 @@ module Asciidoctor
|
|
2398
2413
|
@index.categories.each do |category|
|
2399
2414
|
# NOTE: cursor method always returns 0 inside column_box; breaks reference_bounds.move_past_bottom
|
2400
2415
|
bounds.move_past_bottom if space_needed_for_category > y - reference_bounds.absolute_bottom
|
2401
|
-
|
2416
|
+
ink_prose category.name,
|
2402
2417
|
align: :left,
|
2403
2418
|
inline_format: false,
|
2404
2419
|
margin_bottom: @theme.description_list_term_spacing,
|
2405
|
-
style: @theme.description_list_term_font_style
|
2406
|
-
category.terms.each {|term| convert_index_list_item term }
|
2420
|
+
style: @theme.description_list_term_font_style&.to_sym
|
2421
|
+
category.terms.each {|term| convert_index_list_item term, pagenum_sequence_style }
|
2407
2422
|
# NOTE: see previous note for why we can't use margin_bottom method
|
2408
2423
|
if @theme.prose_margin_bottom > y - reference_bounds.absolute_bottom
|
2409
2424
|
bounds.move_past_bottom
|
@@ -2415,21 +2430,32 @@ module Asciidoctor
|
|
2415
2430
|
nil
|
2416
2431
|
end
|
2417
2432
|
|
2418
|
-
def convert_index_list_item term
|
2433
|
+
def convert_index_list_item term, pagenum_sequence_style = nil
|
2419
2434
|
text = escape_xml term.name
|
2420
2435
|
unless term.container?
|
2421
2436
|
if @media == 'screen'
|
2422
|
-
|
2437
|
+
case pagenum_sequence_style
|
2438
|
+
when 'page'
|
2439
|
+
pagenums = term.dests.uniq {|dest| dest[:page] }.map {|dest| %(<a anchor="#{dest[:anchor]}">#{dest[:page]}</a>) }
|
2440
|
+
when 'range'
|
2441
|
+
first_anchor_per_page = term.dests.each_with_object({}) {|dest, accum| accum[dest[:page]] ||= dest[:anchor] }
|
2442
|
+
pagenums = (consolidate_ranges first_anchor_per_page.keys).map do |range|
|
2443
|
+
anchor = first_anchor_per_page[(range.include? '-') ? (range.partition '-')[0] : range]
|
2444
|
+
%(<a anchor="#{anchor}">#{range}</a>)
|
2445
|
+
end
|
2446
|
+
else # term
|
2447
|
+
pagenums = term.dests.map {|dest| %(<a anchor="#{dest[:anchor]}">#{dest[:page]}</a>) }
|
2448
|
+
end
|
2423
2449
|
else
|
2424
|
-
pagenums = consolidate_ranges term.dests.
|
2450
|
+
pagenums = consolidate_ranges term.dests.map {|dest| dest[:page] }.uniq
|
2425
2451
|
end
|
2426
2452
|
text = %(#{text}, #{pagenums.join ', '})
|
2427
2453
|
end
|
2428
2454
|
subterm_indent = @theme.description_list_description_indent
|
2429
|
-
|
2455
|
+
ink_prose text, align: :left, margin: 0, hanging_indent: subterm_indent * 2
|
2430
2456
|
indent subterm_indent do
|
2431
2457
|
term.subterms.each do |subterm|
|
2432
|
-
convert_index_list_item subterm
|
2458
|
+
convert_index_list_item subterm, pagenum_sequence_style
|
2433
2459
|
end
|
2434
2460
|
end unless term.leaf?
|
2435
2461
|
end
|
@@ -2527,14 +2553,14 @@ module Asciidoctor
|
|
2527
2553
|
end
|
2528
2554
|
|
2529
2555
|
def convert_inline_icon node
|
2530
|
-
if node.document.attr
|
2556
|
+
if (icons = (doc = node.document).attr 'icons') == 'font'
|
2531
2557
|
if (icon_name = node.target).include? '@'
|
2532
2558
|
icon_name, icon_set = icon_name.split '@', 2
|
2533
2559
|
explicit_icon_set = true
|
2534
2560
|
elsif (icon_set = node.attr 'set')
|
2535
2561
|
explicit_icon_set = true
|
2536
2562
|
else
|
2537
|
-
icon_set =
|
2563
|
+
icon_set = doc.attr 'icon-set', 'fa'
|
2538
2564
|
end
|
2539
2565
|
if icon_set == 'fa' || !(IconSets.include? icon_set)
|
2540
2566
|
icon_set = 'fa'
|
@@ -2580,6 +2606,14 @@ module Asciidoctor
|
|
2580
2606
|
log :warn, %(#{icon_name} is not a valid icon name in the #{icon_set} icon set)
|
2581
2607
|
%([#{node.attr 'alt'}])
|
2582
2608
|
end
|
2609
|
+
elsif icons
|
2610
|
+
image_path = ::File.absolute_path %(#{icon_name = node.target}.#{image_format = doc.attr 'icontype', 'png'}), (doc.attr 'iconsdir')
|
2611
|
+
if ::File.readable? image_path
|
2612
|
+
%(<img src="#{image_path}" format="#{image_format}" alt="#{node.attr 'alt'}" fit="line">)
|
2613
|
+
else
|
2614
|
+
log :warn, %(image icon for '#{icon_name}' not found or not readable: #{image_path})
|
2615
|
+
%([#{icon_name}])
|
2616
|
+
end
|
2583
2617
|
else
|
2584
2618
|
%([#{node.attr 'alt'}])
|
2585
2619
|
end
|
@@ -2616,15 +2650,16 @@ module Asciidoctor
|
|
2616
2650
|
end
|
2617
2651
|
|
2618
2652
|
def convert_inline_indexterm node
|
2653
|
+
visible = node.type == :visible
|
2619
2654
|
if scratch?
|
2620
|
-
|
2655
|
+
visible ? node.text : ''
|
2621
2656
|
else
|
2622
2657
|
# NOTE: initialize index in case converter is called before PDF is initialized
|
2623
2658
|
@index ||= IndexCatalog.new
|
2624
2659
|
# NOTE: page number (:page key) is added by InlineDestinationMarker
|
2625
2660
|
dest = { anchor: (anchor_name = @index.next_anchor_name) }
|
2626
|
-
anchor = %(<a id="#{anchor_name}" type="indexterm">#{DummyText}</a>)
|
2627
|
-
if
|
2661
|
+
anchor = %(<a id="#{anchor_name}" type="indexterm"#{visible ? ' visible="true"' : ''}>#{DummyText}</a>)
|
2662
|
+
if visible
|
2628
2663
|
visible_term = node.text
|
2629
2664
|
@index.store_primary_term (sanitize visible_term), dest
|
2630
2665
|
%(#{anchor}#{visible_term})
|
@@ -2671,8 +2706,10 @@ module Asciidoctor
|
|
2671
2706
|
open, close, is_tag = ['<sub>', '</sub>', true]
|
2672
2707
|
when :double
|
2673
2708
|
open, close, is_tag = [theme.quotes[0], theme.quotes[1], false]
|
2709
|
+
quotes = true
|
2674
2710
|
when :single
|
2675
2711
|
open, close, is_tag = [theme.quotes[2], theme.quotes[3], false]
|
2712
|
+
quotes = true
|
2676
2713
|
when :mark
|
2677
2714
|
open, close, is_tag = ['<mark>', '</mark>', true]
|
2678
2715
|
else
|
@@ -2681,6 +2718,11 @@ module Asciidoctor
|
|
2681
2718
|
|
2682
2719
|
inner_text = node.text
|
2683
2720
|
|
2721
|
+
if quotes && (len = inner_text.length) > 3 &&
|
2722
|
+
(inner_text.end_with? '...') && !((inner_text_trunc = inner_text.slice 0, len - 3).end_with? ?\\)
|
2723
|
+
inner_text = inner_text_trunc + '…'
|
2724
|
+
end
|
2725
|
+
|
2684
2726
|
if (roles = node.role)
|
2685
2727
|
roles.split.each do |role|
|
2686
2728
|
if (text_transform = theme[%(role_#{role}_text_transform)])
|
@@ -2697,7 +2739,8 @@ module Asciidoctor
|
|
2697
2739
|
node.id ? %(<a id="#{node.id}">#{DummyText}</a>#{quoted_text}) : quoted_text
|
2698
2740
|
end
|
2699
2741
|
|
2700
|
-
|
2742
|
+
# Returns a Boolean indicating whether the title page was created
|
2743
|
+
def ink_title_page doc
|
2701
2744
|
return unless doc.header? && !doc.notitle && @theme.title_page != false
|
2702
2745
|
|
2703
2746
|
# NOTE: a new page may have already been started at this point, so decide what to do with it
|
@@ -2724,7 +2767,7 @@ module Asciidoctor
|
|
2724
2767
|
font @theme.base_font_family, size: @root_font_size, style: @theme.base_font_style
|
2725
2768
|
|
2726
2769
|
# QUESTION: allow alignment per element on title page?
|
2727
|
-
title_align = (@theme.
|
2770
|
+
title_align = (@theme.title_page_text_align || @base_text_align).to_sym
|
2728
2771
|
|
2729
2772
|
if @theme.title_page_logo_display != 'none' && (logo_image_path = (doc.attr 'title-logo-image') || (logo_image_from_theme = @theme.title_page_logo_image))
|
2730
2773
|
if (logo_image_path.include? ':') && logo_image_path =~ ImageAttributeValueRx
|
@@ -2773,7 +2816,7 @@ module Asciidoctor
|
|
2773
2816
|
move_down @theme.title_page_title_margin_top || 0
|
2774
2817
|
indent (@theme.title_page_title_margin_left || 0), (@theme.title_page_title_margin_right || 0) do
|
2775
2818
|
theme_font :title_page_title do
|
2776
|
-
|
2819
|
+
ink_prose doctitle.main, align: title_align, margin: 0
|
2777
2820
|
end
|
2778
2821
|
end
|
2779
2822
|
move_down @theme.title_page_title_margin_bottom || 0
|
@@ -2782,7 +2825,7 @@ module Asciidoctor
|
|
2782
2825
|
move_down @theme.title_page_subtitle_margin_top || 0
|
2783
2826
|
indent (@theme.title_page_subtitle_margin_left || 0), (@theme.title_page_subtitle_margin_right || 0) do
|
2784
2827
|
theme_font :title_page_subtitle do
|
2785
|
-
|
2828
|
+
ink_prose subtitle, align: title_align, margin: 0
|
2786
2829
|
end
|
2787
2830
|
end
|
2788
2831
|
move_down @theme.title_page_subtitle_margin_bottom || 0
|
@@ -2807,7 +2850,7 @@ module Asciidoctor
|
|
2807
2850
|
end
|
2808
2851
|
end.join @theme.title_page_authors_delimiter
|
2809
2852
|
theme_font :title_page_authors do
|
2810
|
-
|
2853
|
+
ink_prose authors, align: title_align, margin: 0, normalize: true
|
2811
2854
|
end
|
2812
2855
|
end
|
2813
2856
|
move_down @theme.title_page_authors_margin_bottom || 0
|
@@ -2820,17 +2863,18 @@ module Asciidoctor
|
|
2820
2863
|
end
|
2821
2864
|
indent (@theme.title_page_revision_margin_left || 0), (@theme.title_page_revision_margin_right || 0) do
|
2822
2865
|
theme_font :title_page_revision do
|
2823
|
-
|
2866
|
+
ink_prose revision_text, align: title_align, margin: 0, normalize: false
|
2824
2867
|
end
|
2825
2868
|
end
|
2826
2869
|
move_down @theme.title_page_revision_margin_bottom || 0
|
2827
2870
|
end
|
2828
2871
|
end
|
2829
2872
|
|
2830
|
-
|
2873
|
+
ink_prose DummyText, margin: 0, line_height: 1, normalize: false if page.empty?
|
2874
|
+
true
|
2831
2875
|
end
|
2832
2876
|
|
2833
|
-
def
|
2877
|
+
def ink_cover_page doc, face
|
2834
2878
|
image_path, image_opts = resolve_background_image doc, @theme, %(#{face}-cover-image), theme_key: %(cover_#{face}_image).to_sym, symbolic_paths: ['', '~']
|
2835
2879
|
if image_path
|
2836
2880
|
if image_path.empty?
|
@@ -2876,71 +2920,109 @@ module Asciidoctor
|
|
2876
2920
|
|
2877
2921
|
def start_new_chapter chapter
|
2878
2922
|
start_new_page unless at_page_top?
|
2879
|
-
# TODO: must call update_colors before advancing to next page if start_new_page is called in
|
2923
|
+
# TODO: must call update_colors before advancing to next page if start_new_page is called in ink_chapter_title
|
2880
2924
|
start_new_page if @ppbook && verso_page? && !(chapter.option? 'nonfacing')
|
2881
2925
|
end
|
2882
2926
|
|
2883
2927
|
alias start_new_part start_new_chapter
|
2884
2928
|
|
2885
2929
|
def arrange_section sect, title, opts
|
2886
|
-
|
2887
|
-
|
2888
|
-
|
2889
|
-
|
2890
|
-
|
2891
|
-
|
2892
|
-
|
2893
|
-
|
2894
|
-
|
2930
|
+
if sect.option? 'breakable'
|
2931
|
+
orphaned = nil
|
2932
|
+
dry_run single_page: true do
|
2933
|
+
start_page = page
|
2934
|
+
theme_font :heading, level: opts[:level] do
|
2935
|
+
if opts[:part]
|
2936
|
+
ink_part_title sect, title, opts
|
2937
|
+
elsif opts[:chapterlike]
|
2938
|
+
ink_chapter_title sect, title, opts
|
2939
|
+
else
|
2940
|
+
ink_general_heading sect, title, opts
|
2941
|
+
end
|
2942
|
+
end
|
2943
|
+
if page == start_page
|
2944
|
+
page.tare_content_stream
|
2945
|
+
orphaned = stop_if_first_page_empty { traverse sect }
|
2946
|
+
end
|
2947
|
+
end
|
2948
|
+
start_new_page if orphaned
|
2949
|
+
else
|
2950
|
+
theme_font :heading, level: (hlevel = opts[:level]) do
|
2951
|
+
h_padding_t, h_padding_r, h_padding_b, h_padding_l = expand_padding_value @theme[%(heading_h#{hlevel}_padding)]
|
2952
|
+
h_fits = indent h_padding_l, h_padding_r do
|
2953
|
+
# FIXME: this height doesn't account for impact of text transform or inline formatting
|
2954
|
+
heading_h = (height_of_typeset_text title) +
|
2955
|
+
(@theme[%(heading_h#{hlevel}_margin_top)] || @theme.heading_margin_top) +
|
2956
|
+
(@theme[%(heading_h#{hlevel}_margin_bottom)] || @theme.heading_margin_bottom) + h_padding_t + h_padding_b
|
2957
|
+
heading_h += @theme.heading_min_height_after if @theme.heading_min_height_after && sect.blocks?
|
2958
|
+
cursor >= heading_h
|
2959
|
+
end
|
2960
|
+
start_new_page unless h_fits
|
2961
|
+
end
|
2895
2962
|
end
|
2963
|
+
nil
|
2896
2964
|
end
|
2897
2965
|
|
2898
|
-
def
|
2899
|
-
|
2966
|
+
def ink_chapter_title node, title, opts = {}
|
2967
|
+
ink_general_heading node, title, (opts.merge outdent: true)
|
2900
2968
|
end
|
2901
2969
|
|
2902
|
-
alias
|
2970
|
+
alias ink_part_title ink_chapter_title
|
2903
2971
|
|
2904
|
-
def
|
2905
|
-
|
2972
|
+
def ink_general_heading _node, title, opts = {}
|
2973
|
+
ink_heading title, opts
|
2906
2974
|
end
|
2907
2975
|
|
2908
|
-
# NOTE:
|
2909
|
-
def
|
2910
|
-
|
2976
|
+
# NOTE: ink_heading doesn't set the theme font because it's used for various types of headings
|
2977
|
+
def ink_heading string, opts = {}
|
2978
|
+
if (h_level = opts[:level])
|
2979
|
+
h_category = %(heading_h#{h_level})
|
2980
|
+
end
|
2911
2981
|
unless (top_margin = (margin = (opts.delete :margin)) || (opts.delete :margin_top))
|
2912
2982
|
if at_page_top?
|
2913
|
-
if
|
2983
|
+
if h_category && (top_margin = @theme[%(#{h_category}_margin_page_top)] || @theme.heading_margin_page_top) > 0
|
2914
2984
|
move_down top_margin
|
2915
2985
|
end
|
2916
2986
|
top_margin = 0
|
2917
2987
|
else
|
2918
|
-
top_margin = (
|
2988
|
+
top_margin = (h_category ? @theme[%(#{h_category}_margin_top)] : nil) || @theme.heading_margin_top
|
2919
2989
|
end
|
2920
2990
|
end
|
2921
|
-
bot_margin = margin || (opts.delete :margin_bottom) || (
|
2991
|
+
bot_margin = margin || (opts.delete :margin_bottom) || (h_category ? @theme[%(#{h_category}_margin_bottom)] : nil) || @theme.heading_margin_bottom
|
2922
2992
|
if (transform = resolve_text_transform opts)
|
2923
2993
|
string = transform_text string, transform
|
2924
2994
|
end
|
2925
2995
|
outdent_section opts.delete :outdent do
|
2926
2996
|
margin_top top_margin
|
2927
|
-
|
2928
|
-
|
2929
|
-
|
2930
|
-
|
2931
|
-
|
2997
|
+
start_cursor = cursor
|
2998
|
+
start_page_number = page_number
|
2999
|
+
pad_box h_category ? @theme[%(#{h_category}_padding)] : nil do
|
3000
|
+
# QUESTION: should we move inherited styles to typeset_text?
|
3001
|
+
if (inherited = apply_text_decoration font_styles, :heading, h_level).empty?
|
3002
|
+
inline_format_opts = true
|
3003
|
+
else
|
3004
|
+
inline_format_opts = [{ inherited: inherited }]
|
3005
|
+
end
|
3006
|
+
typeset_text string, (calc_line_metrics (opts.delete :line_height) || @base_line_height), {
|
3007
|
+
color: @font_color,
|
3008
|
+
inline_format: inline_format_opts,
|
3009
|
+
align: @base_text_align.to_sym,
|
3010
|
+
}.merge(opts)
|
3011
|
+
end
|
3012
|
+
if h_category && @theme[%(#{h_category}_border_width)] &&
|
3013
|
+
(@theme[%(#{h_category}_border_color)] || @theme.base_border_color) && page_number == start_page_number
|
3014
|
+
float do
|
3015
|
+
bounding_box [bounds.left, start_cursor], width: bounds.width, height: start_cursor - cursor do
|
3016
|
+
theme_fill_and_stroke_bounds h_category
|
3017
|
+
end
|
3018
|
+
end
|
2932
3019
|
end
|
2933
|
-
typeset_text string, (calc_line_metrics (opts.delete :line_height) || @base_line_height), {
|
2934
|
-
color: @font_color,
|
2935
|
-
inline_format: inline_format_opts,
|
2936
|
-
align: @base_align.to_sym,
|
2937
|
-
}.merge(opts)
|
2938
3020
|
margin_bottom bot_margin
|
2939
3021
|
end
|
2940
3022
|
end
|
2941
3023
|
|
2942
3024
|
# NOTE: inline_format is true by default
|
2943
|
-
def
|
3025
|
+
def ink_prose string, opts = {}
|
2944
3026
|
top_margin = (margin = (opts.delete :margin)) || (opts.delete :margin_top) || 0
|
2945
3027
|
bot_margin = margin || (opts.delete :margin_bottom) || @theme.prose_margin_bottom
|
2946
3028
|
if (transform = resolve_text_transform opts)
|
@@ -2964,7 +3046,7 @@ module Asciidoctor
|
|
2964
3046
|
typeset_text string, (calc_line_metrics (opts.delete :line_height) || @base_line_height), {
|
2965
3047
|
color: @font_color,
|
2966
3048
|
inline_format: [inline_format_opts],
|
2967
|
-
align: @
|
3049
|
+
align: @base_text_align.to_sym,
|
2968
3050
|
}.merge(opts)
|
2969
3051
|
margin_bottom bot_margin
|
2970
3052
|
end
|
@@ -2987,13 +3069,13 @@ module Asciidoctor
|
|
2987
3069
|
# The subject argument can either be a String or an AbstractNode. If
|
2988
3070
|
# subject is an AbstractNode, only call this method if the node has a
|
2989
3071
|
# title (i.e., subject.title? returns true).
|
2990
|
-
def
|
3072
|
+
def ink_caption subject, opts = {}
|
2991
3073
|
if opts.delete :dry_run
|
2992
3074
|
force_top_margin = !at_page_top? if (force_top_margin = opts.delete :force_top_margin).nil?
|
2993
3075
|
return (dry_run keep_together: true, single_page: :enforce do
|
2994
3076
|
# TODO: encapsulate this logic to force top margin to be applied
|
2995
3077
|
margin_box.instance_variable_set :@y, margin_box.absolute_top + 0.0001 if force_top_margin
|
2996
|
-
|
3078
|
+
ink_caption subject, opts
|
2997
3079
|
end).single_page_height
|
2998
3080
|
end
|
2999
3081
|
if ::Asciidoctor::AbstractBlock === subject
|
@@ -3001,21 +3083,23 @@ module Asciidoctor
|
|
3001
3083
|
else
|
3002
3084
|
string = subject.to_s
|
3003
3085
|
end
|
3086
|
+
block_align = opts.delete :block_align
|
3087
|
+
block_width = opts.delete :block_width
|
3004
3088
|
category_caption = (category = opts[:category]) ? %(#{category}_caption) : 'caption'
|
3089
|
+
caption_margin_outside = @theme[%(#{category_caption}_margin_outside)] || @theme.caption_margin_outside
|
3090
|
+
caption_margin_inside = @theme[%(#{category_caption}_margin_inside)] || @theme.caption_margin_inside
|
3005
3091
|
container_width = bounds.width
|
3006
|
-
|
3092
|
+
indent_by = [0, 0]
|
3007
3093
|
if (align = @theme[%(#{category_caption}_align)] || @theme.caption_align)
|
3008
|
-
align = align == 'inherit' ? (block_align || @
|
3094
|
+
align = align == 'inherit' ? (block_align || @base_text_align.to_sym) : align.to_sym
|
3009
3095
|
else
|
3010
|
-
align = @
|
3096
|
+
align = @base_text_align.to_sym
|
3011
3097
|
end
|
3012
3098
|
if (text_align = @theme[%(#{category_caption}_text_align)] || @theme.caption_text_align)
|
3013
3099
|
text_align = text_align == 'inherit' ? align : text_align.to_sym
|
3014
3100
|
else
|
3015
3101
|
text_align = align
|
3016
3102
|
end
|
3017
|
-
indent_by = [0, 0]
|
3018
|
-
block_width = opts.delete :block_width
|
3019
3103
|
if (max_width = opts.delete :max_width) && max_width != 'none'
|
3020
3104
|
if ::String === max_width
|
3021
3105
|
if max_width.start_with? 'fit-content'
|
@@ -3047,9 +3131,7 @@ module Asciidoctor
|
|
3047
3131
|
end
|
3048
3132
|
end
|
3049
3133
|
theme_font_cascade [:caption, category_caption] do
|
3050
|
-
|
3051
|
-
caption_margin_inside = @theme[%(#{category_caption}_margin_inside)] || @theme.caption_margin_inside
|
3052
|
-
if ((opts.delete :side) || :top) == :top
|
3134
|
+
if ((opts.delete :end) || (opts.delete :side) || :top) == :top
|
3053
3135
|
margin = { top: caption_margin_outside, bottom: caption_margin_inside }
|
3054
3136
|
else
|
3055
3137
|
margin = { top: caption_margin_inside, bottom: caption_margin_outside }
|
@@ -3057,8 +3139,13 @@ module Asciidoctor
|
|
3057
3139
|
unless (inherited = apply_text_decoration [], :caption).empty?
|
3058
3140
|
opts = opts.merge inherited
|
3059
3141
|
end
|
3142
|
+
unless scratch? || !(bg_color = @theme[%(#{category_caption}_background_color)] || @theme.caption_background_color)
|
3143
|
+
caption_height = height_of_typeset_text string
|
3144
|
+
fill_at = [0, cursor + (margin[:top] || 0)]
|
3145
|
+
float { bounding_box(fill_at, width: container_width, height: caption_height) { fill_bounds bg_color } }
|
3146
|
+
end
|
3060
3147
|
indent(*indent_by) do
|
3061
|
-
|
3148
|
+
ink_prose string, ({
|
3062
3149
|
margin_top: margin[:top],
|
3063
3150
|
margin_bottom: margin[:bottom],
|
3064
3151
|
align: text_align,
|
@@ -3072,35 +3159,45 @@ module Asciidoctor
|
|
3072
3159
|
end
|
3073
3160
|
|
3074
3161
|
# Render the caption for a table and return the height of the rendered content
|
3075
|
-
def
|
3076
|
-
|
3162
|
+
def ink_table_caption node, table_alignment = :left, table_width = nil, max_width = nil, end_ = :top
|
3163
|
+
ink_caption node, category: :table, end: end_, block_align: table_alignment, block_width: table_width, max_width: max_width
|
3077
3164
|
end
|
3078
3165
|
|
3079
|
-
def allocate_toc doc, toc_num_levels, toc_start_cursor,
|
3080
|
-
|
3166
|
+
def allocate_toc doc, toc_num_levels, toc_start_cursor, title_page_on
|
3167
|
+
toc_start_page_number = page_number
|
3168
|
+
to_page = nil
|
3081
3169
|
extent = dry_run onto: self do
|
3082
|
-
|
3083
|
-
margin_bottom @theme.block_margin_bottom unless
|
3170
|
+
to_page = (ink_toc doc, toc_num_levels, toc_start_page_number, toc_start_cursor).end
|
3171
|
+
margin_bottom @theme.block_margin_bottom unless title_page_on
|
3172
|
+
end
|
3173
|
+
# NOTE: patch for custom converters that allocate extra TOC pages without actually creating them
|
3174
|
+
if to_page > extent.to.page
|
3175
|
+
extent.to.page = to_page
|
3176
|
+
extent.to.cursor = bounds.height
|
3084
3177
|
end
|
3085
3178
|
# NOTE: reserve pages for the toc; leaves cursor on page after last page in toc
|
3086
|
-
if
|
3179
|
+
if title_page_on
|
3087
3180
|
extent.each_page { start_new_page }
|
3088
3181
|
else
|
3089
3182
|
extent.each_page {|first_page| start_new_page unless first_page }
|
3090
3183
|
move_cursor_to extent.to.cursor
|
3091
3184
|
end
|
3092
|
-
|
3185
|
+
extent
|
3186
|
+
end
|
3187
|
+
|
3188
|
+
def get_entries_for_toc node
|
3189
|
+
node.sections
|
3093
3190
|
end
|
3094
3191
|
|
3095
3192
|
# NOTE: num_front_matter_pages not used during a dry run
|
3096
|
-
def
|
3193
|
+
def ink_toc doc, num_levels, toc_page_number, start_cursor, num_front_matter_pages = 0
|
3097
3194
|
go_to_page toc_page_number unless (page_number == toc_page_number) || scratch?
|
3098
3195
|
start_page_number = page_number
|
3099
3196
|
move_cursor_to start_cursor
|
3100
3197
|
unless (toc_title = doc.attr 'toc-title').nil_or_empty?
|
3101
3198
|
theme_font_cascade [[:heading, level: 2], :toc_title] do
|
3102
|
-
toc_title_align = (@theme.
|
3103
|
-
|
3199
|
+
toc_title_align = (@theme.toc_title_text_align || @theme.heading_h2_text_align || @theme.heading_text_align || @base_text_align).to_sym
|
3200
|
+
ink_general_heading doc, toc_title, align: toc_title_align, level: 2, outdent: true, role: :toctitle
|
3104
3201
|
end
|
3105
3202
|
end
|
3106
3203
|
# QUESTION: should we skip this whole method if num_levels < 0?
|
@@ -3125,7 +3222,7 @@ module Asciidoctor
|
|
3125
3222
|
}
|
3126
3223
|
end
|
3127
3224
|
theme_margin :toc, :top
|
3128
|
-
|
3225
|
+
ink_toc_level (get_entries_for_toc doc), num_levels, dot_leader, num_front_matter_pages
|
3129
3226
|
end
|
3130
3227
|
# NOTE: range must be calculated relative to toc_page_number; absolute page number in scratch document is arbitrary
|
3131
3228
|
toc_page_numbers = (toc_page_number..(toc_page_number + (page_number - start_page_number)))
|
@@ -3133,38 +3230,48 @@ module Asciidoctor
|
|
3133
3230
|
toc_page_numbers
|
3134
3231
|
end
|
3135
3232
|
|
3136
|
-
def
|
3233
|
+
def ink_toc_level entries, num_levels, dot_leader, num_front_matter_pages
|
3137
3234
|
# NOTE: font options aren't always reliable, so store size separately
|
3138
3235
|
toc_font_info = theme_font :toc do
|
3139
3236
|
{ font: font, size: @font_size }
|
3140
3237
|
end
|
3141
3238
|
hanging_indent = @theme.toc_hanging_indent
|
3142
|
-
|
3143
|
-
next if (
|
3144
|
-
theme_font :toc, level:
|
3145
|
-
|
3146
|
-
|
3239
|
+
entries.each do |entry|
|
3240
|
+
next if (num_levels_for_entry = (entry.attr 'toclevels', num_levels).to_i) < (entry_level = entry.level + 1).pred
|
3241
|
+
theme_font :toc, level: entry_level do
|
3242
|
+
next unless (entry_anchor = (entry.attr 'pdf-anchor') || entry.id)
|
3243
|
+
entry_title = entry.context == :section ? entry.numbered_title : (entry.title? ? entry.title : (entry.xreftext 'basic'))
|
3244
|
+
next if entry_title.empty?
|
3245
|
+
entry_title = transform_text entry_title, @text_transform if @text_transform
|
3147
3246
|
pgnum_label_placeholder_width = rendered_width_of_string '0' * @toc_max_pagenum_digits
|
3148
|
-
# NOTE: only write
|
3247
|
+
# NOTE: only write title (excluding dots and page number) if this is a dry run
|
3149
3248
|
if scratch?
|
3150
3249
|
indent 0, pgnum_label_placeholder_width do
|
3151
3250
|
# NOTE: must wrap title in empty anchor element in case links are styled with different font family / size
|
3152
|
-
|
3251
|
+
ink_prose entry_title, anchor: true, normalize: false, hanging_indent: hanging_indent, normalize_line_height: true, margin: 0
|
3153
3252
|
end
|
3154
3253
|
else
|
3155
|
-
physical_pgnum =
|
3156
|
-
|
3157
|
-
|
3254
|
+
if !(physical_pgnum = entry.attr 'pdf-page-start') &&
|
3255
|
+
(target_page_ref = (get_dest entry_anchor)&.first) &&
|
3256
|
+
(target_page_idx = state.pages.index {|candidate| candidate.dictionary == target_page_ref })
|
3257
|
+
physical_pgnum = target_page_idx + 1
|
3258
|
+
end
|
3259
|
+
if physical_pgnum
|
3260
|
+
virtual_pgnum = physical_pgnum - num_front_matter_pages
|
3261
|
+
pgnum_label = (virtual_pgnum < 1 ? (RomanNumeral.new physical_pgnum, :lower) : virtual_pgnum).to_s
|
3262
|
+
else
|
3263
|
+
pgnum_label = '?'
|
3264
|
+
end
|
3158
3265
|
start_page_number = page_number
|
3159
3266
|
start_cursor = cursor
|
3160
3267
|
start_dots = nil
|
3161
|
-
|
3268
|
+
entry_title_inherited = (apply_text_decoration ::Set.new, :toc, entry_level).merge anchor: entry_anchor, color: @font_color
|
3162
3269
|
# NOTE: use text formatter to add anchor overlay to avoid using inline format with synthetic anchor tag
|
3163
|
-
|
3270
|
+
entry_title_fragments = text_formatter.format entry_title, inherited: entry_title_inherited
|
3164
3271
|
line_metrics = calc_line_metrics @base_line_height
|
3165
3272
|
indent 0, pgnum_label_placeholder_width do
|
3166
|
-
(
|
3167
|
-
typeset_formatted_text
|
3273
|
+
(entry_title_fragments[-1][:callback] ||= []) << (last_fragment_pos = ::Asciidoctor::PDF::FormattedText::FragmentPositionRenderer.new)
|
3274
|
+
typeset_formatted_text entry_title_fragments, line_metrics, hanging_indent: hanging_indent, normalize_line_height: true
|
3168
3275
|
start_dots = last_fragment_pos.right + hanging_indent
|
3169
3276
|
last_fragment_cursor = last_fragment_pos.top + line_metrics.padding_top
|
3170
3277
|
start_cursor = last_fragment_cursor if last_fragment_pos.page_number > start_page_number || (start_cursor - last_fragment_cursor) > line_metrics.height
|
@@ -3172,7 +3279,7 @@ module Asciidoctor
|
|
3172
3279
|
end_cursor = cursor
|
3173
3280
|
move_cursor_to start_cursor
|
3174
3281
|
# NOTE: we're guaranteed to be on the same page as the final line of the entry
|
3175
|
-
if dot_leader[:width] > 0 && (dot_leader[:levels].include?
|
3282
|
+
if dot_leader[:width] > 0 && (dot_leader[:levels].include? entry_level.pred)
|
3176
3283
|
pgnum_label_width = rendered_width_of_string pgnum_label
|
3177
3284
|
pgnum_label_font_settings = { color: @font_color, font: font_family, size: @font_size, styles: font_styles }
|
3178
3285
|
save_font do
|
@@ -3184,18 +3291,18 @@ module Asciidoctor
|
|
3184
3291
|
typeset_formatted_text [
|
3185
3292
|
{ text: dot_leader[:text] * num_dots, color: dot_leader[:font_color] },
|
3186
3293
|
dot_leader[:spacer],
|
3187
|
-
({ text: pgnum_label, anchor:
|
3294
|
+
({ text: pgnum_label, anchor: entry_anchor }.merge pgnum_label_font_settings),
|
3188
3295
|
], line_metrics, align: :right
|
3189
3296
|
end
|
3190
3297
|
else
|
3191
|
-
typeset_formatted_text [{ text: pgnum_label, color: @font_color, anchor:
|
3298
|
+
typeset_formatted_text [{ text: pgnum_label, color: @font_color, anchor: entry_anchor }], line_metrics, align: :right
|
3192
3299
|
end
|
3193
3300
|
move_cursor_to end_cursor
|
3194
3301
|
end
|
3195
3302
|
end
|
3196
3303
|
indent @theme.toc_indent do
|
3197
|
-
|
3198
|
-
end if
|
3304
|
+
ink_toc_level (get_entries_for_toc entry), num_levels_for_entry, dot_leader, num_front_matter_pages
|
3305
|
+
end if num_levels_for_entry >= entry_level
|
3199
3306
|
end
|
3200
3307
|
end
|
3201
3308
|
|
@@ -3222,15 +3329,15 @@ module Asciidoctor
|
|
3222
3329
|
icon_data
|
3223
3330
|
end
|
3224
3331
|
|
3225
|
-
# TODO: delegate to
|
3226
|
-
def
|
3332
|
+
# TODO: delegate to ink_page_header and ink_page_footer per page
|
3333
|
+
def ink_running_content periphery, doc, skip = [1, 1], body_start_page_number = 1
|
3227
3334
|
skip_pages, skip_pagenums = skip
|
3228
3335
|
# NOTE: find and advance to first non-imported content page to use as model page
|
3229
|
-
return unless (
|
3230
|
-
|
3336
|
+
return unless (content_start_page_number = state.pages[skip_pages..-1].index {|it| !it.imported_page? })
|
3337
|
+
content_start_page_number += (skip_pages + 1)
|
3231
3338
|
num_pages = page_count
|
3232
3339
|
prev_page_number = page_number
|
3233
|
-
go_to_page
|
3340
|
+
go_to_page content_start_page_number
|
3234
3341
|
|
3235
3342
|
# FIXME: probably need to treat doctypes differently
|
3236
3343
|
is_book = doc.doctype == 'book'
|
@@ -3326,7 +3433,7 @@ module Asciidoctor
|
|
3326
3433
|
pagenums_enabled = doc.attr? 'pagenums'
|
3327
3434
|
periphery_layout_cache = {}
|
3328
3435
|
# NOTE: this block is invoked during PDF generation, after #write -> #render_file and thus after #convert_document
|
3329
|
-
repeat (
|
3436
|
+
repeat (content_start_page_number..num_pages), dynamic: true do
|
3330
3437
|
pgnum = page_number
|
3331
3438
|
# NOTE: don't write on pages which are imported / inserts (otherwise we can get a corrupt PDF)
|
3332
3439
|
next if page.imported_page? || (disable_on_pages.include? pgnum)
|
@@ -3363,7 +3470,7 @@ module Asciidoctor
|
|
3363
3470
|
theme_font periphery do
|
3364
3471
|
canvas do
|
3365
3472
|
bounding_box [trim_styles[:content_left][side], trim_styles[:top][side]], width: trim_styles[:content_width][side], height: trim_styles[:height] do
|
3366
|
-
if (trim_column_rule_width = trim_styles[:column_rule_width]) > 0
|
3473
|
+
if trim_styles[:column_rule_color] && (trim_column_rule_width = trim_styles[:column_rule_width]) > 0
|
3367
3474
|
trim_column_rule_spacing = trim_styles[:column_rule_spacing]
|
3368
3475
|
else
|
3369
3476
|
trim_column_rule_width = nil
|
@@ -3448,7 +3555,7 @@ module Asciidoctor
|
|
3448
3555
|
trim_content_margin_recto = @theme[%(#{periphery}_recto_content_margin)] || @theme[%(#{periphery}_content_margin)] || [0, 'inherit', 0, 'inherit']
|
3449
3556
|
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 }
|
3450
3557
|
if (trim_padding_recto = @theme[%(#{periphery}_recto_padding)] || @theme[%(#{periphery}_padding)])
|
3451
|
-
trim_padding_recto = (
|
3558
|
+
trim_padding_recto = (expand_padding_value trim_padding_recto).map.with_index {|v, i| v + trim_content_margin_recto[i] }
|
3452
3559
|
else
|
3453
3560
|
trim_padding_recto = trim_content_margin_recto
|
3454
3561
|
end
|
@@ -3458,7 +3565,7 @@ module Asciidoctor
|
|
3458
3565
|
trim_content_margin_verso = @theme[%(#{periphery}_verso_content_margin)] || @theme[%(#{periphery}_content_margin)] || [0, 'inherit', 0, 'inherit']
|
3459
3566
|
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 }
|
3460
3567
|
if (trim_padding_verso = @theme[%(#{periphery}_verso_padding)] || @theme[%(#{periphery}_padding)])
|
3461
|
-
trim_padding_verso = (
|
3568
|
+
trim_padding_verso = (expand_padding_value trim_padding_verso).map.with_index {|v, i| v + trim_content_margin_verso[i] }
|
3462
3569
|
else
|
3463
3570
|
trim_padding_verso = trim_content_margin_verso
|
3464
3571
|
end
|
@@ -3471,12 +3578,12 @@ module Asciidoctor
|
|
3471
3578
|
# NOTE: we've already verified this property is set
|
3472
3579
|
height: (trim_height = @theme[%(#{periphery}_height)]),
|
3473
3580
|
bg_color: (resolve_theme_color %(#{periphery}_background_color).to_sym),
|
3474
|
-
|
3581
|
+
border_width: (trim_border_width = @theme[%(#{periphery}_border_width)] || 0),
|
3582
|
+
border_color: trim_border_width > 0 ? (resolve_theme_color %(#{periphery}_border_color).to_sym, @theme.base_border_color) : nil,
|
3475
3583
|
border_style: (@theme[%(#{periphery}_border_style)]&.to_sym || :solid),
|
3476
|
-
|
3477
|
-
column_rule_color:
|
3584
|
+
column_rule_width: (trim_column_rule_width = @theme[%(#{periphery}_column_rule_width)] || 0),
|
3585
|
+
column_rule_color: trim_column_rule_width > 0 ? (resolve_theme_color %(#{periphery}_column_rule_color).to_sym) : nil,
|
3478
3586
|
column_rule_style: (@theme[%(#{periphery}_column_rule_style)]&.to_sym || :solid),
|
3479
|
-
column_rule_width: (trim_column_rule_color ? @theme[%(#{periphery}_column_rule_width)] || 0 : 0),
|
3480
3587
|
column_rule_spacing: (@theme[%(#{periphery}_column_rule_spacing)] || 0),
|
3481
3588
|
valign: valign_offset ? [valign, valign_offset] : valign,
|
3482
3589
|
img_valign: @theme[%(#{periphery}_image_vertical_align)],
|
@@ -3644,8 +3751,9 @@ module Asciidoctor
|
|
3644
3751
|
outline.define do
|
3645
3752
|
initial_pagenum = has_front_cover ? 2 : 1
|
3646
3753
|
# FIXME: use sanitize: :plain_text on Document#doctitle once available
|
3647
|
-
if document.page_count >= initial_pagenum && (
|
3648
|
-
|
3754
|
+
if document.page_count >= initial_pagenum && (outline_title = doc.attr 'outline-title') &&
|
3755
|
+
(outline_title.empty? ? (outline_title = document.resolve_doctitle doc) : outline_title)
|
3756
|
+
page title: (document.sanitize outline_title), destination: (document.dest_top initial_pagenum)
|
3649
3757
|
end
|
3650
3758
|
# QUESTION: is there any way to get add_outline_level to invoke in the context of the outline?
|
3651
3759
|
document.add_outline_level self, doc.sections, num_levels, expand_levels
|
@@ -3707,7 +3815,14 @@ module Asciidoctor
|
|
3707
3815
|
pdf_doc.render_file target
|
3708
3816
|
# QUESTION: restore attributes first?
|
3709
3817
|
@pdfmark&.generate_file target
|
3710
|
-
(
|
3818
|
+
if (quality = @optimize)
|
3819
|
+
if quality.include? ','
|
3820
|
+
quality, compliance = quality.split ',', 2
|
3821
|
+
elsif quality.include? '/'
|
3822
|
+
quality, compliance = nil, quality
|
3823
|
+
end
|
3824
|
+
(Optimizer.new quality, pdf_doc.min_version, compliance).optimize_file target
|
3825
|
+
end
|
3711
3826
|
to_file = true
|
3712
3827
|
end
|
3713
3828
|
if !ENV['KEEP_ARTIFACTS']
|
@@ -3759,7 +3874,7 @@ module Asciidoctor
|
|
3759
3874
|
end
|
3760
3875
|
|
3761
3876
|
def resolve_text_transform key, use_fallback = true
|
3762
|
-
if (transform = ::Hash === key ? (key.delete :text_transform) : @theme[key
|
3877
|
+
if (transform = ::Hash === key ? (key.delete :text_transform) : @theme[key])
|
3763
3878
|
transform == 'none' ? nil : transform
|
3764
3879
|
elsif use_fallback
|
3765
3880
|
@text_transform
|
@@ -3768,9 +3883,9 @@ module Asciidoctor
|
|
3768
3883
|
|
3769
3884
|
# QUESTION: should we pass a category as an argument?
|
3770
3885
|
# QUESTION: should we make this a method on the theme ostruct? (e.g., @theme.resolve_color key, fallback)
|
3771
|
-
def resolve_theme_color key, fallback_color = nil
|
3772
|
-
if (color = @theme[key
|
3773
|
-
color
|
3886
|
+
def resolve_theme_color key, fallback_color = nil, transparent_color = fallback_color
|
3887
|
+
if (color = @theme[key])
|
3888
|
+
color == 'transparent' ? transparent_color : color
|
3774
3889
|
else
|
3775
3890
|
fallback_color
|
3776
3891
|
end
|
@@ -3781,7 +3896,7 @@ module Asciidoctor
|
|
3781
3896
|
end
|
3782
3897
|
|
3783
3898
|
def theme_fill_and_stroke_bounds category, opts = {}
|
3784
|
-
fill_and_stroke_bounds opts[:background_color], @theme[%(#{category}_border_color)],
|
3899
|
+
fill_and_stroke_bounds opts[:background_color], @theme[%(#{category}_border_color)] || @theme.base_border_color,
|
3785
3900
|
line_width: @theme[%(#{category}_border_width)],
|
3786
3901
|
line_style: (@theme[%(#{category}_border_style)]&.to_sym || :solid),
|
3787
3902
|
radius: @theme[%(#{category}_border_radius)]
|
@@ -3790,7 +3905,7 @@ module Asciidoctor
|
|
3790
3905
|
def theme_fill_and_stroke_block category, extent, opts = {}
|
3791
3906
|
node_with_caption = nil unless (node_with_caption = opts[:caption_node])&.title?
|
3792
3907
|
unless extent
|
3793
|
-
|
3908
|
+
ink_caption node_with_caption, category: category if node_with_caption
|
3794
3909
|
return
|
3795
3910
|
end
|
3796
3911
|
if (b_width = (opts.key? :border_width) ? opts[:border_width] : @theme[%(#{category}_border_width)])
|
@@ -3804,14 +3919,12 @@ module Asciidoctor
|
|
3804
3919
|
bg_color = nil
|
3805
3920
|
end
|
3806
3921
|
unless b_width || bg_color
|
3807
|
-
|
3922
|
+
ink_caption node_with_caption, category: category if node_with_caption
|
3808
3923
|
return
|
3809
3924
|
end
|
3810
|
-
|
3811
|
-
b_color = @page_bg_color
|
3812
|
-
end
|
3925
|
+
b_color = resolve_theme_color %(#{category}_border_color).to_sym, @theme.base_border_color, @page_bg_color
|
3813
3926
|
b_radius ||= (@theme[%(#{category}_border_radius)] || 0) + (b_width || 0)
|
3814
|
-
if b_width
|
3927
|
+
if b_width
|
3815
3928
|
if b_color == @page_bg_color # let page background cut into block background
|
3816
3929
|
b_gap_color, b_shift = @page_bg_color, (b_width * 0.5)
|
3817
3930
|
elsif (b_gap_color = bg_color) && b_gap_color != b_color
|
@@ -3822,7 +3935,7 @@ module Asciidoctor
|
|
3822
3935
|
else # let page background cut into block background; guarantees b_width is set
|
3823
3936
|
b_shift, b_gap_color = (b_width ||= 0.5) * 0.5, @page_bg_color
|
3824
3937
|
end
|
3825
|
-
|
3938
|
+
ink_caption node_with_caption, category: category if node_with_caption
|
3826
3939
|
extent.from.page += 1 unless extent.from.page == page_number # sanity check
|
3827
3940
|
float do
|
3828
3941
|
extent.each_page do |first_page, last_page|
|
@@ -3959,9 +4072,7 @@ module Asciidoctor
|
|
3959
4072
|
# NOTE: it also removes zero-width spaces
|
3960
4073
|
arranger.finalize_line
|
3961
4074
|
actual_width = width_of_fragments arranger.fragments
|
3962
|
-
|
3963
|
-
padding = ::Array.new 4, padding
|
3964
|
-
end
|
4075
|
+
padding = expand_padding_value @theme[%(#{category}_padding)]
|
3965
4076
|
if actual_width > (available_width = bounds.width - padding[3].to_f - padding[1].to_f)
|
3966
4077
|
adjusted_font_size = ((available_width * font_size).to_f / actual_width).truncate 4
|
3967
4078
|
if (min = @theme[%(#{category}_font_size_min)] || @theme.base_font_size_min) && adjusted_font_size < min
|
@@ -4206,7 +4317,7 @@ module Asciidoctor
|
|
4206
4317
|
(align_role.slice 5, align_role.length).to_sym
|
4207
4318
|
elsif use_theme
|
4208
4319
|
roles.reverse.each do |role|
|
4209
|
-
if (align = @theme[%(role_#{role}
|
4320
|
+
if (align = @theme[%(role_#{role}_text_align)])
|
4210
4321
|
return align.to_sym
|
4211
4322
|
end
|
4212
4323
|
end
|
@@ -4542,6 +4653,35 @@ module Asciidoctor
|
|
4542
4653
|
end
|
4543
4654
|
end
|
4544
4655
|
|
4656
|
+
# Deprecated method names
|
4657
|
+
alias layout_footnotes ink_footnotes
|
4658
|
+
alias layout_title_page ink_title_page
|
4659
|
+
alias layout_cover_page ink_cover_page
|
4660
|
+
alias layout_chapter_title ink_chapter_title
|
4661
|
+
alias layout_part_title ink_part_title
|
4662
|
+
alias layout_general_heading ink_general_heading
|
4663
|
+
alias layout_heading ink_heading
|
4664
|
+
alias layout_prose ink_prose
|
4665
|
+
alias layout_caption ink_caption
|
4666
|
+
alias layout_table_caption ink_table_caption
|
4667
|
+
alias layout_toc ink_toc
|
4668
|
+
alias layout_toc_level ink_toc_level
|
4669
|
+
alias layout_running_content ink_running_content
|
4670
|
+
|
4671
|
+
# intercepts "class CustomPDFConverter < (Asciidoctor::Converter.for 'pdf')"
|
4672
|
+
def self.method_added method
|
4673
|
+
if (method_name = method.to_s).start_with? 'layout_'
|
4674
|
+
alias_method %(ink_#{method_name.slice 7, method_name.length}).to_sym, method
|
4675
|
+
end
|
4676
|
+
end
|
4677
|
+
|
4678
|
+
# intercepts "(Asciidoctor::Converter.for 'pdf').prepend CustomConverterExtensions"
|
4679
|
+
def self.prepend *mods
|
4680
|
+
super
|
4681
|
+
mods.each {|mod| (mod.instance_methods false).each {|method| method_added method } }
|
4682
|
+
self
|
4683
|
+
end
|
4684
|
+
|
4545
4685
|
private
|
4546
4686
|
|
4547
4687
|
def add_link_to_image uri, image_info, image_opts
|