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,598 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require_relative '../content/common'
5
+ require 'stringio'
6
+ require 'hexapdf/document'
7
+ require 'hexapdf/type/page'
8
+
9
+ describe HexaPDF::Type::Page do
10
+ before do
11
+ @doc = HexaPDF::Document.new
12
+ end
13
+
14
+ describe "::media_box" do
15
+ it "returns the media box for a given paper size" do
16
+ assert_equal([0, 0, 595, 842], HexaPDF::Type::Page.media_box(:A4))
17
+ end
18
+
19
+ it "respects the orientation key" do
20
+ assert_equal([0, 0, 842, 595], HexaPDF::Type::Page.media_box(:A4, orientation: :landscape))
21
+ end
22
+
23
+ it "fails if the paper size is unknown" do
24
+ assert_raises(HexaPDF::Error) { HexaPDF::Type::Page.media_box(:Unknown) }
25
+ end
26
+ end
27
+
28
+ # Asserts that the page's contents contains the operators.
29
+ def assert_page_operators(page, operators)
30
+ processor = TestHelper::OperatorRecorder.new
31
+ page.process_contents(processor)
32
+ assert_equal(operators, processor.recorded_ops)
33
+ end
34
+
35
+ it "must always be indirect" do
36
+ page = @doc.add({Type: :Page})
37
+ page.must_be_indirect = false
38
+ assert(page.must_be_indirect?)
39
+ end
40
+
41
+ describe "[]" do
42
+ before do
43
+ @root = @doc.add({Type: :Pages})
44
+ @kid = @doc.add({Type: :Pages, Parent: @root})
45
+ @page = @doc.add({Type: :Page, Parent: @kid})
46
+ end
47
+
48
+ it "works normal for non-inheritable fields" do
49
+ assert_equal(:Page, @page[:Type])
50
+ assert_nil(@page[:Dur])
51
+ end
52
+
53
+ it "automatically retrieves inherited values" do
54
+ @root[:MediaBox] = :media
55
+ assert_equal(:media, @page[:MediaBox])
56
+
57
+ @root[:Resources] = :root_res
58
+ @kid[:Resources] = :res
59
+ assert_equal(:res, @page[:Resources])
60
+
61
+ @page[:CropBox] = :cropbox
62
+ assert_equal(:cropbox, @page[:CropBox])
63
+
64
+ @kid[:Rotate] = :kid_rotate
65
+ assert_equal(:kid_rotate, @page[:Rotate])
66
+ end
67
+
68
+ it "returns nil or the default value if no value is set anywhere" do
69
+ assert_nil(@page[:MediaBox])
70
+ assert_equal(0, @page[:Rotate])
71
+ end
72
+ end
73
+
74
+ describe "validation" do
75
+ it "fails if a required inheritable field is not set" do
76
+ root = @doc.add({Type: :Pages})
77
+ page = @doc.add({Type: :Page, Parent: root})
78
+ message = ''
79
+ refute(page.validate {|m, _| message = m })
80
+ assert_match(/inheritable.*MediaBox/i, message)
81
+ end
82
+ end
83
+
84
+ describe "box" do
85
+ before do
86
+ @page = @doc.pages.add
87
+ end
88
+
89
+ it "returns the correct media box" do
90
+ @page[:MediaBox] = :media
91
+ assert_equal(:media, @page.box(:media))
92
+ end
93
+
94
+ it "returns the correct crop box" do
95
+ @page[:MediaBox] = :media
96
+ assert_equal(:media, @page.box(:crop))
97
+ @page[:CropBox] = :crop
98
+ assert_equal(:crop, @page.box(:crop))
99
+ end
100
+
101
+ it "returns the correct bleed, trim and art boxes" do
102
+ @page[:MediaBox] = :media
103
+ assert_equal(:media, @page.box(:bleed))
104
+ assert_equal(:media, @page.box(:trim))
105
+ assert_equal(:media, @page.box(:art))
106
+ @page[:CropBox] = :crop
107
+ assert_equal(:crop, @page.box(:bleed))
108
+ assert_equal(:crop, @page.box(:trim))
109
+ assert_equal(:crop, @page.box(:art))
110
+ @page[:BleedBox] = :bleed
111
+ @page[:TrimBox] = :trim
112
+ @page[:ArtBox] = :art
113
+ assert_equal(:bleed, @page.box(:bleed))
114
+ assert_equal(:trim, @page.box(:trim))
115
+ assert_equal(:art, @page.box(:art))
116
+ end
117
+
118
+ it "fails if an unknown box type is supplied" do
119
+ assert_raises(ArgumentError) { @page.box(:undefined) }
120
+ end
121
+
122
+ it "sets the correct box" do
123
+ @page.box(:media, :media)
124
+ assert_equal(:media, @page.box(:media))
125
+ @page.box(:crop, :crop)
126
+ assert_equal(:crop, @page.box(:crop))
127
+ @page.box(:bleed, :bleed)
128
+ assert_equal(:bleed, @page.box(:bleed))
129
+ @page.box(:trim, :trim)
130
+ assert_equal(:trim, @page.box(:trim))
131
+ @page.box(:art, :art)
132
+ assert_equal(:art, @page.box(:art))
133
+ end
134
+
135
+ it "fails if an unknown box type is supplied when setting a box" do
136
+ assert_raises(ArgumentError) { @page.box(:undefined, [1, 2, 3, 4]) }
137
+ end
138
+ end
139
+
140
+ describe "orientation" do
141
+ before do
142
+ @page = @doc.pages.add
143
+ end
144
+
145
+ it "returns :portrait for appropriate media boxes and rotation values" do
146
+ @page.box(:media, [0, 0, 100, 300])
147
+ assert_equal(:portrait, @page.orientation)
148
+ @page[:Rotate] = 0
149
+ assert_equal(:portrait, @page.orientation)
150
+ @page[:Rotate] = 180
151
+ assert_equal(:portrait, @page.orientation)
152
+
153
+ @page.box(:media, [0, 0, 300, 100])
154
+ @page[:Rotate] = 90
155
+ assert_equal(:portrait, @page.orientation)
156
+ @page[:Rotate] = 270
157
+ assert_equal(:portrait, @page.orientation)
158
+ end
159
+
160
+ it "returns :landscape for appropriate media boxes and rotation values" do
161
+ @page.box(:media, [0, 0, 300, 100])
162
+ assert_equal(:landscape, @page.orientation)
163
+ @page[:Rotate] = 0
164
+ assert_equal(:landscape, @page.orientation)
165
+ @page[:Rotate] = 180
166
+ assert_equal(:landscape, @page.orientation)
167
+
168
+ @page.box(:media, [0, 0, 100, 300])
169
+ @page[:Rotate] = 90
170
+ assert_equal(:landscape, @page.orientation)
171
+ @page[:Rotate] = 270
172
+ assert_equal(:landscape, @page.orientation)
173
+ end
174
+ end
175
+
176
+ describe "rotate" do
177
+ before do
178
+ @page = @doc.pages.add
179
+ reset_media_box
180
+ end
181
+
182
+ def reset_media_box
183
+ @page.box(:media, [50, 100, 200, 300])
184
+ end
185
+
186
+ it "works directly on the :Rotate key" do
187
+ @page.rotate(90)
188
+ assert_equal(270, @page[:Rotate])
189
+
190
+ @page.rotate(180)
191
+ assert_equal(90, @page[:Rotate])
192
+
193
+ @page.rotate(-90)
194
+ assert_equal(180, @page[:Rotate])
195
+ end
196
+
197
+ describe "flatten" do
198
+ it "adjust all page boxes" do
199
+ @page.box(:crop, @page.box)
200
+ @page.box(:bleed, @page.box)
201
+ @page.box(:trim, @page.box)
202
+ @page.box(:art, @page.box)
203
+
204
+ @page.rotate(90, flatten: true)
205
+ box = [-300, 50, -100, 200]
206
+ assert_equal(box, @page.box(:media).value)
207
+ assert_equal(box, @page.box(:crop).value)
208
+ assert_equal(box, @page.box(:bleed).value)
209
+ assert_equal(box, @page.box(:trim).value)
210
+ assert_equal(box, @page.box(:art).value)
211
+ end
212
+
213
+ it "works correctly for 90 degrees" do
214
+ @page.rotate(90, flatten: true)
215
+ assert_equal([-300, 50, -100, 200], @page.box(:media).value)
216
+ assert_equal(" q 0 1 -1 0 0 0 cm Q ", @page.contents)
217
+ end
218
+
219
+ it "works correctly for 180 degrees" do
220
+ @page.rotate(180, flatten: true)
221
+ assert_equal([-200, -300, -50, -100], @page.box(:media).value)
222
+ assert_equal(" q -1 0 0 -1 0 0 cm Q ", @page.contents)
223
+ end
224
+
225
+ it "works correctly for 270 degrees" do
226
+ @page.rotate(270, flatten: true)
227
+ assert_equal([100, -200, 300, -50], @page.box(:media).value)
228
+ assert_equal(" q 0 -1 1 0 0 0 cm Q ", @page.contents)
229
+ end
230
+ end
231
+
232
+ it "fails if the angle is not a multiple of 90" do
233
+ assert_raises(ArgumentError) { @page.rotate(27) }
234
+ end
235
+ end
236
+
237
+ describe "contents" do
238
+ it "returns the contents of a single content stream" do
239
+ page = @doc.pages.add
240
+ page[:Contents] = @doc.wrap({}, stream: 'q 10 w Q')
241
+ assert_equal('q 10 w Q', page.contents)
242
+ end
243
+
244
+ it "returns the concatenated contents of multiple content stream" do
245
+ page = @doc.pages.add
246
+ page[:Contents] = [@doc.wrap({}, stream: 'q 10'), @doc.wrap({}, stream: 'w Q')]
247
+ assert_equal('q 10 w Q', page.contents)
248
+ end
249
+ end
250
+
251
+ describe "contents=" do
252
+ it "creates a content stream if none already exist" do
253
+ page = @doc.pages.add
254
+ page.contents = 'test'
255
+ assert_equal('test', page[:Contents].stream)
256
+ end
257
+
258
+ it "reuses an existing content stream" do
259
+ page = @doc.pages.add
260
+ page[:Contents] = content = @doc.wrap({}, stream: 'q 10 w Q')
261
+ page.contents = 'test'
262
+ assert_equal(content, page[:Contents])
263
+ assert_equal('test', content.stream)
264
+ end
265
+
266
+ it "reuses the first content stream and deletes the rest if more than one exist" do
267
+ page = @doc.pages.add
268
+ page[:Contents] = [content = @doc.add({}, stream: 'q 10 w Q'), @doc.add({}, stream: 'q Q')]
269
+ page.contents = 'test'
270
+ assert_equal(content, page[:Contents])
271
+ assert_equal('test', content.stream)
272
+ end
273
+ end
274
+
275
+ describe "resources" do
276
+ it "creates the resource dictionary if it is not found" do
277
+ page = @doc.add({Type: :Page, Parent: @doc.pages.root})
278
+ resources = page.resources
279
+ assert_equal(:XXResources, resources.type)
280
+ assert_equal({ProcSet: [:PDF, :Text, :ImageB, :ImageC, :ImageI]}, resources.value)
281
+ end
282
+
283
+ it "returns the already used resource dictionary" do
284
+ @doc.pages.root[:Resources] = {Font: {F1: nil}}
285
+ page = @doc.pages.add(@doc.add({Type: :Page}))
286
+ resources = page.resources
287
+ assert_equal(:XXResources, resources.type)
288
+ assert_equal(@doc.pages.root[:Resources], resources)
289
+ end
290
+ end
291
+
292
+ describe "process_contents" do
293
+ it "parses the contents and processes it" do
294
+ page = @doc.pages.add
295
+ page[:Contents] = @doc.wrap({}, stream: 'q 10 w Q')
296
+ assert_page_operators(page, [[:save_graphics_state], [:set_line_width, [10]],
297
+ [:restore_graphics_state]])
298
+ end
299
+ end
300
+
301
+ describe "index" do
302
+ it "returns the index of the page in the page tree" do
303
+ kid1 = @doc.add({Type: :Pages, Parent: @doc.pages.root, Count: 4})
304
+ @doc.pages.root[:Kids] << kid1
305
+
306
+ kid11 = @doc.add({Type: :Pages, Parent: kid1})
307
+ page1 = kid11.add_page
308
+ kid1[:Kids] << kid11
309
+ page2 = kid1.add_page
310
+ kid12 = @doc.add({Type: :Pages, Parent: kid1})
311
+ page3 = kid12.add_page
312
+ page4 = kid12.add_page
313
+ kid1[:Kids] << kid12
314
+
315
+ assert_equal(0, page1.index)
316
+ assert_equal(1, page2.index)
317
+ assert_equal(2, page3.index)
318
+ assert_equal(3, page4.index)
319
+ end
320
+ end
321
+
322
+ it "returns all ancestor page tree nodes of a page" do
323
+ root = @doc.add({Type: :Pages})
324
+ kid = @doc.add({Type: :Pages, Parent: root})
325
+ page = @doc.add({Type: :Page, Parent: kid})
326
+ assert_equal([kid, root], page.ancestor_nodes)
327
+ end
328
+
329
+ describe "canvas" do
330
+ before do
331
+ @page = @doc.pages.add
332
+ end
333
+
334
+ it "works correctly if invoked on an empty page, using type :page in first invocation" do
335
+ @page.canvas.line_width = 10
336
+ assert_page_operators(@page, [[:set_line_width, [10]]])
337
+
338
+ @page.canvas(type: :overlay).line_width = 5
339
+ assert_page_operators(@page, [[:save_graphics_state], [:restore_graphics_state],
340
+ [:save_graphics_state], [:set_line_width, [10]],
341
+ [:restore_graphics_state], [:save_graphics_state],
342
+ [:set_line_width, [5]], [:restore_graphics_state]])
343
+
344
+ @page.canvas(type: :underlay).line_width = 2
345
+ assert_page_operators(@page, [[:save_graphics_state], [:set_line_width, [2]],
346
+ [:restore_graphics_state], [:save_graphics_state],
347
+ [:set_line_width, [10]],
348
+ [:restore_graphics_state], [:save_graphics_state],
349
+ [:set_line_width, [5]], [:restore_graphics_state]])
350
+ end
351
+
352
+ it "works correctly if invoked on an empty page, using type :underlay in first invocation" do
353
+ @page.canvas(type: :underlay).line_width = 2
354
+ assert_page_operators(@page, [[:save_graphics_state], [:set_line_width, [2]],
355
+ [:restore_graphics_state], [:save_graphics_state],
356
+ [:restore_graphics_state], [:save_graphics_state],
357
+ [:restore_graphics_state]])
358
+
359
+ @page.canvas.line_width = 10
360
+ assert_page_operators(@page, [[:save_graphics_state], [:set_line_width, [2]],
361
+ [:restore_graphics_state], [:save_graphics_state],
362
+ [:set_line_width, [10]], [:restore_graphics_state],
363
+ [:save_graphics_state], [:restore_graphics_state]])
364
+
365
+ @page.canvas(type: :overlay).line_width = 5
366
+ assert_page_operators(@page, [[:save_graphics_state], [:set_line_width, [2]],
367
+ [:restore_graphics_state], [:save_graphics_state],
368
+ [:set_line_width, [10]],
369
+ [:restore_graphics_state], [:save_graphics_state],
370
+ [:set_line_width, [5]], [:restore_graphics_state]])
371
+ end
372
+
373
+ it "works correctly if invoked on a page with existing contents" do
374
+ @page.contents = "10 w"
375
+
376
+ @page.canvas(type: :overlay).line_width = 5
377
+ assert_page_operators(@page, [[:save_graphics_state], [:restore_graphics_state],
378
+ [:save_graphics_state], [:set_line_width, [10]],
379
+ [:restore_graphics_state],
380
+ [:save_graphics_state], [:set_line_width, [5]],
381
+ [:restore_graphics_state]])
382
+
383
+ @page.canvas(type: :underlay).line_width = 2
384
+ assert_page_operators(@page, [[:save_graphics_state], [:set_line_width, [2]],
385
+ [:restore_graphics_state], [:save_graphics_state],
386
+ [:set_line_width, [10]],
387
+ [:restore_graphics_state],
388
+ [:save_graphics_state], [:set_line_width, [5]],
389
+ [:restore_graphics_state]])
390
+ end
391
+
392
+ it "works correctly if the page has its origin not at (0,0)" do
393
+ @page.box(:media, [-10, -5, 100, 300])
394
+ @page.canvas(type: :underlay).line_width = 2
395
+ @page.canvas(type: :page).line_width = 2
396
+ @page.canvas(type: :overlay).line_width = 2
397
+
398
+ assert_page_operators(@page, [[:save_graphics_state],
399
+ [:concatenate_matrix, [1, 0, 0, 1, -10, -5]],
400
+ [:set_line_width, [2]],
401
+ [:restore_graphics_state],
402
+
403
+ [:save_graphics_state],
404
+ [:concatenate_matrix, [1, 0, 0, 1, -10, -5]],
405
+ [:set_line_width, [2]],
406
+ [:restore_graphics_state],
407
+
408
+ [:save_graphics_state],
409
+ [:concatenate_matrix, [1, 0, 0, 1, -10, -5]],
410
+ [:set_line_width, [2]],
411
+ [:restore_graphics_state]])
412
+ end
413
+
414
+ it "fails if the page canvas is requested for a page with existing contents" do
415
+ @page.contents = "q Q"
416
+ assert_raises(HexaPDF::Error) { @page.canvas }
417
+ end
418
+
419
+ it "fails if called with an incorrect type argument" do
420
+ assert_raises(ArgumentError) { @page.canvas(type: :something) }
421
+ end
422
+ end
423
+
424
+ describe "to_form_xobject" do
425
+ it "creates an independent form xobject" do
426
+ page = @doc.pages.add
427
+ page.contents = "test"
428
+ form = page.to_form_xobject
429
+ refute(form.indirect?)
430
+ assert_equal(form.box.value, page.box.value)
431
+ end
432
+
433
+ it "works for pages without content" do
434
+ page = @doc.pages.add
435
+ form = page.to_form_xobject
436
+ assert_equal('', form.stream)
437
+ end
438
+
439
+ it "uses the raw stream data if possible to avoid unnecessary work" do
440
+ page = @doc.pages.add
441
+ page.contents = HexaPDF::StreamData.new(StringIO.new("test"))
442
+ form = page.to_form_xobject
443
+ assert_same(form.raw_stream, page[:Contents].raw_stream)
444
+
445
+ form = page.to_form_xobject(reference: false)
446
+ refute_same(form.raw_stream, page[:Contents].raw_stream)
447
+ end
448
+ end
449
+
450
+ describe "flatten_annotations" do
451
+ before do
452
+ @page = @doc.pages.add
453
+ @appearance = @doc.add({Type: :XObject, Subtype: :Form, BBox: [-10, -5, 50, 20]}, stream: "")
454
+ @annot1 = @doc.add({Type: :Annot, Subtype: :Text, Rect: [100, 100, 160, 125], AP: {N: @appearance}})
455
+ @annot2 = @doc.add({Rect: [10, 10, 70, 35], AP: {N: @appearance}})
456
+ @page[:Annots] = [@annot1, @annot2]
457
+ @canvas = @page.canvas(type: :overlay)
458
+ end
459
+
460
+ it "does nothing if the page doesn't have any annotations" do
461
+ @page.delete(:Annots)
462
+ result = @page.flatten_annotations
463
+ assert(result.empty?)
464
+ assert_operators(@canvas.contents, [])
465
+ end
466
+
467
+ it "flattens all annotations of the page by default" do
468
+ result = @page.flatten_annotations
469
+ assert(result.empty?)
470
+ assert_operators(@canvas.contents, [[:save_graphics_state],
471
+ [:save_graphics_state],
472
+ [:concatenate_matrix, [1.0, 0, 0, 1.0, 110, 105]],
473
+ [:paint_xobject, [:XO1]],
474
+ [:restore_graphics_state],
475
+ [:save_graphics_state],
476
+ [:concatenate_matrix, [1.0, 0, 0, 1.0, 20, 15]],
477
+ [:paint_xobject, [:XO1]],
478
+ [:restore_graphics_state],
479
+ [:restore_graphics_state]])
480
+ assert(@annot1.null?)
481
+ assert(@annot2.null?)
482
+ end
483
+
484
+ it "only deletes the widget annotation of a form field even if it is embedded in the field object" do
485
+ form = @doc.acro_form(create: true)
486
+ field = form.create_text_field('test')
487
+ widget = field.create_widget(@page, Rect: [200, 200, 250, 215])
488
+ field.field_value = 'hello'
489
+
490
+ assert_same(widget.data, field.data)
491
+ result = @page.flatten_annotations([widget])
492
+ assert(result.empty?)
493
+ refute(field.null?)
494
+ end
495
+
496
+ it "does nothing if a given annotation is not part of the page" do
497
+ annots = [{Test: :Object}]
498
+ result = @page.flatten_annotations(annots)
499
+ assert_equal(annots, result)
500
+ end
501
+
502
+ it "deletes hidden annotations but doesn't include them in the content stream" do
503
+ @annot1.flag(:hidden)
504
+ result = @page.flatten_annotations
505
+ assert(result.empty?)
506
+ assert(@annot1.null?)
507
+ assert_operators(@canvas.contents, [[:save_graphics_state],
508
+ [:save_graphics_state],
509
+ [:concatenate_matrix, [1.0, 0, 0, 1.0, 20, 15]],
510
+ [:paint_xobject, [:XO1]],
511
+ [:restore_graphics_state],
512
+ [:restore_graphics_state]])
513
+ end
514
+
515
+ it "deletes invisible annotations but doesn't include them in the content stream" do
516
+ @annot1.flag(:invisible)
517
+ result = @page.flatten_annotations
518
+ assert(result.empty?)
519
+ assert(@annot1.null?)
520
+ assert_operators(@canvas.contents, [[:save_graphics_state],
521
+ [:save_graphics_state],
522
+ [:concatenate_matrix, [1.0, 0, 0, 1.0, 20, 15]],
523
+ [:paint_xobject, [:XO1]],
524
+ [:restore_graphics_state],
525
+ [:restore_graphics_state]])
526
+ end
527
+
528
+ it "ignores annotations without appearane stream" do
529
+ @annot1.delete(:AP)
530
+ result = @page.flatten_annotations
531
+ assert_equal([@annot1], result)
532
+ refute(@annot1.empty?)
533
+ assert_operators(@canvas.contents, [[:save_graphics_state],
534
+ [:save_graphics_state],
535
+ [:concatenate_matrix, [1.0, 0, 0, 1.0, 20, 15]],
536
+ [:paint_xobject, [:XO1]],
537
+ [:restore_graphics_state],
538
+ [:restore_graphics_state]])
539
+ end
540
+
541
+ it "adjusts the position to counter the translation in #canvas based on the page's media box" do
542
+ @page[:MediaBox] = [-15, -15, 100, 100]
543
+ @page.flatten_annotations
544
+ assert_operators(@canvas.contents, [:concatenate_matrix, [1, 0, 0, 1, 15, 15]], range: 1)
545
+ end
546
+
547
+ it "adjusts the position in case the form /Matrix has an offset" do
548
+ @appearance[:Matrix] = [1, 0, 0, 1, 15, 15]
549
+ @page.flatten_annotations
550
+ assert_operators(@canvas.contents, [:concatenate_matrix, [1, 0, 0, 1, 95, 90]], range: 2)
551
+ end
552
+
553
+ it "adjusts the position for an appearance with a 90 degree rotation" do
554
+ @appearance[:Matrix] = [0, 1, -1, 0, 0, 0]
555
+ @annot1[:Rect] = [100, 100, 125, 160]
556
+ @page.flatten_annotations
557
+ assert_operators(@canvas.contents, [:concatenate_matrix, [1, 0, 0, 1, 120, 110]], range: 2)
558
+ end
559
+
560
+ it "adjusts the position for an appearance with a -90 degree rotation" do
561
+ @appearance[:Matrix] = [0, -1, 1, 0, 0, 0]
562
+ @annot1[:Rect] = [100, 100, 125, 160]
563
+ @page.flatten_annotations
564
+ assert_operators(@canvas.contents, [:concatenate_matrix, [1, 0, 0, 1, 105, 150]], range: 2)
565
+ end
566
+
567
+ it "adjusts the position for an appearance with a 180 degree rotation" do
568
+ @appearance[:Matrix] = [-1, 0, 0, -1, 0, 0]
569
+ @page.flatten_annotations
570
+ assert_operators(@canvas.contents, [:concatenate_matrix, [1, 0, 0, 1, 150, 120]], range: 2)
571
+ end
572
+
573
+ it "ignores an appearance with a rotation that is not a mulitple of 90" do
574
+ @appearance[:Matrix] = [-1, 0.5, 0.5, -1, 0, 0]
575
+ result = @page.flatten_annotations
576
+ assert_equal([@annot1, @annot2], result)
577
+ assert_operators(@canvas.contents, [[:save_graphics_state], [:restore_graphics_state]])
578
+ end
579
+
580
+ it "scales the appearance to fit into the annotations's rectangle" do
581
+ @annot1[:Rect] = [100, 100, 130, 150]
582
+ @page.flatten_annotations
583
+ assert_operators(@canvas.contents, [:concatenate_matrix, [0.5, 0, 0, 2, 110, 105]], range: 2)
584
+ end
585
+ end
586
+
587
+ it "yields each annotation" do
588
+ page = @doc.pages.add
589
+ annot1 = @doc.add({Type: :Annot, Subtype: :Text, Rect: [100, 100, 160, 125]})
590
+ annot2 = @doc.add({Rect: [10, 10, 70, 35]})
591
+ page[:Annots] = [annot1, nil, annot2]
592
+
593
+ annotations = page.each_annotation.to_a
594
+ assert_equal([100, 100, 160, 125], annotations[0][:Rect])
595
+ assert_equal(:Annot, annotations[0].type)
596
+ assert_equal(:Annot, annotations[1].type)
597
+ end
598
+ end