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,163 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/content/processor'
5
+ require 'hexapdf/document'
6
+
7
+ describe HexaPDF::Content::Processor::GlyphBox do
8
+ before do
9
+ @box = HexaPDF::Content::Processor::GlyphBox.new(5, "5", 1, 2, 3, 4, 5, 6)
10
+ end
11
+
12
+ it "returns the correct bounding box coordinates" do
13
+ assert_equal([1, 2], @box.lower_left)
14
+ assert_equal([3, 4], @box.lower_right)
15
+ assert_equal([5, 6], @box.upper_left)
16
+ assert_equal([7, 8], @box.upper_right)
17
+ end
18
+
19
+ it "returns all box corners in one array when using #points" do
20
+ assert_equal([1, 2, 3, 4, 7, 8, 5, 6], @box.points)
21
+ end
22
+ end
23
+
24
+ describe HexaPDF::Content::Processor::CompositeBox do
25
+ before do
26
+ @glyph_box1 = HexaPDF::Content::Processor::GlyphBox.new(4, "4", 1, 1, 2, 1, 1, 2)
27
+ @glyph_box2 = HexaPDF::Content::Processor::GlyphBox.new(2, "2", 3, 1, 7, 1, 3, 2)
28
+ @boxes = HexaPDF::Content::Processor::CompositeBox.new
29
+ end
30
+
31
+ it "allows appending and retrieving of glyph boxes" do
32
+ result = (@boxes << @glyph_box1)
33
+ assert_equal(@boxes, result)
34
+ assert_equal(@glyph_box1, @boxes[0])
35
+ end
36
+
37
+ it "allows iterating over the glyph boxes " do
38
+ @boxes << @glyph_box1 << @glyph_box2
39
+ assert_equal([@glyph_box1, @glyph_box2], @boxes.each.to_a)
40
+ end
41
+
42
+ it "returns the concatenated string of the individual boxes" do
43
+ @boxes << @glyph_box1 << @glyph_box2
44
+ assert_equal("42", @boxes.string)
45
+ end
46
+
47
+ it "returns the correct bounding box coordinates" do
48
+ @boxes << @glyph_box1 << @glyph_box2
49
+ assert_equal([1, 1], @boxes.lower_left)
50
+ assert_equal([7, 1], @boxes.lower_right)
51
+ assert_equal([1, 2], @boxes.upper_left)
52
+ assert_equal([7, 2], @boxes.upper_right)
53
+ end
54
+ end
55
+
56
+ describe HexaPDF::Content::Processor do
57
+ before do
58
+ @processor = HexaPDF::Content::Processor.new
59
+ end
60
+
61
+ describe "initialization" do
62
+ it "has a prepopulated operators mapping" do
63
+ assert_kind_of(HexaPDF::Content::Operator::BaseOperator, @processor.operators[:q])
64
+ end
65
+ end
66
+
67
+ describe "graphics_object" do
68
+ it "default to :none on initialization" do
69
+ assert_equal(:none, @processor.graphics_object)
70
+ end
71
+ end
72
+
73
+ describe "process" do
74
+ it "invokes the specified operator implementation" do
75
+ op = Minitest::Mock.new
76
+ op.expect(:invoke, nil, [@processor, :arg])
77
+ @processor.operators[:test] = op
78
+ @processor.process(:test, [:arg])
79
+ assert(op.verify)
80
+ end
81
+
82
+ it "invokes the mapped message name" do
83
+ val = nil
84
+ @processor.define_singleton_method(:save_graphics_state) { val = :arg }
85
+ @processor.process(:q)
86
+ assert_equal(:arg, val)
87
+ end
88
+ end
89
+
90
+ describe "paint_xobject" do
91
+ it "processes the contents of a Form xobject" do
92
+ test_case = self
93
+ invoked = false
94
+
95
+ @processor.define_singleton_method(:set_line_width) do |_width|
96
+ test_case.assert_equal([2, 0, 0, 2, 10, 10], graphics_state.ctm.to_a)
97
+ invoked = true
98
+ end
99
+ @processor.resources = resources = Object.new
100
+ @processor.resources.define_singleton_method(:xobject) do |_name|
101
+ obj = {Matrix: [2, 0, 0, 2, 10, 10], Subtype: :Form}
102
+ obj.define_singleton_method(:process_contents) do |processor, original_resources:|
103
+ test_case.assert_same(resources, original_resources)
104
+ processor.process(:w, [10])
105
+ end
106
+ obj
107
+ end
108
+
109
+ @processor.process(:Do, [:Name])
110
+ assert(invoked)
111
+ assert_equal([1, 0, 0, 1, 0, 0], @processor.graphics_state.ctm.to_a)
112
+ assert_same(resources, @processor.resources)
113
+ end
114
+ end
115
+
116
+ describe "text decoding" do
117
+ before do
118
+ @doc = HexaPDF::Document.new
119
+ @processor.process(:BT)
120
+ @processor.graphics_state.font = @font = @doc.add({Type: :Font, Subtype: :Type1,
121
+ Encoding: :WinAnsiEncoding,
122
+ BaseFont: :"Times-Roman"})
123
+ @processor.graphics_state.font_size = 10
124
+ @processor.graphics_state.text_rise = 10
125
+ @processor.graphics_state.character_spacing = 1
126
+ @processor.graphics_state.word_spacing = 2
127
+ end
128
+
129
+ describe "decode_text" do
130
+ it "decodes text provided via one or the other of the two show show text operators" do
131
+ assert_equal("Hülle", @processor.send(:decode_text, "Hülle".encode("Windows-1252")))
132
+ arr = ["Hül".encode("Windows-1252"), 20, "le".encode("Windows-1252")]
133
+ assert_equal("Hülle", @processor.send(:decode_text, arr))
134
+ end
135
+ end
136
+
137
+ describe "decode_text_with_positioning" do
138
+ it "returns a composite box with positioning information" do
139
+ lly = @font.bounding_box[1] / 1000.0 * @processor.graphics_state.font_size +
140
+ @processor.graphics_state.text_rise
141
+ lry = @font.bounding_box[3] / 1000.0 * @processor.graphics_state.font_size +
142
+ @processor.graphics_state.text_rise
143
+ arr = ["Hül".encode("Windows-1252"), 20, " le".encode("Windows-1252")]
144
+ width = "Hül le".encode("Windows-1252").codepoints.inject(0) {|s, cp| s + @font.width(cp) }
145
+ width = (width - 20) * @processor.graphics_state.scaled_font_size +
146
+ 6 * @processor.graphics_state.scaled_character_spacing +
147
+ @processor.graphics_state.scaled_word_spacing
148
+
149
+ box = @processor.send(:decode_text_with_positioning, arr)
150
+ assert_equal("Hül le", box.string)
151
+ assert_in_delta(0, box[0].lower_left[0])
152
+ assert_in_delta(lly, box[0].lower_left[1])
153
+ assert_in_delta(width, box[5].upper_right[0])
154
+ assert_in_delta(lry, box[5].upper_right[1])
155
+ end
156
+
157
+ it "fails if the current font is a vertical font" do
158
+ @processor.graphics_state.font.define_singleton_method(:writing_mode) { :vertical }
159
+ assert_raises(NotImplementedError) { @processor.send(:decode_text_with_positioning, "a") }
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,64 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/content/transformation_matrix'
5
+
6
+ describe HexaPDF::Content::TransformationMatrix do
7
+ before do
8
+ @matrix = HexaPDF::Content::TransformationMatrix.new
9
+ end
10
+
11
+ describe "initialize" do
12
+ it "initializes to the identity matrix" do
13
+ assert_equal(1, @matrix.a)
14
+ assert_equal(0, @matrix.b)
15
+ assert_equal(0, @matrix.c)
16
+ assert_equal(1, @matrix.d)
17
+ assert_equal(0, @matrix.e)
18
+ assert_equal(0, @matrix.f)
19
+ end
20
+
21
+ it "can use arbitrary values" do
22
+ @matrix = HexaPDF::Content::TransformationMatrix.new(2, 3, 4, 5, 6, 7)
23
+ assert_equal(2, @matrix.a)
24
+ assert_equal(3, @matrix.b)
25
+ assert_equal(4, @matrix.c)
26
+ assert_equal(5, @matrix.d)
27
+ assert_equal(6, @matrix.e)
28
+ assert_equal(7, @matrix.f)
29
+ end
30
+ end
31
+
32
+ it "correctly evaluates a point" do
33
+ assert_equal([2, 3], @matrix.evaluate(2, 3))
34
+ end
35
+
36
+ it "correctly translates the matrix" do
37
+ @matrix.translate(5, 10)
38
+ assert_equal([7, 13], @matrix.evaluate(2, 3))
39
+ end
40
+
41
+ it "correctly rotates the matrix" do
42
+ @matrix.rotate(90)
43
+ assert_equal([-3, 2], @matrix.evaluate(2, 3))
44
+ end
45
+
46
+ it "correctly scales the matrix" do
47
+ @matrix.scale(5, 10)
48
+ assert_equal([10, 30], @matrix.evaluate(2, 3))
49
+ end
50
+
51
+ it "correctly skews the matrix" do
52
+ assert_equal([2, 5], @matrix.dup.skew(45, 0).evaluate(2, 3))
53
+ assert_equal([5, 3], @matrix.dup.skew(0, 45).evaluate(2, 3))
54
+ end
55
+
56
+ it "can be compared to another matrix" do
57
+ assert_equal(HexaPDF::Content::TransformationMatrix.new, @matrix)
58
+ refute_equal(HexaPDF::Content::TransformationMatrix.new(5), @matrix)
59
+ end
60
+
61
+ it "allows the conversion of the matrix into an array" do
62
+ assert_equal([1, 0, 0, 1, 0, 0], @matrix.to_a)
63
+ end
64
+ end
@@ -0,0 +1,72 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'stringio'
5
+ require 'tempfile'
6
+ require 'hexapdf/document'
7
+
8
+ describe HexaPDF::Document::Files do
9
+ before do
10
+ @doc = HexaPDF::Document.new
11
+ @data = "embed-test"
12
+ @file = Tempfile.new('file-embed-test')
13
+ @file.write(@data)
14
+ @file.close
15
+ end
16
+
17
+ after do
18
+ @file.unlink
19
+ end
20
+
21
+ describe "add" do
22
+ it "adds a file using a filename and embeds it" do
23
+ spec = @doc.files.add(@file.path)
24
+ assert_equal(File.basename(@file.path), spec.path)
25
+ assert_equal(@data, spec.embedded_file_stream.stream)
26
+ end
27
+
28
+ it "adds a reference to a file" do
29
+ spec = @doc.files.add(@file.path, embed: false)
30
+ assert_equal(File.basename(@file.path), spec.path)
31
+ refute(spec.embedded_file?)
32
+ end
33
+
34
+ it "adds a file using an IO" do
35
+ @file.open
36
+ spec = @doc.files.add(@file, name: 'test', embed: false)
37
+ assert_equal('test', spec.path)
38
+ assert_equal(@data, spec.embedded_file_stream.stream)
39
+ end
40
+
41
+ it "optionally sets the description of the file" do
42
+ spec = @doc.files.add(@file.path, description: 'Some file')
43
+ assert_equal('Some file', spec[:Desc])
44
+ end
45
+
46
+ it "requires the name argument when given an IO object" do
47
+ assert_raises(ArgumentError) { @doc.files.add(StringIO.new) }
48
+ end
49
+ end
50
+
51
+ describe "each" do
52
+ it "iterates only over named embedded files and file annotations if search=false" do
53
+ @doc.add({Type: :Filespec})
54
+ spec1 = @doc.files.add(__FILE__)
55
+ spec2 = @doc.add({Type: :Filespec})
56
+ @doc.pages.add # page without annot
57
+ @doc.pages.add[:Annots] = [
58
+ {Subtype: :FileAttachment, FS: HexaPDF::Reference.new(spec1.oid, spec1.gen)},
59
+ {Subtype: :FileAttachment, FS: spec2},
60
+ {},
61
+ ]
62
+ assert_equal([spec1, spec2], @doc.files.to_a)
63
+ end
64
+
65
+ it "iterates over all file specifications of the document if search=true" do
66
+ specs = []
67
+ specs << @doc.add({Type: :Filespec})
68
+ specs << @doc.add({Type: :Filespec})
69
+ assert_equal(specs, @doc.files.each(search: true).to_a)
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,60 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/document'
5
+
6
+ describe HexaPDF::Document::Fonts do
7
+ before do
8
+ @doc = HexaPDF::Document.new
9
+ @doc.config['font_loader'] = []
10
+ end
11
+
12
+ describe "add" do
13
+ before do
14
+ @doc.config['font_loader'] << lambda do |doc, name, **options|
15
+ assert_equal(@doc, doc)
16
+ if name == :TestFont
17
+ x = Object.new
18
+ x.define_singleton_method(:name) do
19
+ options[:variant] == :bold ? :BoldFont : :NormalFont
20
+ end
21
+ x
22
+ else
23
+ nil
24
+ end
25
+ end
26
+ end
27
+
28
+ it "loads the specified font" do
29
+ assert_equal(:NormalFont, @doc.fonts.add(:TestFont).name)
30
+ assert_equal(:BoldFont, @doc.fonts.add(:TestFont, variant: :bold).name)
31
+ end
32
+
33
+ it "caches loaded fonts" do
34
+ font = @doc.fonts.add(:TestFont)
35
+ assert_same(font, @doc.fonts.add(:TestFont))
36
+ assert_same(font, @doc.fonts.add(:TestFont, variant: :none))
37
+ end
38
+
39
+ it "fails if the requested font is not found" do
40
+ @doc.config['font_loader'] << 'HexaPDF::FontLoader::Standard14'
41
+ error = assert_raises(HexaPDF::Error) { @doc.fonts.add("Unknown") }
42
+ assert_match(/Times \(none/, error.message)
43
+ end
44
+
45
+ it "raises an error if a font loader cannot be correctly retrieved" do
46
+ @doc.config['font_loader'][0] = 'UnknownFontLoader'
47
+ assert_raises(HexaPDF::Error) { @doc.fonts.add(:Other) }
48
+ end
49
+ end
50
+
51
+ it "returns the configured fonts" do
52
+ @doc.config['font_loader'] << 'HexaPDF::FontLoader::Standard14'
53
+ @doc.config['font_loader'] << 'HexaPDF::FontLoader::FromConfiguration'
54
+ @doc.config['font.map'] = {'Times' => {heavy: 'none', none: 'none'}, 'Other' => {none: 'none'}}
55
+ fonts = @doc.fonts.configured_fonts
56
+ assert_equal([:none], fonts['Symbol'])
57
+ assert_equal([:none, :bold, :italic, :bold_italic, :heavy], fonts['Times'])
58
+ assert_equal([:none], fonts['Other'])
59
+ end
60
+ end
@@ -0,0 +1,72 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'stringio'
5
+ require 'hexapdf/document'
6
+
7
+ describe HexaPDF::Document::Images do
8
+ before do
9
+ @doc = HexaPDF::Document.new
10
+ end
11
+
12
+ describe "add" do
13
+ describe "using a custom image loader" do
14
+ before do
15
+ @loader = Object.new
16
+ @loader.define_singleton_method(:handles?) {|*| true }
17
+ @loader.define_singleton_method(:load) do |doc, s|
18
+ s = HexaPDF::StreamData.new(s) if s.kind_of?(IO)
19
+ doc.add({Type: :XObject, Subtype: :Image}, stream: s)
20
+ end
21
+ @doc.config['image_loader'].unshift(@loader)
22
+ end
23
+
24
+ it "adds an image using a filename" do
25
+ data = 'test'
26
+ image = @doc.images.add(data)
27
+ assert_equal(data, image.stream)
28
+ assert_equal(File.absolute_path(data), image.source_path)
29
+ end
30
+
31
+ it "adds an image using an IO" do
32
+ File.open(__FILE__, 'rb') do |file|
33
+ image = @doc.images.add(file)
34
+ assert_equal(File.read(__FILE__), image.stream)
35
+ assert_equal(File.absolute_path(__FILE__), image.source_path)
36
+ end
37
+ end
38
+
39
+ it "doesn't add an image twice" do
40
+ data = 'test'
41
+ image = @doc.images.add(data)
42
+ image1 = @doc.images.add(data)
43
+ assert_same(image, image1)
44
+ end
45
+ end
46
+
47
+ it "fails if the needed image loader can't be resolved" do
48
+ @doc.config['image_loader'].unshift('SomeUnknownConstantHere')
49
+ exp = assert_raises(HexaPDF::Error) { @doc.images.add(StringIO.new('test')) }
50
+ assert_match(/image loader from configuration/, exp.message)
51
+ end
52
+
53
+ it "fails if no image loader is found" do
54
+ exp = assert_raises(HexaPDF::Error) { @doc.images.add(StringIO.new('test')) }
55
+ assert_match(/suitable image loader/, exp.message)
56
+ end
57
+ end
58
+
59
+ describe "each" do
60
+ it "iterates over all non-mask images" do
61
+ @doc.add(5)
62
+ images = []
63
+ images << @doc.add({Type: :XObject, Subtype: :Image})
64
+ images << @doc.add({Type: :XObject, Subtype: :Image, Mask: [5, 6]})
65
+ images << @doc.add({Type: :XObject, Subtype: :Image,
66
+ Mask: @doc.add({Type: :XObject, Subtype: :Image})})
67
+ images << @doc.add({Type: :XObject, Subtype: :Image,
68
+ SMask: @doc.add({Type: :XObject, Subtype: :Image})})
69
+ assert_equal(images.sort, @doc.images.to_a.sort)
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,130 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/document'
5
+
6
+ describe HexaPDF::Document::Pages do
7
+ before do
8
+ @doc = HexaPDF::Document.new
9
+ end
10
+
11
+ describe "root" do
12
+ it "returns the root of the page tree" do
13
+ assert_same(@doc.catalog.pages, @doc.pages.root)
14
+ end
15
+ end
16
+
17
+ describe "add" do
18
+ it "adds a new empty page when no page is given" do
19
+ page = @doc.pages.add
20
+ assert_equal([page], @doc.pages.root[:Kids].value)
21
+ end
22
+
23
+ it "adds a new empty page with the given dimensions" do
24
+ page = @doc.pages.add([0, 0, 20, 20])
25
+ assert_same(page, @doc.pages[0])
26
+ assert_equal([0, 0, 20, 20], @doc.pages[0].box(:media).value)
27
+ end
28
+
29
+ it "adds a new empty page with the given page format" do
30
+ page = @doc.pages.add(:A4, orientation: :landscape)
31
+ assert_same(page, @doc.pages[0])
32
+ assert_equal([0, 0, 842, 595], @doc.pages[0].box(:media).value)
33
+ end
34
+
35
+ it "adds the given page to the end" do
36
+ page = @doc.pages.add
37
+ new_page = @doc.add({Type: :Page})
38
+ assert_same(new_page, @doc.pages.add(new_page))
39
+ assert_equal([page, new_page], @doc.pages.root[:Kids].value)
40
+ end
41
+
42
+ it "fails if an unknown page format is given" do
43
+ assert_raises(HexaPDF::Error) { @doc.pages.add(:A953) }
44
+ end
45
+ end
46
+
47
+ describe "<<" do
48
+ it "works like add but always needs a page returns self" do
49
+ page1 = @doc.add({Type: :Page})
50
+ page2 = @doc.add({Type: :Page})
51
+ @doc.pages << page1 << page2
52
+ assert_equal([page1, page2], @doc.pages.root[:Kids].value)
53
+ end
54
+ end
55
+
56
+ describe "insert" do
57
+ before do
58
+ @doc.pages.add
59
+ @doc.pages.add
60
+ @doc.pages.add
61
+ end
62
+
63
+ it "insert a new page at a given index" do
64
+ page = @doc.pages.insert(2)
65
+ assert_equal(page, @doc.pages.root[:Kids][2])
66
+ end
67
+
68
+ it "insert a given page at a given index" do
69
+ new_page = @doc.add({Type: :Page})
70
+ assert_same(new_page, @doc.pages.insert(2, new_page))
71
+ assert_equal(new_page, @doc.pages.root[:Kids][2])
72
+ end
73
+ end
74
+
75
+ describe "delete" do
76
+ it "deletes a given page" do
77
+ page1 = @doc.pages.add
78
+ page2 = @doc.pages.add
79
+
80
+ @doc.pages.delete(page1)
81
+ assert_equal([page2], @doc.pages.root[:Kids].value)
82
+ end
83
+ end
84
+
85
+ describe "delete_at" do
86
+ it "deletes a page at a given index" do
87
+ page1 = @doc.pages.add
88
+ @doc.pages.add
89
+ page3 = @doc.pages.add
90
+ @doc.pages.delete_at(1)
91
+ assert_equal([page1, page3], @doc.pages.root[:Kids].value)
92
+ end
93
+ end
94
+
95
+ describe "[]" do
96
+ it "returns the page at the given index" do
97
+ page1 = @doc.pages.add
98
+ page2 = @doc.pages.add
99
+ page3 = @doc.pages.add
100
+
101
+ assert_equal(page1, @doc.pages[0])
102
+ assert_equal(page2, @doc.pages[1])
103
+ assert_equal(page3, @doc.pages[2])
104
+ assert_nil(@doc.pages[3])
105
+ assert_equal(page3, @doc.pages[-1])
106
+ assert_equal(page2, @doc.pages[-2])
107
+ assert_equal(page1, @doc.pages[-3])
108
+ assert_nil(@doc.pages[-4])
109
+ end
110
+ end
111
+
112
+ describe "each" do
113
+ it "iterates over all pages" do
114
+ page1 = @doc.pages.add
115
+ page2 = @doc.pages.add
116
+ page3 = @doc.pages.add
117
+ assert_equal([page1, page2, page3], @doc.pages.to_a)
118
+ end
119
+ end
120
+
121
+ describe "count" do
122
+ it "returns the number of pages in the page tree" do
123
+ assert_equal(0, @doc.pages.count)
124
+ @doc.pages.add
125
+ @doc.pages.add
126
+ @doc.pages.add
127
+ assert_equal(3, @doc.pages.count)
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,87 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+
5
+ # Contains tests that validate that an encryption algorithm's class conforms to the general
6
+ # interface.
7
+ #
8
+ # The algorithm class needs to be available in the @algorithm_class variable.
9
+ module EncryptionAlgorithmInterfaceTests
10
+
11
+ def test_responds_to_necessary_methods
12
+ [:encrypt, :decrypt, :encryption_fiber, :decryption_fiber].each do |method|
13
+ assert_respond_to(@algorithm_class, method)
14
+ assert_equal(2, @algorithm_class.method(method).arity)
15
+ end
16
+ end
17
+
18
+ end
19
+
20
+ # Contains common tests for AES algorithms.
21
+ #
22
+ # The algorithm class needs to be available in the @algorithm_class variable.
23
+ module AESEncryptionTests
24
+
25
+ include EncryptionAlgorithmInterfaceTests
26
+
27
+ TEST_VECTOR_FILES = Dir[File.join(TEST_DATA_DIR, 'aes-test-vectors', '*')]
28
+
29
+ def test_processes_the_aes_test_vectors
30
+ TEST_VECTOR_FILES.each do |filename|
31
+ name, size, mode = File.basename(filename, '.data.gz').split('-')
32
+ size = size.to_i / 8
33
+ data = Zlib::GzipReader.open(filename, &:read).force_encoding(Encoding::BINARY)
34
+ data.scan(/(.{#{size}})(.{16})(.{16})(.{16})/m).each_with_index do |(key, iv, plain, cipher), index|
35
+ aes = @algorithm_class.new(key, iv, mode.intern)
36
+ assert_equal(cipher, aes.process(plain),
37
+ "name: #{name}, size: #{size * 8}, mode: #{mode}, index: #{index}")
38
+ end
39
+ end
40
+ end
41
+
42
+ def test_can_accept_one_big_chunk_or_multiple_smaller_ones
43
+ big = @algorithm_class.new('t' * 16, '0' * 16, :encrypt)
44
+ small = @algorithm_class.new('t' * 16, '0' * 16, :encrypt)
45
+ assert_equal(big.process('some' * 16),
46
+ small.process('some' * 8) << small.process('some' * 4) << small.process('some' * 4))
47
+ end
48
+
49
+ def test_raises_error_on_invalid_key_length
50
+ assert_raises(HexaPDF::EncryptionError) { @algorithm_class.new('t' * 7, '0' * 16, :encrypt) }
51
+ end
52
+
53
+ def test_raises_error_on_invalid_iv_length
54
+ assert_raises(HexaPDF::EncryptionError) { @algorithm_class.new('t' * 16, '0' * 7, :encrypt) }
55
+ end
56
+
57
+ end
58
+
59
+ # Contains common tests for AES algorithms.
60
+ #
61
+ # The algorithm class needs to be available in the @algorithm_class variable.
62
+ module ARC4EncryptionTests
63
+
64
+ include EncryptionAlgorithmInterfaceTests
65
+
66
+ def setup
67
+ super
68
+ @encrypted = ['BBF316E8D940AF0AD3', '1021BF0420', '45A01F645FC35B383552544B9BF5'].
69
+ map {|c| [c].pack('H*') }
70
+ @plain = ['Plaintext', 'pedia', 'Attack at dawn'].each {|s| s.force_encoding('BINARY') }
71
+ @keys = ['Key', 'Wiki', 'Secret']
72
+ end
73
+
74
+ def test_processes_the_test_vectors_from_the_rc4_wikipeda_page
75
+ @keys.each_with_index do |key, i|
76
+ assert_equal(@encrypted[i], @algorithm_class.new(key).process(@plain[i]))
77
+ end
78
+ end
79
+
80
+ def test_can_accept_one_big_chunk_or_multiple_smaller_ones
81
+ big = @algorithm_class.new('key')
82
+ small = @algorithm_class.new('key')
83
+ assert_equal(big.process('some big data chunk'),
84
+ small.process('some') << small.process(' big') << small.process(' data chunk'))
85
+ end
86
+
87
+ end