asciidoctor-pdf 1.5.0.beta.6 → 1.5.0.beta.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +22 -0
- data/README.adoc +78 -19
- data/asciidoctor-pdf.gemspec +0 -1
- data/bin/asciidoctor-pdf-optimize +20 -0
- data/data/themes/base-theme.yml +2 -0
- data/docs/theming-guide.adoc +127 -22
- data/lib/asciidoctor/pdf/converter.rb +226 -168
- data/lib/asciidoctor/pdf/ext/rouge/formatters/prawn.rb +1 -1
- data/lib/asciidoctor/pdf/formatted_text/parser.rb +0 -1
- data/lib/asciidoctor/pdf/formatted_text/parser.treetop +1 -1
- data/lib/asciidoctor/pdf/formatted_text/transform.rb +7 -4
- data/lib/asciidoctor/pdf/optimizer.rb +9 -1
- data/lib/asciidoctor/pdf/version.rb +1 -1
- metadata +4 -2
@@ -185,7 +185,7 @@ class Converter < ::Prawn::Document
|
|
185
185
|
blk_0 = blk_1 = preface = nil
|
186
186
|
end
|
187
187
|
|
188
|
-
on_page_create
|
188
|
+
on_page_create(&(method :init_page))
|
189
189
|
|
190
190
|
marked_page_number = page_number
|
191
191
|
# NOTE a new page will already be started (page_number = 2) if the front cover image is a PDF
|
@@ -207,58 +207,62 @@ class Converter < ::Prawn::Document
|
|
207
207
|
end if doc.header? && !doc.notitle
|
208
208
|
end
|
209
209
|
|
210
|
-
|
211
|
-
if (insert_toc = (doc.attr? 'toc') && !(doc.attr? 'toc-placement', 'macro') && doc.sections?)
|
212
|
-
start_new_page if @ppbook && verso_page?
|
213
|
-
allocate_toc doc, toc_num_levels, @y, use_title_page
|
214
|
-
else
|
215
|
-
@toc_extent = nil
|
216
|
-
end
|
210
|
+
num_front_matter_pages = toc_page_nums = toc_num_levels = nil
|
217
211
|
|
218
|
-
|
212
|
+
indent_section do
|
213
|
+
toc_num_levels = (doc.attr 'toclevels', 2).to_i
|
214
|
+
if (insert_toc = (doc.attr? 'toc') && !(doc.attr? 'toc-placement', 'macro') && doc.sections?)
|
215
|
+
start_new_page if @ppbook && verso_page?
|
216
|
+
allocate_toc doc, toc_num_levels, @y, use_title_page
|
217
|
+
else
|
218
|
+
@toc_extent = nil
|
219
|
+
end
|
219
220
|
|
220
|
-
|
221
|
-
zero_page_offset = has_front_cover ? 1 : 0
|
222
|
-
first_page_offset = has_title_page ? zero_page_offset.next : zero_page_offset
|
223
|
-
body_offset = (body_start_page_number = page_number) - 1
|
224
|
-
running_content_start_at = @theme.running_content_start_at || 'body'
|
225
|
-
running_content_start_at = 'toc' if running_content_start_at == 'title' && !has_title_page
|
226
|
-
running_content_start_at = 'body' if running_content_start_at == 'toc' && !insert_toc
|
227
|
-
page_numbering_start_at = @theme.page_numbering_start_at || 'body'
|
228
|
-
page_numbering_start_at = 'toc' if page_numbering_start_at == 'title' && !has_title_page
|
229
|
-
page_numbering_start_at = 'body' if page_numbering_start_at == 'toc' && !insert_toc
|
230
|
-
front_matter_sig = [running_content_start_at, page_numbering_start_at]
|
231
|
-
# table values are number of pages to skip before starting running content and page numbering, respectively
|
232
|
-
num_front_matter_pages = {
|
233
|
-
['title', 'title'] => [zero_page_offset, zero_page_offset],
|
234
|
-
['title', 'toc'] => [zero_page_offset, first_page_offset],
|
235
|
-
['title', 'body'] => [zero_page_offset, body_offset],
|
236
|
-
['toc', 'title'] => [first_page_offset, zero_page_offset],
|
237
|
-
['toc', 'toc'] => [first_page_offset, first_page_offset],
|
238
|
-
['toc', 'body'] => [first_page_offset, body_offset],
|
239
|
-
['body', 'title'] => [body_offset, zero_page_offset],
|
240
|
-
['body', 'toc'] => [body_offset, first_page_offset],
|
241
|
-
}[front_matter_sig] || [body_offset, body_offset]
|
242
|
-
else
|
243
|
-
num_front_matter_pages = [body_start_page_number - 1] * 2
|
244
|
-
end
|
221
|
+
start_new_page if @ppbook && verso_page?
|
245
222
|
|
246
|
-
|
247
|
-
|
248
|
-
|
223
|
+
if use_title_page
|
224
|
+
zero_page_offset = has_front_cover ? 1 : 0
|
225
|
+
first_page_offset = has_title_page ? zero_page_offset.next : zero_page_offset
|
226
|
+
body_offset = (body_start_page_number = page_number) - 1
|
227
|
+
running_content_start_at = @theme.running_content_start_at || 'body'
|
228
|
+
running_content_start_at = 'toc' if running_content_start_at == 'title' && !has_title_page
|
229
|
+
running_content_start_at = 'body' if running_content_start_at == 'toc' && !insert_toc
|
230
|
+
page_numbering_start_at = @theme.page_numbering_start_at || 'body'
|
231
|
+
page_numbering_start_at = 'toc' if page_numbering_start_at == 'title' && !has_title_page
|
232
|
+
page_numbering_start_at = 'body' if page_numbering_start_at == 'toc' && !insert_toc
|
233
|
+
front_matter_sig = [running_content_start_at, page_numbering_start_at]
|
234
|
+
# table values are number of pages to skip before starting running content and page numbering, respectively
|
235
|
+
num_front_matter_pages = {
|
236
|
+
['title', 'title'] => [zero_page_offset, zero_page_offset],
|
237
|
+
['title', 'toc'] => [zero_page_offset, first_page_offset],
|
238
|
+
['title', 'body'] => [zero_page_offset, body_offset],
|
239
|
+
['toc', 'title'] => [first_page_offset, zero_page_offset],
|
240
|
+
['toc', 'toc'] => [first_page_offset, first_page_offset],
|
241
|
+
['toc', 'body'] => [first_page_offset, body_offset],
|
242
|
+
['body', 'title'] => [body_offset, zero_page_offset],
|
243
|
+
['body', 'toc'] => [body_offset, first_page_offset],
|
244
|
+
}[front_matter_sig] || [body_offset, body_offset]
|
245
|
+
else
|
246
|
+
num_front_matter_pages = [body_start_page_number - 1] * 2
|
247
|
+
end
|
249
248
|
|
250
|
-
|
249
|
+
@index.start_page_number = num_front_matter_pages[1] + 1
|
250
|
+
doc.set_attr 'pdf-anchor', (doc_anchor = derive_anchor_from_id doc.id, 'top')
|
251
|
+
add_dest_for_block doc, doc_anchor
|
251
252
|
|
252
|
-
|
253
|
+
convert_section generate_manname_section doc if doc.doctype == 'manpage' && (doc.attr? 'manpurpose')
|
253
254
|
|
254
|
-
|
255
|
-
layout_footnotes doc
|
255
|
+
convert_content_for_block doc
|
256
256
|
|
257
|
-
|
258
|
-
|
259
|
-
delete_page if page.empty? && page_count > 1
|
257
|
+
# NOTE for a book, these are leftover footnotes; for an article this is everything
|
258
|
+
outdent_section { layout_footnotes doc }
|
260
259
|
|
261
|
-
|
260
|
+
# NOTE delete orphaned page (a page was created but there was no additional content)
|
261
|
+
# QUESTION should we delete page if document is empty? (leaving no pages?)
|
262
|
+
delete_page if page.empty? && page_count > 1
|
263
|
+
|
264
|
+
toc_page_nums = @toc_extent ? (layout_toc doc, toc_num_levels, @toc_extent[:page_nums].first, @toc_extent[:start_y], num_front_matter_pages[1]) : []
|
265
|
+
end
|
262
266
|
|
263
267
|
unless page_count < body_start_page_number
|
264
268
|
unless doc.noheader || @theme.header_height.to_f.zero?
|
@@ -343,6 +347,7 @@ class Converter < ::Prawn::Document
|
|
343
347
|
from, to = r.rstrip.split '-', 2
|
344
348
|
to ? ((get_char from)..(get_char to)).to_a : [(get_char from)]
|
345
349
|
}.flatten
|
350
|
+
@section_indent = (val = @theme.section_indent) && (inflate_indent val)
|
346
351
|
@index = IndexCatalog.new
|
347
352
|
# NOTE we have to init Pdfmark class here while we have reference to the doc
|
348
353
|
@pdfmark = (doc.attr? 'pdfmark') ? (Pdfmark.new doc) : nil
|
@@ -458,7 +463,9 @@ class Converter < ::Prawn::Document
|
|
458
463
|
def build_pdf_info doc
|
459
464
|
info = {}
|
460
465
|
# FIXME use sanitize: :plain_text once available
|
461
|
-
|
466
|
+
if (doctitle = doc.header? ? doc.doctitle : (doc.attr 'untitled-label'))
|
467
|
+
info[:Title] = (sanitize doctitle).as_pdf
|
468
|
+
end
|
462
469
|
info[:Author] = (doc.attr 'authors').as_pdf if doc.attr? 'authors'
|
463
470
|
info[:Subject] = (doc.attr 'subject').as_pdf if doc.attr? 'subject'
|
464
471
|
info[:Keywords] = (doc.attr 'keywords').as_pdf if doc.attr? 'keywords'
|
@@ -536,22 +543,35 @@ class Converter < ::Prawn::Document
|
|
536
543
|
elsif type == :chapter
|
537
544
|
layout_chapter_title sect, title, align: align, level: hlevel
|
538
545
|
else
|
539
|
-
layout_heading title, align: align, level: hlevel
|
546
|
+
layout_heading title, align: align, level: hlevel, outdent: true
|
540
547
|
end
|
541
548
|
end
|
542
549
|
|
543
|
-
if
|
544
|
-
|
545
|
-
indent indent_l, indent_r do
|
546
|
-
sect.sectname == 'index' ? (convert_index_section sect) : (convert_content_for_block sect)
|
547
|
-
end
|
550
|
+
if sect.sectname == 'index'
|
551
|
+
outdent_section { convert_index_section sect }
|
548
552
|
else
|
549
|
-
|
553
|
+
convert_content_for_block sect
|
550
554
|
end
|
551
|
-
layout_footnotes sect if type == :chapter
|
555
|
+
outdent_section { layout_footnotes sect } if type == :chapter
|
552
556
|
sect.set_attr 'pdf-page-end', page_number
|
553
557
|
end
|
554
558
|
|
559
|
+
def indent_section
|
560
|
+
if (values = @section_indent)
|
561
|
+
indent(values[0], values[1]) { yield }
|
562
|
+
else
|
563
|
+
yield
|
564
|
+
end
|
565
|
+
end
|
566
|
+
|
567
|
+
def outdent_section enabled = true
|
568
|
+
if enabled && (values = @section_indent)
|
569
|
+
indent(-values[0], -values[1]) { yield }
|
570
|
+
else
|
571
|
+
yield
|
572
|
+
end
|
573
|
+
end
|
574
|
+
|
555
575
|
# QUESTION if a footnote ref appears in a separate chapter, should the footnote def be duplicated?
|
556
576
|
def layout_footnotes node
|
557
577
|
return if (fns = (doc = node.document).footnotes - @footnotes).empty?
|
@@ -571,59 +591,60 @@ class Converter < ::Prawn::Document
|
|
571
591
|
add_dest_for_block node if node.id
|
572
592
|
# QUESTION should we decouple styles from section titles?
|
573
593
|
theme_font :heading, level: (hlevel = node.level + 1) do
|
574
|
-
layout_heading node.title, align: (@theme[%(heading_h#{hlevel}_align)] || @theme.heading_align || @base_align).to_sym, level: hlevel
|
594
|
+
layout_heading node.title, align: (@theme[%(heading_h#{hlevel}_align)] || @theme.heading_align || @base_align).to_sym, level: hlevel, outdent: (node.parent.context == :section)
|
575
595
|
end
|
576
596
|
end
|
577
597
|
|
578
598
|
def convert_abstract node
|
579
599
|
add_dest_for_block node if node.id
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
node.blocks
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
if
|
600
|
-
|
600
|
+
outdent_section do
|
601
|
+
pad_box @theme.abstract_padding do
|
602
|
+
theme_font :abstract_title do
|
603
|
+
layout_prose 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), line_height: @theme.heading_line_height
|
604
|
+
end if node.title?
|
605
|
+
theme_font :abstract do
|
606
|
+
prose_opts = { line_height: @theme.abstract_line_height, align: (initial_alignment = (@theme.abstract_align || @base_align).to_sym) }
|
607
|
+
if (text_indent = @theme.prose_text_indent)
|
608
|
+
prose_opts[:indent_paragraphs] = text_indent
|
609
|
+
end
|
610
|
+
# FIXME control more first_line_options using theme
|
611
|
+
if (line1_font_style = @theme.abstract_first_line_font_style) && line1_font_style.to_sym != font_style
|
612
|
+
prose_opts[:first_line_options] = { styles: [font_style, line1_font_style.to_sym] }
|
613
|
+
end
|
614
|
+
# FIXME make this cleaner!!
|
615
|
+
if node.blocks?
|
616
|
+
node.blocks.each do |child|
|
617
|
+
# FIXME is playback necessary here?
|
618
|
+
child.document.playback_attributes child.attributes
|
619
|
+
if child.context == :paragraph
|
620
|
+
if (alignment = resolve_alignment_from_role child.roles)
|
621
|
+
prose_opts[:align] = alignment
|
622
|
+
end
|
623
|
+
layout_prose child.content, prose_opts
|
624
|
+
prose_opts.delete :first_line_options
|
625
|
+
prose_opts[:align] = initial_alignment
|
626
|
+
else
|
627
|
+
# FIXME this could do strange things if the wrong kind of content shows up
|
628
|
+
convert_content_for_block child
|
601
629
|
end
|
602
|
-
layout_prose child.content, prose_opts
|
603
|
-
prose_opts.delete :first_line_options
|
604
|
-
prose_opts[:align] = initial_alignment
|
605
|
-
else
|
606
|
-
# FIXME this could do strange things if the wrong kind of content shows up
|
607
|
-
convert_content_for_block child
|
608
630
|
end
|
631
|
+
elsif node.content_model != :compound && (string = node.content)
|
632
|
+
if (alignment = resolve_alignment_from_role node.roles)
|
633
|
+
prose_opts[:align] = alignment
|
634
|
+
end
|
635
|
+
layout_prose string, prose_opts
|
609
636
|
end
|
610
|
-
elsif node.content_model != :compound && (string = node.content)
|
611
|
-
if (alignment = resolve_alignment_from_role node.roles)
|
612
|
-
prose_opts[:align] = alignment
|
613
|
-
end
|
614
|
-
layout_prose string, prose_opts
|
615
637
|
end
|
616
638
|
end
|
639
|
+
# QUESTION should we be adding margin below the abstract??
|
640
|
+
#theme_margin :block, :bottom
|
617
641
|
end
|
618
|
-
# QUESTION should we be adding margin below the abstract??
|
619
|
-
#theme_margin :block, :bottom
|
620
642
|
end
|
621
643
|
|
622
644
|
def convert_preamble node
|
623
|
-
# TODO find_by needs to support a depth argument
|
624
645
|
# FIXME core should not be promoting paragraph to preamble if there are no sections
|
625
|
-
if (
|
626
|
-
|
646
|
+
if node.blocks? && (first_block = node.blocks[0]).context == :paragraph && node.document.sections?
|
647
|
+
first_block.add_role 'lead' unless first_block.role?
|
627
648
|
end
|
628
649
|
convert_content_for_block node
|
629
650
|
end
|
@@ -1024,7 +1045,7 @@ class Converter < ::Prawn::Document
|
|
1024
1045
|
pad_box @theme.sidebar_padding do
|
1025
1046
|
theme_font :sidebar_title do
|
1026
1047
|
# QUESTION should we allow margins of sidebar title to be customized?
|
1027
|
-
|
1048
|
+
layout_prose node.title, align: (@theme.sidebar_title_align || @base_align).to_sym, margin_top: 0, margin_bottom: (@theme.heading_margin_bottom || 0), line_height: @theme.heading_line_height
|
1028
1049
|
end if node.title?
|
1029
1050
|
theme_font :sidebar do
|
1030
1051
|
convert_content_for_block node
|
@@ -1418,7 +1439,13 @@ class Converter < ::Prawn::Document
|
|
1418
1439
|
if ::File.readable? image_path
|
1419
1440
|
# NOTE import_page automatically advances to next page afterwards
|
1420
1441
|
# QUESTION should we add destination to top of imported page?
|
1421
|
-
|
1442
|
+
if (pgnums = node.attr 'pages', nil, false)
|
1443
|
+
(resolve_pagenums pgnums).each_with_index do |pgnum, idx|
|
1444
|
+
import_page image_path, page: pgnum, replace: (idx == 0 ? page.empty? : true)
|
1445
|
+
end
|
1446
|
+
else
|
1447
|
+
import_page image_path, page: [(node.attr 'page', nil, 1).to_i, 1].max, replace: page.empty?
|
1448
|
+
end
|
1422
1449
|
else
|
1423
1450
|
# QUESTION should we use alt text in this case?
|
1424
1451
|
logger.warn %(pdf to insert not found or not readable: #{image_path})
|
@@ -1545,7 +1572,7 @@ class Converter < ::Prawn::Document
|
|
1545
1572
|
end
|
1546
1573
|
|
1547
1574
|
def draw_image_border top, w, h, alignment
|
1548
|
-
if (
|
1575
|
+
if (@theme.image_border_width || 0) > 0 && @theme.image_border_color
|
1549
1576
|
if (@theme.image_border_fit || 'content') == 'auto'
|
1550
1577
|
bb_width = bounds.width
|
1551
1578
|
elsif alignment == :center
|
@@ -1953,7 +1980,7 @@ class Converter < ::Prawn::Document
|
|
1953
1980
|
rowspan: cell.rowspan || 1,
|
1954
1981
|
align: (cell.attr 'halign', nil, false).to_sym,
|
1955
1982
|
valign: (val = cell.attr 'valign', nil, false) == 'middle' ? :center : val.to_sym,
|
1956
|
-
padding: theme.table_cell_padding
|
1983
|
+
padding: theme.table_cell_padding,
|
1957
1984
|
}
|
1958
1985
|
cell_transform = nil
|
1959
1986
|
case cell.style
|
@@ -2490,7 +2517,7 @@ class Converter < ::Prawn::Document
|
|
2490
2517
|
|
2491
2518
|
def convert_inline_indexterm node
|
2492
2519
|
# NOTE indexterms not supported if text gets substituted before PDF is initialized
|
2493
|
-
return '' unless
|
2520
|
+
return '' unless defined? @index
|
2494
2521
|
if scratch?
|
2495
2522
|
node.type == :visible ? node.text : ''
|
2496
2523
|
else
|
@@ -2596,7 +2623,7 @@ class Converter < ::Prawn::Document
|
|
2596
2623
|
title_align = (@theme.title_page_align || @base_align).to_sym
|
2597
2624
|
|
2598
2625
|
# TODO disallow .pdf as image type
|
2599
|
-
if (logo_image_path = (doc.attr 'title-logo-image') || (logo_image_from_theme = @theme.title_page_logo_image))
|
2626
|
+
if @theme.title_page_logo_display != 'none' && (logo_image_path = (doc.attr 'title-logo-image') || (logo_image_from_theme = @theme.title_page_logo_image))
|
2600
2627
|
if (logo_image_path.include? ':') && logo_image_path =~ ImageAttributeValueRx
|
2601
2628
|
logo_image_attrs = (AttributeList.new $2).parse ['alt', 'width', 'height']
|
2602
2629
|
relative_to_imagesdir = true
|
@@ -2607,7 +2634,9 @@ class Converter < ::Prawn::Document
|
|
2607
2634
|
logo_image_path = ThemeLoader.resolve_theme_asset (sub_attributes_discretely doc, logo_image_path), @themesdir if logo_image_from_theme
|
2608
2635
|
end
|
2609
2636
|
logo_image_attrs['target'] = logo_image_path
|
2610
|
-
logo_image_attrs
|
2637
|
+
if (logo_align = [(logo_image_attrs.delete 'align'), @theme.title_page_logo_align, title_align.to_s].find {|val| (BlockAlignmentNames.include? val) })
|
2638
|
+
logo_image_attrs['align'] = logo_align
|
2639
|
+
end
|
2611
2640
|
# QUESTION should we allow theme to turn logo image off?
|
2612
2641
|
logo_image_top = logo_image_attrs['top'] || @theme.title_page_logo_top || '10%'
|
2613
2642
|
# FIXME delegate to method to convert page % to y value
|
@@ -2628,7 +2657,6 @@ class Converter < ::Prawn::Document
|
|
2628
2657
|
|
2629
2658
|
# TODO prevent content from spilling to next page
|
2630
2659
|
theme_font :title_page do
|
2631
|
-
doctitle = doc.doctitle partition: true
|
2632
2660
|
if (title_top = @theme.title_page_title_top)
|
2633
2661
|
if title_top.end_with? 'vh'
|
2634
2662
|
title_top = page_height - page_height * title_top.to_f / 100.0
|
@@ -2638,21 +2666,24 @@ class Converter < ::Prawn::Document
|
|
2638
2666
|
# FIXME delegate to method to convert page % to y value
|
2639
2667
|
@y = title_top
|
2640
2668
|
end
|
2641
|
-
|
2642
|
-
|
2643
|
-
|
2644
|
-
|
2645
|
-
|
2646
|
-
|
2647
|
-
|
2669
|
+
unless @theme.title_page_title_display == 'none'
|
2670
|
+
doctitle = doc.doctitle partition: true
|
2671
|
+
move_down(@theme.title_page_title_margin_top || 0)
|
2672
|
+
indent (@theme.title_page_title_margin_left || 0), (@theme.title_page_title_margin_right || 0) do
|
2673
|
+
theme_font :title_page_title do
|
2674
|
+
layout_prose doctitle.main,
|
2675
|
+
align: title_align,
|
2676
|
+
margin: 0,
|
2677
|
+
line_height: @theme.title_page_title_line_height
|
2678
|
+
end
|
2648
2679
|
end
|
2680
|
+
move_down(@theme.title_page_title_margin_bottom || 0)
|
2649
2681
|
end
|
2650
|
-
|
2651
|
-
if doctitle.subtitle
|
2682
|
+
if @theme.title_page_subtitle_display != 'none' && (subtitle = (doctitle || (doc.doctitle partition: true)).subtitle)
|
2652
2683
|
move_down(@theme.title_page_subtitle_margin_top || 0)
|
2653
2684
|
indent (@theme.title_page_subtitle_margin_left || 0), (@theme.title_page_subtitle_margin_right || 0) do
|
2654
2685
|
theme_font :title_page_subtitle do
|
2655
|
-
|
2686
|
+
layout_prose subtitle,
|
2656
2687
|
align: title_align,
|
2657
2688
|
margin: 0,
|
2658
2689
|
line_height: @theme.title_page_subtitle_line_height
|
@@ -2660,7 +2691,7 @@ class Converter < ::Prawn::Document
|
|
2660
2691
|
end
|
2661
2692
|
move_down(@theme.title_page_subtitle_margin_bottom || 0)
|
2662
2693
|
end
|
2663
|
-
if doc.attr? 'authors'
|
2694
|
+
if @theme.title_page_authors_display != 'none' && (doc.attr? 'authors')
|
2664
2695
|
move_down(@theme.title_page_authors_margin_top || 0)
|
2665
2696
|
indent (@theme.title_page_authors_margin_left || 0), (@theme.title_page_authors_margin_right || 0) do
|
2666
2697
|
# TODO provide an API in core to get authors as an array
|
@@ -2676,8 +2707,7 @@ class Converter < ::Prawn::Document
|
|
2676
2707
|
end
|
2677
2708
|
move_down(@theme.title_page_authors_margin_bottom || 0)
|
2678
2709
|
end
|
2679
|
-
revision_info = [(doc.attr? 'revnumber') ? %(#{doc.attr 'version-label'} #{doc.attr 'revnumber'}) : nil, (doc.attr 'revdate')].compact
|
2680
|
-
unless revision_info.empty?
|
2710
|
+
unless @theme.title_page_revision_display == 'none' || (revision_info = [(doc.attr? 'revnumber') ? %(#{doc.attr 'version-label'} #{doc.attr 'revnumber'}) : nil, (doc.attr 'revdate')].compact).empty?
|
2681
2711
|
move_down(@theme.title_page_revision_margin_top || 0)
|
2682
2712
|
revision_text = revision_info.join (@theme.title_page_revision_delimiter || ', ')
|
2683
2713
|
if (revremark = doc.attr 'revremark')
|
@@ -2694,6 +2724,8 @@ class Converter < ::Prawn::Document
|
|
2694
2724
|
move_down(@theme.title_page_revision_margin_bottom || 0)
|
2695
2725
|
end
|
2696
2726
|
end
|
2727
|
+
|
2728
|
+
layout_prose DummyText, margin: 0, line_height: 1, normalize: false if page.empty?
|
2697
2729
|
end
|
2698
2730
|
|
2699
2731
|
def layout_cover_page doc, face
|
@@ -2748,7 +2780,7 @@ class Converter < ::Prawn::Document
|
|
2748
2780
|
end
|
2749
2781
|
|
2750
2782
|
def layout_chapter_title node, title, opts = {}
|
2751
|
-
layout_heading title, opts
|
2783
|
+
layout_heading title, (opts.merge outdent: true)
|
2752
2784
|
end
|
2753
2785
|
|
2754
2786
|
alias start_new_part start_new_chapter
|
@@ -2772,13 +2804,15 @@ class Converter < ::Prawn::Document
|
|
2772
2804
|
if (transform = resolve_text_transform opts)
|
2773
2805
|
string = transform_text string, transform
|
2774
2806
|
end
|
2775
|
-
|
2776
|
-
|
2777
|
-
|
2778
|
-
|
2779
|
-
|
2780
|
-
|
2781
|
-
|
2807
|
+
outdent_section(opts.delete :outdent) do
|
2808
|
+
margin_top top_margin
|
2809
|
+
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), {
|
2810
|
+
color: @font_color,
|
2811
|
+
inline_format: true,
|
2812
|
+
align: @base_align.to_sym
|
2813
|
+
}.merge(opts)
|
2814
|
+
margin_bottom bot_margin
|
2815
|
+
end
|
2782
2816
|
end
|
2783
2817
|
|
2784
2818
|
# NOTE inline_format is true by default
|
@@ -2915,7 +2949,7 @@ class Converter < ::Prawn::Document
|
|
2915
2949
|
theme_font :heading, level: 2 do
|
2916
2950
|
theme_font :toc_title do
|
2917
2951
|
toc_title_align = (@theme.toc_title_align || @theme.heading_h2_align || @theme.heading_align || @base_align).to_sym
|
2918
|
-
layout_heading toc_title, align: toc_title_align, level: 2
|
2952
|
+
layout_heading toc_title, align: toc_title_align, level: 2, outdent: true
|
2919
2953
|
end
|
2920
2954
|
end
|
2921
2955
|
end
|
@@ -3040,12 +3074,12 @@ class Converter < ::Prawn::Document
|
|
3040
3074
|
|
3041
3075
|
# TODO delegate to layout_page_header and layout_page_footer per page
|
3042
3076
|
def layout_running_content periphery, doc, opts = {}
|
3043
|
-
skip, skip_pagenums
|
3077
|
+
skip, skip_pagenums = opts[:skip] || [1, 1]
|
3044
3078
|
body_start_page_number = opts[:body_start_page_number] || 1
|
3045
3079
|
# NOTE find and advance to first non-imported content page to use as model page
|
3046
3080
|
return unless (content_start_page = state.pages[skip..-1].index {|it| !it.imported_page? })
|
3047
3081
|
content_start_page += (skip + 1)
|
3048
|
-
num_pages = page_count
|
3082
|
+
num_pages = page_count
|
3049
3083
|
prev_page_number = page_number
|
3050
3084
|
go_to_page content_start_page
|
3051
3085
|
|
@@ -3054,34 +3088,37 @@ class Converter < ::Prawn::Document
|
|
3054
3088
|
header = doc.header? ? doc.header : nil
|
3055
3089
|
sectlevels = (@theme[%(#{periphery}_sectlevels)] || 2).to_i
|
3056
3090
|
sections = doc.find_by(context: :section) {|sect| sect.level <= sectlevels && sect != header } || []
|
3091
|
+
if (toc_page_nums = @toc_extent && @toc_extent[:page_nums])
|
3092
|
+
toc_title = (doc.attr 'toc-title') || ''
|
3093
|
+
end
|
3057
3094
|
|
3058
3095
|
title_method = TitleStyles[@theme[%(#{periphery}_title_style)]]
|
3059
3096
|
# FIXME we need a proper model for all this page counting
|
3060
3097
|
# FIXME we make a big assumption that part & chapter start on new pages
|
3061
|
-
# index parts, chapters and sections by the
|
3098
|
+
# index parts, chapters and sections by the physical page number on which they start
|
3062
3099
|
part_start_pages = {}
|
3063
3100
|
chapter_start_pages = {}
|
3064
3101
|
section_start_pages = {}
|
3065
3102
|
trailing_section_start_pages = {}
|
3066
3103
|
sections.each do |sect|
|
3067
|
-
|
3104
|
+
pgnum = (sect.attr 'pdf-page-start').to_i
|
3068
3105
|
if is_book && ((sect_is_part = sect.part?) || sect.chapter?)
|
3069
3106
|
if sect_is_part
|
3070
|
-
part_start_pages[
|
3107
|
+
part_start_pages[pgnum] ||= sect.send(*title_method)
|
3071
3108
|
else
|
3072
|
-
chapter_start_pages[
|
3109
|
+
chapter_start_pages[pgnum] ||= sect.send(*title_method)
|
3073
3110
|
if sect.sectname == 'appendix' && !part_start_pages.empty?
|
3074
3111
|
# FIXME need a better way to indicate that part has ended
|
3075
|
-
part_start_pages[
|
3112
|
+
part_start_pages[pgnum] = ''
|
3076
3113
|
end
|
3077
3114
|
end
|
3078
3115
|
else
|
3079
|
-
sect_title = trailing_section_start_pages[
|
3080
|
-
section_start_pages[
|
3116
|
+
sect_title = trailing_section_start_pages[pgnum] = sect.send(*title_method)
|
3117
|
+
section_start_pages[pgnum] ||= sect_title
|
3081
3118
|
end
|
3082
3119
|
end
|
3083
3120
|
|
3084
|
-
# index parts, chapters, and sections by the
|
3121
|
+
# index parts, chapters, and sections by the physical page number on which they appear
|
3085
3122
|
parts_by_page = {}
|
3086
3123
|
chapters_by_page = {}
|
3087
3124
|
sections_by_page = {}
|
@@ -3091,42 +3128,42 @@ class Converter < ::Prawn::Document
|
|
3091
3128
|
last_chap = is_book ? :pre : nil
|
3092
3129
|
last_sect = nil
|
3093
3130
|
sect_search_threshold = 1
|
3094
|
-
(1..num_pages).each do |
|
3095
|
-
if (part = part_start_pages[
|
3131
|
+
(1..num_pages).each do |pgnum|
|
3132
|
+
if (part = part_start_pages[pgnum])
|
3096
3133
|
last_part = part
|
3097
3134
|
last_chap = nil
|
3098
3135
|
last_sect = nil
|
3099
3136
|
end
|
3100
|
-
if (chap = chapter_start_pages[
|
3137
|
+
if (chap = chapter_start_pages[pgnum])
|
3101
3138
|
last_chap = chap
|
3102
3139
|
last_sect = nil
|
3103
3140
|
end
|
3104
|
-
if (sect = section_start_pages[
|
3141
|
+
if (sect = section_start_pages[pgnum])
|
3105
3142
|
last_sect = sect
|
3106
3143
|
elsif part || chap
|
3107
|
-
sect_search_threshold =
|
3144
|
+
sect_search_threshold = pgnum
|
3108
3145
|
# NOTE we didn't find a section on this page; look back to find last section started
|
3109
3146
|
elsif last_sect
|
3110
|
-
((sect_search_threshold)..(
|
3147
|
+
((sect_search_threshold)..(pgnum - 1)).reverse_each do |prev|
|
3111
3148
|
if (sect = trailing_section_start_pages[prev])
|
3112
3149
|
last_sect = sect
|
3113
3150
|
break
|
3114
3151
|
end
|
3115
3152
|
end
|
3116
3153
|
end
|
3117
|
-
parts_by_page[
|
3154
|
+
parts_by_page[pgnum] = last_part
|
3118
3155
|
if last_chap == :pre
|
3119
|
-
if
|
3120
|
-
chapters_by_page[
|
3121
|
-
elsif
|
3122
|
-
chapters_by_page[
|
3156
|
+
if pgnum >= body_start_page_number
|
3157
|
+
chapters_by_page[pgnum] = is_book ? (doc.attr 'preface-title', 'Preface') : nil
|
3158
|
+
elsif toc_page_nums && (toc_page_nums.cover? pgnum)
|
3159
|
+
chapters_by_page[pgnum] = toc_title
|
3123
3160
|
else
|
3124
|
-
chapters_by_page[
|
3161
|
+
chapters_by_page[pgnum] = doc.doctitle
|
3125
3162
|
end
|
3126
3163
|
else
|
3127
|
-
chapters_by_page[
|
3164
|
+
chapters_by_page[pgnum] = last_chap
|
3128
3165
|
end
|
3129
|
-
sections_by_page[
|
3166
|
+
sections_by_page[pgnum] = last_sect
|
3130
3167
|
end
|
3131
3168
|
|
3132
3169
|
doctitle = doc.doctitle partition: true, use_fallback: true
|
@@ -3134,7 +3171,7 @@ class Converter < ::Prawn::Document
|
|
3134
3171
|
doc.set_attr 'doctitle', doctitle.combined
|
3135
3172
|
doc.set_attr 'document-title', doctitle.main
|
3136
3173
|
doc.set_attr 'document-subtitle', doctitle.subtitle
|
3137
|
-
doc.set_attr 'page-count', num_pages
|
3174
|
+
doc.set_attr 'page-count', (num_pages - skip_pagenums)
|
3138
3175
|
|
3139
3176
|
pagenums_enabled = doc.attr? 'pagenums'
|
3140
3177
|
case @media == 'prepress' ? 'physical' : (doc.attr 'pdf-folio-placement')
|
@@ -3148,24 +3185,36 @@ class Converter < ::Prawn::Document
|
|
3148
3185
|
folio_basis, invert_folio = :virtual, false
|
3149
3186
|
end
|
3150
3187
|
periphery_layout_cache = {}
|
3151
|
-
repeat((content_start_page..
|
3188
|
+
repeat((content_start_page..num_pages), dynamic: true) do
|
3152
3189
|
# NOTE don't write on pages which are imported / inserts (otherwise we can get a corrupt PDF)
|
3153
3190
|
next if page.imported_page?
|
3154
|
-
|
3155
|
-
pgnum_label = (RomanNumeral.new
|
3156
|
-
side = page_side((folio_basis == :physical ?
|
3191
|
+
virtual_pgnum = (pgnum = page_number) - skip_pagenums
|
3192
|
+
pgnum_label = (virtual_pgnum < 1 ? (RomanNumeral.new pgnum, :lower) : virtual_pgnum).to_s
|
3193
|
+
side = page_side((folio_basis == :physical ? pgnum : virtual_pgnum), invert_folio)
|
3157
3194
|
# QUESTION should allocation be per side?
|
3158
3195
|
trim_styles, colspec_dict, content_dict, stamp_names = allocate_running_content_layout page, periphery, periphery_layout_cache
|
3159
3196
|
# FIXME we need to have a content setting for chapter pages
|
3160
3197
|
content_by_position, colspec_by_position = content_dict[side], colspec_dict[side]
|
3161
3198
|
# TODO populate chapter-number
|
3162
3199
|
# TODO populate numbered and unnumbered chapter and section titles
|
3163
|
-
doc.set_attr 'page-number', pgnum_label
|
3200
|
+
doc.set_attr 'page-number', pgnum_label if pagenums_enabled
|
3164
3201
|
# QUESTION should the fallback value be nil instead of empty string? or should we remove attribute if no value?
|
3165
|
-
doc.set_attr 'part-title', (parts_by_page[
|
3166
|
-
|
3167
|
-
|
3168
|
-
|
3202
|
+
doc.set_attr 'part-title', (parts_by_page[pgnum] || '')
|
3203
|
+
if toc_page_nums && (toc_page_nums.cover? pgnum)
|
3204
|
+
if is_book
|
3205
|
+
doc.set_attr 'chapter-title', (sect_or_chap_title = toc_title)
|
3206
|
+
doc.set_attr 'section-title', ''
|
3207
|
+
else
|
3208
|
+
doc.set_attr 'chapter-title', ''
|
3209
|
+
doc.set_attr 'section-title', (sect_or_chap_title = section_start_pages[pgnum] ? sections_by_page[pgnum] : toc_title)
|
3210
|
+
end
|
3211
|
+
doc.set_attr 'section-or-chapter-title', sect_or_chap_title
|
3212
|
+
toc_page_nums = nil if toc_page_nums.end == pgnum
|
3213
|
+
else
|
3214
|
+
doc.set_attr 'chapter-title', (chapters_by_page[pgnum] || '')
|
3215
|
+
doc.set_attr 'section-title', (sections_by_page[pgnum] || '')
|
3216
|
+
doc.set_attr 'section-or-chapter-title', (sections_by_page[pgnum] || chapters_by_page[pgnum] || '')
|
3217
|
+
end
|
3169
3218
|
|
3170
3219
|
stamp stamp_names[side] if stamp_names
|
3171
3220
|
|
@@ -3214,7 +3263,7 @@ class Converter < ::Prawn::Document
|
|
3214
3263
|
theme_font %(#{periphery}_#{side}_#{position}) do
|
3215
3264
|
# NOTE minor optimization
|
3216
3265
|
if content == '{page-number}'
|
3217
|
-
content = pagenums_enabled ? pgnum_label
|
3266
|
+
content = pagenums_enabled ? pgnum_label : nil
|
3218
3267
|
else
|
3219
3268
|
content = apply_subs_discretely doc, content, drop_lines_with_unresolved_attributes: true
|
3220
3269
|
content = transform_text content, @text_transform if @text_transform
|
@@ -3245,8 +3294,7 @@ class Converter < ::Prawn::Document
|
|
3245
3294
|
end
|
3246
3295
|
|
3247
3296
|
def allocate_running_content_layout page, periphery, cache
|
3248
|
-
layout = page.layout
|
3249
|
-
cache[layout] ||= begin
|
3297
|
+
cache[layout = page.layout] ||= begin
|
3250
3298
|
trim_styles = {
|
3251
3299
|
line_metrics: (trim_line_metrics = calc_line_metrics @theme[%(#{periphery}_line_height)] || @theme.base_line_height),
|
3252
3300
|
# NOTE we've already verified this property is set
|
@@ -3260,7 +3308,7 @@ class Converter < ::Prawn::Document
|
|
3260
3308
|
column_rule_color: (trim_column_rule_color = resolve_theme_color %(#{periphery}_column_rule_color).to_sym),
|
3261
3309
|
column_rule_style: (@theme[%(#{periphery}_column_rule_style)] || :solid).to_sym,
|
3262
3310
|
column_rule_width: (trim_column_rule_color ? @theme[%(#{periphery}_column_rule_width)] || 0 : 0),
|
3263
|
-
column_rule_spacing: (
|
3311
|
+
column_rule_spacing: (@theme[%(#{periphery}_column_rule_spacing)] || 0),
|
3264
3312
|
valign: (val = (@theme[%(#{periphery}_vertical_align)] || :middle).to_sym) == :middle ? :center : val,
|
3265
3313
|
img_valign: @theme[%(#{periphery}_image_vertical_align)],
|
3266
3314
|
left: {
|
@@ -3349,10 +3397,6 @@ class Converter < ::Prawn::Document
|
|
3349
3397
|
end
|
3350
3398
|
end
|
3351
3399
|
end
|
3352
|
-
# NOTE set fallbacks if not explicitly disabled
|
3353
|
-
if side_content.empty? && periphery == :footer && @theme[%(footer_#{side}_content)] != 'none'
|
3354
|
-
side_content = { side == :recto ? :right : :left => '{page-number}' }
|
3355
|
-
end
|
3356
3400
|
|
3357
3401
|
acc[side] = side_content
|
3358
3402
|
acc
|
@@ -3413,14 +3457,15 @@ class Converter < ::Prawn::Document
|
|
3413
3457
|
end
|
3414
3458
|
|
3415
3459
|
outline.define do
|
3460
|
+
initial_pagenum = has_front_cover ? 2 : 1
|
3416
3461
|
# FIXME use sanitize: :plain_text once available
|
3417
|
-
if
|
3418
|
-
page title: doctitle, destination: (document.dest_top has_front_cover ? 2 : 1)
|
3462
|
+
if document.page_count >= initial_pagenum && (doctitle = doc.header? ? doc.doctitle : (doc.attr 'untitled-label'))
|
3463
|
+
page title: (document.sanitize doctitle), destination: (document.dest_top has_front_cover ? 2 : 1)
|
3419
3464
|
end
|
3420
3465
|
unless toc_page_nums.none? || (toc_title = doc.attr 'toc-title').nil_or_empty?
|
3421
3466
|
page title: toc_title, destination: (document.dest_top toc_page_nums.first)
|
3422
3467
|
end
|
3423
|
-
# QUESTION any way to get add_outline_level to invoke in the context of the outline?
|
3468
|
+
# QUESTION is there any way to get add_outline_level to invoke in the context of the outline?
|
3424
3469
|
document.add_outline_level self, doc.sections, num_levels, expand_levels
|
3425
3470
|
end
|
3426
3471
|
|
@@ -4107,7 +4152,6 @@ class Converter < ::Prawn::Document
|
|
4107
4152
|
# QUESTION is there a better way to do this?
|
4108
4153
|
# I suppose we could have @tmp_files as an instance variable on converter instead
|
4109
4154
|
# It might be sufficient to delete temporary files once per conversion
|
4110
|
-
# NOTE Ruby 1.9 will sometimes delete a tmp file before the process exits
|
4111
4155
|
def unlink_tmp_file path
|
4112
4156
|
path.unlink if TemporaryPath === path && path.exist?
|
4113
4157
|
rescue
|
@@ -4171,6 +4215,20 @@ class Converter < ::Prawn::Document
|
|
4171
4215
|
end
|
4172
4216
|
end
|
4173
4217
|
|
4218
|
+
def resolve_pagenums val
|
4219
|
+
pgnums = []
|
4220
|
+
((val.include? ',') ? (val.split ',') : (val.split ';')).each do |entry|
|
4221
|
+
if entry.include? '..'
|
4222
|
+
from, _, to = entry.partition '..'
|
4223
|
+
pgnums += ([from.to_i, 1].max..[to.to_i, 1].max).to_a
|
4224
|
+
else
|
4225
|
+
pgnums << entry.to_i
|
4226
|
+
end
|
4227
|
+
end
|
4228
|
+
|
4229
|
+
pgnums
|
4230
|
+
end
|
4231
|
+
|
4174
4232
|
def get_char code
|
4175
4233
|
(code.start_with? '\u') ? ([((code.slice 2, code.length).to_i 16)].pack 'U1') : code
|
4176
4234
|
end
|