hexapdf 0.46.0 → 1.6.0

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 (355) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +342 -16
  3. data/LICENSE +1 -1
  4. data/README.md +9 -8
  5. data/examples/009-text_layouter_alignment.rb +4 -0
  6. data/examples/010-text_layouter_inline_boxes.rb +4 -0
  7. data/examples/011-text_layouter_line_wrapping.rb +4 -0
  8. data/examples/012-text_layouter_styling.rb +9 -4
  9. data/examples/013-text_layouter_shapes.rb +5 -0
  10. data/examples/015-boxes.rb +3 -0
  11. data/examples/016-frame_automatic_box_placement.rb +3 -0
  12. data/examples/017-frame_text_flow.rb +3 -0
  13. data/examples/022-outline.rb +5 -1
  14. data/examples/{028-frame_mask_mode.rb → 028-composer_mask_mode.rb} +3 -3
  15. data/lib/hexapdf/cli/batch.rb +1 -1
  16. data/lib/hexapdf/cli/command.rb +65 -65
  17. data/lib/hexapdf/cli/debug_info.rb +98 -0
  18. data/lib/hexapdf/cli/files.rb +1 -1
  19. data/lib/hexapdf/cli/fonts.rb +1 -1
  20. data/lib/hexapdf/cli/form.rb +11 -6
  21. data/lib/hexapdf/cli/image2pdf.rb +1 -1
  22. data/lib/hexapdf/cli/images.rb +19 -4
  23. data/lib/hexapdf/cli/info.rb +1 -1
  24. data/lib/hexapdf/cli/inspect.rb +24 -8
  25. data/lib/hexapdf/cli/merge.rb +1 -1
  26. data/lib/hexapdf/cli/modify.rb +1 -2
  27. data/lib/hexapdf/cli/optimize.rb +6 -6
  28. data/lib/hexapdf/cli/split.rb +1 -1
  29. data/lib/hexapdf/cli/usage.rb +1 -1
  30. data/lib/hexapdf/cli/watermark.rb +1 -1
  31. data/lib/hexapdf/cli.rb +20 -2
  32. data/lib/hexapdf/composer.rb +22 -1
  33. data/lib/hexapdf/configuration.rb +56 -1
  34. data/lib/hexapdf/content/canvas.rb +1 -1
  35. data/lib/hexapdf/content/canvas_composer.rb +1 -1
  36. data/lib/hexapdf/content/color_space.rb +1 -1
  37. data/lib/hexapdf/content/graphic_object/arc.rb +1 -1
  38. data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +1 -1
  39. data/lib/hexapdf/content/graphic_object/geom2d.rb +1 -1
  40. data/lib/hexapdf/content/graphic_object/solid_arc.rb +1 -1
  41. data/lib/hexapdf/content/graphic_object.rb +1 -1
  42. data/lib/hexapdf/content/graphics_state.rb +2 -2
  43. data/lib/hexapdf/content/operator.rb +1 -1
  44. data/lib/hexapdf/content/parser.rb +22 -23
  45. data/lib/hexapdf/content/processor.rb +1 -1
  46. data/lib/hexapdf/content/transformation_matrix.rb +1 -1
  47. data/lib/hexapdf/content.rb +1 -1
  48. data/lib/hexapdf/data_dir.rb +1 -1
  49. data/lib/hexapdf/dictionary.rb +8 -2
  50. data/lib/hexapdf/dictionary_fields.rb +2 -2
  51. data/lib/hexapdf/digital_signature/cms_handler.rb +19 -2
  52. data/lib/hexapdf/digital_signature/handler.rb +1 -1
  53. data/lib/hexapdf/digital_signature/pkcs1_handler.rb +1 -1
  54. data/lib/hexapdf/digital_signature/signature.rb +2 -2
  55. data/lib/hexapdf/digital_signature/signatures.rb +1 -1
  56. data/lib/hexapdf/digital_signature/signing/default_handler.rb +3 -3
  57. data/lib/hexapdf/digital_signature/signing/signed_data_creator.rb +2 -2
  58. data/lib/hexapdf/digital_signature/signing/timestamp_handler.rb +25 -5
  59. data/lib/hexapdf/digital_signature/signing.rb +1 -1
  60. data/lib/hexapdf/digital_signature/verification_result.rb +1 -1
  61. data/lib/hexapdf/digital_signature.rb +1 -1
  62. data/lib/hexapdf/document/annotations.rb +220 -0
  63. data/lib/hexapdf/document/destinations.rb +1 -1
  64. data/lib/hexapdf/document/files.rb +1 -1
  65. data/lib/hexapdf/document/fonts.rb +1 -1
  66. data/lib/hexapdf/document/images.rb +1 -1
  67. data/lib/hexapdf/document/layout.rb +95 -16
  68. data/lib/hexapdf/document/metadata.rb +11 -4
  69. data/lib/hexapdf/document/pages.rb +1 -1
  70. data/lib/hexapdf/document.rb +52 -9
  71. data/lib/hexapdf/encryption/aes.rb +1 -1
  72. data/lib/hexapdf/encryption/arc4.rb +3 -3
  73. data/lib/hexapdf/encryption/fast_aes.rb +1 -1
  74. data/lib/hexapdf/encryption/fast_arc4.rb +1 -1
  75. data/lib/hexapdf/encryption/identity.rb +1 -1
  76. data/lib/hexapdf/encryption/ruby_aes.rb +1 -1
  77. data/lib/hexapdf/encryption/ruby_arc4.rb +1 -1
  78. data/lib/hexapdf/encryption/security_handler.rb +4 -2
  79. data/lib/hexapdf/encryption/standard_security_handler.rb +40 -29
  80. data/lib/hexapdf/encryption.rb +1 -1
  81. data/lib/hexapdf/error.rb +12 -4
  82. data/lib/hexapdf/filter/ascii85_decode.rb +1 -1
  83. data/lib/hexapdf/filter/ascii_hex_decode.rb +1 -1
  84. data/lib/hexapdf/filter/crypt.rb +1 -1
  85. data/lib/hexapdf/filter/encryption.rb +1 -1
  86. data/lib/hexapdf/filter/flate_decode.rb +1 -1
  87. data/lib/hexapdf/filter/lzw_decode.rb +1 -1
  88. data/lib/hexapdf/filter/pass_through.rb +1 -1
  89. data/lib/hexapdf/filter/predictor.rb +1 -1
  90. data/lib/hexapdf/filter/run_length_decode.rb +1 -1
  91. data/lib/hexapdf/filter.rb +1 -1
  92. data/lib/hexapdf/font/cmap/parser.rb +1 -1
  93. data/lib/hexapdf/font/cmap/writer.rb +59 -5
  94. data/lib/hexapdf/font/cmap.rb +18 -7
  95. data/lib/hexapdf/font/encoding/base.rb +28 -1
  96. data/lib/hexapdf/font/encoding/difference_encoding.rb +1 -1
  97. data/lib/hexapdf/font/encoding/glyph_list.rb +1 -1
  98. data/lib/hexapdf/font/encoding/mac_expert_encoding.rb +1 -1
  99. data/lib/hexapdf/font/encoding/mac_roman_encoding.rb +1 -1
  100. data/lib/hexapdf/font/encoding/standard_encoding.rb +1 -1
  101. data/lib/hexapdf/font/encoding/symbol_encoding.rb +1 -1
  102. data/lib/hexapdf/font/encoding/win_ansi_encoding.rb +1 -1
  103. data/lib/hexapdf/font/encoding/zapf_dingbats_encoding.rb +1 -1
  104. data/lib/hexapdf/font/encoding.rb +1 -1
  105. data/lib/hexapdf/font/invalid_glyph.rb +1 -1
  106. data/lib/hexapdf/font/true_type/builder.rb +1 -1
  107. data/lib/hexapdf/font/true_type/font.rb +1 -1
  108. data/lib/hexapdf/font/true_type/optimizer.rb +1 -1
  109. data/lib/hexapdf/font/true_type/subsetter.rb +1 -1
  110. data/lib/hexapdf/font/true_type/table/cmap.rb +1 -1
  111. data/lib/hexapdf/font/true_type/table/cmap_subtable.rb +1 -1
  112. data/lib/hexapdf/font/true_type/table/directory.rb +1 -1
  113. data/lib/hexapdf/font/true_type/table/glyf.rb +1 -1
  114. data/lib/hexapdf/font/true_type/table/head.rb +1 -1
  115. data/lib/hexapdf/font/true_type/table/hhea.rb +1 -1
  116. data/lib/hexapdf/font/true_type/table/hmtx.rb +1 -1
  117. data/lib/hexapdf/font/true_type/table/kern.rb +1 -1
  118. data/lib/hexapdf/font/true_type/table/loca.rb +1 -1
  119. data/lib/hexapdf/font/true_type/table/maxp.rb +1 -1
  120. data/lib/hexapdf/font/true_type/table/name.rb +1 -1
  121. data/lib/hexapdf/font/true_type/table/os2.rb +1 -1
  122. data/lib/hexapdf/font/true_type/table/post.rb +1 -1
  123. data/lib/hexapdf/font/true_type/table.rb +7 -2
  124. data/lib/hexapdf/font/true_type.rb +1 -1
  125. data/lib/hexapdf/font/true_type_wrapper.rb +51 -16
  126. data/lib/hexapdf/font/type1/afm_parser.rb +1 -1
  127. data/lib/hexapdf/font/type1/character_metrics.rb +1 -1
  128. data/lib/hexapdf/font/type1/font.rb +1 -1
  129. data/lib/hexapdf/font/type1/font_metrics.rb +1 -1
  130. data/lib/hexapdf/font/type1/pfb_parser.rb +1 -1
  131. data/lib/hexapdf/font/type1.rb +1 -1
  132. data/lib/hexapdf/font/type1_wrapper.rb +3 -4
  133. data/lib/hexapdf/font_loader/from_configuration.rb +1 -1
  134. data/lib/hexapdf/font_loader/from_file.rb +1 -1
  135. data/lib/hexapdf/font_loader/standard14.rb +1 -1
  136. data/lib/hexapdf/font_loader/variant_from_name.rb +1 -1
  137. data/lib/hexapdf/font_loader.rb +1 -1
  138. data/lib/hexapdf/image_loader/jpeg.rb +1 -1
  139. data/lib/hexapdf/image_loader/pdf.rb +1 -1
  140. data/lib/hexapdf/image_loader/png.rb +1 -1
  141. data/lib/hexapdf/image_loader.rb +1 -1
  142. data/lib/hexapdf/importer.rb +2 -2
  143. data/lib/hexapdf/layout/box.rb +6 -1
  144. data/lib/hexapdf/layout/box_fitter.rb +1 -1
  145. data/lib/hexapdf/layout/column_box.rb +1 -1
  146. data/lib/hexapdf/layout/container_box.rb +64 -29
  147. data/lib/hexapdf/layout/frame.rb +1 -1
  148. data/lib/hexapdf/layout/image_box.rb +1 -1
  149. data/lib/hexapdf/layout/inline_box.rb +1 -1
  150. data/lib/hexapdf/layout/line.rb +1 -1
  151. data/lib/hexapdf/layout/list_box.rb +1 -1
  152. data/lib/hexapdf/layout/numeric_refinements.rb +1 -1
  153. data/lib/hexapdf/layout/page_style.rb +1 -1
  154. data/lib/hexapdf/layout/style.rb +133 -22
  155. data/lib/hexapdf/layout/table_box.rb +86 -14
  156. data/lib/hexapdf/layout/text_box.rb +1 -1
  157. data/lib/hexapdf/layout/text_fragment.rb +13 -2
  158. data/lib/hexapdf/layout/text_layouter.rb +1 -1
  159. data/lib/hexapdf/layout/text_shaper.rb +1 -1
  160. data/lib/hexapdf/layout/width_from_polygon.rb +1 -1
  161. data/lib/hexapdf/layout.rb +1 -1
  162. data/lib/hexapdf/name_tree_node.rb +1 -1
  163. data/lib/hexapdf/number_tree_node.rb +1 -1
  164. data/lib/hexapdf/object.rb +4 -4
  165. data/lib/hexapdf/parser.rb +36 -7
  166. data/lib/hexapdf/pdf_array.rb +26 -4
  167. data/lib/hexapdf/rectangle.rb +1 -1
  168. data/lib/hexapdf/reference.rb +2 -2
  169. data/lib/hexapdf/revision.rb +7 -3
  170. data/lib/hexapdf/revisions.rb +1 -1
  171. data/lib/hexapdf/serializer.rb +8 -8
  172. data/lib/hexapdf/stream.rb +1 -1
  173. data/lib/hexapdf/task/dereference.rb +1 -1
  174. data/lib/hexapdf/task/merge_acro_form.rb +164 -0
  175. data/lib/hexapdf/task/optimize.rb +5 -5
  176. data/lib/hexapdf/task/pdfa.rb +1 -1
  177. data/lib/hexapdf/task.rb +2 -1
  178. data/lib/hexapdf/test_utils.rb +3 -2
  179. data/lib/hexapdf/tokenizer.rb +52 -44
  180. data/lib/hexapdf/type/acro_form/appearance_generator.rb +66 -13
  181. data/lib/hexapdf/type/acro_form/button_field.rb +1 -1
  182. data/lib/hexapdf/type/acro_form/choice_field.rb +1 -1
  183. data/lib/hexapdf/type/acro_form/field.rb +6 -2
  184. data/lib/hexapdf/type/acro_form/form.rb +23 -32
  185. data/lib/hexapdf/type/acro_form/java_script_actions.rb +10 -3
  186. data/lib/hexapdf/type/acro_form/signature_field.rb +19 -8
  187. data/lib/hexapdf/type/acro_form/text_field.rb +10 -3
  188. data/lib/hexapdf/type/acro_form/variable_text_field.rb +13 -5
  189. data/lib/hexapdf/type/acro_form.rb +1 -1
  190. data/lib/hexapdf/type/action.rb +1 -1
  191. data/lib/hexapdf/type/actions/go_to.rb +2 -1
  192. data/lib/hexapdf/type/actions/go_to_r.rb +2 -1
  193. data/lib/hexapdf/type/actions/launch.rb +6 -2
  194. data/lib/hexapdf/type/actions/set_ocg_state.rb +1 -1
  195. data/lib/hexapdf/type/actions/uri.rb +1 -1
  196. data/lib/hexapdf/type/actions.rb +1 -1
  197. data/lib/hexapdf/type/annotation.rb +78 -3
  198. data/lib/hexapdf/type/annotations/appearance_generator.rb +426 -0
  199. data/lib/hexapdf/type/annotations/border_effect.rb +99 -0
  200. data/lib/hexapdf/type/annotations/border_styling.rb +160 -0
  201. data/lib/hexapdf/type/annotations/circle.rb +65 -0
  202. data/lib/hexapdf/type/annotations/interior_color.rb +84 -0
  203. data/lib/hexapdf/type/annotations/line.rb +334 -0
  204. data/lib/hexapdf/type/annotations/line_ending_styling.rb +208 -0
  205. data/lib/hexapdf/type/annotations/link.rb +1 -1
  206. data/lib/hexapdf/type/annotations/markup_annotation.rb +15 -3
  207. data/lib/hexapdf/type/annotations/polygon.rb +64 -0
  208. data/lib/hexapdf/type/annotations/polygon_polyline.rb +109 -0
  209. data/lib/hexapdf/type/annotations/polyline.rb +64 -0
  210. data/lib/hexapdf/type/annotations/square.rb +65 -0
  211. data/lib/hexapdf/type/annotations/square_circle.rb +77 -0
  212. data/lib/hexapdf/type/annotations/text.rb +1 -1
  213. data/lib/hexapdf/type/annotations/widget.rb +56 -118
  214. data/lib/hexapdf/type/annotations.rb +13 -1
  215. data/lib/hexapdf/type/catalog.rb +5 -2
  216. data/lib/hexapdf/type/cid_font.rb +6 -3
  217. data/lib/hexapdf/type/cmap.rb +58 -0
  218. data/lib/hexapdf/type/embedded_file.rb +1 -1
  219. data/lib/hexapdf/type/file_specification.rb +18 -15
  220. data/lib/hexapdf/type/font.rb +1 -1
  221. data/lib/hexapdf/type/font_descriptor.rb +5 -4
  222. data/lib/hexapdf/type/font_simple.rb +4 -2
  223. data/lib/hexapdf/type/font_true_type.rb +3 -1
  224. data/lib/hexapdf/type/font_type0.rb +2 -2
  225. data/lib/hexapdf/type/font_type1.rb +19 -1
  226. data/lib/hexapdf/type/font_type3.rb +1 -2
  227. data/lib/hexapdf/type/form.rb +8 -5
  228. data/lib/hexapdf/type/graphics_state_parameter.rb +8 -5
  229. data/lib/hexapdf/type/icon_fit.rb +1 -1
  230. data/lib/hexapdf/type/image.rb +9 -5
  231. data/lib/hexapdf/type/info.rb +3 -3
  232. data/lib/hexapdf/type/mark_information.rb +3 -3
  233. data/lib/hexapdf/type/marked_content_reference.rb +59 -0
  234. data/lib/hexapdf/type/measure.rb +57 -0
  235. data/lib/hexapdf/type/metadata.rb +1 -1
  236. data/lib/hexapdf/type/names.rb +1 -1
  237. data/lib/hexapdf/type/namespace.rb +57 -0
  238. data/lib/hexapdf/type/object_reference.rb +57 -0
  239. data/lib/hexapdf/type/object_stream.rb +1 -1
  240. data/lib/hexapdf/type/optional_content_configuration.rb +2 -2
  241. data/lib/hexapdf/type/optional_content_group.rb +1 -1
  242. data/lib/hexapdf/type/optional_content_membership.rb +2 -2
  243. data/lib/hexapdf/type/optional_content_properties.rb +1 -1
  244. data/lib/hexapdf/type/outline.rb +1 -1
  245. data/lib/hexapdf/type/outline_item.rb +1 -1
  246. data/lib/hexapdf/type/output_intent.rb +1 -1
  247. data/lib/hexapdf/type/page.rb +6 -4
  248. data/lib/hexapdf/type/page_label.rb +1 -1
  249. data/lib/hexapdf/type/page_tree_node.rb +1 -1
  250. data/lib/hexapdf/type/resources.rb +13 -9
  251. data/lib/hexapdf/type/struct_elem.rb +72 -0
  252. data/lib/hexapdf/type/struct_tree_root.rb +64 -0
  253. data/lib/hexapdf/type/trailer.rb +1 -1
  254. data/lib/hexapdf/type/viewer_preferences.rb +5 -4
  255. data/lib/hexapdf/type/xref_stream.rb +1 -1
  256. data/lib/hexapdf/type.rb +8 -1
  257. data/lib/hexapdf/utils/bit_field.rb +1 -1
  258. data/lib/hexapdf/utils/bit_stream.rb +1 -1
  259. data/lib/hexapdf/utils/graphics_helpers.rb +1 -1
  260. data/lib/hexapdf/utils/lru_cache.rb +1 -1
  261. data/lib/hexapdf/utils/math_helpers.rb +1 -1
  262. data/lib/hexapdf/utils/object_hash.rb +1 -1
  263. data/lib/hexapdf/utils/pdf_doc_encoding.rb +1 -1
  264. data/lib/hexapdf/utils/sorted_tree_node.rb +17 -4
  265. data/lib/hexapdf/utils.rb +1 -1
  266. data/lib/hexapdf/version.rb +2 -2
  267. data/lib/hexapdf/writer.rb +3 -2
  268. data/lib/hexapdf/xref_section.rb +25 -6
  269. data/lib/hexapdf.rb +1 -1
  270. data/test/data/standard-security-handler/bothpwd-aes-256bit-V5-R5.pdf +43 -0
  271. data/test/data/standard-security-handler/nopwd-aes-256bit-V5-R5.pdf +44 -0
  272. data/test/data/standard-security-handler/ownerpwd-aes-256bit-V5-R5.pdf +43 -0
  273. data/test/data/standard-security-handler/userpwd-aes-256bit-V5-R5.pdf +0 -0
  274. data/test/hexapdf/common_tokenizer_tests.rb +7 -7
  275. data/test/hexapdf/content/test_graphics_state.rb +2 -3
  276. data/test/hexapdf/content/test_operator.rb +4 -5
  277. data/test/hexapdf/digital_signature/common.rb +6 -1
  278. data/test/hexapdf/digital_signature/signing/test_default_handler.rb +6 -1
  279. data/test/hexapdf/digital_signature/signing/test_timestamp_handler.rb +12 -0
  280. data/test/hexapdf/digital_signature/test_cms_handler.rb +25 -15
  281. data/test/hexapdf/digital_signature/test_handler.rb +2 -3
  282. data/test/hexapdf/digital_signature/test_pkcs1_handler.rb +1 -2
  283. data/test/hexapdf/digital_signature/test_signature.rb +7 -0
  284. data/test/hexapdf/digital_signature/test_signatures.rb +12 -7
  285. data/test/hexapdf/document/test_annotations.rb +75 -0
  286. data/test/hexapdf/document/test_layout.rb +38 -10
  287. data/test/hexapdf/document/test_metadata.rb +13 -1
  288. data/test/hexapdf/encryption/common.rb +1 -1
  289. data/test/hexapdf/encryption/test_aes.rb +1 -1
  290. data/test/hexapdf/encryption/test_arc4.rb +2 -2
  291. data/test/hexapdf/encryption/test_security_handler.rb +8 -6
  292. data/test/hexapdf/encryption/test_standard_security_handler.rb +7 -3
  293. data/test/hexapdf/filter/test_ascii85_decode.rb +1 -1
  294. data/test/hexapdf/filter/test_ascii_hex_decode.rb +1 -1
  295. data/test/hexapdf/filter/test_flate_decode.rb +2 -3
  296. data/test/hexapdf/font/cmap/test_writer.rb +73 -16
  297. data/test/hexapdf/font/encoding/test_base.rb +20 -0
  298. data/test/hexapdf/font/encoding/test_glyph_list.rb +1 -1
  299. data/test/hexapdf/font/test_true_type_wrapper.rb +31 -5
  300. data/test/hexapdf/font/test_type1_wrapper.rb +8 -1
  301. data/test/hexapdf/font/true_type/test_table.rb +12 -0
  302. data/test/hexapdf/layout/test_box.rb +8 -2
  303. data/test/hexapdf/layout/test_container_box.rb +34 -6
  304. data/test/hexapdf/layout/test_list_box.rb +7 -7
  305. data/test/hexapdf/layout/test_page_style.rb +1 -1
  306. data/test/hexapdf/layout/test_style.rb +46 -12
  307. data/test/hexapdf/layout/test_table_box.rb +66 -16
  308. data/test/hexapdf/layout/test_text_box.rb +0 -6
  309. data/test/hexapdf/layout/test_text_fragment.rb +3 -3
  310. data/test/hexapdf/layout/test_text_layouter.rb +4 -2
  311. data/test/hexapdf/task/test_merge_acro_form.rb +104 -0
  312. data/test/hexapdf/task/test_optimize.rb +3 -1
  313. data/test/hexapdf/test_composer.rb +15 -0
  314. data/test/hexapdf/test_dictionary.rb +15 -0
  315. data/test/hexapdf/test_dictionary_fields.rb +1 -0
  316. data/test/hexapdf/test_document.rb +26 -8
  317. data/test/hexapdf/test_filter.rb +1 -1
  318. data/test/hexapdf/test_importer.rb +7 -0
  319. data/test/hexapdf/test_object.rb +1 -1
  320. data/test/hexapdf/test_parser.rb +87 -18
  321. data/test/hexapdf/test_pdf_array.rb +36 -3
  322. data/test/hexapdf/test_revision.rb +27 -6
  323. data/test/hexapdf/test_revisions.rb +1 -1
  324. data/test/hexapdf/test_serializer.rb +4 -4
  325. data/test/hexapdf/test_stream.rb +1 -2
  326. data/test/hexapdf/test_tokenizer.rb +1 -1
  327. data/test/hexapdf/test_writer.rb +22 -8
  328. data/test/hexapdf/test_xref_section.rb +15 -0
  329. data/test/hexapdf/type/acro_form/test_appearance_generator.rb +118 -26
  330. data/test/hexapdf/type/acro_form/test_button_field.rb +7 -6
  331. data/test/hexapdf/type/acro_form/test_field.rb +10 -0
  332. data/test/hexapdf/type/acro_form/test_form.rb +32 -9
  333. data/test/hexapdf/type/acro_form/test_java_script_actions.rb +21 -0
  334. data/test/hexapdf/type/acro_form/test_signature_field.rb +3 -1
  335. data/test/hexapdf/type/acro_form/test_text_field.rb +7 -1
  336. data/test/hexapdf/type/acro_form/test_variable_text_field.rb +14 -1
  337. data/test/hexapdf/type/actions/test_launch.rb +6 -2
  338. data/test/hexapdf/type/annotations/test_appearance_generator.rb +608 -0
  339. data/test/hexapdf/type/annotations/test_border_effect.rb +59 -0
  340. data/test/hexapdf/type/annotations/test_border_styling.rb +114 -0
  341. data/test/hexapdf/type/annotations/test_interior_color.rb +37 -0
  342. data/test/hexapdf/type/annotations/test_line.rb +144 -0
  343. data/test/hexapdf/type/annotations/test_line_ending_styling.rb +42 -0
  344. data/test/hexapdf/type/annotations/test_polygon_polyline.rb +29 -0
  345. data/test/hexapdf/type/annotations/test_widget.rb +47 -81
  346. data/test/hexapdf/type/test_annotation.rb +58 -0
  347. data/test/hexapdf/type/test_font_type1.rb +20 -1
  348. data/test/hexapdf/type/test_form.rb +7 -1
  349. data/test/hexapdf/type/test_image.rb +1 -1
  350. data/test/hexapdf/type/test_page.rb +7 -1
  351. data/test/hexapdf/type/test_page_tree_node.rb +2 -2
  352. data/test/hexapdf/type/test_resources.rb +3 -1
  353. data/test/hexapdf/utils/test_sorted_tree_node.rb +18 -7
  354. data/test/test_helper.rb +7 -0
  355. metadata +69 -9
@@ -5,7 +5,7 @@ require 'hexapdf/font/cmap/writer'
5
5
 
6
6
  describe HexaPDF::Font::CMap::Writer do
7
7
  before do
8
- @cmap_data = <<~EOF
8
+ @to_unicode_cmap_data = +<<~EOF
9
9
  /CIDInit /ProcSet findresource begin
10
10
  12 dict begin
11
11
  begincmap
@@ -32,35 +32,92 @@ describe HexaPDF::Font::CMap::Writer do
32
32
  end
33
33
  end
34
34
  EOF
35
- @mapping = []
36
- 0x00.upto(0x5e) {|i| @mapping << [i, 0x20 + i] }
37
- @mapping << [0x60, 0x90]
38
- 0x1379.upto(0x137B) {|i| @mapping << [i, 0x90FE + i - 0x1379] }
39
- @mapping << [0x3A51, 0x2003E]
35
+ @cid_cmap_data = +<<~EOF
36
+ %!PS-Adobe-3.0 Resource-CMap
37
+ %%DocumentNeededResources: ProcSet (CIDInit)
38
+ %%IncludeResource: ProcSet (CIDInit)
39
+ %%BeginResource: CMap (Custom)
40
+ %%Title: (Custom Adobe Identity 0)
41
+ %%Version: 1
42
+ /CIDInit /ProcSet findresource begin
43
+ 12 dict begin
44
+ begincmap
45
+ /CIDSystemInfo 3 dict dup begin
46
+ /Registry (Adobe) def
47
+ /Ordering (Identity) def
48
+ /Supplement 0 def
49
+ end def
50
+ /CMapName /Custom def
51
+ /CMapType 1 def
52
+ /CMapVersion 1 def
53
+ /WMode 0 def
54
+ 1 begincodespacerange
55
+ <0000> <FFFF>
56
+ endcodespacerange
57
+ 1 begincidchar
58
+ <0060> 144
59
+ endcidchar
60
+ 1 begincidrange
61
+ <0000><005E> 32
62
+ endcidrange
63
+ endcmap
64
+ CMapName currentdict /CMap defineresource pop
65
+ end
66
+ end
67
+ %%EndResource
68
+ %%EOF
69
+ EOF
70
+
71
+ @to_unicode_mapping = []
72
+ @cid_mapping = []
73
+ 0x00.upto(0x5e) do |i|
74
+ @to_unicode_mapping << [i, 0x20 + i]
75
+ @cid_mapping << [i, 0x20 + i]
76
+ end
77
+ @to_unicode_mapping << [0x60, 0x90]
78
+ @cid_mapping << [0x60, 0x90]
79
+ 0x1379.upto(0x137B) do |i|
80
+ @to_unicode_mapping << [i, 0x90FE + i - 0x1379]
81
+ end
82
+ @to_unicode_mapping << [0x3A51, 0x2003E]
40
83
  end
41
84
 
42
85
  describe "create_to_unicode_cmap" do
43
86
  it "creates a correct CMap file" do
44
- assert_equal(@cmap_data, HexaPDF::Font::CMap.create_to_unicode_cmap(@mapping))
87
+ assert_equal(@to_unicode_cmap_data,
88
+ HexaPDF::Font::CMap.create_to_unicode_cmap(@to_unicode_mapping))
45
89
  end
46
90
 
47
91
  it "works if the last item is a range" do
48
- @mapping.pop
49
- @cmap_data.sub!(/2 beginbfchar/, '1 beginbfchar')
50
- @cmap_data.sub!(/<3A51><d840dc3e>\n/, '')
51
- assert_equal(@cmap_data, HexaPDF::Font::CMap.create_to_unicode_cmap(@mapping))
92
+ @to_unicode_mapping.pop
93
+ @to_unicode_cmap_data.sub!(/2 beginbfchar/, '1 beginbfchar')
94
+ @to_unicode_cmap_data.sub!(/<3A51><d840dc3e>\n/, '')
95
+ assert_equal(@to_unicode_cmap_data,
96
+ HexaPDF::Font::CMap.create_to_unicode_cmap(@to_unicode_mapping))
52
97
  end
53
98
 
54
99
  it "works with only ranges" do
55
- @mapping.delete_at(-1)
56
- @mapping.delete_at(0x5f)
57
- @cmap_data.sub!(/\n2 beginbfchar.*endbfchar/m, '')
58
- assert_equal(@cmap_data, HexaPDF::Font::CMap.create_to_unicode_cmap(@mapping))
100
+ @to_unicode_mapping.delete_at(-1)
101
+ @to_unicode_mapping.delete_at(0x5f)
102
+ @to_unicode_cmap_data.sub!(/\n2 beginbfchar.*endbfchar/m, '')
103
+ assert_equal(@to_unicode_cmap_data,
104
+ HexaPDF::Font::CMap.create_to_unicode_cmap(@to_unicode_mapping))
59
105
  end
60
106
 
61
107
  it "returns an empty CMap if the mapping is empty" do
62
- assert_equal(@cmap_data.sub(/\d+ beginbfchar.*endbfrange/m, ''),
108
+ assert_equal(@to_unicode_cmap_data.sub(/\d+ beginbfchar.*endbfrange/m, ''),
63
109
  HexaPDF::Font::CMap.create_to_unicode_cmap([]))
64
110
  end
65
111
  end
112
+
113
+ describe "create_cid_cmap" do
114
+ it "creates a correct CMap file" do
115
+ assert_equal(@cid_cmap_data, HexaPDF::Font::CMap.create_cid_cmap(@cid_mapping))
116
+ end
117
+
118
+ it "returns an empty CMap if the mapping is empty" do
119
+ assert_equal(@cid_cmap_data.sub(/\d+ begincidchar.*endcidrange/m, ''),
120
+ HexaPDF::Font::CMap.create_cid_cmap([]))
121
+ end
122
+ end
66
123
  end
@@ -1,6 +1,7 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
 
3
3
  require 'test_helper'
4
+ require 'hexapdf/font/encoding'
4
5
  require 'hexapdf/font/encoding/base'
5
6
 
6
7
  describe HexaPDF::Font::Encoding::Base do
@@ -42,4 +43,23 @@ describe HexaPDF::Font::Encoding::Base do
42
43
  assert_nil(@base.code(:Unknown))
43
44
  end
44
45
  end
46
+
47
+ describe "to_compact_array" do
48
+ before do
49
+ @base.code_to_name[66] = :B
50
+ @base.code_to_name[67] = :C
51
+ @base.code_to_name[20] = :space
52
+ @base.code_to_name[28] = :D
53
+ @base.code_to_name[29] = :E
54
+ end
55
+
56
+ it "returns the difference array" do
57
+ assert_equal([20, :space, 28, :D, :E, 65, :A, :B, :C], @base.to_compact_array)
58
+ end
59
+
60
+ it "ignores the codes that are the same in the base encoding" do
61
+ std_encoding = HexaPDF::Font::Encoding.for_name(:StandardEncoding)
62
+ assert_equal([20, :space, 28, :D, :E, ], @base.to_compact_array(base_encoding: std_encoding))
63
+ end
64
+ end
45
65
  end
@@ -32,7 +32,7 @@ describe HexaPDF::Font::Encoding::GlyphList do
32
32
 
33
33
  it "maps special uXXXX[XX] names to unicode values" do
34
34
  assert_equal("A", @list.name_to_unicode(:u0041))
35
- assert_equal("" << "1F000".hex, @list.name_to_unicode(:u1F000))
35
+ assert_equal(+'' << "1F000".hex, @list.name_to_unicode(:u1F000))
36
36
  end
37
37
 
38
38
  it "maps Zapf Dingbats glyph names to their unicode" do
@@ -51,7 +51,7 @@ describe HexaPDF::Font::TrueTypeWrapper do
51
51
  glyphs = @font_wrapper.decode_utf8("😁")
52
52
  assert_equal(1, glyphs.length)
53
53
  assert_kind_of(HexaPDF::Font::InvalidGlyph, glyphs.first)
54
- assert_equal('' << 128_513, glyphs.first.str)
54
+ assert_equal(+'' << 128_513, glyphs.first.str)
55
55
  end
56
56
  end
57
57
 
@@ -71,11 +71,17 @@ describe HexaPDF::Font::TrueTypeWrapper do
71
71
  glyph.inspect)
72
72
  end
73
73
 
74
+ it "caches glyphs based on the id and string" do
75
+ glyph = @font_wrapper.glyph(17)
76
+ assert_same(glyph, @font_wrapper.glyph(17))
77
+ refute_same(glyph, @font_wrapper.glyph(17, "1"))
78
+ end
79
+
74
80
  it "invokes font.on_missing_glyph for missing glyphs" do
75
81
  glyph = @font_wrapper.glyph(9999)
76
82
  assert_kind_of(HexaPDF::Font::InvalidGlyph, glyph)
77
83
  assert_equal(0, glyph.id)
78
- assert_equal('' << 0xFFFD, glyph.str)
84
+ assert_equal(+'' << 0xFFFD, glyph.str)
79
85
  end
80
86
  end
81
87
 
@@ -99,14 +105,23 @@ describe HexaPDF::Font::TrueTypeWrapper do
99
105
  assert_equal([1].pack('n'), code)
100
106
  code = @font_wrapper.encode(@font_wrapper.glyph(10))
101
107
  assert_equal([2].pack('n'), code)
108
+ code = @font_wrapper.encode(@font_wrapper.glyph(10, "o"))
109
+ assert_equal([3].pack('n'), code)
102
110
  end
103
111
 
104
112
  it "returns the encoded glyph ID for fonts that are not subset" do
105
113
  @font_wrapper = HexaPDF::Font::TrueTypeWrapper.new(@doc, @font, subset: false)
106
114
  code = @font_wrapper.encode(@font_wrapper.glyph(3))
107
- assert_equal([3].pack('n'), code)
115
+ assert_equal([1].pack('n'), code)
108
116
  code = @font_wrapper.encode(@font_wrapper.glyph(10))
109
- assert_equal([10].pack('n'), code)
117
+ assert_equal([2].pack('n'), code)
118
+ code = @font_wrapper.encode(@font_wrapper.glyph(10, "o"))
119
+ assert_equal([3].pack('n'), code)
120
+ end
121
+
122
+ it "doesn't use char codes 13, 40, 41 and 92 because they would need to be escaped" do
123
+ codes = 1.upto(93).map {|i| @font_wrapper.encode(@font_wrapper.glyph(i)) }.join
124
+ assert_equal([1..12, 14..39, 42..91, 93..97].flat_map(&:to_a).pack('n*'), codes)
110
125
  end
111
126
 
112
127
  it "raises an error if an InvalidGlyph is encoded" do
@@ -180,16 +195,27 @@ describe HexaPDF::Font::TrueTypeWrapper do
180
195
  it "with fonts that are not subset (only differences to other case)" do
181
196
  @font_wrapper = HexaPDF::Font::TrueTypeWrapper.new(@doc, @font, subset: false)
182
197
  @font_wrapper.encode(@font_wrapper.glyph(3))
198
+ @font_wrapper.encode(@font_wrapper.glyph(3, "-"))
183
199
  glyph = @font_wrapper.decode_utf8('H').first
184
200
  @font_wrapper.encode(glyph)
185
201
  @doc.dispatch_message(:complete_objects)
186
202
 
187
203
  dict = @font_wrapper.pdf_object
188
204
 
189
- assert_equal(HexaPDF::Font::CMap.create_to_unicode_cmap([[3, ' '.ord], [glyph.id, 'H'.ord]]),
205
+ assert_equal(HexaPDF::Font::CMap.create_to_unicode_cmap([[1, ' '.ord], [2, '-'.ord],
206
+ [3, 'H'.ord]]),
190
207
  dict[:ToUnicode].stream)
208
+ assert_equal(HexaPDF::Font::CMap.create_cid_cmap([[1, 3], [2, 3], [3, glyph.id]]),
209
+ dict[:Encoding].stream)
191
210
  assert_equal([glyph.id, [glyph.width]], dict[:DescendantFonts][0][:W].value)
192
211
  end
212
+
213
+ it "handles the case where the font is added but then not used and deleted" do
214
+ @doc.task(:optimize, compact: true)
215
+ assert(@font_wrapper.pdf_object.null?)
216
+ @doc.dispatch_message(:complete_objects)
217
+ assert(@font_wrapper.pdf_object.null?)
218
+ end
193
219
  end
194
220
 
195
221
  describe "font file embedding" do
@@ -56,7 +56,7 @@ describe HexaPDF::Font::Type1Wrapper do
56
56
  glyphs = @times_wrapper.decode_utf8("😁")
57
57
  assert_equal(1, glyphs.length)
58
58
  assert_kind_of(HexaPDF::Font::InvalidGlyph, glyphs.first)
59
- assert_equal('' << 128_513, glyphs.first.str)
59
+ assert_equal(+'' << 128_513, glyphs.first.str)
60
60
  end
61
61
  end
62
62
 
@@ -140,5 +140,12 @@ describe HexaPDF::Font::Type1Wrapper do
140
140
  it "makes sure that the PDF dictionaries are indirect" do
141
141
  assert(@times_wrapper.pdf_object.indirect?)
142
142
  end
143
+
144
+ it "handles the case where the font is added but then not used and deleted" do
145
+ @doc.task(:optimize, compact: true)
146
+ assert(@times_wrapper.pdf_object.null?)
147
+ @doc.dispatch_message(:complete_objects)
148
+ assert(@times_wrapper.pdf_object.null?)
149
+ end
143
150
  end
144
151
  end
@@ -13,6 +13,18 @@ describe HexaPDF::Font::TrueType::Table do
13
13
  @entry = HexaPDF::Font::TrueType::Table::Directory::Entry.new('tagg', 0, 0, @file.io.string.length)
14
14
  end
15
15
 
16
+ describe "self.calculate_checksum" do
17
+ it "works for data with a length divisible by four" do
18
+ klass = HexaPDF::Font::TrueType::Table
19
+ assert_equal(256, klass.calculate_checksum("\x00\x00\x00\x01\x00\x00\x00\xFF"))
20
+ end
21
+
22
+ it "works for data with a length not divisible by four" do
23
+ klass = HexaPDF::Font::TrueType::Table
24
+ assert_equal(512, klass.calculate_checksum("\x00\x00\x00\x01\x00\x00\x00\xFF\x00\x00\x01"))
25
+ end
26
+ end
27
+
16
28
  describe "initialize" do
17
29
  it "reads the data from the associated file" do
18
30
  table = TrueTypeTestTable.new(@file, @entry)
@@ -5,6 +5,14 @@ require 'hexapdf/document'
5
5
  require 'hexapdf/layout/box'
6
6
 
7
7
  describe HexaPDF::Layout::Box::FitResult do
8
+ it "allows setting the status to failure" do
9
+ result = HexaPDF::Layout::Box::FitResult.new(nil)
10
+ result.overflow!
11
+ refute(result.failure?)
12
+ result.failure!
13
+ assert(result.failure?)
14
+ end
15
+
8
16
  it "shows the box's mask area on #draw when using debug output" do
9
17
  doc = HexaPDF::Document.new(config: {'debug' => true})
10
18
  canvas = doc.pages.add.canvas
@@ -270,8 +278,6 @@ describe HexaPDF::Layout::Box do
270
278
  [:restore_graphics_state],
271
279
  [:save_graphics_state],
272
280
  [:set_line_width, [5]],
273
- [:append_rectangle, [5, 5, 150, 130]],
274
- [:clip_path_non_zero], [:end_path],
275
281
  [:append_rectangle, [7.5, 7.5, 145, 125]],
276
282
  [:stroke_path],
277
283
  [:restore_graphics_state],
@@ -10,6 +10,10 @@ describe HexaPDF::Layout::ContainerBox do
10
10
  @frame = HexaPDF::Layout::Frame.new(0, 0, 100, 100)
11
11
  end
12
12
 
13
+ def child_box(height: 0, width: 0, **style_properties)
14
+ @doc.layout.box(height: height, width: width, style: style_properties)
15
+ end
16
+
13
17
  def create_box(children, **kwargs)
14
18
  HexaPDF::Layout::ContainerBox.new(children: Array(children), **kwargs)
15
19
  end
@@ -48,17 +52,41 @@ describe HexaPDF::Layout::ContainerBox do
48
52
 
49
53
  describe "fit_content" do
50
54
  it "fits the children according to their mask_mode, valign, and align style properties" do
51
- box = create_box([@doc.layout.box(height: 20),
52
- @doc.layout.box(height: 20, style: {valign: :bottom, mask_mode: :fill_horizontal}),
53
- @doc.layout.box(width: 20, style: {align: :right, mask_mode: :fill_vertical})])
55
+ box = create_box([child_box(height: 20),
56
+ child_box(height: 20, valign: :bottom, mask_mode: :fill_horizontal),
57
+ child_box(width: 20, align: :right, mask_mode: :fill_vertical)])
54
58
  check_box(box, 100, 100, [[0, 80], [0, 0], [80, 20]])
55
59
  end
56
60
 
57
61
  it "respects the initially set width/height as well as border/padding" do
58
- box = create_box(@doc.layout.box(height: 20), height: 50, width: 40,
62
+ box = create_box(child_box(height: 20), height: 50, width: 40,
59
63
  style: {padding: 2, border: {width: 3}})
60
64
  check_box(box, 40, 50, [[5, 75]])
61
65
  end
66
+
67
+ it "fails if splitting is not allowed and the content is too big" do
68
+ box = create_box([child_box(height: 80), child_box(height: 30)])
69
+ box.fit(@frame.available_width, @frame.available_height, @frame)
70
+ assert(box.fit_result.failure?)
71
+ end
72
+
73
+ it "splits the box if splitting is allowed and the content is too big" do
74
+ box = create_box([child_box(height: 80), child_box(height: 30)], splitable: true)
75
+ box.fit(@frame.available_width, @frame.available_height, @frame)
76
+ assert(box.fit_result.overflow?)
77
+ end
78
+ end
79
+
80
+ describe "split_content" do
81
+ it "assigns the overflown boxes to the split box" do
82
+ box = create_box([child_box(height: 80), child_box(height: 30)], splitable: true)
83
+ box.fit(@frame.available_width, @frame.available_height, @frame)
84
+ assert(box.fit_result.overflow?)
85
+ box_a, box_b = box.split
86
+ assert_same(box, box_a)
87
+ assert(box_b.split_box?)
88
+ assert_equal(1, box_b.children.size)
89
+ end
62
90
  end
63
91
 
64
92
  describe "draw_content" do
@@ -67,10 +95,10 @@ describe HexaPDF::Layout::ContainerBox do
67
95
  end
68
96
 
69
97
  it "draws the result onto the canvas" do
70
- child_box = @doc.layout.box(height: 20) do |canvas, b|
98
+ cbox = @doc.layout.box(height: 20) do |canvas, b|
71
99
  canvas.line(0, 0, b.content_width, b.content_height).end_path
72
100
  end
73
- box = create_box(child_box)
101
+ box = create_box(cbox)
74
102
  box.fit(100, 100, @frame)
75
103
  box.draw(@canvas, 0, 50)
76
104
  assert_operators(@canvas.contents, [[:save_graphics_state],
@@ -185,7 +185,7 @@ describe HexaPDF::Layout::ListBox do
185
185
  [:set_font_and_size, [:F1, 11]],
186
186
  [:set_device_gray_non_stroking_color, [0.5]],
187
187
  [:begin_text],
188
- [:set_text_matrix, [1, 0, 0, 1, 1.15, 92.487]],
188
+ [:move_text, [1.15, 92.487]],
189
189
  [:show_text, ["\x95".b]],
190
190
  [:end_text],
191
191
  [:restore_graphics_state],
@@ -197,7 +197,7 @@ describe HexaPDF::Layout::ListBox do
197
197
  [:set_font_and_size, [:F1, 11]],
198
198
  [:set_device_gray_non_stroking_color, [0.5]],
199
199
  [:begin_text],
200
- [:set_text_matrix, [1, 0, 0, 1, 1.15, 82.487]],
200
+ [:move_text, [1.15, 82.487]],
201
201
  [:show_text, ["\x95".b]],
202
202
  [:end_text],
203
203
  [:restore_graphics_state],
@@ -219,7 +219,7 @@ describe HexaPDF::Layout::ListBox do
219
219
  [:set_text_rise, [-6.111111]],
220
220
  [:set_device_gray_non_stroking_color, [0.5]],
221
221
  [:begin_text],
222
- [:set_text_matrix, [1, 0, 0, 1, 0.1985, 100]],
222
+ [:move_text, [0.1985, 100]],
223
223
  [:show_text, ["m".b]],
224
224
  [:end_text],
225
225
  [:restore_graphics_state],
@@ -241,7 +241,7 @@ describe HexaPDF::Layout::ListBox do
241
241
  [:set_text_rise, [-6.111111]],
242
242
  [:set_device_gray_non_stroking_color, [0.5]],
243
243
  [:begin_text],
244
- [:set_text_matrix, [1, 0, 0, 1, 0.8145, 100]],
244
+ [:move_text, [0.8145, 100]],
245
245
  [:show_text, ["n".b]],
246
246
  [:end_text],
247
247
  [:restore_graphics_state],
@@ -263,7 +263,7 @@ describe HexaPDF::Layout::ListBox do
263
263
  [:set_font_and_size, [:F1, 11]],
264
264
  [:set_device_gray_non_stroking_color, [0.5]],
265
265
  [:begin_text],
266
- [:set_text_matrix, [1, 0, 0, 1, 6.75, 92.487]],
266
+ [:move_text, [6.75, 92.487]],
267
267
  [:show_text, ["1.".b]],
268
268
  [:end_text],
269
269
  [:restore_graphics_state],
@@ -275,7 +275,7 @@ describe HexaPDF::Layout::ListBox do
275
275
  [:set_font_and_size, [:F1, 11]],
276
276
  [:set_device_gray_non_stroking_color, [0.5]],
277
277
  [:begin_text],
278
- [:set_text_matrix, [1, 0, 0, 1, 6.75, 82.487]],
278
+ [:move_text, [6.75, 82.487]],
279
279
  [:show_text, ["2.".b]],
280
280
  [:end_text],
281
281
  [:restore_graphics_state],
@@ -314,7 +314,7 @@ describe HexaPDF::Layout::ListBox do
314
314
  [:save_graphics_state],
315
315
  [:set_font_and_size, [:F1, 10]],
316
316
  [:begin_text],
317
- [:set_text_matrix, [1, 0, 0, 1, 1.5, 93.17]],
317
+ [:move_text, [1.5, 93.17]],
318
318
  [:show_text, ["\x95".b]],
319
319
  [:end_text],
320
320
  [:restore_graphics_state],
@@ -49,7 +49,7 @@ describe HexaPDF::Layout::PageStyle do
49
49
  assert_equal("", page1.contents)
50
50
  assert_equal(523.275591, style.frame.width)
51
51
 
52
- page2 = style.create_page(@doc)
52
+ style.create_page(@doc)
53
53
  refute_same(frame1, style.frame)
54
54
  end
55
55
 
@@ -136,6 +136,19 @@ describe HexaPDF::Layout::Style::Quad do
136
136
  assert_equal(new_quad.bottom, quad.bottom)
137
137
  assert_equal(new_quad.left, quad.left)
138
138
  end
139
+
140
+ it "works with a Hash as value" do
141
+ quad = create_quad(top: 5, left: 10)
142
+ assert_equal(5, quad.top)
143
+ assert_equal(0, quad.bottom)
144
+ assert_equal(10, quad.left)
145
+ assert_equal(0, quad.right)
146
+ quad.set(right: 7)
147
+ assert_equal(5, quad.top)
148
+ assert_equal(0, quad.bottom)
149
+ assert_equal(10, quad.left)
150
+ assert_equal(7, quad.right)
151
+ end
139
152
  end
140
153
 
141
154
  it "can be asked if it contains only a single value" do
@@ -199,8 +212,6 @@ describe HexaPDF::Layout::Style::Border do
199
212
  assert_operators(@canvas.contents, [[:save_graphics_state],
200
213
  [:set_device_gray_stroking_color, [0.5]],
201
214
  [:set_line_width, [10]],
202
- [:append_rectangle, [0, 0, 100, 100]],
203
- [:clip_path_non_zero], [:end_path],
204
215
  [:append_rectangle, [5, 5, 90, 90]],
205
216
  [:stroke_path],
206
217
  [:restore_graphics_state]])
@@ -213,8 +224,6 @@ describe HexaPDF::Layout::Style::Border do
213
224
  [:set_device_gray_stroking_color, [0.5]],
214
225
  [:set_line_width, [10]],
215
226
  [:set_line_cap_style, [2]],
216
- [:append_rectangle, [0, 0, 200, 300]],
217
- [:clip_path_non_zero], [:end_path],
218
227
  [:set_line_dash_pattern, [[10, 20], 25]],
219
228
  [:move_to, [0, 295]], [:line_to, [200, 295]],
220
229
  [:move_to, [200, 5]], [:line_to, [0, 5]],
@@ -234,8 +243,6 @@ describe HexaPDF::Layout::Style::Border do
234
243
  [:set_device_gray_stroking_color, [0.5]],
235
244
  [:set_line_width, [10]],
236
245
  [:set_line_cap_style, [1]],
237
- [:append_rectangle, [0, 0, 200, 300]],
238
- [:clip_path_non_zero], [:end_path],
239
246
  [:set_line_dash_pattern, [[10, 20], 25]],
240
247
  [:move_to, [0, 295]], [:line_to, [200, 295]],
241
248
  [:move_to, [200, 5]], [:line_to, [0, 5]],
@@ -255,8 +262,6 @@ describe HexaPDF::Layout::Style::Border do
255
262
  [:set_device_gray_stroking_color, [0.5]],
256
263
  [:set_line_width, [10]],
257
264
  [:set_line_cap_style, [1]],
258
- [:append_rectangle, [0, 0, 100, 200]],
259
- [:clip_path_non_zero], [:end_path],
260
265
  [:set_line_dash_pattern, [[0, 18], 13]],
261
266
  [:move_to, [0, 195]], [:line_to, [100, 195]],
262
267
  [:move_to, [100, 5]], [:line_to, [0, 5]],
@@ -275,8 +280,6 @@ describe HexaPDF::Layout::Style::Border do
275
280
  assert_operators(@canvas.contents, [[:save_graphics_state],
276
281
  [:set_device_gray_stroking_color, [0.5]],
277
282
  [:set_line_width, [10]],
278
- [:append_rectangle, [-5, -5, 110, 110]],
279
- [:clip_path_non_zero], [:end_path],
280
283
  [:append_rectangle, [0, 0, 100, 100]],
281
284
  [:stroke_path],
282
285
  [:restore_graphics_state]])
@@ -748,6 +751,30 @@ describe HexaPDF::Layout::Style do
748
751
  end
749
752
  end
750
753
 
754
+ describe "each_property" do
755
+ it "yields all set properties with their values" do
756
+ @style.font_size = 5
757
+ @style.line_spacing = 1.2
758
+ assert_equal(0.005, @style.scaled_font_size)
759
+ assert_equal([[:font, @style.font], [:font_size, 5], [:horizontal_scaling, 100],
760
+ [:line_spacing, @style.line_spacing]],
761
+ @style.each_property.to_a.sort)
762
+ end
763
+ end
764
+
765
+ describe "merge" do
766
+ it "merges all set properties" do
767
+ @style.font_size = 5
768
+ @style.line_spacing = 1.2
769
+ new_style = HexaPDF::Layout::Style.new
770
+ new_style.update(font_size: 3, line_spacing: {type: :fixed, value: 2.5})
771
+ new_style.merge(@style)
772
+ assert_equal(5, new_style.font_size)
773
+ assert_equal(:proportional, new_style.line_spacing.type)
774
+ assert_equal(1.2, new_style.line_spacing.value)
775
+ end
776
+ end
777
+
751
778
  it "has several simple and dynamically generated properties with default values" do
752
779
  @style = HexaPDF::Layout::Style.new
753
780
  assert_raises(HexaPDF::Error) { @style.font }
@@ -790,6 +817,7 @@ describe HexaPDF::Layout::Style do
790
817
  assert_equal(:left, @style.align)
791
818
  assert_equal(:top, @style.valign)
792
819
  assert_equal(:default, @style.mask_mode)
820
+ assert_equal({}, @style.box_options)
793
821
  end
794
822
 
795
823
  it "allows using a non-standard setter for generated properties" do
@@ -800,8 +828,8 @@ describe HexaPDF::Layout::Style do
800
828
  @style.stroke_dash_pattern(5, 2)
801
829
  assert_equal([[5], 2], @style.stroke_dash_pattern.to_operands)
802
830
 
803
- @style.line_spacing(1.2)
804
- assert_equal([:proportional, 1.2], [@style.line_spacing.type, @style.line_spacing.value])
831
+ @style.line_spacing(HexaPDF::Layout::Style::LineSpacing.new(type: :double))
832
+ assert_equal([:proportional, 2], [@style.line_spacing.type, @style.line_spacing.value])
805
833
  end
806
834
 
807
835
  it "allows checking for valid values" do
@@ -872,6 +900,9 @@ describe HexaPDF::Layout::Style do
872
900
  end
873
901
 
874
902
  it "handles subscript" do
903
+ @style.subscript = false
904
+ assert_equal(10, @style.calculated_font_size)
905
+ assert_equal(0, @style.calculated_text_rise)
875
906
  @style.subscript = true
876
907
  assert_in_delta(5.83, @style.calculated_font_size)
877
908
  assert_in_delta(0.00583, @style.scaled_font_size, 0.000001)
@@ -879,6 +910,9 @@ describe HexaPDF::Layout::Style do
879
910
  end
880
911
 
881
912
  it "handles superscript" do
913
+ @style.superscript = false
914
+ assert_equal(10, @style.calculated_font_size)
915
+ assert_equal(0, @style.calculated_text_rise)
882
916
  @style.superscript = true
883
917
  assert_in_delta(5.83, @style.calculated_font_size)
884
918
  assert_in_delta(3.30, @style.calculated_text_rise)