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,97 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/document'
5
+ require 'hexapdf/type/annotation'
6
+
7
+ describe HexaPDF::Type::Annotation::AppearanceDictionary do
8
+ before do
9
+ @doc = HexaPDF::Document.new
10
+ @ap = @doc.add({N: :n, D: :d, R: :r}, type: :XXAppearanceDictionary)
11
+ end
12
+
13
+ it "resolves the normal appearance" do
14
+ assert_equal(:n, @ap.normal_appearance)
15
+ end
16
+
17
+ it "resolves the rollover appearance" do
18
+ assert_equal(:r, @ap.rollover_appearance)
19
+ @ap.delete(:R)
20
+ assert_equal(:n, @ap.rollover_appearance)
21
+ end
22
+
23
+ it "resolves the down appearance" do
24
+ assert_equal(:d, @ap.down_appearance)
25
+ @ap.delete(:D)
26
+ assert_equal(:n, @ap.down_appearance)
27
+ end
28
+ end
29
+
30
+ describe HexaPDF::Type::Annotation do
31
+ before do
32
+ @doc = HexaPDF::Document.new
33
+ @annot = @doc.add({Type: :Annot, F: 0b100011})
34
+ end
35
+
36
+ it "must always be indirect" do
37
+ @annot.must_be_indirect = false
38
+ assert(@annot.must_be_indirect?)
39
+ end
40
+
41
+ it "returns the appearance dictionary" do
42
+ @annot[:AP] = :yes
43
+ assert_equal(:yes, @annot.appearance_dict)
44
+ end
45
+
46
+ it "returns the appearance stream of the given type" do
47
+ assert_nil(@annot.appearance)
48
+
49
+ @annot[:AP] = {N: {}}
50
+ assert_nil(@annot.appearance)
51
+
52
+ stream = @doc.wrap({}, stream: '')
53
+ @annot[:AP][:N] = stream
54
+ assert_nil(@annot.appearance)
55
+
56
+ stream[:BBox] = [1, 2, 3, 4]
57
+ appearance = @annot.appearance
58
+ assert_same(stream.data, appearance.data)
59
+ assert_equal(:Form, appearance[:Subtype])
60
+
61
+ @annot[:AP][:N] = {X: {}}
62
+ assert_nil(@annot.appearance)
63
+
64
+ @annot[:AS] = :X
65
+ @annot[:AP][:N][:X] = stream
66
+ assert_same(stream.data, @annot.appearance.data)
67
+
68
+ @annot[:AP][:D] = {X: stream}
69
+ assert_same(stream.data, @annot.appearance(:down).data)
70
+ end
71
+
72
+ describe "flags" do
73
+ it "returns all flags" do
74
+ assert_equal([:invisible, :hidden, :no_view], @annot.flags)
75
+ end
76
+ end
77
+
78
+ describe "flagged?" do
79
+ it "returns true if the given flag is set" do
80
+ assert(@annot.flagged?(:hidden))
81
+ refute(@annot.flagged?(:locked))
82
+ end
83
+
84
+ it "raises an error if an unknown flag name is provided" do
85
+ assert_raises(ArgumentError) { @annot.flagged?(:unknown) }
86
+ end
87
+ end
88
+
89
+ describe "flag" do
90
+ it "sets the given flag bits" do
91
+ @annot.flag(:locked)
92
+ assert_equal([:invisible, :hidden, :no_view, :locked], @annot.flags)
93
+ @annot.flag(:locked, clear_existing: true)
94
+ assert_equal([:locked], @annot.flags)
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,48 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/document'
5
+ require 'hexapdf/type/catalog'
6
+
7
+ describe HexaPDF::Type::Catalog do
8
+ before do
9
+ @doc = HexaPDF::Document.new
10
+ @catalog = @doc.add({Type: :Catalog})
11
+ end
12
+
13
+ it "must always be indirect" do
14
+ @catalog.must_be_indirect = false
15
+ assert(@catalog.must_be_indirect?)
16
+ end
17
+
18
+ it "creates the page tree on access" do
19
+ assert_nil(@catalog[:Pages])
20
+ pages = @catalog.pages
21
+ assert_equal(:Pages, pages.type)
22
+ end
23
+
24
+ describe "acro_form" do
25
+ it "returns an existing form object" do
26
+ @catalog[:AcroForm] = :test
27
+ assert_equal(:test, @catalog.acro_form)
28
+ end
29
+
30
+ it "returns an existing form object even if create: true" do
31
+ @catalog[:AcroForm] = :test
32
+ assert_equal(:test, @catalog.acro_form(create: true))
33
+ end
34
+
35
+ it "creates a new AcroForm object with defaults if create: true" do
36
+ form = @catalog.acro_form(create: true)
37
+ assert_kind_of(HexaPDF::Type::AcroForm::Form, form)
38
+ assert(form[:DA])
39
+ end
40
+ end
41
+
42
+ describe "validation" do
43
+ it "creates the page tree if necessary" do
44
+ refute(@catalog.validate(auto_correct: false))
45
+ assert(@catalog.validate)
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,61 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/document'
5
+ require 'hexapdf/type/cid_font'
6
+
7
+ describe HexaPDF::Type::CIDFont do
8
+ before do
9
+ @doc = HexaPDF::Document.new
10
+ @font = @doc.wrap({Type: :Font, Subtype: :CIDFontType2, W: [1, 2, 3], DW: 100,
11
+ CIDSystemInfo: {Registry: 'Adobe', Ordering: 'Japan1', Supplement: 1}})
12
+ end
13
+
14
+ describe "width" do
15
+ before do
16
+ @font[:W] = [1, [1], 2, [2, 3, 4], 5, 6, 10, 20, [20, 21], 30, 32, 40]
17
+ end
18
+
19
+ it "returns the glyph width for a CID defined via the /W array" do
20
+ assert_equal(1, @font.width(1))
21
+ assert_equal(2, @font.width(2))
22
+ assert_equal(3, @font.width(3))
23
+ assert_equal(4, @font.width(4))
24
+ assert_equal(10, @font.width(5))
25
+ assert_equal(10, @font.width(6))
26
+ assert_equal(20, @font.width(20))
27
+ assert_equal(21, @font.width(21))
28
+ assert_equal(40, @font.width(32))
29
+ end
30
+
31
+ it "returns the /DW value for CIDs not in the /W array" do
32
+ assert_equal(100, @font.width(100))
33
+ @font.delete(:DW)
34
+ assert_equal(1000, @font.width(100))
35
+ end
36
+ end
37
+
38
+ describe "set_widths" do
39
+ it "allows setting the widths" do
40
+ @font.set_widths([[1, 1], [2, 2], [4, 4], [5, 5], [7, 7.1]], default_width: 5.1)
41
+ assert_equal(5, @font[:DW])
42
+ assert_equal([1, [1, 2], 4, [4, 5], 7, [7]], @font[:W].value)
43
+ end
44
+
45
+ it "handles an empty widths array correctly" do
46
+ @font.set_widths([], default_width: 100)
47
+ refute(@font.key?(:W))
48
+ assert_equal(100, @font[:DW])
49
+
50
+ @font.set_widths([])
51
+ refute(@font.key?(:W))
52
+ end
53
+
54
+ it "handles setting /DW to the default value correctly" do
55
+ @font.set_widths([])
56
+ refute(@font.key?(:DW))
57
+ @font.set_widths([[1, 1]])
58
+ refute(@font.key?(:DW))
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,141 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'tempfile'
5
+ require 'stringio'
6
+ require 'hexapdf/type/file_specification'
7
+ require 'hexapdf/document'
8
+
9
+ describe HexaPDF::Type::FileSpecification do
10
+ before do
11
+ @doc = HexaPDF::Document.new
12
+ @obj = HexaPDF::Type::FileSpecification.new({}, document: @doc)
13
+ end
14
+
15
+ it "can be asked whether it is a URL or a file" do
16
+ refute(@obj.url?)
17
+ @obj[:FS] = :URL
18
+ assert(@obj.url?)
19
+ end
20
+
21
+ describe "path" do
22
+ it "returns the first useable file spec string" do
23
+ @obj[:DOS] = 'hälo'.b
24
+ assert_equal('hälo'.b, @obj.path)
25
+
26
+ @obj[:UF] = 'hälo'
27
+ assert_equal('hälo', @obj.path)
28
+ end
29
+
30
+ it "unescapes the path string according to the PDF spec" do
31
+ @obj[:F] = "dir/in\\/out\\too"
32
+ assert_equal('dir/in/out/too', @obj.path)
33
+ end
34
+ end
35
+
36
+ describe "path=" do
37
+ it "only sets /UF and /F, deleting /Mac, /Unix, /DOS entries if they exist" do
38
+ @obj[:Unix] = @obj[:Mac] = @obj[:DOS] = 'a'
39
+ @obj.path = 'file/test'
40
+ assert_equal('file/test', @obj[:UF])
41
+ assert_equal('file/test', @obj[:F])
42
+ refute(@obj.key?(:Unix))
43
+ refute(@obj.key?(:Mac))
44
+ refute(@obj.key?(:DOS))
45
+ end
46
+
47
+ it 'resets the /FS value' do
48
+ @obj[:FS] = :Something
49
+ @obj.path = 'file'
50
+ refute(@obj.key?(:FS))
51
+ end
52
+ end
53
+
54
+ describe "url=" do
55
+ it "sets the path and the file system entry" do
56
+ url = 'http://example.com/some?test=ing#done'
57
+ @obj.url = url
58
+ assert_equal(url, @obj.path)
59
+ assert(@obj.url?)
60
+ end
61
+
62
+ it "fails if the provided string is not a valid URL" do
63
+ assert_raises(HexaPDF::Error) { @obj.url = "a false \\ URL" }
64
+ end
65
+ end
66
+
67
+ describe "embedded_file?" do
68
+ it "checks whether the specification has an embedded file" do
69
+ refute(@obj.embedded_file?)
70
+ @obj[:EF] = {UF: {}}
71
+ assert(@obj.embedded_file?)
72
+ end
73
+ end
74
+
75
+ describe "embedded_file_stream" do
76
+ it "returns the associated embedded file stream" do
77
+ assert_nil(@obj.embedded_file_stream)
78
+ @obj[:EF] = {F: 'data'}
79
+ assert_equal('data', @obj.embedded_file_stream)
80
+ end
81
+ end
82
+
83
+ describe "embed/unembed" do
84
+ before do
85
+ @file = Tempfile.new('file-embed-test')
86
+ @file.write("embed-test")
87
+ @file.close
88
+ end
89
+
90
+ after do
91
+ @file.unlink
92
+ end
93
+
94
+ it "requires the name argument when given an IO object" do
95
+ assert_raises(ArgumentError) { @obj.embed(StringIO.new) }
96
+ end
97
+
98
+ it "embeds the given file and registers it with the global name registry" do
99
+ stream = @obj.embed(@file.path)
100
+ assert_equal(stream, @obj[:EF][:UF])
101
+ assert_equal(stream, @obj[:EF][:F])
102
+ assert_equal(File.basename(@file.path), @obj.path)
103
+ assert_equal(@obj, @doc.catalog[:Names][:EmbeddedFiles].find_entry(@obj.path))
104
+ assert_equal(:FlateDecode, stream[:Filter])
105
+ assert_equal('embed-test', stream.stream)
106
+ end
107
+
108
+ it "embeds the given IO object" do
109
+ @file.open
110
+ stream = @obj.embed(@file, name: 'test')
111
+ assert_equal('embed-test', stream.stream)
112
+
113
+ stream = @obj.embed(StringIO.new('test'), name: 'test')
114
+ assert_equal('test', stream.stream)
115
+ end
116
+
117
+ it "allows overriding the name" do
118
+ @obj.embed(@file.path, name: 'test')
119
+ assert_equal('test', @obj.path)
120
+ assert_equal(@obj, @doc.catalog[:Names][:EmbeddedFiles].find_entry('test'))
121
+ end
122
+
123
+ it "doesn't register the embedded file if instructed to do so" do
124
+ @obj.embed(@file.path, name: 'test', register: false)
125
+ assert_nil(@doc.catalog[:Names])
126
+ end
127
+
128
+ it "replaces the value of an already registered name" do
129
+ (@doc.catalog[:Names] ||= {})[:EmbeddedFiles] = {}
130
+ @doc.catalog[:Names][:EmbeddedFiles].add_entry('test', 'data')
131
+ @obj.embed(@file.path, name: 'test')
132
+ assert_equal(@obj, @doc.catalog[:Names][:EmbeddedFiles].find_entry('test'))
133
+ end
134
+
135
+ it "unembeds an already embedded file before embedding the new one" do
136
+ @obj.embed(@file.path, name: 'test1')
137
+ @obj.embed(@file.path, name: 'test2')
138
+ assert_equal([['test2', @obj]], @doc.catalog[:Names][:EmbeddedFiles].each_entry.to_a)
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,67 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/document'
5
+ require 'hexapdf/type/font'
6
+
7
+ describe HexaPDF::Type::Font do
8
+ before do
9
+ @doc = HexaPDF::Document.new
10
+ cmap = @doc.add({}, stream: <<-EOF)
11
+ 2 beginbfchar
12
+ <20> <0041>
13
+ <22> <0042>
14
+ endbfchar
15
+ EOF
16
+ fd = @doc.add({Type: :FontDescriptor, FontBBox: [0, 1, 2, 3]})
17
+ @font = @doc.add({Type: :Font, BaseFont: :TestFont, FontDescriptor: fd, ToUnicode: cmap})
18
+ end
19
+
20
+ it "allows setting and returning a font wrapper object" do
21
+ @font.font_wrapper = :fake_wrapper
22
+ assert_equal(:fake_wrapper, @font.font_wrapper)
23
+ end
24
+
25
+ it "must always be an indirect" do
26
+ assert(@font.must_be_indirect?)
27
+ end
28
+
29
+ describe "to_utf" do
30
+ it "uses the /ToUnicode CMap if it is available" do
31
+ assert_equal("A", @font.to_utf8(32))
32
+ assert_equal("B", @font.to_utf8(34))
33
+ assert_raises(HexaPDF::Error) { @font.to_utf8(0) }
34
+ end
35
+
36
+ it "calls the configured proc if no /ToUnicode CMap is available" do
37
+ @font.delete(:ToUnicode)
38
+ assert_raises(HexaPDF::Error) { @font.to_utf8(32) }
39
+ end
40
+ end
41
+
42
+ describe "bounding_box" do
43
+ it "returns the bounding box" do
44
+ assert_equal([0, 1, 2, 3], @font.bounding_box)
45
+ end
46
+
47
+ it "returns nil if no bounding box information can be found" do
48
+ @font[:FontDescriptor].delete(:FontBBox)
49
+ assert_nil(@font.bounding_box)
50
+ end
51
+ end
52
+
53
+ describe "embedded" do
54
+ it "returns true if the font is embedded" do
55
+ refute(@font.embedded?)
56
+ @font[:FontDescriptor][:FontFile] = 5
57
+ assert(@font.embedded?)
58
+ end
59
+ end
60
+
61
+ describe "font_file" do
62
+ it "returns the stream object representing the embedded font file" do
63
+ @font[:FontDescriptor][:FontFile] = 5
64
+ assert_equal(5, @font.font_file)
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,61 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/document'
5
+ require 'hexapdf/type/font_descriptor'
6
+
7
+ describe HexaPDF::Type::FontDescriptor do
8
+ before do
9
+ @doc = HexaPDF::Document.new
10
+ @font_desc = @doc.add({Type: :FontDescriptor, FontName: :Test, Flags: 0b1001001,
11
+ ItalicAngle: 10})
12
+ end
13
+
14
+ describe "flags" do
15
+ it "returns all flags" do
16
+ assert_equal([:fixed_pitch, :script, :italic], @font_desc.flags)
17
+ end
18
+ end
19
+
20
+ describe "flagged?" do
21
+ it "returns true if the given flag is set" do
22
+ assert(@font_desc.flagged?(:fixed_pitch))
23
+ refute(@font_desc.flagged?(:serif))
24
+ end
25
+
26
+ it "raises an error if an unknown flag name is provided" do
27
+ assert_raises(ArgumentError) { @font_desc.flagged?(:unknown) }
28
+ end
29
+ end
30
+
31
+ describe "flag" do
32
+ it "sets the given flag bits" do
33
+ @font_desc.flag(:serif)
34
+ assert_equal([:fixed_pitch, :serif, :script, :italic], @font_desc.flags)
35
+ @font_desc.flag(:symbolic, clear_existing: true)
36
+ assert_equal([:symbolic], @font_desc.flags)
37
+ end
38
+ end
39
+
40
+ describe "validation" do
41
+ it "fails if more than one of /FontFile{,2,3} are set" do
42
+ assert(@font_desc.validate {|*args| p args })
43
+ @font_desc[:FontFile] = @font_desc[:FontFile2] = @doc.add({}, stream: 'test')
44
+ refute(@font_desc.validate)
45
+ end
46
+
47
+ it "deletes the /FontWeight value if it doesn't contain a valid value" do
48
+ @font_desc[:FontWeight] = 350
49
+ refute(@font_desc.validate(auto_correct: false))
50
+ assert(@font_desc.validate)
51
+ refute(@font_desc.key?(:FontWeight))
52
+ end
53
+
54
+ it "updates the /Descent value if it is not a negative number" do
55
+ @font_desc[:Descent] = 5
56
+ refute(@font_desc.validate(auto_correct: false))
57
+ assert(@font_desc.validate)
58
+ assert_equal(-5, @font_desc[:Descent])
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,176 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/document'
5
+ require 'hexapdf/type/font_simple'
6
+
7
+ describe HexaPDF::Type::FontSimple do
8
+ before do
9
+ @doc = HexaPDF::Document.new
10
+ cmap = @doc.add({}, stream: <<-EOF)
11
+ 2 beginbfchar
12
+ <20> <0041>
13
+ <22> <0042>
14
+ endbfchar
15
+ EOF
16
+ font_descriptor = @doc.add({Type: :FontDescriptor, FontName: :Embedded, Flags: 0b100,
17
+ FontBBox: [0, 1, 2, 3], ItalicAngle: 0, Ascent: 900,
18
+ Descent: -100, CapHeight: 800, StemV: 20})
19
+ @font = @doc.add({Type: :Font, Encoding: :WinAnsiEncoding,
20
+ BaseFont: :Embedded, FontDescriptor: font_descriptor, ToUnicode: cmap,
21
+ FirstChar: 32, LastChar: 34, Widths: [600, 0, 700]},
22
+ type: HexaPDF::Type::FontSimple)
23
+ end
24
+
25
+ describe "encoding" do
26
+ it "fails if /Encoding is absent because encoding_from_font is not implemented" do
27
+ @font.delete(:Encoding)
28
+ assert_raises(NotImplementedError) { @font.encoding }
29
+ end
30
+
31
+ describe "/Encoding is a name" do
32
+ it "returns a predefined encoding if /Encoding specifies one" do
33
+ assert_equal(HexaPDF::Font::Encoding.for_name(:WinAnsiEncoding), @font.encoding)
34
+ end
35
+
36
+ it "fails if /Encoding is an invalid name because encoding_from_font is not implemented" do
37
+ @font[:Encoding] = :SomethingUnknown
38
+ assert_raises(NotImplementedError) { @font.encoding }
39
+ end
40
+ end
41
+
42
+ describe "/Encoding is a dictionary" do
43
+ before do
44
+ @font[:Encoding] = {}
45
+ end
46
+
47
+ describe "no /BaseEncoding is specified" do
48
+ it "fails if the font is embedded because encoding_from_font is not implemented" do
49
+ @font[:FontDescriptor][:FontFile] = 5
50
+ assert_raises(NotImplementedError) { @font.encoding }
51
+ end
52
+
53
+ it "fails for a symbolic non-embedded font because encoding_from_font is not implemented" do
54
+ @font[:FontDescriptor].flag(:symbolic, clear_existing: true)
55
+ assert_raises(NotImplementedError) { @font.encoding }
56
+ end
57
+
58
+ it "returns the StandardEncoding for a non-symbolic non-embedded font" do
59
+ @font[:FontDescriptor].flag(clear_existing: true)
60
+ assert_equal(HexaPDF::Font::Encoding.for_name(:StandardEncoding), @font.encoding)
61
+ end
62
+ end
63
+
64
+ it "returns the encoding specified by /BaseEncoding" do
65
+ @font[:Encoding] = {BaseEncoding: :WinAnsiEncoding}
66
+ assert_equal(HexaPDF::Font::Encoding.for_name(:WinAnsiEncoding), @font.encoding)
67
+ end
68
+
69
+ it "fails if /BaseEncoding is invalid because encoding_from_font is not implemented" do
70
+ @font[:Encoding] = {BaseEncoding: :SomethingUnknown}
71
+ assert_raises(NotImplementedError) { @font.encoding }
72
+ end
73
+
74
+ it "returns a difference encoding if /Differences is specified" do
75
+ @font[:FontDescriptor].flag(clear_existing: true)
76
+ @font[:Encoding][:Differences] = [32, :A, :B, 34, :Z]
77
+ refute_equal(HexaPDF::Font::Encoding.for_name(:StandardEncoding), @font.encoding)
78
+ assert_equal(:A, @font.encoding.name(32))
79
+ assert_equal(:B, @font.encoding.name(33))
80
+ assert_equal(:Z, @font.encoding.name(34))
81
+ end
82
+
83
+ it "fails if the /Differences array contains invalid data" do
84
+ @font[:Encoding][:BaseEncoding] = :WinAnsiEncoding
85
+ @font[:Encoding][:Differences] = [:B, 32, :A, :B, 34, :Z]
86
+ assert_raises(HexaPDF::Error) { @font.encoding }
87
+
88
+ @font[:Encoding][:Differences] = [32, "data", :A, :B, 34, :Z]
89
+ assert_raises(HexaPDF::Error) { @font.encoding }
90
+ end
91
+ end
92
+
93
+ it "fails if /Encoding contains an invalid value" do
94
+ @font[:Encoding] = 5
95
+ assert_raises(HexaPDF::Error) { @font.encoding }
96
+ end
97
+ end
98
+
99
+ describe "decode" do
100
+ it "just returns the bytes of the string since this is a simple 1-byte-per-code font" do
101
+ assert_equal([65, 66], @font.decode("AB"))
102
+ end
103
+ end
104
+
105
+ describe "to_utf" do
106
+ it "uses a /ToUnicode CMap if it is available" do
107
+ assert_equal("A", @font.to_utf8(32))
108
+ end
109
+
110
+ it "uses the font's encoding to map the code to an UTF-8 string if the /ToUnicode is missing" do
111
+ @font.delete(:ToUnicode)
112
+ assert_equal(" ", @font.to_utf8(32))
113
+ end
114
+
115
+ it "calls the configured proc if no correct mapping could be found" do
116
+ @font.delete(:ToUnicode)
117
+ assert_raises(HexaPDF::Error) { @font.to_utf8(0) }
118
+ end
119
+ end
120
+
121
+ describe "writing_mode" do
122
+ it "is always horizontal" do
123
+ assert_equal(:horizontal, @font.writing_mode)
124
+ end
125
+ end
126
+
127
+ describe "width" do
128
+ it "returns the glyph width for a valid code point" do
129
+ assert_equal(600, @font.width(32))
130
+ end
131
+
132
+ it "returns the /MissingWidth of a /FontDescriptor if available and the width was not found" do
133
+ assert_equal(0, @font.width(0))
134
+ @font[:FontDescriptor][:MissingWidth] = 99
135
+ assert_equal(99, @font.width(0))
136
+ end
137
+
138
+ it "returns 0 for a missing code point when FontDescriptor is not available" do
139
+ @font.delete(:FontDescriptor)
140
+ assert_equal(0, @font.width(0))
141
+ end
142
+ end
143
+
144
+ describe "symbolic?" do
145
+ it "return true if the font is symbolic" do
146
+ @font[:FontDescriptor].flag(clear_existing: true)
147
+ refute(@font.symbolic?)
148
+
149
+ @font[:FontDescriptor].flag(:symbolic)
150
+ assert(@font.symbolic?)
151
+ end
152
+
153
+ it "returns nil if it cannot be determined whether the font is symbolic" do
154
+ @font.delete(:FontDescriptor)
155
+ assert_nil(@font.symbolic?)
156
+ end
157
+ end
158
+
159
+ it "defines word spacing as always applicable" do
160
+ assert(@font.word_spacing_applicable?)
161
+ end
162
+
163
+ describe "validation" do
164
+ before { assert(@font.validate) }
165
+
166
+ it "validates the existence of required keys" do
167
+ @font.delete(:FirstChar)
168
+ refute(@font.validate)
169
+ end
170
+
171
+ it "validates the lengths of the /Widths field" do
172
+ @font[:Widths] = [65]
173
+ refute(@font.validate)
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,31 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/document'
5
+ require 'hexapdf/type/font_true_type'
6
+
7
+ describe HexaPDF::Type::FontTrueType do
8
+ before do
9
+ @doc = HexaPDF::Document.new
10
+ font_descriptor = @doc.add({Type: :FontDescriptor, FontName: :Something, Flags: 0b100,
11
+ FontBBox: [0, 1, 2, 3], ItalicAngle: 0, Ascent: 900,
12
+ Descent: -100, CapHeight: 800, StemV: 20})
13
+ @font = @doc.add({Type: :Font, Subtype: :TrueType, Encoding: :WinAnsiEncoding,
14
+ FirstChar: 32, LastChar: 34, Widths: [600, 0, 700],
15
+ BaseFont: :Something, FontDescriptor: font_descriptor})
16
+ end
17
+
18
+ describe "validation" do
19
+ it "ignores some missing fields if the font name is one of the standard PDF fonts" do
20
+ @font[:BaseFont] = :'Arial,Bold'
21
+ [:FirstChar, :LastChar, :Widths, :FontDescriptor].each {|field| @font.delete(field) }
22
+ assert(@font.validate)
23
+ end
24
+
25
+ it "requires that the FontDescriptor key is set" do
26
+ assert(@font.validate)
27
+ @font.delete(:FontDescriptor)
28
+ refute(@font.validate)
29
+ end
30
+ end
31
+ end