prawnx 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. checksums.yaml +7 -0
  2. data/COPYING +2 -0
  3. data/GPLv2 +339 -0
  4. data/GPLv3 +674 -0
  5. data/LICENSE +65 -0
  6. data/LICENSE-prawn-svg +21 -0
  7. data/README.md +107 -0
  8. data/data/fonts/Courier-Bold.afm +342 -0
  9. data/data/fonts/Courier-BoldOblique.afm +342 -0
  10. data/data/fonts/Courier-Oblique.afm +342 -0
  11. data/data/fonts/Courier.afm +342 -0
  12. data/data/fonts/Helvetica-Bold.afm +2827 -0
  13. data/data/fonts/Helvetica-BoldOblique.afm +2827 -0
  14. data/data/fonts/Helvetica-Oblique.afm +3051 -0
  15. data/data/fonts/Helvetica.afm +3051 -0
  16. data/data/fonts/MustRead.html +19 -0
  17. data/data/fonts/Symbol.afm +213 -0
  18. data/data/fonts/Times-Bold.afm +2588 -0
  19. data/data/fonts/Times-BoldItalic.afm +2384 -0
  20. data/data/fonts/Times-Italic.afm +2667 -0
  21. data/data/fonts/Times-Roman.afm +2419 -0
  22. data/data/fonts/ZapfDingbats.afm +225 -0
  23. data/lib/pdf/core.rb +4 -0
  24. data/lib/prawn/measurement_extensions.rb +4 -0
  25. data/lib/prawn/svg/attributes/clip_path.rb +46 -0
  26. data/lib/prawn/svg/attributes/mask.rb +16 -0
  27. data/lib/prawn/svg/attributes/opacity.rb +30 -0
  28. data/lib/prawn/svg/attributes/space.rb +10 -0
  29. data/lib/prawn/svg/attributes/stroke.rb +50 -0
  30. data/lib/prawn/svg/attributes/transform.rb +10 -0
  31. data/lib/prawn/svg/attributes.rb +6 -0
  32. data/lib/prawn/svg/calculators/arc_to_bezier_curve.rb +57 -0
  33. data/lib/prawn/svg/calculators/aspect_ratio.rb +60 -0
  34. data/lib/prawn/svg/calculators/document_sizing.rb +103 -0
  35. data/lib/prawn/svg/calculators/path_length.rb +168 -0
  36. data/lib/prawn/svg/calculators/pixels.rb +57 -0
  37. data/lib/prawn/svg/color.rb +240 -0
  38. data/lib/prawn/svg/css/font_face_parser.rb +72 -0
  39. data/lib/prawn/svg/css/font_family_parser.rb +39 -0
  40. data/lib/prawn/svg/css/font_parser.rb +46 -0
  41. data/lib/prawn/svg/css/selector_parser.rb +178 -0
  42. data/lib/prawn/svg/css/stylesheets.rb +233 -0
  43. data/lib/prawn/svg/css/values_parser.rb +68 -0
  44. data/lib/prawn/svg/document.rb +170 -0
  45. data/lib/prawn/svg/elements/anchor.rb +10 -0
  46. data/lib/prawn/svg/elements/base.rb +312 -0
  47. data/lib/prawn/svg/elements/bbox_scaling.rb +78 -0
  48. data/lib/prawn/svg/elements/call_duplicator.rb +37 -0
  49. data/lib/prawn/svg/elements/circle.rb +25 -0
  50. data/lib/prawn/svg/elements/clip_path.rb +26 -0
  51. data/lib/prawn/svg/elements/container.rb +10 -0
  52. data/lib/prawn/svg/elements/direct_render_base.rb +27 -0
  53. data/lib/prawn/svg/elements/ellipse.rb +22 -0
  54. data/lib/prawn/svg/elements/gradient.rb +184 -0
  55. data/lib/prawn/svg/elements/ignored.rb +5 -0
  56. data/lib/prawn/svg/elements/image.rb +177 -0
  57. data/lib/prawn/svg/elements/line.rb +27 -0
  58. data/lib/prawn/svg/elements/marker.rb +72 -0
  59. data/lib/prawn/svg/elements/mask.rb +65 -0
  60. data/lib/prawn/svg/elements/path.rb +294 -0
  61. data/lib/prawn/svg/elements/pattern.rb +150 -0
  62. data/lib/prawn/svg/elements/polygon.rb +25 -0
  63. data/lib/prawn/svg/elements/polyline.rb +23 -0
  64. data/lib/prawn/svg/elements/rect.rb +64 -0
  65. data/lib/prawn/svg/elements/root.rb +22 -0
  66. data/lib/prawn/svg/elements/switch.rb +92 -0
  67. data/lib/prawn/svg/elements/text.rb +99 -0
  68. data/lib/prawn/svg/elements/text_component.rb +346 -0
  69. data/lib/prawn/svg/elements/text_node.rb +308 -0
  70. data/lib/prawn/svg/elements/text_path.rb +153 -0
  71. data/lib/prawn/svg/elements/use.rb +142 -0
  72. data/lib/prawn/svg/elements/viewport.rb +29 -0
  73. data/lib/prawn/svg/elements.rb +46 -0
  74. data/lib/prawn/svg/extension.rb +25 -0
  75. data/lib/prawn/svg/font.rb +20 -0
  76. data/lib/prawn/svg/font_metrics.rb +144 -0
  77. data/lib/prawn/svg/font_registry.rb +315 -0
  78. data/lib/prawn/svg/funciri.rb +14 -0
  79. data/lib/prawn/svg/gradient_renderer.rb +332 -0
  80. data/lib/prawn/svg/gradients.rb +53 -0
  81. data/lib/prawn/svg/interface.rb +72 -0
  82. data/lib/prawn/svg/length.rb +43 -0
  83. data/lib/prawn/svg/link_renderer.rb +40 -0
  84. data/lib/prawn/svg/loaders/data.rb +17 -0
  85. data/lib/prawn/svg/loaders/file.rb +111 -0
  86. data/lib/prawn/svg/loaders/web.rb +24 -0
  87. data/lib/prawn/svg/paint.rb +67 -0
  88. data/lib/prawn/svg/pathable.rb +153 -0
  89. data/lib/prawn/svg/pattern_renderer.rb +112 -0
  90. data/lib/prawn/svg/pdfmatrix.rb +37 -0
  91. data/lib/prawn/svg/percentage.rb +24 -0
  92. data/lib/prawn/svg/properties.rb +329 -0
  93. data/lib/prawn/svg/renderer.rb +249 -0
  94. data/lib/prawn/svg/state.rb +24 -0
  95. data/lib/prawn/svg/transform_parser.rb +78 -0
  96. data/lib/prawn/svg/ttc.rb +35 -0
  97. data/lib/prawn/svg/ttf.rb +95 -0
  98. data/lib/prawn/svg/url_loader.rb +46 -0
  99. data/lib/prawn/svg/version.rb +5 -0
  100. data/lib/prawn/table.rb +4 -0
  101. data/lib/prawn-svg.rb +53 -0
  102. data/lib/prawn.rb +10 -0
  103. data/lib/prawnx/annotations.rb +21 -0
  104. data/lib/prawnx/document/bounding_box.rb +193 -0
  105. data/lib/prawnx/document/column_box.rb +91 -0
  106. data/lib/prawnx/document/internals.rb +144 -0
  107. data/lib/prawnx/document/span.rb +25 -0
  108. data/lib/prawnx/document.rb +253 -0
  109. data/lib/prawnx/encoding.rb +29 -0
  110. data/lib/prawnx/errors.rb +37 -0
  111. data/lib/prawnx/font.rb +214 -0
  112. data/lib/prawnx/fonts/afm.rb +171 -0
  113. data/lib/prawnx/fonts/otf.rb +50 -0
  114. data/lib/prawnx/fonts/ttf.rb +410 -0
  115. data/lib/prawnx/graphic_state.rb +41 -0
  116. data/lib/prawnx/graphics/color.rb +148 -0
  117. data/lib/prawnx/graphics/patterns.rb +100 -0
  118. data/lib/prawnx/graphics/style.rb +171 -0
  119. data/lib/prawnx/graphics.rb +208 -0
  120. data/lib/prawnx/grid.rb +216 -0
  121. data/lib/prawnx/images/image.rb +244 -0
  122. data/lib/prawnx/images/png_decoder.rb +65 -0
  123. data/lib/prawnx/images.rb +115 -0
  124. data/lib/prawnx/measurement_extensions.rb +40 -0
  125. data/lib/prawnx/measurements.rb +62 -0
  126. data/lib/prawnx/outline.rb +146 -0
  127. data/lib/prawnx/page.rb +125 -0
  128. data/lib/prawnx/page_geometry.rb +27 -0
  129. data/lib/prawnx/pdf/outline_node.rb +53 -0
  130. data/lib/prawnx/pdf/reference.rb +31 -0
  131. data/lib/prawnx/pdf/writer.rb +105 -0
  132. data/lib/prawnx/pdf.rb +66 -0
  133. data/lib/prawnx/pdf_a/srgb_profile.rb +61 -0
  134. data/lib/prawnx/pdf_a.rb +130 -0
  135. data/lib/prawnx/pdf_core.rb +25 -0
  136. data/lib/prawnx/repeater.rb +79 -0
  137. data/lib/prawnx/security.rb +108 -0
  138. data/lib/prawnx/stamp.rb +74 -0
  139. data/lib/prawnx/table/cell/kinds.rb +246 -0
  140. data/lib/prawnx/table/cell.rb +331 -0
  141. data/lib/prawnx/table/cells.rb +208 -0
  142. data/lib/prawnx/table.rb +296 -0
  143. data/lib/prawnx/text/box.rb +16 -0
  144. data/lib/prawnx/text/formatted/arranger.rb +217 -0
  145. data/lib/prawnx/text/formatted/box.rb +312 -0
  146. data/lib/prawnx/text/formatted/fragment.rb +205 -0
  147. data/lib/prawnx/text/formatted/line_wrap.rb +204 -0
  148. data/lib/prawnx/text/formatted/parser.rb +149 -0
  149. data/lib/prawnx/text/formatted/wrap.rb +103 -0
  150. data/lib/prawnx/text.rb +281 -0
  151. data/lib/prawnx/transformation_stack.rb +43 -0
  152. data/lib/prawnx/true_type/cff_subset.rb +157 -0
  153. data/lib/prawnx/true_type/file.rb +371 -0
  154. data/lib/prawnx/true_type/open_type_layout.rb +189 -0
  155. data/lib/prawnx/true_type/simple_subsets.rb +94 -0
  156. data/lib/prawnx/true_type/subset.rb +187 -0
  157. data/lib/prawnx/version.rb +7 -0
  158. data/lib/prawnx/view.rb +28 -0
  159. data/lib/prawnx.rb +102 -0
  160. metadata +251 -0
@@ -0,0 +1,99 @@
1
+ module Prawn::SVG
2
+ class Elements::Text < Elements::DirectRenderBase
3
+ Cursor = Struct.new(:x, :y)
4
+
5
+ def parse
6
+ @root_component = Elements::TextComponent.new(document, source, [], state.dup)
7
+ @root_component.process
8
+
9
+ reintroduce_trailing_and_leading_whitespace if @root_component.children
10
+ end
11
+
12
+ def render(prawn, renderer)
13
+ return unless @root_component.children
14
+
15
+ origin_x = @root_component.x_values.first
16
+ origin_y = @root_component.y_values.first
17
+
18
+ @root_component.lay_out(prawn)
19
+
20
+ translate_x =
21
+ case @root_component.computed_properties.text_anchor
22
+ when 'middle'
23
+ -@root_component.calculated_width / 2.0
24
+ when 'end'
25
+ -@root_component.calculated_width
26
+ end
27
+
28
+ cursor = Cursor.new(0, document.sizing.output_height)
29
+
30
+ if vertical_writing_mode?
31
+ render_vertical(prawn, renderer, cursor, translate_x, origin_x, origin_y)
32
+ else
33
+ @root_component.render_component(prawn, renderer, cursor, translate_x)
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def vertical_writing_mode?
40
+ wm = @root_component.computed_properties.writing_mode
41
+ ['vertical-rl', 'vertical-lr'].include?(wm)
42
+ end
43
+
44
+ def render_vertical(prawn, renderer, cursor, translate_x, origin_x, origin_y)
45
+ origin_x ||= 0
46
+ origin_y ||= document.sizing.output_height
47
+
48
+ prawn.rotate(-90, origin: [origin_x, origin_y]) do
49
+ @root_component.render_component(prawn, renderer, cursor, translate_x)
50
+ end
51
+ end
52
+
53
+ def reintroduce_trailing_and_leading_whitespace
54
+ text_nodes = []
55
+ build_text_node_queue(text_nodes, @root_component)
56
+
57
+ remove_whitespace_only_text_nodes_and_start_and_end(text_nodes)
58
+ remove_text_nodes_that_are_completely_empty(text_nodes)
59
+ apportion_leading_and_trailing_spaces(text_nodes)
60
+ end
61
+
62
+ def build_text_node_queue(queue, component)
63
+ return unless component.children
64
+
65
+ component.children.each do |element|
66
+ case element
67
+ when Elements::TextNode
68
+ queue << element
69
+ else
70
+ build_text_node_queue(queue, element)
71
+ end
72
+ end
73
+ end
74
+
75
+ def remove_whitespace_only_text_nodes_and_start_and_end(text_nodes)
76
+ text_nodes.pop while text_nodes.last && text_nodes.last.text.empty?
77
+ text_nodes.shift while text_nodes.first && text_nodes.first.text.empty?
78
+ end
79
+
80
+ def remove_text_nodes_that_are_completely_empty(text_nodes)
81
+ text_nodes.reject! do |text_node|
82
+ text_node.text.empty? && !text_node.trailing_space? && !text_node.leading_space?
83
+ end
84
+ end
85
+
86
+ def apportion_leading_and_trailing_spaces(text_nodes)
87
+ text_nodes.each_cons(2) do |a, b|
88
+ if a.text.empty?
89
+ # Empty strings can only get a leading space from the previous non-empty text,
90
+ # and never get a trailing space
91
+ elsif a.trailing_space?
92
+ a.text += ' '
93
+ elsif b.leading_space?
94
+ b.text = " #{b.text}"
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,346 @@
1
+ module Prawn::SVG
2
+ class Elements::TextComponent < Elements::DirectRenderBase
3
+ attr_reader :children, :parent_component
4
+ attr_reader :x_values, :y_values, :dx, :dy, :rotation, :text_length, :length_adjust
5
+ attr_reader :font, :fallback_fonts
6
+ attr_reader :inside_clip_path
7
+
8
+ def initialize(document, source, _calls, state, parent_component = nil)
9
+ if parent_component.nil? && source.name != 'text'
10
+ raise SkipElementError, 'attempted to <use> a component inside a text element, this is not supported'
11
+ end
12
+
13
+ super(document, source, [], state)
14
+ @parent_component = parent_component
15
+ end
16
+
17
+ def parse
18
+ @inside_clip_path = state.inside_clip_path
19
+
20
+ @x_values = parse_wsp('x').map { |n| x(n) }
21
+ @y_values = parse_wsp('y').map { |n| y(n) }
22
+ @dx = parse_wsp('dx').map { |n| x_pixels(n) }
23
+ @dy = parse_wsp('dy').map { |n| y_pixels(n) }
24
+ @rotation = parse_wsp('rotate').map(&:to_f)
25
+ @text_length = normalize_length(attributes['textLength'])
26
+ @length_adjust = attributes['lengthAdjust']
27
+
28
+ @font, @fallback_fonts = select_fonts
29
+
30
+ @children = svg_text_children.flat_map do |child|
31
+ if child.node_type == :text
32
+ build_text_node(child)
33
+ else
34
+ case child.name
35
+ when 'tspan', 'tref'
36
+ build_child(child)
37
+ when 'textPath'
38
+ build_text_path(child)
39
+ else
40
+ warnings << "Unknown tag '#{child.name}' inside text tag; ignoring"
41
+ []
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ def lay_out(prawn)
48
+ with_svg_fonts(prawn) do
49
+ @children.each do |child|
50
+ child.lay_out(prawn)
51
+ end
52
+ end
53
+
54
+ if @text_length
55
+ flexible_width, fixed_width = total_flexible_and_fixed_width
56
+
57
+ if flexible_width.positive?
58
+ target_width = [@text_length - fixed_width, 0].max
59
+ factor = target_width / flexible_width
60
+ apply_factor_to_base_width(factor)
61
+ end
62
+ end
63
+ end
64
+
65
+ def render_component(prawn, renderer, cursor, translate_x = nil, accumulated_baseline_shift = 0)
66
+ raise SkipElementQuietly if computed_properties.display == 'none'
67
+
68
+ add_yield_call do
69
+ prawn.translate(translate_x, 0) if translate_x
70
+
71
+ size = computed_properties.numeric_font_size
72
+
73
+ y_offset = dominant_baseline_offset(prawn, size || prawn.font_size) || 0
74
+
75
+ with_svg_fonts(prawn) do
76
+ total_baseline_shift = accumulated_baseline_shift + baseline_shift_offset(prawn, size)
77
+ y_offset += total_baseline_shift
78
+
79
+ children.each do |child|
80
+ case child
81
+ when Elements::TextNode
82
+ child.render(prawn, size, cursor, y_offset)
83
+ when self.class
84
+ prawn.save_graphics_state
85
+ child.render_component(prawn, renderer, cursor, nil, total_baseline_shift)
86
+ prawn.restore_graphics_state
87
+ else
88
+ raise
89
+ end
90
+ end
91
+ end
92
+ end
93
+
94
+ renderer.render_calls(prawn, base_calls)
95
+ end
96
+
97
+ def calculated_width
98
+ children.reduce(0) { |total, child| total + child.calculated_width }
99
+ end
100
+
101
+ def current_length_adjust_is_scaling?
102
+ if @text_length
103
+ @length_adjust == 'spacingAndGlyphs'
104
+ elsif parent_component
105
+ parent_component.current_length_adjust_is_scaling?
106
+ else
107
+ false
108
+ end
109
+ end
110
+
111
+ # Resolves the effective kerning setting from the SVG 1.1 kerning property and the
112
+ # SVG 2 font-kerning property. kerning takes precedence when non-default; otherwise
113
+ # font-kerning is used.
114
+ def kerning_enabled?
115
+ if computed_properties.kerning == 'auto'
116
+ computed_properties.font_kerning != 'none'
117
+ else
118
+ false
119
+ end
120
+ end
121
+
122
+ def kerning_pixels
123
+ if computed_properties.kerning == 'auto'
124
+ nil
125
+ else
126
+ x_pixels(computed_properties.kerning)
127
+ end
128
+ end
129
+
130
+ def letter_spacing_pixels
131
+ if computed_properties.letter_spacing == 'normal'
132
+ nil
133
+ else
134
+ x_pixels(computed_properties.letter_spacing)
135
+ end
136
+ end
137
+
138
+ def word_spacing_pixels
139
+ if computed_properties.word_spacing == 'normal'
140
+ nil
141
+ else
142
+ x_pixels(computed_properties.word_spacing)
143
+ end
144
+ end
145
+
146
+ protected
147
+
148
+ def build_text_node(child)
149
+ if state.preserve_space
150
+ text = child.value.tr("\n\t", ' ')
151
+ else
152
+ text = child.value.tr("\n", '').tr("\t", ' ')
153
+ leading = text[0] == ' '
154
+ trailing = text[-1] == ' '
155
+ text = text.strip.gsub(/ {2,}/, ' ')
156
+ end
157
+
158
+ Elements::TextNode.new(self, text, leading, trailing)
159
+ end
160
+
161
+ def build_child(child)
162
+ component = self.class.new(document, child, [], state.dup, self)
163
+ component.process
164
+ component
165
+ end
166
+
167
+ def build_text_path(child)
168
+ component = Elements::TextPath.new(document, child, [], state.dup, self)
169
+ component.process
170
+ return [] unless component.children
171
+
172
+ component
173
+ end
174
+
175
+ def svg_text_children
176
+ text_children.select do |child|
177
+ child.node_type == :text || (
178
+ child.node_type == :element &&
179
+ [SVG_NAMESPACE, ''].include?(child.namespace)
180
+
181
+ )
182
+ end
183
+ end
184
+
185
+ def text_children
186
+ if name == 'tref'
187
+ reference = find_referenced_element
188
+ reference ? reference.source.children : []
189
+ else
190
+ source.children
191
+ end
192
+ end
193
+
194
+ def find_referenced_element
195
+ href = href_attribute
196
+
197
+ if href && href[0..0] == '#'
198
+ element = document.elements_by_id[href[1..]]
199
+ element if element.name == 'text'
200
+ end
201
+ end
202
+
203
+ def select_fonts
204
+ font_style = :italic if computed_properties.font_style == 'italic'
205
+ font_weight = computed_properties.font_weight
206
+ font_stretch = computed_properties.font_stretch
207
+ fonts = []
208
+
209
+ font_family_names.each do |name|
210
+ font = document.font_registry.load(name, font_weight, font_style, font_stretch)
211
+ next unless font
212
+ next if fonts.any? { |existing| existing.name == font.name && existing.subfamily == font.subfamily }
213
+
214
+ fonts << font
215
+ end
216
+
217
+ _, *fallback_font_names = fonts.map(&:name).uniq
218
+
219
+ return [fonts.first, fallback_font_names] if fonts.any?
220
+
221
+ warnings << "Font family '#{computed_properties.font_family}' style '#{computed_properties.font_style}' is not a known font, and the fallback font could not be found."
222
+ []
223
+ end
224
+
225
+ def with_svg_fonts(prawn)
226
+ prawn.save_font do
227
+ if font
228
+ prawn.font(font.name, style: font.subfamily)
229
+ prawn.fallback_fonts(fallback_fonts)
230
+ end
231
+
232
+ yield
233
+ end
234
+ end
235
+
236
+ def font_family_names
237
+ names = CSS::FontFamilyParser.parse(computed_properties.font_family.to_s)
238
+ names << document.fallback_font_name if document.fallback_font_name
239
+ names.map(&:strip).reject(&:empty?)
240
+ end
241
+
242
+ def total_flexible_and_fixed_width
243
+ flexible = fixed = 0
244
+ @children.each do |child|
245
+ child.total_flexible_and_fixed_width.tap do |a, b|
246
+ flexible += a
247
+ fixed += b
248
+ end
249
+ end
250
+ [flexible, fixed]
251
+ end
252
+
253
+ def apply_factor_to_base_width(factor)
254
+ @children.each do |child|
255
+ if child.is_a?(Elements::TextNode)
256
+ child.chunks.reject(&:fixed_width).each do |chunk|
257
+ chunk.fixed_width = chunk.base_width * factor
258
+ end
259
+ elsif child.is_a?(self.class)
260
+ child.apply_factor_to_base_width(factor)
261
+ else
262
+ raise
263
+ end
264
+ end
265
+ end
266
+
267
+ # overridden from Base, we don't want to call fill/stroke as draw_text does this for us
268
+ def apply_drawing_call; end
269
+
270
+ # overridden from Base, transforms can't be applied to tspan elements
271
+ def transformable?
272
+ source.name != 'tspan'
273
+ end
274
+
275
+ # overridden from Base, we want the id to point to the Text element
276
+ def add_to_elements_by_id?
277
+ source.name != 'text'
278
+ end
279
+
280
+ def dominant_baseline_offset(prawn, size)
281
+ baseline = effective_baseline
282
+ return nil if baseline.nil?
283
+
284
+ case baseline
285
+ when 'middle'
286
+ -FontMetrics.x_height_in_points(prawn, size) / 2.0
287
+ when 'mathematical'
288
+ -((prawn.font.ascender + prawn.font.descender) / prawn.font_size) * size / 2.0
289
+ when 'central'
290
+ -((prawn.font.ascender - prawn.font.descender) / prawn.font_size) * size / 2.0
291
+ when 'hanging'
292
+ -(prawn.font.ascender / prawn.font_size) * size
293
+ when 'text-before-edge', 'before-edge'
294
+ (-(prawn.font.ascender / prawn.font_size) * size) - half_leading(prawn, size)
295
+ when 'text-after-edge', 'after-edge', 'ideographic'
296
+ ((prawn.font.descender / prawn.font_size) * size) + half_leading(prawn, size)
297
+ end
298
+ end
299
+
300
+ def effective_baseline
301
+ ab = computed_properties.alignment_baseline
302
+ if ab && ab != 'auto' && ab != 'baseline'
303
+ ab
304
+ else
305
+ db = computed_properties.dominant_baseline
306
+ db unless ['auto', 'alphabetic'].include?(db)
307
+ end
308
+ end
309
+
310
+ def baseline_shift_offset(prawn, font_size)
311
+ value = computed_properties.baseline_shift
312
+ return 0 if value.nil? || value == 'baseline'
313
+
314
+ case value
315
+ when 'super'
316
+ sub_super_offset(prawn, font_size)
317
+ when 'sub'
318
+ -sub_super_offset(prawn, font_size)
319
+ when Percentage
320
+ parent_font_size = parent_component&.computed_properties&.numeric_font_size || font_size
321
+ value.to_factor * parent_font_size
322
+ when Length
323
+ value.to_pixels(nil, font_size)
324
+ else
325
+ 0
326
+ end
327
+ end
328
+
329
+ def half_leading(prawn, size)
330
+ (size - ((prawn.font.ascender + prawn.font.descender) / prawn.font_size * size)) / 2.0
331
+ end
332
+
333
+ def sub_super_offset(prawn, font_size)
334
+ ascender_ratio = prawn.font.ascender / prawn.font_size
335
+ ascender_ratio * font_size / 1.2
336
+ end
337
+
338
+ def normalize_length(length)
339
+ x_pixels(length) if length&.match(/\d/)
340
+ end
341
+
342
+ def parse_wsp(name)
343
+ (attributes[name] || '').split(COMMA_WSP_REGEXP)
344
+ end
345
+ end
346
+ end