asciidoctor-pdf 1.5.0.beta.8 → 1.5.0.rc.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +49 -0
- data/LICENSE.adoc +1 -1
- data/NOTICE.adoc +1 -1
- data/README.adoc +43 -47
- data/asciidoctor-pdf.gemspec +5 -1
- data/bin/asciidoctor-pdf-optimize +1 -1
- data/data/themes/base-theme.yml +4 -3
- data/data/themes/default-theme.yml +10 -5
- data/docs/theming-guide.adoc +286 -22
- data/lib/asciidoctor-pdf.rb +1 -0
- data/lib/asciidoctor-pdf/converter.rb +1 -0
- data/lib/asciidoctor-pdf/version.rb +1 -0
- data/lib/asciidoctor/pdf.rb +13 -2
- data/lib/asciidoctor/pdf/converter.rb +3962 -3955
- data/lib/asciidoctor/pdf/ext.rb +9 -0
- data/lib/asciidoctor/pdf/ext/asciidoctor.rb +1 -0
- data/lib/asciidoctor/pdf/ext/asciidoctor/abstract_block.rb +1 -0
- data/lib/asciidoctor/pdf/ext/asciidoctor/abstract_node.rb +1 -0
- data/lib/asciidoctor/pdf/ext/asciidoctor/document.rb +1 -0
- data/lib/asciidoctor/pdf/ext/asciidoctor/image.rb +18 -16
- data/lib/asciidoctor/pdf/ext/asciidoctor/list.rb +3 -2
- data/lib/asciidoctor/pdf/ext/asciidoctor/list_item.rb +2 -1
- data/lib/asciidoctor/pdf/ext/asciidoctor/logging_shim.rb +3 -4
- data/lib/asciidoctor/pdf/ext/asciidoctor/section.rb +8 -6
- data/lib/asciidoctor/pdf/ext/core.rb +2 -0
- data/lib/asciidoctor/pdf/ext/core/array.rb +1 -0
- data/lib/asciidoctor/pdf/ext/core/hash.rb +1 -0
- data/lib/asciidoctor/pdf/ext/core/numeric.rb +4 -3
- data/lib/asciidoctor/pdf/ext/core/object.rb +1 -0
- data/lib/asciidoctor/pdf/ext/core/quantifiable_stdout.rb +8 -1
- data/lib/asciidoctor/pdf/ext/core/regexp.rb +1 -0
- data/lib/asciidoctor/pdf/ext/core/string.rb +6 -7
- data/lib/asciidoctor/pdf/ext/pdf-core.rb +1 -0
- data/lib/asciidoctor/pdf/ext/pdf-core/page.rb +3 -4
- data/lib/asciidoctor/pdf/ext/pdf-core/pdf_object.rb +2 -1
- data/lib/asciidoctor/pdf/ext/prawn-svg.rb +1 -0
- data/lib/asciidoctor/pdf/ext/prawn-svg/interface.rb +11 -8
- data/lib/asciidoctor/pdf/ext/prawn-table.rb +2 -1
- data/lib/asciidoctor/pdf/ext/prawn-table/cell.rb +9 -10
- data/lib/asciidoctor/pdf/ext/prawn-table/cell/asciidoc.rb +62 -57
- data/lib/asciidoctor/pdf/ext/prawn-table/cell/text.rb +5 -3
- data/lib/asciidoctor/pdf/ext/prawn-templates.rb +1 -0
- data/lib/asciidoctor/pdf/ext/prawn.rb +1 -0
- data/lib/asciidoctor/pdf/ext/prawn/coderay_encoder.rb +73 -72
- data/lib/asciidoctor/pdf/ext/prawn/extensions.rb +814 -818
- data/lib/asciidoctor/pdf/ext/prawn/font/afm.rb +4 -3
- data/lib/asciidoctor/pdf/ext/prawn/formatted_text/box.rb +2 -1
- data/lib/asciidoctor/pdf/ext/prawn/formatted_text/fragment.rb +7 -2
- data/lib/asciidoctor/pdf/ext/prawn/images.rb +45 -44
- data/lib/asciidoctor/pdf/ext/pygments.rb +34 -0
- data/lib/asciidoctor/pdf/ext/rouge.rb +1 -1
- data/lib/asciidoctor/pdf/ext/rouge/formatters/prawn.rb +181 -149
- data/lib/asciidoctor/pdf/ext/rouge/themes/asciidoctor_pdf_default.rb +1 -0
- data/lib/asciidoctor/pdf/formatted_text.rb +2 -0
- data/lib/asciidoctor/pdf/formatted_text/formatter.rb +35 -34
- data/lib/asciidoctor/pdf/formatted_text/fragment_position_renderer.rb +8 -7
- data/lib/asciidoctor/pdf/formatted_text/inline_destination_marker.rb +13 -14
- data/lib/asciidoctor/pdf/formatted_text/inline_image_arranger.rb +112 -133
- data/lib/asciidoctor/pdf/formatted_text/inline_image_renderer.rb +43 -41
- data/lib/asciidoctor/pdf/formatted_text/inline_text_aligner.rb +15 -14
- data/lib/asciidoctor/pdf/formatted_text/source_wrap.rb +43 -0
- data/lib/asciidoctor/pdf/formatted_text/text_background_and_border_renderer.rb +46 -37
- data/lib/asciidoctor/pdf/formatted_text/transform.rb +371 -352
- data/lib/asciidoctor/pdf/index_catalog.rb +99 -95
- data/lib/asciidoctor/pdf/measurements.rb +51 -48
- data/lib/asciidoctor/pdf/optimizer.rb +34 -31
- data/lib/asciidoctor/pdf/pdfmark.rb +34 -33
- data/lib/asciidoctor/pdf/roman_numeral.rb +80 -79
- data/lib/asciidoctor/pdf/sanitizer.rb +38 -37
- data/lib/asciidoctor/pdf/temporary_path.rb +10 -9
- data/lib/asciidoctor/pdf/text_transformer.rb +101 -100
- data/lib/asciidoctor/pdf/theme_loader.rb +258 -256
- data/lib/asciidoctor/pdf/version.rb +5 -4
- metadata +55 -6
- data/lib/asciidoctor/pdf/ext/rouge/themes/bw.rb +0 -39
- data/lib/asciidoctor/pdf/ext/ttfunk.rb +0 -9
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
class Prawn::Font::AFM
|
3
4
|
if defined? ::Asciidoctor::Logging
|
4
5
|
include ::Asciidoctor::Logging
|
@@ -11,7 +12,7 @@ class Prawn::Font::AFM
|
|
11
12
|
?\u202f => ?\u00a0,
|
12
13
|
?\u2009 => ' ',
|
13
14
|
?\u25e6 => '-',
|
14
|
-
?\u25aa => ?\u00b7
|
15
|
+
?\u25aa => ?\u00b7,
|
15
16
|
}
|
16
17
|
|
17
18
|
remove_method :normalize_encoding
|
@@ -29,7 +30,7 @@ class Prawn::Font::AFM
|
|
29
30
|
text.encode 'windows-1252', undef: :replace, replace: ?\u00ac
|
30
31
|
rescue ::Encoding::InvalidByteSequenceError
|
31
32
|
raise Prawn::Errors::IncompatibleStringEncoding,
|
32
|
-
|
33
|
-
If you need full UTF-8 support, use TTF fonts instead of the built-in PDF (AFM) fonts.
|
33
|
+
'Your document includes text which is not compatible with the Windows-1252 character set.
|
34
|
+
If you need full UTF-8 support, use TTF fonts instead of the built-in PDF (AFM) fonts.'
|
34
35
|
end
|
35
36
|
end
|
@@ -1,9 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
Prawn::Text::Formatted::Box.prepend (Module.new do
|
3
4
|
def draw_fragment_overlay_styles fragment
|
4
5
|
if (underline = (styles = fragment.styles).include? :underline) || (styles.include? :strikethrough)
|
5
6
|
(doc = fragment.document).save_graphics_state do
|
6
|
-
if (text_decoration_width = (fs = fragment.format_state)[:text_decoration_width])
|
7
|
+
if (text_decoration_width = (fs = fragment.format_state)[:text_decoration_width] || doc.text_decoration_width)
|
7
8
|
doc.line_width = text_decoration_width
|
8
9
|
end
|
9
10
|
if (text_decoration_color = fs[:text_decoration_color])
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
Prawn::Text::Formatted::Fragment.prepend (Module.new do
|
3
4
|
attr_reader :document
|
4
5
|
|
@@ -27,9 +28,13 @@ Prawn::Text::Formatted::Fragment.prepend (Module.new do
|
|
27
28
|
|
28
29
|
def width
|
29
30
|
if (val = format_state[:width])
|
30
|
-
(val.end_with? 'em') ? val.to_f * @document.font_size : val
|
31
|
+
val = (val.end_with? 'em') ? val.to_f * @document.font_size : (@document.str_to_pt val) if ::String === val
|
31
32
|
else
|
32
|
-
super
|
33
|
+
val = super
|
34
|
+
end
|
35
|
+
if (border_offset = format_state[:border_offset])
|
36
|
+
val += border_offset * 2
|
33
37
|
end
|
38
|
+
val
|
34
39
|
end
|
35
40
|
end)
|
@@ -1,53 +1,54 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Asciidoctor
|
3
|
-
module Prawn
|
4
|
-
module Images
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
4
|
+
module Prawn
|
5
|
+
module Images
|
6
|
+
class << self
|
7
|
+
def extended base
|
8
|
+
base.class.__send__ :alias_method, :_initial_image, :image
|
9
|
+
end
|
10
|
+
end
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
12
|
+
# Dispatch to suitable image method in Prawn based on file extension.
|
13
|
+
def image file, opts = {}
|
14
|
+
# FIXME: handle case when SVG is an IO object
|
15
|
+
if ::String === file && (((opts = opts.dup).delete :format) == 'svg' || (file.downcase.end_with? '.svg'))
|
16
|
+
#opts[:enable_file_requests_with_root] = (::File.dirname file) unless opts.key? :enable_file_requests_with_root
|
17
|
+
#opts[:enable_web_requests] = allow_uri_read if !(opts.key? :enable_web_requests) && (respond_to? :allow_uri_read)
|
18
|
+
#opts[:cache_images] = cache_uri if !(opts.key? :cache_images) && (respond_to? :cache_uri)
|
19
|
+
#opts[:fallback_font_name] = fallback_svg_font_name if !(opts.key? :fallback_font_name) && (respond_to? :fallback_svg_font_name)
|
20
|
+
if (opts.key? :fit) && (fit = opts.delete :fit) && !opts[:width] && !opts[:height]
|
21
|
+
svg (::File.read file, mode: 'r:UTF-8'), opts do |svg_doc|
|
22
|
+
max_width, max_height = fit
|
23
|
+
svg_doc.calculate_sizing requested_width: max_width if max_width && svg_doc.sizing.output_width != max_width
|
24
|
+
svg_doc.calculate_sizing requested_height: max_height if max_height && svg_doc.sizing.output_height > max_height
|
25
|
+
end
|
26
|
+
else
|
27
|
+
svg (::File.read file, mode: 'r:UTF-8'), opts
|
28
|
+
end
|
29
|
+
else
|
30
|
+
_initial_image file, opts
|
24
31
|
end
|
25
|
-
else
|
26
|
-
svg (::File.read file, mode: 'r:UTF-8'), opts
|
27
32
|
end
|
28
|
-
else
|
29
|
-
_initial_image file, opts
|
30
|
-
end
|
31
|
-
end
|
32
33
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
34
|
+
# Retrieve the intrinsic image dimensions for the specified path.
|
35
|
+
#
|
36
|
+
# Returns a Hash containing :width and :height keys that map to the image's
|
37
|
+
# intrinsic width and height values (in pixels)
|
38
|
+
def intrinsic_image_dimensions path, format
|
39
|
+
if format == 'svg'
|
40
|
+
img_obj = ::Prawn::SVG::Interface.new (::File.read path, mode: 'r:UTF-8'), self, {}
|
41
|
+
img_size = img_obj.document.sizing
|
42
|
+
{ width: img_size.output_width, height: img_size.output_height }
|
43
|
+
else
|
44
|
+
# NOTE build_image_object caches image data previously loaded
|
45
|
+
_, img_size = ::File.open(path, 'rb') {|fd| build_image_object fd }
|
46
|
+
{ width: img_size.width, height: img_size.height }
|
47
|
+
end
|
48
|
+
rescue
|
49
|
+
end
|
46
50
|
end
|
47
|
-
rescue
|
48
|
-
end
|
49
|
-
end
|
50
51
|
|
51
|
-
::Prawn::Document.extensions << Images
|
52
|
-
end
|
52
|
+
::Prawn::Document.extensions << Images
|
53
|
+
end
|
53
54
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'pygments.rb'
|
4
|
+
|
5
|
+
module Pygments
|
6
|
+
module Ext
|
7
|
+
module BlockStyles
|
8
|
+
BlockSelectorRx = /^\.highlight *\{([^}]+?)\}/
|
9
|
+
HighlightBackgroundColorRx = /^\.highlight +\.hll +{ *background(?:-color)?: *#([a-fA-F0-9]{6})/
|
10
|
+
HexColorRx = /^#[a-fA-F0-9]{6}$/
|
11
|
+
|
12
|
+
@cache = ::Hash.new do |cache, key|
|
13
|
+
styles = {}
|
14
|
+
if BlockSelectorRx =~ (css = ::Pygments.css '.highlight', style: key)
|
15
|
+
($1.strip.split ';').each do |style|
|
16
|
+
pname, pval = (style.split ':', 2).map(&:strip)
|
17
|
+
if pname == 'background' || pname == 'background-color'
|
18
|
+
styles[:background_color] = pval.slice 1, pval.length if HexColorRx.match? pval
|
19
|
+
elsif pname == 'color'
|
20
|
+
styles[:font_color] = pval.slice 1, pval.length if HexColorRx.match? pval
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
styles[:highlight_background_color] = $1 if HighlightBackgroundColorRx =~ css
|
25
|
+
@cache = cache.merge key => styles
|
26
|
+
styles
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.for style
|
30
|
+
@cache[style]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -1,176 +1,208 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Rouge
|
3
|
-
module Formatters
|
4
|
-
# Transforms a token stream into an array of
|
5
|
-
# formatted text fragments for use with Prawn.
|
6
|
-
class Prawn < Formatter
|
7
|
-
|
4
|
+
module Formatters
|
5
|
+
# Transforms a token stream into an array of
|
6
|
+
# formatted text fragments for use with Prawn.
|
7
|
+
class Prawn < Formatter
|
8
|
+
tag 'prawn'
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
10
|
+
Tokens = ::Rouge::Token::Tokens
|
11
|
+
LineOrientedTokens = [
|
12
|
+
::Rouge::Token::Tokens::Generic::Inserted,
|
13
|
+
::Rouge::Token::Tokens::Generic::Deleted,
|
14
|
+
::Rouge::Token::Tokens::Generic::Heading,
|
15
|
+
::Rouge::Token::Tokens::Generic::Subheading,
|
16
|
+
]
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
18
|
+
LF = ?\n
|
19
|
+
DummyText = ?\u0000
|
20
|
+
NoBreakSpace = ?\u00a0
|
21
|
+
InnerIndent = %(#{LF} )
|
22
|
+
GuardedIndent = NoBreakSpace
|
23
|
+
GuardedInnerIndent = %(#{LF}#{NoBreakSpace})
|
24
|
+
BoldStyle = [:bold].to_set
|
25
|
+
ItalicStyle = [:italic].to_set
|
26
|
+
BoldItalicStyle = [:bold, :italic].to_set
|
27
|
+
UnderlineStyle = [:underline].to_set
|
26
28
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
29
|
+
def initialize opts = {}
|
30
|
+
unless ::Rouge::Theme === (theme = opts[:theme])
|
31
|
+
unless theme && (theme = ::Rouge::Theme.find theme)
|
32
|
+
theme = ::Rouge::Themes::AsciidoctorPDFDefault
|
33
|
+
end
|
34
|
+
theme = theme.new
|
35
|
+
end
|
36
|
+
@theme = theme
|
37
|
+
@normalized_colors = {}
|
38
|
+
@background_colorizer = BackgroundColorizer.new line_gap: opts[:line_gap]
|
39
|
+
@linenum_fragment_base = create_fragment Tokens::Generic::Lineno
|
40
|
+
@highlight_line_fragment = create_highlight_line_fragment opts[:highlight_background_color]
|
31
41
|
end
|
32
|
-
theme = theme.new
|
33
|
-
end
|
34
|
-
@theme = theme
|
35
|
-
@normalized_colors = {}
|
36
|
-
@background_colorizer = BackgroundColorizer.new line_gap: opts[:line_gap]
|
37
|
-
@linenum_fragment_base = (create_fragment Tokens::Generic::Lineno).merge linenum: true
|
38
|
-
end
|
39
42
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
# Override format method so fragments don't get flatted to a string
|
45
|
-
# and to add an options Hash.
|
46
|
-
def format tokens, opts = {}
|
47
|
-
stream tokens, opts
|
48
|
-
end
|
43
|
+
def background_color
|
44
|
+
@background_color ||= (normalize_color (@theme.style_for Tokens::Text).bg)
|
45
|
+
end
|
49
46
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
else
|
55
|
-
linenum = 0
|
47
|
+
# Override format method so fragments don't get flatted to a string
|
48
|
+
# and to add an options Hash.
|
49
|
+
def format tokens, opts = {}
|
50
|
+
stream tokens, opts
|
56
51
|
end
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
52
|
+
|
53
|
+
def stream tokens, opts = {}
|
54
|
+
line_numbers = opts[:line_numbers]
|
55
|
+
highlight_lines = opts[:highlight_lines]
|
56
|
+
if line_numbers || highlight_lines
|
57
|
+
linenum = (linenum = opts[:start_line] || 1) > 0 ? linenum : 1
|
58
|
+
fragments = []
|
59
|
+
line_numbers ? (fragments << (create_linenum_fragment linenum)) : (start_of_line = true)
|
60
|
+
fragments << @highlight_line_fragment.dup if highlight_lines && highlight_lines[linenum]
|
61
|
+
tokens.each do |tok, val|
|
62
|
+
if val == LF
|
63
|
+
fragments << { text: LF }
|
64
|
+
linenum += 1
|
65
|
+
line_numbers ? (fragments << (create_linenum_fragment linenum)) : (start_of_line = true)
|
66
|
+
fragments << @highlight_line_fragment.dup if highlight_lines && highlight_lines[linenum]
|
67
|
+
elsif val.include? LF
|
68
|
+
# NOTE we assume if the fragment ends in a line feed, the intention was to match a line-oriented form
|
69
|
+
line_oriented = val.end_with? LF
|
70
|
+
base_fragment = create_fragment tok, val
|
71
|
+
val.each_line do |line|
|
72
|
+
if start_of_line
|
73
|
+
line[0] = GuardedIndent if line.start_with? ' '
|
74
|
+
start_of_line = nil
|
75
|
+
end
|
76
|
+
fragments << (line_oriented ? (base_fragment.merge text: line, inline_block: true) : (base_fragment.merge text: line))
|
77
|
+
next unless line.end_with? LF
|
78
|
+
# NOTE eagerly append linenum fragment or line highlight if there's a next line
|
79
|
+
linenum += 1
|
80
|
+
line_numbers ? (fragments << (create_linenum_fragment linenum)) : (start_of_line = true)
|
81
|
+
fragments << @highlight_line_fragment.dup if highlight_lines && highlight_lines[linenum]
|
82
|
+
end
|
83
|
+
else
|
84
|
+
if start_of_line
|
85
|
+
val[0] = GuardedIndent if val.start_with? ' '
|
86
|
+
start_of_line = nil
|
87
|
+
end
|
88
|
+
fragments << (create_fragment tok, val)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
# NOTE pad numbers that have less digits than the largest line number
|
92
|
+
# FIXME we could store these fragments so we don't have find them again
|
93
|
+
if line_numbers && (linenum_w = linenum.to_s.length) > 1
|
94
|
+
# NOTE extra column is the trailing space after the line number
|
95
|
+
linenum_w += 1
|
96
|
+
fragments.each do |fragment|
|
97
|
+
fragment[:text] = (fragment[:text].rjust linenum_w, NoBreakSpace).to_s if fragment[:linenum]
|
98
|
+
end
|
71
99
|
end
|
100
|
+
fragments
|
72
101
|
else
|
73
|
-
fragments << (create_fragment tok, val)
|
74
|
-
end
|
75
|
-
end
|
76
|
-
# NOTE drop orphaned linenum fragment (due to trailing endline in source)
|
77
|
-
fragments.pop if (last_fragment = fragments[-1]) && last_fragment[:linenum]
|
78
|
-
# NOTE pad numbers that have less digits than the largest line number
|
79
|
-
if (linenum_w = linenum.to_s.length) > 1
|
80
|
-
# NOTE extra column is the trailing space after the line number
|
81
|
-
linenum_w += 1
|
82
|
-
fragments.each do |fragment|
|
83
|
-
fragment[:text] = %(#{fragment[:text].rjust linenum_w, NoBreakSpace}) if fragment[:linenum]
|
84
|
-
end
|
85
|
-
end
|
86
|
-
fragments
|
87
|
-
else
|
88
|
-
start_of_line = true
|
89
|
-
tokens.map do |tok, val|
|
90
|
-
# match one or more consecutive endlines
|
91
|
-
if val == LF || (val == (LF * val.length))
|
92
102
|
start_of_line = true
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
+
tokens.map do |tok, val|
|
104
|
+
# match one or more consecutive endlines
|
105
|
+
if val == LF || (val == (LF * val.length))
|
106
|
+
start_of_line = true
|
107
|
+
{ text: val }
|
108
|
+
else
|
109
|
+
val[0] = GuardedIndent if start_of_line && (val.start_with? ' ')
|
110
|
+
val.gsub! InnerIndent, GuardedInnerIndent if val.include? InnerIndent
|
111
|
+
# QUESTION do we need the call to create_fragment if val contains only spaces? consider bg
|
112
|
+
#fragment = create_fragment tok, val
|
113
|
+
fragment = val.rstrip.empty? ? { text: val } : (create_fragment tok, val)
|
114
|
+
# NOTE we assume if the fragment ends in a line feed, the intention was to match a line-oriented form
|
115
|
+
fragment[:inline_block] = true if (start_of_line = val.end_with? LF)
|
116
|
+
fragment
|
117
|
+
end
|
118
|
+
end
|
103
119
|
end
|
104
120
|
end
|
105
|
-
# QUESTION should we strip trailing newline?
|
106
|
-
end
|
107
|
-
end
|
108
121
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
+
# TODO: method could still be optimized (for instance, check if val is LF or empty)
|
123
|
+
def create_fragment tok, val = nil
|
124
|
+
fragment = val ? { text: val } : {}
|
125
|
+
if (style_rules = @theme.style_for tok)
|
126
|
+
if (bg = normalize_color style_rules.bg) && bg != background_color
|
127
|
+
fragment[:background_color] = bg
|
128
|
+
fragment[:callback] = @background_colorizer
|
129
|
+
if LineOrientedTokens.include? tok
|
130
|
+
fragment[:inline_block] = true unless style_rules[:inline_block] == false
|
131
|
+
fragment[:extend] = true unless style_rules[:extend] == false
|
132
|
+
else
|
133
|
+
fragment[:inline_block] = true if style_rules[:inline_block]
|
134
|
+
fragment[:extend] = true if style_rules[:extend]
|
135
|
+
end
|
136
|
+
end
|
137
|
+
if (fg = normalize_color style_rules.fg)
|
138
|
+
fragment[:color] = fg
|
139
|
+
end
|
140
|
+
if style_rules[:bold]
|
141
|
+
fragment[:styles] = style_rules[:italic] ? BoldItalicStyle.dup : BoldStyle.dup
|
142
|
+
elsif style_rules[:italic]
|
143
|
+
fragment[:styles] = ItalicStyle.dup
|
144
|
+
end
|
145
|
+
if style_rules[:underline]
|
146
|
+
if fragment.key? :styles
|
147
|
+
fragment[:styles] << UnderlineStyle[0]
|
148
|
+
else
|
149
|
+
fragment[:styles] = UnderlineStyle.dup
|
150
|
+
end
|
151
|
+
end
|
122
152
|
end
|
153
|
+
fragment
|
123
154
|
end
|
124
|
-
|
125
|
-
|
155
|
+
|
156
|
+
def create_linenum_fragment linenum
|
157
|
+
@linenum_fragment_base.merge text: %(#{linenum} ), linenum: linenum
|
126
158
|
end
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
159
|
+
|
160
|
+
def create_highlight_line_fragment bg_color
|
161
|
+
{
|
162
|
+
background_color: (bg_color || 'FFFFCC'),
|
163
|
+
callback: @background_colorizer,
|
164
|
+
extend: true,
|
165
|
+
highlight: true,
|
166
|
+
inline_block: true,
|
167
|
+
text: DummyText,
|
168
|
+
width: 0,
|
169
|
+
}
|
131
170
|
end
|
132
|
-
|
133
|
-
|
134
|
-
|
171
|
+
|
172
|
+
def normalize_color raw
|
173
|
+
return unless raw
|
174
|
+
if (normalized = @normalized_colors[raw])
|
175
|
+
normalized
|
135
176
|
else
|
136
|
-
|
177
|
+
normalized = (raw.start_with? '#') ? (raw.slice 1, raw.length) : raw
|
178
|
+
normalized = normalized.each_char.map {|c| c * 2 }.join if normalized.length == 3
|
179
|
+
@normalized_colors[raw] = normalized
|
137
180
|
end
|
138
181
|
end
|
139
|
-
end
|
140
|
-
fragment
|
141
|
-
end
|
142
182
|
|
143
|
-
|
144
|
-
|
145
|
-
|
183
|
+
class BackgroundColorizer
|
184
|
+
def initialize opts = {}
|
185
|
+
@line_gap = opts[:line_gap] || 0
|
186
|
+
end
|
146
187
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
188
|
+
def render_behind fragment
|
189
|
+
pdf = fragment.document
|
190
|
+
data = fragment.format_state
|
191
|
+
prev_fill_color = pdf.fill_color
|
192
|
+
pdf.fill_color data[:background_color]
|
193
|
+
if data[:inline_block]
|
194
|
+
fragment_width = data[:extend] ? pdf.bounds.width - fragment.left : fragment.width
|
195
|
+
v_gap = @line_gap
|
196
|
+
else
|
197
|
+
fragment_width = fragment.width
|
198
|
+
v_gap = 0
|
199
|
+
end
|
200
|
+
pdf.fill_rectangle [fragment.left, fragment.top + v_gap * 0.5], fragment_width, (fragment.height + v_gap)
|
201
|
+
pdf.fill_color prev_fill_color
|
202
|
+
fragment.conceal if fragment.text == DummyText
|
203
|
+
nil
|
204
|
+
end
|
205
|
+
end
|
155
206
|
end
|
156
207
|
end
|
157
208
|
end
|
158
|
-
|
159
|
-
class BackgroundColorizer
|
160
|
-
def initialize opts = {}
|
161
|
-
@line_gap = opts[:line_gap] || 0
|
162
|
-
end
|
163
|
-
|
164
|
-
def render_behind fragment
|
165
|
-
pdf = fragment.document
|
166
|
-
data = fragment.format_state
|
167
|
-
prev_fill_color = pdf.fill_color
|
168
|
-
pdf.fill_color data[:background_color]
|
169
|
-
v_gap = data[:inline_block] ? @line_gap : 0
|
170
|
-
fragment_width = data[:line_oriented] && data[:extend] ? (pdf.bounds.width - fragment.left) : fragment.width
|
171
|
-
pdf.fill_rectangle [fragment.left, fragment.top + v_gap * 0.5], fragment_width, (fragment.height + v_gap)
|
172
|
-
pdf.fill_color prev_fill_color
|
173
|
-
end
|
174
|
-
end
|
175
|
-
end
|
176
|
-
end
|