asciidoctor-pdf 1.6.1 → 2.0.0.alpha.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -1
  3. data/CHANGELOG.adoc +273 -31
  4. data/NOTICE.adoc +16 -4
  5. data/README.adoc +208 -68
  6. data/asciidoctor-pdf.gemspec +3 -7
  7. data/data/fonts/ABOUT-mplus1mn-subset +1 -1
  8. data/data/fonts/ABOUT-mplus1p-subset +2 -2
  9. data/data/fonts/ABOUT-notosans-subset +26 -0
  10. data/data/fonts/ABOUT-notoserif-subset +1 -1
  11. data/data/fonts/{LICENSE-notoserif → LICENSE-noto} +0 -0
  12. data/data/fonts/mplus1mn-bold-subset.ttf +0 -0
  13. data/data/fonts/mplus1mn-bold_italic-subset.ttf +0 -0
  14. data/data/fonts/mplus1mn-italic-subset.ttf +0 -0
  15. data/data/fonts/mplus1mn-regular-subset.ttf +0 -0
  16. data/data/fonts/mplus1p-regular-fallback.ttf +0 -0
  17. data/data/fonts/notoemoji-subset.ttf +0 -0
  18. data/data/fonts/notosans-bold-subset.ttf +0 -0
  19. data/data/fonts/notosans-bold_italic-subset.ttf +0 -0
  20. data/data/fonts/notosans-italic-subset.ttf +0 -0
  21. data/data/fonts/notosans-regular-subset.ttf +0 -0
  22. data/data/fonts/notoserif-bold-subset.ttf +0 -0
  23. data/data/fonts/notoserif-bold_italic-subset.ttf +0 -0
  24. data/data/fonts/notoserif-italic-subset.ttf +0 -0
  25. data/data/fonts/notoserif-regular-subset.ttf +0 -0
  26. data/data/themes/base-theme.yml +21 -24
  27. data/data/themes/default-for-print-theme.yml +24 -0
  28. data/data/themes/default-for-print-with-fallback-font-theme.yml +3 -0
  29. data/data/themes/default-theme.yml +55 -59
  30. data/data/themes/default-with-fallback-font-theme.yml +2 -2
  31. data/data/themes/sans-with-fallback-font-theme.yml +10 -0
  32. data/docs/theming-guide.adoc +977 -352
  33. data/lib/asciidoctor/pdf/converter.rb +1853 -1566
  34. data/lib/asciidoctor/pdf/ext/asciidoctor/document.rb +22 -1
  35. data/lib/asciidoctor/pdf/ext/asciidoctor/image.rb +9 -15
  36. data/lib/asciidoctor/pdf/ext/asciidoctor/list.rb +6 -13
  37. data/lib/asciidoctor/pdf/ext/asciidoctor/section.rb +3 -16
  38. data/lib/asciidoctor/pdf/ext/asciidoctor.rb +1 -5
  39. data/lib/asciidoctor/pdf/ext/core/file.rb +1 -1
  40. data/lib/asciidoctor/pdf/ext/core/quantifiable_stdout.rb +1 -4
  41. data/lib/asciidoctor/pdf/ext/core/string.rb +2 -2
  42. data/lib/asciidoctor/pdf/ext/core.rb +1 -4
  43. data/lib/asciidoctor/pdf/ext/pdf-core/page.rb +8 -33
  44. data/lib/asciidoctor/pdf/ext/pdf-core.rb +0 -16
  45. data/lib/asciidoctor/pdf/ext/prawn/coderay_encoder.rb +5 -7
  46. data/lib/asciidoctor/pdf/ext/prawn/extensions.rb +489 -331
  47. data/lib/asciidoctor/pdf/ext/prawn/font/afm.rb +0 -4
  48. data/lib/asciidoctor/pdf/ext/prawn/font_metric_cache.rb +1 -1
  49. data/lib/asciidoctor/pdf/ext/prawn/formatted_text/arranger.rb +33 -3
  50. data/lib/asciidoctor/pdf/ext/prawn/formatted_text/box.rb +25 -14
  51. data/lib/asciidoctor/pdf/ext/prawn/formatted_text/fragment.rb +9 -3
  52. data/lib/asciidoctor/pdf/ext/prawn/formatted_text/protect_bottom_gutter.rb +13 -0
  53. data/lib/asciidoctor/pdf/ext/prawn/images.rb +20 -18
  54. data/lib/asciidoctor/pdf/ext/prawn-svg/loaders/data.rb +6 -0
  55. data/lib/asciidoctor/pdf/ext/prawn-svg/loaders/web.rb +22 -0
  56. data/lib/asciidoctor/pdf/ext/prawn-svg/url_loader.rb +13 -0
  57. data/lib/asciidoctor/pdf/ext/prawn-svg.rb +5 -2
  58. data/lib/asciidoctor/pdf/ext/prawn-table/cell/asciidoc.rb +76 -20
  59. data/lib/asciidoctor/pdf/ext/prawn-table/cell/text.rb +39 -1
  60. data/lib/asciidoctor/pdf/ext/prawn-table/cell.rb +21 -15
  61. data/lib/asciidoctor/pdf/ext/prawn-table.rb +1 -1
  62. data/lib/asciidoctor/pdf/ext/prawn.rb +1 -0
  63. data/lib/asciidoctor/pdf/ext/pygments.rb +2 -2
  64. data/lib/asciidoctor/pdf/ext/rouge/formatters/prawn.rb +17 -20
  65. data/lib/asciidoctor/pdf/ext/rouge/themes/asciidoctor_pdf_default.rb +1 -0
  66. data/lib/asciidoctor/pdf/ext/rouge.rb +0 -1
  67. data/lib/asciidoctor/pdf/formatted_text/formatter.rb +2 -2
  68. data/lib/asciidoctor/pdf/formatted_text/inline_destination_marker.rb +8 -10
  69. data/lib/asciidoctor/pdf/formatted_text/inline_image_arranger.rb +69 -78
  70. data/lib/asciidoctor/pdf/formatted_text/inline_image_renderer.rb +7 -10
  71. data/lib/asciidoctor/pdf/formatted_text/inline_text_aligner.rb +2 -4
  72. data/lib/asciidoctor/pdf/formatted_text/parser.rb +53 -47
  73. data/lib/asciidoctor/pdf/formatted_text/parser.treetop +5 -7
  74. data/lib/asciidoctor/pdf/formatted_text/source_wrap.rb +14 -14
  75. data/lib/asciidoctor/pdf/formatted_text/text_background_and_border_renderer.rb +4 -7
  76. data/lib/asciidoctor/pdf/formatted_text/transform.rb +122 -110
  77. data/lib/asciidoctor/pdf/formatted_text.rb +0 -1
  78. data/lib/asciidoctor/pdf/index_catalog.rb +7 -11
  79. data/lib/asciidoctor/pdf/nogmagick.rb +6 -0
  80. data/lib/asciidoctor/pdf/optimizer.rb +3 -5
  81. data/lib/asciidoctor/pdf/pdfmark.rb +16 -8
  82. data/lib/asciidoctor/pdf/roman_numeral.rb +4 -22
  83. data/lib/asciidoctor/pdf/sanitizer.rb +18 -13
  84. data/lib/asciidoctor/pdf/section_info_by_page.rb +24 -0
  85. data/lib/asciidoctor/pdf/theme_loader.rb +100 -80
  86. data/lib/asciidoctor/pdf/version.rb +1 -2
  87. data/lib/asciidoctor/pdf.rb +5 -2
  88. metadata +36 -64
  89. data/data/fonts/mplus1mn-bold-ascii.ttf +0 -0
  90. data/data/fonts/mplus1mn-bold_italic-ascii.ttf +0 -0
  91. data/data/fonts/mplus1mn-italic-ascii.ttf +0 -0
  92. data/data/fonts/mplus1mn-regular-ascii-conums.ttf +0 -0
  93. data/lib/asciidoctor/pdf/ext/asciidoctor/abstract_block.rb +0 -7
  94. data/lib/asciidoctor/pdf/ext/asciidoctor/abstract_node.rb +0 -7
  95. data/lib/asciidoctor/pdf/ext/asciidoctor/list_item.rb +0 -18
  96. data/lib/asciidoctor/pdf/ext/asciidoctor/logging_shim.rb +0 -33
  97. data/lib/asciidoctor/pdf/ext/core/array.rb +0 -11
  98. data/lib/asciidoctor/pdf/ext/core/hash.rb +0 -7
  99. data/lib/asciidoctor/pdf/ext/core/regexp.rb +0 -5
  100. data/lib/asciidoctor/pdf/ext/pdf-core/pdf_object.rb +0 -8
  101. data/lib/asciidoctor-pdf/converter.rb +0 -3
  102. data/lib/asciidoctor-pdf/version.rb +0 -3
@@ -28,7 +28,7 @@ module Rouge
28
28
 
29
29
  def initialize opts = {}
30
30
  unless ::Rouge::Theme === (theme = opts[:theme])
31
- unless theme && (theme = ::Rouge::Theme.find theme)
31
+ unless theme && ((::Class === theme && theme < ::Rouge::Theme) || (theme = ::Rouge::Theme.find theme))
32
32
  theme = ::Rouge::Themes::AsciidoctorPDFDefault
33
33
  end
34
34
  theme = theme.new
@@ -41,13 +41,7 @@ module Rouge
41
41
  end
42
42
 
43
43
  def background_color
44
- @background_color ||= (normalize_color (@theme.style_for Tokens::Text).bg)
45
- end
46
-
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
44
+ @background_color ||= (normalize_color ((@theme.style_for Tokens::Text) || (::Rouge::Theme::Style.new @theme)).bg)
51
45
  end
52
46
 
53
47
  def stream tokens, opts = {}
@@ -65,7 +59,7 @@ module Rouge
65
59
  line_numbers ? (fragments << (create_linenum_fragment linenum)) : (start_of_line = true)
66
60
  fragments << @highlight_line_fragment.dup if highlight_lines && highlight_lines[linenum]
67
61
  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
62
+ # NOTE: we assume if the fragment ends in a line feed, the intention was to match a line-oriented form
69
63
  line_oriented = val.end_with? LF
70
64
  base_fragment = create_fragment tok, val
71
65
  val.each_line do |line|
@@ -75,7 +69,7 @@ module Rouge
75
69
  end
76
70
  fragments << (line_oriented ? (base_fragment.merge text: line, inline_block: true) : (base_fragment.merge text: line))
77
71
  next unless line.end_with? LF
78
- # NOTE eagerly append linenum fragment or line highlight if there's a next line
72
+ # NOTE: eagerly append linenum fragment or line highlight if there's a next line
79
73
  linenum += 1
80
74
  line_numbers ? (fragments << (create_linenum_fragment linenum)) : (start_of_line = true)
81
75
  fragments << @highlight_line_fragment.dup if highlight_lines && highlight_lines[linenum]
@@ -88,10 +82,10 @@ module Rouge
88
82
  fragments << (create_fragment tok, val)
89
83
  end
90
84
  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
85
+ # NOTE: pad numbers that have less digits than the largest line number
86
+ # FIXME: we could store these fragments so we don't have find them again
93
87
  if line_numbers && (linenum_w = linenum.to_s.length) > 1
94
- # NOTE extra column is the trailing space after the line number
88
+ # NOTE: extra column is the trailing space after the line number
95
89
  linenum_w += 1
96
90
  fragments.each do |fragment|
97
91
  fragment[:text] = (fragment[:text].rjust linenum_w, NoBreakSpace).to_s if fragment[:linenum]
@@ -108,10 +102,10 @@ module Rouge
108
102
  else
109
103
  val[0] = GuardedIndent if start_of_line && (val.start_with? ' ')
110
104
  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
105
+ # QUESTION: do we need the call to create_fragment if val contains only spaces? consider bg
112
106
  #fragment = create_fragment tok, val
113
107
  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
108
+ # NOTE: we assume if the fragment ends in a line feed, the intention was to match a line-oriented form
115
109
  fragment[:inline_block] = true if (start_of_line = val.end_with? LF)
116
110
  fragment
117
111
  end
@@ -119,6 +113,10 @@ module Rouge
119
113
  end
120
114
  end
121
115
 
116
+ # Override format method so fragments don't get flatted to a string
117
+ # and to add an options Hash.
118
+ alias format stream
119
+
122
120
  # TODO: method could still be optimized (for instance, check if val is LF or empty)
123
121
  def create_fragment tok, val = nil
124
122
  fragment = val ? { text: val } : {}
@@ -144,7 +142,7 @@ module Rouge
144
142
  end
145
143
  if style_rules[:underline]
146
144
  if fragment.key? :styles
147
- fragment[:styles] << UnderlineStyle[0]
145
+ fragment[:styles] |= UnderlineStyle
148
146
  else
149
147
  fragment[:styles] = UnderlineStyle.dup
150
148
  end
@@ -174,9 +172,8 @@ module Rouge
174
172
  if (normalized = @normalized_colors[raw])
175
173
  normalized
176
174
  else
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
175
+ normalized = raw.slice 1, raw.length
176
+ @normalized_colors[raw] = normalized.length == 3 ? normalized.each_char.map {|c| c * 2 }.join : normalized
180
177
  end
181
178
  end
182
179
 
@@ -199,7 +196,7 @@ module Rouge
199
196
  end
200
197
  pdf.fill_rectangle [fragment.left, fragment.top + v_gap * 0.5], fragment_width, (fragment.height + v_gap)
201
198
  pdf.fill_color prev_fill_color
202
- fragment.conceal if fragment.text == DummyText
199
+ fragment.conceal true if fragment.text == DummyText
203
200
  nil
204
201
  end
205
202
  end
@@ -10,6 +10,7 @@ module Rouge
10
10
  # Deviate from pastie here since our italic is actually a thinner font
11
11
  style Comment, fg: '#888888' #, italic: true
12
12
  style Comment::Preproc, fg: '#cc0000', bold: true
13
+ style Comment::PreprocFile, fg: '#cc0000', bold: true if defined? Comment::PreprocFile
13
14
  # Deviate from pastie here by not using a background color
14
15
  style Comment::Special, fg: '#cc0000', bold: true #, bg: '#fff0f0'
15
16
 
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'rouge'
4
3
  require_relative 'rouge/formatters/prawn'
5
4
  require_relative 'rouge/themes/asciidoctor_pdf_default'
@@ -24,9 +24,9 @@ module Asciidoctor
24
24
  if FormattingSnifferPattern.match? string
25
25
  if (parsed = @parser.parse string)
26
26
  return @transform.apply parsed.content, [], inherited
27
- elsif !@scratch
28
- logger.error %(failed to parse formatted text: #{string})
29
27
  end
28
+ reason = @parser.failure_reason.sub %r/ at line \d+, column \d+ \(byte (\d+)\)(.*)/, '\2 at byte \1'
29
+ logger.error %(failed to parse formatted text: #{string} (reason: #{reason})) unless @scratch
30
30
  end
31
31
  [inherited ? (inherited.merge text: string) : { text: string }]
32
32
  end
@@ -6,16 +6,14 @@ module Asciidoctor::PDF::FormattedText
6
6
 
7
7
  # render_behind is called before the text is printed
8
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
9
+ return if (pdf = fragment.document).scratch?
10
+ name = fragment.format_state[:name]
11
+ pdf.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 true
19
17
  end
20
18
  end
21
19
  end
@@ -37,7 +37,7 @@ module Asciidoctor::PDF::FormattedText
37
37
  doc = @document
38
38
  return if (raw_image_fragments = fragments.select {|f| (f.key? :image_path) && !(f.key? :image_obj) }).empty?
39
39
  scratch = doc.scratch?
40
- available_w = doc.bounds.width
40
+ available_w = available_width
41
41
  available_h = doc.page.empty? ? doc.cursor : doc.bounds.height
42
42
  last_fragment = {}
43
43
  raw_image_fragments.each do |fragment|
@@ -47,92 +47,83 @@ module Asciidoctor::PDF::FormattedText
47
47
  else
48
48
  drop = scratch
49
49
  end
50
- begin
51
- image_path = fragment[:image_path]
50
+ image_path = fragment[:image_path]
51
+ image_w = fragment[:image_width] || '100%'
52
52
 
53
- # NOTE only attempt to convert an unresolved (i.e., String) value
54
- if ::String === (image_w = fragment[:image_width])
55
- image_w = [available_w, (image_w.end_with? '%') ? (image_w.to_f / 100 * available_w) : image_w.to_f].min
56
- end
53
+ # NOTE: intrinsic width is stored behind % symbol
54
+ if (pctidx = image_w.index '%') && pctidx + 1 < image_w.length
55
+ pct = (image_w.slice 0, pctidx).to_f / 100
56
+ intrinsic_w = (image_w.slice pctidx + 1, image_w.length).to_f
57
+ image_w = [available_w, pct * intrinsic_w].min
58
+ else
59
+ image_w = [available_w, pctidx ? (image_w.to_f / 100 * available_w) : image_w.to_f].min
60
+ end
57
61
 
58
- max_image_h = fragment[:image_fit] == 'line' ? [available_h, doc.font.height].min : available_h
62
+ max_image_h = fragment[:image_fit] == 'line' ? [available_h, doc.font.height].min : available_h
59
63
 
60
- # TODO: make helper method to calculate width and height of image
61
- if fragment[:image_format] == 'svg'
62
- svg_obj = ::Prawn::SVG::Interface.new ::File.read(image_path, mode: 'r:UTF-8'), doc,
63
- at: doc.bounds.top_left,
64
- width: image_w,
65
- fallback_font_name: doc.fallback_svg_font_name,
66
- enable_web_requests: doc.allow_uri_read,
67
- enable_file_requests_with_root: (::File.dirname image_path),
68
- cache_images: doc.cache_uri
69
- svg_size = image_w ? svg_obj.document.sizing :
70
- # NOTE convert intrinsic dimensions to points; constrain to content width
71
- (svg_obj.resize width: [(to_pt svg_obj.document.sizing.output_width, :px), available_w].min)
72
- # NOTE the best we can do is make the image fit within full height of bounds
73
- if (image_h = svg_size.output_height) > max_image_h
74
- image_w = (svg_obj.resize height: (image_h = max_image_h)).output_width
75
- else
76
- image_w = svg_size.output_width
77
- end
78
- fragment[:image_obj] = svg_obj
64
+ # TODO: make helper method to calculate width and height of image
65
+ if fragment[:image_format] == 'svg'
66
+ svg_obj = ::Prawn::SVG::Interface.new ::File.read(image_path, mode: 'r:UTF-8'), doc,
67
+ at: doc.bounds.top_left,
68
+ width: image_w,
69
+ fallback_font_name: doc.fallback_svg_font_name,
70
+ enable_web_requests: doc.allow_uri_read ? (doc.method :load_open_uri).to_proc : false,
71
+ enable_file_requests_with_root: (::File.dirname image_path),
72
+ cache_images: doc.cache_uri
73
+ svg_size = svg_obj.document.sizing
74
+ # NOTE: the best we can do is make the image fit within full height of bounds
75
+ if (image_h = svg_size.output_height) > max_image_h
76
+ image_w = (svg_obj.resize height: (image_h = max_image_h)).output_width
79
77
  else
80
- # TODO: cache image info based on path (Prawn caches based on SHA1 of content)
81
- # NOTE image_obj is constrained to image_width by renderer
82
- image_obj, image_info = ::File.open(image_path, 'rb') {|fd| doc.build_image_object fd }
83
- if image_w
84
- if image_w == image_info.width
85
- image_h = image_info.height.to_f
86
- else
87
- image_h = image_w * (image_info.height.fdiv image_info.width)
88
- end
89
- # NOTE convert intrinsic dimensions to points; constrain to content width
90
- elsif (image_w = to_pt image_info.width, :px) > available_w
91
- image_h = (image_w = available_w) * (image_info.height.fdiv image_info.width)
92
- else
93
- image_h = to_pt image_info.height, :px
94
- end
95
- # NOTE the best we can do is make the image fit within full height of bounds
96
- image_w = (image_h = max_image_h) * (image_info.width.fdiv image_info.height) if image_h > max_image_h
97
- fragment[:image_obj] = image_obj
98
- fragment[:image_info] = image_info
78
+ image_w = svg_size.output_width
99
79
  end
100
-
101
- doc.fragment_font fragment do
102
- # NOTE if image height exceeds line height by more than 1.5x, increase the line height
103
- # FIXME: we could really use a nicer API from Prawn here; this is an ugly hack
104
- if (f_height = image_h) > (line_font = doc.font).height * 1.5
105
- # align with descender (equivalent to vertical-align: bottom in CSS)
106
- fragment[:ascender] = f_height - (fragment[:descender] = line_font.descender)
107
- doc.font_size (fragment[:size] = f_height * (doc.font_size / line_font.height))
108
- # align with baseline (roughly equivalent to vertical-align: baseline in CSS)
109
- #fragment[:ascender] = f_height
110
- #fragment[:descender] = 0
111
- #doc.font_size(fragment[:size] = (f_height + line_font.descender) * (doc.font_size / line_font.height))
112
- fragment[:line_height_increased] = true
113
- end
80
+ fragment[:image_obj] = svg_obj
81
+ else
82
+ # TODO: cache image info based on path (Prawn caches based on SHA1 of content)
83
+ # NOTE: image_obj is constrained to image_width by renderer
84
+ image_obj, image_info = ::File.open(image_path, 'rb') {|fd| doc.build_image_object fd }
85
+ if (image_h = image_w * (image_info.height.fdiv image_info.width)) > max_image_h
86
+ # NOTE: the best we can do is make the image fit within full height of bounds
87
+ image_w = (image_h = max_image_h) * (image_info.width.fdiv image_info.height)
114
88
  end
89
+ fragment[:image_obj] = image_obj
90
+ fragment[:image_info] = image_info
91
+ end
115
92
 
116
- # NOTE we can't rely on the fragment width because the line wrap mechanism ignores it;
117
- # it only considers the text (string) and character spacing, rebuilding the string several times
118
- fragment[:text] = PlaceholderChar
119
- fragment[:actual_character_spacing] = doc.character_spacing
120
- fragment[:character_spacing] = image_w
121
- fragment[:image_width] = fragment[:width] = image_w
122
- fragment[:image_height] = image_h
123
- rescue
124
- logger.warn %(could not embed image: #{image_path}; #{$!.message}#{::Prawn::Errors::UnsupportedImageType === $! && !(defined? ::GMagick::Image) ? '; install prawn-gmagick gem to add support' : ''}) unless scratch
125
- drop = true # delegate to cleanup logic in ensure block
126
- ensure
127
- # NOTE skip rendering image in scratch document or if image can't be loaded
128
- if drop
129
- fragment.delete :callback
130
- fragment.delete :image_info
131
- # NOTE retain key to indicate we've visited fragment already
132
- fragment[:image_obj] = nil
93
+ doc.save_font do
94
+ # NOTE: if image height exceeds line height by more than 1.5x, increase the line height
95
+ # FIXME: we could really use a nicer API from Prawn here; this is an ugly hack
96
+ if (f_height = image_h) > (line_font = doc.font).height * 1.5
97
+ # align with descender (equivalent to vertical-align: bottom in CSS)
98
+ fragment[:ascender] = f_height - (fragment[:descender] = line_font.descender)
99
+ doc.font_size (fragment[:size] = f_height * (doc.font_size / line_font.height))
100
+ # align with baseline (roughly equivalent to vertical-align: baseline in CSS)
101
+ #fragment[:ascender] = f_height
102
+ #fragment[:descender] = 0
103
+ #doc.font_size(fragment[:size] = (f_height + line_font.descender) * (doc.font_size / line_font.height))
104
+ fragment[:line_height_increased] = true
133
105
  end
134
- last_fragment = fragment
135
106
  end
107
+
108
+ # NOTE: we can't rely on the fragment width because the line wrap mechanism ignores it;
109
+ # it only considers the text (string) and character spacing, rebuilding the string several times
110
+ fragment[:text] = PlaceholderChar
111
+ fragment[:actual_character_spacing] = doc.character_spacing
112
+ fragment[:character_spacing] = image_w
113
+ fragment[:image_width] = fragment[:width] = image_w
114
+ fragment[:image_height] = image_h
115
+ rescue
116
+ logger.warn %(could not embed image: #{image_path}; #{$!.message}#{::Prawn::Errors::UnsupportedImageType === $! && !(defined? ::GMagick::Image) ? '; install prawn-gmagick gem to add support' : ''}) unless scratch
117
+ drop = true # delegate to cleanup logic in ensure block
118
+ ensure
119
+ # NOTE: skip rendering image in scratch document or if image can't be loaded
120
+ if drop
121
+ fragment.delete :callback
122
+ fragment.delete :image_info
123
+ # NOTE: retain key to indicate we've visited fragment already
124
+ fragment[:image_obj] = nil
125
+ end
126
+ last_fragment = fragment
136
127
  end
137
128
  end
138
129
  end
@@ -2,6 +2,8 @@
2
2
 
3
3
  module Asciidoctor::PDF::FormattedText
4
4
  module InlineImageRenderer
5
+ include ::Asciidoctor::Logging
6
+
5
7
  module_function
6
8
 
7
9
  # Embeds the image object in this fragment into the document in place of the
@@ -26,20 +28,15 @@ module Asciidoctor::PDF::FormattedText
26
28
  image_top = fragment.top - ((fragment.height - data[:image_height]) / 2.0)
27
29
  end
28
30
  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)
31
+ if Prawn::SVG::Interface === (image_obj = data[:image_obj])
32
+ image_obj.options[:at] = [image_left, image_top]
33
+ # NOTE: prawn-svg messes with the cursor; use float to workaround
34
34
  pdf.float do
35
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
36
+ image_obj.document.warnings.each {|img_warning| logger.warn %(problem encountered in image: #{data[:image_path]}; #{img_warning}) }
40
37
  end
41
38
  else
42
- pdf.embed_image data[:image_obj], data[:image_info], at: [image_left, image_top], width: data[:image_width], height: data[:image_height]
39
+ pdf.embed_image image_obj, data[:image_info], at: [image_left, image_top], width: data[:image_width], height: data[:image_height]
43
40
  end
44
41
  # ...or use the public interface, loading the image again
45
42
  #pdf.image data[:image_path], at: [image_left, image_top], width: data[:image_width]
@@ -10,10 +10,8 @@ module Asciidoctor::PDF::FormattedText
10
10
  x = fragment.left
11
11
  y = fragment.baseline
12
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
13
+ if (align == :center || align == :right) && (gap_width = fragment.width - (document.width_of text)) != 0
14
+ x += gap_width * (align == :center ? 0.5 : 1)
17
15
  end
18
16
  document.draw_text! text, at: [x, y]
19
17
  fragment.conceal