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,195 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'stringio'
5
+ require 'hexapdf/serializer'
6
+ require 'hexapdf/object'
7
+ require 'hexapdf/stream'
8
+
9
+ describe HexaPDF::Serializer do
10
+ before do
11
+ @serializer = HexaPDF::Serializer.new
12
+ end
13
+
14
+ it "allows access to the top serialized object" do
15
+ object = nil
16
+ @serializer.singleton_class.send(:define_method, :serialize_symbol) do |obj|
17
+ object = @object
18
+ "/#{obj}"
19
+ end
20
+ @serializer.serialize(this: :is, null: nil)
21
+ assert_equal({this: :is, null: nil}, object)
22
+ end
23
+
24
+ def assert_serialized(result, object)
25
+ assert_equal(result, @serializer.serialize(object))
26
+ end
27
+
28
+ it "works correctly with unnamed classes and modules" do
29
+ klass = Class.new(String)
30
+ s = klass.new("test")
31
+ assert_serialized("(test)", s)
32
+ end
33
+
34
+ it "serializes nil" do
35
+ assert_serialized("null", nil)
36
+ end
37
+
38
+ it "serializes true" do
39
+ assert_serialized("true", true)
40
+ end
41
+
42
+ it "serializes false" do
43
+ assert_serialized("false", false)
44
+ end
45
+
46
+ it "serializes integers" do
47
+ assert_serialized("100", 100)
48
+ assert_serialized("-100", -100)
49
+ assert_serialized("0", 0)
50
+ assert_serialized("1208925819614629174706176", 1_208_925_819_614_629_174_706_176)
51
+ end
52
+
53
+ it "serializes floats with a precision of 4" do
54
+ assert_serialized("1.5", 1.5)
55
+ assert_serialized("-1.5", -1.5)
56
+ assert_serialized("9.123456", 9.123456)
57
+ assert_serialized("9.123457", 9.1234567)
58
+ assert_serialized("0.000005", 0.000005)
59
+ assert_serialized("-0.000005", -0.000005)
60
+ assert_serialized("0.0", 0.0)
61
+ assert_raises(HexaPDF::Error) { @serializer.serialize(0.0 / 0) }
62
+ assert_raises(HexaPDF::Error) { @serializer.serialize(1.0 / 0) }
63
+ assert_raises(HexaPDF::Error) { @serializer.serialize(-1.0 / 0) }
64
+ end
65
+
66
+ it "serializes numerics" do
67
+ assert_equal("1", @serializer.serialize_numeric(1))
68
+ assert_equal("1.5", @serializer.serialize_numeric(1.5))
69
+ end
70
+
71
+ it "serializes symbols" do
72
+ assert_serialized("/Name", :Name)
73
+ assert_serialized("/A;Name_With-Various***Chars?", 'A;Name_With-Various***Chars?'.intern)
74
+ assert_serialized("/1.2", '1.2'.intern)
75
+ assert_serialized("/$$", '$$'.intern)
76
+ assert_serialized("/@pattern", '@pattern'.intern)
77
+ assert_serialized('/.notdef', '.notdef'.intern)
78
+ assert_serialized('/lime#20Green', 'lime Green'.intern)
79
+ assert_serialized('/paired#28#29parentheses', 'paired()parentheses'.intern)
80
+ assert_serialized('/The_Key_of_F#23_Minor', 'The_Key_of_F#_Minor'.intern)
81
+ assert_serialized('/ ', ''.intern)
82
+ assert_serialized('/H#c3#b6#c3#9fgang', "Hößgang".intern)
83
+ assert_serialized('/H#e8lp', "H\xE8lp".force_encoding('BINARY').intern)
84
+ end
85
+
86
+ it "serializes arrays" do
87
+ assert_serialized("[-12/ 2.4321/Name true(345)true]", [-12, :"", 2.4321, :Name, true, '345', true])
88
+ assert_serialized("[]", [])
89
+ end
90
+
91
+ it "serializes hashes" do
92
+ assert_serialized("<</hallo 5/ true/other true/name[5]>>", hallo: 5, "": true, other: true, name: [5])
93
+ assert_serialized("<<>>", {})
94
+ end
95
+
96
+ it "serializes strings" do
97
+ assert_serialized("(Hallo)", "Hallo")
98
+ assert_serialized("(Hallo\\r\n\t\\(\\)\\\\)", "Hallo\r\n\t()\\")
99
+ assert_serialized("(\xFE\xFF\x00H\x00a\x00l\x00\f\x00\b\x00\\()".force_encoding('BINARY'),
100
+ "Hal\f\b(")
101
+ end
102
+
103
+ it "serializes time like objects" do
104
+ begin
105
+ tz = ENV['TZ']
106
+ ENV['TZ'] = 'Europe/Vienna'
107
+ assert_serialized("(D:20150416094100)", Time.new(2015, 04, 16, 9, 41, 0, 0))
108
+ assert_serialized("(D:20150416094100+01'00')", Time.new(2015, 04, 16, 9, 41, 0, 3600))
109
+ assert_serialized("(D:20150416094100-01'20')", Time.new(2015, 04, 16, 9, 41, 0, -4800))
110
+ assert_serialized("(D:20150416000000+02'00')", Date.parse("2015-04-16 9:41:00 +02:00"))
111
+ assert_serialized("(D:20150416094100+02'00')",
112
+ Time.parse("2015-04-16 9:41:00 +02:00").to_datetime)
113
+ ensure
114
+ ENV['TZ'] = tz
115
+ end
116
+ end
117
+
118
+ it "serializes HexaPDF objects" do
119
+ assert_serialized("/Name", HexaPDF::Object.new(:Name))
120
+ assert_serialized("/Name", HexaPDF::Object.new(:Name, oid: 1))
121
+ assert_serialized("<</Name 2 0 R>>",
122
+ HexaPDF::Object.new({Name: HexaPDF::Object.new(5, oid: 2)}, oid: 1))
123
+ obj = HexaPDF::Object.new({Name: HexaPDF::Object.new(5, oid: 2)}, oid: 1)
124
+ obj.value[:Self] = obj
125
+ assert_serialized("<</Name 2 0 R/Self 1 0 R>>", obj)
126
+ end
127
+
128
+ it "serializes HexaPDF reference objects" do
129
+ assert_serialized("5 3 R", HexaPDF::Reference.new(5, 3))
130
+ end
131
+
132
+ describe "stream serialization" do
133
+ before do
134
+ @doc = Object.new
135
+ def (@doc).unwrap(obj); obj; end
136
+ def (@doc).config; {chunk_size: 100}; end
137
+ @stream = HexaPDF::Stream.new({Key: "value", Length: 5}, oid: 2, document: @doc)
138
+ end
139
+
140
+ it "serializes streams" do
141
+ @stream.stream = "somedata"
142
+ assert_serialized("<</Key(value)/Length 8>>stream\nsomedata\nendstream", @stream)
143
+ assert_serialized("<</Name 2 0 R>>", HexaPDF::Object.new({Name: @stream}))
144
+ end
145
+
146
+ it "handles self-referencing streams" do
147
+ @stream.value[:Self] = @stream
148
+ assert_serialized("<</Key(value)/Length 0/Self 2 0 R>>stream\n\nendstream",
149
+ @stream)
150
+ end
151
+
152
+ it "serializes stream more efficiently when an IO is provided" do
153
+ @stream.stream = HexaPDF::StreamData.new(proc { "some" }, length: 6)
154
+ io = StringIO.new(''.b)
155
+ @serializer.serialize_to_io(@stream, io)
156
+ assert_equal("<</Key(value)/Length 6>>stream\nsome\nendstream", io.string)
157
+ end
158
+
159
+ it "doesn't reset the internal recursion flag if the stream is serialized as part of another object" do
160
+ object = HexaPDF::Dictionary.new({}, oid: 5)
161
+ object[:Stream] = @stream
162
+ object[:Self] = object # needs to be the last entry so that :Stream gets serialized first!
163
+ assert_serialized("<</Stream 2 0 R/Self 5 0 R>>", object)
164
+ end
165
+
166
+ it "fails if a stream without object identifier is serialized" do
167
+ @stream.oid = 0
168
+ assert_raises(HexaPDF::Error) { @serializer.serialize(@stream) }
169
+ assert_raises(HexaPDF::Error) { @serializer.serialize(Name: @stream) }
170
+ end
171
+ end
172
+
173
+ describe "with an encrypter" do
174
+ before do
175
+ @serializer.encrypter = encrypter = Object.new
176
+ def encrypter.encrypt_string(str, obj); "enc:#{obj.oid}:#{str}"; end
177
+ def encrypter.encrypt_stream(obj); Fiber.new { "encs:#{obj.oid}:#{obj.stream}" }; end
178
+ end
179
+
180
+ it "encrypts strings in indirect PDF objects" do
181
+ assert_serialized("(enc:1:test)", HexaPDF::Object.new("test", oid: 1))
182
+ assert_serialized("<</x[(enc:1:test)]>>", HexaPDF::Object.new({x: ["test"]}, oid: 1))
183
+ end
184
+
185
+ it "doesn't encrypt strings in direct PDF objects" do
186
+ assert_serialized("(test)", HexaPDF::Object.new("test"))
187
+ assert_serialized("(test)", "test")
188
+ end
189
+
190
+ it "encrypts streams" do
191
+ obj = HexaPDF::Stream.new({}, oid: 1, stream: "stream")
192
+ assert_serialized("<</Length 13>>stream\nencs:1:stream\nendstream", obj)
193
+ end
194
+ end
195
+ end
@@ -0,0 +1,274 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'ostruct'
5
+ require 'stringio'
6
+ require 'tempfile'
7
+ require 'hexapdf/configuration'
8
+ require 'hexapdf/stream'
9
+
10
+ describe HexaPDF::StreamData do
11
+ it "fails if no valid source is specified on creation" do
12
+ assert_raises(ArgumentError) { HexaPDF::StreamData.new }
13
+ end
14
+
15
+ it "normalizes the filter value" do
16
+ s = HexaPDF::StreamData.new(:source, filter: :test)
17
+ assert_equal([:test], s.filter)
18
+ s = HexaPDF::StreamData.new(:source, filter: [:a, nil, :b])
19
+ assert_equal([:a, :b], s.filter)
20
+ s = HexaPDF::StreamData.new(:source)
21
+ assert_equal([], s.filter)
22
+ end
23
+
24
+ it "normalizes the decode_parms value" do
25
+ s = HexaPDF::StreamData.new(:source, decode_parms: :test)
26
+ assert_equal([:test], s.decode_parms)
27
+ s = HexaPDF::StreamData.new(:source, decode_parms: [:a, nil, :b])
28
+ assert_equal([:a, nil, :b], s.decode_parms)
29
+ s = HexaPDF::StreamData.new(:source)
30
+ assert_equal([nil], s.decode_parms)
31
+ end
32
+
33
+ describe "fiber" do
34
+ it "returns a fiber for a Proc source" do
35
+ s = HexaPDF::StreamData.new(proc { :source })
36
+ assert_equal(:source, s.fiber.resume)
37
+ end
38
+
39
+ it "returns a fiber for a source specified via a block" do
40
+ s = HexaPDF::StreamData.new { :source }
41
+ assert_equal(:source, s.fiber.resume)
42
+ end
43
+
44
+ it "returns a fiber for an IO source" do
45
+ s = HexaPDF::StreamData.new(StringIO.new('source'))
46
+ assert_equal('source', s.fiber.resume)
47
+ end
48
+
49
+ it "returns a fiber for a string representing a file name" do
50
+ begin
51
+ file = Tempfile.new('hexapdf-stream')
52
+ file.write('source')
53
+ file.close
54
+ s = HexaPDF::StreamData.new(file.path)
55
+ assert_equal('source', s.fiber.resume)
56
+ ensure
57
+ file.unlink
58
+ end
59
+ end
60
+ end
61
+
62
+ describe "==" do
63
+ it "compares with other stream data objects" do
64
+ s = HexaPDF::StreamData.new(:source, decode_parms: [:a, nil, :b])
65
+ assert_equal(s, HexaPDF::StreamData.new(:source, decode_parms: [:a, nil, :b]))
66
+ refute_equal(s, HexaPDF::StreamData.new(:source, decode_parms: [:a, :b]))
67
+ refute_equal(s, HexaPDF::StreamData.new(:source, decode_parms: [:a, nil, :b], offset: 5))
68
+ end
69
+
70
+ it "returns false if compared with an other object of a different class" do
71
+ refute_equal(:source, HexaPDF::StreamData.new(:source))
72
+ end
73
+ end
74
+ end
75
+
76
+ describe HexaPDF::Stream do
77
+ include TestHelper
78
+
79
+ before do
80
+ @document = OpenStruct.new
81
+ @document.config = HexaPDF::Configuration.with_defaults
82
+ @document.instance_variable_set(:@version, '1.2')
83
+ def (@document).unwrap(obj); obj; end
84
+ def (@document).wrap(obj, *); obj; end
85
+ def (@document).deref(obj); obj; end
86
+
87
+ @stm = HexaPDF::Stream.new({}, oid: 1, document: @document)
88
+ end
89
+
90
+ it "#initialize accepts the stream keyword" do
91
+ stm = HexaPDF::Stream.new({}, document: @document, stream: 'other')
92
+ assert_equal('other', stm.stream)
93
+ end
94
+
95
+ it "must always be indirect" do
96
+ @stm.must_be_indirect = false
97
+ assert(@stm.must_be_indirect?)
98
+ end
99
+
100
+ describe "stream=" do
101
+ it "allows assigning nil" do
102
+ @stm.stream = nil
103
+ assert_equal('', @stm.raw_stream)
104
+ assert_equal('', @stm.stream)
105
+ assert_equal(Encoding::BINARY, @stm.stream.encoding)
106
+ end
107
+
108
+ it "allows assigning a string" do
109
+ @stm.stream = 'hallo'
110
+ assert_equal('hallo', @stm.raw_stream)
111
+ assert_equal('hallo', @stm.stream)
112
+ end
113
+
114
+ it "retains the encoding if a String is assigned" do
115
+ @stm.stream = 'hallo'
116
+ assert_equal(Encoding::UTF_8, @stm.stream.encoding)
117
+ @stm.stream = 'hallo'.encode('ISO-8859-1')
118
+ assert_equal(Encoding::ISO_8859_1, @stm.stream.encoding)
119
+ end
120
+
121
+ it "allows assigning a StreamData object" do
122
+ @stmdata = HexaPDF::StreamData.new(StringIO.new('testing'))
123
+ @stm.stream = @stmdata
124
+ assert_equal(@stmdata, @stm.raw_stream)
125
+ assert_equal('testing', @stm.stream)
126
+ assert_equal(Encoding::BINARY, @stm.stream.encoding)
127
+ end
128
+
129
+ it "fails on any object class other than String, StreamData, NilClass" do
130
+ assert_raises(ArgumentError) { @stm.stream = 5 }
131
+ end
132
+ end
133
+
134
+ describe "stream" do
135
+ it "doesn't allow changing the returned value directly" do
136
+ @stm.stream = 'data'
137
+ @stm.stream.upcase!
138
+ assert_equal('data', @stm.stream)
139
+
140
+ @stm.stream = HexaPDF::StreamData.new { "data" }
141
+ @stm.stream.upcase!
142
+ assert_equal('data', @stm.stream)
143
+ end
144
+ end
145
+
146
+ def encoded_data(str, encoders = [])
147
+ map = @document.config['filter.map']
148
+ tmp = feeder(str)
149
+ encoders.each {|e| tmp = ::Object.const_get(map[e]).encoder(tmp) }
150
+ collector(tmp)
151
+ end
152
+
153
+ describe "stream_decoder" do
154
+ it "works with a string stream" do
155
+ @stm.stream = 'testing'
156
+ result = collector(@stm.stream_decoder)
157
+ assert_equal('testing', result)
158
+ assert_equal(Encoding::BINARY, result.encoding)
159
+ end
160
+
161
+ it "works with an IO object inside StreamData" do
162
+ io = StringIO.new(encoded_data('testing', [:A85, :AHx]))
163
+ @stm.stream = HexaPDF::StreamData.new(io, filter: [:AHx, :A85])
164
+ assert_equal('testing', collector(@stm.stream_decoder))
165
+ end
166
+
167
+ it "works with a Proc object inside StreamData" do
168
+ @stm.stream = HexaPDF::StreamData.new(proc { 'testing' })
169
+ assert_equal('testing', collector(@stm.stream_decoder))
170
+ end
171
+
172
+ it "fails if an unknown filter name is used" do
173
+ @stm.stream = HexaPDF::StreamData.new(feeder('testing'), filter: [:Unknown])
174
+ assert_raises(HexaPDF::Error) { @stm.stream_decoder }
175
+ end
176
+ end
177
+
178
+ describe "stream_encoder" do
179
+ it "uses the :Filter and :DecodeParms entries of the value attribute correctly" do
180
+ @stm.value[:Filter] = nil
181
+ @stm.stream = 'test'
182
+ assert_equal('test', collector(@stm.stream_encoder))
183
+
184
+ @stm.value[:Filter] = :AHx
185
+ @stm.stream = 'test'
186
+ assert_equal('74657374>', collector(@stm.stream_encoder))
187
+
188
+ @stm.value[:Filter] = [:AHx, :Fl]
189
+ @stm.value[:DecodeParms] = nil
190
+ @stm.stream = 'abcdefg'
191
+ assert_equal("78da4b4c4a4e494d4b07000adb02bd>", collector(@stm.stream_encoder))
192
+
193
+ @stm.value[:Filter] = [:AHx, :Fl]
194
+ @stm.value[:DecodeParms] = [nil, {Predictor: 12}]
195
+ @stm.stream = 'abcdefg'
196
+ assert_equal("78da634a6462444000058f0076>", collector(@stm.stream_encoder))
197
+
198
+ @stm.value[:Filter] = [:AHx, nil, :Fl]
199
+ @stm.value[:DecodeParms] = [nil, nil, {Predictor: 10}]
200
+ @stm.stream = 'abcdefg'
201
+ assert_equal("78da6348644862486648614865486348070012fa02bd>", collector(@stm.stream_encoder))
202
+ end
203
+
204
+ it "decodes a StreamData stream before encoding" do
205
+ @stm.value[:Filter] = :AHx
206
+ data_proc = proc { encoded_data('test', [:A85, :AHx]) }
207
+ @stm.stream = HexaPDF::StreamData.new(data_proc, filter: [:AHx, :A85])
208
+ assert_equal('74657374>', collector(@stm.stream_encoder))
209
+ end
210
+
211
+ it "decodes only what is necessary of a StreamData stream on encoding" do
212
+ @stm.value[:Filter] = :AHx
213
+ data_proc = proc { encoded_data('test', [:AHx, :A85]) }
214
+ @stm.stream = HexaPDF::StreamData.new(data_proc, filter: [:A85, :AHx])
215
+ assert_equal('74657374>', collector(@stm.stream_encoder))
216
+
217
+ @stm.value[:Filter] = [:Unknown]
218
+ @stm.stream = HexaPDF::StreamData.new(proc { 'test' }, filter: [:Unknown])
219
+ assert_equal('test', collector(@stm.stream_encoder))
220
+ end
221
+ end
222
+
223
+ describe "set_filter" do
224
+ it "sets correct filter values without decode parameters" do
225
+ @stm.set_filter(:Test)
226
+ assert_equal(:Test, @stm.value[:Filter])
227
+ refute(@stm.value.key?(:DecodeParms))
228
+
229
+ @stm.set_filter([:Test, :Test1])
230
+ assert_equal([:Test, :Test1], @stm.value[:Filter])
231
+ refute(@stm.value.key?(:DecodeParms))
232
+ end
233
+
234
+ it "sets correct filter/decode parameter values" do
235
+ @stm.set_filter([:Test, :Test], :Other)
236
+ assert_equal([:Test, :Test], @stm.value[:Filter])
237
+ assert_equal(:Other, @stm.value[:DecodeParms])
238
+
239
+ @stm.set_filter([:Test, :Test], [:Other, :Other])
240
+ assert_equal([:Test, :Test], @stm.value[:Filter])
241
+ assert_equal([:Other, :Other], @stm.value[:DecodeParms])
242
+ end
243
+
244
+ it "deletes the /Filter and/or decode parameters values when arguments are nil" do
245
+ @stm.set_filter(:Test, :Other)
246
+ @stm.set_filter(nil, :Other)
247
+ refute(@stm.value.key?(:Filter))
248
+ refute(@stm.value.key?(:DecodeParms))
249
+
250
+ @stm.set_filter(:Test, :Other)
251
+ @stm.set_filter(:Test, nil)
252
+ assert_equal(:Test, @stm[:Filter])
253
+ refute(@stm.value.key?(:DecodeParms))
254
+ end
255
+ end
256
+
257
+ describe "validation" do
258
+ it "validates the /Filter entry" do
259
+ assert(@stm.validate)
260
+
261
+ @stm.set_filter(:FlateDecode)
262
+ assert(@stm.validate(auto_correct: false))
263
+ assert_equal(:FlateDecode, @stm[:Filter])
264
+
265
+ @stm.set_filter(:AHx)
266
+ assert(@stm.validate(auto_correct: true))
267
+ assert_equal(:ASCIIHexDecode, @stm[:Filter])
268
+
269
+ @stm.set_filter([:FlateDecode, :AHx])
270
+ assert(@stm.validate(auto_correct: true))
271
+ assert_equal([:FlateDecode, :ASCIIHexDecode], @stm[:Filter])
272
+ end
273
+ end
274
+ end
@@ -0,0 +1,80 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/tokenizer'
5
+ require 'stringio'
6
+ require_relative 'common_tokenizer_tests'
7
+
8
+ describe HexaPDF::Tokenizer do
9
+ include CommonTokenizerTests
10
+
11
+ def create_tokenizer(str)
12
+ @tokenizer = HexaPDF::Tokenizer.new(StringIO.new(str.b))
13
+ end
14
+
15
+ it "handles object references" do
16
+ create_tokenizer("1 0 R 2 15 R ")
17
+ assert_equal(HexaPDF::Reference.new(1, 0), @tokenizer.next_token)
18
+ assert_equal(HexaPDF::Reference.new(2, 15), @tokenizer.next_token)
19
+ @tokenizer.pos = 0
20
+ assert_equal(HexaPDF::Reference.new(1, 0), @tokenizer.next_object)
21
+ assert_equal(HexaPDF::Reference.new(2, 15), @tokenizer.next_object)
22
+ end
23
+
24
+ it "next_token: should not fail when resetting the position (due to use of an internal buffer)" do
25
+ create_tokenizer("0 1 2 3 4 " * 4000)
26
+ 4000.times do
27
+ 5.times {|i| assert_equal(i, @tokenizer.next_token) }
28
+ end
29
+ end
30
+
31
+ it "has a special token scanning method for use with xref reconstruction" do
32
+ create_tokenizer(<<-EOF.chomp.gsub(/^ {8}/, ''))
33
+ % Comment
34
+ true
35
+ 123 50
36
+ obj
37
+ (ignored)
38
+ /Ignored
39
+ [/Ignored]
40
+ <</Ignored /Values>>
41
+ EOF
42
+
43
+ scan_to_newline = proc { @tokenizer.scan_until(/(\n|\r\n?)+|\z/) }
44
+
45
+ assert_nil(@tokenizer.next_integer_or_keyword)
46
+ scan_to_newline.call
47
+ assert_equal(true, @tokenizer.next_integer_or_keyword)
48
+ assert_equal(123, @tokenizer.next_integer_or_keyword)
49
+ assert_equal(50, @tokenizer.next_integer_or_keyword)
50
+ assert_equal('obj', @tokenizer.next_integer_or_keyword)
51
+ 4.times do
52
+ assert_nil(@tokenizer.next_integer_or_keyword)
53
+ scan_to_newline.call
54
+ end
55
+ assert_equal(HexaPDF::Tokenizer::NO_MORE_TOKENS, @tokenizer.next_integer_or_keyword)
56
+ end
57
+
58
+ describe "can correct some problems" do
59
+ describe "invalid inf/-inf/nan/-nan tokens" do
60
+ it "turns them into zeros" do
61
+ count = 0
62
+ on_correctable_error = lambda do |msg, pos|
63
+ count += 1
64
+ assert_match(/Invalid object, got token/, msg)
65
+ assert_equal(8, pos) if count == 2
66
+ false
67
+ end
68
+ tokenizer = HexaPDF::Tokenizer.new(StringIO.new("inf -Inf NaN -nan"),
69
+ on_correctable_error: on_correctable_error)
70
+ assert_equal([0, 0, 0, 0], 4.times.map { tokenizer.next_object })
71
+ assert_equal(4, count)
72
+ end
73
+
74
+ it "raises an error if configured so" do
75
+ tokenizer = HexaPDF::Tokenizer.new(StringIO.new("inf"), on_correctable_error: proc { true })
76
+ assert_raises(HexaPDF::MalformedPDFError) { tokenizer.next_object }
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,18 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/type'
5
+
6
+ describe HexaPDF::Type do
7
+ it "all autoload files have no syntax error" do
8
+ HexaPDF::Type.constants.each do |const|
9
+ HexaPDF::Type.const_get(const) # no assert needed here
10
+ end
11
+ HexaPDF::Type::Actions.constants.each do |const|
12
+ HexaPDF::Type::Actions.const_get(const) # no assert needed here
13
+ end
14
+ HexaPDF::Type::Annotations.constants.each do |const|
15
+ HexaPDF::Type::Annotations.const_get(const) # no assert needed here
16
+ end
17
+ end
18
+ end