asciidoctor-pdf 2.0.0.alpha.2 → 2.0.0.beta.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +185 -83
- data/README.adoc +31 -126
- data/data/fonts/ABOUT-mplus1p-subset +1 -0
- data/data/fonts/ABOUT-notosans-subset +1 -0
- data/data/fonts/ABOUT-notoserif-subset +1 -0
- data/data/fonts/mplus1mn-bold-subset.ttf +0 -0
- data/data/fonts/mplus1mn-bold_italic-subset.ttf +0 -0
- data/data/fonts/mplus1mn-italic-subset.ttf +0 -0
- data/data/fonts/mplus1mn-regular-subset.ttf +0 -0
- data/data/fonts/mplus1p-regular-fallback.ttf +0 -0
- data/data/fonts/notosans-bold-subset.ttf +0 -0
- data/data/fonts/notosans-bold_italic-subset.ttf +0 -0
- data/data/fonts/notosans-italic-subset.ttf +0 -0
- data/data/fonts/notosans-regular-subset.ttf +0 -0
- data/data/fonts/notoserif-bold-subset.ttf +0 -0
- data/data/fonts/notoserif-bold_italic-subset.ttf +0 -0
- data/data/fonts/notoserif-italic-subset.ttf +0 -0
- data/data/fonts/notoserif-regular-subset.ttf +0 -0
- data/data/themes/base-theme.yml +10 -20
- data/data/themes/default-for-print-theme.yml +4 -4
- data/data/themes/default-for-print-with-fallback-font-theme.yml +2 -3
- data/data/themes/default-for-print-with-font-fallbacks-theme.yml +3 -0
- data/data/themes/{sans-with-fallback-font-theme.yml → default-sans-theme.yml} +1 -1
- data/data/themes/default-sans-with-font-fallbacks-theme.yml +3 -0
- data/data/themes/default-theme.yml +5 -4
- data/data/themes/default-with-fallback-font-theme.yml +2 -9
- data/data/themes/default-with-font-fallbacks-theme.yml +9 -0
- data/docs/theming-guide.adoc +5 -6050
- data/lib/asciidoctor/pdf/converter.rb +505 -294
- data/lib/asciidoctor/pdf/ext/pdf-core/page.rb +8 -0
- data/lib/asciidoctor/pdf/ext/prawn/document/column_box.rb +16 -0
- data/lib/asciidoctor/pdf/ext/prawn/extensions.rb +110 -58
- data/lib/asciidoctor/pdf/ext/prawn/formatted_text/box.rb +14 -0
- data/lib/asciidoctor/pdf/ext/prawn/formatted_text/indented_paragraph_wrap.rb +39 -0
- data/lib/asciidoctor/pdf/ext/prawn-table/cell/asciidoc.rb +3 -10
- data/lib/asciidoctor/pdf/ext/prawn.rb +2 -0
- data/lib/asciidoctor/pdf/formatted_text/source_wrap.rb +7 -2
- data/lib/asciidoctor/pdf/nopngmagick.rb +3 -0
- data/lib/asciidoctor/pdf/optimizer.rb +12 -5
- data/lib/asciidoctor/pdf/text_transformer.rb +14 -0
- data/lib/asciidoctor/pdf/theme_loader.rb +9 -3
- data/lib/asciidoctor/pdf/version.rb +1 -1
- metadata +9 -3
@@ -14,6 +14,14 @@ class PDF::Core::Page
|
|
14
14
|
content.stream.filtered_stream == (@tare_content_stream ||= InitialPageContent) && document.page_number > 0
|
15
15
|
end
|
16
16
|
|
17
|
+
# Flags this page as imported.
|
18
|
+
#
|
19
|
+
def imported
|
20
|
+
@imported_page = true
|
21
|
+
end
|
22
|
+
|
23
|
+
alias imported_page imported
|
24
|
+
|
17
25
|
# Reset the content of the page.
|
18
26
|
# Note that this method may leave behind an orphaned background image.
|
19
27
|
def reset_content
|
@@ -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)
|
@@ -103,7 +103,7 @@ module Asciidoctor
|
|
103
103
|
NewPageRequiredError = ::Class.new ::StopIteration
|
104
104
|
|
105
105
|
InhibitNewPageProc = proc do |pdf|
|
106
|
-
pdf.
|
106
|
+
pdf.delete_current_page
|
107
107
|
raise NewPageRequiredError
|
108
108
|
end
|
109
109
|
|
@@ -111,7 +111,7 @@ module Asciidoctor
|
|
111
111
|
|
112
112
|
DetectEmptyFirstPageProc = proc do |delegate, pdf|
|
113
113
|
if pdf.state.pages[pdf.page_number - 2].empty?
|
114
|
-
pdf.
|
114
|
+
pdf.delete_current_page
|
115
115
|
raise NewPageRequiredError
|
116
116
|
end
|
117
117
|
delegate.call pdf if (pdf.state.on_page_create_callback = delegate)
|
@@ -252,6 +252,15 @@ module Asciidoctor
|
|
252
252
|
@y == @margin_box.absolute_top
|
253
253
|
end
|
254
254
|
|
255
|
+
# Prevents at_page_top? from returning true while yielding to the specified block.
|
256
|
+
#
|
257
|
+
def conceal_page_top
|
258
|
+
margin_box.instance_variable_set :@y, (old_top = margin_box.absolute_top) + 0.0001
|
259
|
+
yield
|
260
|
+
ensure
|
261
|
+
margin_box.instance_variable_set :@y, old_top
|
262
|
+
end
|
263
|
+
|
255
264
|
# Returns whether the current page is the last page in the document.
|
256
265
|
#
|
257
266
|
def last_page?
|
@@ -269,6 +278,20 @@ module Asciidoctor
|
|
269
278
|
dest_xyz 0, page_height, nil, (page_num ? state.pages[page_num - 1] : page)
|
270
279
|
end
|
271
280
|
|
281
|
+
# Gets the destination registered for the specified name. The return value
|
282
|
+
# matches that which was passed to the add_dest method.
|
283
|
+
#
|
284
|
+
def get_dest name, node = dests.data
|
285
|
+
node.children.each do |child|
|
286
|
+
if ::PDF::Core::NameTree::Value === child
|
287
|
+
return child.value.data if child.name == name
|
288
|
+
elsif (found = get_dest name, child)
|
289
|
+
return found
|
290
|
+
end
|
291
|
+
end
|
292
|
+
nil
|
293
|
+
end
|
294
|
+
|
272
295
|
# Fonts
|
273
296
|
|
274
297
|
# Registers a new custom font described in the data parameter
|
@@ -396,20 +419,11 @@ module Asciidoctor
|
|
396
419
|
def parse_text string, options = {}
|
397
420
|
return [] if string.nil?
|
398
421
|
|
399
|
-
|
400
|
-
if (format_option = options.delete :inline_format)
|
422
|
+
if (format_option = options[:inline_format])
|
401
423
|
format_option = [] unless ::Array === format_option
|
402
|
-
|
403
|
-
else
|
404
|
-
fragments = [text: string]
|
405
|
-
end
|
406
|
-
|
407
|
-
if (color = options.delete :color)
|
408
|
-
fragments.map do |fragment|
|
409
|
-
fragment[:color] ? fragment : (fragment.merge color: color)
|
410
|
-
end
|
424
|
+
text_formatter.format string, *format_option
|
411
425
|
else
|
412
|
-
|
426
|
+
[text: string]
|
413
427
|
end
|
414
428
|
end
|
415
429
|
|
@@ -421,7 +435,8 @@ module Asciidoctor
|
|
421
435
|
@no_text_printed = box.nothing_printed?
|
422
436
|
@all_text_printed = box.everything_printed?
|
423
437
|
|
424
|
-
if ((defined? @final_gap) && @final_gap) ||
|
438
|
+
if ((defined? @final_gap) && @final_gap) ||
|
439
|
+
(options[:first_line] && (options[:final_gap] || !(@no_text_printed || @all_text_printed)))
|
425
440
|
self.y -= box.height + box.line_gap + box.leading
|
426
441
|
else
|
427
442
|
self.y -= box.height
|
@@ -439,31 +454,50 @@ module Asciidoctor
|
|
439
454
|
# renderered. It's necessary to use low-level APIs in this method so we only style the first line and not the
|
440
455
|
# remaining lines (which is the default behavior in Prawn).
|
441
456
|
def text_with_formatted_first_line string, first_line_options, options
|
442
|
-
|
457
|
+
if (first_line_font_color = first_line_options.delete :color)
|
458
|
+
other_lines_font_color, options[:color] = options[:color], first_line_font_color
|
459
|
+
end
|
443
460
|
fragments = parse_text string, options
|
444
461
|
# NOTE: the low-level APIs we're using don't recognize the :styles option, so we must resolve
|
445
|
-
# NOTE: disabled until we have a need for it
|
462
|
+
# NOTE: disabled until we have a need for it; currently handled in convert_abstract
|
446
463
|
#if (styles = options.delete :styles)
|
447
464
|
# options[:style] = resolve_font_style styles
|
448
465
|
#end
|
449
466
|
if (first_line_styles = first_line_options.delete :styles)
|
450
467
|
first_line_options[:style] = resolve_font_style first_line_styles
|
451
468
|
end
|
452
|
-
|
469
|
+
first_line_text_transform = first_line_options.delete :text_transform
|
453
470
|
options = options.merge document: self
|
471
|
+
text_indent = options.delete :indent_paragraphs
|
454
472
|
# QUESTION: should we merge more carefully here? (hand-select keys?)
|
455
473
|
first_line_options = (options.merge first_line_options).merge single_line: true, first_line: true
|
456
474
|
box = ::Prawn::Text::Formatted::Box.new fragments, first_line_options
|
457
|
-
|
458
|
-
if (text_indent = options.delete :indent_paragraphs)
|
475
|
+
if text_indent
|
459
476
|
remaining_fragments = indent text_indent do
|
460
477
|
box.render dry_run: true
|
461
478
|
end
|
462
479
|
else
|
463
480
|
remaining_fragments = box.render dry_run: true
|
464
481
|
end
|
465
|
-
|
466
|
-
|
482
|
+
if first_line_text_transform
|
483
|
+
# NOTE: applying text transform here could alter the wrapping, so we need to isolate first line and shrink to fit
|
484
|
+
first_line_text = (box.instance_variable_get :@printed_lines)[0]
|
485
|
+
unless first_line_text == fragments[0][:text]
|
486
|
+
original_fragments, fragments = fragments, []
|
487
|
+
original_fragments.reduce '' do |traced, fragment|
|
488
|
+
fragments << fragment
|
489
|
+
# NOTE: we could just do a length comparison here
|
490
|
+
if (traced += fragment[:text]).start_with? first_line_text
|
491
|
+
fragment[:text] = fragment[:text][0...-(traced.length - first_line_text.length)]
|
492
|
+
break
|
493
|
+
end
|
494
|
+
traced
|
495
|
+
end
|
496
|
+
end
|
497
|
+
fragments.each {|fragment| fragment[:text] = transform_text fragment[:text], first_line_text_transform }
|
498
|
+
first_line_options[:overflow] = :shrink_to_fit
|
499
|
+
first_line_options[:final_gap] = first_line_options[:force_justify] = true unless remaining_fragments.empty?
|
500
|
+
end
|
467
501
|
if text_indent
|
468
502
|
indent text_indent do
|
469
503
|
fill_formatted_text_box fragments, first_line_options
|
@@ -472,8 +506,7 @@ module Asciidoctor
|
|
472
506
|
fill_formatted_text_box fragments, first_line_options
|
473
507
|
end
|
474
508
|
unless remaining_fragments.empty?
|
475
|
-
|
476
|
-
remaining_fragments.each {|fragment| fragment[:color] ||= color }
|
509
|
+
options[:color] = other_lines_font_color if first_line_font_color
|
477
510
|
remaining_fragments = fill_formatted_text_box remaining_fragments, options
|
478
511
|
draw_remaining_formatted_text_on_new_pages remaining_fragments, options
|
479
512
|
end
|
@@ -495,6 +528,8 @@ module Asciidoctor
|
|
495
528
|
lowercase_pcdata text
|
496
529
|
when :capitalize, 'capitalize'
|
497
530
|
capitalize_words_pcdata text
|
531
|
+
when :smallcaps, 'smallcaps'
|
532
|
+
smallcaps_pcdata text
|
498
533
|
else
|
499
534
|
text
|
500
535
|
end
|
@@ -538,17 +573,20 @@ module Asciidoctor
|
|
538
573
|
# Example:
|
539
574
|
#
|
540
575
|
# pad_box 20 do
|
541
|
-
# text 'A paragraph inside a blox with even padding
|
576
|
+
# text 'A paragraph inside a blox with even padding from all edges.'
|
577
|
+
# end
|
578
|
+
#
|
579
|
+
# pad_box [10, 5] do
|
580
|
+
# text 'A paragraph inside a box with different padding from ends and sides.'
|
542
581
|
# end
|
543
582
|
#
|
544
|
-
# pad_box [
|
545
|
-
# text '
|
583
|
+
# pad_box [5, 10, 15, 20] do
|
584
|
+
# text 'A paragraph inside a box with different padding from each edge.'
|
546
585
|
# end
|
547
586
|
#
|
548
587
|
def pad_box padding, node = nil
|
549
588
|
if padding
|
550
|
-
|
551
|
-
p_top, p_right, p_bottom, p_left = ::Array === padding ? padding : (::Array.new 4, padding)
|
589
|
+
p_top, p_right, p_bottom, p_left = expand_padding_value padding
|
552
590
|
# logic is intentionally inlined
|
553
591
|
begin
|
554
592
|
if node && ((last_block = node).content_model != :compound || (last_block = node.blocks[-1])&.context == :paragraph)
|
@@ -560,8 +598,8 @@ module Asciidoctor
|
|
560
598
|
bounds.add_left_padding p_left
|
561
599
|
bounds.add_right_padding p_right
|
562
600
|
yield
|
563
|
-
cursor > p_bottom ? (move_down p_bottom) : reference_bounds.move_past_bottom unless at_page_top?
|
564
601
|
ensure
|
602
|
+
cursor > p_bottom ? (move_down p_bottom) : reference_bounds.move_past_bottom unless at_page_top?
|
565
603
|
@bottom_gutters.pop
|
566
604
|
bounds.subtract_left_padding p_left
|
567
605
|
bounds.subtract_right_padding p_right
|
@@ -576,26 +614,25 @@ module Asciidoctor
|
|
576
614
|
end
|
577
615
|
|
578
616
|
def expand_padding_value shorthand
|
579
|
-
|
580
|
-
if ::Array ===
|
581
|
-
case
|
617
|
+
(@edge_shorthand_cache ||= ::Hash.new do |store, key|
|
618
|
+
if ::Array === key
|
619
|
+
case key.size
|
582
620
|
when 1
|
583
|
-
|
621
|
+
value = [(value0 = key[0] || 0), value0, value0, value0]
|
584
622
|
when 2
|
585
|
-
|
623
|
+
value = [(value0 = key[0] || 0), (value1 = key[1] || 0), value0, value1]
|
586
624
|
when 3
|
587
|
-
|
625
|
+
value = [key[0] || 0, (value1 = key[1] || 0), key[2] || 0, value1]
|
588
626
|
when 4
|
589
|
-
|
627
|
+
value = key.map {|it| it || 0 }
|
590
628
|
else
|
591
|
-
|
629
|
+
value = (key.slice 0, 4).map {|it| it || 0 }
|
592
630
|
end
|
593
631
|
else
|
594
|
-
|
632
|
+
value = [(value0 = key || 0), value0, value0, value0]
|
595
633
|
end
|
596
|
-
|
597
|
-
end
|
598
|
-
padding.dup
|
634
|
+
store[key] = value
|
635
|
+
end)[shorthand]
|
599
636
|
end
|
600
637
|
|
601
638
|
alias expand_margin_value expand_padding_value
|
@@ -700,7 +737,8 @@ module Asciidoctor
|
|
700
737
|
def fill_and_stroke_bounds f_color = fill_color, s_color = stroke_color, options = {}
|
701
738
|
no_fill = !f_color || f_color == 'transparent'
|
702
739
|
if ::Array === (s_width = options[:line_width] || 0)
|
703
|
-
|
740
|
+
s_width = [s_width[0], s_width[1], s_width[0], s_width[1]] if s_width.size == 2
|
741
|
+
s_width_max = (s_width = s_width.map {|it| it || 0 }).max
|
704
742
|
radius = 0
|
705
743
|
else
|
706
744
|
radius = options[:radius] || 0
|
@@ -718,13 +756,19 @@ module Asciidoctor
|
|
718
756
|
|
719
757
|
# stroke
|
720
758
|
if s_width_max
|
721
|
-
|
722
|
-
|
723
|
-
|
759
|
+
s_width_top, s_width_right, s_width_bottom, s_width_left = s_width
|
760
|
+
projection_top, projection_right, projection_bottom, projection_left = s_width.map {|it| it * 0.5 }
|
761
|
+
if s_width_top > 0
|
762
|
+
stroke_horizontal_rule s_color, line_width: s_width_top, line_style: options[:line_style], left_projection: projection_left, right_projection: projection_right
|
763
|
+
end
|
764
|
+
if s_width_right > 0
|
765
|
+
stroke_vertical_rule s_color, line_width: s_width_right, line_style: options[:line_style], at: bounds.width, top_projection: projection_top, bottom_projection: projection_bottom
|
724
766
|
end
|
725
|
-
if
|
726
|
-
|
727
|
-
|
767
|
+
if s_width_bottom > 0
|
768
|
+
stroke_horizontal_rule s_color, line_width: s_width_bottom, line_style: options[:line_style], at: bounds.height, left_projection: projection_left, right_projection: projection_right
|
769
|
+
end
|
770
|
+
if s_width_left > 0
|
771
|
+
stroke_vertical_rule s_color, line_width: s_width_left, line_style: options[:line_style], top_projection: projection_top, bottom_projection: projection_bottom
|
728
772
|
end
|
729
773
|
else
|
730
774
|
stroke_color s_color
|
@@ -759,8 +803,8 @@ module Asciidoctor
|
|
759
803
|
rule_y = cursor - (options[:at] || 0)
|
760
804
|
rule_style = options[:line_style]
|
761
805
|
rule_width = options[:line_width] || 0.5
|
762
|
-
rule_x_start = bounds.left
|
763
|
-
rule_x_end = bounds.right
|
806
|
+
rule_x_start = bounds.left - (options[:left_projection] || 0)
|
807
|
+
rule_x_end = bounds.right - (options[:right_projection] || 0)
|
764
808
|
save_graphics_state do
|
765
809
|
stroke_color rule_color
|
766
810
|
case rule_style
|
@@ -790,8 +834,8 @@ module Asciidoctor
|
|
790
834
|
#
|
791
835
|
def stroke_vertical_rule rule_color = stroke_color, options = {}
|
792
836
|
rule_x = options[:at] || 0
|
793
|
-
rule_y_from = bounds.top
|
794
|
-
rule_y_to = bounds.bottom
|
837
|
+
rule_y_from = bounds.top + (options[:top_projection] || 0)
|
838
|
+
rule_y_to = bounds.bottom - (options[:bottom_projection] || 0)
|
795
839
|
rule_style = options[:line_style]
|
796
840
|
rule_width = options[:line_width] || 0.5
|
797
841
|
save_graphics_state do
|
@@ -814,7 +858,7 @@ module Asciidoctor
|
|
814
858
|
|
815
859
|
# Deletes the current page and move the cursor
|
816
860
|
# to the previous page.
|
817
|
-
def
|
861
|
+
def delete_current_page
|
818
862
|
pg = page_number
|
819
863
|
pdf_store = state.store
|
820
864
|
content_id = page.content.identifier
|
@@ -848,7 +892,7 @@ module Asciidoctor
|
|
848
892
|
prev_page_size = page.size
|
849
893
|
state.compress = false if state.compress # can't use compression if using template
|
850
894
|
prev_text_rendering_mode = (defined? @text_rendering_mode) ? @text_rendering_mode : nil
|
851
|
-
|
895
|
+
delete_current_page if options[:replace]
|
852
896
|
# NOTE: use functionality provided by prawn-templates
|
853
897
|
start_new_page_discretely template: file, template_page: options[:page]
|
854
898
|
# prawn-templates sets text_rendering_mode to :unknown, which breaks running content; revert
|
@@ -860,11 +904,11 @@ module Asciidoctor
|
|
860
904
|
# way atm to prevent the size & layout of the imported page from affecting subsequent pages
|
861
905
|
advance_page size: prev_page_size, layout: prev_page_layout if options.fetch :advance, true
|
862
906
|
elsif options.fetch :advance_if_missing, true
|
863
|
-
|
907
|
+
delete_current_page
|
864
908
|
# NOTE: see previous comment
|
865
909
|
advance_page size: prev_page_size, layout: prev_page_layout
|
866
910
|
else
|
867
|
-
|
911
|
+
delete_current_page
|
868
912
|
end
|
869
913
|
nil
|
870
914
|
end
|
@@ -993,7 +1037,15 @@ module Asciidoctor
|
|
993
1037
|
state.on_page_create_callback = delegate
|
994
1038
|
end
|
995
1039
|
|
996
|
-
#
|
1040
|
+
# This method delegates to the provided block, then tares (i.e., resets) the content stream of
|
1041
|
+
# the initial page.
|
1042
|
+
#
|
1043
|
+
# The purpose of this method is to ink content while making it appear as though the page is
|
1044
|
+
# empty. This technique allows the caller to detect whether any subsequent content was written
|
1045
|
+
# to the page following the content inked by the block. It's often used to keep the title of a
|
1046
|
+
# content block with the block's first child.
|
1047
|
+
#
|
1048
|
+
# NOTE: this method should only used inside dry_run since that's when DetectEmptyFirstPage is active
|
997
1049
|
def tare_first_page_content_stream
|
998
1050
|
return yield unless DetectEmptyFirstPage === (delegate = state.on_page_create_callback)
|
999
1051
|
on_page_create_called = nil
|
@@ -4,13 +4,18 @@ Prawn::Text::Formatted::Box.prepend (Module.new do
|
|
4
4
|
include Asciidoctor::Logging
|
5
5
|
|
6
6
|
def initialize formatted_text, options = {}
|
7
|
+
if (color = options[:color]) && !formatted_text.empty?
|
8
|
+
formatted_text = formatted_text.map {|fragment| fragment[:color] ? fragment : (fragment.merge color: color) }
|
9
|
+
end
|
7
10
|
super
|
8
11
|
formatted_text[0][:normalize_line_height] = true if options[:normalize_line_height] && !formatted_text.empty?
|
9
12
|
options[:extensions]&.each {|extension| extend extension }
|
13
|
+
extend Prawn::Text::Formatted::IndentedParagraphWrap if (@indent_paragraphs = options[:indent_paragraphs])
|
10
14
|
if (bottom_gutter = options[:bottom_gutter]) && bottom_gutter > 0
|
11
15
|
@bottom_gutter = bottom_gutter
|
12
16
|
extend Prawn::Text::Formatted::ProtectBottomGutter
|
13
17
|
end
|
18
|
+
@force_justify = options[:force_justify]
|
14
19
|
end
|
15
20
|
|
16
21
|
def draw_fragment_overlay_styles fragment
|
@@ -71,6 +76,15 @@ Prawn::Text::Formatted::Box.prepend (Module.new do
|
|
71
76
|
end
|
72
77
|
end
|
73
78
|
|
79
|
+
# Override method to force text justification when :force_justify option is set (typically for rendering a single line)
|
80
|
+
def word_spacing_for_this_line
|
81
|
+
if @align == :justify && (@force_justify || (@line_wrap.space_count > 0 && !@line_wrap.paragraph_finished?))
|
82
|
+
(available_width - @line_wrap.width) / @line_wrap.space_count
|
83
|
+
else
|
84
|
+
0
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
74
88
|
# Override method in super class to provide support for a tuple consisting of alignment and offset
|
75
89
|
def process_vertical_alignment text
|
76
90
|
return super if Symbol === (valign = @vertical_align)
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Prawn::Text::Formatted
|
4
|
+
module IndentedParagraphWrap
|
5
|
+
# Override Prawn::Text::Formatted::Box#wrap method to add support for :indent_paragraphs to (formatted_)text_box.
|
6
|
+
def wrap array
|
7
|
+
initialize_wrap array
|
8
|
+
stop = nil
|
9
|
+
until stop
|
10
|
+
if (first_line_indent = @indent_paragraphs) && @printed_lines.empty?
|
11
|
+
@width -= first_line_indent
|
12
|
+
stop = @document.indent(first_line_indent) { wrap_and_print_line }
|
13
|
+
@width += first_line_indent
|
14
|
+
else
|
15
|
+
stop = wrap_and_print_line
|
16
|
+
end
|
17
|
+
end
|
18
|
+
@text = @printed_lines.join ?\n
|
19
|
+
@everything_printed = @arranger.finished?
|
20
|
+
@arranger.unconsumed
|
21
|
+
end
|
22
|
+
|
23
|
+
def wrap_and_print_line
|
24
|
+
@line_wrap.wrap_line \
|
25
|
+
document: @document,
|
26
|
+
kerning: @kerning,
|
27
|
+
width: @width,
|
28
|
+
arranger: @arranger,
|
29
|
+
disable_wrap_by_char: @disable_wrap_by_char
|
30
|
+
if enough_height_for_this_line?
|
31
|
+
move_baseline_down
|
32
|
+
print_line
|
33
|
+
@single_line || @arranger.finished?
|
34
|
+
else
|
35
|
+
true
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -41,17 +41,10 @@ module Prawn
|
|
41
41
|
extent = @pdf.dry_run keep_together: true, single_page: true do
|
42
42
|
push_scratch parent_doc
|
43
43
|
doc.catalog[:footnotes] = parent_doc.catalog[:footnotes]
|
44
|
-
if padding_y > 0
|
45
|
-
move_down padding_y
|
46
|
-
#elsif at_page_top?
|
47
|
-
else
|
48
|
-
# TODO: encapsulate this logic to force top margin to be applied
|
49
|
-
margin_box.instance_variable_set :@y, margin_box.absolute_top + 0.0001
|
50
|
-
end
|
51
44
|
# NOTE: we should be able to use cell.max_width, but returns 0 in some conditions (like when colspan > 1)
|
52
45
|
indent cell.padding_left, bounds.width - cell.width + cell.padding_right do
|
53
|
-
|
54
|
-
traverse cell.content
|
46
|
+
move_down padding_y if padding_y > 0
|
47
|
+
conceal_page_top { traverse cell.content }
|
55
48
|
end
|
56
49
|
pop_scratch parent_doc
|
57
50
|
doc.catalog[:footnotes] = parent_doc.catalog[:footnotes]
|
@@ -99,7 +92,7 @@ module Prawn
|
|
99
92
|
apply_font_properties { pdf.traverse content }
|
100
93
|
if (extra_pages = pdf.page_number - start_page) > 0
|
101
94
|
logger.error %(the table cell on page #{start_page} has been truncated; Asciidoctor PDF does not support table cell content that exceeds the height of a single page) unless extra_pages == 1 && pdf.page.empty?
|
102
|
-
extra_pages.times { pdf.
|
95
|
+
extra_pages.times { pdf.delete_current_page }
|
103
96
|
end
|
104
97
|
nil
|
105
98
|
end
|
@@ -1,11 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# the following are organized under the Asciidoctor::Prawn namespace
|
4
|
+
require_relative 'prawn/document/column_box'
|
4
5
|
require_relative 'prawn/font_metric_cache'
|
5
6
|
require_relative 'prawn/font/afm'
|
6
7
|
require_relative 'prawn/images'
|
7
8
|
require_relative 'prawn/formatted_text/arranger'
|
8
9
|
require_relative 'prawn/formatted_text/box'
|
9
10
|
require_relative 'prawn/formatted_text/fragment'
|
11
|
+
require_relative 'prawn/formatted_text/indented_paragraph_wrap'
|
10
12
|
require_relative 'prawn/formatted_text/protect_bottom_gutter'
|
11
13
|
require_relative 'prawn/extensions'
|
@@ -24,14 +24,19 @@ module Asciidoctor
|
|
24
24
|
@arranger.unconsumed.unshift highlight_line if highlight_line
|
25
25
|
@arranger.unconsumed.unshift linenum_spacer.dup
|
26
26
|
end
|
27
|
-
@line_wrap.wrap_line
|
27
|
+
@line_wrap.wrap_line \
|
28
|
+
document: @document,
|
29
|
+
kerning: @kerning,
|
30
|
+
width: @width,
|
31
|
+
arranger: @arranger,
|
32
|
+
disable_wrap_by_char: @disable_wrap_by_char
|
28
33
|
if enough_height_for_this_line?
|
29
34
|
move_baseline_down
|
30
35
|
print_line
|
36
|
+
stop = @arranger.finished?
|
31
37
|
else
|
32
38
|
stop = true
|
33
39
|
end
|
34
|
-
stop ||= @arranger.finished?
|
35
40
|
end
|
36
41
|
@text = @printed_lines.join ?\n
|
37
42
|
@everything_printed = @arranger.finished?
|
@@ -40,9 +40,10 @@ module Asciidoctor
|
|
40
40
|
attr_reader :quality
|
41
41
|
attr_reader :compatibility_level
|
42
42
|
|
43
|
-
def initialize quality = 'default', compatibility_level = '1.4'
|
43
|
+
def initialize quality = 'default', compatibility_level = '1.4', compliance = 'PDF'
|
44
44
|
@quality = QUALITY_NAMES[quality]
|
45
45
|
@compatibility_level = compatibility_level
|
46
|
+
@compliance = compliance
|
46
47
|
if (gs_path = ::ENV['GS'])
|
47
48
|
::RGhost::Config::GS[:path] = gs_path
|
48
49
|
end
|
@@ -57,10 +58,16 @@ module Asciidoctor
|
|
57
58
|
else
|
58
59
|
inputs = target
|
59
60
|
end
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
d:
|
61
|
+
d = { Printed: false, CannotEmbedFontPolicy: '/Warning', CompatibilityLevel: @compatibility_level }
|
62
|
+
case @compliance
|
63
|
+
when 'PDF/A', 'PDF/A-1', 'PDF/A-2', 'PDF/A-3'
|
64
|
+
d[:PDFA] = ((@compliance.split '-', 2)[1] || 1).to_i
|
65
|
+
d[:ShowAnnots] = false
|
66
|
+
when 'PDF/X', 'PDF/X-1', 'PDF/X-3'
|
67
|
+
d[:PDFX] = true
|
68
|
+
d[:ShowAnnots] = false
|
69
|
+
end
|
70
|
+
(::RGhost::Convert.new inputs).to :pdf, filename: filename_tmp.to_s, quality: @quality, d: d
|
64
71
|
filename_o.binwrite filename_tmp.binread
|
65
72
|
end
|
66
73
|
nil
|
@@ -10,6 +10,12 @@ module Asciidoctor
|
|
10
10
|
WordRx = /\p{Word}+/
|
11
11
|
Hyphen = '-'
|
12
12
|
SoftHyphen = ?\u00ad
|
13
|
+
LowerAlphaChars = 'a-z'
|
14
|
+
# NOTE: use more widely-supported ғ instead of ꜰ as replacement for F
|
15
|
+
# NOTE: use more widely-supported ǫ instead of ꞯ as replacement for Q
|
16
|
+
# NOTE: use more widely-supported s (lowercase latin "s") instead of ꜱ as replacement for S
|
17
|
+
# NOTE: in small caps, x (lowercase latin "x") remains unchanged
|
18
|
+
SmallCapsChars = 'ᴀʙᴄᴅᴇғɢʜɪᴊᴋʟᴍɴoᴘǫʀsᴛᴜᴠᴡxʏᴢ'
|
13
19
|
|
14
20
|
def capitalize_words_pcdata string
|
15
21
|
if XMLMarkupRx.match? string
|
@@ -50,6 +56,14 @@ module Asciidoctor
|
|
50
56
|
string.upcase
|
51
57
|
end
|
52
58
|
end
|
59
|
+
|
60
|
+
def smallcaps_pcdata string
|
61
|
+
if XMLMarkupRx.match? string
|
62
|
+
string.gsub(PCDATAFilterRx) { $2 ? ($2.tr LowerAlphaChars, SmallCapsChars) : $1 }
|
63
|
+
else
|
64
|
+
string.tr LowerAlphaChars, SmallCapsChars
|
65
|
+
end
|
66
|
+
end
|
53
67
|
end
|
54
68
|
end
|
55
69
|
end
|
@@ -15,9 +15,10 @@ module Asciidoctor
|
|
15
15
|
BaseThemePath = ::File.join ThemesDir, 'base-theme.yml'
|
16
16
|
BundledThemeNames = (::Dir.children ThemesDir).map {|it| it.slice 0, it.length - 10 }
|
17
17
|
DeprecatedCategoryKeys = { 'blockquote' => 'quote', 'key' => 'kbd', 'literal' => 'codespan', 'outline_list' => 'list' }
|
18
|
-
|
18
|
+
DeprecatedKeys = %w(base heading heading_h1 heading_h2 heading_h3 heading_h4 heading_h5 heading_h6 title_page abstract abstract_title admonition_label sidebar_title toc_title).each_with_object({ 'table_caption_side' => 'table_caption_end' }) do |prefix, accum|
|
19
19
|
accum[%(#{prefix}_align)] = %(#{prefix}_text_align)
|
20
20
|
end
|
21
|
+
PaddingBottomHackKeys = %w(example_padding quote_padding sidebar_padding verse_padding)
|
21
22
|
|
22
23
|
VariableRx = /\$([a-z0-9_-]+)/
|
23
24
|
LoneVariableRx = /^\$([a-z0-9_-]+)$/
|
@@ -170,9 +171,14 @@ module Asciidoctor
|
|
170
171
|
val.each do |subkey, subval|
|
171
172
|
process_entry %(#{key}_#{key == 'role' || !(subkey.include? '-') ? subkey : (subkey.tr '-', '_')}), subval, data
|
172
173
|
end
|
173
|
-
elsif (rekey =
|
174
|
+
elsif (rekey = DeprecatedKeys[key]) ||
|
174
175
|
((key.start_with? 'role_') && (key.end_with? '_align') && (rekey = key.sub RoleAlignKeyRx, '_text_align'))
|
175
176
|
data[rekey] = evaluate val, data
|
177
|
+
elsif PaddingBottomHackKeys.include? key
|
178
|
+
val = evaluate val, data
|
179
|
+
# normalize padding hacks for themes designed before the converter had smart margins
|
180
|
+
val[2] = val[0] if ::Array === val && val[0].to_f >= 0 && val[2].to_f <= 0
|
181
|
+
data[key] = val
|
176
182
|
# QUESTION: do we really need to evaluate_math in this case?
|
177
183
|
elsif key.end_with? '_color'
|
178
184
|
if key == 'table_border_color'
|
@@ -216,7 +222,7 @@ module Asciidoctor
|
|
216
222
|
var = var.tr '-', '_' if var.include? '-'
|
217
223
|
if (vars.respond_to? var) ||
|
218
224
|
DeprecatedCategoryKeys.any? {|old, new| (var.start_with? old + '_') && (vars.respond_to? (replace = new + (var.slice old.length, var.length))) && (var = replace) } ||
|
219
|
-
((replace =
|
225
|
+
((replace = DeprecatedKeys[var]) && (vars.respond_to? replace) && (var = replace))
|
220
226
|
vars[var]
|
221
227
|
else
|
222
228
|
logger.warn %(unknown variable reference in PDF theme: #{ref})
|