asciidoctor-pdf 2.0.0.alpha.1 → 2.0.0.beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +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
|