asciidoctor-pdf 1.5.0.beta.1 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +277 -2
- data/LICENSE.adoc +1 -1
- data/NOTICE.adoc +1 -1
- data/README.adoc +486 -292
- data/asciidoctor-pdf.gemspec +12 -11
- data/bin/asciidoctor-pdf +2 -6
- data/bin/asciidoctor-pdf-optimize +20 -0
- data/data/fonts/ABOUT-mplus1mn-subset +26 -0
- data/data/fonts/ABOUT-mplus1p-subset +26 -0
- data/data/fonts/ABOUT-notoemoji-subset +3 -0
- data/data/fonts/ABOUT-notoserif-subset +26 -0
- data/data/fonts/{LICENSE-mplus-testflight-58 → LICENSE-mplus} +2 -2
- data/data/fonts/{LICENSE-noto-2015-06-05 → LICENSE-notoserif} +0 -0
- data/data/fonts/mplus1mn-bold-ascii.ttf +0 -0
- data/data/fonts/mplus1mn-bold-subset.ttf +0 -0
- data/data/fonts/mplus1mn-bold_italic-ascii.ttf +0 -0
- data/data/fonts/mplus1mn-bold_italic-subset.ttf +0 -0
- data/data/fonts/mplus1mn-italic-ascii.ttf +0 -0
- data/data/fonts/mplus1mn-italic-subset.ttf +0 -0
- data/data/fonts/mplus1mn-regular-ascii-conums.ttf +0 -0
- data/data/fonts/mplus1mn-regular-subset.ttf +0 -0
- data/data/fonts/mplus1p-regular-fallback.ttf +0 -0
- data/data/fonts/notoemoji-subset.ttf +0 -0
- data/data/fonts/notoserif-bold-subset.ttf +0 -0
- data/data/fonts/notoserif-bold_italic-subset.ttf +0 -0
- data/data/fonts/notoserif-italic-subset.ttf +0 -0
- data/data/fonts/notoserif-regular-subset.ttf +0 -0
- data/data/themes/base-theme.yml +22 -4
- data/data/themes/default-theme.yml +59 -29
- data/data/themes/default-with-fallback-font-theme.yml +4 -17
- data/docs/theming-guide.adoc +1647 -167
- data/lib/asciidoctor/pdf/converter.rb +4489 -0
- data/lib/{asciidoctor-pdf/asciidoctor_ext → asciidoctor/pdf/ext/asciidoctor}/abstract_block.rb +2 -0
- data/lib/asciidoctor/pdf/ext/asciidoctor/abstract_node.rb +7 -0
- data/lib/{asciidoctor-pdf/asciidoctor_ext → asciidoctor/pdf/ext/asciidoctor}/document.rb +2 -0
- data/lib/asciidoctor/pdf/ext/asciidoctor/image.rb +35 -0
- data/lib/{asciidoctor-pdf/asciidoctor_ext → asciidoctor/pdf/ext/asciidoctor}/list.rb +4 -2
- data/lib/{asciidoctor-pdf/asciidoctor_ext → asciidoctor/pdf/ext/asciidoctor}/list_item.rb +3 -1
- data/lib/asciidoctor/pdf/ext/asciidoctor/logging_shim.rb +33 -0
- data/lib/{asciidoctor-pdf/asciidoctor_ext → asciidoctor/pdf/ext/asciidoctor}/section.rb +9 -6
- data/lib/asciidoctor/pdf/ext/asciidoctor.rb +11 -0
- data/lib/{asciidoctor-pdf/core_ext → asciidoctor/pdf/ext/core}/array.rb +6 -0
- data/lib/asciidoctor/pdf/ext/core/file.rb +9 -0
- data/lib/{asciidoctor-pdf/core_ext → asciidoctor/pdf/ext/core}/hash.rb +2 -0
- data/lib/{asciidoctor-pdf/core_ext → asciidoctor/pdf/ext/core}/numeric.rb +5 -3
- data/lib/{asciidoctor-pdf/core_ext → asciidoctor/pdf/ext/core}/object.rb +3 -1
- data/lib/{asciidoctor-pdf/core_ext → asciidoctor/pdf/ext/core}/quantifiable_stdout.rb +9 -1
- data/lib/{asciidoctor-pdf/core_ext → asciidoctor/pdf/ext/core}/regexp.rb +2 -0
- data/lib/{asciidoctor-pdf/core_ext → asciidoctor/pdf/ext/core}/string.rb +9 -13
- data/lib/asciidoctor/pdf/ext/core.rb +10 -0
- data/lib/asciidoctor/pdf/ext/pdf-core/page.rb +54 -0
- data/lib/asciidoctor/pdf/ext/pdf-core/pdf_object.rb +8 -0
- data/lib/asciidoctor/pdf/ext/pdf-core.rb +4 -0
- data/lib/asciidoctor/pdf/ext/prawn/coderay_encoder.rb +117 -0
- data/lib/asciidoctor/pdf/ext/prawn/extensions.rb +922 -0
- data/lib/{asciidoctor-pdf/prawn_ext → asciidoctor/pdf/ext/prawn}/font/afm.rb +14 -10
- data/lib/asciidoctor/pdf/ext/prawn/font_metric_cache.rb +9 -0
- data/lib/asciidoctor/pdf/ext/prawn/formatted_text/box.rb +66 -0
- data/lib/{asciidoctor-pdf/prawn_ext → asciidoctor/pdf/ext/prawn}/formatted_text/fragment.rb +16 -12
- data/lib/asciidoctor/pdf/ext/prawn/images.rb +54 -0
- data/lib/asciidoctor/pdf/ext/prawn-svg/interface.rb +14 -0
- data/lib/{asciidoctor-pdf/prawn-svg_ext.rb → asciidoctor/pdf/ext/prawn-svg.rb} +3 -1
- data/lib/asciidoctor/pdf/ext/prawn-table/cell/asciidoc.rb +76 -0
- data/lib/{asciidoctor-pdf/prawn-table_ext → asciidoctor/pdf/ext/prawn-table}/cell/text.rb +6 -3
- data/lib/{asciidoctor-pdf/prawn-table_ext → asciidoctor/pdf/ext/prawn-table}/cell.rb +10 -10
- data/lib/asciidoctor/pdf/ext/prawn-table.rb +6 -0
- data/lib/{asciidoctor-pdf/prawn-templates_ext.rb → asciidoctor/pdf/ext/prawn-templates.rb} +2 -0
- data/lib/asciidoctor/pdf/ext/prawn.rb +9 -0
- data/lib/asciidoctor/pdf/ext/pygments.rb +34 -0
- data/lib/asciidoctor/pdf/ext/rouge/formatters/prawn.rb +208 -0
- data/lib/{asciidoctor-pdf/rouge_ext → asciidoctor/pdf/ext/rouge}/themes/asciidoctor_pdf_default.rb +2 -0
- data/lib/asciidoctor/pdf/ext/rouge.rb +5 -0
- data/lib/asciidoctor/pdf/ext.rb +9 -0
- data/lib/asciidoctor/pdf/formatted_text/formatter.rb +43 -0
- data/lib/asciidoctor/pdf/formatted_text/fragment_position_renderer.rb +14 -0
- data/lib/asciidoctor/pdf/formatted_text/inline_destination_marker.rb +21 -0
- data/lib/asciidoctor/pdf/formatted_text/inline_image_arranger.rb +134 -0
- data/lib/asciidoctor/pdf/formatted_text/inline_image_renderer.rb +51 -0
- data/lib/asciidoctor/pdf/formatted_text/inline_text_aligner.rb +22 -0
- data/lib/{asciidoctor-pdf → asciidoctor/pdf}/formatted_text/parser.rb +31 -7
- data/lib/{asciidoctor-pdf → asciidoctor/pdf}/formatted_text/parser.treetop +3 -4
- data/lib/asciidoctor/pdf/formatted_text/source_wrap.rb +43 -0
- data/lib/asciidoctor/pdf/formatted_text/text_background_and_border_renderer.rb +55 -0
- data/lib/asciidoctor/pdf/formatted_text/transform.rb +394 -0
- data/lib/{asciidoctor-pdf → asciidoctor/pdf}/formatted_text.rb +4 -0
- data/lib/asciidoctor/pdf/index_catalog.rb +133 -0
- data/lib/asciidoctor/pdf/measurements.rb +62 -0
- data/lib/asciidoctor/pdf/optimizer.rb +44 -0
- data/lib/asciidoctor/pdf/pdfmark.rb +41 -0
- data/lib/asciidoctor/pdf/roman_numeral.rb +128 -0
- data/lib/asciidoctor/pdf/sanitizer.rb +45 -0
- data/lib/asciidoctor/pdf/text_transformer.rb +116 -0
- data/lib/asciidoctor/pdf/theme_loader.rb +305 -0
- data/lib/asciidoctor/pdf/version.rb +8 -1
- data/lib/asciidoctor/pdf.rb +15 -1
- data/lib/asciidoctor-pdf/converter.rb +2 -3824
- data/lib/asciidoctor-pdf/version.rb +3 -6
- data/lib/asciidoctor-pdf.rb +3 -4
- metadata +130 -85
- data/lib/asciidoctor-pdf/asciidoctor_ext/image.rb +0 -24
- data/lib/asciidoctor-pdf/asciidoctor_ext/logging_shim.rb +0 -25
- data/lib/asciidoctor-pdf/asciidoctor_ext.rb +0 -8
- data/lib/asciidoctor-pdf/core_ext/ostruct.rb +0 -8
- data/lib/asciidoctor-pdf/core_ext.rb +0 -6
- data/lib/asciidoctor-pdf/formatted_text/formatter.rb +0 -40
- data/lib/asciidoctor-pdf/formatted_text/inline_destination_marker.rb +0 -21
- data/lib/asciidoctor-pdf/formatted_text/inline_image_arranger.rb +0 -160
- data/lib/asciidoctor-pdf/formatted_text/inline_image_renderer.rb +0 -46
- data/lib/asciidoctor-pdf/formatted_text/inline_text_aligner.rb +0 -20
- data/lib/asciidoctor-pdf/formatted_text/text_background_and_border_renderer.rb +0 -45
- data/lib/asciidoctor-pdf/formatted_text/transform.rb +0 -294
- data/lib/asciidoctor-pdf/implicit_header_processor.rb +0 -63
- data/lib/asciidoctor-pdf/index_catalog.rb +0 -127
- data/lib/asciidoctor-pdf/measurements.rb +0 -58
- data/lib/asciidoctor-pdf/pdf-core_ext/page.rb +0 -25
- data/lib/asciidoctor-pdf/pdf-core_ext/pdf_object.rb +0 -6
- data/lib/asciidoctor-pdf/pdf-core_ext.rb +0 -2
- data/lib/asciidoctor-pdf/pdfmark.rb +0 -33
- data/lib/asciidoctor-pdf/prawn-svg_ext/interface.rb +0 -10
- data/lib/asciidoctor-pdf/prawn-table_ext/cell/asciidoc.rb +0 -69
- data/lib/asciidoctor-pdf/prawn-table_ext.rb +0 -4
- data/lib/asciidoctor-pdf/prawn_ext/coderay_encoder.rb +0 -115
- data/lib/asciidoctor-pdf/prawn_ext/extensions.rb +0 -904
- data/lib/asciidoctor-pdf/prawn_ext/images.rb +0 -51
- data/lib/asciidoctor-pdf/prawn_ext.rb +0 -5
- data/lib/asciidoctor-pdf/roman_numeral.rb +0 -126
- data/lib/asciidoctor-pdf/rouge_ext/formatters/prawn.rb +0 -175
- data/lib/asciidoctor-pdf/rouge_ext/themes/bw.rb +0 -38
- data/lib/asciidoctor-pdf/rouge_ext.rb +0 -4
- data/lib/asciidoctor-pdf/sanitizer.rb +0 -101
- data/lib/asciidoctor-pdf/temporary_path.rb +0 -13
- data/lib/asciidoctor-pdf/theme_loader.rb +0 -280
- data/lib/asciidoctor-pdf/ttfunk_ext.rb +0 -8
@@ -0,0 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'ext/core'
|
4
|
+
require_relative 'ext/asciidoctor'
|
5
|
+
require_relative 'ext/pdf-core'
|
6
|
+
require_relative 'ext/prawn'
|
7
|
+
require_relative 'ext/prawn-svg'
|
8
|
+
require_relative 'ext/prawn-table'
|
9
|
+
require_relative 'ext/prawn-templates'
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Asciidoctor
|
4
|
+
module PDF
|
5
|
+
module FormattedText
|
6
|
+
class Formatter
|
7
|
+
include ::Asciidoctor::Logging
|
8
|
+
|
9
|
+
attr_accessor :scratch
|
10
|
+
|
11
|
+
FormattingSnifferPattern = /[<&]/
|
12
|
+
WHITESPACE = %( \t\n)
|
13
|
+
|
14
|
+
def initialize options = {}
|
15
|
+
@parser = MarkupParser.new
|
16
|
+
@transform = Transform.new merge_adjacent_text_nodes: true, theme: options[:theme]
|
17
|
+
@scratch = false
|
18
|
+
end
|
19
|
+
|
20
|
+
def format string, *args
|
21
|
+
options = args[0] || {}
|
22
|
+
string = string.tr_s WHITESPACE, ' ' if options[:normalize]
|
23
|
+
inherited = options[:inherited]
|
24
|
+
if FormattingSnifferPattern.match? string
|
25
|
+
if (parsed = @parser.parse string)
|
26
|
+
return @transform.apply parsed.content, [], inherited
|
27
|
+
elsif !@scratch
|
28
|
+
logger.error %(failed to parse formatted text: #{string})
|
29
|
+
end
|
30
|
+
end
|
31
|
+
[inherited ? (inherited.merge text: string) : { text: string }]
|
32
|
+
end
|
33
|
+
|
34
|
+
# The original purpose of this method is to split paragraphs, but our formatter only works on paragraphs that have
|
35
|
+
# been presplit. Therefore, we just need to wrap the fragments in a single-element array (representing a single
|
36
|
+
# paragraph) and return them.
|
37
|
+
def array_paragraphs fragments
|
38
|
+
[fragments]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Asciidoctor::PDF::FormattedText
|
4
|
+
class FragmentPositionRenderer
|
5
|
+
attr_reader :top, :right, :bottom, :left, :page_number
|
6
|
+
|
7
|
+
def render_behind fragment
|
8
|
+
@top = fragment.top
|
9
|
+
@right = (@left = fragment.left) + fragment.width
|
10
|
+
@bottom = fragment.bottom
|
11
|
+
@page_number = fragment.document.page_number
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Asciidoctor::PDF::FormattedText
|
4
|
+
module InlineDestinationMarker
|
5
|
+
module_function
|
6
|
+
|
7
|
+
# render_behind is called before the text is printed
|
8
|
+
def render_behind fragment
|
9
|
+
unless (pdf = fragment.document).scratch?
|
10
|
+
if (name = fragment.format_state[:name])
|
11
|
+
(pdf.instance_variable_get :@index).link_dest_to_page name, pdf.page_number if fragment.format_state[:type] == :indexterm
|
12
|
+
# get precise position of the reference (x, y)
|
13
|
+
dest_rect = fragment.absolute_bounding_box
|
14
|
+
pdf.add_dest name, (pdf.dest_xyz dest_rect[0], dest_rect[-1])
|
15
|
+
# prevent any text from being written
|
16
|
+
fragment.conceal
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Asciidoctor::PDF::FormattedText
|
4
|
+
module InlineImageArranger
|
5
|
+
include ::Asciidoctor::PDF::Measurements
|
6
|
+
include ::Asciidoctor::Logging
|
7
|
+
|
8
|
+
PlaceholderChar = ::Asciidoctor::Prawn::Extensions::PlaceholderChar
|
9
|
+
|
10
|
+
def wrap fragments
|
11
|
+
arrange_images fragments
|
12
|
+
super
|
13
|
+
end
|
14
|
+
|
15
|
+
# Iterates over the fragments that represent inline images and prepares the
|
16
|
+
# image data to be embedded into the document.
|
17
|
+
#
|
18
|
+
# This method populates the image_width, image_height, image_obj and
|
19
|
+
# image_info (PNG only) keys on the fragment. The text is replaced with
|
20
|
+
# placeholder text that will be used to reserve enough room in the line to
|
21
|
+
# fit the image.
|
22
|
+
#
|
23
|
+
# The image height is scaled down to 75% of the specified width (px to pt
|
24
|
+
# conversion). If the image height is more than 1.5x the height of the
|
25
|
+
# surrounding line of text, the font size and line metrics of the fragment
|
26
|
+
# are modified to fit the image in the line.
|
27
|
+
#
|
28
|
+
# If this is the scratch document, the image renderer callback is removed so
|
29
|
+
# that the image is not embedded.
|
30
|
+
#
|
31
|
+
# When this method is called, the cursor is positioned at start of where this
|
32
|
+
# group of fragments will be written (offset by the leading padding).
|
33
|
+
#
|
34
|
+
# This method is called each time the set of fragments overflow to another
|
35
|
+
# page, so it's necessary to short-circuit if that case is detected.
|
36
|
+
def arrange_images fragments
|
37
|
+
doc = @document
|
38
|
+
return if (raw_image_fragments = fragments.select {|f| (f.key? :image_path) && !(f.key? :image_obj) }).empty?
|
39
|
+
scratch = doc.scratch?
|
40
|
+
available_w = doc.bounds.width
|
41
|
+
available_h = doc.page.empty? ? doc.cursor : doc.bounds.height
|
42
|
+
raw_image_fragments.each do |fragment|
|
43
|
+
drop = scratch
|
44
|
+
begin
|
45
|
+
image_path = fragment[:image_path]
|
46
|
+
|
47
|
+
# NOTE only attempt to convert an unresolved (i.e., String) value
|
48
|
+
if ::String === (image_w = fragment[:image_width])
|
49
|
+
image_w = [available_w, (image_w.end_with? '%') ? (image_w.to_f / 100 * available_w) : image_w.to_f].min
|
50
|
+
end
|
51
|
+
|
52
|
+
max_image_h = fragment[:image_fit] == 'line' ? [available_h, doc.font.height].min : available_h
|
53
|
+
|
54
|
+
# TODO: make helper method to calculate width and height of image
|
55
|
+
if fragment[:image_format] == 'svg'
|
56
|
+
svg_obj = ::Prawn::SVG::Interface.new ::File.read(image_path, mode: 'r:UTF-8'), doc,
|
57
|
+
at: doc.bounds.top_left,
|
58
|
+
width: image_w,
|
59
|
+
fallback_font_name: doc.fallback_svg_font_name,
|
60
|
+
enable_web_requests: doc.allow_uri_read,
|
61
|
+
enable_file_requests_with_root: (::File.dirname image_path),
|
62
|
+
cache_images: doc.cache_uri
|
63
|
+
svg_size = image_w ? svg_obj.document.sizing :
|
64
|
+
# NOTE convert intrinsic dimensions to points; constrain to content width
|
65
|
+
(svg_obj.resize width: [(to_pt svg_obj.document.sizing.output_width, :px), available_w].min)
|
66
|
+
# NOTE the best we can do is make the image fit within full height of bounds
|
67
|
+
if (image_h = svg_size.output_height) > max_image_h
|
68
|
+
image_w = (svg_obj.resize height: (image_h = max_image_h)).output_width
|
69
|
+
else
|
70
|
+
image_w = svg_size.output_width
|
71
|
+
end
|
72
|
+
fragment[:image_obj] = svg_obj
|
73
|
+
else
|
74
|
+
# TODO: cache image info based on path (Prawn caches based on SHA1 of content)
|
75
|
+
# NOTE image_obj is constrained to image_width by renderer
|
76
|
+
image_obj, image_info = ::File.open(image_path, 'rb') {|fd| doc.build_image_object fd }
|
77
|
+
if image_w
|
78
|
+
if image_w == image_info.width
|
79
|
+
image_h = image_info.height.to_f
|
80
|
+
else
|
81
|
+
image_h = image_w * (image_info.height.fdiv image_info.width)
|
82
|
+
end
|
83
|
+
# NOTE convert intrinsic dimensions to points; constrain to content width
|
84
|
+
elsif (image_w = to_pt image_info.width, :px) > available_w
|
85
|
+
image_h = (image_w = available_w) * (image_info.height.fdiv image_info.width)
|
86
|
+
else
|
87
|
+
image_h = to_pt image_info.height, :px
|
88
|
+
end
|
89
|
+
# NOTE the best we can do is make the image fit within full height of bounds
|
90
|
+
image_w = (image_h = max_image_h) * (image_info.width.fdiv image_info.height) if image_h > max_image_h
|
91
|
+
fragment[:image_obj] = image_obj
|
92
|
+
fragment[:image_info] = image_info
|
93
|
+
end
|
94
|
+
|
95
|
+
doc.fragment_font fragment do
|
96
|
+
# NOTE if image height exceeds line height by more than 1.5x, increase the line height
|
97
|
+
# FIXME: we could really use a nicer API from Prawn here; this is an ugly hack
|
98
|
+
if (f_height = image_h) > (line_font = doc.font).height * 1.5
|
99
|
+
# align with descender (equivalent to vertical-align: bottom in CSS)
|
100
|
+
fragment[:ascender] = f_height - (fragment[:descender] = line_font.descender)
|
101
|
+
doc.font_size (fragment[:size] = f_height * (doc.font_size / line_font.height))
|
102
|
+
# align with baseline (roughly equivalent to vertical-align: baseline in CSS)
|
103
|
+
#fragment[:ascender] = f_height
|
104
|
+
#fragment[:descender] = 0
|
105
|
+
#doc.font_size(fragment[:size] = (f_height + line_font.descender) * (doc.font_size / line_font.height))
|
106
|
+
fragment[:line_height_increased] = true
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# NOTE we can't rely on the fragment width because the line wrap mechanism ignores it;
|
111
|
+
# it only considers the text (string) and character spacing, rebuilding the string several times
|
112
|
+
fragment[:text] = PlaceholderChar
|
113
|
+
fragment[:actual_character_spacing] = doc.character_spacing
|
114
|
+
fragment[:character_spacing] = image_w
|
115
|
+
fragment[:image_width] = fragment[:width] = image_w
|
116
|
+
fragment[:image_height] = image_h
|
117
|
+
rescue
|
118
|
+
logger.warn %(could not embed image: #{image_path}; #{$!.message}#{::Prawn::Errors::UnsupportedImageType === $! ? '; install prawn-gmagick gem to add support' : ''}) unless scratch
|
119
|
+
drop = true # delegate to cleanup logic in ensure block
|
120
|
+
ensure
|
121
|
+
# NOTE skip rendering image in scratch document or if image can't be loaded
|
122
|
+
if drop
|
123
|
+
fragment.delete :callback
|
124
|
+
fragment.delete :image_info
|
125
|
+
# NOTE retain key to indicate we've visited fragment already
|
126
|
+
fragment[:image_obj] = nil
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
::Prawn::Text::Formatted::Box.prepend InlineImageArranger
|
134
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Asciidoctor::PDF::FormattedText
|
4
|
+
module InlineImageRenderer
|
5
|
+
module_function
|
6
|
+
|
7
|
+
# Embeds the image object in this fragment into the document in place of the
|
8
|
+
# text that was previously used to reserve space for the image in the line.
|
9
|
+
#
|
10
|
+
# If the image height is less than 1.5x the height of the surrounding text,
|
11
|
+
# it is centered vertically in the line. If the image height is greater, then
|
12
|
+
# the image is aligned to the bottom of the text.
|
13
|
+
#
|
14
|
+
# Note that render_behind is called before the text is printed.
|
15
|
+
#
|
16
|
+
# This handler is only used on the main document (not the scratch document).
|
17
|
+
#
|
18
|
+
def render_behind fragment
|
19
|
+
pdf = fragment.document
|
20
|
+
data = fragment.format_state
|
21
|
+
if data.key? :line_height_increased
|
22
|
+
# align image to bottom of line (differs from fragment.top by descender value)
|
23
|
+
image_top = fragment.bottom + data[:image_height]
|
24
|
+
else
|
25
|
+
# center image in line
|
26
|
+
image_top = fragment.top - ((fragment.height - data[:image_height]) / 2.0)
|
27
|
+
end
|
28
|
+
image_left = fragment.left + ((fragment.width - data[:image_width]) / 2.0)
|
29
|
+
case data[:image_format]
|
30
|
+
when 'svg'
|
31
|
+
(image_obj = data[:image_obj]).options[:at] = [image_left, image_top]
|
32
|
+
# NOTE prawn-svg messes with the cursor; use float to workaround
|
33
|
+
# NOTE prawn-svg 0.24.0, 0.25.0, & 0.25.1 didn't restore font after call to draw (see mogest/prawn-svg#80)
|
34
|
+
pdf.float do
|
35
|
+
pdf.character_spacing(data[:actual_character_spacing]) { image_obj.draw }
|
36
|
+
image_obj.document.warnings.each do |img_warning|
|
37
|
+
# NOTE shim logger can't be imported into a module, so use the one from the PDF document instead
|
38
|
+
pdf.logger.warn %(problem encountered in image: #{data[:image_path]}; #{img_warning})
|
39
|
+
end
|
40
|
+
end
|
41
|
+
else
|
42
|
+
pdf.embed_image data[:image_obj], data[:image_info], at: [image_left, image_top], width: data[:image_width], height: data[:image_height]
|
43
|
+
end
|
44
|
+
# ...or use the public interface, loading the image again
|
45
|
+
#pdf.image data[:image_path], at: [image_left, image_top], width: data[:image_width]
|
46
|
+
|
47
|
+
# prevent any text from being written
|
48
|
+
fragment.conceal
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Asciidoctor::PDF::FormattedText
|
4
|
+
module InlineTextAligner
|
5
|
+
module_function
|
6
|
+
|
7
|
+
def render_behind fragment
|
8
|
+
document = fragment.document
|
9
|
+
text = fragment.text
|
10
|
+
x = fragment.left
|
11
|
+
y = fragment.baseline
|
12
|
+
align = fragment.format_state[:align]
|
13
|
+
if align == :center || align == :right
|
14
|
+
if (gap_width = fragment.width - (document.width_of text)) != 0
|
15
|
+
x += gap_width * (align == :center ? 0.5 : 1)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
document.draw_text! text, at: [x, y]
|
19
|
+
fragment.conceal
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
# Autogenerated from a Treetop grammar. Edits may be lost.
|
3
3
|
|
4
4
|
|
@@ -433,11 +433,11 @@ module Markup
|
|
433
433
|
r8 = SyntaxNode.new(input, (index-1)...index) if r8 == true
|
434
434
|
r0 = r8
|
435
435
|
else
|
436
|
-
if (match_len = has_terminal?('
|
436
|
+
if (match_len = has_terminal?('key', false, index))
|
437
437
|
r9 = instantiate_node(SyntaxNode,input, index...(index + match_len))
|
438
438
|
@index += match_len
|
439
439
|
else
|
440
|
-
terminal_parse_failure('
|
440
|
+
terminal_parse_failure('key')
|
441
441
|
r9 = nil
|
442
442
|
end
|
443
443
|
if r9
|
@@ -455,19 +455,43 @@ module Markup
|
|
455
455
|
r10 = SyntaxNode.new(input, (index-1)...index) if r10 == true
|
456
456
|
r0 = r10
|
457
457
|
else
|
458
|
-
if (match_len = has_terminal?('
|
458
|
+
if (match_len = has_terminal?('sub', false, index))
|
459
459
|
r11 = instantiate_node(SyntaxNode,input, index...(index + match_len))
|
460
460
|
@index += match_len
|
461
461
|
else
|
462
|
-
terminal_parse_failure('
|
462
|
+
terminal_parse_failure('sub')
|
463
463
|
r11 = nil
|
464
464
|
end
|
465
465
|
if r11
|
466
466
|
r11 = SyntaxNode.new(input, (index-1)...index) if r11 == true
|
467
467
|
r0 = r11
|
468
468
|
else
|
469
|
-
|
470
|
-
|
469
|
+
if (match_len = has_terminal?('mark', false, index))
|
470
|
+
r12 = instantiate_node(SyntaxNode,input, index...(index + match_len))
|
471
|
+
@index += match_len
|
472
|
+
else
|
473
|
+
terminal_parse_failure('mark')
|
474
|
+
r12 = nil
|
475
|
+
end
|
476
|
+
if r12
|
477
|
+
r12 = SyntaxNode.new(input, (index-1)...index) if r12 == true
|
478
|
+
r0 = r12
|
479
|
+
else
|
480
|
+
if (match_len = has_terminal?('del', false, index))
|
481
|
+
r13 = instantiate_node(SyntaxNode,input, index...(index + match_len))
|
482
|
+
@index += match_len
|
483
|
+
else
|
484
|
+
terminal_parse_failure('del')
|
485
|
+
r13 = nil
|
486
|
+
end
|
487
|
+
if r13
|
488
|
+
r13 = SyntaxNode.new(input, (index-1)...index) if r13 == true
|
489
|
+
r0 = r13
|
490
|
+
else
|
491
|
+
@index = i0
|
492
|
+
r0 = nil
|
493
|
+
end
|
494
|
+
end
|
471
495
|
end
|
472
496
|
end
|
473
497
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
module Asciidoctor
|
3
3
|
module PDF
|
4
4
|
module FormattedText
|
@@ -49,9 +49,8 @@ grammar Markup
|
|
49
49
|
|
50
50
|
rule tag_name
|
51
51
|
# QUESTION faster to do regex?
|
52
|
-
# QUESTION
|
53
|
-
|
54
|
-
'a' / 'strong' / 'em' / 'code' / 'color' / 'font' / 'span' / 'button' / 'sub' / 'sup' / 'del'
|
52
|
+
# QUESTION what about supporting hr?
|
53
|
+
'a' / 'strong' / 'em' / 'code' / 'color' / 'font' / 'span' / 'button' / 'key' / 'sup' / 'sub' / 'mark' / 'del'
|
55
54
|
end
|
56
55
|
|
57
56
|
rule void_tag_name
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Asciidoctor
|
4
|
+
module PDF
|
5
|
+
module FormattedText
|
6
|
+
module SourceWrap
|
7
|
+
NoBreakSpace = ?\u00a0
|
8
|
+
|
9
|
+
def wrap array
|
10
|
+
initialize_wrap array
|
11
|
+
stop = nil
|
12
|
+
highlight_line = nil
|
13
|
+
unconsumed = @arranger.unconsumed
|
14
|
+
if (linenum_fragment = unconsumed[0] || {})[:linenum]
|
15
|
+
linenum_spacer = { text: NoBreakSpace + (' ' * (linenum_fragment[:text].length - 1)) }
|
16
|
+
end
|
17
|
+
until stop
|
18
|
+
if linenum_spacer && (first_fragment = unconsumed[0])
|
19
|
+
if first_fragment[:linenum]
|
20
|
+
highlight_line = (second_fragment = unconsumed[1]) && (second_fragment[:highlight]) ? second_fragment.dup : nil
|
21
|
+
else
|
22
|
+
first_fragment[:text] = first_fragment[:text].lstrip
|
23
|
+
@arranger.unconsumed.unshift highlight_line if highlight_line
|
24
|
+
@arranger.unconsumed.unshift linenum_spacer.dup
|
25
|
+
end
|
26
|
+
end
|
27
|
+
@line_wrap.wrap_line document: @document, kerning: @kerning, width: available_width, arranger: @arranger, disable_wrap_by_char: @disable_wrap_by_char
|
28
|
+
if enough_height_for_this_line?
|
29
|
+
move_baseline_down
|
30
|
+
print_line
|
31
|
+
else
|
32
|
+
stop = true
|
33
|
+
end
|
34
|
+
stop ||= @single_line || @arranger.finished?
|
35
|
+
end
|
36
|
+
@text = @printed_lines.join ?\n
|
37
|
+
@everything_printed = @arranger.finished?
|
38
|
+
@arranger.unconsumed
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Asciidoctor::Pdf::FormattedText
|
4
|
+
module TextBackgroundAndBorderRenderer
|
5
|
+
module_function
|
6
|
+
|
7
|
+
DummyText = ?\u0000
|
8
|
+
|
9
|
+
# render_behind is called before the text is printed
|
10
|
+
def render_behind fragment
|
11
|
+
return if (pdf = fragment.document).scratch?
|
12
|
+
data = fragment.format_state
|
13
|
+
if data[:inline_block]
|
14
|
+
padding = (height = fragment.line_height) - fragment.height
|
15
|
+
at = [fragment.left, fragment.top + padding * 0.5]
|
16
|
+
width = data[:extend] ? (pdf.bounds.width - fragment.left) : fragment.width
|
17
|
+
fragment.conceal if fragment.text == DummyText
|
18
|
+
elsif (border_offset = data[:border_offset])
|
19
|
+
at = [fragment.left, fragment.top + border_offset]
|
20
|
+
width = fragment.width
|
21
|
+
height = fragment.height + border_offset * 2
|
22
|
+
else
|
23
|
+
at = fragment.top_left
|
24
|
+
width = fragment.width
|
25
|
+
height = fragment.height
|
26
|
+
end
|
27
|
+
border_radius = data[:border_radius]
|
28
|
+
if (background_color = data[:background_color])
|
29
|
+
prev_fill_color = pdf.fill_color
|
30
|
+
pdf.fill_color background_color
|
31
|
+
if border_radius
|
32
|
+
pdf.fill_rounded_rectangle at, width, height, border_radius
|
33
|
+
else
|
34
|
+
pdf.fill_rectangle at, width, height
|
35
|
+
end
|
36
|
+
pdf.fill_color prev_fill_color
|
37
|
+
end
|
38
|
+
if (border_width = data[:border_width])
|
39
|
+
border_color = data[:border_color]
|
40
|
+
prev_stroke_color = pdf.stroke_color
|
41
|
+
prev_line_width = pdf.line_width
|
42
|
+
pdf.stroke_color border_color
|
43
|
+
pdf.line_width border_width
|
44
|
+
if border_radius
|
45
|
+
pdf.stroke_rounded_rectangle at, width, height, border_radius
|
46
|
+
else
|
47
|
+
pdf.stroke_rectangle at, width, height
|
48
|
+
end
|
49
|
+
pdf.stroke_color prev_stroke_color
|
50
|
+
pdf.line_width prev_line_width
|
51
|
+
end
|
52
|
+
nil
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|