hexapdf 0.17.1 → 0.17.2

Sign up to get free protection for your applications and to get access to all the features.
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,140 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require_relative '../content/common'
5
+ require 'hexapdf/document'
6
+ require 'hexapdf/layout/text_box'
7
+
8
+ describe HexaPDF::Layout::TextBox do
9
+ before do
10
+ @frame = HexaPDF::Layout::Frame.new(0, 0, 100, 100)
11
+ @inline_box = HexaPDF::Layout::InlineBox.create(width: 10, height: 10) {}
12
+ end
13
+
14
+ def create_box(items, **kwargs)
15
+ HexaPDF::Layout::TextBox.new(items, **kwargs)
16
+ end
17
+
18
+ describe "initialize" do
19
+ it "takes the inline items to be layed out in the box" do
20
+ box = create_box([], width: 100)
21
+ assert_equal(100, box.width)
22
+ end
23
+ end
24
+
25
+ describe "fit" do
26
+ it "fits into a rectangular area" do
27
+ box = create_box([@inline_box] * 5, style: {padding: 10})
28
+ assert(box.fit(100, 100, @frame))
29
+ assert_equal(70, box.width)
30
+ assert_equal(30, box.height)
31
+ end
32
+
33
+ it "respects the set width and height" do
34
+ box = create_box([@inline_box], width: 40, height: 50, style: {padding: 10})
35
+ assert(box.fit(100, 100, @frame))
36
+ assert_equal(40, box.width)
37
+ assert_equal(50, box.height)
38
+ assert_equal([10], box.instance_variable_get(:@result).lines.map(&:width))
39
+ end
40
+
41
+ it "fits into the frame's outline" do
42
+ inline_box = HexaPDF::Layout::InlineBox.create(width: 10, height: 10) {}
43
+ box = create_box([inline_box] * 20, style: {position: :flow})
44
+ assert(box.fit(100, 100, @frame))
45
+ assert_equal(100, box.width)
46
+ assert_equal(20, box.height)
47
+ end
48
+
49
+ it "takes the style option last_line_gap into account" do
50
+ box = create_box([@inline_box] * 5, style: {last_line_gap: true, line_spacing: :double})
51
+ assert(box.fit(100, 100, @frame))
52
+ assert_equal(50, box.width)
53
+ assert_equal(20, box.height)
54
+ end
55
+
56
+ it "can't fit the text box if the set width is bigger than the available width" do
57
+ box = create_box([@inline_box], width: 101)
58
+ refute(box.fit(100, 100, @frame))
59
+ end
60
+
61
+ it "can't fit the text box if the set height is bigger than the available height" do
62
+ box = create_box([@inline_box], height: 101)
63
+ refute(box.fit(100, 100, @frame))
64
+ end
65
+ end
66
+
67
+ describe "split" do
68
+ it "works for an empty text box" do
69
+ box = create_box([])
70
+ assert_equal([box], box.split(100, 100, @frame))
71
+ end
72
+
73
+ it "doesn't need to split the box if it completely fits" do
74
+ box = create_box([@inline_box] * 5)
75
+ assert_equal([box], box.split(50, 100, @frame))
76
+ end
77
+
78
+ it "works if no item of the text box fits" do
79
+ box = create_box([@inline_box])
80
+ assert_equal([nil, box], box.split(5, 20, @frame))
81
+ end
82
+
83
+ it "works if the whole text box doesn't fits" do
84
+ box = create_box([@inline_box], width: 102)
85
+ assert_equal([nil, box], box.split(100, 100, @frame))
86
+
87
+ box = create_box([@inline_box], height: 102)
88
+ assert_equal([nil, box], box.split(100, 100, @frame))
89
+ end
90
+
91
+ it "splits the box if necessary" do
92
+ box = create_box([@inline_box] * 10)
93
+ boxes = box.split(50, 10, @frame)
94
+ assert_equal(2, boxes.length)
95
+ assert_equal(box, boxes[0])
96
+ assert_equal(5, boxes[1].instance_variable_get(:@items).length)
97
+ end
98
+ end
99
+
100
+ describe "draw" do
101
+ it "draws the layed out inline items onto the canvas" do
102
+ inline_box = HexaPDF::Layout::InlineBox.create(width: 10, height: 10,
103
+ border: {width: 1})
104
+ box = create_box([inline_box], width: 100, height: 30, style: {padding: [10, 5]})
105
+ box.fit(100, 100, nil)
106
+
107
+ @canvas = HexaPDF::Document.new.pages.add.canvas
108
+ box.draw(@canvas, 0, 0)
109
+ assert_operators(@canvas.contents, [[:save_graphics_state],
110
+ [:restore_graphics_state],
111
+ [:save_graphics_state],
112
+ [:append_rectangle, [5, 10, 10, 10]],
113
+ [:clip_path_non_zero],
114
+ [:end_path],
115
+ [:append_rectangle, [5.5, 10.5, 9, 9]],
116
+ [:stroke_path],
117
+ [:restore_graphics_state],
118
+ [:save_graphics_state],
119
+ [:restore_graphics_state]])
120
+ end
121
+
122
+ it "draws nothing onto the canvas if the box is empty" do
123
+ @canvas = HexaPDF::Document.new.pages.add.canvas
124
+ box = create_box([])
125
+ box.draw(@canvas, 5, 5)
126
+ assert_operators(@canvas.contents, [])
127
+ end
128
+ end
129
+
130
+ it "is empty if there is a result without any text lines" do
131
+ box = create_box([])
132
+ assert(box.empty?)
133
+ box.fit(100, 100, @frame)
134
+ assert(box.empty?)
135
+
136
+ box = create_box([@inline_box])
137
+ box.fit(100, 100, @frame)
138
+ refute(box.empty?)
139
+ end
140
+ end
@@ -0,0 +1,375 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require_relative '../content/common'
5
+ require 'hexapdf/document'
6
+ require 'hexapdf/layout/text_fragment'
7
+
8
+ # Numeric values were manually calculated using the information from the AFM file.
9
+ describe HexaPDF::Layout::TextFragment do
10
+ before do
11
+ @doc = HexaPDF::Document.new
12
+ @font = @doc.fonts.add("Times", custom_encoding: true)
13
+ end
14
+
15
+ def setup_fragment(items, text_rise = 0)
16
+ style = HexaPDF::Layout::Style.new(font: @font, font_size: 20,
17
+ horizontal_scaling: 200, character_spacing: 1,
18
+ word_spacing: 2, text_rise: text_rise)
19
+ @fragment = HexaPDF::Layout::TextFragment.new(items, style)
20
+ end
21
+
22
+ describe "create" do
23
+ it "creates a TextFragment from text and options" do
24
+ frag = HexaPDF::Layout::TextFragment.create("Tom", font: @font, font_size: 20,
25
+ font_features: {kern: true})
26
+ assert_equal(4, frag.items.length)
27
+ assert_equal(36.18, frag.width)
28
+ assert_equal(13.66 + 4.34, frag.height)
29
+ end
30
+
31
+ it "creates a TextFragment from text and a Style object" do
32
+ style = HexaPDF::Layout::Style.new(font: @font)
33
+ frag = HexaPDF::Layout::TextFragment.create("Tom", style)
34
+ assert_equal(style, frag.style)
35
+ end
36
+ end
37
+
38
+ describe "initialize" do
39
+ before do
40
+ @items = @font.decode_utf8("Tom")
41
+ end
42
+
43
+ it "can use a Style object" do
44
+ style = HexaPDF::Layout::Style.new(font: @font, font_size: 20)
45
+ frag = HexaPDF::Layout::TextFragment.new(@items, style)
46
+ assert_equal(20, frag.style.font_size)
47
+ end
48
+
49
+ it "can use style options" do
50
+ frag = HexaPDF::Layout::TextFragment.new(@items, font: @font, font_size: 20)
51
+ assert_equal(20, frag.style.font_size)
52
+ end
53
+ end
54
+
55
+ it "returns :text for valign" do
56
+ assert_equal(:text, setup_fragment([]).valign)
57
+ end
58
+
59
+ describe "draw" do
60
+ def setup_with_style(**styles)
61
+ setup_fragment(@font.decode_utf8('H'), 2)
62
+ styles.each {|name, value| @fragment.style.send(name, value) }
63
+ @canvas = @doc.pages.add.canvas
64
+ @fragment.draw(@canvas, 10, 15)
65
+ end
66
+
67
+ def assert_draw_operators(*args, front: [], middle: args, back: [])
68
+ ops = [
69
+ *front,
70
+ [:set_font_and_size, [:F1, 20]],
71
+ [:set_horizontal_scaling, [200]],
72
+ [:set_character_spacing, [1]],
73
+ [:set_word_spacing, [2]],
74
+ [:set_text_rise, [2]],
75
+ *middle,
76
+ [:begin_text],
77
+ [:set_text_matrix, [1, 0, 0, 1, 10, 15]],
78
+ [:show_text, ['!']],
79
+ *back,
80
+ ].compact
81
+ assert_operators(@canvas.contents, ops)
82
+ end
83
+
84
+ it "draws text onto the canvas" do
85
+ setup_with_style
86
+ assert_draw_operators
87
+ end
88
+
89
+ it "doesn't set the text properties if instructed to do so" do
90
+ setup_fragment([])
91
+ @canvas = @doc.pages.add.canvas
92
+ @fragment.draw(@canvas, 10, 15, ignore_text_properties: true)
93
+ assert_operators(@canvas.contents, [[:begin_text],
94
+ [:set_text_matrix, [1, 0, 0, 1, 10, 15]]])
95
+ end
96
+
97
+ describe "uses an appropriate text position setter" do
98
+ before do
99
+ setup_fragment([])
100
+ @canvas = @doc.pages.add.canvas
101
+ end
102
+
103
+ it "with text leading graphics state" do
104
+ @canvas.begin_text.leading(10)
105
+ @fragment.draw(@canvas, 0, -10, ignore_text_properties: true)
106
+ assert_operators(@canvas.contents, [[:begin_text],
107
+ [:set_leading, [10]],
108
+ [:move_text_next_line]])
109
+ end
110
+
111
+ it "only horizontal movement" do
112
+ @fragment.draw(@canvas, 20, 0, ignore_text_properties: true)
113
+ assert_operators(@canvas.contents, [[:begin_text],
114
+ [:move_text, [20, 0]]])
115
+ end
116
+
117
+ it "only vertical movement" do
118
+ @fragment.draw(@canvas, 0, 10, ignore_text_properties: true)
119
+ assert_operators(@canvas.contents, [[:begin_text],
120
+ [:move_text, [0, 10]]])
121
+ end
122
+
123
+ it "horizontal and vertical movement" do
124
+ @fragment.draw(@canvas, 10, 10, ignore_text_properties: true)
125
+ assert_operators(@canvas.contents, [[:begin_text],
126
+ [:set_text_matrix, [1, 0, 0, 1, 10, 10]]])
127
+ end
128
+ end
129
+
130
+ it "draws styled filled text" do
131
+ setup_with_style(fill_color: 0.5, fill_alpha: 0.5)
132
+ assert_draw_operators([:set_graphics_state_parameters, [:GS1]],
133
+ [:set_device_gray_non_stroking_color, [0.5]])
134
+ assert_equal({Type: :ExtGState, CA: 1, ca: 0.5}, @canvas.resources[:ExtGState][:GS1])
135
+ end
136
+
137
+ it "draws style stroked text" do
138
+ setup_with_style(text_rendering_mode: :stroke,
139
+ stroke_color: [1.0, 0, 0], stroke_alpha: 0.5, stroke_width: 2,
140
+ stroke_cap_style: :round, stroke_join_style: :round, stroke_miter_limit: 5,
141
+ stroke_dash_pattern: [1, 2, 3])
142
+ assert_draw_operators([:set_text_rendering_mode, [1]],
143
+ [:set_graphics_state_parameters, [:GS1]],
144
+ [:set_device_rgb_stroking_color, [1, 0, 0]],
145
+ [:set_line_width, [2]],
146
+ [:set_line_cap_style, [1]],
147
+ [:set_line_join_style, [1]],
148
+ [:set_miter_limit, [5]],
149
+ [:set_line_dash_pattern, [[1, 2, 3], 0]])
150
+ assert_equal({Type: :ExtGState, CA: 0.5, ca: 1}, @canvas.resources[:ExtGState][:GS1])
151
+ end
152
+
153
+ it "invokes the underlays" do
154
+ setup_with_style(underlays: [proc { @canvas.stroke_color(0.5) }])
155
+ assert_draw_operators(front: [[:save_graphics_state],
156
+ [:concatenate_matrix, [1, 0, 0, 1, 10, 15 + @fragment.y_min]],
157
+ [:save_graphics_state],
158
+ [:set_device_gray_stroking_color, [0.5]],
159
+ [:restore_graphics_state],
160
+ [:restore_graphics_state]])
161
+ end
162
+
163
+ it "invokes the overlays" do
164
+ setup_with_style(overlays: [proc { @canvas.stroke_color(0.5) }])
165
+ assert_draw_operators(back: [[:end_text],
166
+ [:save_graphics_state],
167
+ [:concatenate_matrix, [1, 0, 0, 1, 10, 15 + @fragment.y_min]],
168
+ [:save_graphics_state],
169
+ [:set_device_gray_stroking_color, [0.5]],
170
+ [:restore_graphics_state],
171
+ [:restore_graphics_state]])
172
+ end
173
+
174
+ it "draws the underline" do
175
+ setup_with_style(underline: true, text_rendering_mode: :stroke,
176
+ stroke_width: 5, stroke_color: [0.5], stroke_cap_style: :round,
177
+ stroke_dash_pattern: 5)
178
+ assert_draw_operators(middle: [[:set_text_rendering_mode, [1]],
179
+ [:set_device_gray_stroking_color, [0.5]],
180
+ [:set_line_width, [5]],
181
+ [:set_line_cap_style, [1]],
182
+ [:set_line_dash_pattern, [[5], 0]]],
183
+ back: [[:end_text],
184
+ [:save_graphics_state],
185
+ [:set_device_gray_stroking_color, [0]],
186
+ [:set_line_width, [@fragment.style.calculated_underline_thickness]],
187
+ [:set_line_cap_style, [0]],
188
+ [:set_line_dash_pattern, [[], 0]],
189
+ [:move_to, [10, 15]],
190
+ [:line_to, [40.88, 15]],
191
+ [:stroke_path],
192
+ [:restore_graphics_state]])
193
+ end
194
+
195
+ it "draws the strikeout line" do
196
+ setup_with_style(strikeout: true, text_rendering_mode: :stroke,
197
+ stroke_width: 5, stroke_color: [0.5], stroke_cap_style: :round,
198
+ stroke_dash_pattern: 5)
199
+ assert_draw_operators(middle: [[:set_text_rendering_mode, [1]],
200
+ [:set_device_gray_stroking_color, [0.5]],
201
+ [:set_line_width, [5]],
202
+ [:set_line_cap_style, [1]],
203
+ [:set_line_dash_pattern, [[5], 0]]],
204
+ back: [[:end_text],
205
+ [:save_graphics_state],
206
+ [:set_device_gray_stroking_color, [0]],
207
+ [:set_line_width, [@fragment.style.calculated_strikeout_thickness]],
208
+ [:set_line_cap_style, [0]],
209
+ [:set_line_dash_pattern, [[], 0]],
210
+ [:move_to, [10, 21.01]],
211
+ [:line_to, [40.88, 21.01]],
212
+ [:stroke_path],
213
+ [:restore_graphics_state]])
214
+ end
215
+ end
216
+
217
+ describe "empty fragment" do
218
+ before do
219
+ setup_fragment([])
220
+ end
221
+
222
+ it "calculates the x_min" do
223
+ assert_equal(0, @fragment.x_min)
224
+ end
225
+
226
+ it "calculates the x_max" do
227
+ assert_equal(0, @fragment.x_max)
228
+ end
229
+
230
+ it "calculates the y_min" do
231
+ assert_equal(-4.34, @fragment.y_min)
232
+ end
233
+
234
+ it "calculates the y_max" do
235
+ assert_equal(13.66, @fragment.y_max)
236
+ end
237
+
238
+ it "calculates the width" do
239
+ assert_equal(0, @fragment.width)
240
+ end
241
+
242
+ it "calculates the height" do
243
+ assert_equal(13.66 + 4.34, @fragment.height)
244
+ end
245
+ end
246
+
247
+ describe "normal text" do
248
+ before do
249
+ setup_fragment(@font.decode_utf8("Hal lo").insert(2, -35).insert(1, -10))
250
+ end
251
+
252
+ it "calculates the x_min" do
253
+ assert_in_delta(0.76, @fragment.x_min)
254
+ end
255
+
256
+ it "calculates the x_max" do
257
+ assert_in_delta(116.68 - 1.2 - 2, @fragment.x_max)
258
+ end
259
+
260
+ it "calculates the exact y_min" do
261
+ assert_in_delta(-0.2, @fragment.exact_y_min)
262
+ end
263
+
264
+ it "calculates the exact y_max" do
265
+ assert_in_delta(13.66, @fragment.exact_y_max)
266
+ end
267
+
268
+ it "calculates the y_min" do
269
+ assert_in_delta(-4.34, @fragment.y_min)
270
+ end
271
+
272
+ it "calculates the y_max" do
273
+ assert_in_delta(13.66, @fragment.y_max)
274
+ end
275
+
276
+ it "calculates the width" do
277
+ assert_in_delta(116.68, @fragment.width)
278
+ end
279
+
280
+ it "calculates the height" do
281
+ assert_in_delta(13.66 + 4.34, @fragment.height)
282
+ end
283
+ end
284
+
285
+ describe "with a positive text rise" do
286
+ before do
287
+ setup_fragment(@font.decode_utf8("l,"), 4)
288
+ end
289
+
290
+ it "calculates the y_min" do
291
+ assert_in_delta(-4.34 + 4, @fragment.y_min)
292
+ end
293
+
294
+ it "calculates the y_max" do
295
+ assert_in_delta(13.66 + 4, @fragment.y_max)
296
+ end
297
+
298
+ it "calculates the height" do
299
+ assert_in_delta(13.66 + 4 + 0.34, @fragment.height)
300
+ end
301
+ end
302
+
303
+ describe "with a negative text rise" do
304
+ before do
305
+ setup_fragment(@font.decode_utf8("l,"), -15)
306
+ end
307
+
308
+ it "calculates the y_min" do
309
+ assert_in_delta(-4.34 - 15, @fragment.y_min)
310
+ end
311
+
312
+ it "calculates the y_max" do
313
+ assert_in_delta(13.66 - 15, @fragment.y_max)
314
+ end
315
+
316
+ it "calculates the height" do
317
+ assert_in_delta(4.34 + 15, @fragment.height)
318
+ end
319
+ end
320
+
321
+ describe "with a glyph without outline as last item" do
322
+ before do
323
+ setup_fragment(@font.decode_utf8("H "))
324
+ end
325
+
326
+ it "calculates the x_max" do
327
+ assert_in_delta(46.88 - 2 - 4, @fragment.x_max)
328
+ end
329
+
330
+ it "calculates the width" do
331
+ assert_in_delta(46.88, @fragment.width)
332
+ end
333
+ end
334
+
335
+ describe "with a glyph with x_min < 0 and x_max > width as first and last item" do
336
+ before do
337
+ setup_fragment(@font.decode_utf8("\u{2044}o\u{2044}".unicode_normalize(:nfd)))
338
+ end
339
+
340
+ it "calculates the x_min" do
341
+ assert_in_delta(-6.72, @fragment.x_min)
342
+ end
343
+
344
+ it "calculates the x_max" do
345
+ assert_in_delta(39.36 + 6.56 - 2, @fragment.x_max)
346
+ end
347
+
348
+ it "calculates the width" do
349
+ assert_in_delta(39.36, @fragment.width)
350
+ end
351
+ end
352
+
353
+ describe "with positive kerning values as first and last items" do
354
+ before do
355
+ setup_fragment([100, 100] + @font.decode_utf8("Hallo") + [100, 100])
356
+ end
357
+
358
+ it "calculates the x_min" do
359
+ assert_in_delta(-7.24, @fragment.x_min)
360
+ end
361
+
362
+ it "calculates the x_max" do
363
+ assert_in_delta(82.88 - 1.2 - 2 - -4 - -4, @fragment.x_max)
364
+ end
365
+
366
+ it "calculates the width" do
367
+ assert_in_delta(82.88, @fragment.width)
368
+ end
369
+ end
370
+
371
+ it "can be inspected" do
372
+ frag = setup_fragment(@font.decode_utf8("H"))
373
+ assert_match(/:H/, frag.inspect)
374
+ end
375
+ end