asciidoctor-pdf 2.0.8 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +18 -0
- data/README.adoc +6 -12
- data/lib/asciidoctor/pdf/converter.rb +131 -59
- data/lib/asciidoctor/pdf/ext/asciidoctor/section.rb +4 -0
- data/lib/asciidoctor/pdf/formatted_string.rb +13 -0
- data/lib/asciidoctor/pdf/formatted_text/inline_image_arranger.rb +5 -0
- data/lib/asciidoctor/pdf/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3fc9b0baae841d33e6205109c5aeacbb88bc196e6396b5dac8df187e0d209f9c
|
|
4
|
+
data.tar.gz: 21fd8ea4cebcb721dc273fa1a8f81104ec8f14f6a4c1a6f396bf6194f5e2309d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 956e66763693837a9483919f0b8b0428adbda5858efbfa75aa7b3a2fc04aff92a8e666643f7aae74ee01b656c1b9ad16984173dec4d0b0552bf668d0ad73d024
|
|
7
|
+
data.tar.gz: e6e2ca9d393d1eafbea531bdf9b828d50075bf398723ecca99549175331aa9f1422a9640128ccbf511e8ec97db3e7ce0c6d763a3bc28aeadf92fc6fd2a130f77
|
data/CHANGELOG.adoc
CHANGED
|
@@ -5,6 +5,24 @@
|
|
|
5
5
|
This document provides a high-level view of the changes to the {project-name} by release.
|
|
6
6
|
For a detailed view of what has changed, refer to the {url-repo}/commits/main[commit history] on GitHub.
|
|
7
7
|
|
|
8
|
+
== 2.1.0 (2022-06-11) - @mojavelinux
|
|
9
|
+
|
|
10
|
+
Enhancements::
|
|
11
|
+
|
|
12
|
+
* arrange body of article or manpage doctype into multiple columns if `page-columns` key is set in theme (#327)
|
|
13
|
+
* allow column gap to be specified using `page-column-gap` key (#327)
|
|
14
|
+
* introduce `convert_index_categories` method to handle rendering of categories for index inside column box (#327)
|
|
15
|
+
* rename `convert_index_list` method to `convert_index_term` to make its purpose more clear (#327)
|
|
16
|
+
* add `save_theme` helper to work with a copy of the theme within a scope (#2196)
|
|
17
|
+
* add support for `scale` attribute or `iw` unit on `pdfwidth` attribute on image macros (#1933)
|
|
18
|
+
* add backlink from bibref on bibliography entry to first reference to that entry in the document (#1737)
|
|
19
|
+
* preserve text formatting on index term in index section (#897)
|
|
20
|
+
* don't insert page break between part and first chapter if `heading-part-break-after` key in theme is `avoid` (#1795)
|
|
21
|
+
|
|
22
|
+
=== Details
|
|
23
|
+
|
|
24
|
+
{url-repo}/releases/tag/v2.1.0[git tag] | {url-repo}/compare/v2.0.8\...v2.1.0[full diff]
|
|
25
|
+
|
|
8
26
|
== 2.0.8 (2022-06-08) - @mojavelinux
|
|
9
27
|
|
|
10
28
|
Improvements::
|
data/README.adoc
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
= Asciidoctor PDF: A native PDF converter for AsciiDoc
|
|
2
2
|
Dan Allen <https://github.com/mojavelinux[@mojavelinux]>; Sarah White <https://github.com/graphitefriction[@graphitefriction]>
|
|
3
|
-
v2.0
|
|
3
|
+
v2.1.0, 2022-06-11
|
|
4
4
|
// Settings:
|
|
5
5
|
:experimental:
|
|
6
6
|
:idprefix:
|
|
@@ -23,8 +23,7 @@ endif::[]
|
|
|
23
23
|
:project-name: Asciidoctor PDF
|
|
24
24
|
:project-handle: asciidoctor-pdf
|
|
25
25
|
// Variables:
|
|
26
|
-
:release-line: 2.
|
|
27
|
-
:release-version: 2.0.8
|
|
26
|
+
:release-line: 2.1.x
|
|
28
27
|
// URLs:
|
|
29
28
|
:url-gem: https://rubygems.org/gems/asciidoctor-pdf
|
|
30
29
|
:url-project: https://github.com/asciidoctor/asciidoctor-pdf
|
|
@@ -47,7 +46,7 @@ The aim of this library is to take the pain out of creating PDF documents from A
|
|
|
47
46
|
|
|
48
47
|
[NOTE]
|
|
49
48
|
====
|
|
50
|
-
The documentation for Asciidoctor PDF
|
|
49
|
+
The documentation for the latest, stable release of Asciidoctor PDF 2.0.x is available at {url-project-docs}/.
|
|
51
50
|
|
|
52
51
|
If you're looking for the documentation for Asciidoctor PDF 1.6, refer to the {url-project-repo}/tree/v1.6.x#readme[README] in the v1.6.x branch.
|
|
53
52
|
Asciidoctor PDF 1.6 is no longer being developed and will reach EOL later this year.
|
|
@@ -114,11 +113,11 @@ There are several optional features of this converter that require additional ge
|
|
|
114
113
|
Those features are as follows.
|
|
115
114
|
|
|
116
115
|
Source highlighting::
|
|
117
|
-
You'll need to {url-project-
|
|
116
|
+
You'll need to {url-project-docs}/syntax-highlighting/[install a syntax highlighter] to use source highlighting (build-time only).
|
|
118
117
|
|
|
119
118
|
PDF optimization::
|
|
120
119
|
If you want to optimize your PDF, you'll need rghost or hexapdf.
|
|
121
|
-
See {url-project-
|
|
120
|
+
See {url-project-docs}/optimize-pdf/[Optimize the PDF] for installation and usage instructions.
|
|
122
121
|
|
|
123
122
|
Automatic hyphenation::
|
|
124
123
|
To turn on automatic hyphenation using the `hyphens` attribute, you'll need to install the `text-hyphen` gem:
|
|
@@ -128,7 +127,7 @@ To turn on automatic hyphenation using the `hyphens` attribute, you'll need to i
|
|
|
128
127
|
Accelerated image decoding::
|
|
129
128
|
Ruby is not particularly fast at decoding images, and the image formats it supports are limited.
|
|
130
129
|
To help, you can install prawn-gmagick, which delegates the work of decoding images to GraphicsMagick.
|
|
131
|
-
Refer to {url-project-
|
|
130
|
+
Refer to {url-project-docs}/image-paths-and-formats/#other-image-formats[Supporting additional image file formats] for instructions about how to enable this integration.
|
|
132
131
|
|
|
133
132
|
Check the {url-project-docs}/install/#table-minimum-version[minimum supported version table] to make sure you're using a supported version of the dependency.
|
|
134
133
|
|
|
@@ -197,11 +196,6 @@ ifndef::env-site[]
|
|
|
197
196
|
See the <<CONTRIBUTING.adoc#,contributing guide>>.
|
|
198
197
|
To help develop {project-name}, or to simply use the development version, refer to the <<CONTRIBUTING-CODE.adoc#,developing and contributing code guide>>.
|
|
199
198
|
|
|
200
|
-
[[resources,Links]]
|
|
201
|
-
== Resources
|
|
202
|
-
|
|
203
|
-
* https://groups.google.com/forum/#!msg/prawn-ruby/MbMsCx862iY/6ImCsvLGfVcJ[Discussion about image quality in PDFs]
|
|
204
|
-
|
|
205
199
|
== Authors
|
|
206
200
|
|
|
207
201
|
{project-name} was written by https://github.com/mojavelinux[Dan Allen] and https://github.com/graphitefriction[Sarah White] of OpenDevise Inc. on behalf of the Asciidoctor Project.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative 'formatted_string'
|
|
3
4
|
require_relative 'formatted_text'
|
|
4
5
|
require_relative 'index_catalog'
|
|
5
6
|
require_relative 'pdfmark'
|
|
@@ -124,6 +125,7 @@ module Asciidoctor
|
|
|
124
125
|
DropAnchorRx = %r(<(?:a\b[^>]*|/a)>)
|
|
125
126
|
SourceHighlighters = %w(coderay pygments rouge).to_set
|
|
126
127
|
ViewportWidth = ::Module.new
|
|
128
|
+
ImageWidth = ::Module.new
|
|
127
129
|
(TitleStyles = {
|
|
128
130
|
'toc' => [:numbered_title],
|
|
129
131
|
'basic' => [:title],
|
|
@@ -169,7 +171,8 @@ module Asciidoctor
|
|
|
169
171
|
# NOTE: a new page will already be started (page_number = 2) if the front cover image is a PDF
|
|
170
172
|
ink_cover_page doc, :front
|
|
171
173
|
has_front_cover = page_number > marked_page_number
|
|
172
|
-
|
|
174
|
+
doctype = doc.doctype
|
|
175
|
+
if (has_title_page = (title_page_on = doctype == 'book' || (doc.attr? 'title-page')) && (start_title_page doc))
|
|
173
176
|
# NOTE: the base font must be set before any content is written to the main or scratch document
|
|
174
177
|
font @theme.base_font_family, size: @root_font_size, style: @theme.base_font_style
|
|
175
178
|
if perform_on_single_page { ink_title_page doc }
|
|
@@ -278,12 +281,19 @@ module Asciidoctor
|
|
|
278
281
|
doc.set_attr 'pdf-anchor', (derive_anchor_from_id doc.id, 'top')
|
|
279
282
|
doc.set_attr 'pdf-page-start', page_number
|
|
280
283
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
284
|
+
if doctype == 'book' || (columns = @theme.page_columns || 1) < 2
|
|
285
|
+
convert_section generate_manname_section doc if doctype == 'manpage' && (doc.attr? 'manpurpose')
|
|
286
|
+
traverse doc
|
|
287
|
+
# NOTE: for a book, these are leftover footnotes; for an article this is everything
|
|
288
|
+
outdent_section { ink_footnotes doc }
|
|
289
|
+
else
|
|
290
|
+
column_box [bounds.left, cursor], columns: columns, width: bounds.width, reflow_margins: true, spacer: @theme.page_column_gap do
|
|
291
|
+
convert_section generate_manname_section doc if doctype == 'manpage' && (doc.attr? 'manpurpose')
|
|
292
|
+
traverse doc
|
|
293
|
+
# NOTE: for a book, these are leftover footnotes; for an article this is everything
|
|
294
|
+
outdent_section { ink_footnotes doc }
|
|
295
|
+
end
|
|
296
|
+
end
|
|
287
297
|
|
|
288
298
|
if (toc_extent = @toc_extent)
|
|
289
299
|
if title_page_on && !insert_toc
|
|
@@ -402,6 +412,7 @@ module Asciidoctor
|
|
|
402
412
|
@list_bullets = []
|
|
403
413
|
@bottom_gutters = [{}]
|
|
404
414
|
@rendered_footnotes = []
|
|
415
|
+
@bibref_refs = ::Set.new
|
|
405
416
|
@conum_glyphs = ConumSets[@theme.conum_glyphs || 'circled'] || (@theme.conum_glyphs.split ',').map do |r|
|
|
406
417
|
from, to = r.lstrip.split '-', 2
|
|
407
418
|
to ? ((get_char from)..(get_char to)).to_a : [(get_char from)]
|
|
@@ -575,6 +586,8 @@ module Asciidoctor
|
|
|
575
586
|
theme.base_font_style = theme.base_font_style&.to_sym || :normal
|
|
576
587
|
theme.page_numbering_start_at ||= 'body'
|
|
577
588
|
theme.running_content_start_at ||= 'body'
|
|
589
|
+
theme.heading_chapter_break_before ||= 'always'
|
|
590
|
+
theme.heading_part_break_before ||= 'always'
|
|
578
591
|
theme.heading_margin_page_top ||= 0
|
|
579
592
|
theme.heading_margin_top ||= 0
|
|
580
593
|
theme.heading_margin_bottom ||= 0
|
|
@@ -609,6 +622,13 @@ module Asciidoctor
|
|
|
609
622
|
theme
|
|
610
623
|
end
|
|
611
624
|
|
|
625
|
+
def save_theme
|
|
626
|
+
@theme = (original_theme = theme).dup
|
|
627
|
+
yield
|
|
628
|
+
ensure
|
|
629
|
+
@theme = original_theme
|
|
630
|
+
end
|
|
631
|
+
|
|
612
632
|
def indent_section
|
|
613
633
|
if (values = @section_indent)
|
|
614
634
|
indent(values[0], values[1]) { yield }
|
|
@@ -647,13 +667,14 @@ module Asciidoctor
|
|
|
647
667
|
hidden = sect.option? 'notitle'
|
|
648
668
|
hopts = { align: text_align, level: hlevel, part: part, chapterlike: chapterlike, outdent: !(part || chapterlike) }
|
|
649
669
|
if part
|
|
650
|
-
|
|
670
|
+
if @theme.heading_part_break_before == 'always'
|
|
651
671
|
started_new = true
|
|
652
672
|
start_new_part sect
|
|
653
673
|
end
|
|
654
674
|
elsif chapterlike
|
|
655
|
-
if @theme.heading_chapter_break_before
|
|
656
|
-
(@theme.heading_part_break_after == '
|
|
675
|
+
if (@theme.heading_chapter_break_before == 'always' &&
|
|
676
|
+
!(@theme.heading_part_break_after == 'avoid' && sect.first_section_of_part?)) ||
|
|
677
|
+
(@theme.heading_part_break_after == 'always' && sect.first_section_of_part?)
|
|
657
678
|
started_new = true
|
|
658
679
|
start_new_chapter sect
|
|
659
680
|
end
|
|
@@ -700,53 +721,74 @@ module Asciidoctor
|
|
|
700
721
|
end
|
|
701
722
|
|
|
702
723
|
def convert_index_section node
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
category.terms.each {|term| convert_index_list_item term, pagenum_sequence_style }
|
|
715
|
-
@theme.prose_margin_bottom > cursor ? bounds.move_past_bottom : (move_down @theme.prose_margin_bottom)
|
|
716
|
-
end
|
|
717
|
-
end_cursor = cursor if bounds.current_column == 0
|
|
718
|
-
end
|
|
719
|
-
# Q: could we move this logic into column_box?
|
|
720
|
-
move_cursor_to end_cursor if end_cursor
|
|
724
|
+
if ColumnBox === bounds || (columns = @theme.index_columns || 1) < 2
|
|
725
|
+
convert_index_categories @index.categories, (node.document.attr 'index-pagenum-sequence-style')
|
|
726
|
+
else
|
|
727
|
+
end_cursor = nil
|
|
728
|
+
column_box [bounds.left, cursor], columns: columns, width: bounds.width, reflow_margins: true, spacer: @theme.index_column_gap do
|
|
729
|
+
convert_index_categories @index.categories, (node.document.attr 'index-pagenum-sequence-style')
|
|
730
|
+
end_cursor = cursor if bounds.current_column == 0
|
|
731
|
+
end
|
|
732
|
+
# Q: could we move this logic into column_box?
|
|
733
|
+
move_cursor_to end_cursor if end_cursor
|
|
734
|
+
end
|
|
721
735
|
nil
|
|
722
736
|
end
|
|
723
737
|
|
|
724
|
-
def
|
|
725
|
-
|
|
738
|
+
def convert_index_categories categories, pagenum_sequence_style = nil
|
|
739
|
+
space_needed_for_category = @theme.description_list_term_spacing + (2 * (height_of_typeset_text 'A'))
|
|
740
|
+
categories.each do |category|
|
|
741
|
+
bounds.move_past_bottom if space_needed_for_category > cursor
|
|
742
|
+
ink_prose category.name,
|
|
743
|
+
align: :left,
|
|
744
|
+
inline_format: false,
|
|
745
|
+
margin_bottom: @theme.description_list_term_spacing,
|
|
746
|
+
style: @theme.description_list_term_font_style&.to_sym
|
|
747
|
+
category.terms.each {|term| convert_index_term term, pagenum_sequence_style }
|
|
748
|
+
@theme.prose_margin_bottom > cursor ? bounds.move_past_bottom : (move_down @theme.prose_margin_bottom)
|
|
749
|
+
end
|
|
750
|
+
end
|
|
751
|
+
|
|
752
|
+
def convert_index_term term, pagenum_sequence_style = nil
|
|
753
|
+
term_fragments = term.name.fragments
|
|
726
754
|
unless term.container?
|
|
755
|
+
pagenum_fragment = (parse_text %(<a>#{DummyText}</a>), inline_format: true)[0]
|
|
727
756
|
if @media == 'screen'
|
|
728
757
|
case pagenum_sequence_style
|
|
729
758
|
when 'page'
|
|
730
|
-
pagenums = term.dests.uniq {|dest| dest[:page] }.map {|dest|
|
|
759
|
+
pagenums = term.dests.uniq {|dest| dest[:page] }.map {|dest| pagenum_fragment.merge anchor: dest[:anchor], text: dest[:page] }
|
|
731
760
|
when 'range'
|
|
732
761
|
first_anchor_per_page = {}.tap {|accum| term.dests.each {|dest| accum[dest[:page]] ||= dest[:anchor] } }
|
|
733
762
|
pagenums = (consolidate_ranges first_anchor_per_page.keys).map do |range|
|
|
734
763
|
anchor = first_anchor_per_page[(range.include? '-') ? (range.partition '-')[0] : range]
|
|
735
|
-
|
|
764
|
+
pagenum_fragment.merge text: range, anchor: anchor
|
|
736
765
|
end
|
|
737
766
|
else # term
|
|
738
|
-
pagenums = term.dests.map {|dest|
|
|
767
|
+
pagenums = term.dests.map {|dest| pagenum_fragment.merge text: dest[:page], anchor: dest[:anchor] }
|
|
739
768
|
end
|
|
740
769
|
else
|
|
741
770
|
pagenums = consolidate_ranges term.dests.map {|dest| dest[:page] }.uniq
|
|
742
771
|
end
|
|
743
|
-
|
|
772
|
+
pagenums.each do |pagenum|
|
|
773
|
+
# NOTE: addresses a very minor kerning issue for text adjacent to the comma
|
|
774
|
+
if (prev_fragment = term_fragments[-1]).size == 1
|
|
775
|
+
if ::String === pagenum
|
|
776
|
+
term_fragments[-1] = prev_fragment.merge text: %(#{prev_fragment[:text]}, #{pagenum})
|
|
777
|
+
next
|
|
778
|
+
else
|
|
779
|
+
term_fragments[-1] = prev_fragment.merge text: %(#{prev_fragment[:text]}, )
|
|
780
|
+
end
|
|
781
|
+
else
|
|
782
|
+
term_fragments << ({ text: ', ' })
|
|
783
|
+
end
|
|
784
|
+
term_fragments << (::String === pagenum ? { text: pagenum } : pagenum)
|
|
785
|
+
end
|
|
744
786
|
end
|
|
745
787
|
subterm_indent = @theme.description_list_description_indent
|
|
746
|
-
|
|
788
|
+
typeset_formatted_text term_fragments, (calc_line_metrics @base_line_height), align: :left, color: @font_color, hanging_indent: subterm_indent * 2
|
|
747
789
|
indent subterm_indent do
|
|
748
790
|
term.subterms.each do |subterm|
|
|
749
|
-
|
|
791
|
+
convert_index_term subterm, pagenum_sequence_style
|
|
750
792
|
end
|
|
751
793
|
end unless term.leaf?
|
|
752
794
|
end
|
|
@@ -1715,9 +1757,14 @@ module Asciidoctor
|
|
|
1715
1757
|
return on_image_error :missing, node, target, (opts.merge align: alignment) unless image_path
|
|
1716
1758
|
|
|
1717
1759
|
# TODO: support cover (aka canvas) image layout using "canvas" (or "cover") role
|
|
1718
|
-
width = resolve_explicit_width node.attributes, bounds_width: (available_w = bounds.width), support_vw: true, use_fallback: true, constrain_to_bounds: true
|
|
1719
|
-
|
|
1720
|
-
|
|
1760
|
+
case (width = resolve_explicit_width node.attributes, bounds_width: (available_w = bounds.width), support_vw: true, use_fallback: true, constrain_to_bounds: true)
|
|
1761
|
+
when ViewportWidth
|
|
1762
|
+
# TODO: add `to_pt page_width` method to ViewportWidth type
|
|
1763
|
+
width = page_width * (width.to_f / 100)
|
|
1764
|
+
when ImageWidth
|
|
1765
|
+
scale = width.to_f / 100
|
|
1766
|
+
width = nil
|
|
1767
|
+
end
|
|
1721
1768
|
|
|
1722
1769
|
caption_end = @theme.image_caption_end&.to_sym || :bottom
|
|
1723
1770
|
caption_max_width = @theme.image_caption_max_width
|
|
@@ -1747,7 +1794,9 @@ module Asciidoctor
|
|
|
1747
1794
|
enable_file_requests_with_root: file_request_root,
|
|
1748
1795
|
cache_images: cache_uri
|
|
1749
1796
|
rendered_w = (svg_size = svg_obj.document.sizing).output_width
|
|
1750
|
-
if
|
|
1797
|
+
if scale
|
|
1798
|
+
svg_size = svg_obj.resize width: (rendered_w = [available_w, rendered_w * scale].min)
|
|
1799
|
+
elsif !width && (svg_obj.document.root.attributes.key? 'width') && rendered_w > available_w
|
|
1751
1800
|
# NOTE: restrict width to available width (prawn-svg already coerces to pixels)
|
|
1752
1801
|
svg_size = svg_obj.resize width: (rendered_w = available_w)
|
|
1753
1802
|
end
|
|
@@ -1781,8 +1830,10 @@ module Asciidoctor
|
|
|
1781
1830
|
image_obj, image_info = ::Base64 === image_path ?
|
|
1782
1831
|
::StringIO.open((::Base64.decode64 image_path), 'rb') {|fd| build_image_object fd } :
|
|
1783
1832
|
::File.open(image_path, 'rb') {|fd| build_image_object fd }
|
|
1833
|
+
actual_w = to_pt image_info.width, :px
|
|
1834
|
+
width = actual_w * scale if scale
|
|
1784
1835
|
# NOTE: if width is not specified, scale native width & height from px to pt and restrict width to available width
|
|
1785
|
-
rendered_w, rendered_h = image_info.calc_image_dimensions width: (width || [available_w,
|
|
1836
|
+
rendered_w, rendered_h = image_info.calc_image_dimensions width: (width || [available_w, actual_w].min)
|
|
1786
1837
|
# NOTE: shrink image so it fits within available space; group image & caption
|
|
1787
1838
|
if rendered_h > (available_h = cursor - caption_h)
|
|
1788
1839
|
unless pinned || at_page_top?
|
|
@@ -2380,9 +2431,12 @@ module Asciidoctor
|
|
|
2380
2431
|
if (text = ref.xreftext node.attr 'xrefstyle', nil, true)&.include? '<a'
|
|
2381
2432
|
text = text.gsub DropAnchorRx, ''
|
|
2382
2433
|
end
|
|
2434
|
+
if ref.inline? && ref.type == :bibref && !scratch? && (@bibref_refs.add? refid)
|
|
2435
|
+
anchor = %(<a id="_bibref_ref_#{refid}">#{DummyText}</a>)
|
|
2436
|
+
end
|
|
2383
2437
|
@resolving_xref = nil
|
|
2384
2438
|
end
|
|
2385
|
-
%(<a anchor="#{derive_anchor_from_id refid}">#{text || "[#{refid}]"}</a>).gsub ']', ']'
|
|
2439
|
+
%(#{anchor || ''}<a anchor="#{derive_anchor_from_id refid}">#{text || "[#{refid}]"}</a>).gsub ']', ']'
|
|
2386
2440
|
else
|
|
2387
2441
|
%(<a anchor="#{doc.attr 'pdf-anchor'}">#{node.text || '[^top]'}</a>)
|
|
2388
2442
|
end
|
|
@@ -2390,10 +2444,12 @@ module Asciidoctor
|
|
|
2390
2444
|
# NOTE: destination is created inside callback registered by FormattedTextTransform#build_fragment
|
|
2391
2445
|
%(<a id="#{node.id}">#{DummyText}</a>)
|
|
2392
2446
|
when :bibref
|
|
2393
|
-
|
|
2447
|
+
id = node.id
|
|
2394
2448
|
# NOTE: technically node.text should be node.reftext, but subs have already been applied to text
|
|
2395
|
-
reftext = (reftext = node.reftext) ? %([#{reftext}]) : %([#{
|
|
2396
|
-
%(<a
|
|
2449
|
+
reftext = (reftext = node.reftext) ? %([#{reftext}]) : %([#{id}])
|
|
2450
|
+
reftext = %(<a anchor="_bibref_ref_#{id}">#{reftext}</a>) if @bibref_refs.include? id
|
|
2451
|
+
# NOTE: destination is created inside callback registered by FormattedTextTransform#build_fragment
|
|
2452
|
+
%(<a id="#{id}">#{DummyText}</a>#{reftext})
|
|
2397
2453
|
else
|
|
2398
2454
|
log :warn, %(unknown anchor type: #{node.type.inspect})
|
|
2399
2455
|
nil
|
|
@@ -2515,12 +2571,18 @@ module Asciidoctor
|
|
|
2515
2571
|
class_attr = (role = node.role) ? %( class="#{role}") : ''
|
|
2516
2572
|
fit_attr = (fit = node.attr 'fit') ? %( fit="#{fit}") : ''
|
|
2517
2573
|
if (width = resolve_explicit_width node.attributes)
|
|
2518
|
-
if
|
|
2519
|
-
|
|
2574
|
+
if ImageWidth === width
|
|
2575
|
+
if state # check that converter is initialized
|
|
2576
|
+
width = (intrinsic_image_width image_path, image_format) * (width.to_f / 100)
|
|
2577
|
+
else
|
|
2578
|
+
width = %(auto*#{width})
|
|
2579
|
+
end
|
|
2580
|
+
elsif node.parent.context == :table_cell && ::String === width && (width.end_with? '%')
|
|
2581
|
+
width += (intrinsic_image_width image_path, image_format).to_s
|
|
2520
2582
|
end
|
|
2521
2583
|
width_attr = %( width="#{width}")
|
|
2522
2584
|
elsif state # check that converter is initialized
|
|
2523
|
-
width_attr = %( width="#{
|
|
2585
|
+
width_attr = %( width="#{intrinsic_image_width image_path, image_format}")
|
|
2524
2586
|
else
|
|
2525
2587
|
width_attr = ' width="auto"' # defer operation until arranger runs
|
|
2526
2588
|
end
|
|
@@ -2541,17 +2603,20 @@ module Asciidoctor
|
|
|
2541
2603
|
if scratch?
|
|
2542
2604
|
visible ? node.text : ''
|
|
2543
2605
|
else
|
|
2544
|
-
|
|
2545
|
-
|
|
2606
|
+
unless defined? @index
|
|
2607
|
+
# NOTE: initialize index and text formatter in case converter is called before PDF is initialized
|
|
2608
|
+
@index = IndexCatalog.new
|
|
2609
|
+
@text_formatter = FormattedText::Formatter.new theme: (load_theme node.document)
|
|
2610
|
+
end
|
|
2546
2611
|
# NOTE: page number (:page key) is added by InlineDestinationMarker
|
|
2547
2612
|
dest = { anchor: (anchor_name = @index.next_anchor_name) }
|
|
2548
2613
|
anchor = %(<a id="#{anchor_name}" type="indexterm"#{visible ? ' visible="true"' : ''}>#{DummyText}</a>)
|
|
2549
2614
|
if visible
|
|
2550
2615
|
visible_term = node.text
|
|
2551
|
-
@index.store_primary_term (
|
|
2616
|
+
@index.store_primary_term (FormattedString.new parse_text visible_term, inline_format: [normalize: true]), dest
|
|
2552
2617
|
%(#{anchor}#{visible_term})
|
|
2553
2618
|
else
|
|
2554
|
-
@index.store_term (node.attr 'terms').map {|term|
|
|
2619
|
+
@index.store_term (node.attr 'terms').map {|term| FormattedString.new parse_text term, inline_format: [normalize: true] }, dest
|
|
2555
2620
|
anchor
|
|
2556
2621
|
end
|
|
2557
2622
|
end
|
|
@@ -2578,8 +2643,6 @@ module Asciidoctor
|
|
|
2578
2643
|
end
|
|
2579
2644
|
|
|
2580
2645
|
def convert_inline_quoted node
|
|
2581
|
-
theme = load_theme node.document
|
|
2582
|
-
|
|
2583
2646
|
case node.type
|
|
2584
2647
|
when :emphasis
|
|
2585
2648
|
open, close, is_tag = ['<em>', '</em>', true]
|
|
@@ -2592,25 +2655,26 @@ module Asciidoctor
|
|
|
2592
2655
|
when :subscript
|
|
2593
2656
|
open, close, is_tag = ['<sub>', '</sub>', true]
|
|
2594
2657
|
when :double
|
|
2595
|
-
open, close
|
|
2658
|
+
open, close = (load_theme node.document).quotes.slice 0, 2
|
|
2596
2659
|
quotes = true
|
|
2597
2660
|
when :single
|
|
2598
|
-
open, close
|
|
2661
|
+
open, close = (load_theme node.document).quotes.slice 2, 2
|
|
2599
2662
|
quotes = true
|
|
2600
2663
|
when :mark
|
|
2601
2664
|
open, close, is_tag = ['<mark>', '</mark>', true]
|
|
2602
2665
|
else
|
|
2603
|
-
open
|
|
2666
|
+
open = close = ''
|
|
2604
2667
|
end
|
|
2605
2668
|
|
|
2606
2669
|
inner_text = node.text
|
|
2607
2670
|
|
|
2608
|
-
if quotes && (len = inner_text.length) > 3 &&
|
|
2609
|
-
|
|
2671
|
+
if quotes && (len = inner_text.length) > 3 && (inner_text.end_with? '...') &&
|
|
2672
|
+
!((inner_text_trunc = inner_text.slice 0, len - 3).end_with? ?\\)
|
|
2610
2673
|
inner_text = inner_text_trunc + '…'
|
|
2611
2674
|
end
|
|
2612
2675
|
|
|
2613
2676
|
if (roles = node.role)
|
|
2677
|
+
theme = load_theme node.document
|
|
2614
2678
|
roles.split.each do |role|
|
|
2615
2679
|
if (text_transform = theme[%(role_#{role}_text_transform)])
|
|
2616
2680
|
inner_text = transform_text inner_text, text_transform
|
|
@@ -3871,6 +3935,10 @@ module Asciidoctor
|
|
|
3871
3935
|
{ width: 0, height: 0 }
|
|
3872
3936
|
end
|
|
3873
3937
|
|
|
3938
|
+
def intrinsic_image_width path, format
|
|
3939
|
+
(intrinsic_image_dimensions path, format)[:width]
|
|
3940
|
+
end
|
|
3941
|
+
|
|
3874
3942
|
# Sends the specified message to the log unless this method is called from the scratch document
|
|
3875
3943
|
def log severity, message = nil, &block
|
|
3876
3944
|
logger.send severity, message, &block unless scratch?
|
|
@@ -4042,11 +4110,15 @@ module Asciidoctor
|
|
|
4042
4110
|
if attrs.key? 'pdfwidth'
|
|
4043
4111
|
if (width = attrs['pdfwidth']).end_with? '%'
|
|
4044
4112
|
bounds_width ? (width.to_f / 100) * bounds_width : width
|
|
4113
|
+
elsif width.end_with? 'iw'
|
|
4114
|
+
(width.chomp 'iw').extend ImageWidth
|
|
4045
4115
|
elsif opts[:support_vw] && (width.end_with? 'vw')
|
|
4046
4116
|
(width.chomp 'vw').extend ViewportWidth
|
|
4047
4117
|
else
|
|
4048
4118
|
str_to_pt width
|
|
4049
4119
|
end
|
|
4120
|
+
elsif attrs.key? 'scale'
|
|
4121
|
+
attrs['scale'].dup.extend ImageWidth
|
|
4050
4122
|
elsif attrs.key? 'scaledwidth'
|
|
4051
4123
|
# NOTE: the parser automatically appends % if value is unitless
|
|
4052
4124
|
if (width = attrs['scaledwidth']).end_with? '%'
|
|
@@ -30,4 +30,8 @@ class Asciidoctor::Section
|
|
|
30
30
|
end
|
|
31
31
|
opts[:formal] ? @cached_formal_numbered_title : @cached_numbered_title
|
|
32
32
|
end unless method_defined? :numbered_title
|
|
33
|
+
|
|
34
|
+
def first_section_of_part?
|
|
35
|
+
(par = @parent).context == :section && par.sectname == 'part' && self == par.blocks.find {|it| it.context == :section }
|
|
36
|
+
end unless method_defined? :first_section_of_part?
|
|
33
37
|
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class FormattedString < String
|
|
4
|
+
attr_reader :fragments
|
|
5
|
+
|
|
6
|
+
def initialize fragments
|
|
7
|
+
super [].tap {|accum| (@fragments = fragments).each {|it| accum << it[:text] } }.join
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def eql? other
|
|
11
|
+
super && (FormattedString === other ? (@fragments ||= nil) == other.fragments : true)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -51,6 +51,9 @@ module Asciidoctor::PDF::FormattedText
|
|
|
51
51
|
image_format = fragment[:image_format]
|
|
52
52
|
if (image_w = fragment[:image_width] || '100%') == 'auto'
|
|
53
53
|
image_w = nil # use intrinsic width
|
|
54
|
+
elsif image_w.start_with? 'auto*'
|
|
55
|
+
image_scale = (image_w.slice 5, image_w.length).to_f
|
|
56
|
+
image_w = nil # use intrinsic width
|
|
54
57
|
elsif (pctidx = image_w.index '%') && pctidx + 1 < image_w.length
|
|
55
58
|
# NOTE: intrinsic width is stored behind % symbol
|
|
56
59
|
pct = (image_w.slice 0, pctidx).to_f / 100
|
|
@@ -79,6 +82,7 @@ module Asciidoctor::PDF::FormattedText
|
|
|
79
82
|
image_w = svg_size.output_width
|
|
80
83
|
else
|
|
81
84
|
fragment[:image_width] = (image_w = svg_size.output_width).to_s
|
|
85
|
+
image_w *= image_scale if image_scale
|
|
82
86
|
image_w = available_w if image_w > available_w
|
|
83
87
|
end
|
|
84
88
|
fragment[:image_obj] = svg_obj
|
|
@@ -88,6 +92,7 @@ module Asciidoctor::PDF::FormattedText
|
|
|
88
92
|
image_obj, image_info = ::File.open(image_path, 'rb') {|fd| doc.build_image_object fd }
|
|
89
93
|
unless image_w
|
|
90
94
|
fragment[:image_width] = (image_w = to_pt image_info.width, :px).to_s
|
|
95
|
+
image_w *= image_scale if image_scale
|
|
91
96
|
image_w = available_w if image_w > available_w
|
|
92
97
|
end
|
|
93
98
|
if (image_h = image_w * (image_info.height.fdiv image_info.width)) > max_image_h
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: asciidoctor-pdf
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.0
|
|
4
|
+
version: 2.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dan Allen
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2022-06-
|
|
12
|
+
date: 2022-06-11 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: asciidoctor
|
|
@@ -286,6 +286,7 @@ files:
|
|
|
286
286
|
- lib/asciidoctor/pdf/ext/rouge.rb
|
|
287
287
|
- lib/asciidoctor/pdf/ext/rouge/formatters/prawn.rb
|
|
288
288
|
- lib/asciidoctor/pdf/ext/rouge/themes/asciidoctor_pdf_default.rb
|
|
289
|
+
- lib/asciidoctor/pdf/formatted_string.rb
|
|
289
290
|
- lib/asciidoctor/pdf/formatted_text.rb
|
|
290
291
|
- lib/asciidoctor/pdf/formatted_text/formatter.rb
|
|
291
292
|
- lib/asciidoctor/pdf/formatted_text/fragment_position_renderer.rb
|