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,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/font/encoding'
5
+
6
+ describe HexaPDF::Font::Encoding do
7
+ describe "::for_name" do
8
+ it "returns nil if an unsupported encoding is given" do
9
+ assert_nil(HexaPDF::Font::Encoding.for_name(:some_unknown_encoding))
10
+ end
11
+
12
+ it "returns the requested encoding object" do
13
+ assert_kind_of(HexaPDF::Font::Encoding::WinAnsiEncoding,
14
+ HexaPDF::Font::Encoding.for_name(:WinAnsiEncoding))
15
+ assert_kind_of(HexaPDF::Font::Encoding::MacRomanEncoding,
16
+ HexaPDF::Font::Encoding.for_name(:MacRomanEncoding))
17
+ assert_kind_of(HexaPDF::Font::Encoding::StandardEncoding,
18
+ HexaPDF::Font::Encoding.for_name(:StandardEncoding))
19
+ assert_kind_of(HexaPDF::Font::Encoding::MacExpertEncoding,
20
+ HexaPDF::Font::Encoding.for_name(:MacExpertEncoding))
21
+ assert_kind_of(HexaPDF::Font::Encoding::SymbolEncoding,
22
+ HexaPDF::Font::Encoding.for_name(:SymbolEncoding))
23
+ assert_kind_of(HexaPDF::Font::Encoding::ZapfDingbatsEncoding,
24
+ HexaPDF::Font::Encoding.for_name(:ZapfDingbatsEncoding))
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,34 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/font/invalid_glyph'
5
+
6
+ describe HexaPDF::Font::InvalidGlyph do
7
+ before do
8
+ font = Object.new
9
+ font.define_singleton_method(:missing_glyph_id) { 0 }
10
+ font.define_singleton_method(:full_name) { "Test Roman" }
11
+ @glyph = HexaPDF::Font::InvalidGlyph.new(font, "str")
12
+ end
13
+
14
+ it "returns the missing glyph id for id/name" do
15
+ assert_equal(0, @glyph.id)
16
+ assert_equal(0, @glyph.name)
17
+ end
18
+
19
+ it "returns 0 for all glyph dimensions" do
20
+ assert_equal(0, @glyph.x_min)
21
+ assert_equal(0, @glyph.x_max)
22
+ assert_equal(0, @glyph.y_min)
23
+ assert_equal(0, @glyph.y_max)
24
+ end
25
+
26
+ it "doesn't allow the application of word spacing" do
27
+ refute(@glyph.apply_word_spacing?)
28
+ end
29
+
30
+ it "can represent itself for debug purposes" do
31
+ assert_equal('#<HexaPDF::Font::InvalidGlyph font="Test Roman" id=0 "str">',
32
+ @glyph.inspect)
33
+ end
34
+ end
@@ -0,0 +1,186 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/font/true_type_wrapper'
5
+ require 'hexapdf/document'
6
+
7
+ describe HexaPDF::Font::TrueTypeWrapper do
8
+ before do
9
+ @doc = HexaPDF::Document.new
10
+ @font_file = File.join(TEST_DATA_DIR, "fonts", "Ubuntu-Title.ttf")
11
+ @font = HexaPDF::Font::TrueType::Font.new(File.open(@font_file))
12
+ @cmap = @font[:cmap].preferred_table
13
+ @font_wrapper = HexaPDF::Font::TrueTypeWrapper.new(@doc, @font)
14
+ end
15
+
16
+ after do
17
+ @font.io.close
18
+ end
19
+
20
+ describe "initialize" do
21
+ it "fails if the TrueType font has no Unicode cmap table" do
22
+ @font[:cmap].tables.clear
23
+ assert_raises(HexaPDF::Error) { HexaPDF::Font::TrueTypeWrapper.new(@doc, @font) }
24
+ end
25
+ end
26
+
27
+ it "can be asked whether font wil be subset" do
28
+ assert(@font_wrapper.subset?)
29
+ refute(HexaPDF::Font::TrueTypeWrapper.new(@doc, @font, subset: false).subset?)
30
+ end
31
+
32
+ describe "decode_utf8" do
33
+ it "returns an array of glyph objects" do
34
+ assert_equal("Test",
35
+ @font_wrapper.decode_utf8("Test").map {|g| @cmap.gid_to_code(g.id) }.pack('U*'))
36
+ end
37
+
38
+ it "invokes font.on_missing_glyph for UTF-8 characters for which no glyph exists" do
39
+ glyphs = @font_wrapper.decode_utf8("😁")
40
+ assert_equal(1, glyphs.length)
41
+ assert_kind_of(HexaPDF::Font::InvalidGlyph, glyphs.first)
42
+ assert_equal('' << 128_513, glyphs.first.str)
43
+ end
44
+ end
45
+
46
+ describe "glyph" do
47
+ it "returns the glyph object for the given id" do
48
+ glyph = @font_wrapper.glyph(17)
49
+ assert_equal(17, glyph.id)
50
+ assert_equal("0", glyph.str)
51
+ assert_equal(628, glyph.width)
52
+ assert_equal(47, glyph.x_min)
53
+ assert_equal(0, glyph.y_min)
54
+ assert_equal(584, glyph.x_max)
55
+ assert_equal(696, glyph.y_max)
56
+ refute(glyph.apply_word_spacing?)
57
+ assert_equal('#<HexaPDF::Font::TrueTypeWrapper::Glyph font="Ubuntu-Title" id=17 "0">',
58
+ glyph.inspect)
59
+ end
60
+
61
+ it "invokes font.on_missing_glyph for missing glyphs" do
62
+ glyph = @font_wrapper.glyph(9999)
63
+ assert_kind_of(HexaPDF::Font::InvalidGlyph, glyph)
64
+ assert_equal(0, glyph.id)
65
+ assert_equal('' << 0xFFFD, glyph.str)
66
+ end
67
+ end
68
+
69
+ describe "encode" do
70
+ it "returns the encoded glyph ID for fonts that are subset" do
71
+ code = @font_wrapper.encode(@font_wrapper.glyph(3))
72
+ assert_equal([1].pack('n'), code)
73
+ code = @font_wrapper.encode(@font_wrapper.glyph(10))
74
+ assert_equal([2].pack('n'), code)
75
+ end
76
+
77
+ it "returns the encoded glyph ID for fonts that are not subset" do
78
+ @font_wrapper = HexaPDF::Font::TrueTypeWrapper.new(@doc, @font, subset: false)
79
+ code = @font_wrapper.encode(@font_wrapper.glyph(3))
80
+ assert_equal([3].pack('n'), code)
81
+ code = @font_wrapper.encode(@font_wrapper.glyph(10))
82
+ assert_equal([10].pack('n'), code)
83
+ end
84
+
85
+ it "raises an error if an InvalidGlyph is encoded" do
86
+ assert_raises(HexaPDF::Error) { @font_wrapper.encode(@font_wrapper.glyph(9999)) }
87
+ end
88
+ end
89
+
90
+ describe "creates the necessary PDF dictionaries" do
91
+ it "with fonts that are subset" do
92
+ @font_wrapper.encode(@font_wrapper.glyph(3))
93
+ glyph = @font_wrapper.decode_utf8('H').first
94
+ @font_wrapper.encode(glyph)
95
+ @doc.dispatch_message(:complete_objects)
96
+
97
+ dict = @font_wrapper.pdf_object
98
+
99
+ # Checking the circular reference
100
+ assert_same(@font_wrapper, dict.font_wrapper)
101
+
102
+ # Checking Type 0 font dictionary
103
+ assert_equal(:Font, dict[:Type])
104
+ assert_equal(:Type0, dict[:Subtype])
105
+ assert_equal(:'Identity-H', dict[:Encoding])
106
+ assert_equal(1, dict[:DescendantFonts].length)
107
+ assert_equal(dict[:BaseFont], dict[:DescendantFonts][0][:BaseFont])
108
+ assert_equal(HexaPDF::Font::CMap.create_to_unicode_cmap([[1, ' '.ord], [2, 'H'.ord]]),
109
+ dict[:ToUnicode].stream)
110
+ assert_match(/\A[A-Z]{6}\+Ubuntu-Title\z/, dict[:BaseFont])
111
+
112
+ # Checking CIDFont dictionary
113
+ cidfont = dict[:DescendantFonts][0]
114
+ assert_equal(:Font, cidfont[:Type])
115
+ assert_equal(:CIDFontType2, cidfont[:Subtype])
116
+ assert_equal({Registry: "Adobe", Ordering: "Identity", Supplement: 0},
117
+ cidfont[:CIDSystemInfo].value)
118
+ assert_equal(:Identity, cidfont[:CIDToGIDMap])
119
+ assert_equal(@font_wrapper.glyph(3).width, cidfont[:DW])
120
+ assert_equal([2, [glyph.width]], cidfont[:W].value)
121
+ assert(cidfont.validate)
122
+
123
+ # Checking font descriptor
124
+ fd = cidfont[:FontDescriptor]
125
+ assert_equal(dict[:BaseFont], fd[:FontName])
126
+ assert(fd.flagged?(:symbolic))
127
+ assert(fd.key?(:FontFile2))
128
+ assert(fd.validate)
129
+
130
+ # Two special cases for determining cap height and x-height
131
+ @cmap.stub(:[], nil) do
132
+ @font[:'OS/2'].typo_ascender = 1000
133
+ font_wrapper = HexaPDF::Font::TrueTypeWrapper.new(@doc, @font)
134
+ font_wrapper.encode(glyph)
135
+ fd = font_wrapper.pdf_object[:DescendantFonts][0][:FontDescriptor]
136
+ assert_equal(800, fd[:CapHeight])
137
+ assert_equal(500, fd[:XHeight])
138
+ end
139
+
140
+ @font[:'OS/2'].version = 2
141
+ @font[:'OS/2'].x_height = 500 * @font[:head].units_per_em / 1000
142
+ @font[:'OS/2'].cap_height = 1000 * @font[:head].units_per_em / 1000
143
+ font_wrapper = HexaPDF::Font::TrueTypeWrapper.new(@doc, @font)
144
+ font_wrapper.encode(glyph)
145
+ fd = font_wrapper.pdf_object[:DescendantFonts][0][:FontDescriptor]
146
+ assert_equal(1000, fd[:CapHeight])
147
+ assert_equal(500, fd[:XHeight])
148
+ end
149
+
150
+ it "with fonts that are not subset (only differences to other case)" do
151
+ @font_wrapper = HexaPDF::Font::TrueTypeWrapper.new(@doc, @font, subset: false)
152
+ @font_wrapper.encode(@font_wrapper.glyph(3))
153
+ glyph = @font_wrapper.decode_utf8('H').first
154
+ @font_wrapper.encode(glyph)
155
+ @doc.dispatch_message(:complete_objects)
156
+
157
+ dict = @font_wrapper.pdf_object
158
+
159
+ assert_equal(HexaPDF::Font::CMap.create_to_unicode_cmap([[3, ' '.ord], [glyph.id, 'H'.ord]]),
160
+ dict[:ToUnicode].stream)
161
+ assert_equal([glyph.id, [glyph.width]], dict[:DescendantFonts][0][:W].value)
162
+ end
163
+ end
164
+
165
+ describe "font file embedding" do
166
+ it "embeds subset fonts" do
167
+ @font_wrapper.encode(@font_wrapper.glyph(10))
168
+ @doc.dispatch_message(:complete_objects)
169
+
170
+ font_data = @font_wrapper.pdf_object[:DescendantFonts][0][:FontDescriptor][:FontFile2].stream
171
+ font = HexaPDF::Font::TrueType::Font.new(StringIO.new(font_data))
172
+ assert_equal(@font[:glyf][0].raw_data, font[:glyf][0].raw_data)
173
+ assert_equal(@font[:glyf][10].raw_data, font[:glyf][1].raw_data)
174
+ end
175
+
176
+ it "embeds full fonts" do
177
+ @font_wrapper = HexaPDF::Font::TrueTypeWrapper.new(@doc, @font, subset: false)
178
+ @doc.dispatch_message(:complete_objects)
179
+
180
+ assert_equal(File.size(@font_file),
181
+ @font_wrapper.pdf_object[:DescendantFonts][0][:FontDescriptor][:FontFile2][:Length1])
182
+ assert_equal(File.binread(@font_file),
183
+ @font_wrapper.pdf_object[:DescendantFonts][0][:FontDescriptor][:FontFile2].stream)
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,107 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require_relative 'type1/common'
5
+ require 'hexapdf/font/type1_wrapper'
6
+ require 'hexapdf/document'
7
+
8
+ describe HexaPDF::Font::Type1Wrapper do
9
+ before do
10
+ @doc = HexaPDF::Document.new
11
+ @times_wrapper = HexaPDF::Font::Type1Wrapper.new(@doc, FONT_TIMES)
12
+ @symbol_wrapper = HexaPDF::Font::Type1Wrapper.new(@doc, FONT_SYMBOL)
13
+ end
14
+
15
+ it "can be used with an existing PDF object" do
16
+ font = @doc.add({Type: :Font, Subtype: :Type1, Encoding: {Differences: [65, :B]},
17
+ BaseFont: :"Times-Roman"})
18
+ wrapper = HexaPDF::Font::Type1Wrapper.new(@doc, FONT_TIMES, pdf_object: font)
19
+ assert_equal([:B, :E, :A, :S, :T], wrapper.decode_utf8("BEAST").map(&:name))
20
+ assert_equal("A", wrapper.encode(wrapper.glyph(:A)))
21
+ assert_equal("A", wrapper.encode(wrapper.glyph(:B)))
22
+ end
23
+
24
+ it "returns 1 for the scaling factor" do
25
+ assert_equal(1, @times_wrapper.scaling_factor)
26
+ end
27
+
28
+ describe "decode_utf8" do
29
+ it "returns an array of glyph objects" do
30
+ assert_equal([:T, :e, :s, :t], @times_wrapper.decode_utf8("Test").map(&:name))
31
+ end
32
+
33
+ it "falls back to the internal font encoding if the Unicode codepoint is not mapped" do
34
+ assert_equal([:Delta, :Delta], @symbol_wrapper.decode_utf8("D∆").map(&:name))
35
+ end
36
+
37
+ it "UTF-8 characters for which no glyph name exists, are mapped to InvalidGlyph objects" do
38
+ glyphs = @times_wrapper.decode_utf8("😁")
39
+ assert_equal(1, glyphs.length)
40
+ assert_kind_of(HexaPDF::Font::InvalidGlyph, glyphs.first)
41
+ assert_equal('' << 128_513, glyphs.first.str)
42
+ end
43
+ end
44
+
45
+ describe "glyph" do
46
+ it "returns the glyph object for the given name" do
47
+ glyph = @times_wrapper.glyph(:A)
48
+ assert_equal(:A, glyph.name)
49
+ assert_equal("A", glyph.str)
50
+ assert_equal(722, glyph.width)
51
+ assert_equal(15, glyph.x_min)
52
+ assert_equal(0, glyph.y_min)
53
+ assert_equal(706, glyph.x_max)
54
+ assert_equal(674, glyph.y_max)
55
+ refute(glyph.apply_word_spacing?)
56
+ assert_equal('#<HexaPDF::Font::Type1Wrapper::Glyph font="Times Roman" id=:A "A">',
57
+ glyph.inspect)
58
+ end
59
+
60
+ it "invokes font.on_missing_glyph for missing glyphs" do
61
+ glyph = @times_wrapper.glyph(:ffi)
62
+ assert_kind_of(HexaPDF::Font::InvalidGlyph, glyph)
63
+ assert_equal(:'.notdef', glyph.name)
64
+ assert_equal('ffi', glyph.str)
65
+ end
66
+ end
67
+
68
+ describe "encode" do
69
+ describe "uses WinAnsiEncoding as initial encoding for non-symbolic fonts" do
70
+ it "returns the PDF font dictionary using WinAnsiEncoding and encoded glyph" do
71
+ code = @times_wrapper.encode(@times_wrapper.glyph(:a))
72
+ @doc.dispatch_message(:complete_objects)
73
+ assert_equal("a", code)
74
+ assert_equal(:WinAnsiEncoding, @times_wrapper.pdf_object[:Encoding])
75
+ end
76
+
77
+ it "fails if an InvalidGlyph is encoded" do
78
+ assert_raises(HexaPDF::Error) { @times_wrapper.encode(@times_wrapper.glyph(:ffi)) }
79
+ end
80
+
81
+ it "fails if the encoding does not support the given glyph" do
82
+ assert_raises(HexaPDF::Error) { @times_wrapper.encode(@times_wrapper.glyph(:uring)) }
83
+ end
84
+ end
85
+
86
+ it "uses the font's internal encoding for fonts with the Special character set" do
87
+ code = @symbol_wrapper.encode(@symbol_wrapper.glyph(:plus))
88
+ @doc.dispatch_message(:complete_objects)
89
+ assert_equal("+", code)
90
+ assert_nil(@symbol_wrapper.pdf_object[:Encoding])
91
+ end
92
+
93
+ it "uses an empty encoding as initial encoding if a custom encoding is needed" do
94
+ wrapper = HexaPDF::Font::Type1Wrapper.new(@doc, FONT_TIMES, custom_encoding: true)
95
+ code = wrapper.encode(wrapper.glyph(:plus))
96
+ @doc.dispatch_message(:complete_objects)
97
+ assert_equal("\x21", code)
98
+ assert_equal({Differences: [32, :space, :plus]}, wrapper.pdf_object[:Encoding].value)
99
+ end
100
+ end
101
+
102
+ describe "creates the necessary PDF dictionaries" do
103
+ it "sets the circular reference" do
104
+ assert_same(@times_wrapper, @times_wrapper.pdf_object.font_wrapper)
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,17 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'hexapdf/font/true_type/table'
4
+
5
+ module TestHelper
6
+
7
+ class TrueTypeTestTable < HexaPDF::Font::TrueType::Table
8
+
9
+ attr_reader :data
10
+
11
+ def parse_table
12
+ @data = io.read(directory_entry.length)
13
+ end
14
+
15
+ end
16
+
17
+ end
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'stringio'
5
+ require 'hexapdf/font/true_type'
6
+
7
+ module TestHelper
8
+
9
+ def set_up_stub_true_type_font(initial_data = ''.b, register_vars: true)
10
+ font = Object.new
11
+ font.define_singleton_method(:io) { @io ||= StringIO.new(initial_data) }
12
+ font.define_singleton_method(:config) { @config ||= {} }
13
+ entry = HexaPDF::Font::TrueType::Table::Directory::Entry.new('mock', 0, 0, initial_data.length)
14
+ @font, @entry = font, entry if register_vars
15
+ [font, entry]
16
+ end
17
+
18
+ def create_table(name, data = nil, standalone: false)
19
+ font, entry = !standalone ? [@font, @entry] : set_up_stub_true_type_font(register_vars: false)
20
+ if data
21
+ font.io.string = data
22
+ entry.length = font.io.length
23
+ end
24
+ HexaPDF::Font::TrueType::Table.const_get(name).new(font, entry)
25
+ end
26
+
27
+ end
@@ -0,0 +1,47 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require_relative 'common'
5
+ require 'hexapdf/font/true_type/table/cmap'
6
+
7
+ describe HexaPDF::Font::TrueType::Table::Cmap do
8
+ before do
9
+ f0 = [0, 262, 0].pack('n3') + (0..255).to_a.pack('C*')
10
+ data = [0, 3].pack('n2') << [
11
+ [0, 1, 28],
12
+ [3, 1, 28 + f0.length],
13
+ [1, 0, 28],
14
+ ].map {|a| a.pack('n2N') }.join('') << f0 << [10, 22, 0, 0, 2, 10, 13].pack('nN2N2n2')
15
+ set_up_stub_true_type_font(data)
16
+ end
17
+
18
+ describe "initialize" do
19
+ it "reads the data from the associated file" do
20
+ table = create_table(:Cmap)
21
+ assert_equal(0, table.version)
22
+ assert_equal(3, table.tables.length)
23
+ end
24
+
25
+ it "ignores unknown subtable when the config option is set to :ignore" do
26
+ table = create_table(:Cmap, [0, 1].pack('n2') << [3, 1, 12].pack('n2N') << "\x00\x03")
27
+ assert_equal(0, table.tables.length)
28
+ end
29
+
30
+ it "raises an error when an unsupported subtable is found and the option is set to :raise" do
31
+ data = [0, 1].pack('n2') << [3, 1, 12].pack('n2N') << "\x00\x03"
32
+ @font.config['font.true_type.unknown_format'] = :raise
33
+ assert_raises(HexaPDF::Error) { create_table(:Cmap, data) }
34
+ end
35
+
36
+ it "loads data from subtables with identical offsets only once" do
37
+ table = create_table(:Cmap)
38
+ assert_same(table.tables[0].gid_map, table.tables[2].gid_map)
39
+ refute_same(table.tables[0].gid_map, table.tables[1].gid_map)
40
+ end
41
+ end
42
+
43
+ it "returns the preferred table" do
44
+ table = create_table(:Cmap)
45
+ assert_equal(table.tables[1], table.preferred_table)
46
+ end
47
+ end
@@ -0,0 +1,141 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'stringio'
5
+ require 'hexapdf/font/true_type/table/cmap_subtable'
6
+
7
+ describe HexaPDF::Font::TrueType::Table::CmapSubtable do
8
+ before do
9
+ @klass = HexaPDF::Font::TrueType::Table::CmapSubtable
10
+ end
11
+
12
+ describe "initialize" do
13
+ it "uses default values" do
14
+ t = @klass.new(3, 1)
15
+ assert_equal(3, t.platform_id)
16
+ assert_equal(1, t.encoding_id)
17
+ assert_nil(t.format)
18
+ assert_equal(0, t.language)
19
+ end
20
+ end
21
+
22
+ describe "unicode?" do
23
+ it "identifies (0,x), (3,1) and (3,10) as unicode" do
24
+ assert(@klass.new(0, 1).unicode?)
25
+ assert(@klass.new(0, 5).unicode?)
26
+ assert(@klass.new(3, 1).unicode?)
27
+ assert(@klass.new(3, 10).unicode?)
28
+ refute(@klass.new(1, 0).unicode?)
29
+ refute(@klass.new(3, 0).unicode?)
30
+ end
31
+ end
32
+
33
+ describe "inspect" do
34
+ it "can represent itself nicely" do
35
+ assert_equal("#<#{@klass.name} (3, 1, 0, nil)>", @klass.new(3, 1).inspect)
36
+ end
37
+ end
38
+
39
+ describe "parse" do
40
+ def table(data)
41
+ @klass.new(3, 1).tap {|obj| obj.parse(StringIO.new(data), 0) }
42
+ end
43
+
44
+ it "works for format 0" do
45
+ t = table([0, 262, 0].pack('n3') + [255].pack('C*') + (0..254).to_a.pack('C*'))
46
+ assert_equal(0, t.gid_to_code(255))
47
+ assert_equal(234, t.gid_to_code(233))
48
+
49
+ assert_equal(255, t[0])
50
+ assert_equal(233, t[234])
51
+ assert_nil(t[256])
52
+
53
+ assert_raises(HexaPDF::Error) { table([0, 20, 0].pack('n3') + "a" * 20)[0] }
54
+ end
55
+
56
+ it "works for format 2" do
57
+ f2 = ([0, 8] + [0] * 254).pack('n*') + \
58
+ [[0, 256, 0, 2 + 8], [0x33, 3, 5, 2 + 256 * 2]].map {|a| a.pack('n2s>n') }.join('') + \
59
+ ((0..255).to_a + [35, 65534, 0]).pack('n*')
60
+ t = table([2, f2.length + 6, 0].pack('n3') << f2)
61
+ assert_nil(t[0x0132])
62
+ assert_equal(40, t[0x0133])
63
+ assert_equal(3, t[0x0134])
64
+ assert_nil(t[0x0135])
65
+ assert_nil(t[0x0136])
66
+ assert_equal(16, t[0x1036])
67
+ assert_nil(t[0xfffff])
68
+
69
+ assert_equal(0x0133, t.gid_to_code(40))
70
+ assert_equal(0x0134, t.gid_to_code(3))
71
+ assert_equal(16, t.gid_to_code(16))
72
+ end
73
+
74
+ it "works for format 4" do
75
+ f4 = [6, 8, 2, 4, 35, 134, 65535, 0, 30, 133, 65535, 100, 110, 120,
76
+ 0, 6, 2, 65500, 0, 1].pack('n*')
77
+ t = table([4, f4.length + 6, 0].pack('n3') << f4)
78
+ assert_nil(t[25])
79
+ assert_equal(130, t[30])
80
+ assert_equal(133, t[33])
81
+ assert_equal(135, t[35])
82
+ assert_nil(t[36])
83
+ assert_nil(t[133])
84
+ assert_equal(111, t[134])
85
+ assert_equal(84, t[65535])
86
+ assert_nil(t[70000])
87
+
88
+ assert_equal(30, t.gid_to_code(130))
89
+ assert_equal(33, t.gid_to_code(133))
90
+ assert_equal(35, t.gid_to_code(135))
91
+ assert_equal(134, t.gid_to_code(111))
92
+ assert_equal(65535, t.gid_to_code(84))
93
+ end
94
+
95
+ it "works for format 4 with invalid 0xffff entry" do
96
+ f4 = [2, 0, 0, 0, 65535, 0, 65535, 0, 32767].pack('n*')
97
+ t = table([4, f4.length + 6, 0].pack('n3') << f4)
98
+ assert_nil(t[65535])
99
+ assert_equal(65535, t.gid_to_code(0))
100
+ end
101
+
102
+ it "works for format 6" do
103
+ t = table(([6, 30, 0, 1024, 10] + (1..10).to_a).pack('n*'))
104
+ assert_nil(t[0])
105
+ assert_equal(1, t[1024])
106
+ assert_equal(7, t[1030])
107
+ assert_equal(10, t[1033])
108
+ assert_nil(t[1034])
109
+
110
+ assert_equal(1033, t.gid_to_code(10))
111
+ end
112
+
113
+ it "works for format 10" do
114
+ t = table([10, 0].pack('n2') + [40, 0, 1024, 10].pack('N*') + (1..10).to_a.pack('n*'))
115
+ assert_nil(t[234])
116
+ assert_equal(1, t[1024])
117
+ assert_equal(7, t[1030])
118
+ assert_equal(10, t[1033])
119
+ assert_nil(t[1034])
120
+
121
+ assert_equal(1033, t.gid_to_code(10))
122
+ end
123
+
124
+ it "works for format 12" do
125
+ t = table([12, 0].pack('n2') + [40, 0, 2, 35, 38, 1000, 70000, 90000, 80000].pack('N*'))
126
+ assert_nil(t[30])
127
+ assert_equal(1000, t[35])
128
+ assert_equal(1003, t[38])
129
+ assert_equal(100_000, t[90_000])
130
+ assert_nil(t[90_001])
131
+
132
+ assert_equal(35, t.gid_to_code(1000))
133
+ assert_equal(38, t.gid_to_code(1003))
134
+ assert_equal(90_000, t.gid_to_code(100_000))
135
+ end
136
+
137
+ it "returns false if a format is not supported" do
138
+ refute(@klass.new(3, 1).parse(StringIO.new("\x00\x03".b), 0))
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/font/true_type/table/directory'
5
+
6
+ describe HexaPDF::Font::TrueType::Table::Directory do
7
+ before do
8
+ io = StringIO.new("TEST\x00\x01\x00\x00\x00\x00\x00\x00" \
9
+ "CUST----\x00\x00\x00\x1C\x00\x00\x00\x05ENTRY".b)
10
+ @file = Object.new
11
+ @file.define_singleton_method(:io) { io }
12
+ @self_entry = HexaPDF::Font::TrueType::Table::Directory::SELF_ENTRY
13
+ end
14
+
15
+ it "has a dummy entry referring to itself" do
16
+ assert_equal(0, @self_entry.offset)
17
+ assert_equal(12, @self_entry.length)
18
+ end
19
+
20
+ describe "initialize" do
21
+ it "loads the table entries from the IO" do
22
+ dir = HexaPDF::Font::TrueType::Table::Directory.new(@file, @self_entry)
23
+ entry = dir.entry('CUST')
24
+ assert_equal('CUST', entry.tag)
25
+ assert_equal('----'.unpack1('N'), entry.checksum)
26
+ assert_equal(28, entry.offset)
27
+ assert_equal(5, entry.length)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,58 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require_relative 'common'
5
+ require 'hexapdf/font/true_type/table/glyf'
6
+
7
+ describe HexaPDF::Font::TrueType::Table::Glyf do
8
+ before do
9
+ loca = Object.new
10
+ loca.define_singleton_method(:offsets) { @offsets ||= [] }
11
+ loca.define_singleton_method(:offset) {|i| @offsets[i] }
12
+ loca.define_singleton_method(:length) {|i| @offsets[i + 1] - @offsets[i] }
13
+ loca.offsets << 0 << 0
14
+ data = [1, -10, -20, 100, 150].pack('s>5')
15
+ loca.offsets << data.size
16
+ data << [-1, 10, 20, -100, -150].pack('s>5')
17
+ data << [0b00100000, 1, 20, 30].pack('n2C2')
18
+ data << [0b00101001, 2, 20, 30, 40].pack('n2n2n')
19
+ data << [0b01100001, 3, 20, 30, 40, 50].pack('n2n2n2')
20
+ data << [0b10100001, 4, 20, 30, 40, 50, 60, 70].pack('n2n2n4')
21
+ data << [0b00000000, 1, 20, 30].pack('n2C2')
22
+ loca.offsets << data.size
23
+ set_up_stub_true_type_font(data)
24
+ @font.define_singleton_method(:[]) {|_arg| loca }
25
+ end
26
+
27
+ describe "initialize" do
28
+ it "reads the data from the associated file" do
29
+ table = create_table(:Glyf)
30
+ glyph = table[0]
31
+ refute(glyph.compound?)
32
+ assert_equal(0, glyph.number_of_contours)
33
+ assert_equal(0, glyph.x_min)
34
+ assert_equal(0, glyph.y_min)
35
+ assert_equal(0, glyph.x_max)
36
+ assert_equal(0, glyph.y_max)
37
+
38
+ glyph = table[1]
39
+ refute(glyph.compound?)
40
+ assert_equal(1, glyph.number_of_contours)
41
+ assert_equal(-10, glyph.x_min)
42
+ assert_equal(-20, glyph.y_min)
43
+ assert_equal(100, glyph.x_max)
44
+ assert_equal(150, glyph.y_max)
45
+ assert_same(glyph, table[1])
46
+
47
+ glyph = table[2]
48
+ assert(glyph.compound?)
49
+ assert_equal(-1, glyph.number_of_contours)
50
+ assert_equal(10, glyph.x_min)
51
+ assert_equal(20, glyph.y_min)
52
+ assert_equal(-100, glyph.x_max)
53
+ assert_equal(-150, glyph.y_max)
54
+ assert_equal([1, 2, 3, 4, 1], glyph.components)
55
+ assert_equal([12, 18, 28, 40, 56], glyph.component_offsets)
56
+ end
57
+ end
58
+ end