hexapdf 0.34.1 → 0.35.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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +59 -0
  3. data/examples/009-text_layouter_alignment.rb +7 -7
  4. data/examples/010-text_layouter_inline_boxes.rb +1 -1
  5. data/examples/011-text_layouter_line_wrapping.rb +2 -4
  6. data/examples/013-text_layouter_shapes.rb +9 -11
  7. data/examples/014-text_in_polygon.rb +2 -2
  8. data/examples/016-frame_automatic_box_placement.rb +6 -7
  9. data/examples/017-frame_text_flow.rb +2 -2
  10. data/examples/018-composer.rb +5 -6
  11. data/examples/020-column_box.rb +2 -2
  12. data/examples/021-list_box.rb +1 -1
  13. data/examples/027-composer_optional_content.rb +5 -5
  14. data/examples/028-frame_mask_mode.rb +23 -0
  15. data/examples/029-composer_fallback_fonts.rb +22 -0
  16. data/lib/hexapdf/cli/info.rb +1 -0
  17. data/lib/hexapdf/cli/inspect.rb +55 -2
  18. data/lib/hexapdf/composer.rb +2 -2
  19. data/lib/hexapdf/configuration.rb +61 -1
  20. data/lib/hexapdf/content/canvas.rb +63 -0
  21. data/lib/hexapdf/content/canvas_composer.rb +142 -0
  22. data/lib/hexapdf/content.rb +1 -0
  23. data/lib/hexapdf/dictionary.rb +14 -3
  24. data/lib/hexapdf/document/layout.rb +35 -13
  25. data/lib/hexapdf/encryption/standard_security_handler.rb +15 -0
  26. data/lib/hexapdf/error.rb +2 -1
  27. data/lib/hexapdf/font/invalid_glyph.rb +22 -6
  28. data/lib/hexapdf/font/true_type_wrapper.rb +48 -20
  29. data/lib/hexapdf/font/type1_wrapper.rb +48 -24
  30. data/lib/hexapdf/layout/box.rb +11 -8
  31. data/lib/hexapdf/layout/column_box.rb +5 -3
  32. data/lib/hexapdf/layout/frame.rb +77 -39
  33. data/lib/hexapdf/layout/image_box.rb +3 -3
  34. data/lib/hexapdf/layout/list_box.rb +20 -19
  35. data/lib/hexapdf/layout/style.rb +173 -68
  36. data/lib/hexapdf/layout/table_box.rb +3 -3
  37. data/lib/hexapdf/layout/text_box.rb +5 -5
  38. data/lib/hexapdf/layout/text_fragment.rb +50 -0
  39. data/lib/hexapdf/layout/text_layouter.rb +7 -6
  40. data/lib/hexapdf/object.rb +5 -2
  41. data/lib/hexapdf/pdf_array.rb +5 -0
  42. data/lib/hexapdf/type/acro_form/appearance_generator.rb +16 -11
  43. data/lib/hexapdf/utils/sorted_tree_node.rb +0 -10
  44. data/lib/hexapdf/version.rb +1 -1
  45. data/test/hexapdf/content/test_canvas.rb +37 -0
  46. data/test/hexapdf/content/test_canvas_composer.rb +112 -0
  47. data/test/hexapdf/document/test_layout.rb +40 -12
  48. data/test/hexapdf/encryption/test_standard_security_handler.rb +43 -0
  49. data/test/hexapdf/font/test_invalid_glyph.rb +13 -1
  50. data/test/hexapdf/font/test_true_type_wrapper.rb +15 -2
  51. data/test/hexapdf/font/test_type1_wrapper.rb +21 -2
  52. data/test/hexapdf/layout/test_column_box.rb +14 -0
  53. data/test/hexapdf/layout/test_frame.rb +181 -95
  54. data/test/hexapdf/layout/test_list_box.rb +7 -7
  55. data/test/hexapdf/layout/test_style.rb +14 -10
  56. data/test/hexapdf/layout/test_table_box.rb +3 -3
  57. data/test/hexapdf/layout/test_text_box.rb +2 -2
  58. data/test/hexapdf/layout/test_text_fragment.rb +37 -0
  59. data/test/hexapdf/layout/test_text_layouter.rb +10 -10
  60. data/test/hexapdf/test_configuration.rb +49 -0
  61. data/test/hexapdf/test_dictionary.rb +1 -1
  62. data/test/hexapdf/test_object.rb +13 -12
  63. data/test/hexapdf/test_pdf_array.rb +9 -0
  64. data/test/hexapdf/test_writer.rb +3 -3
  65. data/test/hexapdf/type/acro_form/test_appearance_generator.rb +41 -13
  66. data/test/hexapdf/utils/test_sorted_tree_node.rb +1 -1
  67. metadata +7 -3
@@ -114,15 +114,22 @@ describe HexaPDF::Layout::Frame do
114
114
  when :left then Geom2D::Rectangle(10, 10, 10, 100)
115
115
  when :right then Geom2D::Rectangle(100, 10, 10, 100)
116
116
  when :top then Geom2D::Rectangle(10, 100, 100, 10)
117
+ when :bottom then Geom2D::Rectangle(10, 10, 100, 10)
117
118
  end
118
119
  )
119
120
  end
120
121
  end
121
122
 
123
+ it "fails if an unkown position value is provided" do
124
+ box = HexaPDF::Layout::Box.create(position: :unknown)
125
+ exception = assert_raises(HexaPDF::Error) { @frame.fit(box) }
126
+ assert_match(/Invalid value 'unknown'/, exception.message)
127
+ end
128
+
122
129
  describe "absolute position" do
123
130
  it "draws the box at the given absolute position" do
124
131
  check_box(
125
- {width: 50, height: 50, position: :absolute, position_hint: [10, 10]},
132
+ {width: 50, height: 50, position: [10, 10]},
126
133
  [20, 20],
127
134
  [20, 20, 70, 70],
128
135
  [[[10, 10], [110, 10], [110, 110], [10, 110]],
@@ -132,22 +139,12 @@ describe HexaPDF::Layout::Frame do
132
139
 
133
140
  it "determines the available space for #fit by using the space to the right and above" do
134
141
  check_box(
135
- {position: :absolute, position_hint: [10, 10]},
142
+ {position: [10, 10]},
136
143
  [20, 20],
137
144
  [20, 20, 110, 110],
138
145
  [[[10, 10], [110, 10], [110, 20], [20, 20], [20, 110], [10, 110]]]
139
146
  )
140
147
  end
141
-
142
- it "always removes the whole margin box from the frame" do
143
- check_box(
144
- {width: 50, height: 50, position: :absolute, position_hint: [10, 10],
145
- margin: [10, 20, 30, 40]},
146
- [20, 20],
147
- [-20, -10, 90, 80],
148
- [[[10, 80], [90, 80], [90, 10], [110, 10], [110, 110], [10, 110]]]
149
- )
150
- end
151
148
  end
152
149
 
153
150
  describe "default position" do
@@ -159,40 +156,52 @@ describe HexaPDF::Layout::Frame do
159
156
  end
160
157
 
161
158
  it "draws the box on the right side" do
162
- check_box({width: 50, height: 50, position_hint: :right},
159
+ check_box({width: 50, height: 50, align: :right},
163
160
  [60, 60],
164
161
  [10, 60, 110, 110],
165
162
  [[[10, 10], [110, 10], [110, 60], [10, 60]]])
166
163
  end
167
164
 
168
165
  it "draws the box in the center" do
169
- check_box({width: 50, height: 50, position_hint: :center},
166
+ check_box({width: 50, height: 50, align: :center},
170
167
  [35, 60],
171
168
  [10, 60, 110, 110],
172
169
  [[[10, 10], [110, 10], [110, 60], [10, 60]]])
173
170
  end
174
171
 
172
+ it "draws the box vertically in the center" do
173
+ check_box({width: 50, height: 50, valign: :center},
174
+ [10, 35],
175
+ [10, 35, 110, 110],
176
+ [[[10, 10], [110, 10], [110, 35], [10, 35]]])
177
+ end
178
+
179
+ it "draws the box vertically at the bottom" do
180
+ check_box({width: 50, height: 50, valign: :bottom},
181
+ [10, 10], [10, 10, 110, 110], [])
182
+ end
183
+
175
184
  describe "with margin" do
176
185
  [:left, :center, :right].each do |hint|
177
- it "ignores all margins if the box fills the whole frame, with position hint #{hint}" do
178
- check_box({margin: 10, position_hint: hint},
186
+ it "ignores all margins if the box fills the whole frame, with alignment #{hint}" do
187
+ check_box({margin: 10, align: hint},
179
188
  [10, 10], [10, 10, 110, 110], [])
180
189
  assert_equal(100, @box.width)
181
190
  assert_equal(100, @box.height)
182
191
  end
183
192
 
184
193
  it "ignores the left/top/right margin if the available bounds coincide with the " \
185
- "frame's, with position hint #{hint}" do
186
- check_box({height: 50, margin: 10, position_hint: hint},
194
+ "frame's, with alignment #{hint}" do
195
+ check_box({height: 50, margin: 10, align: hint},
187
196
  [10, 60],
188
197
  [10, 50, 110, 110],
189
198
  [[[10, 10], [110, 10], [110, 50], [10, 50]]])
190
199
  end
191
200
 
192
201
  it "doesn't ignore top margin if the available bounds' top doesn't coincide with the " \
193
- "frame's top, with position hint #{hint}" do
202
+ "frame's top, with alignment #{hint}" do
194
203
  remove_area(:top)
195
- check_box({height: 50, margin: 10, position_hint: hint},
204
+ check_box({height: 50, margin: 10, align: hint},
196
205
  [10, 40],
197
206
  [10, 30, 110, 100],
198
207
  [[[10, 10], [110, 10], [110, 30], [10, 30]]])
@@ -200,9 +209,9 @@ describe HexaPDF::Layout::Frame do
200
209
  end
201
210
 
202
211
  it "doesn't ignore left margin if the available bounds' left doesn't coincide with the " \
203
- "frame's left, with position hint #{hint}" do
212
+ "frame's left, with alignment #{hint}" do
204
213
  remove_area(:left)
205
- check_box({height: 50, margin: 10, position_hint: hint},
214
+ check_box({height: 50, margin: 10, align: hint},
206
215
  [30, 60],
207
216
  [10, 50, 110, 110],
208
217
  [[[20, 10], [110, 10], [110, 50], [20, 50]]])
@@ -210,9 +219,9 @@ describe HexaPDF::Layout::Frame do
210
219
  end
211
220
 
212
221
  it "doesn't ignore right margin if the available bounds' right doesn't coincide with " \
213
- "the frame's right, with position hint #{hint}" do
222
+ "the frame's right, with alignment #{hint}" do
214
223
  remove_area(:right)
215
- check_box({height: 50, margin: 10, position_hint: hint},
224
+ check_box({height: 50, margin: 10, align: hint},
216
225
  [10, 60],
217
226
  [10, 50, 110, 110],
218
227
  [[[10, 10], [100, 10], [100, 50], [10, 50]]])
@@ -220,117 +229,194 @@ describe HexaPDF::Layout::Frame do
220
229
  end
221
230
  end
222
231
 
223
- it "perfectly centers a box if possible, margins ignored" do
224
- check_box({width: 50, height: 10, margin: [10, 10, 10, 20], position_hint: :center},
232
+ [:top, :center, :bottom].each do |hint|
233
+ it "ignores all margins if the box fills the whole frame, with vertical alignment #{hint}" do
234
+ check_box({margin: 10, valign: hint},
235
+ [10, 10], [10, 10, 110, 110], [])
236
+ assert_equal(100, @box.width)
237
+ assert_equal(100, @box.height)
238
+ end
239
+
240
+ it "ignores the left/top/bottom margin if the available bounds coincide with the " \
241
+ "frame's, with vertical alignment #{hint}" do
242
+ check_box({width: 50, margin: 10, valign: hint},
243
+ [10, 10], [10, 10, 110, 110], [])
244
+ assert_equal(100, @box.height)
245
+ end
246
+
247
+ it "doesn't ignore top margin if the available bounds' top doesn't coincide with the " \
248
+ "frame's top, with vertical alignment #{hint}" do
249
+ remove_area(:top)
250
+ check_box({width: 50, margin: 10, valign: hint},
251
+ [10, 10], [10, 10, 110, 100], [])
252
+ assert_equal(80, @box.height)
253
+ end
254
+
255
+ it "doesn't ignore left margin if the available bounds' left doesn't coincide with the " \
256
+ "frame's left, with vertical alignment #{hint}" do
257
+ remove_area(:left)
258
+ check_box({width: 50, margin: 10, valign: hint},
259
+ [30, 10], [10, 10, 110, 110], [])
260
+ assert_equal(100, @box.height)
261
+ end
262
+
263
+ it "doesn't ignore bottom margin if the available bounds' bottom doesn't coincide with " \
264
+ "the frame's bottom, with vertical alignment #{hint}" do
265
+ remove_area(:bottom)
266
+ check_box({width: 50, margin: 10, valign: hint},
267
+ [10, 30], [10, 20, 110, 110], [])
268
+ assert_equal(80, @box.height)
269
+ end
270
+ end
271
+
272
+ it "perfectly centers a box horizontally if possible, margins ignored" do
273
+ check_box({width: 50, height: 10, margin: [10, 10, 10, 20], align: :center},
225
274
  [35, 100],
226
275
  [10, 90, 110, 110],
227
276
  [[[10, 10], [110, 10], [110, 90], [10, 90]]])
228
277
  end
229
278
 
230
- it "perfectly centers a box if possible, margins not ignored" do
279
+ it "perfectly centers a box horizontally if possible, margins not ignored" do
231
280
  remove_area(:left, :right)
232
- check_box({width: 40, height: 10, margin: [10, 10, 10, 20], position_hint: :center},
281
+ check_box({width: 40, height: 10, margin: [10, 10, 10, 20], align: :center},
233
282
  [40, 100],
234
283
  [10, 90, 110, 110],
235
284
  [[[20, 10], [100, 10], [100, 90], [20, 90]]])
236
285
  end
237
286
 
238
- it "centers a box as good as possible when margins aren't equal" do
287
+ it "horizontally centers a box as good as possible when margins aren't equal" do
239
288
  remove_area(:left, :right)
240
- check_box({width: 20, height: 10, margin: [10, 10, 10, 40], position_hint: :center},
289
+ check_box({width: 20, height: 10, margin: [10, 10, 10, 40], align: :center},
241
290
  [65, 100],
242
291
  [10, 90, 110, 110],
243
292
  [[[20, 10], [100, 10], [100, 90], [20, 90]]])
244
293
  end
294
+
295
+ it "perfectly centers a box vertically if possible, margins ignored" do
296
+ check_box({width: 10, height: 50, margin: [10, 10, 20, 10], valign: :center},
297
+ [10, 35],
298
+ [10, 15, 110, 110],
299
+ [[[10, 10], [110, 10], [110, 15], [10, 15]]])
300
+ end
301
+
302
+ it "perfectly centers a box vertically if possible, margins not ignored" do
303
+ remove_area(:top, :bottom)
304
+ check_box({width: 10, height: 40, margin: [10, 10, 20, 10], valign: :center},
305
+ [10, 40], [10, 20, 110, 100], [])
306
+ end
307
+
308
+ it "vertically centers a box as good as possible when margins aren't equal" do
309
+ remove_area(:top, :bottom)
310
+ check_box({width: 10, height: 20, margin: [10, 10, 40, 10], valign: :center},
311
+ [10, 65],
312
+ [10, 25, 110, 100],
313
+ [[[10, 20], [110, 20], [110, 25], [10, 25]]])
314
+ end
245
315
  end
246
316
  end
247
317
 
248
- describe "floating boxes" do
249
- it "draws the box on the left side" do
250
- check_box({width: 50, height: 50, position: :float},
251
- [10, 60],
252
- [10, 60, 60, 110],
253
- [[[10, 10], [110, 10], [110, 110], [60, 110], [60, 60], [10, 60]]])
318
+ describe "flowing boxes" do
319
+ it "flows inside the frame's outline" do
320
+ check_box({width: 10, height: 20, margin: 10, position: :flow},
321
+ [0, 90],
322
+ [10, 80, 110, 110],
323
+ [[[10, 10], [110, 10], [110, 80], [10, 80]]])
254
324
  end
255
325
 
256
- it "draws the box on the right side" do
257
- check_box({width: 50, height: 50, position: :float, position_hint: :right},
258
- [60, 60],
259
- [60, 60, 110, 110],
260
- [[[10, 10], [110, 10], [110, 60], [60, 60], [60, 110], [10, 110]]])
326
+ it "uses position=default if the box indicates it doesn't support flowing contents" do
327
+ check_box({width: 10, height: 20, margin: 10, position: :flow, doesnt_support_position_flow: true},
328
+ [10, 90],
329
+ [10, 80, 110, 110],
330
+ [[[10, 10], [110, 10], [110, 80], [10, 80]]])
261
331
  end
332
+ end
262
333
 
263
- it "draws the box in the center" do
264
- check_box({width: 50, height: 50, position: :float, position_hint: :center},
265
- [35, 60],
266
- [35, 60, 85, 110],
267
- [[[10, 10], [110, 10], [110, 110], [85, 110], [85, 60], [35, 60],
268
- [35, 110], [10, 110]]])
334
+ describe "mask mode" do
335
+ describe "none" do
336
+ it "doesn't remove any area" do
337
+ check_box({width: 50, height: 50, mask_mode: :none},
338
+ [10, 60],
339
+ [10, 60, 10, 60],
340
+ [[[10, 10], [110, 10], [110, 110], [10, 110]]]
341
+ )
342
+ end
269
343
  end
270
344
 
271
- describe "with margin" do
272
- [:left, :center, :right].each do |hint|
273
- it "ignores all margins if the box fills the whole frame, with position hint #{hint}" do
274
- check_box({margin: 10, position: :float, position_hint: hint},
275
- [10, 10], [10, 10, 110, 110], [])
276
- assert_equal(100, @box.width)
277
- assert_equal(100, @box.height)
278
- end
345
+ describe "box" do
346
+ it "removes the box area" do
347
+ check_box({width: 50, height: 50, mask_mode: :box},
348
+ [10, 60],
349
+ [10, 60, 60, 110],
350
+ [[[10, 10], [110, 10], [110, 110], [60, 110], [60, 60], [10, 60]]])
279
351
  end
280
352
 
281
- it "ignores the left, but not the right margin if aligned left to the frame border" do
282
- check_box({width: 50, height: 50, margin: 10, position: :float, position_hint: :left},
283
- [10, 60],
284
- [10, 50, 70, 110],
285
- [[[10, 10], [110, 10], [110, 110], [70, 110], [70, 50], [10, 50]]])
353
+ it "ignores the margin if sides are on the frame border" do
354
+ check_box({margin: 10, mask_mode: :box},
355
+ [10, 10], [10, 10, 110, 110], [])
286
356
  end
287
357
 
288
- it "uses the left and the right margin if aligned left and not to the frame border" do
289
- remove_area(:left)
290
- check_box({width: 50, height: 50, margin: 10, position: :float, position_hint: :left},
291
- [30, 60],
292
- [20, 50, 90, 110],
293
- [[[20, 10], [110, 10], [110, 110], [90, 110], [90, 50], [20, 50]]])
358
+ it "uses the margin if sides are not on the frame border" do
359
+ remove_area(:left, :right, :top, :bottom)
360
+ check_box({margin: 10, mask_mode: :box},
361
+ [30, 30], [20, 20, 100, 100], [])
294
362
  end
363
+ end
295
364
 
296
- it "uses the left and the right margin if aligned center" do
297
- check_box({width: 50, height: 50, margin: 10, position: :float, position_hint: :center},
298
- [35, 60],
299
- [25, 50, 95, 110],
300
- [[[10, 10], [110, 10], [110, 110], [95, 110], [95, 50], [25, 50],
301
- [25, 110], [10, 110]]])
365
+ describe "fill_horizontal" do
366
+ it "removes the horizontal part to the bottom of the box in the current region" do
367
+ remove_area(:left, :right)
368
+ check_box({width: 50, height: 50, mask_mode: :fill_horizontal},
369
+ [20, 60],
370
+ [20, 60, 100, 110],
371
+ [[[20, 10], [100, 10], [100, 60], [20, 60]]])
302
372
  end
303
373
 
304
- it "ignores the right, but not the left margin if aligned right to the frame border" do
305
- check_box({width: 50, height: 50, margin: 10, position: :float, position_hint: :right},
306
- [60, 60],
307
- [50, 50, 110, 110],
308
- [[[10, 10], [110, 10], [110, 50], [50, 50], [50, 110], [10, 110]]])
374
+ it "respects the top and bottom margins for the mask" do
375
+ remove_area(:top, :bottom)
376
+ check_box({width: 50, margin: 10, mask_mode: :fill_horizontal},
377
+ [10, 30], [10, 20, 110, 100], [])
378
+ end
379
+ end
380
+
381
+ describe "fill_frame_horizontal" do
382
+ it "removes the horizontal part to the bottom of the box in the frame" do
383
+ remove_area(:left, :right)
384
+ check_box({width: 50, height: 50, mask_mode: :fill_frame_horizontal},
385
+ [20, 60],
386
+ [10, 60, 110, 110],
387
+ [[[20, 10], [100, 10], [100, 60], [20, 60]]])
309
388
  end
310
389
 
311
- it "uses the left and the right margin if aligned right and not to the frame border" do
312
- remove_area(:right)
313
- check_box({width: 50, height: 50, margin: 10, position: :float, position_hint: :right},
314
- [40, 60],
315
- [30, 50, 100, 110],
316
- [[[10, 10], [100, 10], [100, 50], [30, 50], [30, 110], [10, 110]]])
390
+ it "respects the bottom margin for the mask" do
391
+ remove_area(:left, :right, :top, :bottom)
392
+ check_box({width: 50, margin: 10, mask_mode: :fill_frame_horizontal},
393
+ [30, 30], [10, 20, 110, 100], [])
317
394
  end
318
395
  end
319
- end
320
396
 
321
- describe "flowing boxes" do
322
- it "flows inside the frame's outline" do
323
- check_box({width: 10, height: 20, margin: 10, position: :flow},
324
- [0, 90],
325
- [10, 80, 110, 110],
326
- [[[10, 10], [110, 10], [110, 80], [10, 80]]])
397
+ describe "fill_vertical" do
398
+ it "removes the vertical part covering the box in the current region" do
399
+ check_box({width: 50, height: 50, mask_mode: :fill_vertical, align: :center},
400
+ [35, 60],
401
+ [35, 10, 85, 110],
402
+ [[[10, 10], [35, 10], [35, 110], [10, 110]],
403
+ [[85, 10], [110, 10], [110, 110], [85, 110]]])
404
+ end
405
+
406
+ it "respects the left and right margins for the mask" do
407
+ check_box({width: 50, height: 50, margin: 10, mask_mode: :fill_vertical, align: :center},
408
+ [35, 60],
409
+ [25, 10, 95, 110],
410
+ [[[10, 10], [25, 10], [25, 110], [10, 110]],
411
+ [[95, 10], [110, 10], [110, 110], [95, 110]]])
412
+ end
327
413
  end
328
414
 
329
- it "uses position=default if the box indicates it doesn't support flowing contents" do
330
- check_box({width: 10, height: 20, margin: 10, position: :flow, doesnt_support_position_flow: true},
331
- [10, 90],
332
- [10, 80, 110, 110],
333
- [[[10, 10], [110, 10], [110, 80], [10, 80]]])
415
+ describe "fill" do
416
+ it "removes the current region completely" do
417
+ check_box({width: 50, height: 50, mask_mode: :fill},
418
+ [10, 60], [10, 10, 110, 110], [])
419
+ end
334
420
  end
335
421
  end
336
422
 
@@ -38,10 +38,10 @@ describe HexaPDF::Layout::ListBox do
38
38
 
39
39
  describe "initialize" do
40
40
  it "creates a new instance with the given arguments" do
41
- box = create_box(children: [:a], item_type: :circle, content_indentation: 15,
41
+ box = create_box(children: [:a], marker_type: :circle, content_indentation: 15,
42
42
  start_number: 4, item_spacing: 20)
43
43
  assert_equal([:a], box.children)
44
- assert_equal(:circle, box.item_type)
44
+ assert_equal(:circle, box.marker_type)
45
45
  assert_equal(15, box.content_indentation)
46
46
  assert_equal(4, box.start_number)
47
47
  assert_equal(20, box.item_spacing)
@@ -109,7 +109,7 @@ describe HexaPDF::Layout::ListBox do
109
109
  end
110
110
 
111
111
  it "fails for unknown item types" do
112
- box = create_box(children: @text_boxes[0, 1], item_type: :unknown)
112
+ box = create_box(children: @text_boxes[0, 1], marker_type: :unknown)
113
113
  assert_raises(HexaPDF::Error) { box.fit(100, 100, @frame) }
114
114
  end
115
115
  end
@@ -179,7 +179,7 @@ describe HexaPDF::Layout::ListBox do
179
179
  end
180
180
 
181
181
  it "draws a circle as marker" do
182
- box = create_box(children: @fixed_size_boxes[0, 1], item_type: :circle,
182
+ box = create_box(children: @fixed_size_boxes[0, 1], marker_type: :circle,
183
183
  style: {font_size: 11, fill_color: 0.5})
184
184
  box.fit(100, 100, @frame)
185
185
  box.draw(@canvas, 0, 100 - box.height)
@@ -201,7 +201,7 @@ describe HexaPDF::Layout::ListBox do
201
201
  end
202
202
 
203
203
  it "draws a square as marker" do
204
- box = create_box(children: @fixed_size_boxes[0, 1], item_type: :square,
204
+ box = create_box(children: @fixed_size_boxes[0, 1], marker_type: :square,
205
205
  style: {font_size: 11, fill_color: 0.5})
206
206
  box.fit(100, 100, @frame)
207
207
  box.draw(@canvas, 0, 100 - box.height)
@@ -223,7 +223,7 @@ describe HexaPDF::Layout::ListBox do
223
223
  end
224
224
 
225
225
  it "draws decimal numbers as marker" do
226
- box = create_box(children: @fixed_size_boxes[0, 2], item_type: :decimal,
226
+ box = create_box(children: @fixed_size_boxes[0, 2], marker_type: :decimal,
227
227
  style: {font_size: 11, fill_color: 0.5},
228
228
  content_indentation: 20)
229
229
  box.fit(100, 100, @frame)
@@ -260,7 +260,7 @@ describe HexaPDF::Layout::ListBox do
260
260
  marker = lambda do |_doc, _list_box, _index|
261
261
  HexaPDF::Layout::Box.create(width: 10, height: 10) {}
262
262
  end
263
- box = create_box(children: @fixed_size_boxes[0, 1], item_type: marker)
263
+ box = create_box(children: @fixed_size_boxes[0, 1], marker_type: marker)
264
264
  box.fit(100, 100, @frame)
265
265
  box.draw(@canvas, 0, 100 - box.height)
266
266
  operators = [
@@ -767,8 +767,8 @@ describe HexaPDF::Layout::Style do
767
767
  assert_equal(HexaPDF::Content::LineCapStyle::BUTT_CAP, @style.stroke_cap_style)
768
768
  assert_equal(HexaPDF::Content::LineJoinStyle::MITER_JOIN, @style.stroke_join_style)
769
769
  assert_equal(10.0, @style.stroke_miter_limit)
770
- assert_equal(:left, @style.align)
771
- assert_equal(:top, @style.valign)
770
+ assert_equal(:left, @style.text_align)
771
+ assert_equal(:top, @style.text_valign)
772
772
  assert_equal(0, @style.text_indent)
773
773
  assert_nil(@style.background_color)
774
774
  assert_equal(1, @style.background_alpha)
@@ -785,6 +785,10 @@ describe HexaPDF::Layout::Style do
785
785
  refute(@style.fill_horizontal)
786
786
  assert_kind_of(HexaPDF::Layout::Style::Layers, @style.underlays)
787
787
  assert_kind_of(HexaPDF::Layout::Style::Layers, @style.overlays)
788
+ assert_equal(:default, @style.position)
789
+ assert_equal(:left, @style.align)
790
+ assert_equal(:top, @style.valign)
791
+ assert_equal(:default, @style.mask_mode)
788
792
  end
789
793
 
790
794
  it "allows using a non-standard setter for generated properties" do
@@ -800,18 +804,18 @@ describe HexaPDF::Layout::Style do
800
804
  end
801
805
 
802
806
  it "allows checking for valid values" do
803
- error = assert_raises(ArgumentError) { @style.align = :none }
804
- assert_match(/not a valid align value \(:left, :center, :right, :justify\)/, error.message)
807
+ error = assert_raises(ArgumentError) { @style.text_align = :none }
808
+ assert_match(/not a valid text_align value \(:left, :center, :right, :justify\)/, error.message)
805
809
  end
806
810
 
807
811
  it "allows checking whether a property has been set or accessed" do
808
- refute(@style.align?)
809
- assert_equal(:left, @style.align)
810
- assert(@style.align?)
812
+ refute(@style.text_align?)
813
+ assert_equal(:left, @style.text_align)
814
+ assert(@style.text_align?)
811
815
 
812
- refute(@style.valign?)
813
- @style.valign = :bottom
814
- assert(@style.valign?)
816
+ refute(@style.text_valign?)
817
+ @style.text_valign = :bottom
818
+ assert(@style.text_valign?)
815
819
  end
816
820
 
817
821
  it "has several dynamically generated properties with default values that take blocks" do
@@ -83,7 +83,7 @@ describe HexaPDF::Layout::TableBox::Cell do
83
83
  end
84
84
 
85
85
  it "fits a single box with horizontal aligning not being :left" do
86
- cell = create_cell(children: HexaPDF::Layout::Box.create(width: 20, height: 10, position_hint: :center))
86
+ cell = create_cell(children: HexaPDF::Layout::Box.create(width: 20, height: 10, align: :center))
87
87
  cell.fit(100, 100, @frame)
88
88
  assert_equal(66, cell.preferred_width)
89
89
  end
@@ -100,7 +100,7 @@ describe HexaPDF::Layout::TableBox::Cell do
100
100
  end
101
101
 
102
102
  it "fits multiple boxes with horizontal aligning not being :left" do
103
- box1 = HexaPDF::Layout::Box.create(width: 20, height: 10, position_hint: :center)
103
+ box1 = HexaPDF::Layout::Box.create(width: 20, height: 10, align: :center)
104
104
  box2 = HexaPDF::Layout::Box.create(width: 50, height: 15)
105
105
  cell = create_cell(children: [box1, box2])
106
106
  cell.fit(100, 100, @frame)
@@ -130,7 +130,7 @@ describe HexaPDF::Layout::TableBox::Cell do
130
130
 
131
131
  it "draws the boxes at the correct location" do
132
132
  draw_block = lambda {|canvas, _| canvas.move_to(0, 0).end_path }
133
- box1 = HexaPDF::Layout::Box.create(width: 20, height: 10, position_hint: :center, &draw_block)
133
+ box1 = HexaPDF::Layout::Box.create(width: 20, height: 10, align: :center, &draw_block)
134
134
  box2 = HexaPDF::Layout::Box.create(width: 50, height: 15, &draw_block)
135
135
  box = create_cell(children: [box1, box2])
136
136
  box.fit(100, 100, @frame)
@@ -66,7 +66,7 @@ describe HexaPDF::Layout::TextBox do
66
66
 
67
67
  it "uses the whole available width when aligning to the center or right" do
68
68
  [:center, :right].each do |align|
69
- box = create_box([@inline_box], style: {align: align})
69
+ box = create_box([@inline_box], style: {text_align: align})
70
70
  assert(box.fit(100, 100, @frame))
71
71
  assert_equal(100, box.width)
72
72
  end
@@ -74,7 +74,7 @@ describe HexaPDF::Layout::TextBox do
74
74
 
75
75
  it "uses the whole available height when vertically aligning to the center or bottom" do
76
76
  [:center, :bottom].each do |valign|
77
- box = create_box([@inline_box], style: {valign: valign})
77
+ box = create_box([@inline_box], style: {text_valign: valign})
78
78
  assert(box.fit(100, 100, @frame))
79
79
  assert_equal(100, box.height)
80
80
  end
@@ -34,6 +34,43 @@ describe HexaPDF::Layout::TextFragment do
34
34
  end
35
35
  end
36
36
 
37
+ describe "create_with_fallback_glyphs" do
38
+ it "creates an array with a single fragment object if no block is given" do
39
+ frags = HexaPDF::Layout::TextFragment.create_with_fallback_glyphs("Tom", font: @font, font_size: 20)
40
+ assert_equal(1, frags.size)
41
+ assert_equal(37.78, frags[0].width)
42
+ end
43
+
44
+ it "allows using a style object instead of directly specifying style properties" do
45
+ style = HexaPDF::Layout::Style.new(font: @font, font_size: 20)
46
+ frags = HexaPDF::Layout::TextFragment.create_with_fallback_glyphs("Tom", style)
47
+ assert_equal(37.78, frags[0].width)
48
+ end
49
+
50
+ it "replaces invalid glyphs with the result of the block" do
51
+ zapf_dingbats = @doc.fonts.add('ZapfDingbats')
52
+ i = 0
53
+ fallback = lambda do |codepoint, invalid_glyph|
54
+ case (i += 1) % 3
55
+ when 0 then []
56
+ when 1 then [zapf_dingbats.decode_codepoint(codepoint)]
57
+ when 2 then @font.decode_utf8("Tom")
58
+ end
59
+ end
60
+ style = HexaPDF::Layout::Style.new(font: @font, font_size: 20, font_features: {kern: true})
61
+
62
+ frags = HexaPDF::Layout::TextFragment.create_with_fallback_glyphs("✂Tom✂✂Tom✂", style, &fallback)
63
+ assert_equal(5, frags.size)
64
+ assert_equal(zapf_dingbats, frags[0].style.font)
65
+ assert_equal(:a2, frags[0].items[0].name)
66
+ assert_equal(@font, frags[2].style.font)
67
+
68
+ frags = HexaPDF::Layout::TextFragment.create_with_fallback_glyphs("Tom✂Tom", style, &fallback)
69
+ assert_equal(3, frags.size)
70
+ assert_equal(frags[0].width, frags[1].width)
71
+ end
72
+ end
73
+
37
74
  describe "initialize" do
38
75
  before do
39
76
  @items = @font.decode_utf8("Tom")