asciidoctor-pdf 1.5.0.alpha.16 → 1.5.0.alpha.17
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.yardopts +12 -0
- data/CHANGELOG.adoc +66 -0
- data/LICENSE.adoc +1 -1
- data/README.adoc +221 -68
- data/asciidoctor-pdf.gemspec +41 -42
- data/bin/asciidoctor-pdf +3 -3
- 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/default-theme.yml +6 -3
- data/docs/theming-guide.adoc +162 -23
- data/lib/asciidoctor-pdf.rb +2 -1
- data/lib/asciidoctor-pdf/asciidoctor_ext.rb +1 -0
- data/lib/asciidoctor-pdf/asciidoctor_ext/logging_shim.rb +19 -0
- data/lib/asciidoctor-pdf/converter.rb +408 -186
- data/lib/asciidoctor-pdf/core_ext/array.rb +0 -6
- data/lib/asciidoctor-pdf/core_ext/numeric.rb +21 -12
- data/lib/asciidoctor-pdf/core_ext/ostruct.rb +3 -12
- data/lib/asciidoctor-pdf/core_ext/string.rb +1 -1
- data/lib/asciidoctor-pdf/formatted_text.rb +1 -0
- data/lib/asciidoctor-pdf/formatted_text/formatter.rb +8 -2
- data/lib/asciidoctor-pdf/formatted_text/inline_destination_marker.rb +1 -1
- data/lib/asciidoctor-pdf/formatted_text/inline_image_arranger.rb +18 -32
- data/lib/asciidoctor-pdf/formatted_text/inline_image_renderer.rb +3 -3
- data/lib/asciidoctor-pdf/formatted_text/inline_text_aligner.rb +20 -0
- data/lib/asciidoctor-pdf/formatted_text/parser.rb +124 -38
- data/lib/asciidoctor-pdf/formatted_text/parser.treetop +17 -10
- data/lib/asciidoctor-pdf/formatted_text/transform.rb +30 -20
- data/lib/asciidoctor-pdf/implicit_header_processor.rb +2 -2
- data/lib/asciidoctor-pdf/index_catalog.rb +25 -23
- data/lib/asciidoctor-pdf/measurements.rb +1 -1
- data/lib/asciidoctor-pdf/pdf-core_ext/pdf_object.rb +1 -1
- data/lib/asciidoctor-pdf/pdfmark.rb +13 -13
- data/lib/asciidoctor-pdf/prawn-svg_ext.rb +2 -2
- data/lib/asciidoctor-pdf/prawn-svg_ext/interface.rb +2 -2
- data/lib/asciidoctor-pdf/prawn-table_ext.rb +1 -0
- data/lib/asciidoctor-pdf/prawn-table_ext/cell.rb +60 -0
- data/lib/asciidoctor-pdf/prawn-table_ext/cell/text.rb +3 -3
- data/lib/asciidoctor-pdf/prawn_ext/coderay_encoder.rb +3 -3
- data/lib/asciidoctor-pdf/prawn_ext/extensions.rb +39 -14
- data/lib/asciidoctor-pdf/prawn_ext/formatted_text/fragment.rb +9 -10
- data/lib/asciidoctor-pdf/prawn_ext/images.rb +2 -2
- data/lib/asciidoctor-pdf/roman_numeral.rb +7 -7
- data/lib/asciidoctor-pdf/rouge_ext.rb +2 -2
- data/lib/asciidoctor-pdf/rouge_ext/formatters/prawn.rb +20 -9
- data/lib/asciidoctor-pdf/rouge_ext/themes/{pastie.rb → asciidoctor_pdf_default.rb} +5 -5
- data/lib/asciidoctor-pdf/rouge_ext/themes/bw.rb +38 -0
- data/lib/asciidoctor-pdf/sanitizer.rb +36 -23
- data/lib/asciidoctor-pdf/temporary_path.rb +1 -1
- data/lib/asciidoctor-pdf/theme_loader.rb +17 -14
- data/lib/asciidoctor-pdf/version.rb +3 -2
- data/lib/asciidoctor/pdf.rb +1 -0
- data/lib/asciidoctor/pdf/version.rb +1 -0
- metadata +113 -84
- data/Gemfile +0 -22
- data/Rakefile +0 -81
- data/lib/asciidoctor-pdf/rouge_ext/css_theme.rb +0 -15
@@ -1,6 +1,6 @@
|
|
1
1
|
# regenerate parser.rb using `tt parser.treetop`
|
2
2
|
module Asciidoctor
|
3
|
-
module
|
3
|
+
module PDF
|
4
4
|
module FormattedText
|
5
5
|
grammar Markup
|
6
6
|
rule text
|
@@ -8,7 +8,7 @@ grammar Markup
|
|
8
8
|
end
|
9
9
|
|
10
10
|
rule complex
|
11
|
-
(cdata / element /
|
11
|
+
(cdata / element / charref)* {
|
12
12
|
def content
|
13
13
|
elements.map {|e| e.content }
|
14
14
|
end
|
@@ -95,25 +95,32 @@ grammar Markup
|
|
95
95
|
}
|
96
96
|
end
|
97
97
|
|
98
|
-
rule
|
99
|
-
'&' ('#'
|
98
|
+
rule charref
|
99
|
+
'&' ('#' character_decimal / '#x' character_hex / character_name) ';' {
|
100
100
|
def content
|
101
|
-
if (
|
102
|
-
{ type: :
|
101
|
+
if (ref_data = elements[1]).terminal?
|
102
|
+
{ type: :charref, reference_type: :name, value: ref_data.text_value.to_sym }
|
103
|
+
elsif ref_data.elements[0].text_value == '#'
|
104
|
+
{ type: :charref, reference_type: :decimal, value: ref_data.elements[1].text_value.to_i }
|
103
105
|
else
|
104
|
-
{ type: :
|
106
|
+
{ type: :charref, reference_type: :hex, value: ref_data.elements[1].text_value }
|
105
107
|
end
|
106
108
|
end
|
107
109
|
}
|
108
110
|
end
|
109
111
|
|
110
|
-
rule
|
112
|
+
rule character_decimal
|
111
113
|
# NOTE 6 decimals only supported in Asciidoctor 1.5.5 and up
|
112
114
|
[0-9] 2..6
|
113
115
|
end
|
114
116
|
|
115
|
-
rule
|
116
|
-
|
117
|
+
rule character_hex
|
118
|
+
# NOTE 5 hexadecimals only supported in Asciidoctor 1.5.5 and up
|
119
|
+
[0-9a-f] 2..5
|
120
|
+
end
|
121
|
+
|
122
|
+
rule character_name
|
123
|
+
'amp' / 'apos' / 'gt' / 'lt' / 'nbsp' / 'quot'
|
117
124
|
end
|
118
125
|
|
119
126
|
rule spaces
|
@@ -1,18 +1,20 @@
|
|
1
1
|
module Asciidoctor
|
2
|
-
module
|
2
|
+
module PDF
|
3
3
|
module FormattedText
|
4
4
|
class Transform
|
5
|
-
LF =
|
5
|
+
LF = ?\n
|
6
|
+
ZeroWidthSpace = ?\u200b
|
6
7
|
CharEntityTable = {
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
amp: ?&,
|
9
|
+
apos: ?',
|
10
|
+
gt: ?>,
|
11
|
+
lt: ?<,
|
12
|
+
nbsp: ?\u00a0,
|
13
|
+
quot: ?",
|
12
14
|
}
|
13
|
-
CharRefRx = /&(
|
15
|
+
CharRefRx = /&(?:(#{CharEntityTable.keys * ?|})|#(?:(\d\d\d{0,4})|x([a-f\d][a-f\d][a-f\d]{0,3})));/
|
14
16
|
TextDecorationTable = { 'underline' => :underline, 'line-through' => :strikethrough }
|
15
|
-
#DummyText =
|
17
|
+
#DummyText = ?\u0000
|
16
18
|
|
17
19
|
def initialize(options = {})
|
18
20
|
@merge_adjacent_text_nodes = options[:merge_adjacent_text_nodes]
|
@@ -75,7 +77,8 @@ class Transform
|
|
75
77
|
fragment = {
|
76
78
|
image_path: attributes[:tmp] == 'true' ? attributes[:src].extend(TemporaryPath) : attributes[:src],
|
77
79
|
image_format: attributes[:format],
|
78
|
-
text
|
80
|
+
# a zero-width space in the text will cause the image to be duplicated
|
81
|
+
text: (attributes[:alt].delete ZeroWidthSpace),
|
79
82
|
callback: InlineImageRenderer
|
80
83
|
}
|
81
84
|
if (img_w = attributes[:width])
|
@@ -86,22 +89,22 @@ class Transform
|
|
86
89
|
end
|
87
90
|
end
|
88
91
|
when :text
|
89
|
-
text = node[:value]
|
90
|
-
# NOTE the remaining logic is shared with :entity
|
91
92
|
if @merge_adjacent_text_nodes && previous_fragment_is_text
|
92
|
-
fragments << { text: %(#{fragments.pop[:text]}#{
|
93
|
+
fragments << { text: %(#{fragments.pop[:text]}#{node[:value]}) }
|
93
94
|
else
|
94
|
-
fragments << { text:
|
95
|
+
fragments << { text: node[:value] }
|
95
96
|
end
|
96
97
|
previous_fragment_is_text = true
|
97
|
-
when :
|
98
|
-
if (
|
99
|
-
text = CharEntityTable[
|
98
|
+
when :charref
|
99
|
+
if (ref_type = node[:reference_type]) == :name
|
100
|
+
text = CharEntityTable[node[:value]]
|
101
|
+
elsif ref_type == :decimal
|
102
|
+
# FIXME AFM fonts do not include a thin space glyph; set fallback_fonts to allow glyph to be resolved
|
103
|
+
text = [node[:value]].pack('U1')
|
100
104
|
else
|
101
105
|
# FIXME AFM fonts do not include a thin space glyph; set fallback_fonts to allow glyph to be resolved
|
102
|
-
text = [node[:
|
106
|
+
text = [(node[:value].to_i 16)].pack('U1')
|
103
107
|
end
|
104
|
-
# NOTE the remaining logic is shared with :text
|
105
108
|
if @merge_adjacent_text_nodes && previous_fragment_is_text
|
106
109
|
fragments << { text: %(#{fragments.pop[:text]}#{text}) }
|
107
110
|
else
|
@@ -161,6 +164,13 @@ class Transform
|
|
161
164
|
fragment[:size] = value
|
162
165
|
end
|
163
166
|
end
|
167
|
+
if (value = attrs[:width])
|
168
|
+
fragment[:width] = value
|
169
|
+
if (value = attrs[:align])
|
170
|
+
fragment[:align] = value.to_sym
|
171
|
+
fragment[:callback] = InlineTextAligner
|
172
|
+
end
|
173
|
+
end
|
164
174
|
#if !fragment[:character_spacing] && (value = attrs[:character_spacing])
|
165
175
|
# fragment[:character_spacing] = value.to_f
|
166
176
|
#end
|
@@ -173,7 +183,7 @@ class Transform
|
|
173
183
|
fragment[:anchor] = value
|
174
184
|
elsif (value = attrs[:href])
|
175
185
|
fragment[:link] = value.include?(';') ? value.gsub(CharRefRx) {
|
176
|
-
$
|
186
|
+
$1 ? CharEntityTable[$1.to_sym] : [$2 ? $2.to_i : ($3.to_i 16)].pack('U1')
|
177
187
|
} : value
|
178
188
|
elsif (value = attrs[:name])
|
179
189
|
# NOTE text is null character, which is used as placeholder text so Prawn doesn't drop fragment
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'asciidoctor/extensions'
|
2
2
|
|
3
3
|
module Asciidoctor
|
4
|
-
module
|
4
|
+
module PDF
|
5
5
|
# An include processor that skips the implicit author line below
|
6
6
|
# the document title within include documents.
|
7
7
|
class ImplicitHeaderProcessor < ::Asciidoctor::Extensions::IncludeProcessor
|
@@ -59,5 +59,5 @@ end
|
|
59
59
|
end
|
60
60
|
|
61
61
|
Asciidoctor::Extensions.register :pdf do
|
62
|
-
include_processor Asciidoctor::
|
62
|
+
include_processor Asciidoctor::PDF::ImplicitHeaderProcessor if @document.backend == 'pdf'
|
63
63
|
end
|
@@ -1,5 +1,7 @@
|
|
1
|
-
module Asciidoctor; module
|
1
|
+
module Asciidoctor; module PDF
|
2
2
|
class IndexCatalog
|
3
|
+
LeadingAlphaRx = /^\p{Alpha}/
|
4
|
+
|
3
5
|
attr_accessor :start_page_number
|
4
6
|
|
5
7
|
def initialize
|
@@ -7,7 +9,7 @@ module Asciidoctor; module Pdf
|
|
7
9
|
@start_page_number = 1
|
8
10
|
@dests = {}
|
9
11
|
end
|
10
|
-
|
12
|
+
|
11
13
|
def store_term names, dest = nil
|
12
14
|
if (num_terms = names.size) > 2
|
13
15
|
store_tertiary_term names[0], names[1], names[2], dest
|
@@ -17,27 +19,27 @@ module Asciidoctor; module Pdf
|
|
17
19
|
store_primary_term names[0], dest
|
18
20
|
end
|
19
21
|
end
|
20
|
-
|
22
|
+
|
21
23
|
def store_primary_term name, dest = nil
|
22
24
|
store_dest dest if dest
|
23
25
|
(init_category name.chr.upcase).store_term name, dest
|
24
26
|
end
|
25
|
-
|
27
|
+
|
26
28
|
def store_secondary_term primary_name, secondary_name, dest = nil
|
27
29
|
store_dest dest if dest
|
28
30
|
(store_primary_term primary_name).store_term secondary_name, dest
|
29
31
|
end
|
30
|
-
|
32
|
+
|
31
33
|
def store_tertiary_term primary_name, secondary_name, tertiary_name, dest = nil
|
32
34
|
store_dest dest if dest
|
33
35
|
(store_secondary_term primary_name, secondary_name).store_term tertiary_name, dest
|
34
36
|
end
|
35
|
-
|
37
|
+
|
36
38
|
def init_category name
|
37
|
-
name = '@'
|
39
|
+
name = '@' unless LeadingAlphaRx =~ name
|
38
40
|
@categories[name] ||= (IndexTermCategory.new name)
|
39
41
|
end
|
40
|
-
|
42
|
+
|
41
43
|
def find_category name
|
42
44
|
@categories[name]
|
43
45
|
end
|
@@ -51,7 +53,7 @@ module Asciidoctor; module Pdf
|
|
51
53
|
dest[:page] = physical_page_number - (@start_page_number - 1)
|
52
54
|
end
|
53
55
|
end
|
54
|
-
|
56
|
+
|
55
57
|
def empty?
|
56
58
|
@categories.empty?
|
57
59
|
end
|
@@ -60,26 +62,26 @@ module Asciidoctor; module Pdf
|
|
60
62
|
@categories.empty? ? [] : @categories.values.sort
|
61
63
|
end
|
62
64
|
end
|
63
|
-
|
65
|
+
|
64
66
|
class IndexTermGroup
|
65
67
|
include Comparable
|
66
68
|
attr_reader :name
|
67
|
-
|
69
|
+
|
68
70
|
def initialize name
|
69
|
-
@name = name
|
71
|
+
@name = name
|
70
72
|
@terms = {}
|
71
73
|
end
|
72
|
-
|
74
|
+
|
73
75
|
def store_term name, dest = nil
|
74
76
|
term = (@terms[name] ||= (IndexTerm.new name))
|
75
77
|
term.add_dest dest if dest
|
76
78
|
term
|
77
79
|
end
|
78
|
-
|
80
|
+
|
79
81
|
def find_term name
|
80
82
|
@terms[name]
|
81
83
|
end
|
82
|
-
|
84
|
+
|
83
85
|
def terms
|
84
86
|
@terms.empty? ? [] : @terms.values.sort
|
85
87
|
end
|
@@ -88,30 +90,30 @@ module Asciidoctor; module Pdf
|
|
88
90
|
@name <=> other.name
|
89
91
|
end
|
90
92
|
end
|
91
|
-
|
93
|
+
|
92
94
|
class IndexTermCategory < IndexTermGroup; end
|
93
|
-
|
95
|
+
|
94
96
|
class IndexTerm < IndexTermGroup
|
95
97
|
def initialize name
|
96
98
|
super
|
97
99
|
@dests = ::Set.new
|
98
100
|
end
|
99
|
-
|
100
|
-
alias
|
101
|
-
|
101
|
+
|
102
|
+
alias subterms terms
|
103
|
+
|
102
104
|
def add_dest dest
|
103
105
|
@dests << dest
|
104
106
|
self
|
105
107
|
end
|
106
|
-
|
108
|
+
|
107
109
|
def dests
|
108
110
|
@dests.select {|d| d.key? :page }.sort {|a, b| a[:page] <=> b[:page] }
|
109
111
|
end
|
110
|
-
|
112
|
+
|
111
113
|
def container?
|
112
114
|
@dests.empty? || @dests.none? {|d| d.key? :page }
|
113
115
|
end
|
114
|
-
|
116
|
+
|
115
117
|
def leaf?
|
116
118
|
@terms.empty?
|
117
119
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module Asciidoctor
|
2
|
-
module
|
2
|
+
module PDF
|
3
3
|
class Pdfmark
|
4
|
-
include ::Asciidoctor::
|
4
|
+
include ::Asciidoctor::PDF::Sanitizer
|
5
5
|
|
6
6
|
def initialize doc
|
7
7
|
@doc = doc
|
@@ -10,23 +10,23 @@ class Pdfmark
|
|
10
10
|
def generate
|
11
11
|
doc = @doc
|
12
12
|
# FIXME use sanitize: :plain_text once available
|
13
|
-
content =
|
14
|
-
[ /Title #{sanitize(doc.doctitle use_fallback: true).to_pdf}
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
13
|
+
content = <<~EOS
|
14
|
+
[ /Title #{sanitize(doc.doctitle use_fallback: true).to_pdf}
|
15
|
+
/Author #{(doc.attr 'authors').to_pdf}
|
16
|
+
/Subject #{(doc.attr 'subject').to_pdf}
|
17
|
+
/Keywords #{(doc.attr 'keywords').to_pdf}
|
18
|
+
/ModDate #{date = ::Time.now.to_pdf}
|
19
|
+
/CreationDate #{date}
|
20
|
+
/Creator (Asciidoctor PDF #{::Asciidoctor::PDF::VERSION}, based on Prawn #{::Prawn::VERSION})
|
21
|
+
/Producer #{(doc.attr 'publisher').to_pdf}
|
22
|
+
/DOCINFO pdfmark
|
23
23
|
EOS
|
24
24
|
content
|
25
25
|
end
|
26
26
|
|
27
27
|
def generate_file pdf_file
|
28
28
|
# QUESTION should we use the extension pdfmeta to be more clear?
|
29
|
-
::
|
29
|
+
::File.write %(#{pdf_file}mark), generate
|
30
30
|
end
|
31
31
|
end
|
32
32
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'prawn-svg' unless defined? Prawn::
|
1
|
+
require 'prawn-svg' unless defined? Prawn::SVG::Interface
|
2
2
|
require_relative 'prawn-svg_ext/interface'
|
3
3
|
# NOTE disable system fonts since they're non-portable
|
4
|
-
Prawn::
|
4
|
+
Prawn::SVG::Interface.font_path.clear
|
@@ -1,4 +1,4 @@
|
|
1
|
-
module Prawn; module
|
1
|
+
module Prawn; module SVG
|
2
2
|
class Interface
|
3
3
|
def resize opts = {}
|
4
4
|
sizing = document.sizing
|
@@ -7,4 +7,4 @@ module Prawn; module Svg
|
|
7
7
|
sizing.calculate
|
8
8
|
end
|
9
9
|
end
|
10
|
-
end; end unless Prawn::
|
10
|
+
end; end unless Prawn::SVG::Interface.method_defined? :resize
|
@@ -0,0 +1,60 @@
|
|
1
|
+
class Prawn::Table::Cell
|
2
|
+
remove_method :draw_borders
|
3
|
+
# Draws borders around the cell. Borders are centered on the bounds of
|
4
|
+
# the cell outside of any padding, so the caller is responsible for
|
5
|
+
# setting appropriate padding to ensure the border does not overlap with
|
6
|
+
# cell content.
|
7
|
+
#
|
8
|
+
def draw_borders(pt)
|
9
|
+
x, y = pt
|
10
|
+
|
11
|
+
@pdf.mask(:line_width, :stroke_color) do
|
12
|
+
@borders.each do |border|
|
13
|
+
idx = { top: 0, right: 1, bottom: 2, left: 3 }[border]
|
14
|
+
border_color = @border_colors[idx]
|
15
|
+
border_width = @border_widths[idx]
|
16
|
+
border_line = @border_lines[idx]
|
17
|
+
|
18
|
+
next unless border_width > 0
|
19
|
+
|
20
|
+
# Left and right borders are drawn one-half border beyond the center
|
21
|
+
# of the corner, so that the corners end up square.
|
22
|
+
from, to = case border
|
23
|
+
when :top
|
24
|
+
[[x, y], [x+width, y]]
|
25
|
+
when :bottom
|
26
|
+
[[x, y-height], [x+width, y-height]]
|
27
|
+
when :left
|
28
|
+
[[x, y + (border_top_width / 2.0)],
|
29
|
+
[x, y - height - (border_bottom_width / 2.0)]]
|
30
|
+
when :right
|
31
|
+
[[x+width, y + (border_top_width / 2.0)],
|
32
|
+
[x+width, y - height - (border_bottom_width / 2.0)]]
|
33
|
+
end
|
34
|
+
|
35
|
+
case border_line
|
36
|
+
when :dashed
|
37
|
+
@pdf.dash border_width * 4
|
38
|
+
when :dotted
|
39
|
+
@pdf.dash border_width, space: border_width * 2
|
40
|
+
when :solid
|
41
|
+
# normal line style
|
42
|
+
else
|
43
|
+
raise ::ArgumentError, 'border_line must be :solid, :dotted or :dashed'
|
44
|
+
end
|
45
|
+
|
46
|
+
@pdf.line_width = border_width
|
47
|
+
if border_color == 'transparent'
|
48
|
+
@pdf.stroke_color = '000000'
|
49
|
+
@pdf.transparent 0 do
|
50
|
+
@pdf.stroke_line(from, to)
|
51
|
+
end
|
52
|
+
else
|
53
|
+
@pdf.stroke_color = border_color
|
54
|
+
@pdf.stroke_line(from, to)
|
55
|
+
end
|
56
|
+
@pdf.undash
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -4,9 +4,9 @@ class Prawn::Table::Cell::Text
|
|
4
4
|
def draw_content
|
5
5
|
with_font do
|
6
6
|
with_text_color do
|
7
|
-
(text_box :
|
8
|
-
:
|
9
|
-
:
|
7
|
+
(text_box width: spanned_content_width + FPTolerance,
|
8
|
+
height: spanned_content_height + FPTolerance,
|
9
|
+
at: [0, @pdf.cursor]).render
|
10
10
|
end
|
11
11
|
end
|
12
12
|
end
|