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.
Files changed (134) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.adoc +277 -2
  3. data/LICENSE.adoc +1 -1
  4. data/NOTICE.adoc +1 -1
  5. data/README.adoc +486 -292
  6. data/asciidoctor-pdf.gemspec +12 -11
  7. data/bin/asciidoctor-pdf +2 -6
  8. data/bin/asciidoctor-pdf-optimize +20 -0
  9. data/data/fonts/ABOUT-mplus1mn-subset +26 -0
  10. data/data/fonts/ABOUT-mplus1p-subset +26 -0
  11. data/data/fonts/ABOUT-notoemoji-subset +3 -0
  12. data/data/fonts/ABOUT-notoserif-subset +26 -0
  13. data/data/fonts/{LICENSE-mplus-testflight-58 → LICENSE-mplus} +2 -2
  14. data/data/fonts/{LICENSE-noto-2015-06-05 → LICENSE-notoserif} +0 -0
  15. data/data/fonts/mplus1mn-bold-ascii.ttf +0 -0
  16. data/data/fonts/mplus1mn-bold-subset.ttf +0 -0
  17. data/data/fonts/mplus1mn-bold_italic-ascii.ttf +0 -0
  18. data/data/fonts/mplus1mn-bold_italic-subset.ttf +0 -0
  19. data/data/fonts/mplus1mn-italic-ascii.ttf +0 -0
  20. data/data/fonts/mplus1mn-italic-subset.ttf +0 -0
  21. data/data/fonts/mplus1mn-regular-ascii-conums.ttf +0 -0
  22. data/data/fonts/mplus1mn-regular-subset.ttf +0 -0
  23. data/data/fonts/mplus1p-regular-fallback.ttf +0 -0
  24. data/data/fonts/notoemoji-subset.ttf +0 -0
  25. data/data/fonts/notoserif-bold-subset.ttf +0 -0
  26. data/data/fonts/notoserif-bold_italic-subset.ttf +0 -0
  27. data/data/fonts/notoserif-italic-subset.ttf +0 -0
  28. data/data/fonts/notoserif-regular-subset.ttf +0 -0
  29. data/data/themes/base-theme.yml +22 -4
  30. data/data/themes/default-theme.yml +59 -29
  31. data/data/themes/default-with-fallback-font-theme.yml +4 -17
  32. data/docs/theming-guide.adoc +1647 -167
  33. data/lib/asciidoctor/pdf/converter.rb +4489 -0
  34. data/lib/{asciidoctor-pdf/asciidoctor_ext → asciidoctor/pdf/ext/asciidoctor}/abstract_block.rb +2 -0
  35. data/lib/asciidoctor/pdf/ext/asciidoctor/abstract_node.rb +7 -0
  36. data/lib/{asciidoctor-pdf/asciidoctor_ext → asciidoctor/pdf/ext/asciidoctor}/document.rb +2 -0
  37. data/lib/asciidoctor/pdf/ext/asciidoctor/image.rb +35 -0
  38. data/lib/{asciidoctor-pdf/asciidoctor_ext → asciidoctor/pdf/ext/asciidoctor}/list.rb +4 -2
  39. data/lib/{asciidoctor-pdf/asciidoctor_ext → asciidoctor/pdf/ext/asciidoctor}/list_item.rb +3 -1
  40. data/lib/asciidoctor/pdf/ext/asciidoctor/logging_shim.rb +33 -0
  41. data/lib/{asciidoctor-pdf/asciidoctor_ext → asciidoctor/pdf/ext/asciidoctor}/section.rb +9 -6
  42. data/lib/asciidoctor/pdf/ext/asciidoctor.rb +11 -0
  43. data/lib/{asciidoctor-pdf/core_ext → asciidoctor/pdf/ext/core}/array.rb +6 -0
  44. data/lib/asciidoctor/pdf/ext/core/file.rb +9 -0
  45. data/lib/{asciidoctor-pdf/core_ext → asciidoctor/pdf/ext/core}/hash.rb +2 -0
  46. data/lib/{asciidoctor-pdf/core_ext → asciidoctor/pdf/ext/core}/numeric.rb +5 -3
  47. data/lib/{asciidoctor-pdf/core_ext → asciidoctor/pdf/ext/core}/object.rb +3 -1
  48. data/lib/{asciidoctor-pdf/core_ext → asciidoctor/pdf/ext/core}/quantifiable_stdout.rb +9 -1
  49. data/lib/{asciidoctor-pdf/core_ext → asciidoctor/pdf/ext/core}/regexp.rb +2 -0
  50. data/lib/{asciidoctor-pdf/core_ext → asciidoctor/pdf/ext/core}/string.rb +9 -13
  51. data/lib/asciidoctor/pdf/ext/core.rb +10 -0
  52. data/lib/asciidoctor/pdf/ext/pdf-core/page.rb +54 -0
  53. data/lib/asciidoctor/pdf/ext/pdf-core/pdf_object.rb +8 -0
  54. data/lib/asciidoctor/pdf/ext/pdf-core.rb +4 -0
  55. data/lib/asciidoctor/pdf/ext/prawn/coderay_encoder.rb +117 -0
  56. data/lib/asciidoctor/pdf/ext/prawn/extensions.rb +922 -0
  57. data/lib/{asciidoctor-pdf/prawn_ext → asciidoctor/pdf/ext/prawn}/font/afm.rb +14 -10
  58. data/lib/asciidoctor/pdf/ext/prawn/font_metric_cache.rb +9 -0
  59. data/lib/asciidoctor/pdf/ext/prawn/formatted_text/box.rb +66 -0
  60. data/lib/{asciidoctor-pdf/prawn_ext → asciidoctor/pdf/ext/prawn}/formatted_text/fragment.rb +16 -12
  61. data/lib/asciidoctor/pdf/ext/prawn/images.rb +54 -0
  62. data/lib/asciidoctor/pdf/ext/prawn-svg/interface.rb +14 -0
  63. data/lib/{asciidoctor-pdf/prawn-svg_ext.rb → asciidoctor/pdf/ext/prawn-svg.rb} +3 -1
  64. data/lib/asciidoctor/pdf/ext/prawn-table/cell/asciidoc.rb +76 -0
  65. data/lib/{asciidoctor-pdf/prawn-table_ext → asciidoctor/pdf/ext/prawn-table}/cell/text.rb +6 -3
  66. data/lib/{asciidoctor-pdf/prawn-table_ext → asciidoctor/pdf/ext/prawn-table}/cell.rb +10 -10
  67. data/lib/asciidoctor/pdf/ext/prawn-table.rb +6 -0
  68. data/lib/{asciidoctor-pdf/prawn-templates_ext.rb → asciidoctor/pdf/ext/prawn-templates.rb} +2 -0
  69. data/lib/asciidoctor/pdf/ext/prawn.rb +9 -0
  70. data/lib/asciidoctor/pdf/ext/pygments.rb +34 -0
  71. data/lib/asciidoctor/pdf/ext/rouge/formatters/prawn.rb +208 -0
  72. data/lib/{asciidoctor-pdf/rouge_ext → asciidoctor/pdf/ext/rouge}/themes/asciidoctor_pdf_default.rb +2 -0
  73. data/lib/asciidoctor/pdf/ext/rouge.rb +5 -0
  74. data/lib/asciidoctor/pdf/ext.rb +9 -0
  75. data/lib/asciidoctor/pdf/formatted_text/formatter.rb +43 -0
  76. data/lib/asciidoctor/pdf/formatted_text/fragment_position_renderer.rb +14 -0
  77. data/lib/asciidoctor/pdf/formatted_text/inline_destination_marker.rb +21 -0
  78. data/lib/asciidoctor/pdf/formatted_text/inline_image_arranger.rb +134 -0
  79. data/lib/asciidoctor/pdf/formatted_text/inline_image_renderer.rb +51 -0
  80. data/lib/asciidoctor/pdf/formatted_text/inline_text_aligner.rb +22 -0
  81. data/lib/{asciidoctor-pdf → asciidoctor/pdf}/formatted_text/parser.rb +31 -7
  82. data/lib/{asciidoctor-pdf → asciidoctor/pdf}/formatted_text/parser.treetop +3 -4
  83. data/lib/asciidoctor/pdf/formatted_text/source_wrap.rb +43 -0
  84. data/lib/asciidoctor/pdf/formatted_text/text_background_and_border_renderer.rb +55 -0
  85. data/lib/asciidoctor/pdf/formatted_text/transform.rb +394 -0
  86. data/lib/{asciidoctor-pdf → asciidoctor/pdf}/formatted_text.rb +4 -0
  87. data/lib/asciidoctor/pdf/index_catalog.rb +133 -0
  88. data/lib/asciidoctor/pdf/measurements.rb +62 -0
  89. data/lib/asciidoctor/pdf/optimizer.rb +44 -0
  90. data/lib/asciidoctor/pdf/pdfmark.rb +41 -0
  91. data/lib/asciidoctor/pdf/roman_numeral.rb +128 -0
  92. data/lib/asciidoctor/pdf/sanitizer.rb +45 -0
  93. data/lib/asciidoctor/pdf/text_transformer.rb +116 -0
  94. data/lib/asciidoctor/pdf/theme_loader.rb +305 -0
  95. data/lib/asciidoctor/pdf/version.rb +8 -1
  96. data/lib/asciidoctor/pdf.rb +15 -1
  97. data/lib/asciidoctor-pdf/converter.rb +2 -3824
  98. data/lib/asciidoctor-pdf/version.rb +3 -6
  99. data/lib/asciidoctor-pdf.rb +3 -4
  100. metadata +130 -85
  101. data/lib/asciidoctor-pdf/asciidoctor_ext/image.rb +0 -24
  102. data/lib/asciidoctor-pdf/asciidoctor_ext/logging_shim.rb +0 -25
  103. data/lib/asciidoctor-pdf/asciidoctor_ext.rb +0 -8
  104. data/lib/asciidoctor-pdf/core_ext/ostruct.rb +0 -8
  105. data/lib/asciidoctor-pdf/core_ext.rb +0 -6
  106. data/lib/asciidoctor-pdf/formatted_text/formatter.rb +0 -40
  107. data/lib/asciidoctor-pdf/formatted_text/inline_destination_marker.rb +0 -21
  108. data/lib/asciidoctor-pdf/formatted_text/inline_image_arranger.rb +0 -160
  109. data/lib/asciidoctor-pdf/formatted_text/inline_image_renderer.rb +0 -46
  110. data/lib/asciidoctor-pdf/formatted_text/inline_text_aligner.rb +0 -20
  111. data/lib/asciidoctor-pdf/formatted_text/text_background_and_border_renderer.rb +0 -45
  112. data/lib/asciidoctor-pdf/formatted_text/transform.rb +0 -294
  113. data/lib/asciidoctor-pdf/implicit_header_processor.rb +0 -63
  114. data/lib/asciidoctor-pdf/index_catalog.rb +0 -127
  115. data/lib/asciidoctor-pdf/measurements.rb +0 -58
  116. data/lib/asciidoctor-pdf/pdf-core_ext/page.rb +0 -25
  117. data/lib/asciidoctor-pdf/pdf-core_ext/pdf_object.rb +0 -6
  118. data/lib/asciidoctor-pdf/pdf-core_ext.rb +0 -2
  119. data/lib/asciidoctor-pdf/pdfmark.rb +0 -33
  120. data/lib/asciidoctor-pdf/prawn-svg_ext/interface.rb +0 -10
  121. data/lib/asciidoctor-pdf/prawn-table_ext/cell/asciidoc.rb +0 -69
  122. data/lib/asciidoctor-pdf/prawn-table_ext.rb +0 -4
  123. data/lib/asciidoctor-pdf/prawn_ext/coderay_encoder.rb +0 -115
  124. data/lib/asciidoctor-pdf/prawn_ext/extensions.rb +0 -904
  125. data/lib/asciidoctor-pdf/prawn_ext/images.rb +0 -51
  126. data/lib/asciidoctor-pdf/prawn_ext.rb +0 -5
  127. data/lib/asciidoctor-pdf/roman_numeral.rb +0 -126
  128. data/lib/asciidoctor-pdf/rouge_ext/formatters/prawn.rb +0 -175
  129. data/lib/asciidoctor-pdf/rouge_ext/themes/bw.rb +0 -38
  130. data/lib/asciidoctor-pdf/rouge_ext.rb +0 -4
  131. data/lib/asciidoctor-pdf/sanitizer.rb +0 -101
  132. data/lib/asciidoctor-pdf/temporary_path.rb +0 -13
  133. data/lib/asciidoctor-pdf/theme_loader.rb +0 -280
  134. 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
- # regenerate parser.rb using `tt parser.treetop`
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?('sub', false, index))
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('sub')
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?('del', false, index))
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('del')
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
- @index = i0
470
- r0 = nil
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
- # regenerate parser.rb using `tt parser.treetop`
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 can we cut stuff we aren't using? what about supporting hr?
53
- #'a' / 'b' / 'code' / 'color' / 'del' / 'em' / 'font' / 'i' / 'img' / 'link' / 'span' / 'strikethrough' / 'strong' / 'sub' / 'sup' / 'u'
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