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,73 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'stringio'
5
+ require 'hexapdf/font/true_type'
6
+
7
+ describe HexaPDF::Font::TrueType::Subsetter do
8
+ before do
9
+ font_file = File.join(TEST_DATA_DIR, "fonts", "Ubuntu-Title.ttf")
10
+ @font = HexaPDF::Font::TrueType::Font.new(File.open(font_file))
11
+ @subsetter = HexaPDF::Font::TrueType::Subsetter.new(@font)
12
+ end
13
+
14
+ after do
15
+ @font.io.close
16
+ end
17
+
18
+ it "adds glyphs to the subset" do
19
+ assert_equal(1, @subsetter.use_glyph(5))
20
+ assert_equal(2, @subsetter.use_glyph(6))
21
+ assert_equal(1, @subsetter.use_glyph(5))
22
+ end
23
+
24
+ it "returns the subset glyph ID for already subset glyphs" do
25
+ assert_nil(@subsetter.subset_glyph_id(5))
26
+ value = @subsetter.use_glyph(5)
27
+ assert_equal(value, @subsetter.subset_glyph_id(5))
28
+ end
29
+
30
+ it "doesn't use certain subset glyph IDs for performance reasons" do
31
+ 1.upto(93) {|i| @subsetter.use_glyph(i) }
32
+ # glyph 0, 93 used glyph, 4 special glyphs
33
+ assert_equal(1 + 93 + 4, @subsetter.instance_variable_get(:@glyph_map).size)
34
+ 1.upto(12) {|i| assert_equal(i, @subsetter.subset_glyph_id(i), "id=#{i}") }
35
+ 13.upto(38) {|i| assert_equal(i + 1, @subsetter.subset_glyph_id(i), "id=#{i}") }
36
+ 39.upto(88) {|i| assert_equal(i + 3, @subsetter.subset_glyph_id(i), "id=#{i}") }
37
+ 89.upto(93) {|i| assert_equal(i + 4, @subsetter.subset_glyph_id(i), "id=#{i}") }
38
+ end
39
+
40
+ it "creates the subset font file" do
41
+ gid = @font[:cmap].preferred_table[0x41]
42
+ @subsetter.use_glyph(gid)
43
+ subset = HexaPDF::Font::TrueType::Font.new(StringIO.new(@subsetter.build_font))
44
+
45
+ assert(Time.now - subset[:head].modified < 10)
46
+ assert_equal(2, subset[:maxp].num_glyphs)
47
+ assert_equal(2, subset[:hhea].num_of_long_hor_metrics)
48
+ assert_equal(3, subset[:loca].offsets.length)
49
+
50
+ assert_equal(subset[:hmtx][0], @font[:hmtx][0])
51
+ assert_equal(subset[:hmtx][1], @font[:hmtx][gid])
52
+
53
+ assert_equal(subset[:glyf][1].raw_data, @font[:glyf][gid].raw_data)
54
+ end
55
+
56
+ it "correctly subsets compound glyphs" do
57
+ font_file = "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"
58
+ skip unless File.exist?(font_file)
59
+
60
+ begin
61
+ @font = HexaPDF::Font::TrueType::Font.new(File.open(font_file))
62
+ @subsetter = HexaPDF::Font::TrueType::Subsetter.new(@font)
63
+
64
+ @subsetter.use_glyph(@font[:cmap].preferred_table['À'.ord])
65
+ subset = HexaPDF::Font::TrueType::Font.new(StringIO.new(@subsetter.build_font))
66
+
67
+ assert_equal(4, subset[:maxp].num_glyphs)
68
+ assert_equal([2, 3], subset[:glyf][1].components)
69
+ ensure
70
+ @font.io.close
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,48 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'stringio'
5
+ require 'hexapdf/font/true_type/table'
6
+ require_relative 'common'
7
+
8
+ describe HexaPDF::Font::TrueType::Table do
9
+ before do
10
+ io = StringIO.new('some string')
11
+ @file = Object.new
12
+ @file.define_singleton_method(:io) { io }
13
+ @entry = HexaPDF::Font::TrueType::Table::Directory::Entry.new('tagg', 0, 0, @file.io.string.length)
14
+ end
15
+
16
+ describe "initialize" do
17
+ it "reads the data from the associated file" do
18
+ table = TestHelper::TrueTypeTestTable.new(@file, @entry)
19
+ assert_equal(@file.io.string, table.data)
20
+ end
21
+ end
22
+
23
+ describe "checksum_valid?" do
24
+ it "checks whether an entry's checksum is valid" do
25
+ @file.io.string = 254.chr * 17 + 0.chr * 3
26
+ @entry.checksum = (0xfefefefe * 4 + 0xfe000000) % 2**32
27
+ @entry.length = @file.io.string.length
28
+ table = TestHelper::TrueTypeTestTable.new(@file, @entry)
29
+ assert(table.checksum_valid?)
30
+ end
31
+ end
32
+
33
+ describe "read_fixed" do
34
+ it "works for unsigned values" do
35
+ @file.io.string = [1, 20480].pack('nn')
36
+ @entry.length = @file.io.string.length
37
+ table = TestHelper::TrueTypeTestTable.new(@file, @entry)
38
+ assert_equal(1 + Rational(20480, 65536), table.send(:read_fixed))
39
+ end
40
+
41
+ it "works for signed values" do
42
+ @file.io.string = [-1, 20480].pack('nn')
43
+ @entry.length = @file.io.string.length
44
+ table = TestHelper::TrueTypeTestTable.new(@file, @entry)
45
+ assert_equal(-1 + Rational(20480, 65536), table.send(:read_fixed))
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,6 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'hexapdf/font/type1'
4
+
5
+ FONT_TIMES = HexaPDF::Font::Type1::Font.from_afm(File.join(HexaPDF.data_dir, 'afm', "Times-Roman.afm"))
6
+ FONT_SYMBOL = HexaPDF::Font::Type1::Font.from_afm(File.join(HexaPDF.data_dir, 'afm', "Symbol.afm"))
@@ -0,0 +1,65 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require_relative 'common'
5
+ require 'hexapdf/font/type1'
6
+ require 'hexapdf/data_dir'
7
+ require 'tempfile'
8
+ require 'stringio'
9
+
10
+ describe HexaPDF::Font::Type1::AFMParser do
11
+ describe "::parse" do
12
+ before do
13
+ @file = Tempfile.new('hexapdf-afm')
14
+ @file.write("StartFontMetrics 4.1\nFontName Test\nEndFontMetrics\nFontName Other\n")
15
+ @file.close
16
+ end
17
+
18
+ after do
19
+ @file.unlink
20
+ end
21
+
22
+ it "can work with file names" do
23
+ assert_equal('Test', HexaPDF::Font::Type1::AFMParser.parse(@file.path).font_name)
24
+ end
25
+
26
+ it "can work with IO streams" do
27
+ @file.open
28
+ assert_equal('Test', HexaPDF::Font::Type1::AFMParser.parse(@file).font_name)
29
+ end
30
+ end
31
+
32
+ it "can parse the 14 core PDF font files" do
33
+ Dir[File.join(HexaPDF.data_dir, 'afm', '*.afm')].each do |file|
34
+ metrics = HexaPDF::Font::Type1::AFMParser.parse(file)
35
+ basename = File.basename(file, '.*')
36
+ assert_equal(basename, metrics.font_name, basename)
37
+ assert_equal(basename.sub(/-.*/, ''), metrics.family_name, basename)
38
+ refute(metrics.character_metrics.empty?, basename)
39
+ end
40
+ end
41
+
42
+ it "parses until EOF if no end token is found" do
43
+ io = StringIO.new("StartFontMetrics 4.1\nFontName Test")
44
+ assert_equal('Test', HexaPDF::Font::Type1::AFMParser.parse(io).font_name)
45
+ end
46
+
47
+ it "extracts kerning and ligature information" do
48
+ metrics = FONT_TIMES.metrics
49
+ glyph = metrics.character_metrics[:f]
50
+ assert_equal([20, 0, 383, 683], glyph.bbox)
51
+ assert_equal(-20, metrics.kerning_pairs.dig(:f, :i))
52
+ assert_equal(:fi, metrics.ligature_pairs.dig(:f, :i))
53
+ end
54
+
55
+ it "calculates an ascender and descender value from the font bounding box if necessary" do
56
+ metrics = HexaPDF::Font::Type1::AFMParser.parse(File.join(HexaPDF.data_dir, 'afm/Symbol.afm'))
57
+ assert_equal(metrics.bounding_box[1], metrics.descender)
58
+ assert_equal(metrics.bounding_box[3], metrics.ascender)
59
+ end
60
+
61
+ it "fails if the file doesn't start with the correct line" do
62
+ file = StringIO.new("some\nthing")
63
+ assert_raises(HexaPDF::Error) { HexaPDF::Font::Type1::AFMParser.parse(file) }
64
+ end
65
+ end
@@ -0,0 +1,104 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require_relative 'common'
5
+ require 'hexapdf/font/type1'
6
+
7
+ describe HexaPDF::Font::Type1::Font do
8
+ before do
9
+ metrics = HexaPDF::Font::Type1::FontMetrics.new
10
+ @font = HexaPDF::Font::Type1::Font.new(metrics)
11
+ end
12
+
13
+ describe "::from_afm" do
14
+ it "can load the Font object from an AFM file" do
15
+ font = HexaPDF::Font::Type1::Font.from_afm(File.join(HexaPDF.data_dir, 'afm/Symbol.afm'))
16
+ assert_equal('Symbol', font.family_name)
17
+ end
18
+ end
19
+
20
+ describe "encoding" do
21
+ it "uses the StandardEncoding if possible" do
22
+ @font.metrics.encoding_scheme = 'AdobeStandardEncoding'
23
+ assert_equal(HexaPDF::Font::Encoding.for_name(:StandardEncoding), @font.encoding)
24
+ end
25
+
26
+ it "handles the special case of the ZapfDingbats font" do
27
+ @font.metrics.font_name = "ZapfDingbats"
28
+ assert_equal(HexaPDF::Font::Encoding.for_name(:ZapfDingbatsEncoding), @font.encoding)
29
+ end
30
+
31
+ it "handles the special case of the Symbol font" do
32
+ @font.metrics.font_name = "Symbol"
33
+ assert_equal(HexaPDF::Font::Encoding.for_name(:SymbolEncoding), @font.encoding)
34
+ end
35
+
36
+ it "generates an encoding object if necessary" do
37
+ char_metrics = HexaPDF::Font::Type1::CharacterMetrics.new
38
+ char_metrics.code = 5
39
+ char_metrics.name = :A
40
+ @font.metrics.character_metrics[5] = char_metrics.dup
41
+ char_metrics.code = 6
42
+ char_metrics.name = :Z
43
+ @font.metrics.character_metrics[6] = char_metrics.dup
44
+
45
+ assert_equal({5 => :A, 6 => :Z}, @font.encoding.code_to_name)
46
+ end
47
+ end
48
+
49
+ describe "width" do
50
+ before do
51
+ @char_metrics = HexaPDF::Font::Type1::CharacterMetrics.new
52
+ @char_metrics.width = 100
53
+ end
54
+
55
+ it "returns the width for a code point in the built-in encoding" do
56
+ @font.metrics.character_metrics[5] = @char_metrics
57
+ assert_equal(100, @font.width(5))
58
+ end
59
+
60
+ it "returns the width for a named glyph" do
61
+ @font.metrics.character_metrics[:A] = @char_metrics
62
+ assert_equal(100, @font.width(:A))
63
+ end
64
+ end
65
+
66
+ it "is able to return the ID of the missing glyph" do
67
+ assert_equal(:'.notdef', @font.missing_glyph_id)
68
+ end
69
+
70
+ it "returns the features available for a font" do
71
+ assert_equal([:kern, :liga].to_set, FONT_TIMES.features)
72
+ assert(FONT_SYMBOL.features.empty?)
73
+ end
74
+
75
+ describe "underline properties" do
76
+ before do
77
+ @font.metrics.underline_position = -100
78
+ @font.metrics.underline_thickness = 50
79
+ end
80
+
81
+ it "returns the underline position" do
82
+ assert_equal(-75, @font.underline_position)
83
+ end
84
+
85
+ it "returns the underline thickness" do
86
+ assert_equal(50, @font.underline_thickness)
87
+ end
88
+ end
89
+
90
+ describe "strikeout properties" do
91
+ it "returns the strikeout position" do
92
+ assert_equal(225, @font.strikeout_position)
93
+ end
94
+
95
+ it "returns the strikeout thickness" do
96
+ assert_equal(50, @font.strikeout_thickness)
97
+
98
+ emdash = HexaPDF::Font::Type1::CharacterMetrics.new
99
+ emdash.bbox = [0, 200, 1000, 240]
100
+ @font.metrics.character_metrics[:emdash] = emdash
101
+ assert_equal(40, @font.strikeout_thickness)
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/font/type1'
5
+
6
+ describe HexaPDF::Font::Type1::FontMetrics do
7
+ before do
8
+ @metrics = HexaPDF::Font::Type1::FontMetrics.new
9
+ end
10
+
11
+ describe "weight_class" do
12
+ it "converts known weight names" do
13
+ @metrics.weight = 'Bold'
14
+ assert_equal(700, @metrics.weight_class)
15
+ end
16
+
17
+ it "returns 0 for unknown weight names" do
18
+ @metrics.weight = 'Unknown'
19
+ assert_equal(0, @metrics.weight_class)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,37 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/font/type1'
5
+
6
+ describe HexaPDF::Font::Type1::PFBParser do
7
+ describe "::encoding" do
8
+ it "can extract the encoding specified as StandardEncoding" do
9
+ data = "bla bla bla /Encoding StandardEncoding def bla bla"
10
+ assert_same(HexaPDF::Font::Encoding.for_name(:StandardEncoding),
11
+ HexaPDF::Font::Type1::PFBParser.encoding(data))
12
+ end
13
+
14
+ it "can extract the encoding specified as array" do
15
+ data = <<-EOF
16
+ bla bla bla
17
+ /Encoding 256 array
18
+ 0 1 255 {1 index exch /.notdef put} for
19
+ dup 32 /space put
20
+ dup 33 /exclam put
21
+ dup 34 /universal put
22
+ dup 35 /numbersign put
23
+ dup 36 /existential put
24
+ readonly def
25
+ bla bla bla
26
+ EOF
27
+ enc = HexaPDF::Font::Type1::PFBParser.encoding(data)
28
+ assert_equal(:space, enc.name(32))
29
+ assert_equal(:existential, enc.name(36))
30
+ end
31
+
32
+ it "fails if the encoding can't be extracted" do
33
+ data = "something without an encoding"
34
+ assert_raises(HexaPDF::Error) { HexaPDF::Font::Type1::PFBParser.encoding(data) }
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,43 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/font_loader'
5
+ require 'hexapdf/document'
6
+
7
+ describe HexaPDF::FontLoader::FromConfiguration do
8
+ before do
9
+ @doc = HexaPDF::Document.new
10
+ font_file = File.join(TEST_DATA_DIR, "fonts", "Ubuntu-Title.ttf")
11
+ @font_obj = HexaPDF::Font::TrueType::Font.new(File.open(font_file, 'rb'))
12
+ @doc.config['font.map'] = {'font' => {none: font_file}, 'font1' => {none: @font_obj}}
13
+ @klass = HexaPDF::FontLoader::FromConfiguration
14
+ end
15
+
16
+ it "loads the configured font" do
17
+ wrapper = @klass.call(@doc, "font")
18
+ assert_equal("Ubuntu-Title", wrapper.wrapped_font.font_name)
19
+ wrapper = @klass.call(@doc, "font1")
20
+ assert_equal("Ubuntu-Title", wrapper.wrapped_font.font_name)
21
+ assert_same(@font_obj, wrapper.wrapped_font)
22
+ end
23
+
24
+ it "passes the subset value to the wrapper" do
25
+ wrapper = @klass.call(@doc, "font")
26
+ assert(wrapper.subset?)
27
+ wrapper = @klass.call(@doc, "font", subset: false)
28
+ refute(wrapper.subset?)
29
+ end
30
+
31
+ it "fails if the provided font is invalid" do
32
+ @doc.config['font.map']['font'][:none] << "unknown"
33
+ assert_raises(HexaPDF::Error) { @klass.call(@doc, "font") }
34
+ end
35
+
36
+ it "returns nil for unknown fonts" do
37
+ assert_nil(@klass.call(@doc, "Unknown"))
38
+ end
39
+
40
+ it "returns a hash with all configured fonts" do
41
+ assert_equal({'font' => [:none], 'font1' => [:none]}, @klass.available_fonts(@doc))
42
+ end
43
+ end
@@ -0,0 +1,36 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/font_loader'
5
+ require 'hexapdf/document'
6
+
7
+ describe HexaPDF::FontLoader::FromFile do
8
+ before do
9
+ @doc = HexaPDF::Document.new
10
+ @font_file = File.join(TEST_DATA_DIR, "fonts", "Ubuntu-Title.ttf")
11
+ @klass = HexaPDF::FontLoader::FromFile
12
+ end
13
+
14
+ it "loads the specified font file" do
15
+ wrapper = @klass.call(@doc, @font_file)
16
+ assert_equal("Ubuntu-Title", wrapper.wrapped_font.font_name)
17
+ end
18
+
19
+ it "loads the specified font object" do
20
+ font = HexaPDF::Font::TrueType::Font.new(File.open(@font_file, 'rb'))
21
+ wrapper = @klass.call(@doc, font)
22
+ assert_equal("Ubuntu-Title", wrapper.wrapped_font.font_name)
23
+ assert_same(font, wrapper.wrapped_font)
24
+ end
25
+
26
+ it "passes the subset value to the wrapper" do
27
+ wrapper = @klass.call(@doc, @font_file)
28
+ assert(wrapper.subset?)
29
+ wrapper = @klass.call(@doc, @font_file, subset: false)
30
+ refute(wrapper.subset?)
31
+ end
32
+
33
+ it "returns nil if the given name doesn't represent a file" do
34
+ assert_nil(@klass.call(@doc, "Unknown"))
35
+ end
36
+ end
@@ -0,0 +1,33 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/font_loader'
5
+ require 'hexapdf/document'
6
+
7
+ describe HexaPDF::FontLoader::Standard14 do
8
+ before do
9
+ @doc = HexaPDF::Document.new
10
+ @obj = HexaPDF::FontLoader::Standard14
11
+ end
12
+
13
+ it "loads the font if it is a standard PDF built-in font" do
14
+ wrapper = @obj.call(@doc, "Times")
15
+ assert_equal("Times-Roman", wrapper.wrapped_font.font_name)
16
+ wrapper = @obj.call(@doc, "Helvetica", variant: :bold)
17
+ assert_equal("Helvetica-Bold", wrapper.wrapped_font.font_name)
18
+ end
19
+
20
+ it "returns nil for unknown fonts" do
21
+ assert_nil(@obj.call(@doc, "Unknown"))
22
+ end
23
+
24
+ it "returns a hash with all standard PDF fonts" do
25
+ assert_equal({
26
+ 'Times' => [:none, :bold, :italic, :bold_italic],
27
+ 'Helvetica' => [:none, :bold, :italic, :bold_italic],
28
+ 'Courier' => [:none, :bold, :italic, :bold_italic],
29
+ 'Symbol' => [:none], 'ZapfDingbats' => [:none]
30
+ },
31
+ @obj.available_fonts(@doc))
32
+ end
33
+ end
@@ -0,0 +1,93 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/document'
5
+ require 'hexapdf/image_loader/jpeg'
6
+
7
+ describe HexaPDF::ImageLoader::JPEG do
8
+ before do
9
+ @images = Dir.glob(File.join(TEST_DATA_DIR, 'images', '*.jpg'))
10
+ @doc = HexaPDF::Document.new
11
+ @loader = HexaPDF::ImageLoader::JPEG
12
+ end
13
+
14
+ describe "handles?" do
15
+ it "works for jpeg files" do
16
+ @images.each do |image|
17
+ assert(@loader.handles?(image))
18
+ File.open(image, 'rb') {|file| assert(@loader.handles?(file)) }
19
+ end
20
+ end
21
+ end
22
+
23
+ describe "load" do
24
+ it "can work with an IO stream instead of a file" do
25
+ jpeg = @images.grep(/rgb\.jpg/).first
26
+ File.open(jpeg, 'rb') do |file|
27
+ image = @loader.load(@doc, file)
28
+ assert_equal(File.binread(jpeg), image.stream)
29
+ end
30
+ end
31
+
32
+ it "works for a grayscale jpeg" do
33
+ jpeg = @images.grep(/gray\.jpg/).first
34
+ image = @loader.load(@doc, jpeg)
35
+ assert_equal(5, image[:Width])
36
+ assert_equal(5, image[:Height])
37
+ assert_equal(:DeviceGray, image[:ColorSpace])
38
+ assert_equal(File.binread(jpeg), image.stream)
39
+ end
40
+
41
+ it "works for a standard RGB jpeg" do
42
+ jpeg = @images.grep(/rgb\.jpg/).first
43
+ image = @loader.load(@doc, jpeg)
44
+ assert_equal(5, image[:Width])
45
+ assert_equal(5, image[:Height])
46
+ assert_equal(:DeviceRGB, image[:ColorSpace])
47
+ assert_equal(File.binread(jpeg), image.stream)
48
+ end
49
+
50
+ it "works for a jpeg image containing fill bytes" do
51
+ jpeg = @images.grep(/fillbytes\.jpg/).first
52
+ image = @loader.load(@doc, jpeg)
53
+ assert_equal(5, image[:Width])
54
+ assert_equal(5, image[:Height])
55
+ assert_equal(:DeviceRGB, image[:ColorSpace])
56
+ assert_equal(File.binread(jpeg), image.stream)
57
+ end
58
+
59
+ it "works for a CMYK jpeg" do
60
+ jpeg = @images.grep(/cmyk\.jpg/).first
61
+ image = @loader.load(@doc, jpeg)
62
+ assert_equal(5, image[:Width])
63
+ assert_equal(5, image[:Height])
64
+ assert_equal(:DeviceCMYK, image[:ColorSpace])
65
+ assert_equal([1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0], image[:Decode].value)
66
+ assert_equal(File.binread(jpeg), image.stream)
67
+ end
68
+
69
+ it "works for a YCCK jpeg" do
70
+ jpeg = @images.grep(/ycck\.jpg/).first
71
+ image = @loader.load(@doc, jpeg)
72
+ assert_equal(5, image[:Width])
73
+ assert_equal(5, image[:Height])
74
+ assert_equal(:DeviceCMYK, image[:ColorSpace])
75
+ refute(image.key?(:Decode))
76
+ assert_equal(File.binread(jpeg), image.stream)
77
+ end
78
+
79
+ it "fails if the JPEG is corrupt" do
80
+ exp = assert_raises(HexaPDF::Error) do
81
+ @loader.load(@doc, StringIO.new("some non JPEG data"))
82
+ end
83
+ assert_match(/marker code/, exp.message)
84
+ end
85
+
86
+ it "fails if the JPEG contains components with more/less bits than 8" do
87
+ exp = assert_raises(HexaPDF::Error) do
88
+ @loader.load(@doc, StringIO.new("\xFF\xD8\xFF\xC0\x00\x06\x04\x00\x05\x00\x05\x03".b))
89
+ end
90
+ assert_match(/Unsupported.*bits.*4/, exp.message)
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,47 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/document'
5
+ require 'hexapdf/image_loader/pdf'
6
+
7
+ describe HexaPDF::ImageLoader::PDF do
8
+ before do
9
+ @doc = HexaPDF::Document.new
10
+ @loader = HexaPDF::ImageLoader::PDF
11
+ @pdf = File.join(TEST_DATA_DIR, 'minimal.pdf')
12
+ end
13
+
14
+ describe "handles?" do
15
+ it "works for PDF files" do
16
+ assert(@loader.handles?(@pdf))
17
+ File.open(@pdf, 'rb') {|file| assert(@loader.handles?(file)) }
18
+ end
19
+ end
20
+
21
+ describe "load" do
22
+ it "works for PDF files using a File object" do
23
+ File.open(@pdf, 'rb') do |file|
24
+ form = @loader.load(@doc, file)
25
+ assert_equal(:Form, form[:Subtype])
26
+ end
27
+ end
28
+
29
+ it "works for PDF files using a string object and use_stringio=true" do
30
+ @doc.config['image_loader.pdf.use_stringio'] = true
31
+ form = @loader.load(@doc, @pdf)
32
+ assert_equal(:Form, form[:Subtype])
33
+ end
34
+
35
+ it "works for PDF files using a string object and use_stringio=false" do
36
+ begin
37
+ @doc.config['image_loader.pdf.use_stringio'] = false
38
+ form = @loader.load(@doc, @pdf)
39
+ assert_equal(:Form, form[:Subtype])
40
+ ensure
41
+ ObjectSpace.each_object(File) do |file|
42
+ file.close if file.path == @pdf && !file.closed?
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end