hexapdf 0.33.0 → 0.34.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +57 -1
  3. data/examples/026-optional_content.rb +55 -0
  4. data/examples/027-composer_optional_content.rb +83 -0
  5. data/lib/hexapdf/cli/command.rb +7 -1
  6. data/lib/hexapdf/cli/fonts.rb +1 -1
  7. data/lib/hexapdf/cli/inspect.rb +2 -4
  8. data/lib/hexapdf/composer.rb +3 -2
  9. data/lib/hexapdf/configuration.rb +21 -1
  10. data/lib/hexapdf/content/canvas.rb +52 -0
  11. data/lib/hexapdf/content/operator.rb +3 -1
  12. data/lib/hexapdf/dictionary.rb +1 -0
  13. data/lib/hexapdf/dictionary_fields.rb +1 -2
  14. data/lib/hexapdf/digital_signature/verification_result.rb +1 -2
  15. data/lib/hexapdf/document/layout.rb +3 -0
  16. data/lib/hexapdf/document/pages.rb +1 -1
  17. data/lib/hexapdf/document.rb +7 -0
  18. data/lib/hexapdf/encryption/ruby_aes.rb +10 -20
  19. data/lib/hexapdf/layout/box.rb +23 -3
  20. data/lib/hexapdf/layout/column_box.rb +2 -1
  21. data/lib/hexapdf/layout/frame.rb +23 -6
  22. data/lib/hexapdf/layout/inline_box.rb +20 -9
  23. data/lib/hexapdf/layout/list_box.rb +34 -20
  24. data/lib/hexapdf/layout/page_style.rb +2 -1
  25. data/lib/hexapdf/layout/style.rb +46 -6
  26. data/lib/hexapdf/layout/table_box.rb +9 -7
  27. data/lib/hexapdf/layout/text_box.rb +9 -2
  28. data/lib/hexapdf/layout/text_fragment.rb +28 -2
  29. data/lib/hexapdf/layout/text_layouter.rb +21 -5
  30. data/lib/hexapdf/stream.rb +1 -2
  31. data/lib/hexapdf/type/actions/set_ocg_state.rb +86 -0
  32. data/lib/hexapdf/type/actions.rb +1 -0
  33. data/lib/hexapdf/type/annotations/text.rb +1 -2
  34. data/lib/hexapdf/type/catalog.rb +10 -1
  35. data/lib/hexapdf/type/cid_font.rb +15 -1
  36. data/lib/hexapdf/type/form.rb +75 -5
  37. data/lib/hexapdf/type/optional_content_configuration.rb +170 -0
  38. data/lib/hexapdf/type/optional_content_group.rb +370 -0
  39. data/lib/hexapdf/type/optional_content_membership.rb +63 -0
  40. data/lib/hexapdf/type/optional_content_properties.rb +158 -0
  41. data/lib/hexapdf/type/page.rb +40 -16
  42. data/lib/hexapdf/type/page_label.rb +4 -8
  43. data/lib/hexapdf/type.rb +4 -0
  44. data/lib/hexapdf/utils/pdf_doc_encoding.rb +0 -1
  45. data/lib/hexapdf/version.rb +1 -1
  46. data/test/hexapdf/content/test_canvas.rb +49 -0
  47. data/test/hexapdf/content/test_operator.rb +3 -1
  48. data/test/hexapdf/document/test_layout.rb +7 -2
  49. data/test/hexapdf/document/test_pages.rb +6 -6
  50. data/test/hexapdf/layout/test_box.rb +13 -4
  51. data/test/hexapdf/layout/test_frame.rb +13 -1
  52. data/test/hexapdf/layout/test_inline_box.rb +17 -8
  53. data/test/hexapdf/layout/test_list_box.rb +48 -31
  54. data/test/hexapdf/layout/test_style.rb +10 -0
  55. data/test/hexapdf/layout/test_table_box.rb +32 -26
  56. data/test/hexapdf/layout/test_text_box.rb +8 -0
  57. data/test/hexapdf/layout/test_text_fragment.rb +33 -0
  58. data/test/hexapdf/layout/test_text_layouter.rb +32 -5
  59. data/test/hexapdf/test_composer.rb +30 -0
  60. data/test/hexapdf/test_dictionary.rb +10 -0
  61. data/test/hexapdf/test_document.rb +4 -0
  62. data/test/hexapdf/test_writer.rb +3 -3
  63. data/test/hexapdf/type/actions/test_set_ocg_state.rb +40 -0
  64. data/test/hexapdf/type/test_catalog.rb +11 -0
  65. data/test/hexapdf/type/test_form.rb +119 -0
  66. data/test/hexapdf/type/test_optional_content_configuration.rb +112 -0
  67. data/test/hexapdf/type/test_optional_content_group.rb +158 -0
  68. data/test/hexapdf/type/test_optional_content_properties.rb +109 -0
  69. data/test/hexapdf/type/test_page.rb +7 -5
  70. metadata +14 -3
@@ -261,12 +261,23 @@ module HexaPDF
261
261
  # Rotates the page +angle+ degrees counterclockwise where +angle+ has to be a multiple of 90.
262
262
  #
263
263
  # Positive values rotate the page to the left, negative values to the right. If +flatten+ is
264
- # +true+, the rotation is not done via the page's meta data but by "rotating" the canvas
265
- # itself.
264
+ # +true+, the rotation is not done via the page's meta (i.e. the /Rotate key) data but by
265
+ # rotating the canvas itself and all other necessary objects like the various page boxes and
266
+ # annotations.
266
267
  #
267
- # Note that the :Rotate key of a page object describes the angle in a clockwise orientation
268
- # but this method uses counterclockwise rotation to be consistent with other rotation methods
269
- # (e.g. HexaPDF::Content::Canvas#rotate).
268
+ # Notes:
269
+ #
270
+ # * The given +angle+ is applied in addition to a possibly already existing rotation
271
+ # (specified via the /Rotate key) and does not replace it.
272
+ #
273
+ # * Specifying 0 for +angle+ is valid and means that no additional rotation should be applied.
274
+ # The only meaningful usage of 0 for +angle+ is when +flatten+ is set to +true+ (so that the
275
+ # /Rotate key is removed and the existing rotation information incorporated into the canvas,
276
+ # page boxes and annotations).
277
+ #
278
+ # * The /Rotate key of a page object describes the angle in a clockwise orientation but this
279
+ # method uses counterclockwise rotation to be consistent with other rotation methods (e.g.
280
+ # HexaPDF::Content::Canvas#rotate).
270
281
  def rotate(angle, flatten: false)
271
282
  if angle % 90 != 0
272
283
  raise ArgumentError, "Page rotation has to be multiple of 90 degrees"
@@ -423,7 +434,7 @@ module HexaPDF
423
434
  #
424
435
  # To check whether the origin has been translated or not, use
425
436
  #
426
- # canvas.graphics_state.ctm.evaluate(0, 0)
437
+ # canvas.pos(0, 0)
427
438
  #
428
439
  # and check whether the result is [0, 0]. If it is, then the origin has not been
429
440
  # translated.
@@ -523,7 +534,7 @@ module HexaPDF
523
534
  def each_annotation
524
535
  return to_enum(__method__) unless block_given?
525
536
  Array(self[:Annots]).each do |annotation|
526
- next unless annotation&.key?(:Subtype) && annotation&.key?(:Rect)
537
+ next unless annotation?(annotation)
527
538
  yield(document.wrap(annotation, type: :Annot))
528
539
  end
529
540
  self
@@ -540,17 +551,19 @@ module HexaPDF
540
551
  # field itself.
541
552
  def flatten_annotations(annotations = self[:Annots])
542
553
  not_flattened = Array(annotations) || []
543
- return not_flattened unless key?(:Annots)
554
+ unless self[:Annots].kind_of?(PDFArray)
555
+ return (not_flattened == [annotations] ? [] : not_flattened)
556
+ end
544
557
 
545
558
  annotations = if annotations == self[:Annots]
546
559
  not_flattened
547
560
  else
548
- not_flattened & Array(self[:Annots])
561
+ not_flattened & self[:Annots]
549
562
  end
550
563
  return not_flattened if annotations.empty?
551
564
 
552
565
  canvas = self.canvas(type: :overlay)
553
- if (pos = canvas.graphics_state.ctm.evaluate(0, 0)) != [0, 0]
566
+ if (pos = canvas.pos(0, 0)) != [0, 0]
554
567
  canvas.save_graphics_state
555
568
  canvas.translate(-pos[0], -pos[1])
556
569
  end
@@ -558,8 +571,8 @@ module HexaPDF
558
571
  to_delete = []
559
572
  not_flattened -= annotations
560
573
  annotations.each do |annotation|
561
- unless annotation&.key?(:Subtype) && annotation&.key?(:Rect)
562
- to_delete << annotation if annotation
574
+ unless annotation?(annotation)
575
+ self[:Annots].delete(annotation)
563
576
  next
564
577
  end
565
578
 
@@ -592,13 +605,18 @@ module HexaPDF
592
605
  end
593
606
 
594
607
  # Step 2) Fit calculated rectangle to annotation rectangle by translating/scaling
595
- a = HexaPDF::Content::TransformationMatrix.new
596
- a.translate(rect.left - left, rect.bottom - bottom)
597
- a.scale(rect.width.fdiv(right - left), rect.height.fdiv(top - bottom))
608
+
609
+ # The final matrix is composed by translating the bottom-left corner of the transformed
610
+ # bounding box to the bottom-left corner of the annotation rectangle and scaling from the
611
+ # bottom-left corner of the transformed bounding box.
612
+ sx = rect.width.fdiv(right - left)
613
+ sy = rect.height.fdiv(top - bottom)
614
+ tx = rect.left - left + left - left * sx
615
+ ty = rect.bottom - bottom + bottom - bottom * sy
598
616
 
599
617
  # Step 3) Premultiply form matrix - done implicitly when drawing the XObject
600
618
 
601
- canvas.transform(*a) do
619
+ canvas.transform(sx, 0, 0, sy, tx, ty) do
602
620
  # Use [box.left, box.bottom] to counter default translation in #xobject since that
603
621
  # is already taken care of in matrix a
604
622
  canvas.xobject(appearance, at: [box.left, box.bottom])
@@ -621,6 +639,12 @@ module HexaPDF
621
639
 
622
640
  private
623
641
 
642
+ # Returns +true+ if the given object seems to be an annotation.
643
+ def annotation?(obj)
644
+ (obj.kind_of?(Hash) || obj.kind_of?(Dictionary)) &&
645
+ obj&.key?(:Subtype) && obj&.key?(:Rect)
646
+ end
647
+
624
648
  # Ensures that the required inheritable fields are set.
625
649
  def perform_validation(&block)
626
650
  root_node = document.catalog.pages
@@ -103,8 +103,7 @@ module HexaPDF
103
103
  end
104
104
  end
105
105
 
106
- # :nodoc:
107
- NUMBERING_STYLE_MAPPING = {
106
+ NUMBERING_STYLE_MAPPING = { # :nodoc:
108
107
  decimal: :D, D: :D,
109
108
  uppercase_roman: :R, R: :R,
110
109
  lowercase_roman: :r, r: :r,
@@ -113,8 +112,7 @@ module HexaPDF
113
112
  none: nil
114
113
  }
115
114
 
116
- # :nodoc:
117
- REVERSE_NUMBERING_STYLE_MAPPING = Hash[*NUMBERING_STYLE_MAPPING.flatten.reverse]
115
+ REVERSE_NUMBERING_STYLE_MAPPING = Hash[*NUMBERING_STYLE_MAPPING.flatten.reverse] # :nodoc:
118
116
 
119
117
  # :call-seq:
120
118
  # page_label.numbering_style -> numbering_style
@@ -174,8 +172,7 @@ module HexaPDF
174
172
 
175
173
  private
176
174
 
177
- # :nodoc:
178
- ALPHABET = ('A'..'Z').to_a
175
+ ALPHABET = ('A'..'Z').to_a # :nodoc:
179
176
 
180
177
  # Maps the given number to uppercase (or, if +lowercase+ is +true+, lowercase) letters (e.g. 1
181
178
  # -> A, 27 -> AA, 28 -> AB, ...).
@@ -188,8 +185,7 @@ module HexaPDF
188
185
  lowercase ? result.downcase : result
189
186
  end
190
187
 
191
- # :nodoc:
192
- ROMAN_NUMERAL_MAPPING = {
188
+ ROMAN_NUMERAL_MAPPING = { # :nodoc:
193
189
  1000 => "M",
194
190
  900 => "CM",
195
191
  500 => "D",
data/lib/hexapdf/type.rb CHANGED
@@ -76,6 +76,10 @@ module HexaPDF
76
76
  autoload(:OutlineItem, 'hexapdf/type/outline_item')
77
77
  autoload(:PageLabel, 'hexapdf/type/page_label')
78
78
  autoload(:MarkInformation, 'hexapdf/type/mark_information')
79
+ autoload(:OptionalContentGroup, 'hexapdf/type/optional_content_group')
80
+ autoload(:OptionalContentMembership, 'hexapdf/type/optional_content_membership')
81
+ autoload(:OptionalContentProperties, 'hexapdf/type/optional_content_properties')
82
+ autoload(:OptionalContentConfiguration, 'hexapdf/type/optional_content_configuration')
79
83
 
80
84
  end
81
85
 
@@ -52,7 +52,6 @@ module HexaPDF
52
52
  # See: PDF2.0 s7.9.2, D.1, D.3
53
53
  module PDFDocEncoding
54
54
 
55
- # :nodoc:
56
55
  CHARACTER_MAP = %W[\uFFFD \uFFFD \uFFFD \uFFFD \uFFFD \uFFFD \uFFFD \uFFFD
57
56
  \uFFFD \u0009 \u000A \uFFFD \uFFFD \u000D \uFFFD \uFFFD
58
57
  \uFFFD \uFFFD \uFFFD \uFFFD \uFFFD \uFFFD \uFFFD \uFFFD
@@ -37,6 +37,6 @@
37
37
  module HexaPDF
38
38
 
39
39
  # The version of HexaPDF.
40
- VERSION = '0.33.0'
40
+ VERSION = '0.34.1'
41
41
 
42
42
  end
@@ -71,6 +71,13 @@ describe HexaPDF::Content::Canvas do
71
71
  end
72
72
  end
73
73
 
74
+ describe "pos" do
75
+ it "returns the transformed position" do
76
+ @canvas.translate(9, 4)
77
+ assert_equal([10, 5], @canvas.pos(1, 1))
78
+ end
79
+ end
80
+
74
81
  describe "save_graphics_state" do
75
82
  it "invokes the operator implementation" do
76
83
  assert_operator_invoked(:q) { @canvas.save_graphics_state }
@@ -1280,6 +1287,48 @@ describe HexaPDF::Content::Canvas do
1280
1287
  end
1281
1288
  end
1282
1289
 
1290
+ describe "optional_content" do
1291
+ it "invokes the marked-sequence operator implementation" do
1292
+ assert_operator_invoked(:BDC, :OC, :P1) { @canvas.optional_content('Test') }
1293
+ end
1294
+
1295
+ it "is serialized correctly when no block is used" do
1296
+ @canvas.optional_content('Test')
1297
+ assert_operators(@canvas.contents, [[:begin_marked_content_with_property_list, [:OC, :P1]]])
1298
+ end
1299
+
1300
+ it "is serialized correctly when a block is used" do
1301
+ @canvas.optional_content('Test') {}
1302
+ assert_operators(@canvas.contents, [[:begin_marked_content_with_property_list, [:OC, :P1]],
1303
+ [:end_marked_content]])
1304
+ end
1305
+
1306
+ it "uses the provided OCG dictionary" do
1307
+ ocg = @doc.optional_content.add_ocg('Test')
1308
+ @canvas.optional_content(ocg)
1309
+ assert_equal(ocg, @page.resources.property_list(:P1))
1310
+ end
1311
+
1312
+ it "uses an existing OCG specified by name" do
1313
+ ocg = @doc.optional_content.add_ocg('Test')
1314
+ @canvas.optional_content('Test')
1315
+ assert_equal(ocg, @page.resources.property_list(:P1))
1316
+ end
1317
+
1318
+ it "creates an OCG if the named one doesn't yet exist" do
1319
+ @canvas.optional_content('Test')
1320
+ assert_equal(@doc.optional_content.ocg('Test'), @page.resources.property_list(:P1))
1321
+ end
1322
+
1323
+ it "always creates a new OCG if use_existing_ocg is false" do
1324
+ ocg = @doc.optional_content.add_ocg('Test')
1325
+ @canvas.optional_content('Test', use_existing_ocg: false)
1326
+ pl_item = @page.resources.property_list(:P1)
1327
+ refute_equal(ocg, pl_item)
1328
+ assert_equal(@doc.optional_content.ocgs.last, pl_item)
1329
+ end
1330
+ end
1331
+
1283
1332
  describe "color_from_specification "do
1284
1333
  it "accepts a color string" do
1285
1334
  assert_equal([1, 0, 0], @canvas.color_from_specification("red").components)
@@ -195,7 +195,8 @@ describe_operator :SetGraphicsStateParameters, :gs do
195
195
  font.glyph_scaling_factor = 0.01
196
196
  @processor.resources[:ExtGState] = {Name: {LW: 10, LC: 2, LJ: 2, ML: 2, D: [[3, 5], 2],
197
197
  RI: 2, SA: true, BM: :Multiply, CA: 0.5, ca: 0.5,
198
- AIS: true, TK: false, Font: [font, 10]}}
198
+ AIS: true, TK: false, Font: [font, 10],
199
+ SMask: {Type: :Mask, S: :Luminosity}}}
199
200
  @processor.resources.define_singleton_method(:document) do
200
201
  Object.new.tap {|obj| obj.define_singleton_method(:deref) {|o| o } }
201
202
  end
@@ -213,6 +214,7 @@ describe_operator :SetGraphicsStateParameters, :gs do
213
214
  assert_equal(0.5, gs.stroke_alpha)
214
215
  assert_equal(0.5, gs.fill_alpha)
215
216
  assert(gs.alpha_source)
217
+ assert_equal({Type: :Mask, S: :Luminosity}, gs.soft_mask)
216
218
  assert_equal(font, gs.font)
217
219
  assert_equal(10, gs.font_size)
218
220
  refute(gs.text_knockout)
@@ -219,6 +219,10 @@ describe HexaPDF::Document::Layout do
219
219
  box = @layout.text_box("Test", box_style: :named)
220
220
  assert_equal(20, box.style.font_size)
221
221
  end
222
+
223
+ it "raises an error if the to-be-used style doesn't exist" do
224
+ assert_raises(HexaPDF::Error) { @layout.text_box("Test", style: :unknown) }
225
+ end
222
226
  end
223
227
 
224
228
  describe "formatted_text" do
@@ -305,9 +309,10 @@ describe HexaPDF::Document::Layout do
305
309
 
306
310
  it "allows creating an inline box through a hash with a :box key" do
307
311
  block = lambda {|item| item.box(:base, width: 5, height: 15) }
308
- box = @layout.formatted_text_box([{box: :list, width: 10, block: block}])
312
+ box = @layout.formatted_text_box([{box: :column, columns: 1, width: 100, block: block}])
309
313
  ibox = box.instance_variable_get(:@items).first
310
- assert_equal(10, ibox.width)
314
+ ibox.fit_wrapped_box(nil)
315
+ assert_equal(100, ibox.width)
311
316
  assert_equal(15, ibox.height)
312
317
  end
313
318
 
@@ -196,15 +196,15 @@ describe HexaPDF::Document::Pages do
196
196
  end
197
197
 
198
198
  it "works for multiple page label entries" do
199
- @doc.catalog[:PageLabels] = {Nums: [0, {S: :r}, 2, {S: :d}, 7, {S: :A}]}
199
+ @doc.catalog[:PageLabels] = {Nums: [0, {S: :r}, 2, {S: :D}, 7, {S: :A}]}
200
200
  result = @doc.pages.each_labelling_range.to_a
201
- assert_equal([[0, 2, {S: :r}], [2, 5, {S: :d}], [7, 3, {S: :A}]],
201
+ assert_equal([[0, 2, {S: :r}], [2, 5, {S: :D}], [7, 3, {S: :A}]],
202
202
  result.map {|s, c, l| [s, c, l.value] })
203
203
  end
204
204
 
205
205
  it "returns a zero or negative count for the last range if there aren't enough pages" do
206
206
  assert_equal(10, @doc.pages.count)
207
- @doc.catalog[:PageLabels] = {Nums: [0, {S: :d}, 10, {S: :r}]}
207
+ @doc.catalog[:PageLabels] = {Nums: [0, {S: :D}, 10, {S: :r}]}
208
208
  assert_equal(0, @doc.pages.each_labelling_range.to_a[-1][1])
209
209
  @doc.catalog[:PageLabels][:Nums][2] = 11
210
210
  assert_equal(-1, @doc.pages.each_labelling_range.to_a[-1][1])
@@ -221,19 +221,19 @@ describe HexaPDF::Document::Pages do
221
221
 
222
222
  it "adds an entry for the range starting at 0 if it doesn't exist" do
223
223
  label = @doc.pages.add_labelling_range(5)
224
- assert_equal([{S: :d}, label],
224
+ assert_equal([{S: :D}, label],
225
225
  @doc.catalog.page_labels[:Nums].value.values_at(1, 3))
226
226
  end
227
227
  end
228
228
 
229
229
  describe "delete_labelling_range" do
230
230
  before do
231
- @doc.catalog[:PageLabels] = {Nums: [0, {S: :r}, 5, {S: :d}]}
231
+ @doc.catalog[:PageLabels] = {Nums: [0, {S: :r}, 5, {S: :D}]}
232
232
  end
233
233
 
234
234
  it "deletes the labelling range for a given start index" do
235
235
  label = @doc.pages.delete_labelling_range(5)
236
- assert_equal({S: :d}, label)
236
+ assert_equal({S: :D}, label)
237
237
  end
238
238
 
239
239
  it "deletes the labelling range for 0 if it is the last, together with the number tree" do
@@ -56,8 +56,8 @@ describe HexaPDF::Layout::Box do
56
56
  end
57
57
 
58
58
  it "allows setting custom properties" do
59
- box = create_box(properties: {'key' => :value})
60
- assert_equal({'key' => :value}, box.properties)
59
+ assert_equal({}, create_box(properties: nil).properties)
60
+ assert_equal({'key' => :value}, create_box(properties: {'key' => :value}).properties)
61
61
  end
62
62
  end
63
63
 
@@ -150,6 +150,10 @@ describe HexaPDF::Layout::Box do
150
150
  end
151
151
 
152
152
  describe "draw" do
153
+ before do
154
+ @canvas = HexaPDF::Document.new.pages.add.canvas
155
+ end
156
+
153
157
  it "draws the box onto the canvas" do
154
158
  box = create_box(width: 150, height: 130) do |canvas, _|
155
159
  canvas.line_width(15)
@@ -161,7 +165,6 @@ describe HexaPDF::Layout::Box do
161
165
  box.style.underlays.add {|canvas, _| canvas.line_width(10) }
162
166
  box.style.overlays.add {|canvas, _| canvas.line_width(20) }
163
167
 
164
- @canvas = HexaPDF::Document.new.pages.add.canvas
165
168
  box.draw(@canvas, 5, 5)
166
169
  assert_operators(@canvas.contents, [[:save_graphics_state],
167
170
  [:set_graphics_state_parameters, [:GS1]],
@@ -195,7 +198,6 @@ describe HexaPDF::Layout::Box do
195
198
  end
196
199
 
197
200
  it "draws nothing onto the canvas if the box is empty" do
198
- @canvas = HexaPDF::Document.new.pages.add.canvas
199
201
  box = create_box
200
202
  box.draw(@canvas, 5, 5)
201
203
  assert_operators(@canvas.contents, [])
@@ -204,6 +206,13 @@ describe HexaPDF::Layout::Box do
204
206
  refute(box.style.border?)
205
207
  refute(box.style.overlays?)
206
208
  end
209
+
210
+ it "wraps the box in optional content markers if the optional_content property is set" do
211
+ box = create_box(properties: {'optional_content' => 'Text'})
212
+ box.draw(@canvas, 0, 0)
213
+ assert_operators(@canvas.contents, [[:begin_marked_content_with_property_list, [:OC, :P1]],
214
+ [:end_marked_content]])
215
+ end
207
216
  end
208
217
 
209
218
  describe "empty?" do
@@ -5,7 +5,7 @@ require 'hexapdf/layout/frame'
5
5
  require 'hexapdf/layout/box'
6
6
  require 'hexapdf/document'
7
7
 
8
- describe HexaPDF::Layout::Frame do
8
+ describe HexaPDF::Layout::Frame::FitResult do
9
9
  it "shows the box's mask area on #draw when using debug output" do
10
10
  doc = HexaPDF::Document.new(config: {'debug' => true})
11
11
  canvas = doc.pages.add.canvas
@@ -15,6 +15,7 @@ describe HexaPDF::Layout::Frame do
15
15
  result.x = result.y = 0
16
16
  result.draw(canvas)
17
17
  assert_equal(<<~CONTENTS, canvas.contents)
18
+ /OC /P1 BDC
18
19
  q
19
20
  0.0 0.501961 0.0 rg
20
21
  0.0 0.392157 0.0 RG
@@ -22,10 +23,13 @@ describe HexaPDF::Layout::Frame do
22
23
  0 0 20 20 re
23
24
  B
24
25
  Q
26
+ EMC
25
27
  q
26
28
  1 0 0 1 0 0 cm
27
29
  Q
28
30
  CONTENTS
31
+ ocg = doc.optional_content.ocgs.first
32
+ assert_equal([['Debug', ocg]], doc.optional_content.default_configuration[:Order])
29
33
  end
30
34
  end
31
35
 
@@ -34,6 +38,14 @@ describe HexaPDF::Layout::Frame do
34
38
  @frame = HexaPDF::Layout::Frame.new(5, 10, 100, 150)
35
39
  end
36
40
 
41
+ it "allows accessing the context's document" do
42
+ assert_nil(@frame.document)
43
+ context = Minitest::Mock.new
44
+ context.expect(:document, :document)
45
+ assert_equal(:document, HexaPDF::Layout::Frame.new(0, 0, 10, 10, context: context).document)
46
+ context.verify
47
+ end
48
+
37
49
  it "allows access to the bounding box attributes" do
38
50
  assert_equal(5, @frame.left)
39
51
  assert_equal(10, @frame.bottom)
@@ -24,25 +24,28 @@ describe HexaPDF::Layout::InlineBox do
24
24
  assert_equal(:top, ibox.valign)
25
25
  end
26
26
 
27
+ it "fails if the wrapped box has not width set" do
28
+ box = HexaPDF::Document.new.layout.text("test is not going good")
29
+ assert_raises(HexaPDF::Error) { inline_box(box) }
30
+ end
31
+ end
32
+
33
+ describe "fit_wrapped_box" do
27
34
  it "automatically fits the provided box into a frame" do
28
35
  ibox = inline_box(HexaPDF::Document.new.layout.text("test is going good", width: 20))
36
+ ibox.fit_wrapped_box(nil)
29
37
  assert_equal(20, ibox.width)
30
38
  assert_equal(45, ibox.height)
31
39
  end
32
40
 
33
- it "fails if the wrapped box has not width set" do
34
- box = HexaPDF::Document.new.layout.text("test is not going good")
35
- assert_raises(HexaPDF::Error) { inline_box(box) }
36
- end
37
-
38
41
  it "fails if the wrapped box could not be fit" do
39
42
  box = HexaPDF::Document.new.layout.text("test is not going good", width: 1)
40
- assert_raises(HexaPDF::Error) { inline_box(box) }
43
+ assert_raises(HexaPDF::Error) { inline_box(box).fit_wrapped_box(nil) }
41
44
  end
42
45
 
43
46
  it "fails if the height is not set explicitly and during fitting" do
44
47
  assert_raises(HexaPDF::Error) do
45
- inline_box(HexaPDF::Layout::Box.create(width: 10))
48
+ inline_box(HexaPDF::Layout::Box.create(width: 10)).fit_wrapped_box(nil)
46
49
  end
47
50
  end
48
51
  end
@@ -50,7 +53,9 @@ describe HexaPDF::Layout::InlineBox do
50
53
  it "draws the wrapped box at the correct position" do
51
54
  doc = HexaPDF::Document.new
52
55
  canvas = doc.pages.add.canvas
53
- inline_box(doc.layout.text("", width: 20, margin: [15, 10])).draw(canvas, 100, 200)
56
+ box = inline_box(doc.layout.text("", width: 20, margin: [15, 10]))
57
+ box.fit_wrapped_box(nil)
58
+ box.draw(canvas, 100, 200)
54
59
  assert_equal("q\n1 0 0 1 110 -99785 cm\nQ\n", canvas.contents)
55
60
  end
56
61
 
@@ -59,6 +64,10 @@ describe HexaPDF::Layout::InlineBox do
59
64
  refute(HexaPDF::Layout::InlineBox.create(width: 10, height: 15) {}.empty?)
60
65
  end
61
66
 
67
+ it "returns the style of the box" do
68
+ assert_same(@box.box.style, @box.style)
69
+ end
70
+
62
71
  describe "valign" do
63
72
  it "has a default value of :baseline" do
64
73
  assert_equal(:baseline, @box.valign)