asciidoctor-pdf 1.5.0.beta.2 → 1.5.0.beta.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +41 -0
- data/README.adoc +233 -18
- data/asciidoctor-pdf.gemspec +5 -2
- data/data/fonts/ABOUT-mplus1mn-subset +2 -0
- data/data/fonts/ABOUT-mplus1p-subset +1 -0
- data/data/fonts/ABOUT-notoserif-subset +1 -0
- data/data/fonts/mplus1mn-bold-ascii.ttf +0 -0
- data/data/fonts/mplus1mn-bold-subset.ttf +0 -0
- data/data/fonts/mplus1mn-bold_italic-ascii.ttf +0 -0
- data/data/fonts/mplus1mn-bold_italic-subset.ttf +0 -0
- data/data/fonts/mplus1mn-italic-ascii.ttf +0 -0
- data/data/fonts/mplus1mn-italic-subset.ttf +0 -0
- data/data/fonts/mplus1mn-regular-ascii-conums.ttf +0 -0
- data/data/fonts/mplus1mn-regular-subset.ttf +0 -0
- data/data/fonts/mplus1p-regular-fallback.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 +5 -2
- data/data/themes/default-theme.yml +5 -1
- data/docs/theming-guide.adoc +176 -53
- data/lib/asciidoctor-pdf/converter.rb +342 -238
- data/lib/asciidoctor-pdf/formatted_text/inline_image_arranger.rb +1 -1
- data/lib/asciidoctor-pdf/formatted_text/parser.rb +16 -4
- data/lib/asciidoctor-pdf/formatted_text/parser.treetop +1 -1
- data/lib/asciidoctor-pdf/formatted_text/transform.rb +22 -6
- data/lib/asciidoctor-pdf/implicit_header_processor.rb +1 -1
- data/lib/asciidoctor-pdf/prawn_ext/extensions.rb +3 -3
- data/lib/asciidoctor-pdf/prawn_ext/images.rb +3 -3
- data/lib/asciidoctor-pdf/sanitizer.rb +1 -1
- data/lib/asciidoctor-pdf/theme_loader.rb +54 -31
- data/lib/asciidoctor-pdf/version.rb +1 -1
- metadata +47 -5
@@ -39,8 +39,8 @@ class Converter < ::Prawn::Document
|
|
39
39
|
register_for 'pdf'
|
40
40
|
|
41
41
|
# NOTE require_library doesn't support require_relative and we don't modify the load path for this gem
|
42
|
-
CodeRayRequirePath = ::File.join
|
43
|
-
RougeRequirePath = ::File.join
|
42
|
+
CodeRayRequirePath = ::File.join __dir__, 'prawn_ext/coderay_encoder'
|
43
|
+
RougeRequirePath = ::File.join __dir__, 'rouge_ext'
|
44
44
|
|
45
45
|
AsciidoctorVersion = ::Gem::Version.create ::Asciidoctor::VERSION
|
46
46
|
AdmonitionIcons = {
|
@@ -101,6 +101,7 @@ class Converter < ::Prawn::Document
|
|
101
101
|
UriSchemeBoundaryRx = /(?<=:\/\/)/
|
102
102
|
LineScanRx = /\n|.+/
|
103
103
|
BlankLineRx = /\n{2,}/
|
104
|
+
CjkLineBreakRx = /(?=[\u3000\u30a0-\u30ff\u3040-\u309f\p{Han}\uff00-\uffef])/
|
104
105
|
WhitespaceChars = ' ' + TAB + LF
|
105
106
|
SourceHighlighters = ['coderay', 'pygments', 'rouge'].to_set
|
106
107
|
PygmentsBgColorRx = /^\.highlight +{ *background: *#([^;]+);/
|
@@ -121,7 +122,6 @@ class Converter < ::Prawn::Document
|
|
121
122
|
doc.attributes['data-uri'] = ((doc.instance_variable_get :@attribute_overrides) || {})['data-uri'] = ''
|
122
123
|
end
|
123
124
|
@capabilities = {
|
124
|
-
expands_tabs: (::Asciidoctor::VERSION.start_with? '1.5.3.') || AsciidoctorVersion >= (::Gem::Version.create '1.5.3'),
|
125
125
|
special_sectnums: AsciidoctorVersion >= (::Gem::Version.create '1.5.7'),
|
126
126
|
syntax_highlighter: AsciidoctorVersion >= (::Gem::Version.create '2.0.0'),
|
127
127
|
}
|
@@ -196,12 +196,12 @@ class Converter < ::Prawn::Document
|
|
196
196
|
theme_font :heading, level: 1 do
|
197
197
|
layout_heading doc.doctitle, align: (@theme.heading_h1_align || :center).to_sym, level: 1
|
198
198
|
end
|
199
|
-
toc_start = @y
|
200
199
|
end
|
200
|
+
toc_start = @y
|
201
201
|
end
|
202
202
|
|
203
203
|
# NOTE font must be set before toc dry run to ensure dry run size is accurate
|
204
|
-
font @theme.base_font_family, size: @
|
204
|
+
font @theme.base_font_family, size: @root_font_size, style: (@theme.base_font_style || :normal).to_sym
|
205
205
|
|
206
206
|
num_toc_levels = (doc.attr 'toclevels', 2).to_i
|
207
207
|
if (insert_toc = (doc.attr? 'toc') && doc.sections?)
|
@@ -222,9 +222,7 @@ class Converter < ::Prawn::Document
|
|
222
222
|
end
|
223
223
|
end
|
224
224
|
|
225
|
-
|
226
|
-
#start_new_page if @ppbook && verso_page?
|
227
|
-
start_new_page if @media == 'prepress' && verso_page?
|
225
|
+
start_new_page if @ppbook && verso_page?
|
228
226
|
|
229
227
|
if insert_title_page
|
230
228
|
body_offset = (body_start_page_number = page_number) - 1
|
@@ -278,9 +276,16 @@ class Converter < ::Prawn::Document
|
|
278
276
|
end
|
279
277
|
|
280
278
|
add_outline doc, (doc.attr 'outlinelevels', num_toc_levels).to_i, toc_page_nums, num_front_matter_pages[1]
|
281
|
-
|
282
|
-
|
283
|
-
|
279
|
+
if state.pages.size > 0 && (initial_zoom = @theme.page_initial_zoom)
|
280
|
+
case initial_zoom.to_sym
|
281
|
+
when :Fit
|
282
|
+
catalog.data[:OpenAction] = dest_fit state.pages[0]
|
283
|
+
when :FitV
|
284
|
+
catalog.data[:OpenAction] = dest_fit_vertically 0, state.pages[0]
|
285
|
+
when :FitH
|
286
|
+
catalog.data[:OpenAction] = dest_fit_horizontally page_height, state.pages[0]
|
287
|
+
end
|
288
|
+
end
|
284
289
|
catalog.data[:ViewerPreferences] = { DisplayDocTitle: true }
|
285
290
|
|
286
291
|
layout_cover_page doc, :back
|
@@ -294,11 +299,10 @@ class Converter < ::Prawn::Document
|
|
294
299
|
|
295
300
|
# TODO only allow method to be called once (or we need a reset)
|
296
301
|
def init_pdf doc
|
297
|
-
@allow_uri_read = doc.attr? 'allow-uri-read'
|
298
302
|
pdf_opts = build_pdf_options doc, (theme = load_theme doc)
|
299
303
|
# QUESTION should page options be preserved? (otherwise, not readily available)
|
300
304
|
#@page_opts = { size: pdf_opts[:page_size], layout: pdf_opts[:page_layout] }
|
301
|
-
::Prawn::Document.instance_method
|
305
|
+
((::Prawn::Document.instance_method :initialize).bind self).call pdf_opts
|
302
306
|
renderer.min_version PDFVersions[doc.attr 'pdf-version']
|
303
307
|
@page_margin_by_side = { recto: page_margin, verso: page_margin }
|
304
308
|
if (@media = doc.attr 'media', 'screen') == 'prepress'
|
@@ -313,12 +317,13 @@ class Converter < ::Prawn::Document
|
|
313
317
|
# NOTE prepare scratch document to use page margin from recto side (which has same width as verso side)
|
314
318
|
set_page_margin page_margin_recto unless page_margin_recto == page_margin
|
315
319
|
else
|
316
|
-
@ppbook =
|
320
|
+
@ppbook = nil
|
317
321
|
end
|
318
322
|
# QUESTION should ThemeLoader handle registering fonts instead?
|
319
|
-
register_fonts theme.font_catalog, (doc.attr '
|
323
|
+
register_fonts theme.font_catalog, (doc.attr 'pdf-fontsdir', ThemeLoader::FontsDir)
|
320
324
|
default_kerning theme.base_font_kerning != 'none'
|
321
325
|
@fallback_fonts = [*theme.font_fallbacks]
|
326
|
+
@allow_uri_read = doc.attr? 'allow-uri-read'
|
322
327
|
if (bg_image = resolve_background_image doc, theme, 'page-background-image') && bg_image[0]
|
323
328
|
@page_bg_image = { verso: bg_image, recto: bg_image }
|
324
329
|
else
|
@@ -331,8 +336,10 @@ class Converter < ::Prawn::Document
|
|
331
336
|
@page_bg_image[:recto] = bg_image[0] && bg_image
|
332
337
|
end
|
333
338
|
@page_bg_color = resolve_theme_color :page_background_color, 'FFFFFF'
|
339
|
+
@root_font_size = theme.base_font_size || 12
|
334
340
|
@font_color = theme.base_font_color || '000000'
|
335
341
|
@base_align = (align = doc.attr 'text-align') && (TextAlignmentNames.include? align) ? align : theme.base_align
|
342
|
+
@cjk_line_breaks = doc.attr? 'scripts', 'cjk'
|
336
343
|
@text_transform = nil
|
337
344
|
@list_numerals = []
|
338
345
|
@list_bullets = []
|
@@ -351,18 +358,17 @@ class Converter < ::Prawn::Document
|
|
351
358
|
def load_theme doc
|
352
359
|
@theme ||= begin
|
353
360
|
if (theme = doc.options[:pdf_theme])
|
354
|
-
@themesdir = theme.__dir__ || (doc.attr 'pdf-themesdir') || (doc.attr 'pdf-stylesdir')
|
361
|
+
@themesdir = ::File.expand_path theme.__dir__ || (doc.attr 'pdf-themesdir') || (doc.attr 'pdf-stylesdir') || ::Dir.pwd
|
355
362
|
elsif (theme_name = (doc.attr 'pdf-theme') || (doc.attr 'pdf-style'))
|
356
|
-
theme = ThemeLoader.load_theme theme_name, (
|
363
|
+
theme = ThemeLoader.load_theme theme_name, (user_themesdir = (doc.attr 'pdf-themesdir') || (doc.attr 'pdf-stylesdir'))
|
357
364
|
@themesdir = theme.__dir__
|
358
365
|
else
|
359
|
-
theme = ThemeLoader.load_theme
|
360
|
-
@themesdir = theme.__dir__
|
366
|
+
@themesdir = (theme = ThemeLoader.load_theme).__dir__
|
361
367
|
end
|
362
368
|
theme
|
363
369
|
rescue
|
364
|
-
if
|
365
|
-
message = %(could not locate or load the pdf theme `#{theme_name}' in #{
|
370
|
+
if user_themesdir
|
371
|
+
message = %(could not locate or load the pdf theme `#{theme_name}' in #{user_themesdir})
|
366
372
|
else
|
367
373
|
message = %(could not locate or load the built-in pdf theme `#{theme_name}')
|
368
374
|
end
|
@@ -457,18 +463,10 @@ class Converter < ::Prawn::Document
|
|
457
463
|
info = {}
|
458
464
|
# FIXME use sanitize: :plain_text once available
|
459
465
|
info[:Title] = sanitize(doc.doctitle use_fallback: true).as_pdf
|
460
|
-
if doc.attr? 'authors'
|
461
|
-
|
462
|
-
|
463
|
-
if doc.attr? '
|
464
|
-
info[:Subject] = (doc.attr 'subject').as_pdf
|
465
|
-
end
|
466
|
-
if doc.attr? 'keywords'
|
467
|
-
info[:Keywords] = (doc.attr 'keywords').as_pdf
|
468
|
-
end
|
469
|
-
if (doc.attr? 'publisher')
|
470
|
-
info[:Producer] = (doc.attr 'publisher').as_pdf
|
471
|
-
end
|
466
|
+
info[:Author] = (doc.attr 'authors').as_pdf if doc.attr? 'authors'
|
467
|
+
info[:Subject] = (doc.attr 'subject').as_pdf if doc.attr? 'subject'
|
468
|
+
info[:Keywords] = (doc.attr 'keywords').as_pdf if doc.attr? 'keywords'
|
469
|
+
info[:Producer] = (doc.attr 'publisher').as_pdf if doc.attr? 'publisher'
|
472
470
|
info[:Creator] = %(Asciidoctor PDF #{::Asciidoctor::PDF::VERSION}, based on Prawn #{::Prawn::VERSION}).as_pdf
|
473
471
|
info[:Producer] ||= (info[:Author] || info[:Creator])
|
474
472
|
unless doc.attr? 'reproducible'
|
@@ -487,14 +485,16 @@ class Converter < ::Prawn::Document
|
|
487
485
|
if @media == 'prepress' && (next_page_margin = @page_margin_by_side[page_side]) != page_margin
|
488
486
|
set_page_margin next_page_margin
|
489
487
|
end
|
488
|
+
if @page_bg_color && @page_bg_color != 'FFFFFF'
|
489
|
+
tare = true
|
490
|
+
fill_absolute_bounds @page_bg_color
|
491
|
+
end
|
490
492
|
# TODO implement as a watermark (on top)
|
491
493
|
if (bg_image = @page_bg_image[page_side])
|
494
|
+
tare = true
|
492
495
|
canvas { image bg_image[0], ({ position: :center, vposition: :center }.merge bg_image[1]) }
|
493
|
-
page.tare_content_stream
|
494
|
-
elsif @page_bg_color && @page_bg_color != 'FFFFFF'
|
495
|
-
fill_absolute_bounds @page_bg_color
|
496
|
-
page.tare_content_stream
|
497
496
|
end
|
497
|
+
page.tare_content_stream if tare
|
498
498
|
end
|
499
499
|
|
500
500
|
def convert_section sect, opts = {}
|
@@ -511,14 +511,24 @@ class Converter < ::Prawn::Document
|
|
511
511
|
if sect.part_or_chapter?
|
512
512
|
if sect.chapter?
|
513
513
|
type = :chapter
|
514
|
-
|
514
|
+
if @theme.heading_chapter_break_before == 'auto'
|
515
|
+
start_new_chapter sect if @theme.heading_part_break_after == 'always' && sect == sect.parent.sections[0]
|
516
|
+
else
|
517
|
+
start_new_chapter sect
|
518
|
+
end
|
515
519
|
else
|
516
520
|
type = :part
|
517
|
-
start_new_part sect
|
521
|
+
start_new_part sect unless @theme.heading_part_break_before == 'auto'
|
518
522
|
end
|
519
|
-
|
520
|
-
|
521
|
-
|
523
|
+
end
|
524
|
+
unless at_page_top?
|
525
|
+
# FIXME this height doesn't account for impact of text transform or inline formatting
|
526
|
+
heading_height =
|
527
|
+
(height_of_typeset_text title, line_height: (@theme[%(heading_h#{hlevel}_line_height)] || @theme.heading_line_height)) +
|
528
|
+
(@theme[%(heading_h#{hlevel}_margin_top)] || @theme.heading_margin_top || 0) +
|
529
|
+
(@theme[%(heading_h#{hlevel}_margin_bottom)] || @theme.heading_margin_bottom || 0)
|
530
|
+
heading_height += (@theme.heading_min_height_after || 0) if sect.blocks?
|
531
|
+
start_new_page unless cursor > heading_height
|
522
532
|
end
|
523
533
|
# QUESTION should we store pdf-page-start, pdf-anchor & pdf-destination in internal map?
|
524
534
|
sect.set_attr 'pdf-page-start', (start_pgnum = page_number)
|
@@ -567,13 +577,11 @@ class Converter < ::Prawn::Document
|
|
567
577
|
def convert_abstract node
|
568
578
|
add_dest_for_block node if node.id
|
569
579
|
pad_box @theme.abstract_padding do
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
end
|
574
|
-
end
|
580
|
+
theme_font :abstract_title do
|
581
|
+
layout_heading node.title, align: (@theme.abstract_title_align || @base_align).to_sym, margin_top: (@theme.heading_margin_top || 0), margin_bottom: (@theme.heading_margin_bottom || 0)
|
582
|
+
end if node.title?
|
575
583
|
theme_font :abstract do
|
576
|
-
prose_opts = { line_height: @theme.abstract_line_height, align: (@theme.abstract_align || @base_align).to_sym }
|
584
|
+
prose_opts = { line_height: @theme.abstract_line_height, align: (initial_alignment = (@theme.abstract_align || @base_align).to_sym) }
|
577
585
|
if (text_indent = @theme.prose_text_indent)
|
578
586
|
prose_opts[:indent_paragraphs] = text_indent
|
579
587
|
end
|
@@ -587,14 +595,21 @@ class Converter < ::Prawn::Document
|
|
587
595
|
# FIXME is playback necessary here?
|
588
596
|
child.document.playback_attributes child.attributes
|
589
597
|
if child.context == :paragraph
|
598
|
+
if (alignment = resolve_alignment_from_role child.roles)
|
599
|
+
prose_opts[:align] = alignment
|
600
|
+
end
|
590
601
|
layout_prose child.content, prose_opts
|
591
602
|
prose_opts.delete :first_line_options
|
603
|
+
prose_opts[:align] = initial_alignment
|
592
604
|
else
|
593
605
|
# FIXME this could do strange things if the wrong kind of content shows up
|
594
606
|
convert_content_for_block child
|
595
607
|
end
|
596
608
|
end
|
597
609
|
elsif node.content_model != :compound && (string = node.content)
|
610
|
+
if (alignment = resolve_alignment_from_role node.roles)
|
611
|
+
prose_opts[:align] = alignment
|
612
|
+
end
|
598
613
|
layout_prose string, prose_opts
|
599
614
|
end
|
600
615
|
end
|
@@ -657,29 +672,30 @@ class Converter < ::Prawn::Document
|
|
657
672
|
if (label_min_width = @theme.admonition_label_min_width)
|
658
673
|
label_min_width = label_min_width.to_f
|
659
674
|
end
|
660
|
-
icons = ((doc = node.document).attr? 'icons') ? (doc.attr 'icons') :
|
675
|
+
icons = ((doc = node.document).attr? 'icons') ? (doc.attr 'icons') : nil
|
661
676
|
if (data_uri_enabled = doc.attr? 'data-uri')
|
662
677
|
doc.remove_attr 'data-uri'
|
663
678
|
end
|
664
679
|
if icons == 'font' && !(node.attr? 'icon', nil, false)
|
665
680
|
icon_data = admonition_icon_data(label_text = type.to_sym)
|
666
|
-
label_width = label_min_width ? label_min_width : (icon_data[:size] * 1.5)
|
681
|
+
label_width = label_min_width ? label_min_width : ((icon_size = icon_data[:size] || 24) * 1.5)
|
667
682
|
# NOTE icon_uri will consider icon attribute on node first, then type
|
668
|
-
|
683
|
+
# QUESTION should we use resolve_image_path here?
|
684
|
+
elsif icons && (icon_path = node.icon_uri type) &&
|
685
|
+
(icon_path = node.normalize_system_path icon_path, nil, nil, target_name: 'admonition icon') &&
|
686
|
+
(::File.readable? icon_path)
|
669
687
|
icons = true
|
670
688
|
# TODO introduce @theme.admonition_image_width? or use size key from admonition_icon_<name>?
|
671
689
|
label_width = label_min_width ? label_min_width : 36.0
|
672
690
|
else
|
673
691
|
if icons
|
674
|
-
icons =
|
675
|
-
logger.warn %(admonition icon
|
692
|
+
icons = nil
|
693
|
+
logger.warn %(admonition icon not found or not readable: #{icon_path}) unless scratch?
|
676
694
|
end
|
677
695
|
label_text = node.caption
|
678
696
|
theme_font :admonition_label do
|
679
697
|
theme_font %(admonition_label_#{type}) do
|
680
|
-
|
681
|
-
label_text = transform_text label_text, transform
|
682
|
-
end
|
698
|
+
label_text = transform_text label_text, @text_transform if @text_transform
|
683
699
|
label_width = rendered_width_of_string label_text
|
684
700
|
label_width = label_min_width if label_min_width && label_min_width > label_width
|
685
701
|
end
|
@@ -715,7 +731,7 @@ class Converter < ::Prawn::Document
|
|
715
731
|
bounding_box [0, cursor], width: label_width, height: box_height do
|
716
732
|
if icons == 'font'
|
717
733
|
# FIXME we're assume icon is a square
|
718
|
-
icon_size = fit_icon_to_bounds
|
734
|
+
icon_size = fit_icon_to_bounds icon_size
|
719
735
|
# NOTE Prawn's vertical center is not reliable, so calculate it manually
|
720
736
|
if label_valign == :center
|
721
737
|
label_valign = :top
|
@@ -731,7 +747,7 @@ class Converter < ::Prawn::Document
|
|
731
747
|
elsif icons
|
732
748
|
if (::Asciidoctor::Image.format icon_path) == 'svg'
|
733
749
|
begin
|
734
|
-
svg_obj = ::Prawn::SVG::Interface.new ::File.read(icon_path), self,
|
750
|
+
svg_obj = ::Prawn::SVG::Interface.new ::File.read(icon_path, mode: 'r:UTF-8'), self,
|
735
751
|
position: label_align,
|
736
752
|
vposition: label_valign,
|
737
753
|
width: label_width,
|
@@ -746,7 +762,7 @@ class Converter < ::Prawn::Document
|
|
746
762
|
end
|
747
763
|
svg_obj.draw
|
748
764
|
rescue
|
749
|
-
logger.warn %(could not embed admonition icon
|
765
|
+
logger.warn %(could not embed admonition icon: #{icon_path}; #{$!.message})
|
750
766
|
end
|
751
767
|
else
|
752
768
|
begin
|
@@ -761,7 +777,7 @@ class Converter < ::Prawn::Document
|
|
761
777
|
embed_image image_obj, image_info, width: icon_width, position: label_align, vposition: label_valign
|
762
778
|
rescue
|
763
779
|
# QUESTION should we show the label in this case?
|
764
|
-
logger.warn %(could not embed admonition icon
|
780
|
+
logger.warn %(could not embed admonition icon: #{icon_path}; #{$!.message})
|
765
781
|
end
|
766
782
|
end
|
767
783
|
else
|
@@ -807,15 +823,60 @@ class Converter < ::Prawn::Document
|
|
807
823
|
def convert_example node
|
808
824
|
add_dest_for_block node if node.id
|
809
825
|
theme_margin :block, :top
|
826
|
+
caption_height = 0
|
827
|
+
dry_run do
|
828
|
+
move_down 1 # hack to force top margin to be applied
|
829
|
+
caption_height = (layout_caption node) - 1
|
830
|
+
end if node.title?
|
810
831
|
keep_together do |box_height = nil|
|
811
832
|
push_scratch node.document if scratch?
|
812
|
-
caption_height = node.title? ? (layout_caption node) : 0
|
813
833
|
if box_height
|
834
|
+
# FIXME due to the calculation error logged in #789, we must advance page even when content is split across pages
|
835
|
+
advance_page if box_height > cursor && !at_page_top?
|
836
|
+
layout_caption node
|
814
837
|
float do
|
815
|
-
|
816
|
-
|
838
|
+
# TODO move the multi-page logic to theme_fill_and_stroke_bounds
|
839
|
+
if (b_width = @theme.example_border_width || 0) > 0 && (b_color = @theme.example_border_color)
|
840
|
+
if b_color == @page_bg_color # let page background cut into example background
|
841
|
+
b_gap_color, b_shift = @page_bg_color, b_width
|
842
|
+
elsif (b_gap_color = @theme.example_background_color) && b_gap_color != b_color
|
843
|
+
b_shift = 0
|
844
|
+
else # let page background cut into border
|
845
|
+
b_gap_color, b_shift = @page_bg_color, 0
|
846
|
+
end
|
847
|
+
else # let page background cut into sidebar background
|
848
|
+
b_width = 0.5 if b_width == 0
|
849
|
+
b_shift, b_gap_color = b_width * 0.5, @page_bg_color
|
850
|
+
end
|
851
|
+
b_radius = (@theme.example_border_radius || 0) + b_width
|
852
|
+
initial_page, remaining_height = true, box_height - caption_height
|
853
|
+
while remaining_height > 0
|
854
|
+
advance_page unless initial_page
|
855
|
+
fragment_height = [(available_height = cursor), remaining_height].min
|
856
|
+
bounding_box [0, available_height], width: bounds.width, height: fragment_height do
|
857
|
+
theme_fill_and_stroke_bounds :example
|
858
|
+
unless b_width == 0
|
859
|
+
indent b_radius, b_radius do
|
860
|
+
move_down b_shift
|
861
|
+
# dashed line to indicate continuation from previous page; swell line to cover background
|
862
|
+
stroke_horizontal_rule b_gap_color, line_width: b_width * 1.2, line_style: :dashed
|
863
|
+
move_up b_shift
|
864
|
+
end unless initial_page
|
865
|
+
if remaining_height > fragment_height
|
866
|
+
move_down fragment_height - b_shift
|
867
|
+
indent b_radius, b_radius do
|
868
|
+
# dashed line to indicate continuation to next page; swell line to cover background
|
869
|
+
stroke_horizontal_rule b_gap_color, line_width: b_width * 1.2, line_style: :dashed
|
870
|
+
end
|
871
|
+
end
|
872
|
+
end
|
873
|
+
end
|
874
|
+
remaining_height -= fragment_height
|
875
|
+
initial_page = false
|
817
876
|
end
|
818
877
|
end
|
878
|
+
else
|
879
|
+
move_down caption_height
|
819
880
|
end
|
820
881
|
pad_box @theme.example_padding do
|
821
882
|
theme_font :example do
|
@@ -856,13 +917,13 @@ class Converter < ::Prawn::Document
|
|
856
917
|
if node.context == :quote
|
857
918
|
convert_content_for_block node
|
858
919
|
else # verse
|
859
|
-
content =
|
920
|
+
content = guard_indentation node.content
|
860
921
|
layout_prose content, normalize: false, align: :left
|
861
922
|
end
|
862
923
|
end
|
863
924
|
if node.attr? 'attribution', nil, false
|
864
925
|
theme_font :blockquote_cite do
|
865
|
-
layout_prose %(#{EmDash} #{[(node.attr 'attribution'), (node.attr 'citetitle', nil, false)].compact
|
926
|
+
layout_prose %(#{EmDash} #{[(node.attr 'attribution'), (node.attr 'citetitle', nil, false)].compact.join ', '}), align: :left, normalize: false
|
866
927
|
end
|
867
928
|
end
|
868
929
|
end
|
@@ -957,12 +1018,10 @@ class Converter < ::Prawn::Document
|
|
957
1018
|
end
|
958
1019
|
end
|
959
1020
|
pad_box @theme.sidebar_padding do
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
964
|
-
end
|
965
|
-
end
|
1021
|
+
theme_font :sidebar_title do
|
1022
|
+
# QUESTION should we allow margins of sidebar title to be customized?
|
1023
|
+
layout_heading node.title, align: (@theme.sidebar_title_align || @base_align).to_sym, margin_top: 0, margin_bottom: (@theme.heading_margin_bottom || 0)
|
1024
|
+
end if node.title?
|
966
1025
|
theme_font :sidebar do
|
967
1026
|
convert_content_for_block node
|
968
1027
|
end
|
@@ -989,10 +1048,9 @@ class Converter < ::Prawn::Document
|
|
989
1048
|
@list_numerals ||= []
|
990
1049
|
@list_numerals << 1
|
991
1050
|
#stroke_horizontal_rule @theme.caption_border_bottom_color
|
992
|
-
line_metrics = calc_line_metrics @theme.base_line_height
|
993
|
-
node.items.
|
994
|
-
|
995
|
-
advance_page if cursor < (line_metrics.height + line_metrics.leading + line_metrics.padding_top) + 1
|
1051
|
+
line_metrics = theme_font :conum do calc_line_metrics @theme.base_line_height end
|
1052
|
+
node.items.each do |item|
|
1053
|
+
allocate_space_for_list_item line_metrics
|
996
1054
|
convert_colist_item item
|
997
1055
|
end
|
998
1056
|
@list_numerals.pop
|
@@ -1033,11 +1091,11 @@ class Converter < ::Prawn::Document
|
|
1033
1091
|
# and advance to the next page if so (similar to logic for section titles)
|
1034
1092
|
layout_caption node.title if node.title?
|
1035
1093
|
|
1094
|
+
line_metrics = calc_line_metrics @theme.base_line_height
|
1036
1095
|
node.items.each do |terms, desc|
|
1037
1096
|
terms = [*terms]
|
1038
|
-
# NOTE don't orphan the terms
|
1039
|
-
|
1040
|
-
advance_page if cursor < (@theme.base_line_height_length || (font_size * @theme.base_line_height)) * (terms.size + 1)
|
1097
|
+
# NOTE don't orphan the terms (keep together terms and at least one line of content)
|
1098
|
+
allocate_space_for_list_item line_metrics, (terms.size + 1), ((@theme.description_list_term_spacing || 0) + 0.05)
|
1041
1099
|
terms.each do |term|
|
1042
1100
|
# FIXME layout_prose should pass style downward when parsing formatted text
|
1043
1101
|
#layout_prose term.text, style: @theme.description_list_term_font_style.to_sym, margin_top: 0, margin_bottom: @theme.description_list_term_spacing, align: :left
|
@@ -1167,8 +1225,7 @@ class Converter < ::Prawn::Document
|
|
1167
1225
|
end
|
1168
1226
|
indent list_indent do
|
1169
1227
|
node.items.each do |item|
|
1170
|
-
|
1171
|
-
advance_page if cursor < (line_metrics.height + line_metrics.leading + line_metrics.padding_top)
|
1228
|
+
allocate_space_for_list_item line_metrics
|
1172
1229
|
convert_outline_list_item item, node, opts
|
1173
1230
|
end
|
1174
1231
|
end
|
@@ -1267,11 +1324,19 @@ class Converter < ::Prawn::Document
|
|
1267
1324
|
convert_content_for_block desc
|
1268
1325
|
end
|
1269
1326
|
else
|
1270
|
-
|
1327
|
+
if (primary_text = node.text).nil_or_empty?
|
1328
|
+
layout_prose DummyText, opts unless node.blocks?
|
1329
|
+
else
|
1330
|
+
layout_prose primary_text, opts
|
1331
|
+
end
|
1271
1332
|
convert_content_for_block node
|
1272
1333
|
end
|
1273
1334
|
end
|
1274
1335
|
|
1336
|
+
def allocate_space_for_list_item line_metrics, number = 1, additional_gap = 0
|
1337
|
+
advance_page if !at_page_top? && cursor < (line_metrics.height + line_metrics.leading + line_metrics.padding_top + additional_gap) * number
|
1338
|
+
end
|
1339
|
+
|
1275
1340
|
def convert_image node, opts = {}
|
1276
1341
|
node.extend ::Asciidoctor::Image unless ::Asciidoctor::Image === node
|
1277
1342
|
target, image_format = node.target_and_format
|
@@ -1282,15 +1347,17 @@ class Converter < ::Prawn::Document
|
|
1282
1347
|
elsif ::Base64 === target
|
1283
1348
|
image_path = target
|
1284
1349
|
elsif (image_path = resolve_image_path node, target, (opts.fetch :relative_to_imagesdir, true), image_format)
|
1285
|
-
if
|
1286
|
-
|
1287
|
-
|
1288
|
-
|
1289
|
-
|
1290
|
-
|
1291
|
-
|
1350
|
+
if image_format == 'pdf'
|
1351
|
+
if ::File.readable? image_path
|
1352
|
+
# NOTE import_page automatically advances to next page afterwards
|
1353
|
+
# QUESTION should we add destination to top of imported page?
|
1354
|
+
import_page image_path, page: [(node.attr 'page').to_i, 1].max, replace: page.empty?
|
1355
|
+
else
|
1356
|
+
# QUESTION should we use alt text in this case?
|
1357
|
+
logger.warn %(pdf to insert not found or not readable: #{image_path})
|
1358
|
+
end
|
1292
1359
|
return
|
1293
|
-
|
1360
|
+
elsif !(::File.readable? image_path)
|
1294
1361
|
logger.warn %(image to embed not found or not readable: #{image_path}) unless scratch?
|
1295
1362
|
image_path = nil
|
1296
1363
|
end
|
@@ -1306,9 +1373,9 @@ class Converter < ::Prawn::Document
|
|
1306
1373
|
# TODO move this calculation into a method, such as layout_caption node, side: :bottom, dry_run: true
|
1307
1374
|
caption_h = 0
|
1308
1375
|
dry_run do
|
1309
|
-
move_down
|
1376
|
+
move_down 1 # hack to force top margin to be applied
|
1310
1377
|
# NOTE we assume caption fits on a single page, which seems reasonable
|
1311
|
-
caption_h = layout_caption node, side: :bottom
|
1378
|
+
caption_h = (layout_caption node, side: :bottom) - 1
|
1312
1379
|
end if node.title?
|
1313
1380
|
|
1314
1381
|
# TODO support cover (aka canvas) image layout using "canvas" (or "cover") role
|
@@ -1316,7 +1383,7 @@ class Converter < ::Prawn::Document
|
|
1316
1383
|
# TODO add `to_pt page_width` method to ViewportWidth type
|
1317
1384
|
width = (width.to_f / 100) * page_width if ViewportWidth === width
|
1318
1385
|
|
1319
|
-
alignment = ((node.attr 'align', nil, false) || @theme.image_align).to_sym
|
1386
|
+
alignment = ((node.attr 'align', nil, false) || @theme.image_align || :left).to_sym
|
1320
1387
|
align_to_page = node.option? 'align-to-page'
|
1321
1388
|
|
1322
1389
|
begin
|
@@ -1326,7 +1393,7 @@ class Converter < ::Prawn::Document
|
|
1326
1393
|
svg_data = ::Base64.decode64 image_path
|
1327
1394
|
file_request_root = false
|
1328
1395
|
else
|
1329
|
-
svg_data = ::File.read image_path
|
1396
|
+
svg_data = ::File.read image_path, mode: 'r:UTF-8'
|
1330
1397
|
file_request_root = ::File.dirname image_path
|
1331
1398
|
end
|
1332
1399
|
svg_obj = ::Prawn::SVG::Interface.new svg_data, self,
|
@@ -1353,6 +1420,7 @@ class Converter < ::Prawn::Document
|
|
1353
1420
|
end
|
1354
1421
|
end
|
1355
1422
|
image_y = y
|
1423
|
+
image_cursor = cursor
|
1356
1424
|
add_dest_for_block node if node.id
|
1357
1425
|
# NOTE workaround to fix Prawn not adding fill and stroke commands on page that only has an image;
|
1358
1426
|
# breakage occurs when running content (stamps) are added to page
|
@@ -1361,6 +1429,7 @@ class Converter < ::Prawn::Document
|
|
1361
1429
|
# NOTE prawn-svg 0.24.0, 0.25.0, & 0.25.1 didn't restore font after call to draw (see mogest/prawn-svg#80)
|
1362
1430
|
# NOTE cursor advances automatically
|
1363
1431
|
svg_obj.draw
|
1432
|
+
draw_image_border image_cursor, rendered_w, rendered_h, alignment unless node.role? 'noborder'
|
1364
1433
|
if (link = node.attr 'link', nil, false)
|
1365
1434
|
add_link_to_image link, { width: rendered_w, height: rendered_h }, position: alignment, y: image_y
|
1366
1435
|
end
|
@@ -1383,6 +1452,7 @@ class Converter < ::Prawn::Document
|
|
1383
1452
|
end
|
1384
1453
|
end
|
1385
1454
|
image_y = y
|
1455
|
+
image_cursor = cursor
|
1386
1456
|
add_dest_for_block node if node.id
|
1387
1457
|
# NOTE workaround to fix Prawn not adding fill and stroke commands on page that only has an image;
|
1388
1458
|
# breakage occurs when running content (stamps) are added to page
|
@@ -1390,6 +1460,7 @@ class Converter < ::Prawn::Document
|
|
1390
1460
|
update_colors if graphic_state.color_space.empty?
|
1391
1461
|
# NOTE specify both width and height to avoid recalculation
|
1392
1462
|
embed_image image_obj, image_info, width: rendered_w, height: rendered_h, position: alignment
|
1463
|
+
draw_image_border image_cursor, rendered_w, rendered_h, alignment unless node.role? 'noborder'
|
1393
1464
|
if (link = node.attr 'link', nil, false)
|
1394
1465
|
add_link_to_image link, { width: rendered_w, height: rendered_h }, position: alignment, y: image_y
|
1395
1466
|
end
|
@@ -1406,6 +1477,22 @@ class Converter < ::Prawn::Document
|
|
1406
1477
|
unlink_tmp_file image_path if image_path
|
1407
1478
|
end
|
1408
1479
|
|
1480
|
+
def draw_image_border top, w, h, alignment
|
1481
|
+
if (b_width = @theme.image_border_width || 0) > 0 && @theme.image_border_color
|
1482
|
+
if (@theme.image_border_fit || 'content') == 'auto'
|
1483
|
+
bb_width = bounds.width
|
1484
|
+
elsif alignment == :center
|
1485
|
+
bb_x = (bounds.width - w) * 0.5
|
1486
|
+
elsif alignment == :right
|
1487
|
+
bb_x = bounds.width - w
|
1488
|
+
end
|
1489
|
+
bounding_box [(bb_x || 0), top], width: (bb_width || w), height: h, position: alignment do
|
1490
|
+
theme_fill_and_stroke_bounds :image, background_color: 'transparent'
|
1491
|
+
end
|
1492
|
+
true
|
1493
|
+
end
|
1494
|
+
end
|
1495
|
+
|
1409
1496
|
def on_image_error reason, node, target, opts = {}
|
1410
1497
|
logger.warn opts[:message] if opts.key? :message
|
1411
1498
|
alt_text = (link = node.attr 'link', nil, false) ?
|
@@ -1482,7 +1569,7 @@ class Converter < ::Prawn::Document
|
|
1482
1569
|
# HACK disable built-in syntax highlighter; must be done before calling node.content!
|
1483
1570
|
if node.style == 'source' && node.attributes['language'] &&
|
1484
1571
|
(highlighter = node.document.attributes['source-highlighter']) && (SourceHighlighters.include? highlighter) &&
|
1485
|
-
(@capabilities[:syntax_highlighter] ? node.document.syntax_highlighter.highlight? : true)
|
1572
|
+
(@capabilities[:syntax_highlighter] ? (syntax_hl = node.document.syntax_highlighter) && syntax_hl.highlight? : true)
|
1486
1573
|
case highlighter
|
1487
1574
|
when 'coderay'
|
1488
1575
|
unless defined? ::Asciidoctor::Prawn::CodeRayEncoder
|
@@ -1509,7 +1596,7 @@ class Converter < ::Prawn::Document
|
|
1509
1596
|
else
|
1510
1597
|
prev_subs = nil
|
1511
1598
|
end
|
1512
|
-
source_string =
|
1599
|
+
source_string = guard_indentation node.content
|
1513
1600
|
else
|
1514
1601
|
# NOTE the source highlighter logic below handles the callouts and highlight subs
|
1515
1602
|
if highlight_idx
|
@@ -1518,12 +1605,12 @@ class Converter < ::Prawn::Document
|
|
1518
1605
|
subs.delete_all :specialcharacters, :callouts
|
1519
1606
|
end
|
1520
1607
|
# the indent guard will be added by the source highlighter logic
|
1521
|
-
source_string =
|
1608
|
+
source_string = node.content || ''
|
1522
1609
|
end
|
1523
1610
|
else
|
1524
1611
|
highlighter = nil
|
1525
1612
|
prev_subs = nil
|
1526
|
-
source_string =
|
1613
|
+
source_string = guard_indentation node.content
|
1527
1614
|
end
|
1528
1615
|
|
1529
1616
|
bg_color_override = nil
|
@@ -1551,7 +1638,7 @@ class Converter < ::Prawn::Document
|
|
1551
1638
|
# TODO enable once we support background color on spans
|
1552
1639
|
#if node.attr? 'highlight', nil, false
|
1553
1640
|
# unless (hl_lines = node.resolve_lines_to_highlight(node.attr 'highlight', nil, false)).empty?
|
1554
|
-
# pygments_config[:hl_lines] = hl_lines
|
1641
|
+
# pygments_config[:hl_lines] = hl_lines.join ' '
|
1555
1642
|
# end
|
1556
1643
|
#end
|
1557
1644
|
# QUESTION should we treat white background as inherit?
|
@@ -1576,7 +1663,7 @@ class Converter < ::Prawn::Document
|
|
1576
1663
|
end
|
1577
1664
|
fragments = text_formatter.format result
|
1578
1665
|
fragments = restore_conums fragments, conum_mapping, num_trailing_spaces, linenums if conum_mapping
|
1579
|
-
fragments =
|
1666
|
+
fragments = guard_indentation_in_fragments fragments
|
1580
1667
|
when 'rouge'
|
1581
1668
|
if (srclang = node.attr 'language', nil, false)
|
1582
1669
|
if srclang.include? '?'
|
@@ -1685,7 +1772,7 @@ class Converter < ::Prawn::Document
|
|
1685
1772
|
else
|
1686
1773
|
line
|
1687
1774
|
end
|
1688
|
-
}
|
1775
|
+
}.join LF
|
1689
1776
|
conum_mapping = nil if conum_mapping.empty?
|
1690
1777
|
[string, conum_mapping]
|
1691
1778
|
end
|
@@ -1720,10 +1807,11 @@ class Converter < ::Prawn::Document
|
|
1720
1807
|
# append conums to appropriate lines, then flatten to an array of fragments
|
1721
1808
|
lines.flat_map.with_index do |line, cur_line_num|
|
1722
1809
|
last_line = cur_line_num == last_line_num
|
1723
|
-
|
1810
|
+
# NOTE use ::String.new to ensure string is not frozen
|
1811
|
+
line.unshift text: (::String.new %(#{(cur_line_num + linenums).to_s.rjust pad_size} )), color: linenum_color if linenums
|
1724
1812
|
if (conums = conum_mapping.delete cur_line_num)
|
1725
1813
|
line << { text: ' ' * num_trailing_spaces } if last_line && num_trailing_spaces > 0
|
1726
|
-
conum_text = conums.map {|num| conum_glyph num }
|
1814
|
+
conum_text = conums.map {|num| conum_glyph num }.join ' '
|
1727
1815
|
line << (conum_color ? { text: conum_text, color: conum_color } : { text: conum_text })
|
1728
1816
|
end
|
1729
1817
|
line << { text: LF } unless last_line
|
@@ -1735,18 +1823,6 @@ class Converter < ::Prawn::Document
|
|
1735
1823
|
@conum_glyphs[number - 1]
|
1736
1824
|
end
|
1737
1825
|
|
1738
|
-
# Adds guards to preserve indentation
|
1739
|
-
def guard_indentation fragments
|
1740
|
-
start_of_line = true
|
1741
|
-
fragments.each do |fragment|
|
1742
|
-
next if (text = fragment[:text]).empty?
|
1743
|
-
text[0] = GuardedIndent if start_of_line && (text.start_with? ' ')
|
1744
|
-
text.gsub! InnerIndent, GuardedInnerIndent if text.include? InnerIndent
|
1745
|
-
start_of_line = text.end_with? LF
|
1746
|
-
end
|
1747
|
-
fragments
|
1748
|
-
end
|
1749
|
-
|
1750
1826
|
def convert_table node
|
1751
1827
|
add_dest_for_block node if node.id
|
1752
1828
|
# TODO we could skip a lot of the logic below when num_rows == 0
|
@@ -1780,7 +1856,7 @@ class Converter < ::Prawn::Document
|
|
1780
1856
|
row_data = []
|
1781
1857
|
row.each do |cell|
|
1782
1858
|
row_data << {
|
1783
|
-
content: (head_transform ? (transform_text cell.text, head_transform) : cell.text),
|
1859
|
+
content: (head_transform ? (transform_text cell.text.strip, head_transform) : cell.text.strip),
|
1784
1860
|
inline_format: [normalize: true],
|
1785
1861
|
background_color: head_bg_color,
|
1786
1862
|
text_color: (theme.table_head_font_color || theme.table_font_color || @font_color),
|
@@ -1853,7 +1929,7 @@ class Converter < ::Prawn::Document
|
|
1853
1929
|
cell_line_metrics = calc_line_metrics theme.base_line_height
|
1854
1930
|
when :literal
|
1855
1931
|
# FIXME core should not substitute in this case
|
1856
|
-
cell_data[:content] =
|
1932
|
+
cell_data[:content] = guard_indentation cell.instance_variable_get :@text
|
1857
1933
|
# NOTE the absence of the inline_format option implies it's disabled
|
1858
1934
|
# QUESTION should we use literal_font_*, code_font_*, or introduce another category?
|
1859
1935
|
cell_data[:font] = theme.code_font_family
|
@@ -1865,7 +1941,7 @@ class Converter < ::Prawn::Document
|
|
1865
1941
|
end
|
1866
1942
|
cell_line_metrics = calc_line_metrics theme.code_line_height
|
1867
1943
|
when :verse
|
1868
|
-
cell_data[:content] =
|
1944
|
+
cell_data[:content] = guard_indentation cell.text
|
1869
1945
|
cell_data[:inline_format] = true
|
1870
1946
|
cell_line_metrics = calc_line_metrics theme.base_line_height
|
1871
1947
|
when :asciidoc
|
@@ -1889,7 +1965,9 @@ class Converter < ::Prawn::Document
|
|
1889
1965
|
#cell_data[:final_gap] = cell_line_metrics.final_gap
|
1890
1966
|
end
|
1891
1967
|
unless cell_data.key? :content
|
1892
|
-
|
1968
|
+
cell_text = cell.text.strip
|
1969
|
+
cell_text = transform_text cell_text if cell_transform
|
1970
|
+
if cell_text.include? LF
|
1893
1971
|
# NOTE effectively the same as calling cell.content (should we use that instead?)
|
1894
1972
|
# FIXME hard breaks not quite the same result as separate paragraphs; need custom cell impl here
|
1895
1973
|
cell_data[:content] = (cell_text.split BlankLineRx).map {|l| l.tr_s WhitespaceChars, ' ' }.join DoubleLF
|
@@ -1899,6 +1977,7 @@ class Converter < ::Prawn::Document
|
|
1899
1977
|
cell_data[:inline_format] = [normalize: true]
|
1900
1978
|
end
|
1901
1979
|
end
|
1980
|
+
cell_data[:background_color] = (node.document.attr 'cellbgcolor')[1..-1] if node.document.attr? 'cellbgcolor'
|
1902
1981
|
row_data << cell_data
|
1903
1982
|
end
|
1904
1983
|
table_data << row_data
|
@@ -1952,7 +2031,7 @@ class Converter < ::Prawn::Document
|
|
1952
2031
|
|
1953
2032
|
if node.option? 'autowidth'
|
1954
2033
|
table_width = (node.attr? 'width', nil, false) ? bounds.width * ((node.attr 'tablepcwidth') / 100.0) :
|
1955
|
-
((node.has_role? 'spread') ? bounds.width : nil)
|
2034
|
+
(((node.has_role? 'stretch') || (node.has_role? 'spread')) ? bounds.width : nil)
|
1956
2035
|
column_widths = []
|
1957
2036
|
else
|
1958
2037
|
table_width = bounds.width * ((node.attr 'tablepcwidth') / 100.0)
|
@@ -2156,7 +2235,7 @@ class Converter < ::Prawn::Document
|
|
2156
2235
|
else
|
2157
2236
|
pagenums = consolidate_ranges term.dests.uniq {|dest| dest[:page] }.map {|dest| dest[:page].to_s }
|
2158
2237
|
end
|
2159
|
-
text = %(#{text}, #{pagenums
|
2238
|
+
text = %(#{text}, #{pagenums.join ', '})
|
2160
2239
|
end
|
2161
2240
|
layout_prose text, align: :left, margin: 0
|
2162
2241
|
|
@@ -2264,18 +2343,6 @@ class Converter < ::Prawn::Document
|
|
2264
2343
|
icon_set = node.attr 'set', (node.document.attr 'icon-set', 'fa'), false
|
2265
2344
|
end
|
2266
2345
|
icon_set = 'fa' unless IconSets.include? icon_set
|
2267
|
-
if node.attr? 'size', nil, false
|
2268
|
-
case (size = node.attr 'size')
|
2269
|
-
when 'lg'
|
2270
|
-
size_attr = %( size="1.333em")
|
2271
|
-
when 'fw'
|
2272
|
-
size_attr = %( width="1em" align="center")
|
2273
|
-
else
|
2274
|
-
size_attr = %( size="#{size.sub 'x', 'em'}")
|
2275
|
-
end
|
2276
|
-
else
|
2277
|
-
size_attr = ''
|
2278
|
-
end
|
2279
2346
|
if icon_set == 'fa'
|
2280
2347
|
# legacy name from Font Awesome < 5
|
2281
2348
|
if (remapped_icon_name = resolve_legacy_icon_name icon_name)
|
@@ -2296,8 +2363,21 @@ class Converter < ::Prawn::Document
|
|
2296
2363
|
glyph = (icon_font_data icon_set).unicode icon_name rescue nil
|
2297
2364
|
end
|
2298
2365
|
if glyph
|
2366
|
+
if node.attr? 'size', nil, false
|
2367
|
+
case (size = node.attr 'size')
|
2368
|
+
when 'lg'
|
2369
|
+
size_attr = %( size="1.333em")
|
2370
|
+
when 'fw'
|
2371
|
+
size_attr = %( width="1em" align="center")
|
2372
|
+
else
|
2373
|
+
size_attr = %( size="#{size.sub 'x', 'em'}")
|
2374
|
+
end
|
2375
|
+
else
|
2376
|
+
size_attr = ''
|
2377
|
+
end
|
2378
|
+
class_attr = node.role? ? %( class="#{node.role}") : ''
|
2299
2379
|
# TODO support rotate and flip attributes
|
2300
|
-
%(<font name="#{icon_set}"#{size_attr}>#{glyph}</font>)
|
2380
|
+
%(<font name="#{icon_set}"#{size_attr}#{class_attr}>#{glyph}</font>)
|
2301
2381
|
else
|
2302
2382
|
logger.warn %(#{icon_name} is not a valid icon name in the #{icon_set} icon set)
|
2303
2383
|
%([#{node.attr 'alt'}])
|
@@ -2365,7 +2445,7 @@ class Converter < ::Prawn::Document
|
|
2365
2445
|
menu = node.attr 'menu'
|
2366
2446
|
caret = (load_theme node.document).menu_caret_content || %( \u203a )
|
2367
2447
|
if !(submenus = node.attr 'submenus').empty?
|
2368
|
-
%(<strong>#{[menu, *submenus, (node.attr 'menuitem')]
|
2448
|
+
%(<strong>#{[menu, *submenus, (node.attr 'menuitem')].join caret}</strong>)
|
2369
2449
|
elsif (menuitem = node.attr 'menuitem')
|
2370
2450
|
%(<strong>#{menu}#{caret}#{menuitem}</strong>)
|
2371
2451
|
else
|
@@ -2389,6 +2469,8 @@ class Converter < ::Prawn::Document
|
|
2389
2469
|
open, close, is_tag = [?\u201c, ?\u201d, false]
|
2390
2470
|
when :single
|
2391
2471
|
open, close, is_tag = [?\u2018, ?\u2019, false]
|
2472
|
+
when :mark
|
2473
|
+
open, close, is_tag = ['<mark>', '</mark>', true]
|
2392
2474
|
#when :asciimath, :latexmath
|
2393
2475
|
else
|
2394
2476
|
open, close, is_tag = [nil, nil, false]
|
@@ -2413,8 +2495,8 @@ class Converter < ::Prawn::Document
|
|
2413
2495
|
|
2414
2496
|
# NOTE a new page may have already been started at this point, so decide what to do with it
|
2415
2497
|
if page.empty?
|
2416
|
-
page.reset_content if (recycle = @ppbook ?
|
2417
|
-
elsif @ppbook &&
|
2498
|
+
page.reset_content if (recycle = @ppbook ? recto_page? : true)
|
2499
|
+
elsif @ppbook && page_number > 0 && recto_page?
|
2418
2500
|
start_new_page
|
2419
2501
|
end
|
2420
2502
|
|
@@ -2432,23 +2514,22 @@ class Converter < ::Prawn::Document
|
|
2432
2514
|
@page_bg_color = prev_bg_color if bg_color
|
2433
2515
|
|
2434
2516
|
# IMPORTANT this is the first page created, so we need to set the base font
|
2435
|
-
font @theme.base_font_family, size: @
|
2517
|
+
font @theme.base_font_family, size: @root_font_size
|
2436
2518
|
|
2437
2519
|
# QUESTION allow alignment per element on title page?
|
2438
2520
|
title_align = (@theme.title_page_align || @base_align).to_sym
|
2439
2521
|
|
2440
2522
|
# TODO disallow .pdf as image type
|
2441
|
-
if (logo_image_path = (doc.attr 'title-logo-image'
|
2523
|
+
if (logo_image_path = (doc.attr 'title-logo-image') || (logo_image_from_theme = @theme.title_page_logo_image))
|
2442
2524
|
if (logo_image_path.include? ':') && logo_image_path =~ ImageAttributeValueRx
|
2443
|
-
logo_image_path = $1
|
2444
2525
|
logo_image_attrs = (AttributeList.new $2).parse ['alt', 'width', 'height']
|
2445
2526
|
relative_to_imagesdir = true
|
2527
|
+
logo_image_path = logo_image_from_theme ? (ThemeLoader.resolve_theme_asset (sub_attributes_discretely doc, $1), @themesdir) : $1
|
2446
2528
|
else
|
2447
2529
|
logo_image_attrs = {}
|
2448
2530
|
relative_to_imagesdir = false
|
2531
|
+
logo_image_path = ThemeLoader.resolve_theme_asset (sub_attributes_discretely doc, logo_image_path), @themesdir if logo_image_from_theme
|
2449
2532
|
end
|
2450
|
-
# HACK quick fix to resolve image path relative to theme
|
2451
|
-
logo_image_path = ThemeLoader.resolve_theme_asset logo_image_path, @themesdir unless doc.attr? 'title-logo-image'
|
2452
2533
|
logo_image_attrs['target'] = logo_image_path
|
2453
2534
|
logo_image_attrs['align'] ||= (@theme.title_page_logo_align || title_align.to_s)
|
2454
2535
|
# QUESTION should we allow theme to turn logo image off?
|
@@ -2509,7 +2590,7 @@ class Converter < ::Prawn::Document
|
|
2509
2590
|
# TODO provide an API in core to get authors as an array
|
2510
2591
|
authors = (1..(doc.attr 'authorcount', 1).to_i).map {|idx|
|
2511
2592
|
doc.attr(idx == 1 ? 'author' : %(author_#{idx}))
|
2512
|
-
}
|
2593
|
+
}.join (@theme.title_page_authors_delimiter || ', ')
|
2513
2594
|
theme_font :title_page_authors do
|
2514
2595
|
layout_prose authors,
|
2515
2596
|
align: title_align,
|
@@ -2522,7 +2603,10 @@ class Converter < ::Prawn::Document
|
|
2522
2603
|
revision_info = [(doc.attr? 'revnumber') ? %(#{doc.attr 'version-label'} #{doc.attr 'revnumber'}) : nil, (doc.attr 'revdate')].compact
|
2523
2604
|
unless revision_info.empty?
|
2524
2605
|
move_down(@theme.title_page_revision_margin_top || 0)
|
2525
|
-
revision_text = revision_info
|
2606
|
+
revision_text = revision_info.join (@theme.title_page_revision_delimiter || ', ')
|
2607
|
+
if (revremark = doc.attr 'revremark')
|
2608
|
+
revision_text = %(#{revision_text}: #{revremark})
|
2609
|
+
end
|
2526
2610
|
indent (@theme.title_page_revision_margin_left || 0), (@theme.title_page_revision_margin_right || 0) do
|
2527
2611
|
theme_font :title_page_revision do
|
2528
2612
|
layout_prose revision_text,
|
@@ -2555,7 +2639,7 @@ class Converter < ::Prawn::Document
|
|
2555
2639
|
|
2556
2640
|
go_to_page page_count if face == :back
|
2557
2641
|
if image_path.downcase.end_with? '.pdf'
|
2558
|
-
import_page image_path, advance: face != :back
|
2642
|
+
import_page image_path, page: [((image_attrs || {})['page']).to_i, 1].max, advance: face != :back
|
2559
2643
|
else
|
2560
2644
|
image_opts = resolve_image_options image_path, image_attrs, background: true, format: image_format
|
2561
2645
|
image_page image_path, (image_opts.merge canvas: true)
|
@@ -2578,16 +2662,26 @@ class Converter < ::Prawn::Document
|
|
2578
2662
|
alias start_new_part start_new_chapter
|
2579
2663
|
alias layout_part_title layout_chapter_title
|
2580
2664
|
|
2581
|
-
#
|
2665
|
+
# NOTE layout_heading doesn't set the theme font because it's used for various types of headings
|
2582
2666
|
# QUESTION why doesn't layout_heading accept a node?
|
2583
2667
|
def layout_heading string, opts = {}
|
2584
|
-
|
2585
|
-
|
2668
|
+
hlevel = opts[:level]
|
2669
|
+
unless (top_margin = (margin = (opts.delete :margin)) || (opts.delete :margin_top))
|
2670
|
+
if at_page_top?
|
2671
|
+
if hlevel && (top_margin = @theme[%(heading_h#{hlevel}_margin_page_top)] || @theme.heading_margin_page_top || 0) > 0
|
2672
|
+
move_down top_margin
|
2673
|
+
end
|
2674
|
+
top_margin = 0
|
2675
|
+
else
|
2676
|
+
top_margin = (hlevel ? @theme[%(heading_h#{hlevel}_margin_top)] : nil) || @theme.heading_margin_top
|
2677
|
+
end
|
2678
|
+
end
|
2679
|
+
bot_margin = margin || (opts.delete :margin_bottom) || (hlevel ? @theme[%(heading_h#{hlevel}_margin_bottom)] : nil) || @theme.heading_margin_bottom
|
2586
2680
|
if (transform = resolve_text_transform opts)
|
2587
2681
|
string = transform_text string, transform
|
2588
2682
|
end
|
2589
2683
|
margin_top top_margin
|
2590
|
-
typeset_text string, calc_line_metrics((opts.delete :line_height) || @theme[%(heading_h#{
|
2684
|
+
typeset_text string, calc_line_metrics((opts.delete :line_height) || (hlevel ? @theme[%(heading_h#{hlevel}_line_height)] : nil) || @theme.heading_line_height || @theme.base_line_height), {
|
2591
2685
|
color: @font_color,
|
2592
2686
|
inline_format: true,
|
2593
2687
|
align: @base_align.to_sym
|
@@ -2697,7 +2791,7 @@ class Converter < ::Prawn::Document
|
|
2697
2791
|
theme_font :heading, level: 2 do
|
2698
2792
|
theme_font :toc_title do
|
2699
2793
|
toc_title_align = (@theme.toc_title_align || @theme.heading_h2_align || @theme.heading_align || @base_align).to_sym
|
2700
|
-
layout_heading toc_title, align: toc_title_align
|
2794
|
+
layout_heading toc_title, align: toc_title_align, level: 2
|
2701
2795
|
end
|
2702
2796
|
end
|
2703
2797
|
end
|
@@ -2737,7 +2831,7 @@ class Converter < ::Prawn::Document
|
|
2737
2831
|
end
|
2738
2832
|
sections.each do |sect|
|
2739
2833
|
theme_font :toc, level: (sect.level + 1) do
|
2740
|
-
sect_title =
|
2834
|
+
sect_title = @text_transform ? (transform_text sect.numbered_title, @text_transform) : sect.numbered_title
|
2741
2835
|
# NOTE only write section title (excluding dots and page number) if this is a dry run
|
2742
2836
|
if scratch?
|
2743
2837
|
# FIXME use layout_prose
|
@@ -2910,7 +3004,6 @@ class Converter < ::Prawn::Document
|
|
2910
3004
|
doc.set_attr 'page-count', num_pages
|
2911
3005
|
|
2912
3006
|
pagenums_enabled = doc.attr? 'pagenums'
|
2913
|
-
attribute_missing_doc = doc.attr 'attribute-missing'
|
2914
3007
|
case @media == 'prepress' ? 'physical' : (doc.attr 'pdf-folio-placement')
|
2915
3008
|
when 'physical'
|
2916
3009
|
folio_basis, invert_folio = :physical, false
|
@@ -2990,16 +3083,8 @@ class Converter < ::Prawn::Document
|
|
2990
3083
|
if content == '{page-number}'
|
2991
3084
|
content = pagenums_enabled ? pgnum_label.to_s : nil
|
2992
3085
|
else
|
2993
|
-
|
2994
|
-
|
2995
|
-
if (content = doc.apply_subs content).include? '{'
|
2996
|
-
# NOTE must use { in place of {, not \{, to escape attribute reference
|
2997
|
-
content = content.split(LF).delete_if {|line| SimpleAttributeRefRx.match? line } * LF
|
2998
|
-
end
|
2999
|
-
doc.set_attr 'attribute-missing', attribute_missing_doc unless attribute_missing_doc == 'skip'
|
3000
|
-
if (transform = @text_transform) && transform != 'none'
|
3001
|
-
content = transform_text content, @text_transform
|
3002
|
-
end
|
3086
|
+
content = apply_subs_discretely doc, content, drop_lines_with_unresolved_attributes: true
|
3087
|
+
content = transform_text content, @text_transform if @text_transform
|
3003
3088
|
end
|
3004
3089
|
formatted_text_box parse_text(content, color: @font_color, inline_format: [normalize: true]),
|
3005
3090
|
at: [left, bounds.top - trim_styles[:padding][0] - trim_styles[:content_offset] + (trim_styles[:valign] == :center ? font.descender * 0.5 : 0)],
|
@@ -3075,7 +3160,7 @@ class Converter < ::Prawn::Document
|
|
3075
3160
|
trim_styles[:img_valign] = trim_styles[:img_valign].to_sym
|
3076
3161
|
end
|
3077
3162
|
|
3078
|
-
colspec_dict = PageSides.
|
3163
|
+
colspec_dict = PageSides.reduce({}) do |acc, side|
|
3079
3164
|
side_trim_content_width = trim_content_width[side]
|
3080
3165
|
if (custom_colspecs = @theme[%(#{periphery}_#{side}_columns)] || @theme[%(#{periphery}_columns)])
|
3081
3166
|
case (colspecs = (custom_colspecs.to_s.tr ',', ' ').split[0..2]).size
|
@@ -3112,7 +3197,7 @@ class Converter < ::Prawn::Document
|
|
3112
3197
|
acc
|
3113
3198
|
end
|
3114
3199
|
|
3115
|
-
content_dict = PageSides.
|
3200
|
+
content_dict = PageSides.reduce({}) do |acc, side|
|
3116
3201
|
side_content = {}
|
3117
3202
|
ColumnPositions.each do |position|
|
3118
3203
|
unless (val = @theme[%(#{periphery}_#{side}_#{position}_content)]).nil_or_empty?
|
@@ -3231,9 +3316,24 @@ class Converter < ::Prawn::Document
|
|
3231
3316
|
nil
|
3232
3317
|
end
|
3233
3318
|
|
3234
|
-
def register_fonts font_catalog,
|
3235
|
-
|
3236
|
-
|
3319
|
+
def register_fonts font_catalog, fonts_dir
|
3320
|
+
return unless font_catalog
|
3321
|
+
dirs = (fonts_dir.split ::File::PATH_SEPARATOR, -1).map do |dir|
|
3322
|
+
dir.empty? || dir == 'GEM_FONTS_DIR' ? ThemeLoader::FontsDir : dir
|
3323
|
+
end
|
3324
|
+
font_catalog.each do |key, styles|
|
3325
|
+
styles = styles.reduce({}) do |accum, (style, path)|
|
3326
|
+
found = dirs.find do |dir|
|
3327
|
+
resolved_font_path = font_path path, dir
|
3328
|
+
if ::File.readable? resolved_font_path
|
3329
|
+
accum[style.to_sym] = resolved_font_path
|
3330
|
+
true
|
3331
|
+
end
|
3332
|
+
end
|
3333
|
+
raise ::Errno::ENOENT, %(#{path} not found in #{fonts_dir}) unless found
|
3334
|
+
accum
|
3335
|
+
end
|
3336
|
+
register_font key => styles
|
3237
3337
|
end
|
3238
3338
|
end
|
3239
3339
|
|
@@ -3273,25 +3373,28 @@ class Converter < ::Prawn::Document
|
|
3273
3373
|
radius: @theme[%(#{category}_border_radius)]
|
3274
3374
|
end
|
3275
3375
|
|
3276
|
-
# Insert a top margin
|
3277
|
-
# Start a new page if
|
3278
|
-
|
3279
|
-
|
3376
|
+
# Insert a top margin equal to amount if cursor is not at the top of the
|
3377
|
+
# page. Start a new page instead if amount is greater than the remaining
|
3378
|
+
# space on the page.
|
3379
|
+
def margin_top amount
|
3380
|
+
margin amount, :top
|
3280
3381
|
end
|
3281
3382
|
|
3282
|
-
# Insert a bottom margin
|
3283
|
-
# Start a new page if
|
3284
|
-
|
3285
|
-
|
3383
|
+
# Insert a bottom margin equal to amount unless cursor is at the top of the
|
3384
|
+
# page (not likely). Start a new page instead if amount is greater than the
|
3385
|
+
# remaining space on the page.
|
3386
|
+
def margin_bottom amount
|
3387
|
+
margin amount, :bottom
|
3286
3388
|
end
|
3287
3389
|
|
3288
|
-
# Insert a margin
|
3289
|
-
# Start a new page if
|
3290
|
-
|
3291
|
-
|
3390
|
+
# Insert a margin at the specified side if the cursor is not at the top of
|
3391
|
+
# the page. Start a new page if amount is greater than the remaining space on
|
3392
|
+
# the page.
|
3393
|
+
def margin amount, side
|
3394
|
+
unless (amount || 0) == 0 || at_page_top?
|
3292
3395
|
# NOTE use low-level cursor calculation to workaround cursor bug in column_box context
|
3293
|
-
if y - reference_bounds.absolute_bottom >
|
3294
|
-
move_down
|
3396
|
+
if y - reference_bounds.absolute_bottom > amount
|
3397
|
+
move_down amount
|
3295
3398
|
else
|
3296
3399
|
# set cursor at top of next page
|
3297
3400
|
reference_bounds.move_past_bottom
|
@@ -3312,8 +3415,8 @@ class Converter < ::Prawn::Document
|
|
3312
3415
|
# TODO inheriting from generic category should be an option
|
3313
3416
|
if opts.key? :level
|
3314
3417
|
level = opts[:level]
|
3315
|
-
family = @theme[%(#{category}_h#{level}_font_family)] || @theme[%(#{category}_font_family)] || @theme.base_font_family
|
3316
|
-
size = @theme[%(#{category}_h#{level}_font_size)] || @theme[%(#{category}_font_size)] || @
|
3418
|
+
family = @theme[%(#{category}_h#{level}_font_family)] || @theme[%(#{category}_font_family)] || @theme.base_font_family || font_family
|
3419
|
+
size = @theme[%(#{category}_h#{level}_font_size)] || @theme[%(#{category}_font_size)] || @root_font_size
|
3317
3420
|
style = @theme[%(#{category}_h#{level}_font_style)] || @theme[%(#{category}_font_style)]
|
3318
3421
|
color = @theme[%(#{category}_h#{level}_font_color)] || @theme[%(#{category}_font_color)]
|
3319
3422
|
# NOTE global text_transform is not currently supported
|
@@ -3437,6 +3540,7 @@ class Converter < ::Prawn::Document
|
|
3437
3540
|
def typeset_text string, line_metrics, opts = {}
|
3438
3541
|
move_down line_metrics.padding_top
|
3439
3542
|
opts = { leading: line_metrics.leading, final_gap: line_metrics.final_gap }.merge opts
|
3543
|
+
string = string.gsub CjkLineBreakRx, ZeroWidthSpace if @cjk_line_breaks
|
3440
3544
|
if (first_line_opts = opts.delete :first_line_options)
|
3441
3545
|
# TODO good candidate for Prawn enhancement!
|
3442
3546
|
text_with_formatted_first_line string, first_line_opts, opts
|
@@ -3458,62 +3562,27 @@ class Converter < ::Prawn::Document
|
|
3458
3562
|
(height_of string, leading: line_metrics.leading, final_gap: line_metrics.final_gap) + line_metrics.padding_top + line_metrics.padding_bottom
|
3459
3563
|
end
|
3460
3564
|
|
3461
|
-
def
|
3462
|
-
|
3463
|
-
|
3464
|
-
|
3465
|
-
if ((tab_size = tab_size.to_i) < 1 || !@capabilities[:expands_tabs]) && (string.include? TAB)
|
3466
|
-
# Asciidoctor <= 1.5.2 already does tab replacement in some cases, so be consistent about tab size
|
3467
|
-
full_tab_space = ' ' * (tab_size = 4)
|
3468
|
-
result = []
|
3469
|
-
string.each_line do |line|
|
3470
|
-
if line.start_with? TAB
|
3471
|
-
if guard_indent
|
3472
|
-
# NOTE '+' operator is faster than interpolation
|
3473
|
-
line.sub!(TabIndentRx) { GuardedIndent + (full_tab_space * $&.length).chop! }
|
3474
|
-
else
|
3475
|
-
line.sub!(TabIndentRx) { full_tab_space * $&.length }
|
3476
|
-
end
|
3477
|
-
leading_space = false
|
3478
|
-
# QUESTION should we check for LF first?
|
3479
|
-
elsif line == LF
|
3480
|
-
result << line
|
3481
|
-
next
|
3482
|
-
else
|
3483
|
-
leading_space = guard_indent && (line.start_with? ' ')
|
3484
|
-
end
|
3485
|
-
|
3486
|
-
if line.include? TAB
|
3487
|
-
# keep track of how many spaces were added to adjust offset in match data
|
3488
|
-
spaces_added = 0
|
3489
|
-
line.gsub!(TabRx) {
|
3490
|
-
# calculate how many spaces this tab represents, then replace tab with spaces
|
3491
|
-
if (offset = ($~.begin 0) + spaces_added) % tab_size == 0
|
3492
|
-
spaces_added += (tab_size - 1)
|
3493
|
-
full_tab_space
|
3494
|
-
else
|
3495
|
-
unless (spaces = tab_size - offset % tab_size) == 1
|
3496
|
-
spaces_added += (spaces - 1)
|
3497
|
-
end
|
3498
|
-
' ' * spaces
|
3499
|
-
end
|
3500
|
-
}
|
3501
|
-
end
|
3502
|
-
|
3503
|
-
# NOTE we save time by adding indent guard per line while performing tab expansion
|
3504
|
-
line[0] = GuardedIndent if leading_space
|
3505
|
-
result << line
|
3506
|
-
end
|
3507
|
-
result.join
|
3508
|
-
else
|
3509
|
-
if guard_indent
|
3510
|
-
string[0] = GuardedIndent if string.start_with? ' '
|
3511
|
-
string.gsub! InnerIndent, GuardedInnerIndent if string.include? InnerIndent
|
3512
|
-
end
|
3565
|
+
def guard_indentation string
|
3566
|
+
if string
|
3567
|
+
string[0] = GuardedIndent if string.start_with? ' '
|
3568
|
+
string.gsub! InnerIndent, GuardedInnerIndent if string.include? InnerIndent
|
3513
3569
|
string
|
3570
|
+
else
|
3571
|
+
''
|
3514
3572
|
end
|
3515
3573
|
end
|
3516
3574
|
|
3575
|
+
def guard_indentation_in_fragments fragments
|
3576
|
+
start_of_line = true
|
3577
|
+
fragments.each do |fragment|
|
3578
|
+
next if (text = fragment[:text]).empty?
|
3579
|
+
text[0] = GuardedIndent if start_of_line && (text.start_with? ' ')
|
3580
|
+
text.gsub! InnerIndent, GuardedInnerIndent if text.include? InnerIndent
|
3581
|
+
start_of_line = text.end_with? LF
|
3582
|
+
end
|
3583
|
+
fragments
|
3584
|
+
end
|
3585
|
+
|
3517
3586
|
# Derive a PDF-safe, ASCII-only anchor name from the given value.
|
3518
3587
|
# Encodes value into hex if it contains characters outside the ASCII range.
|
3519
3588
|
# If value is nil, derive an anchor name from the default_value, if given.
|
@@ -3619,7 +3688,7 @@ class Converter < ::Prawn::Document
|
|
3619
3688
|
end
|
3620
3689
|
# handle case when image is a local file
|
3621
3690
|
else
|
3622
|
-
|
3691
|
+
node.normalize_system_path image_path, imagesdir, nil, target_name: 'image'
|
3623
3692
|
end
|
3624
3693
|
end
|
3625
3694
|
|
@@ -3634,10 +3703,17 @@ class Converter < ::Prawn::Document
|
|
3634
3703
|
return []
|
3635
3704
|
elsif (image_path.include? ':') && image_path =~ ImageAttributeValueRx
|
3636
3705
|
image_attrs = (AttributeList.new $2).parse ['alt', 'width']
|
3706
|
+
if from_theme
|
3707
|
+
# TODO support remote image when loaded from theme
|
3708
|
+
image_path = ThemeLoader.resolve_theme_asset (sub_attributes_discretely doc, $1), @themesdir
|
3709
|
+
else
|
3710
|
+
image_path = resolve_image_path doc, $1, true, (image_format = image_attrs['format'])
|
3711
|
+
end
|
3712
|
+
elsif from_theme
|
3637
3713
|
# TODO support remote image when loaded from theme
|
3638
|
-
image_path =
|
3714
|
+
image_path = ThemeLoader.resolve_theme_asset (sub_attributes_discretely doc, image_path), @themesdir
|
3639
3715
|
else
|
3640
|
-
image_path =
|
3716
|
+
image_path = resolve_image_path doc, image_path, false
|
3641
3717
|
end
|
3642
3718
|
|
3643
3719
|
return unless image_path
|
@@ -3835,6 +3911,34 @@ class Converter < ::Prawn::Document
|
|
3835
3911
|
logger.warn %(could not delete temporary image: #{path}; #{$!.message})
|
3836
3912
|
end
|
3837
3913
|
|
3914
|
+
def apply_subs_discretely doc, value, opts = {}
|
3915
|
+
imagesdir = doc.attr 'imagesdir'
|
3916
|
+
doc.set_attr 'imagesdir', @themesdir
|
3917
|
+
# FIXME get sub_attributes to handle drop-line w/o a warning
|
3918
|
+
doc.set_attr 'attribute-missing', 'skip' unless (attribute_missing = doc.attr 'attribute-missing') == 'skip'
|
3919
|
+
value = value.gsub '\{', '\\\\\\{' if (escaped_attr_ref = value.include? '\{')
|
3920
|
+
value = doc.apply_subs value
|
3921
|
+
if opts[:drop_lines_with_unresolved_attributes] && (value.include? '{')
|
3922
|
+
value = (value.split LF).delete_if {|line| SimpleAttributeRefRx.match? line }.join LF
|
3923
|
+
end
|
3924
|
+
value = value.gsub '\{', '{' if escaped_attr_ref
|
3925
|
+
doc.set_attr 'attribute-missing', attribute_missing unless attribute_missing == 'skip'
|
3926
|
+
if imagesdir
|
3927
|
+
doc.set_attr 'imagesdir', imagesdir
|
3928
|
+
else
|
3929
|
+
# NOTE remove_attr not defined until Asciidoctor 1.5.6
|
3930
|
+
doc.attributes.delete 'imagesdir'
|
3931
|
+
end
|
3932
|
+
value
|
3933
|
+
end
|
3934
|
+
|
3935
|
+
def sub_attributes_discretely doc, value
|
3936
|
+
doc.set_attr 'attribute-missing', 'skip' unless (attribute_missing = doc.attr 'attribute-missing') == 'skip'
|
3937
|
+
value = doc.apply_subs value
|
3938
|
+
doc.set_attr 'attribute-missing', attribute_missing unless attribute_missing == 'skip'
|
3939
|
+
value
|
3940
|
+
end
|
3941
|
+
|
3838
3942
|
# NOTE assume URL is escaped (i.e., contains character references such as &)
|
3839
3943
|
def breakable_uri uri
|
3840
3944
|
scheme, address = uri.split UriSchemeBoundaryRx, 2
|
@@ -3850,7 +3954,7 @@ class Converter < ::Prawn::Document
|
|
3850
3954
|
def consolidate_ranges nums
|
3851
3955
|
if nums.size > 1
|
3852
3956
|
prev = nil
|
3853
|
-
nums.
|
3957
|
+
nums.reduce([]) {|accum, num|
|
3854
3958
|
if prev && (prev.to_i + 1) == num.to_i
|
3855
3959
|
accum[-1][1] = num
|
3856
3960
|
else
|