hexapdf 0.4.0 → 0.5.0

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 (90) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +46 -0
  3. data/CONTRIBUTERS +1 -1
  4. data/README.md +5 -5
  5. data/VERSION +1 -1
  6. data/examples/emoji-smile.png +0 -0
  7. data/examples/emoji-wink.png +0 -0
  8. data/examples/graphics.rb +9 -8
  9. data/examples/standard_pdf_fonts.rb +2 -1
  10. data/examples/text_box_alignment.rb +47 -0
  11. data/examples/text_box_inline_boxes.rb +56 -0
  12. data/examples/text_box_line_wrapping.rb +57 -0
  13. data/examples/text_box_shapes.rb +166 -0
  14. data/examples/text_box_styling.rb +72 -0
  15. data/examples/truetype.rb +3 -4
  16. data/lib/hexapdf/cli/optimize.rb +2 -2
  17. data/lib/hexapdf/configuration.rb +8 -6
  18. data/lib/hexapdf/content/canvas.rb +8 -5
  19. data/lib/hexapdf/content/parser.rb +3 -2
  20. data/lib/hexapdf/content/processor.rb +14 -3
  21. data/lib/hexapdf/document.rb +1 -0
  22. data/lib/hexapdf/document/fonts.rb +2 -1
  23. data/lib/hexapdf/document/pages.rb +23 -0
  24. data/lib/hexapdf/font/invalid_glyph.rb +78 -0
  25. data/lib/hexapdf/font/true_type/font.rb +14 -3
  26. data/lib/hexapdf/font/true_type/table.rb +1 -0
  27. data/lib/hexapdf/font/true_type/table/cmap.rb +1 -1
  28. data/lib/hexapdf/font/true_type/table/cmap_subtable.rb +1 -0
  29. data/lib/hexapdf/font/true_type/table/glyf.rb +4 -0
  30. data/lib/hexapdf/font/true_type/table/kern.rb +170 -0
  31. data/lib/hexapdf/font/true_type/table/post.rb +5 -1
  32. data/lib/hexapdf/font/true_type_wrapper.rb +71 -24
  33. data/lib/hexapdf/font/type1/afm_parser.rb +3 -2
  34. data/lib/hexapdf/font/type1/character_metrics.rb +0 -9
  35. data/lib/hexapdf/font/type1/font.rb +11 -0
  36. data/lib/hexapdf/font/type1/font_metrics.rb +6 -1
  37. data/lib/hexapdf/font/type1_wrapper.rb +51 -7
  38. data/lib/hexapdf/font_loader/standard14.rb +1 -1
  39. data/lib/hexapdf/layout.rb +51 -0
  40. data/lib/hexapdf/layout/inline_box.rb +95 -0
  41. data/lib/hexapdf/layout/line_fragment.rb +333 -0
  42. data/lib/hexapdf/layout/numeric_refinements.rb +56 -0
  43. data/lib/hexapdf/layout/style.rb +365 -0
  44. data/lib/hexapdf/layout/text_box.rb +727 -0
  45. data/lib/hexapdf/layout/text_fragment.rb +206 -0
  46. data/lib/hexapdf/layout/text_shaper.rb +155 -0
  47. data/lib/hexapdf/task.rb +0 -1
  48. data/lib/hexapdf/task/dereference.rb +1 -1
  49. data/lib/hexapdf/tokenizer.rb +3 -2
  50. data/lib/hexapdf/type/font_descriptor.rb +2 -1
  51. data/lib/hexapdf/type/font_type0.rb +3 -1
  52. data/lib/hexapdf/type/form.rb +12 -4
  53. data/lib/hexapdf/version.rb +1 -1
  54. data/test/hexapdf/common_tokenizer_tests.rb +7 -0
  55. data/test/hexapdf/content/common.rb +8 -0
  56. data/test/hexapdf/content/test_canvas.rb +10 -22
  57. data/test/hexapdf/content/test_processor.rb +4 -1
  58. data/test/hexapdf/document/test_pages.rb +16 -0
  59. data/test/hexapdf/font/test_invalid_glyph.rb +34 -0
  60. data/test/hexapdf/font/test_true_type_wrapper.rb +25 -11
  61. data/test/hexapdf/font/test_type1_wrapper.rb +26 -10
  62. data/test/hexapdf/font/true_type/table/common.rb +27 -0
  63. data/test/hexapdf/font/true_type/table/test_cmap.rb +14 -20
  64. data/test/hexapdf/font/true_type/table/test_cmap_subtable.rb +7 -0
  65. data/test/hexapdf/font/true_type/table/test_glyf.rb +8 -6
  66. data/test/hexapdf/font/true_type/table/test_head.rb +9 -13
  67. data/test/hexapdf/font/true_type/table/test_hhea.rb +16 -23
  68. data/test/hexapdf/font/true_type/table/test_hmtx.rb +4 -7
  69. data/test/hexapdf/font/true_type/table/test_kern.rb +61 -0
  70. data/test/hexapdf/font/true_type/table/test_loca.rb +7 -13
  71. data/test/hexapdf/font/true_type/table/test_maxp.rb +4 -9
  72. data/test/hexapdf/font/true_type/table/test_name.rb +14 -17
  73. data/test/hexapdf/font/true_type/table/test_os2.rb +3 -5
  74. data/test/hexapdf/font/true_type/table/test_post.rb +21 -19
  75. data/test/hexapdf/font/true_type/test_font.rb +4 -0
  76. data/test/hexapdf/font/type1/common.rb +6 -0
  77. data/test/hexapdf/font/type1/test_afm_parser.rb +9 -0
  78. data/test/hexapdf/font/type1/test_font.rb +6 -0
  79. data/test/hexapdf/layout/test_inline_box.rb +40 -0
  80. data/test/hexapdf/layout/test_line_fragment.rb +206 -0
  81. data/test/hexapdf/layout/test_style.rb +143 -0
  82. data/test/hexapdf/layout/test_text_box.rb +640 -0
  83. data/test/hexapdf/layout/test_text_fragment.rb +208 -0
  84. data/test/hexapdf/layout/test_text_shaper.rb +64 -0
  85. data/test/hexapdf/task/test_dereference.rb +1 -0
  86. data/test/hexapdf/test_writer.rb +2 -2
  87. data/test/hexapdf/type/test_font_descriptor.rb +4 -2
  88. data/test/hexapdf/type/test_font_type0.rb +7 -0
  89. data/test/hexapdf/type/test_form.rb +12 -0
  90. metadata +29 -2
@@ -93,4 +93,8 @@ describe HexaPDF::Font::TrueType::Font do
93
93
  it "is able to return the ID of the missing glyph" do
94
94
  assert_equal(0, @font.missing_glyph_id)
95
95
  end
96
+
97
+ it "returns the features available for a font" do
98
+ assert(@font.features.empty?)
99
+ end
96
100
  end
@@ -0,0 +1,6 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'hexapdf/font/type1'
4
+
5
+ FONT_TIMES = HexaPDF::Font::Type1::Font.from_afm(File.join(HexaPDF.data_dir, 'afm', "Times-Roman.afm"))
6
+ FONT_SYMBOL = HexaPDF::Font::Type1::Font.from_afm(File.join(HexaPDF.data_dir, 'afm', "Symbol.afm"))
@@ -1,6 +1,7 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
 
3
3
  require 'test_helper'
4
+ require_relative 'common'
4
5
  require 'hexapdf/font/type1'
5
6
  require 'hexapdf/data_dir'
6
7
  require 'tempfile'
@@ -38,6 +39,14 @@ describe HexaPDF::Font::Type1::AFMParser do
38
39
  end
39
40
  end
40
41
 
42
+ it "extracts kerning and ligature information" do
43
+ metrics = FONT_TIMES.metrics
44
+ glyph = metrics.character_metrics[:f]
45
+ assert_equal([20, 0, 383, 683], glyph.bbox)
46
+ assert_equal(-20, metrics.kerning_pairs.dig(:f, :i))
47
+ assert_equal(:fi, metrics.ligature_pairs.dig(:f, :i))
48
+ end
49
+
41
50
  it "calculates an ascender and descender value from the font bounding box if necessary" do
42
51
  metrics = HexaPDF::Font::Type1::AFMParser.parse(File.join(HexaPDF.data_dir, 'afm/Symbol.afm'))
43
52
  assert_equal(metrics.bounding_box[1], metrics.descender)
@@ -1,6 +1,7 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
 
3
3
  require 'test_helper'
4
+ require_relative 'common'
4
5
  require 'hexapdf/font/type1'
5
6
 
6
7
  describe HexaPDF::Font::Type1::Font do
@@ -65,4 +66,9 @@ describe HexaPDF::Font::Type1::Font do
65
66
  it "is able to return the ID of the missing glyph" do
66
67
  assert_equal(:'.notdef', @font.missing_glyph_id)
67
68
  end
69
+
70
+ it "returns the features available for a font" do
71
+ assert_equal([:kern, :liga].to_set, FONT_TIMES.features)
72
+ assert(FONT_SYMBOL.features.empty?)
73
+ end
68
74
  end
@@ -0,0 +1,40 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/layout/inline_box'
5
+
6
+ describe HexaPDF::Layout::InlineBox do
7
+ before do
8
+ @box = HexaPDF::Layout::InlineBox.new(10, 15) {|box, canvas| [box, canvas]}
9
+ end
10
+
11
+ describe "draw" do
12
+ before do
13
+ @canvas = Object.new
14
+ @canvas.define_singleton_method(:translate) {|x, y, &block| [x, y, block.call] }
15
+ end
16
+
17
+ it "returns the value of the drawing block" do
18
+ assert_equal([1, 2, [@box, @canvas]], @box.draw(@canvas, 1, 2))
19
+ end
20
+ end
21
+
22
+ describe "valign" do
23
+ it "has a default value of :baseline" do
24
+ assert_equal(:baseline, @box.valign)
25
+ end
26
+
27
+ it "can be changed on creation" do
28
+ box = HexaPDF::Layout::InlineBox.new(10, 15, valign: :test) {}
29
+ assert_equal(:test, box.valign)
30
+ end
31
+ end
32
+
33
+ it "returns 0 for x_min" do
34
+ assert_equal(0, @box.x_min)
35
+ end
36
+
37
+ it "returns width for x_max" do
38
+ assert_equal(10, @box.x_max)
39
+ end
40
+ end
@@ -0,0 +1,206 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/document'
5
+
6
+ describe HexaPDF::Layout::LineFragment::HeightCalculator do
7
+ before do
8
+ @calc = HexaPDF::Layout::LineFragment::HeightCalculator.new
9
+ end
10
+
11
+ it "simulate the height as if an item was added" do
12
+ @calc << HexaPDF::Layout::InlineBox.new(10, 20, valign: :baseline) {}
13
+ assert_equal([0, 20, 0, 0], @calc.result)
14
+ new_item = HexaPDF::Layout::InlineBox.new(10, 30, valign: :top) {}
15
+ assert_equal(30, @calc.simulate_height(new_item))
16
+ assert_equal([0, 20, 0, 0], @calc.result)
17
+ end
18
+ end
19
+
20
+ describe HexaPDF::Layout::LineFragment do
21
+ before do
22
+ @doc = HexaPDF::Document.new
23
+ @font = @doc.fonts.load("Times", custom_encoding: true)
24
+ @line = HexaPDF::Layout::LineFragment.new
25
+ end
26
+
27
+ def setup_fragment(text)
28
+ HexaPDF::Layout::TextFragment.create(text, font: @font, font_size: 10)
29
+ end
30
+
31
+ def setup_box(width, height, valign = :baseline)
32
+ HexaPDF::Layout::InlineBox.new(width, height, valign: valign) {}
33
+ end
34
+
35
+ describe "initialize" do
36
+ it "allows setting the items of the line fragment" do
37
+ frag1 = setup_fragment("Hello")
38
+ frag2 = HexaPDF::Layout::TextFragment.new(items: frag1.items.slice!(3, 2), style: frag1.style)
39
+ line = HexaPDF::Layout::LineFragment.new([frag1, frag2])
40
+ assert_equal(1, line.items.count)
41
+ assert_equal(5, line.items[0].items.count)
42
+ end
43
+ end
44
+
45
+ describe "add" do
46
+ it "adds items to the line" do
47
+ @line << :test << :other
48
+ assert_equal([:test, :other], @line.items)
49
+ end
50
+
51
+ it "combines text fragments if possible" do
52
+ frag1 = setup_fragment("Home")
53
+ frag2 = HexaPDF::Layout::TextFragment.new(items: frag1.items.slice!(2, 2), style: frag1.style)
54
+ @line << setup_fragment("o") << :other << frag1 << frag2
55
+ assert_equal(3, @line.items.length)
56
+ assert_equal(4, @line.items.last.items.length)
57
+ end
58
+
59
+ it "duplicates the first of two combinable text fragments if its items are frozen" do
60
+ frag1 = setup_fragment("Home")
61
+ frag2 = HexaPDF::Layout::TextFragment.new(items: frag1.items.slice!(2, 2), style: frag1.style)
62
+ frag1.items.freeze
63
+ frag2.items.freeze
64
+
65
+ @line << setup_fragment("o") << frag1 << frag2 << :other
66
+ assert_equal(3, @line.items.length)
67
+ assert_equal(4, @line.items[-2].items.length)
68
+ end
69
+ end
70
+
71
+ describe "with text fragments" do
72
+ before do
73
+ @frag_h = setup_fragment("H")
74
+ @frag_y = setup_fragment("y")
75
+ @line << @frag_h << @frag_y << @frag_h
76
+ end
77
+
78
+ it "calculates the various x/y values correctly" do
79
+ assert_equal(@frag_h.x_min, @line.x_min)
80
+ assert_equal(@frag_h.width + @frag_y.width + @frag_h.x_max, @line.x_max)
81
+ assert_equal(@frag_y.y_min, @line.y_min)
82
+ assert_equal(@frag_h.y_max, @line.y_max)
83
+ assert_equal(@frag_y.y_min, @line.text_y_min)
84
+ assert_equal(@frag_h.y_max, @line.text_y_max)
85
+ assert_equal(2 * @frag_h.width + @frag_y.width, @line.width)
86
+ assert_equal(@frag_h.y_max - @frag_y.y_min, @line.height)
87
+ end
88
+
89
+ describe "and with inline boxes" do
90
+ it "x_min is correct if an inline box is the first item" do
91
+ @line.items.unshift(setup_box(10, 10))
92
+ assert_equal(0, @line.x_min)
93
+ end
94
+
95
+ it "x_max is correct if an inline box is the last item" do
96
+ @line << setup_box(10, 10)
97
+ assert_equal(@line.width, @line.x_max)
98
+ end
99
+
100
+ it "doesn't change text_y_min/text_y_max" do
101
+ text_y_min, text_y_max = @line.text_y_min, @line.text_y_max
102
+ @line << setup_box(10, 30, :text_top) << setup_box(10, 30, :text_bottom)
103
+ @line.clear_cache
104
+ assert_equal(text_y_min, @line.text_y_min)
105
+ assert_equal(text_y_max, @line.text_y_max)
106
+ end
107
+
108
+ it "y values are not changed if all boxes are smaller than the text's height" do
109
+ *y_values = @line.y_min, @line.y_max, @line.text_y_min, @line.text_y_max
110
+ @line << setup_box(10, 5, :baseline)
111
+ @line.clear_cache
112
+ assert_equal(y_values, [@line.y_min, @line.y_max, @line.text_y_min, @line.text_y_max])
113
+ end
114
+
115
+ it "changes y_max to fit if baseline boxes are higher than the text" do
116
+ y_min = @line.y_min
117
+ box = setup_box(10, 50, :baseline)
118
+ @line.add(box)
119
+
120
+ @line.clear_cache
121
+ assert_equal(50, @line.y_max)
122
+ assert_equal(y_min, @line.y_min)
123
+ end
124
+
125
+ it "changes y_max to fit if text_bottom boxes are higher than the text" do
126
+ y_min = @line.y_min
127
+ box = setup_box(10, 50, :text_bottom)
128
+ @line.add(box)
129
+
130
+ @line.clear_cache
131
+ assert_equal(50 + @line.text_y_min, @line.y_max)
132
+ assert_equal(y_min, @line.y_min)
133
+ end
134
+
135
+ it "changes y_max to fit if bottom boxes are higher than the text" do
136
+ y_min = @line.y_min
137
+ box = setup_box(10, 50, :bottom)
138
+ @line.add(box)
139
+
140
+ @line.clear_cache
141
+ assert_equal(50 + @line.text_y_min, @line.y_max)
142
+ assert_equal(y_min, @line.y_min)
143
+ end
144
+
145
+ it "changes y_min to fit if text_top/top boxes are higher than the text" do
146
+ y_max = @line.y_max
147
+ box = setup_box(10, 50, :text_top)
148
+ @line.add(box)
149
+
150
+ @line.clear_cache
151
+ assert_equal(@line.text_y_max - 50, @line.y_min)
152
+ assert_equal(y_max, @line.y_max)
153
+
154
+ box.instance_variable_set(:@valign, :top)
155
+ @line.clear_cache
156
+ assert_equal(@line.text_y_max - 50, @line.y_min)
157
+ assert_equal(y_max, @line.y_max)
158
+ end
159
+
160
+ it "changes y_min/y_max to fit if boxes are aligned in both directions" do
161
+ @line << setup_box(10, 20, :text_top) <<
162
+ setup_box(10, 20, :text_bottom) <<
163
+ setup_box(10, 20, :top) <<
164
+ setup_box(10, 70, :bottom)
165
+ assert_equal(@line.text_y_max - 20, @line.y_min)
166
+ assert_equal(@line.text_y_max - 20 + 70, @line.y_max)
167
+ end
168
+ end
169
+ end
170
+
171
+ it "fails when accessing a vertical measurement if an item uses an invalid valign value" do
172
+ @line << setup_box(10, 20, :invalid)
173
+ assert_raises(HexaPDF::Error) { @line.y_min }
174
+ end
175
+
176
+ describe "each" do
177
+ it "iterates over all items and yields them with their offset values" do
178
+ @line << setup_fragment("H") <<
179
+ setup_box(10, 10, :top) <<
180
+ setup_box(10, 10, :text_top) <<
181
+ setup_box(10, 10, :baseline) <<
182
+ setup_box(10, 10, :text_bottom) <<
183
+ setup_box(10, 10, :bottom)
184
+ result = [
185
+ [@line.items[0], 0, 0],
186
+ [@line.items[1], @line.items[0].width, @line.y_max - 10],
187
+ [@line.items[2], @line.items[0].width + 10, @line.text_y_max - 10],
188
+ [@line.items[3], @line.items[0].width + 20, 0],
189
+ [@line.items[4], @line.items[0].width + 30, @line.text_y_min],
190
+ [@line.items[5], @line.items[0].width + 40, @line.y_min],
191
+ ]
192
+ assert_equal(result, @line.to_enum(:each).map {|*a| a})
193
+ end
194
+
195
+ it "fails if an item uses an invalid valign value" do
196
+ @line << setup_box(10, 10, :invalid)
197
+ assert_raises(HexaPDF::Error) { @line.each {} }
198
+ end
199
+ end
200
+
201
+ it "allows ignoring line justification" do
202
+ refute(@line.ignore_justification?)
203
+ @line.ignore_justification!
204
+ assert(@line.ignore_justification?)
205
+ end
206
+ end
@@ -0,0 +1,143 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/layout/style'
5
+ require 'hexapdf/layout/text_box'
6
+
7
+ describe HexaPDF::Layout::Style::LineSpacing do
8
+ before do
9
+ @line1 = Object.new
10
+ @line1.define_singleton_method(:y_min) { - 1}
11
+ @line1.define_singleton_method(:y_max) { 2 }
12
+ @line2 = Object.new
13
+ @line2.define_singleton_method(:y_min) { -3 }
14
+ @line2.define_singleton_method(:y_max) { 4 }
15
+ end
16
+
17
+ def line_spacing(type, value = nil)
18
+ HexaPDF::Layout::Style::LineSpacing.new(type, value: value)
19
+ end
20
+
21
+ it "allows single line spacing" do
22
+ obj = line_spacing(:single)
23
+ assert_equal(:proportional, obj.type)
24
+ assert_equal(1, obj.value)
25
+ assert_equal(1 + 4, obj.baseline_distance(@line1, @line2))
26
+ assert_equal(0, obj.gap(@line1, @line2))
27
+ end
28
+
29
+ it "allows double line spacing" do
30
+ obj = line_spacing(:double)
31
+ assert_equal(:proportional, obj.type)
32
+ assert_equal(2, obj.value)
33
+ assert_equal((1 + 4) * 2, obj.baseline_distance(@line1, @line2))
34
+ assert_equal(1 + 4, obj.gap(@line1, @line2))
35
+ end
36
+
37
+ it "allows proportional line spacing" do
38
+ obj = line_spacing(:proportional, 1.5)
39
+ assert_equal(:proportional, obj.type)
40
+ assert_equal(1.5, obj.value)
41
+ assert_equal((1 + 4) * 1.5, obj.baseline_distance(@line1, @line2))
42
+ assert_equal((1 + 4) * 0.5, obj.gap(@line1, @line2))
43
+ end
44
+
45
+ it "allows fixed line spacing" do
46
+ obj = line_spacing(:fixed, 7)
47
+ assert_equal(:fixed, obj.type)
48
+ assert_equal(7, obj.value)
49
+ assert_equal(7, obj.baseline_distance(@line1, @line2))
50
+ assert_equal(7 - 1 - 4, obj.gap(@line1, @line2))
51
+ end
52
+
53
+ it "allows line spacing using a leading value" do
54
+ obj = line_spacing(:leading, 3)
55
+ assert_equal(:leading, obj.type)
56
+ assert_equal(3, obj.value)
57
+ assert_equal(1 + 4 + 3, obj.baseline_distance(@line1, @line2))
58
+ assert_equal(3, obj.gap(@line1, @line2))
59
+ end
60
+
61
+ it "allows using a LineSpacing object as type" do
62
+ obj = line_spacing(line_spacing(:single))
63
+ assert_equal(:proportional, obj.type)
64
+ end
65
+
66
+ it "raises an error if a value is needed and none is provided" do
67
+ assert_raises(ArgumentError) { line_spacing(:proportional) }
68
+ end
69
+
70
+ it "raises an error if an invalid type is provided" do
71
+ assert_raises(ArgumentError) { line_spacing(:invalid) }
72
+ end
73
+ end
74
+
75
+ describe HexaPDF::Layout::Style do
76
+ before do
77
+ @style = HexaPDF::Layout::Style.new
78
+ end
79
+
80
+ it "can assign values on initialization" do
81
+ style = HexaPDF::Layout::Style.new(font_size: 10)
82
+ assert_equal(10, style.font_size)
83
+ end
84
+
85
+ it "has several dynamically generated properties with default values" do
86
+ assert_raises(HexaPDF::Error) { @style.font }
87
+ assert_equal(10, @style.font_size)
88
+ assert_equal(0, @style.character_spacing)
89
+ assert_equal(0, @style.word_spacing)
90
+ assert_equal(100, @style.horizontal_scaling)
91
+ assert_equal(0, @style.text_rise)
92
+ assert_equal({}, @style.font_features)
93
+ assert_equal(:left, @style.align)
94
+ assert_equal(:top, @style.valign)
95
+ end
96
+
97
+ it "can set and retrieve line spacing objects" do
98
+ assert_equal([:proportional, 1], [@style.line_spacing.type, @style.line_spacing.value])
99
+ @style.line_spacing = :double
100
+ assert_equal([:proportional, 2], [@style.line_spacing.type, @style.line_spacing.value])
101
+ end
102
+
103
+ it "can set and retrieve text segmentation algorithms" do
104
+ assert_equal(HexaPDF::Layout::TextBox::SimpleTextSegmentation,
105
+ @style.text_segmentation_algorithm)
106
+ block = proc { :y }
107
+ @style.text_segmentation_algorithm(&block)
108
+ assert_equal(block, @style.text_segmentation_algorithm)
109
+ end
110
+
111
+ it "can set and retrieve line wrapping algorithms" do
112
+ assert_equal(HexaPDF::Layout::TextBox::SimpleLineWrapping,
113
+ @style.text_line_wrapping_algorithm)
114
+ @style.text_line_wrapping_algorithm(:callable)
115
+ assert_equal(:callable, @style.text_line_wrapping_algorithm)
116
+ end
117
+
118
+ it "has methods for some derived and cached values" do
119
+ assert_equal(0.01, @style.scaled_font_size)
120
+ assert_equal(0, @style.scaled_character_spacing)
121
+ assert_equal(0, @style.scaled_word_spacing)
122
+ assert_equal(1, @style.scaled_horizontal_scaling)
123
+
124
+ wrapped_font = Object.new
125
+ wrapped_font.define_singleton_method(:ascender) { 600 }
126
+ wrapped_font.define_singleton_method(:descender) { -100 }
127
+ font = Object.new
128
+ font.define_singleton_method(:scaling_factor) { 1 }
129
+ font.define_singleton_method(:wrapped_font) { wrapped_font }
130
+ @style.font = font
131
+
132
+ assert_equal(6, @style.scaled_font_ascender)
133
+ assert_equal(-1, @style.scaled_font_descender)
134
+ end
135
+
136
+ it "can clear cached values" do
137
+ assert_equal(0.01, @style.scaled_font_size)
138
+ @style.font_size = 20
139
+ assert_equal(0.01, @style.scaled_font_size)
140
+ @style.clear_cache
141
+ assert_equal(0.02, @style.scaled_font_size)
142
+ end
143
+ end