hexapdf 0.17.1 → 0.17.2

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 (255) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1024 -0
  3. data/LICENSE +29 -0
  4. data/README.md +129 -0
  5. data/Rakefile +109 -0
  6. data/agpl-3.0.txt +661 -0
  7. data/examples/001-hello_world.rb +16 -0
  8. data/examples/002-graphics.rb +275 -0
  9. data/examples/003-arcs.rb +50 -0
  10. data/examples/004-optimizing.rb +23 -0
  11. data/examples/005-merging.rb +27 -0
  12. data/examples/006-standard_pdf_fonts.rb +73 -0
  13. data/examples/007-truetype.rb +42 -0
  14. data/examples/008-show_char_bboxes.rb +55 -0
  15. data/examples/009-text_layouter_alignment.rb +47 -0
  16. data/examples/010-text_layouter_inline_boxes.rb +64 -0
  17. data/examples/011-text_layouter_line_wrapping.rb +57 -0
  18. data/examples/012-text_layouter_styling.rb +122 -0
  19. data/examples/013-text_layouter_shapes.rb +176 -0
  20. data/examples/014-text_in_polygon.rb +60 -0
  21. data/examples/015-boxes.rb +76 -0
  22. data/examples/016-frame_automatic_box_placement.rb +90 -0
  23. data/examples/017-frame_text_flow.rb +60 -0
  24. data/examples/018-composer.rb +44 -0
  25. data/examples/019-acro_form.rb +88 -0
  26. data/examples/emoji-smile.png +0 -0
  27. data/examples/emoji-wink.png +0 -0
  28. data/examples/machupicchu.jpg +0 -0
  29. data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +66 -0
  30. data/lib/hexapdf/content/graphic_object/geom2d.rb +13 -0
  31. data/lib/hexapdf/version.rb +1 -1
  32. data/test/data/aes-test-vectors/CBCGFSbox-128-decrypt.data.gz +0 -0
  33. data/test/data/aes-test-vectors/CBCGFSbox-128-encrypt.data.gz +0 -0
  34. data/test/data/aes-test-vectors/CBCGFSbox-192-decrypt.data.gz +0 -0
  35. data/test/data/aes-test-vectors/CBCGFSbox-192-encrypt.data.gz +0 -0
  36. data/test/data/aes-test-vectors/CBCGFSbox-256-decrypt.data.gz +0 -0
  37. data/test/data/aes-test-vectors/CBCGFSbox-256-encrypt.data.gz +0 -0
  38. data/test/data/aes-test-vectors/CBCKeySbox-128-decrypt.data.gz +0 -0
  39. data/test/data/aes-test-vectors/CBCKeySbox-128-encrypt.data.gz +0 -0
  40. data/test/data/aes-test-vectors/CBCKeySbox-192-decrypt.data.gz +0 -0
  41. data/test/data/aes-test-vectors/CBCKeySbox-192-encrypt.data.gz +0 -0
  42. data/test/data/aes-test-vectors/CBCKeySbox-256-decrypt.data.gz +0 -0
  43. data/test/data/aes-test-vectors/CBCKeySbox-256-encrypt.data.gz +0 -0
  44. data/test/data/aes-test-vectors/CBCVarKey-128-decrypt.data.gz +0 -0
  45. data/test/data/aes-test-vectors/CBCVarKey-128-encrypt.data.gz +0 -0
  46. data/test/data/aes-test-vectors/CBCVarKey-192-decrypt.data.gz +0 -0
  47. data/test/data/aes-test-vectors/CBCVarKey-192-encrypt.data.gz +0 -0
  48. data/test/data/aes-test-vectors/CBCVarKey-256-decrypt.data.gz +0 -0
  49. data/test/data/aes-test-vectors/CBCVarKey-256-encrypt.data.gz +0 -0
  50. data/test/data/aes-test-vectors/CBCVarTxt-128-decrypt.data.gz +0 -0
  51. data/test/data/aes-test-vectors/CBCVarTxt-128-encrypt.data.gz +0 -0
  52. data/test/data/aes-test-vectors/CBCVarTxt-192-decrypt.data.gz +0 -0
  53. data/test/data/aes-test-vectors/CBCVarTxt-192-encrypt.data.gz +0 -0
  54. data/test/data/aes-test-vectors/CBCVarTxt-256-decrypt.data.gz +0 -0
  55. data/test/data/aes-test-vectors/CBCVarTxt-256-encrypt.data.gz +0 -0
  56. data/test/data/fonts/Ubuntu-Title.ttf +0 -0
  57. data/test/data/images/cmyk.jpg +0 -0
  58. data/test/data/images/fillbytes.jpg +0 -0
  59. data/test/data/images/gray.jpg +0 -0
  60. data/test/data/images/greyscale-1bit.png +0 -0
  61. data/test/data/images/greyscale-2bit.png +0 -0
  62. data/test/data/images/greyscale-4bit.png +0 -0
  63. data/test/data/images/greyscale-8bit.png +0 -0
  64. data/test/data/images/greyscale-alpha-8bit.png +0 -0
  65. data/test/data/images/greyscale-trns-8bit.png +0 -0
  66. data/test/data/images/greyscale-with-gamma1.0.png +0 -0
  67. data/test/data/images/greyscale-with-gamma1.5.png +0 -0
  68. data/test/data/images/indexed-1bit.png +0 -0
  69. data/test/data/images/indexed-2bit.png +0 -0
  70. data/test/data/images/indexed-4bit.png +0 -0
  71. data/test/data/images/indexed-8bit.png +0 -0
  72. data/test/data/images/indexed-alpha-4bit.png +0 -0
  73. data/test/data/images/indexed-alpha-8bit.png +0 -0
  74. data/test/data/images/rgb.jpg +0 -0
  75. data/test/data/images/truecolour-8bit.png +0 -0
  76. data/test/data/images/truecolour-alpha-8bit.png +0 -0
  77. data/test/data/images/truecolour-gama-chrm-8bit.png +0 -0
  78. data/test/data/images/truecolour-srgb-8bit.png +0 -0
  79. data/test/data/images/ycck.jpg +0 -0
  80. data/test/data/minimal.pdf +44 -0
  81. data/test/data/standard-security-handler/README +9 -0
  82. data/test/data/standard-security-handler/bothpwd-aes-128bit-V4.pdf +44 -0
  83. data/test/data/standard-security-handler/bothpwd-aes-256bit-V5.pdf +0 -0
  84. data/test/data/standard-security-handler/bothpwd-arc4-128bit-V2.pdf +43 -0
  85. data/test/data/standard-security-handler/bothpwd-arc4-128bit-V4.pdf +43 -0
  86. data/test/data/standard-security-handler/bothpwd-arc4-40bit-V1.pdf +0 -0
  87. data/test/data/standard-security-handler/nopwd-aes-128bit-V4.pdf +43 -0
  88. data/test/data/standard-security-handler/nopwd-aes-256bit-V5.pdf +0 -0
  89. data/test/data/standard-security-handler/nopwd-arc4-128bit-V2.pdf +43 -0
  90. data/test/data/standard-security-handler/nopwd-arc4-128bit-V4.pdf +43 -0
  91. data/test/data/standard-security-handler/nopwd-arc4-40bit-V1.pdf +43 -0
  92. data/test/data/standard-security-handler/ownerpwd-aes-128bit-V4.pdf +0 -0
  93. data/test/data/standard-security-handler/ownerpwd-aes-256bit-V5.pdf +43 -0
  94. data/test/data/standard-security-handler/ownerpwd-arc4-128bit-V2.pdf +43 -0
  95. data/test/data/standard-security-handler/ownerpwd-arc4-128bit-V4.pdf +43 -0
  96. data/test/data/standard-security-handler/ownerpwd-arc4-40bit-V1.pdf +43 -0
  97. data/test/data/standard-security-handler/userpwd-aes-128bit-V4.pdf +43 -0
  98. data/test/data/standard-security-handler/userpwd-aes-256bit-V5.pdf +43 -0
  99. data/test/data/standard-security-handler/userpwd-arc4-128bit-V2.pdf +0 -0
  100. data/test/data/standard-security-handler/userpwd-arc4-128bit-V4.pdf +0 -0
  101. data/test/data/standard-security-handler/userpwd-arc4-40bit-V1.pdf +43 -0
  102. data/test/hexapdf/common_tokenizer_tests.rb +236 -0
  103. data/test/hexapdf/content/common.rb +39 -0
  104. data/test/hexapdf/content/graphic_object/test_arc.rb +102 -0
  105. data/test/hexapdf/content/graphic_object/test_endpoint_arc.rb +90 -0
  106. data/test/hexapdf/content/graphic_object/test_geom2d.rb +79 -0
  107. data/test/hexapdf/content/graphic_object/test_solid_arc.rb +86 -0
  108. data/test/hexapdf/content/test_canvas.rb +1279 -0
  109. data/test/hexapdf/content/test_color_space.rb +176 -0
  110. data/test/hexapdf/content/test_graphics_state.rb +151 -0
  111. data/test/hexapdf/content/test_operator.rb +619 -0
  112. data/test/hexapdf/content/test_parser.rb +99 -0
  113. data/test/hexapdf/content/test_processor.rb +163 -0
  114. data/test/hexapdf/content/test_transformation_matrix.rb +64 -0
  115. data/test/hexapdf/document/test_files.rb +72 -0
  116. data/test/hexapdf/document/test_fonts.rb +60 -0
  117. data/test/hexapdf/document/test_images.rb +72 -0
  118. data/test/hexapdf/document/test_pages.rb +130 -0
  119. data/test/hexapdf/encryption/common.rb +87 -0
  120. data/test/hexapdf/encryption/test_aes.rb +129 -0
  121. data/test/hexapdf/encryption/test_arc4.rb +39 -0
  122. data/test/hexapdf/encryption/test_fast_aes.rb +17 -0
  123. data/test/hexapdf/encryption/test_fast_arc4.rb +12 -0
  124. data/test/hexapdf/encryption/test_identity.rb +21 -0
  125. data/test/hexapdf/encryption/test_ruby_aes.rb +23 -0
  126. data/test/hexapdf/encryption/test_ruby_arc4.rb +20 -0
  127. data/test/hexapdf/encryption/test_security_handler.rb +380 -0
  128. data/test/hexapdf/encryption/test_standard_security_handler.rb +322 -0
  129. data/test/hexapdf/filter/common.rb +53 -0
  130. data/test/hexapdf/filter/test_ascii85_decode.rb +59 -0
  131. data/test/hexapdf/filter/test_ascii_hex_decode.rb +38 -0
  132. data/test/hexapdf/filter/test_crypt.rb +21 -0
  133. data/test/hexapdf/filter/test_encryption.rb +24 -0
  134. data/test/hexapdf/filter/test_flate_decode.rb +44 -0
  135. data/test/hexapdf/filter/test_lzw_decode.rb +52 -0
  136. data/test/hexapdf/filter/test_predictor.rb +219 -0
  137. data/test/hexapdf/filter/test_run_length_decode.rb +32 -0
  138. data/test/hexapdf/font/cmap/test_parser.rb +102 -0
  139. data/test/hexapdf/font/cmap/test_writer.rb +66 -0
  140. data/test/hexapdf/font/encoding/test_base.rb +45 -0
  141. data/test/hexapdf/font/encoding/test_difference_encoding.rb +29 -0
  142. data/test/hexapdf/font/encoding/test_glyph_list.rb +59 -0
  143. data/test/hexapdf/font/encoding/test_zapf_dingbats_encoding.rb +16 -0
  144. data/test/hexapdf/font/test_cmap.rb +104 -0
  145. data/test/hexapdf/font/test_encoding.rb +27 -0
  146. data/test/hexapdf/font/test_invalid_glyph.rb +34 -0
  147. data/test/hexapdf/font/test_true_type_wrapper.rb +186 -0
  148. data/test/hexapdf/font/test_type1_wrapper.rb +107 -0
  149. data/test/hexapdf/font/true_type/common.rb +17 -0
  150. data/test/hexapdf/font/true_type/table/common.rb +27 -0
  151. data/test/hexapdf/font/true_type/table/test_cmap.rb +47 -0
  152. data/test/hexapdf/font/true_type/table/test_cmap_subtable.rb +141 -0
  153. data/test/hexapdf/font/true_type/table/test_directory.rb +30 -0
  154. data/test/hexapdf/font/true_type/table/test_glyf.rb +58 -0
  155. data/test/hexapdf/font/true_type/table/test_head.rb +56 -0
  156. data/test/hexapdf/font/true_type/table/test_hhea.rb +26 -0
  157. data/test/hexapdf/font/true_type/table/test_hmtx.rb +30 -0
  158. data/test/hexapdf/font/true_type/table/test_kern.rb +61 -0
  159. data/test/hexapdf/font/true_type/table/test_loca.rb +33 -0
  160. data/test/hexapdf/font/true_type/table/test_maxp.rb +50 -0
  161. data/test/hexapdf/font/true_type/table/test_name.rb +76 -0
  162. data/test/hexapdf/font/true_type/table/test_os2.rb +55 -0
  163. data/test/hexapdf/font/true_type/table/test_post.rb +78 -0
  164. data/test/hexapdf/font/true_type/test_builder.rb +42 -0
  165. data/test/hexapdf/font/true_type/test_font.rb +116 -0
  166. data/test/hexapdf/font/true_type/test_optimizer.rb +26 -0
  167. data/test/hexapdf/font/true_type/test_subsetter.rb +73 -0
  168. data/test/hexapdf/font/true_type/test_table.rb +48 -0
  169. data/test/hexapdf/font/type1/common.rb +6 -0
  170. data/test/hexapdf/font/type1/test_afm_parser.rb +65 -0
  171. data/test/hexapdf/font/type1/test_font.rb +104 -0
  172. data/test/hexapdf/font/type1/test_font_metrics.rb +22 -0
  173. data/test/hexapdf/font/type1/test_pfb_parser.rb +37 -0
  174. data/test/hexapdf/font_loader/test_from_configuration.rb +43 -0
  175. data/test/hexapdf/font_loader/test_from_file.rb +36 -0
  176. data/test/hexapdf/font_loader/test_standard14.rb +33 -0
  177. data/test/hexapdf/image_loader/test_jpeg.rb +93 -0
  178. data/test/hexapdf/image_loader/test_pdf.rb +47 -0
  179. data/test/hexapdf/image_loader/test_png.rb +259 -0
  180. data/test/hexapdf/layout/test_box.rb +154 -0
  181. data/test/hexapdf/layout/test_frame.rb +350 -0
  182. data/test/hexapdf/layout/test_image_box.rb +73 -0
  183. data/test/hexapdf/layout/test_inline_box.rb +71 -0
  184. data/test/hexapdf/layout/test_line.rb +206 -0
  185. data/test/hexapdf/layout/test_style.rb +790 -0
  186. data/test/hexapdf/layout/test_text_box.rb +140 -0
  187. data/test/hexapdf/layout/test_text_fragment.rb +375 -0
  188. data/test/hexapdf/layout/test_text_layouter.rb +758 -0
  189. data/test/hexapdf/layout/test_text_shaper.rb +62 -0
  190. data/test/hexapdf/layout/test_width_from_polygon.rb +109 -0
  191. data/test/hexapdf/task/test_dereference.rb +51 -0
  192. data/test/hexapdf/task/test_optimize.rb +162 -0
  193. data/test/hexapdf/test_composer.rb +258 -0
  194. data/test/hexapdf/test_configuration.rb +93 -0
  195. data/test/hexapdf/test_data_dir.rb +32 -0
  196. data/test/hexapdf/test_dictionary.rb +340 -0
  197. data/test/hexapdf/test_dictionary_fields.rb +269 -0
  198. data/test/hexapdf/test_document.rb +641 -0
  199. data/test/hexapdf/test_filter.rb +100 -0
  200. data/test/hexapdf/test_importer.rb +106 -0
  201. data/test/hexapdf/test_object.rb +258 -0
  202. data/test/hexapdf/test_parser.rb +645 -0
  203. data/test/hexapdf/test_pdf_array.rb +169 -0
  204. data/test/hexapdf/test_rectangle.rb +73 -0
  205. data/test/hexapdf/test_reference.rb +50 -0
  206. data/test/hexapdf/test_revision.rb +188 -0
  207. data/test/hexapdf/test_revisions.rb +196 -0
  208. data/test/hexapdf/test_serializer.rb +195 -0
  209. data/test/hexapdf/test_stream.rb +274 -0
  210. data/test/hexapdf/test_tokenizer.rb +80 -0
  211. data/test/hexapdf/test_type.rb +18 -0
  212. data/test/hexapdf/test_writer.rb +140 -0
  213. data/test/hexapdf/test_xref_section.rb +61 -0
  214. data/test/hexapdf/type/acro_form/test_appearance_generator.rb +795 -0
  215. data/test/hexapdf/type/acro_form/test_button_field.rb +308 -0
  216. data/test/hexapdf/type/acro_form/test_choice_field.rb +220 -0
  217. data/test/hexapdf/type/acro_form/test_field.rb +259 -0
  218. data/test/hexapdf/type/acro_form/test_form.rb +357 -0
  219. data/test/hexapdf/type/acro_form/test_signature_field.rb +38 -0
  220. data/test/hexapdf/type/acro_form/test_text_field.rb +201 -0
  221. data/test/hexapdf/type/acro_form/test_variable_text_field.rb +88 -0
  222. data/test/hexapdf/type/actions/test_launch.rb +24 -0
  223. data/test/hexapdf/type/actions/test_uri.rb +23 -0
  224. data/test/hexapdf/type/annotations/test_markup_annotation.rb +22 -0
  225. data/test/hexapdf/type/annotations/test_text.rb +34 -0
  226. data/test/hexapdf/type/annotations/test_widget.rb +225 -0
  227. data/test/hexapdf/type/test_annotation.rb +97 -0
  228. data/test/hexapdf/type/test_catalog.rb +48 -0
  229. data/test/hexapdf/type/test_cid_font.rb +61 -0
  230. data/test/hexapdf/type/test_file_specification.rb +141 -0
  231. data/test/hexapdf/type/test_font.rb +67 -0
  232. data/test/hexapdf/type/test_font_descriptor.rb +61 -0
  233. data/test/hexapdf/type/test_font_simple.rb +176 -0
  234. data/test/hexapdf/type/test_font_true_type.rb +31 -0
  235. data/test/hexapdf/type/test_font_type0.rb +120 -0
  236. data/test/hexapdf/type/test_font_type1.rb +142 -0
  237. data/test/hexapdf/type/test_font_type3.rb +26 -0
  238. data/test/hexapdf/type/test_form.rb +120 -0
  239. data/test/hexapdf/type/test_image.rb +261 -0
  240. data/test/hexapdf/type/test_info.rb +9 -0
  241. data/test/hexapdf/type/test_object_stream.rb +117 -0
  242. data/test/hexapdf/type/test_page.rb +598 -0
  243. data/test/hexapdf/type/test_page_tree_node.rb +315 -0
  244. data/test/hexapdf/type/test_resources.rb +209 -0
  245. data/test/hexapdf/type/test_trailer.rb +116 -0
  246. data/test/hexapdf/type/test_xref_stream.rb +143 -0
  247. data/test/hexapdf/utils/test_bit_field.rb +63 -0
  248. data/test/hexapdf/utils/test_bit_stream.rb +69 -0
  249. data/test/hexapdf/utils/test_graphics_helpers.rb +37 -0
  250. data/test/hexapdf/utils/test_lru_cache.rb +22 -0
  251. data/test/hexapdf/utils/test_object_hash.rb +120 -0
  252. data/test/hexapdf/utils/test_pdf_doc_encoding.rb +18 -0
  253. data/test/hexapdf/utils/test_sorted_tree_node.rb +239 -0
  254. data/test/test_helper.rb +58 -0
  255. metadata +263 -3
@@ -0,0 +1,169 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/pdf_array'
5
+ require 'hexapdf/reference'
6
+ require 'hexapdf/dictionary'
7
+
8
+ describe HexaPDF::PDFArray do
9
+ def deref(obj)
10
+ if obj.kind_of?(HexaPDF::Reference)
11
+ HexaPDF::Object.new('deref', oid: obj.oid, gen: obj.gen)
12
+ else
13
+ obj
14
+ end
15
+ end
16
+
17
+ def add(obj)
18
+ HexaPDF::Object.new(obj, oid: 1)
19
+ end
20
+
21
+ def delete(_obj)
22
+ end
23
+
24
+ def wrap(obj, type:)
25
+ type.new(obj, document: self)
26
+ end
27
+
28
+ before do
29
+ @array = HexaPDF::PDFArray.new([1, HexaPDF::Object.new(:data), HexaPDF::Reference.new(1, 0),
30
+ HexaPDF::Dictionary.new({a: 'b'})], document: self)
31
+ end
32
+
33
+ describe "after_data_change" do
34
+ it "uses an empty array if nil is provided" do
35
+ array = HexaPDF::PDFArray.new(nil)
36
+ assert_equal([], array.value)
37
+ end
38
+ it "fails if the value is not an array" do
39
+ assert_raises(ArgumentError) { HexaPDF::PDFArray.new(:Name) }
40
+ end
41
+ end
42
+
43
+ describe "[]" do
44
+ it "allows retrieving values by index" do
45
+ assert_equal(1, @array[0])
46
+ end
47
+
48
+ it "allows retrieving values by start/length" do
49
+ assert_equal([1, :data], @array[0, 2])
50
+ end
51
+
52
+ it "allows retrieving values by range" do
53
+ assert_equal([1, :data], @array[0..1])
54
+ end
55
+
56
+ it "fetches the value out of a HexaPDF::Object" do
57
+ assert_equal(:data, @array[1])
58
+ end
59
+
60
+ it "resolves references and stores the resolved object in place of the reference" do
61
+ assert_equal('deref', @array[2])
62
+ assert_kind_of(HexaPDF::Object, @array.value[2])
63
+ end
64
+ end
65
+
66
+ describe "[]=" do
67
+ it "directly stores the value if the stored value is no HexaPDF::Object" do
68
+ @array[0] = 2
69
+ assert_equal(2, @array.value[0])
70
+ end
71
+
72
+ it "stores the value in an existing HexaPDF::Object but only if it is not such an object" do
73
+ @array[1] = [4, 5]
74
+ assert_equal([4, 5], @array.value[1].value)
75
+
76
+ @array[1] = temp = HexaPDF::Object.new(:other)
77
+ assert_equal(temp, @array.value[1])
78
+ end
79
+
80
+ it "doesn't store the value inside the existing object for subclasses of HexaPDF::Object" do
81
+ @array[3] = [4, 5]
82
+ assert_equal([4, 5], @array.value[3])
83
+ end
84
+
85
+ it "doesn't store the value inside for HexaPDF::Reference objects" do
86
+ @array[1] = HexaPDF::Reference.new(5, 0)
87
+ assert_kind_of(HexaPDF::Reference, @array.value[1])
88
+ end
89
+ end
90
+
91
+ it "allows getting multiple values at once" do
92
+ assert_equal([1, :data, @array[3]], @array.values_at(0, 1, 3))
93
+ end
94
+
95
+ it "allows adding values to the end" do
96
+ @array << 5
97
+ assert_equal(5, @array[4])
98
+ end
99
+
100
+ it "allows inserting values like Array#insert" do
101
+ @array.insert(1, :a, :b)
102
+ assert_equal([1, :a, :b, :data], @array[0, 4])
103
+ end
104
+
105
+ it "allows deleting values at a certain index" do
106
+ @array.delete_at(2)
107
+ assert_equal([1, :data, @array[2]], @array[0, 5])
108
+ end
109
+
110
+ it "allows deleting an object" do
111
+ obj = @array.value[1]
112
+ assert_same(obj, @array.delete(obj))
113
+ ref = HexaPDF::Object.new(:test, oid: 1)
114
+ assert_equal(ref, @array.delete(ref))
115
+ end
116
+
117
+ describe "slice!" do
118
+ it "allows deleting a single element" do
119
+ @array.slice!(2)
120
+ assert_equal([1, :data, @array[2]], @array[0, 5])
121
+ end
122
+
123
+ it "allows deleting elements given by start/length" do
124
+ @array.slice!(1, 2)
125
+ assert_equal([1, @array[1]], @array[0, 5])
126
+ end
127
+
128
+ it "allows deleting elements given a range" do
129
+ @array.slice!(1..2)
130
+ assert_equal([1, @array[1]], @array[0, 5])
131
+ end
132
+ end
133
+
134
+ it "allows deleting elements that are selected using a block" do
135
+ @array.reject! {|item| item == :data }
136
+ assert_equal([1, "deref", @array[2]], @array[0, 5])
137
+ end
138
+
139
+ describe "index" do
140
+ it "allows getting the index of an element" do
141
+ assert_equal(2, @array.index("deref"))
142
+ end
143
+
144
+ it "allows getting the index of the first element for which a block returns true" do
145
+ assert_equal(2, @array.index {|item| item == "deref" })
146
+ end
147
+ end
148
+
149
+ it "returns the length of the array" do
150
+ assert_equal(4, @array.length)
151
+ end
152
+
153
+ it "allows checking for emptiness" do
154
+ refute(@array.empty?)
155
+ @array.slice!(0, 5)
156
+ assert(@array.empty?)
157
+ end
158
+
159
+ describe "each" do
160
+ it "iterates over all elements in the dictionary" do
161
+ data = [1, :data, "deref", @array[3]]
162
+ @array.each {|value| assert_equal(data.shift, value) }
163
+ end
164
+ end
165
+
166
+ it "can be converted to a simple array" do
167
+ assert_equal([1, :data, "deref", @array[3]], @array.to_ary)
168
+ end
169
+ end
@@ -0,0 +1,73 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/rectangle'
5
+ require 'hexapdf/document'
6
+
7
+ describe HexaPDF::Rectangle do
8
+ describe "after_data_change" do
9
+ it "fails if the rectangle doesn't contain four numbers" do
10
+ assert_raises(ArgumentError) { HexaPDF::Rectangle.new([1, 2, 3]) }
11
+ assert_raises(ArgumentError) { HexaPDF::Rectangle.new([1, 2, 3, :a]) }
12
+ end
13
+
14
+ it "normalizes the array values" do
15
+ rect = HexaPDF::Rectangle.new([0, 1, 2, 3])
16
+ assert_equal([0, 1, 2, 3], rect.value)
17
+
18
+ rect = HexaPDF::Rectangle.new([2, 3, 0, 1])
19
+ assert_equal([0, 1, 2, 3], rect.value)
20
+
21
+ rect = HexaPDF::Rectangle.new([0, 3, 2, 1])
22
+ assert_equal([0, 1, 2, 3], rect.value)
23
+
24
+ rect = HexaPDF::Rectangle.new([2, 1, 0, 3])
25
+ assert_equal([0, 1, 2, 3], rect.value)
26
+ end
27
+ end
28
+
29
+ it "returns individual fields of the rectangle" do
30
+ rect = HexaPDF::Rectangle.new([2, 1, 0, 5])
31
+ assert_equal(0, rect.left)
32
+ assert_equal(2, rect.right)
33
+ assert_equal(1, rect.bottom)
34
+ assert_equal(5, rect.top)
35
+ assert_equal(2, rect.width)
36
+ assert_equal(4, rect.height)
37
+ end
38
+
39
+ it "allows setting all fields of the rectangle" do
40
+ rect = HexaPDF::Rectangle.new([2, 1, 0, 5])
41
+ rect.left = 5
42
+ rect.right = 1
43
+ rect.bottom = 2
44
+ rect.top = 3
45
+ assert_equal([5, 2, 1, 3], rect.value)
46
+
47
+ rect.width = 10
48
+ assert_equal(15, rect.right)
49
+ rect.height = 10
50
+ assert_equal(12, rect.top)
51
+ end
52
+
53
+ it "allows comparison to arrays" do
54
+ rect = HexaPDF::Rectangle.new([0, 1, 2, 5])
55
+ assert(rect == [0, 1, 2, 5])
56
+ rect.oid = 5
57
+ refute(rect == [0, 1, 2, 5])
58
+ end
59
+
60
+ describe "validation" do
61
+ it "ensures that it is a correct PDF rectangle" do
62
+ doc = HexaPDF::Document.new
63
+ rect = HexaPDF::Rectangle.new([0, 1, 2, 3], document: doc)
64
+ assert(rect.validate)
65
+
66
+ rect.value.shift
67
+ refute(rect.validate)
68
+
69
+ rect.value.unshift(:A)
70
+ refute(rect.validate)
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,50 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/reference'
5
+ require 'hexapdf/object'
6
+
7
+ describe HexaPDF::Reference do
8
+ it "correctly assigns oid and gen on initialization" do
9
+ r = HexaPDF::Reference.new(5, 7)
10
+ assert_equal(5, r.oid)
11
+ assert_equal(7, r.gen)
12
+ end
13
+
14
+ it "raises an error when invalid objects are supplied on initialization" do
15
+ assert_raises(ArgumentError) { HexaPDF::Reference.new('a', 7) }
16
+ assert_raises(ArgumentError) { HexaPDF::Reference.new(5, 'b') }
17
+ end
18
+
19
+ it "is sortable w.r.t to other objects implementing #oid and #gen" do
20
+ obj = Object.new
21
+ obj.define_singleton_method(:oid) { 1 }
22
+ obj.define_singleton_method(:gen) { 0 }
23
+ obj.define_singleton_method(:<=>) {|o| HexaPDF::Reference.new(oid, gen) <=> o }
24
+ assert_equal([obj, HexaPDF::Reference.new(1, 1), HexaPDF::Reference.new(5, 7)],
25
+ [HexaPDF::Reference.new(5, 7), HexaPDF::Reference.new(1, 1), obj].sort)
26
+ assert_nil(HexaPDF::Reference.new(1, 0) <=> 5)
27
+ end
28
+
29
+ it "is comparable to objects that have an oid and gen" do
30
+ assert_equal(HexaPDF::Reference.new(5, 7), HexaPDF::Reference.new(5, 7))
31
+ refute_equal(HexaPDF::Reference.new(5, 7), HexaPDF::Reference.new(5, 8))
32
+ refute_equal(HexaPDF::Reference.new(5, 7), HexaPDF::Reference.new(4, 7))
33
+ assert_equal(HexaPDF::Reference.new(5, 7), HexaPDF::Object.new(:data, oid: 5, gen: 7))
34
+ end
35
+
36
+ it "behaves correctly as hash key" do
37
+ h = {}
38
+ h[HexaPDF::Reference.new(5, 7)] = true
39
+ assert(h.key?(HexaPDF::Reference.new(5, 7)))
40
+ refute(h.key?(HexaPDF::Reference.new(5, 8)))
41
+ end
42
+
43
+ it "shows the PDF serialization as string representation " do
44
+ assert_equal("5 7 R", HexaPDF::Reference.new(5, 7).to_s)
45
+ end
46
+
47
+ it "shows oid and gen on inspection" do
48
+ assert_match(/\[5, 7\]/, HexaPDF::Reference.new(5, 7).inspect)
49
+ end
50
+ end
@@ -0,0 +1,188 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/revision'
5
+ require 'hexapdf/object'
6
+ require 'hexapdf/reference'
7
+ require 'hexapdf/xref_section'
8
+ require 'stringio'
9
+
10
+ describe HexaPDF::Revision do
11
+ before do
12
+ @xref_section = HexaPDF::XRefSection.new
13
+ @xref_section.add_in_use_entry(2, 0, 5000)
14
+ @xref_section.add_free_entry(3, 0)
15
+ @obj = HexaPDF::Object.new(:val, oid: 1, gen: 0)
16
+ @ref = HexaPDF::Reference.new(1, 0)
17
+
18
+ @loader = lambda do |entry|
19
+ if entry.type == :free
20
+ HexaPDF::Object.new(nil, oid: entry.oid, gen: entry.gen)
21
+ else
22
+ HexaPDF::Object.new(:Test, oid: entry.oid, gen: entry.gen)
23
+ end
24
+ end
25
+ @rev = HexaPDF::Revision.new({}, xref_section: @xref_section, loader: @loader)
26
+ end
27
+
28
+ it "needs the trailer as first argument on initialization" do
29
+ rev = HexaPDF::Revision.new({})
30
+ assert_equal({}, rev.trailer)
31
+ end
32
+
33
+ it "takes an xref section and/or a parser on initialization" do
34
+ rev = HexaPDF::Revision.new({}, loader: @loader, xref_section: @xref_section)
35
+ assert_equal(:Test, rev.object(2).value)
36
+ end
37
+
38
+ it "returns the next free object number" do
39
+ assert_equal(4, @rev.next_free_oid)
40
+ @obj.oid = 4
41
+ @rev.add(@obj)
42
+ assert_equal(5, @rev.next_free_oid)
43
+ end
44
+
45
+ describe "add" do
46
+ it "works correctly" do
47
+ @rev.add(@obj)
48
+ assert(@rev.object?(@ref))
49
+ end
50
+
51
+ it "also returns the supplied object" do
52
+ assert_equal(@obj, @rev.add(@obj))
53
+ end
54
+
55
+ it "fails if the revision already has an object with the same object number" do
56
+ @rev.add(@obj)
57
+ assert_raises(HexaPDF::Error) { @rev.add(@obj) }
58
+ assert_raises(HexaPDF::Error) { @rev.add(HexaPDF::Object.new(:val, oid: 2)) }
59
+ end
60
+
61
+ it "fails if the given object has an object number of zero" do
62
+ assert_raises(HexaPDF::Error) { @rev.add(HexaPDF::Object.new(:val)) }
63
+ end
64
+ end
65
+
66
+ describe "xref" do
67
+ it "returns the xref structure" do
68
+ assert_equal(@xref_section[2, 0], @rev.xref(HexaPDF::Reference.new(2, 0)))
69
+ assert_equal(@xref_section[2, 0], @rev.xref(2))
70
+ end
71
+
72
+ it "returns nil if no xref entry is found" do
73
+ assert_nil(@rev.xref(@ref))
74
+ assert_nil(@rev.xref(1))
75
+ end
76
+ end
77
+
78
+ describe "object" do
79
+ it "returns nil if no object is found" do
80
+ assert_nil(@rev.object(@ref))
81
+ assert_nil(@rev.object(1))
82
+ end
83
+
84
+ it "returns an object that was added before" do
85
+ @rev.add(@obj)
86
+ assert_equal(@obj, @rev.object(@ref))
87
+ assert_equal(@obj, @rev.object(1))
88
+ end
89
+
90
+ it "loads an object that is defined in the cross-reference section" do
91
+ obj = @rev.object(HexaPDF::Reference.new(2, 0))
92
+ assert_equal(:Test, obj.value)
93
+ assert_equal(2, obj.oid)
94
+ assert_equal(0, obj.gen)
95
+ end
96
+
97
+ it "loads an object that is defined in the cross-reference section using the object number" do
98
+ obj = @rev.object(2)
99
+ refute_nil(obj)
100
+ end
101
+
102
+ it "loads free entries in the cross-reference section as special PDF null objects" do
103
+ obj = @rev.object(HexaPDF::Reference.new(3, 0))
104
+ assert_nil(obj.value)
105
+ end
106
+ end
107
+
108
+ describe "update" do
109
+ before do
110
+ @rev.add(@obj)
111
+ end
112
+
113
+ it "updates the object if it has the same data instance" do
114
+ x = HexaPDF::Object.new(@obj.data)
115
+ y = @rev.update(x)
116
+ assert_same(x, y)
117
+ refute_same(x, @obj)
118
+ assert_same(x, @rev.object(@ref))
119
+ end
120
+
121
+ it "doesn't update the object if it refers to a different data instance" do
122
+ x = HexaPDF::Object.new(:value, oid: 5)
123
+ assert_nil(@rev.update(x))
124
+ x.data.oid = 1
125
+ assert_nil(@rev.update(x))
126
+ end
127
+ end
128
+
129
+ describe "delete" do
130
+ before do
131
+ @rev.add(@obj)
132
+ end
133
+
134
+ it "deletes objects specified by reference" do
135
+ @rev.delete(@ref, mark_as_free: false)
136
+ refute(@rev.object?(@ref))
137
+ assert(@obj.null?)
138
+ assert_raises(HexaPDF::Error) { @obj.document }
139
+ end
140
+
141
+ it "deletes objects specified by object number" do
142
+ @rev.delete(@ref.oid, mark_as_free: false)
143
+ refute(@rev.object?(@ref.oid))
144
+ assert(@obj.null?)
145
+ assert_raises(HexaPDF::Error) { @obj.document }
146
+ end
147
+
148
+ it "marks the object as PDF null object when using mark_as_free=true" do
149
+ refute(@obj.null?)
150
+ @rev.delete(@ref)
151
+ assert(@rev.object(@ref).null?)
152
+ assert(@obj.null?)
153
+ assert_raises(HexaPDF::Error) { @obj.document }
154
+ end
155
+ end
156
+
157
+ describe "object iteration" do
158
+ it "iterates over all objects via each" do
159
+ @rev.add(@obj)
160
+ obj2 = @rev.object(2)
161
+ obj3 = @rev.object(3)
162
+ assert_equal([@obj, obj2, obj3], @rev.each.to_a)
163
+ end
164
+
165
+ it "iterates only over loaded objects" do
166
+ obj = @rev.object(2)
167
+ assert_equal([obj], @rev.each(only_loaded: true).to_a)
168
+ end
169
+ end
170
+
171
+ it "works without a cross-reference section" do
172
+ rev = HexaPDF::Revision.new({})
173
+ rev.add(@obj)
174
+ assert_equal(@obj, rev.object(@ref))
175
+ assert(rev.object?(@ref))
176
+ assert_equal([@obj], rev.each.to_a)
177
+ rev.delete(@ref, mark_as_free: false)
178
+ refute(rev.object?(@ref))
179
+ end
180
+
181
+ it "can iterate over all modified objects" do
182
+ obj = @rev.object(2)
183
+ assert_equal([], @rev.each_modified_object.to_a)
184
+ obj.value = :Other
185
+ @rev.add(@obj)
186
+ assert_equal([obj, @obj], @rev.each_modified_object.to_a)
187
+ end
188
+ end
@@ -0,0 +1,196 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/revisions'
5
+ require 'hexapdf/document'
6
+ require 'stringio'
7
+
8
+ describe HexaPDF::Revisions do
9
+ before do
10
+ @io = StringIO.new(<<~EOF)
11
+ %PDF-1.7
12
+ 1 0 obj
13
+ 10
14
+ endobj
15
+
16
+ 2 0 obj
17
+ 20
18
+ endobj
19
+
20
+ xref
21
+ 0 3
22
+ 0000000000 65535 f
23
+ 0000000009 00000 n
24
+ 0000000028 00000 n
25
+ trailer
26
+ << /Size 3 >>
27
+ startxref
28
+ 47
29
+ %%EOF
30
+
31
+ 2 0 obj
32
+ 300
33
+ endobj
34
+
35
+ 3 0 obj
36
+ << /Type /XRef /Size 4 /Index [2 1] /W [1 1 1] /Filter /ASCIIHexDecode /Length 6
37
+ >>stream
38
+ 019E00
39
+ endstream
40
+ endobj
41
+
42
+ 2 0 obj
43
+ 200
44
+ endobj
45
+
46
+ xref
47
+ 2 2
48
+ 0000000301 00000 n
49
+ 0000000178 00000 n
50
+ trailer
51
+ << /Size 4 /Prev 47 >>
52
+ startxref
53
+ 321
54
+ %%EOF
55
+
56
+ 2 0 obj
57
+ 400
58
+ endobj
59
+
60
+ xref
61
+ 2 1
62
+ 0000000422 00000 n
63
+ trailer
64
+ << /Size 4 /Prev 321 /XRefStm 178 >>
65
+ startxref
66
+ 442
67
+ %%EOF
68
+ EOF
69
+ @doc = HexaPDF::Document.new(io: @io)
70
+ @revisions = @doc.revisions
71
+ end
72
+
73
+ describe "add" do
74
+ it "adds an empty revision as the current revision" do
75
+ rev = @revisions.add
76
+ assert_equal({Size: 4}, rev.trailer.value)
77
+ assert_equal(rev, @revisions.current)
78
+ end
79
+ end
80
+
81
+ describe "delete_revision" do
82
+ it "allows deleting a revision by index" do
83
+ rev = @revisions.revision(0)
84
+ @revisions.delete(0)
85
+ refute(@revisions.any? {|r| r == rev })
86
+ end
87
+
88
+ it "allows deleting a revision by specifying a revision" do
89
+ rev = @revisions.revision(0)
90
+ @revisions.delete(rev)
91
+ refute(@revisions.any? {|r| r == rev })
92
+ end
93
+
94
+ it "fails when trying to delete the only existing revision" do
95
+ assert_raises(HexaPDF::Error) { @revisions.delete(0) while @revisions.current }
96
+ end
97
+ end
98
+
99
+ describe "merge" do
100
+ it "does nothing when only one revision is specified" do
101
+ @revisions.merge(1..1)
102
+ assert_equal(3, @revisions.each.to_a.size)
103
+ end
104
+
105
+ it "merges the higher into the the lower revision" do
106
+ @revisions.merge
107
+ assert_equal(1, @revisions.each.to_a.size)
108
+ assert_equal([10, 400, @doc.object(3).value], @revisions.current.each.to_a.sort.map(&:value))
109
+ end
110
+
111
+ it "handles objects correctly that are in multiple revisions" do
112
+ @revisions.current.add(@revisions[0].object(1))
113
+ @revisions.merge
114
+ assert_equal(1, @revisions.each.to_a.size)
115
+ assert_equal([10, 400, @doc.object(3).value], @revisions.current.each.to_a.sort.map(&:value))
116
+ end
117
+ end
118
+
119
+ describe "initialize" do
120
+ it "automatically loads all revisions from the underlying IO object" do
121
+ assert_kind_of(HexaPDF::Parser, @revisions.parser)
122
+ assert_equal(20, @revisions.revision(0).object(2).value)
123
+ assert_equal(300, @revisions[1].object(2).value)
124
+ assert_equal(400, @revisions[2].object(2).value)
125
+ end
126
+ end
127
+
128
+ it "handles invalid PDFs that have a loop via the xref /Prev or /XRefStm entries" do
129
+ io = StringIO.new(<<~EOF)
130
+ %PDF-1.7
131
+ 1 0 obj
132
+ 10
133
+ endobj
134
+
135
+ xref
136
+ 0 2
137
+ 0000000000 65535 f
138
+ 0000000009 00000 n
139
+ trailer
140
+ << /Size 2 /Prev 148>>
141
+ startxref
142
+ 28
143
+ %%EOF
144
+
145
+ 2 0 obj
146
+ 300
147
+ endobj
148
+
149
+ xref
150
+ 2 1
151
+ 0000000301 00000 n
152
+ trailer
153
+ << /Size 3 /Prev 28 /XRefStm 148>>
154
+ startxref
155
+ 148
156
+ %%EOF
157
+ EOF
158
+ doc = HexaPDF::Document.new(io: io)
159
+ assert_equal(2, doc.revisions.count)
160
+ end
161
+
162
+ it "uses the reconstructed revision if errors are found when loading from an IO" do
163
+ io = StringIO.new(<<~EOF)
164
+ %PDF-1.7
165
+ 1 0 obj
166
+ 10
167
+ endobj
168
+
169
+ xref
170
+ 0 2
171
+ 0000000000 65535 f
172
+ 0000000009 00000 n
173
+ trailer
174
+ << /Size 5 >>
175
+ startxref
176
+ 28
177
+ %%EOF
178
+
179
+ 2 0 obj
180
+ 300
181
+ endobj
182
+
183
+ xref
184
+ 2 1
185
+ 0000000301 00000 n
186
+ trailer
187
+ << /Size 3 /Prev 100>>
188
+ startxref
189
+ 139
190
+ %%EOF
191
+ EOF
192
+ doc = HexaPDF::Document.new(io: io)
193
+ assert_equal(2, doc.revisions.count)
194
+ assert_same(doc.revisions[0].trailer.value, doc.revisions[1].trailer.value)
195
+ end
196
+ end