asciidoctor-pdf 2.0.0.beta.1 → 2.0.0.beta.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +128 -82
- data/README.adoc +6 -119
- data/data/themes/base-theme.yml +1 -1
- data/data/themes/default-for-print-theme.yml +4 -4
- data/data/themes/default-for-print-with-fallback-font-theme.yml +2 -3
- data/data/themes/default-for-print-with-font-fallbacks-theme.yml +3 -0
- data/data/themes/{sans-with-fallback-font-theme.yml → default-sans-theme.yml} +1 -1
- data/data/themes/default-sans-with-font-fallbacks-theme.yml +3 -0
- data/data/themes/default-theme.yml +3 -2
- data/data/themes/default-with-fallback-font-theme.yml +2 -9
- data/data/themes/default-with-font-fallbacks-theme.yml +9 -0
- data/docs/theming-guide.adoc +5 -6051
- data/lib/asciidoctor/pdf/converter.rb +237 -92
- data/lib/asciidoctor/pdf/ext/prawn/document/column_box.rb +16 -0
- data/lib/asciidoctor/pdf/ext/prawn/extensions.rb +50 -23
- data/lib/asciidoctor/pdf/ext/prawn/formatted_text/box.rb +14 -0
- data/lib/asciidoctor/pdf/ext/prawn/formatted_text/indented_paragraph_wrap.rb +39 -0
- data/lib/asciidoctor/pdf/ext/prawn-table/cell/asciidoc.rb +2 -9
- data/lib/asciidoctor/pdf/ext/prawn.rb +2 -0
- data/lib/asciidoctor/pdf/formatted_text/source_wrap.rb +7 -2
- data/lib/asciidoctor/pdf/version.rb +1 -1
- metadata +8 -3
@@ -52,6 +52,7 @@ module Asciidoctor
|
|
52
52
|
TextAlignmentRoles = %w(text-justify text-left text-center text-right)
|
53
53
|
TextDecorationStyleTable = { 'underline' => :underline, 'line-through' => :strikethrough }
|
54
54
|
FontKerningTable = { 'normal' => true, 'none' => false }
|
55
|
+
BlockFloatNames = %w(left right)
|
55
56
|
BlockAlignmentNames = %w(left center right)
|
56
57
|
(AlignmentTable = { '<' => :left, '=' => :center, '>' => :right }).default = :left
|
57
58
|
ColumnPositions = [:left, :center, :right]
|
@@ -486,8 +487,9 @@ module Asciidoctor
|
|
486
487
|
theme.code_linenum_font_color ||= '999999'
|
487
488
|
theme.callout_list_margin_top_after_code ||= 0
|
488
489
|
theme.role_unresolved_font_color ||= 'FF0000'
|
489
|
-
theme.index_columns ||= 2
|
490
490
|
theme.footnotes_item_spacing ||= 0
|
491
|
+
theme.index_columns ||= 2
|
492
|
+
theme.index_column_gap ||= theme.base_font_size
|
491
493
|
theme.kbd_separator ||= '+'
|
492
494
|
theme.title_page_authors_delimiter ||= ', '
|
493
495
|
theme.title_page_revision_delimiter ||= ', '
|
@@ -665,24 +667,24 @@ module Asciidoctor
|
|
665
667
|
title, _, subtitle = title.rpartition sep
|
666
668
|
title = %(#{title}\n<em class="subtitle">#{subtitle}</em>)
|
667
669
|
end
|
668
|
-
hlevel = sect.level
|
670
|
+
hlevel = sect.level.next
|
669
671
|
align = (@theme[%(heading_h#{hlevel}_text_align)] || @theme.heading_text_align || @base_text_align).to_sym
|
670
672
|
chapterlike = !(part = sectname == 'part') && (sectname == 'chapter' || (sect.document.doctype == 'book' && sect.level == 1))
|
671
673
|
hidden = sect.option? 'notitle'
|
672
674
|
hopts = { align: align, level: hlevel, part: part, chapterlike: chapterlike, outdent: !(part || chapterlike) }
|
673
675
|
if part
|
674
676
|
unless @theme.heading_part_break_before == 'auto'
|
675
|
-
|
677
|
+
started_new = true
|
676
678
|
start_new_part sect
|
677
679
|
end
|
678
680
|
elsif chapterlike
|
679
681
|
if @theme.heading_chapter_break_before != 'auto' ||
|
680
682
|
(@theme.heading_part_break_after == 'always' && sect == sect.parent.sections[0])
|
681
|
-
|
683
|
+
started_new = true
|
682
684
|
start_new_chapter sect
|
683
685
|
end
|
684
686
|
end
|
685
|
-
|
687
|
+
arrange_heading sect, title, hopts unless hidden || started_new || at_page_top? || !sect.blocks?
|
686
688
|
# QUESTION: should we store pdf-page-start, pdf-anchor & pdf-destination in internal map?
|
687
689
|
sect.set_attr 'pdf-page-start', (start_pgnum = page_number)
|
688
690
|
# QUESTION: should we just assign the section this generated id?
|
@@ -753,14 +755,17 @@ module Asciidoctor
|
|
753
755
|
end
|
754
756
|
|
755
757
|
def convert_floating_title node
|
756
|
-
|
758
|
+
title = node.title
|
757
759
|
hlevel = node.level.next
|
758
|
-
unless (align =
|
760
|
+
unless (align = resolve_text_align_from_role node.roles)
|
759
761
|
align = (@theme[%(heading_h#{hlevel}_text_align)] || @theme.heading_text_align || @base_text_align).to_sym
|
760
762
|
end
|
763
|
+
hopts = { align: align, level: hlevel, outdent: (parent = node.parent).context == :section }
|
764
|
+
arrange_heading node, title, hopts unless at_page_top? || node == parent.blocks[-1]
|
765
|
+
add_dest_for_block node if node.id
|
761
766
|
# QUESTION: should we decouple styles from section titles?
|
762
767
|
theme_font :heading, level: hlevel do
|
763
|
-
ink_general_heading node,
|
768
|
+
ink_general_heading node, title, hopts
|
764
769
|
end
|
765
770
|
end
|
766
771
|
|
@@ -778,11 +783,21 @@ module Asciidoctor
|
|
778
783
|
end
|
779
784
|
# FIXME: allow theme to control more first line options
|
780
785
|
if (line1_font_style = @theme.abstract_first_line_font_style&.to_sym) && line1_font_style != font_style
|
781
|
-
|
786
|
+
case line1_font_style
|
787
|
+
when :normal
|
788
|
+
first_line_options = { styles: [] }
|
789
|
+
when :normal_italic
|
790
|
+
first_line_options = { styles: [:italic] }
|
791
|
+
else
|
792
|
+
first_line_options = { styles: [font_style, line1_font_style] }
|
793
|
+
end
|
782
794
|
end
|
783
795
|
if (line1_font_color = @theme.abstract_first_line_font_color)
|
784
796
|
(first_line_options ||= {})[:color] = line1_font_color
|
785
797
|
end
|
798
|
+
if (line1_text_transform = @theme.abstract_first_line_text_transform)
|
799
|
+
(first_line_options ||= {})[:text_transform] = line1_text_transform
|
800
|
+
end
|
786
801
|
prose_opts[:first_line_options] = first_line_options if first_line_options
|
787
802
|
# FIXME: make this cleaner!!
|
788
803
|
if node.blocks?
|
@@ -791,7 +806,7 @@ module Asciidoctor
|
|
791
806
|
if child.context == :paragraph
|
792
807
|
child.document.playback_attributes child.attributes
|
793
808
|
prose_opts[:margin_bottom] = 0 if child == last_block
|
794
|
-
ink_prose child.content, ((align =
|
809
|
+
ink_prose child.content, ((align = resolve_text_align_from_role child.roles) ? (prose_opts.merge align: align) : prose_opts.dup)
|
795
810
|
prose_opts.delete :first_line_options
|
796
811
|
prose_opts.delete :margin_bottom
|
797
812
|
else
|
@@ -800,7 +815,7 @@ module Asciidoctor
|
|
800
815
|
end
|
801
816
|
end
|
802
817
|
elsif node.content_model != :compound && (string = node.content)
|
803
|
-
if (align =
|
818
|
+
if (align = resolve_text_align_from_role node.roles)
|
804
819
|
prose_opts[:align] = align
|
805
820
|
end
|
806
821
|
ink_prose string, (prose_opts.merge margin_bottom: 0)
|
@@ -824,39 +839,39 @@ module Asciidoctor
|
|
824
839
|
|
825
840
|
def convert_paragraph node
|
826
841
|
add_dest_for_block node if node.id
|
842
|
+
|
827
843
|
prose_opts = { margin_bottom: 0, hyphenate: true }
|
828
|
-
if (align =
|
844
|
+
if (align = resolve_text_align_from_role (roles = node.roles), query_theme: true, remove_predefined: true)
|
829
845
|
prose_opts[:align] = align
|
830
|
-
roles -= TextAlignmentRoles
|
831
846
|
end
|
832
|
-
|
847
|
+
role_keys = roles.map {|role| %(role_#{role}).to_sym } unless roles.empty?
|
833
848
|
if (text_indent = @theme.prose_text_indent) > 0 ||
|
834
849
|
((text_indent = @theme.prose_text_indent_inner) > 0 &&
|
835
850
|
(self_idx = (siblings = node.parent.blocks).index node) > 0 && siblings[self_idx - 1].context == :paragraph)
|
836
851
|
prose_opts[:indent_paragraphs] = text_indent
|
837
852
|
end
|
838
|
-
|
839
|
-
# TODO: check if we're within one line of the bottom of the page
|
840
|
-
# and advance to the next page if so (similar to logic for section titles)
|
841
|
-
ink_caption node, labeled: false if node.title?
|
842
|
-
|
843
853
|
if (bottom_gutter = @bottom_gutters[-1][node])
|
844
854
|
prose_opts[:bottom_gutter] = bottom_gutter
|
845
855
|
end
|
846
856
|
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
857
|
+
block_next = next_enclosed_block node
|
858
|
+
|
859
|
+
insert_margin_bottom = proc do
|
860
|
+
if (margin_inner_val = @theme.prose_margin_inner) && block_next&.context == :paragraph
|
861
|
+
margin_bottom margin_inner_val
|
862
|
+
else
|
863
|
+
theme_margin :prose, :bottom, block_next
|
852
864
|
end
|
853
865
|
end
|
854
866
|
|
855
|
-
|
856
|
-
|
857
|
-
margin_bottom margin_inner_val
|
867
|
+
if (float_box = (@float_box ||= nil))
|
868
|
+
ink_paragraph_in_float_box node, float_box, prose_opts, role_keys, block_next, insert_margin_bottom
|
858
869
|
else
|
859
|
-
|
870
|
+
# TODO: check if we're within one line of the bottom of the page
|
871
|
+
# and advance to the next page if so (similar to logic for section titles)
|
872
|
+
ink_caption node, labeled: false if node.title?
|
873
|
+
role_keys ? theme_font_cascade(role_keys) { ink_prose node.content, prose_opts } : (ink_prose node.content, prose_opts)
|
874
|
+
insert_margin_bottom.call
|
860
875
|
end
|
861
876
|
end
|
862
877
|
|
@@ -1122,9 +1137,11 @@ module Asciidoctor
|
|
1122
1137
|
add_dest_for_block node if node.id
|
1123
1138
|
theme_fill_and_stroke_block :sidebar, extent if extent
|
1124
1139
|
pad_box @theme.sidebar_padding, node do
|
1125
|
-
|
1126
|
-
|
1127
|
-
|
1140
|
+
tare_first_page_content_stream do
|
1141
|
+
theme_font :sidebar_title do
|
1142
|
+
# QUESTION: should we allow margins of sidebar title to be customized?
|
1143
|
+
ink_prose node.title, align: (@theme.sidebar_title_text_align || @theme.heading_text_align || @base_text_align).to_sym, margin_bottom: @theme.heading_margin_bottom, line_height: (@theme.heading_line_height || @theme.base_line_height)
|
1144
|
+
end
|
1128
1145
|
end if node.title?
|
1129
1146
|
theme_font :sidebar do
|
1130
1147
|
traverse node
|
@@ -1135,13 +1152,15 @@ module Asciidoctor
|
|
1135
1152
|
end
|
1136
1153
|
|
1137
1154
|
def convert_colist node
|
1138
|
-
|
1155
|
+
unless at_page_top? || (self_idx = (siblings = node.parent.blocks).index node) == 0 || !([:listing, :literal].include? siblings[self_idx - 1].context)
|
1156
|
+
margin_top @theme.callout_list_margin_top_after_code
|
1157
|
+
end
|
1139
1158
|
add_dest_for_block node if node.id
|
1140
1159
|
@list_numerals << 1
|
1141
1160
|
last_item = node.items[-1]
|
1142
1161
|
item_spacing = @theme.callout_list_item_spacing || @theme.list_item_spacing
|
1143
1162
|
item_opts = { margin_bottom: item_spacing, normalize_line_height: true }
|
1144
|
-
if (item_align = (
|
1163
|
+
if (item_align = (resolve_text_align_from_role node.roles) || @theme.list_text_align&.to_sym)
|
1145
1164
|
item_opts[:align] = item_align
|
1146
1165
|
end
|
1147
1166
|
theme_font :callout_list do
|
@@ -1360,7 +1379,7 @@ module Asciidoctor
|
|
1360
1379
|
ink_caption node, category: :list, labeled: false if node.title?
|
1361
1380
|
|
1362
1381
|
opts = {}
|
1363
|
-
if (align =
|
1382
|
+
if (align = resolve_text_align_from_role node.roles)
|
1364
1383
|
opts[:align] = align
|
1365
1384
|
elsif node.style == 'bibliography'
|
1366
1385
|
opts[:align] = :left
|
@@ -1553,9 +1572,13 @@ module Asciidoctor
|
|
1553
1572
|
|
1554
1573
|
return on_image_error :missing, node, target, opts unless image_path
|
1555
1574
|
|
1556
|
-
|
1557
|
-
|
1558
|
-
|
1575
|
+
if (float_to = node.attr 'float') && ((BlockFloatNames.include? float_to) ? float_to : (float_to = nil))
|
1576
|
+
alignment = float_to.to_sym
|
1577
|
+
elsif (alignment = node.attr 'align')
|
1578
|
+
alignment = (BlockAlignmentNames.include? alignment) ? alignment.to_sym : :left
|
1579
|
+
else
|
1580
|
+
alignment = (resolve_text_align_from_role node.roles) || @theme.image_align&.to_sym || :left
|
1581
|
+
end
|
1559
1582
|
# TODO: support cover (aka canvas) image layout using "canvas" (or "cover") role
|
1560
1583
|
width = resolve_explicit_width node.attributes, bounds_width: (available_w = bounds.width), support_vw: true, use_fallback: true, constrain_to_bounds: true
|
1561
1584
|
# TODO: add `to_pt page_width` method to ViewportWidth type
|
@@ -1563,6 +1586,7 @@ module Asciidoctor
|
|
1563
1586
|
|
1564
1587
|
caption_end = @theme.image_caption_end&.to_sym || :bottom
|
1565
1588
|
caption_max_width = @theme.image_caption_max_width
|
1589
|
+
caption_max_width = 'fit-content' if float_to && !(caption_max_width&.start_with? 'fit-content')
|
1566
1590
|
# NOTE: if width is not set explicitly and max-width is fit-content, caption height may not be accurate
|
1567
1591
|
caption_h = node.title? ? (ink_caption node, category: :image, end: caption_end, block_align: alignment, block_width: width, max_width: caption_max_width, dry_run: true, force_top_margin: caption_end == :bottom) : 0
|
1568
1592
|
|
@@ -1570,7 +1594,7 @@ module Asciidoctor
|
|
1570
1594
|
pinned = opts[:pinned]
|
1571
1595
|
|
1572
1596
|
begin
|
1573
|
-
rendered_w = nil
|
1597
|
+
rendered_h = rendered_w = nil
|
1574
1598
|
span_page_width_if align_to_page do
|
1575
1599
|
if image_format == 'svg'
|
1576
1600
|
if ::Base64 === image_path
|
@@ -1649,13 +1673,37 @@ module Asciidoctor
|
|
1649
1673
|
end
|
1650
1674
|
end
|
1651
1675
|
ink_caption node, category: :image, end: :bottom, block_align: alignment, block_width: rendered_w, max_width: caption_max_width if caption_end == :bottom && node.title?
|
1652
|
-
|
1676
|
+
if !pinned && (block_next = next_enclosed_block node)
|
1677
|
+
if float_to && (supports_float_wrapping? block_next) && rendered_w < bounds.width
|
1678
|
+
init_float_box node, rendered_w, rendered_h + caption_h, float_to
|
1679
|
+
else
|
1680
|
+
theme_margin :block, :bottom, block_next
|
1681
|
+
end
|
1682
|
+
end
|
1653
1683
|
rescue => e
|
1654
1684
|
raise if ::StopIteration === e
|
1655
1685
|
on_image_error :exception, node, target, (opts.merge message: %(could not embed image: #{image_path}; #{e.message}#{::Prawn::Errors::UnsupportedImageType === e && !(defined? ::GMagick::Image) ? '; install prawn-gmagick gem to add support' : ''}))
|
1656
1686
|
end
|
1657
1687
|
end
|
1658
1688
|
|
1689
|
+
def supports_float_wrapping? node
|
1690
|
+
node.context == :paragraph
|
1691
|
+
end
|
1692
|
+
|
1693
|
+
def init_float_box _node, block_width, block_height, float_to
|
1694
|
+
gap = ::Array === (gap = @theme.image_float_gap) ? gap.dup : [gap, gap]
|
1695
|
+
float_w = block_width + (gap[0] ||= 12)
|
1696
|
+
float_h = block_height + (gap[1] ||= 6)
|
1697
|
+
box_l = bounds.left + (float_to == 'right' ? 0 : float_w)
|
1698
|
+
box_t = cursor + block_height
|
1699
|
+
box_w = bounds.width - float_w
|
1700
|
+
box_r = box_l + box_w
|
1701
|
+
box_h = [box_t, float_h].min
|
1702
|
+
box_b = box_t - box_h
|
1703
|
+
move_cursor_to box_t
|
1704
|
+
@float_box = { page: page_number, top: box_t, right: box_r, bottom: box_b, left: box_l, width: box_w, height: box_h, gap: gap }
|
1705
|
+
end
|
1706
|
+
|
1659
1707
|
def draw_image_border top, w, h, alignment
|
1660
1708
|
if (Array @theme.image_border_width).any? {|it| it&.> 0 } && (@theme.image_border_color || @theme.base_border_color)
|
1661
1709
|
if (@theme.image_border_fit || 'content') == 'auto'
|
@@ -1687,7 +1735,7 @@ module Asciidoctor
|
|
1687
1735
|
theme_font :image_alt do
|
1688
1736
|
alignment = (alignment = node.attr 'align') ?
|
1689
1737
|
((BlockAlignmentNames.include? alignment) ? alignment.to_sym : :left) :
|
1690
|
-
(
|
1738
|
+
(resolve_text_align_from_role node.roles) || (@theme.image_align&.to_sym || :left)
|
1691
1739
|
ink_prose alt_text_template % alt_text_vars, align: alignment, margin: 0, normalize: false, single_line: true
|
1692
1740
|
end
|
1693
1741
|
ink_caption node, category: :image, end: :bottom if node.title?
|
@@ -1742,7 +1790,7 @@ module Asciidoctor
|
|
1742
1790
|
end
|
1743
1791
|
|
1744
1792
|
# QUESTION: can we avoid arranging fragments multiple times (conums & autofit) by eagerly preparing arranger?
|
1745
|
-
def
|
1793
|
+
def convert_code node
|
1746
1794
|
extensions = []
|
1747
1795
|
source_chunks = bg_color_override = font_color_override = adjusted_font_size = nil
|
1748
1796
|
theme_font :code do
|
@@ -1889,17 +1937,31 @@ module Asciidoctor
|
|
1889
1937
|
theme_margin :block, :bottom, (next_enclosed_block node)
|
1890
1938
|
end
|
1891
1939
|
|
1892
|
-
alias convert_listing
|
1893
|
-
alias convert_literal
|
1940
|
+
alias convert_listing convert_code
|
1941
|
+
alias convert_literal convert_code
|
1942
|
+
alias convert_listing_or_literal convert_code
|
1894
1943
|
|
1895
1944
|
def convert_pass node
|
1896
|
-
|
1897
|
-
|
1898
|
-
|
1899
|
-
|
1945
|
+
theme_font :code do
|
1946
|
+
typeset_formatted_text [text: (guard_indentation node.content), color: @theme.base_font_color], (calc_line_metrics @base_line_height)
|
1947
|
+
end
|
1948
|
+
theme_margin :block, :bottom, (next_enclosed_block node)
|
1900
1949
|
end
|
1901
1950
|
|
1902
|
-
|
1951
|
+
def convert_stem node
|
1952
|
+
arrange_block node do |extent|
|
1953
|
+
add_dest_for_block node if node.id
|
1954
|
+
tare_first_page_content_stream { theme_fill_and_stroke_block :code, extent, caption_node: node }
|
1955
|
+
pad_box @theme.code_padding, node do
|
1956
|
+
theme_font :code do
|
1957
|
+
typeset_formatted_text [text: (guard_indentation node.content), color: @font_color],
|
1958
|
+
(calc_line_metrics @base_line_height),
|
1959
|
+
bottom_gutter: @bottom_gutters[-1][node]
|
1960
|
+
end
|
1961
|
+
end
|
1962
|
+
end
|
1963
|
+
theme_margin :block, :bottom, (next_enclosed_block node)
|
1964
|
+
end
|
1903
1965
|
|
1904
1966
|
# Extract callout marks from string, indexed by 0-based line number
|
1905
1967
|
# Return an Array with the processed string as the first argument
|
@@ -2353,11 +2415,12 @@ module Asciidoctor
|
|
2353
2415
|
end
|
2354
2416
|
|
2355
2417
|
def convert_thematic_break node
|
2356
|
-
|
2357
|
-
|
2358
|
-
|
2359
|
-
|
2360
|
-
|
2418
|
+
pad_box @theme.thematic_break_padding || [@theme.thematic_break_margin_top, 0] do
|
2419
|
+
stroke_horizontal_rule @theme.thematic_break_border_color,
|
2420
|
+
line_width: @theme.thematic_break_border_width,
|
2421
|
+
line_style: (@theme.thematic_break_border_style&.to_sym || :solid)
|
2422
|
+
end
|
2423
|
+
conceal_page_top { theme_margin :block, :bottom, (next_enclosed_block node) }
|
2361
2424
|
end
|
2362
2425
|
|
2363
2426
|
def convert_toc node, opts = {}
|
@@ -2405,26 +2468,16 @@ module Asciidoctor
|
|
2405
2468
|
def convert_index_section node
|
2406
2469
|
space_needed_for_category = @theme.description_list_term_spacing + (2 * (height_of_typeset_text 'A'))
|
2407
2470
|
pagenum_sequence_style = node.document.attr 'index-pagenum-sequence-style'
|
2408
|
-
column_box [0, cursor], columns: @theme.index_columns, width: bounds.width, reflow_margins: true do
|
2409
|
-
def @bounding_box.move_past_bottom *args # rubocop:disable Lint/NestedMethodDefinition
|
2410
|
-
super(*args)
|
2411
|
-
@document.bounds = @parent = @document.margin_box if @current_column == 0 && @reflow_margins
|
2412
|
-
end
|
2471
|
+
column_box [0, cursor], columns: @theme.index_columns, width: bounds.width, reflow_margins: true, spacer: @theme.index_column_gap do
|
2413
2472
|
@index.categories.each do |category|
|
2414
|
-
|
2415
|
-
bounds.move_past_bottom if space_needed_for_category > y - reference_bounds.absolute_bottom
|
2473
|
+
bounds.move_past_bottom if space_needed_for_category > cursor
|
2416
2474
|
ink_prose category.name,
|
2417
2475
|
align: :left,
|
2418
2476
|
inline_format: false,
|
2419
2477
|
margin_bottom: @theme.description_list_term_spacing,
|
2420
2478
|
style: @theme.description_list_term_font_style&.to_sym
|
2421
2479
|
category.terms.each {|term| convert_index_list_item term, pagenum_sequence_style }
|
2422
|
-
|
2423
|
-
if @theme.prose_margin_bottom > y - reference_bounds.absolute_bottom
|
2424
|
-
bounds.move_past_bottom
|
2425
|
-
else
|
2426
|
-
move_down @theme.prose_margin_bottom
|
2427
|
-
end
|
2480
|
+
@theme.prose_margin_bottom > cursor ? bounds.move_past_bottom : (move_down @theme.prose_margin_bottom)
|
2428
2481
|
end
|
2429
2482
|
end
|
2430
2483
|
nil
|
@@ -2926,23 +2979,31 @@ module Asciidoctor
|
|
2926
2979
|
|
2927
2980
|
alias start_new_part start_new_chapter
|
2928
2981
|
|
2929
|
-
|
2930
|
-
|
2982
|
+
# Position the cursor for where to ink the specified section title or discrete heading node.
|
2983
|
+
#
|
2984
|
+
# This method computes whether there is enough room on the page to prevent the specified node
|
2985
|
+
# from being orphaned. If there is not enough room, the method will advance the cursor to
|
2986
|
+
# the next page. This method is not called if the cursor is already at the top of the page or
|
2987
|
+
# whether this node has no node that follows it in document order.
|
2988
|
+
def arrange_heading node, title, opts
|
2989
|
+
if node.option? 'breakable'
|
2931
2990
|
orphaned = nil
|
2932
2991
|
dry_run single_page: true do
|
2933
2992
|
start_page = page
|
2934
2993
|
theme_font :heading, level: opts[:level] do
|
2935
2994
|
if opts[:part]
|
2936
|
-
ink_part_title
|
2995
|
+
ink_part_title node, title, opts
|
2937
2996
|
elsif opts[:chapterlike]
|
2938
|
-
ink_chapter_title
|
2997
|
+
ink_chapter_title node, title, opts
|
2939
2998
|
else
|
2940
|
-
ink_general_heading
|
2999
|
+
ink_general_heading node, title, opts
|
2941
3000
|
end
|
2942
3001
|
end
|
2943
3002
|
if page == start_page
|
2944
3003
|
page.tare_content_stream
|
2945
|
-
orphaned = stop_if_first_page_empty
|
3004
|
+
orphaned = stop_if_first_page_empty do
|
3005
|
+
node.context == :section ? (traverse node) : (convert (siblings = node.parent.blocks)[(siblings.index node) + 1])
|
3006
|
+
end
|
2946
3007
|
end
|
2947
3008
|
end
|
2948
3009
|
start_new_page if orphaned
|
@@ -2954,7 +3015,10 @@ module Asciidoctor
|
|
2954
3015
|
heading_h = (height_of_typeset_text title) +
|
2955
3016
|
(@theme[%(heading_h#{hlevel}_margin_top)] || @theme.heading_margin_top) +
|
2956
3017
|
(@theme[%(heading_h#{hlevel}_margin_bottom)] || @theme.heading_margin_bottom) + h_padding_t + h_padding_b
|
2957
|
-
|
3018
|
+
if (min_height_after = @theme.heading_min_height_after) &&
|
3019
|
+
(node.context == :section ? node.blocks? : node != node.parent.blocks[-1])
|
3020
|
+
heading_h += min_height_after
|
3021
|
+
end
|
2958
3022
|
cursor >= heading_h
|
2959
3023
|
end
|
2960
3024
|
start_new_page unless h_fits
|
@@ -3021,6 +3085,64 @@ module Asciidoctor
|
|
3021
3085
|
end
|
3022
3086
|
end
|
3023
3087
|
|
3088
|
+
# private
|
3089
|
+
def ink_paragraph_in_float_box node, float_box, prose_opts, role_keys, block_next, insert_margin_bottom
|
3090
|
+
@float_box = para_font_descender = para_font_size = end_cursor = nil
|
3091
|
+
if role_keys
|
3092
|
+
line_metrics = theme_font_cascade role_keys do
|
3093
|
+
para_font_descender = font.descender
|
3094
|
+
para_font_size = font_size
|
3095
|
+
calc_line_metrics @base_line_height
|
3096
|
+
end
|
3097
|
+
else
|
3098
|
+
para_font_descender = font.descender
|
3099
|
+
para_font_size = font_size
|
3100
|
+
line_metrics = calc_line_metrics @base_line_height
|
3101
|
+
end
|
3102
|
+
# allocate the space of at least one empty line below block
|
3103
|
+
line_height_length = line_metrics.height + line_metrics.leading + line_metrics.padding_top
|
3104
|
+
start_page_number = float_box[:page]
|
3105
|
+
start_cursor = cursor
|
3106
|
+
block_bottom = (float_box_bottom = float_box[:bottom]) + float_box[:gap][1]
|
3107
|
+
# use :at to incorporate padding top from line metrics
|
3108
|
+
# use :final_gap to incorporate padding bottom from line metrics
|
3109
|
+
# use :draw_text_callback to track end cursor (requires applying :final_gap to result manually)
|
3110
|
+
prose_opts.update \
|
3111
|
+
at: [float_box[:left], start_cursor - line_metrics.padding_top],
|
3112
|
+
width: float_box[:width],
|
3113
|
+
height: [cursor, float_box[:height] - (float_box[:top] - start_cursor) + line_height_length].min,
|
3114
|
+
final_gap: para_font_descender + line_metrics.padding_bottom,
|
3115
|
+
draw_text_callback: (proc do |text, opts|
|
3116
|
+
draw_text! text, opts
|
3117
|
+
end_cursor = opts[:at][1] # does not include :final_gap value
|
3118
|
+
end)
|
3119
|
+
overflow_text = role_keys ?
|
3120
|
+
theme_font_cascade(role_keys) { ink_prose node.content, prose_opts } :
|
3121
|
+
(ink_prose node.content, prose_opts)
|
3122
|
+
move_cursor_to end_cursor -= prose_opts[:final_gap] if end_cursor # ink_prose with :height does not move cursor
|
3123
|
+
if overflow_text.empty?
|
3124
|
+
if block_next && (supports_float_wrapping? block_next)
|
3125
|
+
insert_margin_bottom.call
|
3126
|
+
@float_box = float_box if page_number == start_page_number && cursor > start_cursor - prose_opts[:height]
|
3127
|
+
elsif end_cursor > block_bottom
|
3128
|
+
move_cursor_to block_bottom
|
3129
|
+
theme_margin :block, :bottom, block_next
|
3130
|
+
else
|
3131
|
+
insert_margin_bottom.call
|
3132
|
+
end
|
3133
|
+
else
|
3134
|
+
overflow_prose_opts = { align: prose_opts[:align] || @base_text_align.to_sym }
|
3135
|
+
unless end_cursor
|
3136
|
+
overflow_prose_opts[:indent_paragraphs] = prose_opts[:indent_paragraphs]
|
3137
|
+
move_cursor_to float_box_bottom if start_cursor > float_box_bottom
|
3138
|
+
end
|
3139
|
+
role_keys ?
|
3140
|
+
theme_font_cascade(role_keys) { typeset_formatted_text overflow_text, line_metrics, overflow_prose_opts } :
|
3141
|
+
(typeset_formatted_text overflow_text, line_metrics, overflow_prose_opts)
|
3142
|
+
insert_margin_bottom.call
|
3143
|
+
end
|
3144
|
+
end
|
3145
|
+
|
3024
3146
|
# NOTE: inline_format is true by default
|
3025
3147
|
def ink_prose string, opts = {}
|
3026
3148
|
top_margin = (margin = (opts.delete :margin)) || (opts.delete :margin_top) || 0
|
@@ -3043,12 +3165,13 @@ module Asciidoctor
|
|
3043
3165
|
text_decoration_width: (opts.delete :text_decoration_width),
|
3044
3166
|
}.compact
|
3045
3167
|
end
|
3046
|
-
typeset_text string, (calc_line_metrics (opts.delete :line_height) || @base_line_height), {
|
3168
|
+
result = typeset_text string, (calc_line_metrics (opts.delete :line_height) || @base_line_height), {
|
3047
3169
|
color: @font_color,
|
3048
3170
|
inline_format: [inline_format_opts],
|
3049
3171
|
align: @base_text_align.to_sym,
|
3050
3172
|
}.merge(opts)
|
3051
3173
|
margin_bottom bot_margin
|
3174
|
+
result
|
3052
3175
|
end
|
3053
3176
|
|
3054
3177
|
def generate_manname_section node
|
@@ -3071,11 +3194,12 @@ module Asciidoctor
|
|
3071
3194
|
# title (i.e., subject.title? returns true).
|
3072
3195
|
def ink_caption subject, opts = {}
|
3073
3196
|
if opts.delete :dry_run
|
3074
|
-
force_top_margin = !at_page_top? if (force_top_margin = opts.delete :force_top_margin).nil?
|
3075
3197
|
return (dry_run keep_together: true, single_page: :enforce do
|
3076
|
-
|
3077
|
-
|
3078
|
-
|
3198
|
+
if opts.delete :force_top_margin
|
3199
|
+
conceal_page_top { ink_caption subject, opts }
|
3200
|
+
else
|
3201
|
+
ink_caption subject, opts
|
3202
|
+
end
|
3079
3203
|
end).single_page_height
|
3080
3204
|
end
|
3081
3205
|
if ::Asciidoctor::AbstractBlock === subject
|
@@ -3103,11 +3227,22 @@ module Asciidoctor
|
|
3103
3227
|
if (max_width = opts.delete :max_width) && max_width != 'none'
|
3104
3228
|
if ::String === max_width
|
3105
3229
|
if max_width.start_with? 'fit-content'
|
3106
|
-
|
3107
|
-
|
3108
|
-
|
3109
|
-
|
3230
|
+
block_width ||= container_width
|
3231
|
+
unless max_width.end_with? 't', '()'
|
3232
|
+
max_width = block_width * (max_width.slice 12, max_width.length - 1).to_f / 100.0
|
3233
|
+
if (caption_width_delta = block_width - max_width) > 0
|
3234
|
+
case align
|
3235
|
+
when :right
|
3236
|
+
indent_by[0] += caption_width_delta
|
3237
|
+
when :center
|
3238
|
+
indent_by[0] += caption_width_delta * 0.5
|
3239
|
+
indent_by[1] += caption_width_delta * 0.5
|
3240
|
+
else # :left, nil
|
3241
|
+
indent_by[1] += caption_width_delta
|
3242
|
+
end
|
3243
|
+
end
|
3110
3244
|
end
|
3245
|
+
max_width = block_width
|
3111
3246
|
elsif max_width.end_with? '%'
|
3112
3247
|
max_width = [max_width.to_f / 100 * bounds.width, bounds.width].min
|
3113
3248
|
block_align = align
|
@@ -3122,11 +3257,12 @@ module Asciidoctor
|
|
3122
3257
|
if (remainder = container_width - max_width) > 0
|
3123
3258
|
case block_align
|
3124
3259
|
when :right
|
3125
|
-
indent_by
|
3260
|
+
indent_by[0] += remainder
|
3126
3261
|
when :center
|
3127
|
-
indent_by
|
3262
|
+
indent_by[0] += remainder * 0.5
|
3263
|
+
indent_by[1] += remainder * 0.5
|
3128
3264
|
else # :left, nil
|
3129
|
-
indent_by
|
3265
|
+
indent_by[1] += remainder
|
3130
3266
|
end
|
3131
3267
|
end
|
3132
3268
|
end
|
@@ -3522,8 +3658,9 @@ module Asciidoctor
|
|
3522
3658
|
content = apply_subs_discretely doc, content, drop_lines_with_unresolved_attributes: true, imagesdir: @themesdir
|
3523
3659
|
content = transform_text content, @text_transform if @text_transform
|
3524
3660
|
end
|
3525
|
-
formatted_text_box (parse_text content,
|
3661
|
+
formatted_text_box (parse_text content, inline_format: [normalize: true]),
|
3526
3662
|
at: [left, bounds.top - trim_styles[:padding][side][0] - trim_styles[:content_offset] + ((Array trim_styles[:valign])[0] == :center ? font.descender * 0.5 : 0)],
|
3663
|
+
color: @font_color,
|
3527
3664
|
width: colwidth,
|
3528
3665
|
height: trim_styles[:prose_content_height][side],
|
3529
3666
|
align: colspec[:align],
|
@@ -4146,9 +4283,10 @@ module Asciidoctor
|
|
4146
4283
|
|
4147
4284
|
# TODO: document me, esp the first line formatting functionality
|
4148
4285
|
def typeset_text string, line_metrics, opts = {}
|
4149
|
-
move_down line_metrics.padding_top
|
4150
4286
|
opts = { leading: line_metrics.leading, final_gap: line_metrics.final_gap }.merge opts
|
4151
4287
|
string = string.gsub CjkLineBreakRx, ZeroWidthSpace if @cjk_line_breaks
|
4288
|
+
return text_box string, opts if opts[:height]
|
4289
|
+
move_down line_metrics.padding_top
|
4152
4290
|
if (hanging_indent = (opts.delete :hanging_indent) || 0) > 0
|
4153
4291
|
indent hanging_indent do
|
4154
4292
|
text string, (opts.merge indent_paragraphs: -hanging_indent)
|
@@ -4312,10 +4450,11 @@ module Asciidoctor
|
|
4312
4450
|
end
|
4313
4451
|
end
|
4314
4452
|
|
4315
|
-
def
|
4453
|
+
def resolve_text_align_from_role roles, query_theme: false, remove_predefined: false
|
4316
4454
|
if (align_role = roles.reverse.find {|r| TextAlignmentRoles.include? r })
|
4455
|
+
roles.replace roles - TextAlignmentRoles if remove_predefined
|
4317
4456
|
(align_role.slice 5, align_role.length).to_sym
|
4318
|
-
elsif
|
4457
|
+
elsif query_theme
|
4319
4458
|
roles.reverse.each do |role|
|
4320
4459
|
if (align = @theme[%(role_#{role}_text_align)])
|
4321
4460
|
return align.to_sym
|
@@ -4325,6 +4464,9 @@ module Asciidoctor
|
|
4325
4464
|
end
|
4326
4465
|
end
|
4327
4466
|
|
4467
|
+
# Deprecated
|
4468
|
+
alias resolve_alignment_from_role resolve_text_align_from_role
|
4469
|
+
|
4328
4470
|
# QUESTION: is this method still necessary?
|
4329
4471
|
def resolve_imagesdir doc
|
4330
4472
|
if (imagesdir = doc.attr 'imagesdir').nil_or_empty? || (imagesdir = imagesdir.chomp '/') == '.'
|
@@ -4669,9 +4811,12 @@ module Asciidoctor
|
|
4669
4811
|
alias layout_running_content ink_running_content
|
4670
4812
|
|
4671
4813
|
# intercepts "class CustomPDFConverter < (Asciidoctor::Converter.for 'pdf')"
|
4672
|
-
def self.method_added
|
4673
|
-
if (method_name =
|
4674
|
-
alias_method %(ink_#{method_name.slice 7, method_name.length}).to_sym,
|
4814
|
+
def self.method_added method_sym
|
4815
|
+
if (method_name = method_sym.to_s).start_with? 'layout_'
|
4816
|
+
alias_method %(ink_#{method_name.slice 7, method_name.length}).to_sym, method_sym
|
4817
|
+
elsif method_name == 'convert_listing_or_literal' || method_name == 'convert_code'
|
4818
|
+
alias_method :convert_listing, method_sym
|
4819
|
+
alias_method :convert_literal, method_sym
|
4675
4820
|
end
|
4676
4821
|
end
|
4677
4822
|
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Prawn::Document::ColumnBox.prepend (Module.new do
|
4
|
+
def absolute_bottom
|
5
|
+
stretchy? ? @parent.absolute_bottom : super
|
6
|
+
end
|
7
|
+
|
8
|
+
def move_past_bottom *_args
|
9
|
+
initial_page = @document.page
|
10
|
+
super
|
11
|
+
if (page = @document.page) != initial_page && page.margins != initial_page.margins
|
12
|
+
@document.bounds = self.class.new @document, @parent, (margin_box = @document.margin_box).absolute_top_left,
|
13
|
+
columns: @columns, reflow_margins: true, spacer: @spacer, width: margin_box.width
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end)
|