pdfrb 0.2.1 → 0.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 (406) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/CHANGELOG.md +24 -0
  4. data/Gemfile +1 -0
  5. data/PROPOSAL.canvas-clip.md +92 -0
  6. data/PROPOSAL.canvas-draw-image-matrix.md +87 -0
  7. data/PROPOSAL.canvas-rich-text.md +103 -0
  8. data/PROPOSAL.canvas-text-lines.md +77 -0
  9. data/PROPOSAL.canvas-transparency.md +106 -0
  10. data/PROPOSAL.external-font-metrics.md +129 -0
  11. data/PROPOSAL.font-embedding-fontfile2.md +88 -0
  12. data/PROPOSAL.font-resolver-ttc.md +105 -0
  13. data/PROPOSAL.font-resolver.md +96 -0
  14. data/PROPOSAL.font-subsetting.md +98 -0
  15. data/PROPOSAL.gradient-shadings.md +110 -0
  16. data/PROPOSAL.page-mediabox-setter.md +66 -0
  17. data/PROPOSAL.per-glyph-width.md +91 -0
  18. data/PROPOSAL.stringio-require.md +53 -0
  19. data/PROPOSAL.struct-tree-root.md +102 -0
  20. data/PROPOSAL.text-measurement.md +93 -0
  21. data/PROPOSAL.ttf-table-parsing-fix.md +112 -0
  22. data/RELEASE.md +48 -0
  23. data/lib/pdfrb/action/base.rb +21 -0
  24. data/lib/pdfrb/action/types.rb +178 -0
  25. data/lib/pdfrb/action.rb +55 -0
  26. data/lib/pdfrb/annotation/base.rb +79 -0
  27. data/lib/pdfrb/annotation/types.rb +222 -0
  28. data/lib/pdfrb/annotation.rb +78 -0
  29. data/lib/pdfrb/appearance/generator.rb +216 -0
  30. data/lib/pdfrb/appearance.rb +11 -0
  31. data/lib/pdfrb/arlington/object_definition.rb +2 -2
  32. data/lib/pdfrb/arlington/predicate/evaluator.rb +1 -1
  33. data/lib/pdfrb/arlington/predicate/functions.rb +25 -9
  34. data/lib/pdfrb/arlington/predicate.rb +1 -5
  35. data/lib/pdfrb/cli/batch.rb +18 -0
  36. data/lib/pdfrb/cli/debug_info.rb +18 -0
  37. data/lib/pdfrb/cli/files.rb +16 -0
  38. data/lib/pdfrb/cli/fonts.rb +16 -0
  39. data/lib/pdfrb/cli/form.rb +18 -0
  40. data/lib/pdfrb/cli/image2pdf.rb +18 -0
  41. data/lib/pdfrb/cli/images.rb +34 -0
  42. data/lib/pdfrb/cli/info.rb +20 -0
  43. data/lib/pdfrb/cli/inspect.rb +18 -0
  44. data/lib/pdfrb/cli/merge.rb +18 -0
  45. data/lib/pdfrb/cli/modify.rb +18 -0
  46. data/lib/pdfrb/cli/optimize.rb +18 -0
  47. data/lib/pdfrb/cli/split.rb +21 -0
  48. data/lib/pdfrb/cli/watermark.rb +18 -0
  49. data/lib/pdfrb/cli.rb +312 -7
  50. data/lib/pdfrb/color/color_space.rb +259 -0
  51. data/lib/pdfrb/color/icc_profile.rb +90 -0
  52. data/lib/pdfrb/color.rb +12 -0
  53. data/lib/pdfrb/compare/comparator.rb +244 -0
  54. data/lib/pdfrb/compare/report.rb +78 -0
  55. data/lib/pdfrb/compare.rb +42 -0
  56. data/lib/pdfrb/composer.rb +161 -0
  57. data/lib/pdfrb/conformance/pdf_a.rb +317 -56
  58. data/lib/pdfrb/conformance/pdf_ua.rb +186 -29
  59. data/lib/pdfrb/conformance/pdf_x.rb +247 -0
  60. data/lib/pdfrb/conformance/rule.rb +78 -0
  61. data/lib/pdfrb/conformance/structure_elements.rb +174 -0
  62. data/lib/pdfrb/conformance/verapdf_bridge.rb +92 -0
  63. data/lib/pdfrb/conformance.rb +7 -3
  64. data/lib/pdfrb/content/canvas.rb +217 -72
  65. data/lib/pdfrb/content/color_space.rb +157 -0
  66. data/lib/pdfrb/content/hidden_text_detector.rb +167 -0
  67. data/lib/pdfrb/content/operator.rb +21 -1
  68. data/lib/pdfrb/content/operators/clipping.rb +36 -0
  69. data/lib/pdfrb/content/operators/graphics_state_params.rb +15 -0
  70. data/lib/pdfrb/content/operators.rb +21 -0
  71. data/lib/pdfrb/content/shading/base.rb +139 -0
  72. data/lib/pdfrb/content/shading.rb +17 -0
  73. data/lib/pdfrb/content/smart_text_extractor.rb +85 -0
  74. data/lib/pdfrb/content/tiling_pattern.rb +78 -0
  75. data/lib/pdfrb/content/transformation_matrix.rb +61 -0
  76. data/lib/pdfrb/content.rb +7 -26
  77. data/lib/pdfrb/destination/fit.rb +185 -0
  78. data/lib/pdfrb/destination.rb +13 -0
  79. data/lib/pdfrb/digital_signature/cms_handler.rb +22 -0
  80. data/lib/pdfrb/digital_signature/signing.rb +107 -11
  81. data/lib/pdfrb/digital_signature/timestamp_handler.rb +45 -0
  82. data/lib/pdfrb/digital_signature/verification.rb +88 -9
  83. data/lib/pdfrb/digital_signature/verification_result.rb +14 -0
  84. data/lib/pdfrb/digital_signature.rb +26 -2
  85. data/lib/pdfrb/document/annotations.rb +120 -3
  86. data/lib/pdfrb/document/associated_files.rb +104 -0
  87. data/lib/pdfrb/document/colors.rb +95 -0
  88. data/lib/pdfrb/document/destinations.rb +127 -5
  89. data/lib/pdfrb/document/display.rb +84 -0
  90. data/lib/pdfrb/document/fonts.rb +417 -45
  91. data/lib/pdfrb/document/form.rb +338 -0
  92. data/lib/pdfrb/document/form_xobject.rb +68 -0
  93. data/lib/pdfrb/document/graphics_state.rb +91 -0
  94. data/lib/pdfrb/document/images.rb +4 -4
  95. data/lib/pdfrb/document/info.rb +102 -0
  96. data/lib/pdfrb/document/layers.rb +62 -0
  97. data/lib/pdfrb/document/metadata.rb +12 -5
  98. data/lib/pdfrb/document/output_intents.rb +91 -0
  99. data/lib/pdfrb/document/page_labels.rb +80 -0
  100. data/lib/pdfrb/document/pages.rb +68 -19
  101. data/lib/pdfrb/document/portfolio.rb +119 -0
  102. data/lib/pdfrb/document/shadings.rb +159 -0
  103. data/lib/pdfrb/document/stamps.rb +68 -0
  104. data/lib/pdfrb/document/structure.rb +242 -0
  105. data/lib/pdfrb/document.rb +73 -4
  106. data/lib/pdfrb/encryption/identity.rb +13 -0
  107. data/lib/pdfrb/encryption/password_verification.rb +53 -8
  108. data/lib/pdfrb/encryption/security_handler.rb +8 -5
  109. data/lib/pdfrb/encryption/standard_security_handler.rb +154 -81
  110. data/lib/pdfrb/encryption.rb +17 -0
  111. data/lib/pdfrb/error.rb +2 -2
  112. data/lib/pdfrb/filter/brotli_decode.rb +32 -0
  113. data/lib/pdfrb/filter/ccitt_fax_decode.rb +12 -0
  114. data/lib/pdfrb/filter/crypt.rb +2 -2
  115. data/lib/pdfrb/filter/dct_decode.rb +18 -0
  116. data/lib/pdfrb/filter/flate_decode.rb +4 -18
  117. data/lib/pdfrb/filter/jbig2_decode.rb +12 -0
  118. data/lib/pdfrb/filter/jpx_decode.rb +18 -0
  119. data/lib/pdfrb/filter/lzw_decode.rb +3 -2
  120. data/lib/pdfrb/filter/png_predictor.rb +94 -0
  121. data/lib/pdfrb/filter.rb +26 -13
  122. data/lib/pdfrb/font/afm_parser.rb +50 -26
  123. data/lib/pdfrb/font/cmap/writer.rb +9 -0
  124. data/lib/pdfrb/font/encoding/win_ansi_encoding.rb +17 -0
  125. data/lib/pdfrb/font/encoding.rb +32 -3
  126. data/lib/pdfrb/font/metrics_helper.rb +121 -0
  127. data/lib/pdfrb/font/true_type/file.rb +10 -1
  128. data/lib/pdfrb/font/true_type/glyf.rb +82 -0
  129. data/lib/pdfrb/font/true_type/kern.rb +58 -0
  130. data/lib/pdfrb/font/true_type/loca.rb +42 -0
  131. data/lib/pdfrb/font/true_type/maxp.rb +44 -0
  132. data/lib/pdfrb/font/true_type/name.rb +53 -0
  133. data/lib/pdfrb/font/true_type/post.rb +93 -0
  134. data/lib/pdfrb/font/true_type/subsetter.rb +3 -5
  135. data/lib/pdfrb/font/true_type/wrapper.rb +102 -0
  136. data/lib/pdfrb/font/true_type.rb +7 -0
  137. data/lib/pdfrb/font.rb +2 -0
  138. data/lib/pdfrb/font_loader/from_configuration.rb +19 -0
  139. data/lib/pdfrb/font_loader/from_file.rb +20 -0
  140. data/lib/pdfrb/font_loader/standard14.rb +20 -0
  141. data/lib/pdfrb/font_loader/variant_from_name.rb +36 -0
  142. data/lib/pdfrb/font_loader.rb +4 -0
  143. data/lib/pdfrb/font_resolver.rb +138 -0
  144. data/lib/pdfrb/image/audit.rb +94 -0
  145. data/lib/pdfrb/image/downsampler.rb +124 -0
  146. data/lib/pdfrb/image.rb +13 -0
  147. data/lib/pdfrb/image_loader/jpeg.rb +39 -58
  148. data/lib/pdfrb/image_loader/png.rb +31 -257
  149. data/lib/pdfrb/layout/bidi.rb +198 -0
  150. data/lib/pdfrb/layout/box.rb +49 -0
  151. data/lib/pdfrb/layout/box_fitter.rb +54 -0
  152. data/lib/pdfrb/layout/column_box.rb +71 -0
  153. data/lib/pdfrb/layout/container_box.rb +45 -0
  154. data/lib/pdfrb/layout/frame.rb +67 -0
  155. data/lib/pdfrb/layout/image_box.rb +44 -0
  156. data/lib/pdfrb/layout/inline_box.rb +25 -0
  157. data/lib/pdfrb/layout/line.rb +55 -0
  158. data/lib/pdfrb/layout/list_box.rb +87 -0
  159. data/lib/pdfrb/layout/numeric_refinements.rb +43 -0
  160. data/lib/pdfrb/layout/page_style.rb +65 -0
  161. data/lib/pdfrb/layout/style.rb +62 -0
  162. data/lib/pdfrb/layout/table_box.rb +66 -0
  163. data/lib/pdfrb/layout/text_box.rb +38 -0
  164. data/lib/pdfrb/layout/text_fragment.rb +45 -0
  165. data/lib/pdfrb/layout/text_layouter.rb +123 -0
  166. data/lib/pdfrb/layout.rb +28 -0
  167. data/lib/pdfrb/linearization/hint_stream.rb +93 -0
  168. data/lib/pdfrb/linearization/writer.rb +259 -0
  169. data/lib/pdfrb/linearization.rb +21 -0
  170. data/lib/pdfrb/model/cos/dictionary.rb +12 -0
  171. data/lib/pdfrb/model/pdf_array.rb +2 -2
  172. data/lib/pdfrb/model/type/action.rb +42 -0
  173. data/lib/pdfrb/model/type/action_go_to_3d_view.rb +14 -0
  174. data/lib/pdfrb/model/type/action_go_to_dp.rb +16 -0
  175. data/lib/pdfrb/model/type/action_go_to_r.rb +26 -0
  176. data/lib/pdfrb/model/type/action_goto.rb +34 -0
  177. data/lib/pdfrb/model/type/action_hide.rb +26 -0
  178. data/lib/pdfrb/model/type/action_import_data.rb +18 -0
  179. data/lib/pdfrb/model/type/action_java_script.rb +18 -0
  180. data/lib/pdfrb/model/type/action_launch.rb +22 -0
  181. data/lib/pdfrb/model/type/action_movie.rb +22 -0
  182. data/lib/pdfrb/model/type/action_named.rb +23 -0
  183. data/lib/pdfrb/model/type/action_nop.rb +12 -0
  184. data/lib/pdfrb/model/type/action_rendition.rb +28 -0
  185. data/lib/pdfrb/model/type/action_reset_form.rb +22 -0
  186. data/lib/pdfrb/model/type/action_rich_media_execute.rb +17 -0
  187. data/lib/pdfrb/model/type/action_set_ocg_state.rb +32 -0
  188. data/lib/pdfrb/model/type/action_set_state.rb +14 -0
  189. data/lib/pdfrb/model/type/action_sound_action.rb +23 -0
  190. data/lib/pdfrb/model/type/action_submit_form.rb +64 -0
  191. data/lib/pdfrb/model/type/action_thread.rb +23 -0
  192. data/lib/pdfrb/model/type/action_trans.rb +30 -0
  193. data/lib/pdfrb/model/type/action_uri.rb +30 -0
  194. data/lib/pdfrb/model/type/af_embedded_file.rb +11 -0
  195. data/lib/pdfrb/model/type/af_file_specification.rb +34 -0
  196. data/lib/pdfrb/model/type/annotation.rb +49 -10
  197. data/lib/pdfrb/model/type/appearance.rb +41 -0
  198. data/lib/pdfrb/model/type/appearance_characteristics.rb +36 -0
  199. data/lib/pdfrb/model/type/appearance_generator.rb +22 -0
  200. data/lib/pdfrb/model/type/appearance_trap_net.rb +84 -0
  201. data/lib/pdfrb/model/type/border_effect.rb +25 -0
  202. data/lib/pdfrb/model/type/border_styling.rb +38 -0
  203. data/lib/pdfrb/model/type/box_color_info.rb +21 -0
  204. data/lib/pdfrb/model/type/box_style.rb +19 -0
  205. data/lib/pdfrb/model/type/button.rb +17 -0
  206. data/lib/pdfrb/model/type/cal_gray.rb +17 -0
  207. data/lib/pdfrb/model/type/cal_rgb.rb +22 -0
  208. data/lib/pdfrb/model/type/caret_annotation.rb +21 -0
  209. data/lib/pdfrb/model/type/catalog.rb +99 -16
  210. data/lib/pdfrb/model/type/choice.rb +21 -0
  211. data/lib/pdfrb/model/type/cid_font.rb +52 -1
  212. data/lib/pdfrb/model/type/cid_font_descriptor_metrics.rb +28 -0
  213. data/lib/pdfrb/model/type/cid_system_info.rb +28 -0
  214. data/lib/pdfrb/model/type/circle_annotation.rb +20 -0
  215. data/lib/pdfrb/model/type/collection.rb +27 -0
  216. data/lib/pdfrb/model/type/collection_field.rb +24 -0
  217. data/lib/pdfrb/model/type/collection_item.rb +19 -0
  218. data/lib/pdfrb/model/type/collection_schema.rb +25 -0
  219. data/lib/pdfrb/model/type/collection_sort.rb +13 -0
  220. data/lib/pdfrb/model/type/crypt_filter.rb +58 -0
  221. data/lib/pdfrb/model/type/device_n.rb +37 -0
  222. data/lib/pdfrb/model/type/device_n_mixing_hints.rb +17 -0
  223. data/lib/pdfrb/model/type/device_n_process.rb +13 -0
  224. data/lib/pdfrb/model/type/doc_mdp_transform_parameters.rb +21 -0
  225. data/lib/pdfrb/model/type/document_security_store.rb +55 -0
  226. data/lib/pdfrb/model/type/embedded_file.rb +19 -0
  227. data/lib/pdfrb/model/type/embedded_file_parameter.rb +48 -0
  228. data/lib/pdfrb/model/type/encryption_public_key.rb +38 -0
  229. data/lib/pdfrb/model/type/encryption_standard.rb +68 -0
  230. data/lib/pdfrb/model/type/field_mdp_transform_parameters.rb +26 -0
  231. data/lib/pdfrb/model/type/file_attachment_annotation.rb +29 -0
  232. data/lib/pdfrb/model/type/file_spec_ef.rb +22 -0
  233. data/lib/pdfrb/model/type/file_spec_rf.rb +21 -0
  234. data/lib/pdfrb/model/type/file_specification.rb +43 -0
  235. data/lib/pdfrb/model/type/file_trailer.rb +54 -0
  236. data/lib/pdfrb/model/type/font.rb +73 -2
  237. data/lib/pdfrb/model/type/font_cid_type0.rb +17 -0
  238. data/lib/pdfrb/model/type/font_cid_type2.rb +17 -0
  239. data/lib/pdfrb/model/type/font_descriptor.rb +47 -0
  240. data/lib/pdfrb/model/type/font_descriptor_cid_type0.rb +15 -0
  241. data/lib/pdfrb/model/type/font_descriptor_cid_type2.rb +15 -0
  242. data/lib/pdfrb/model/type/font_encoding.rb +47 -0
  243. data/lib/pdfrb/model/type/font_file.rb +28 -0
  244. data/lib/pdfrb/model/type/font_file2.rb +32 -0
  245. data/lib/pdfrb/model/type/font_file3.rb +23 -0
  246. data/lib/pdfrb/model/type/font_multiple_master.rb +60 -0
  247. data/lib/pdfrb/model/type/font_true_type.rb +1 -1
  248. data/lib/pdfrb/model/type/font_type0.rb +1 -1
  249. data/lib/pdfrb/model/type/font_type1.rb +1 -1
  250. data/lib/pdfrb/model/type/font_type3.rb +1 -1
  251. data/lib/pdfrb/model/type/form_field.rb +73 -0
  252. data/lib/pdfrb/model/type/free_text_annotation.rb +37 -0
  253. data/lib/pdfrb/model/type/function.rb +42 -0
  254. data/lib/pdfrb/model/type/function_exponential.rb +31 -0
  255. data/lib/pdfrb/model/type/function_postscript.rb +25 -0
  256. data/lib/pdfrb/model/type/function_sampled.rb +55 -0
  257. data/lib/pdfrb/model/type/function_stitching.rb +33 -0
  258. data/lib/pdfrb/model/type/generic_appearance.rb +40 -0
  259. data/lib/pdfrb/model/type/graphics_state_parameter.rb +69 -0
  260. data/lib/pdfrb/model/type/halftone.rb +21 -0
  261. data/lib/pdfrb/model/type/halftone_type1.rb +17 -0
  262. data/lib/pdfrb/model/type/halftone_type10.rb +16 -0
  263. data/lib/pdfrb/model/type/halftone_type16.rb +11 -0
  264. data/lib/pdfrb/model/type/halftone_type5.rb +24 -0
  265. data/lib/pdfrb/model/type/halftone_type6.rb +16 -0
  266. data/lib/pdfrb/model/type/highlight_annotation.rb +11 -0
  267. data/lib/pdfrb/model/type/icc_based_color_space.rb +24 -0
  268. data/lib/pdfrb/model/type/icon_fit.rb +31 -0
  269. data/lib/pdfrb/model/type/indexed.rb +32 -0
  270. data/lib/pdfrb/model/type/info.rb +44 -0
  271. data/lib/pdfrb/model/type/ink_annotation.rb +29 -0
  272. data/lib/pdfrb/model/type/interactive_form.rb +36 -0
  273. data/lib/pdfrb/model/type/interior_color.rb +12 -0
  274. data/lib/pdfrb/model/type/jbig2_globals.rb +15 -0
  275. data/lib/pdfrb/model/type/lab.rb +27 -0
  276. data/lib/pdfrb/model/type/line_annotation.rb +44 -0
  277. data/lib/pdfrb/model/type/line_ending_styling.rb +39 -0
  278. data/lib/pdfrb/model/type/link_annotation.rb +41 -0
  279. data/lib/pdfrb/model/type/link_appearance.rb +9 -0
  280. data/lib/pdfrb/model/type/mark_information.rb +23 -0
  281. data/lib/pdfrb/model/type/marked_content_reference.rb +24 -0
  282. data/lib/pdfrb/model/type/markup_annotation.rb +36 -0
  283. data/lib/pdfrb/model/type/mdp_dict.rb +28 -0
  284. data/lib/pdfrb/model/type/measure.rb +39 -0
  285. data/lib/pdfrb/model/type/media_clip.rb +28 -0
  286. data/lib/pdfrb/model/type/media_offset.rb +106 -0
  287. data/lib/pdfrb/model/type/metadata.rb +27 -3
  288. data/lib/pdfrb/model/type/misc_helpers.rb +75 -0
  289. data/lib/pdfrb/model/type/movie.rb +28 -0
  290. data/lib/pdfrb/model/type/names.rb +51 -0
  291. data/lib/pdfrb/model/type/namespace.rb +27 -0
  292. data/lib/pdfrb/model/type/object_reference.rb +31 -0
  293. data/lib/pdfrb/model/type/object_stream.rb +38 -0
  294. data/lib/pdfrb/model/type/opt_content_ext.rb +94 -0
  295. data/lib/pdfrb/model/type/opt_content_page_element.rb +18 -0
  296. data/lib/pdfrb/model/type/optional_content_config.rb +56 -0
  297. data/lib/pdfrb/model/type/optional_content_group.rb +35 -10
  298. data/lib/pdfrb/model/type/optional_content_membership.rb +36 -2
  299. data/lib/pdfrb/model/type/optional_content_properties.rb +16 -1
  300. data/lib/pdfrb/model/type/outline.rb +40 -6
  301. data/lib/pdfrb/model/type/outline_item.rb +45 -0
  302. data/lib/pdfrb/model/type/output_intent.rb +30 -0
  303. data/lib/pdfrb/model/type/page.rb +127 -0
  304. data/lib/pdfrb/model/type/page_label.rb +68 -0
  305. data/lib/pdfrb/model/type/page_tree_node.rb +51 -16
  306. data/lib/pdfrb/model/type/page_tree_node_root.rb +15 -0
  307. data/lib/pdfrb/model/type/pattern.rb +16 -0
  308. data/lib/pdfrb/model/type/pattern_shading.rb +25 -0
  309. data/lib/pdfrb/model/type/pattern_tiling.rb +25 -0
  310. data/lib/pdfrb/model/type/polygon_annotation.rb +29 -0
  311. data/lib/pdfrb/model/type/polygon_polyline.rb +14 -0
  312. data/lib/pdfrb/model/type/polyline_annotation.rb +29 -0
  313. data/lib/pdfrb/model/type/popup_annotation.rb +20 -0
  314. data/lib/pdfrb/model/type/printer_mark_annotation.rb +13 -0
  315. data/lib/pdfrb/model/type/redact_annotation.rb +28 -0
  316. data/lib/pdfrb/model/type/rendition.rb +31 -0
  317. data/lib/pdfrb/model/type/resources.rb +59 -14
  318. data/lib/pdfrb/model/type/rich_media_activation.rb +24 -0
  319. data/lib/pdfrb/model/type/rich_media_animation.rb +25 -0
  320. data/lib/pdfrb/model/type/rich_media_annotation.rb +18 -0
  321. data/lib/pdfrb/model/type/rich_media_command.rb +19 -0
  322. data/lib/pdfrb/model/type/rich_media_configuration.rb +32 -0
  323. data/lib/pdfrb/model/type/rich_media_content.rb +28 -0
  324. data/lib/pdfrb/model/type/rich_media_cue_point.rb +18 -0
  325. data/lib/pdfrb/model/type/rich_media_deactivation.rb +17 -0
  326. data/lib/pdfrb/model/type/rich_media_instance.rb +33 -0
  327. data/lib/pdfrb/model/type/rich_media_presentation.rb +29 -0
  328. data/lib/pdfrb/model/type/rich_media_settings.rb +33 -0
  329. data/lib/pdfrb/model/type/rich_media_window.rb +20 -0
  330. data/lib/pdfrb/model/type/screen_annotation.rb +19 -0
  331. data/lib/pdfrb/model/type/separation.rb +25 -0
  332. data/lib/pdfrb/model/type/sig_field_lock.rb +25 -0
  333. data/lib/pdfrb/model/type/sig_field_seed_value.rb +25 -0
  334. data/lib/pdfrb/model/type/signature.rb +65 -0
  335. data/lib/pdfrb/model/type/signature_build.rb +86 -0
  336. data/lib/pdfrb/model/type/signature_field.rb +17 -0
  337. data/lib/pdfrb/model/type/software_identifier.rb +19 -0
  338. data/lib/pdfrb/model/type/sound.rb +38 -0
  339. data/lib/pdfrb/model/type/sound_annotation.rb +21 -0
  340. data/lib/pdfrb/model/type/square_annotation.rb +24 -0
  341. data/lib/pdfrb/model/type/square_circle.rb +13 -0
  342. data/lib/pdfrb/model/type/squiggly_annotation.rb +11 -0
  343. data/lib/pdfrb/model/type/stamp_annotation.rb +19 -0
  344. data/lib/pdfrb/model/type/strikeout_annotation.rb +11 -0
  345. data/lib/pdfrb/model/type/struct_elem.rb +75 -0
  346. data/lib/pdfrb/model/type/struct_tree_root.rb +34 -0
  347. data/lib/pdfrb/model/type/structure_attributes.rb +44 -0
  348. data/lib/pdfrb/model/type/structure_element_kid.rb +33 -0
  349. data/lib/pdfrb/model/type/text_annotation.rb +25 -0
  350. data/lib/pdfrb/model/type/text_appearance.rb +9 -0
  351. data/lib/pdfrb/model/type/text_field.rb +21 -0
  352. data/lib/pdfrb/model/type/text_markup_annotation.rb +30 -0
  353. data/lib/pdfrb/model/type/three_d_activation.rb +32 -0
  354. data/lib/pdfrb/model/type/three_d_animation_style.rb +24 -0
  355. data/lib/pdfrb/model/type/three_d_background.rb +28 -0
  356. data/lib/pdfrb/model/type/three_d_cross_section.rb +25 -0
  357. data/lib/pdfrb/model/type/three_d_lighting_scheme.rb +26 -0
  358. data/lib/pdfrb/model/type/three_d_measure.rb +20 -0
  359. data/lib/pdfrb/model/type/three_d_measure_3dc.rb +23 -0
  360. data/lib/pdfrb/model/type/three_d_measure_ad3.rb +14 -0
  361. data/lib/pdfrb/model/type/three_d_measure_ld3.rb +14 -0
  362. data/lib/pdfrb/model/type/three_d_measure_pd3.rb +20 -0
  363. data/lib/pdfrb/model/type/three_d_measure_rd3.rb +14 -0
  364. data/lib/pdfrb/model/type/three_d_node.rb +37 -0
  365. data/lib/pdfrb/model/type/three_d_reference.rb +21 -0
  366. data/lib/pdfrb/model/type/three_d_render_mode.rb +34 -0
  367. data/lib/pdfrb/model/type/three_d_stream.rb +48 -0
  368. data/lib/pdfrb/model/type/three_d_units.rb +18 -0
  369. data/lib/pdfrb/model/type/three_d_view.rb +40 -0
  370. data/lib/pdfrb/model/type/to_unicode_cmap_stream.rb +23 -0
  371. data/lib/pdfrb/model/type/transition.rb +68 -0
  372. data/lib/pdfrb/model/type/underline_annotation.rb +11 -0
  373. data/lib/pdfrb/model/type/ur_transform_parameters.rb +33 -0
  374. data/lib/pdfrb/model/type/uri_dict.rb +18 -0
  375. data/lib/pdfrb/model/type/url_alias.rb +19 -0
  376. data/lib/pdfrb/model/type/variable_text_field.rb +47 -0
  377. data/lib/pdfrb/model/type/viewer_preferences.rb +42 -0
  378. data/lib/pdfrb/model/type/watermark_annotation.rb +13 -0
  379. data/lib/pdfrb/model/type/widget_annotation.rb +48 -0
  380. data/lib/pdfrb/model/type/widget_appearance.rb +9 -0
  381. data/lib/pdfrb/model/type/xobject_form.rb +41 -0
  382. data/lib/pdfrb/model/type/xobject_image.rb +56 -0
  383. data/lib/pdfrb/model/type/xref_stream.rb +37 -0
  384. data/lib/pdfrb/model/type.rb +286 -6
  385. data/lib/pdfrb/serializer.rb +2 -2
  386. data/lib/pdfrb/source/tokenizer.rb +0 -2
  387. data/lib/pdfrb/task/benchmark.rb +83 -0
  388. data/lib/pdfrb/task/extract_text.rb +1 -1
  389. data/lib/pdfrb/task/generate_corpus.rb +130 -0
  390. data/lib/pdfrb/task/memory_profile.rb +84 -0
  391. data/lib/pdfrb/task/optimize.rb +97 -7
  392. data/lib/pdfrb/task.rb +3 -3
  393. data/lib/pdfrb/test_utils.rb +112 -0
  394. data/lib/pdfrb/validator.rb +82 -0
  395. data/lib/pdfrb/version.rb +1 -1
  396. data/lib/pdfrb/writer.rb +117 -27
  397. data/lib/pdfrb/xmp/packet.rb +122 -0
  398. data/lib/pdfrb/xmp/schemas.rb +96 -0
  399. data/lib/pdfrb/xmp.rb +44 -0
  400. data/lib/pdfrb/xref_section.rb +2 -2
  401. data/lib/pdfrb.rb +25 -53
  402. data/pdfrb.gemspec +45 -0
  403. data/script/apply_fixes.sh +843 -0
  404. data/script/make_jpeg_fixture.rb +53 -0
  405. data/script/make_png_fixture.rb +25 -0
  406. metadata +312 -2
@@ -0,0 +1,167 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "stringio"
4
+
5
+ module Pdfrb
6
+ module Content
7
+ # Detects hidden / transparent text in content streams. Hidden text
8
+ # is text rendered with Tr=3 (invisible) or with alpha=0 (fully
9
+ # transparent). PDF/A and PDF/UA require such text to be tagged as
10
+ # /Artifact or wrapped in a structure element.
11
+ #
12
+ # Inspired by mn2pdf's PDFHiddenTextStripper. Walks content streams
13
+ # via the Content::Parser, tracking graphics state changes.
14
+ class HiddenTextDetector
15
+ HiddenItem = Struct.new(:text, :reason, :page, keyword_init: true) do
16
+ def hidden?
17
+ !reason.nil?
18
+ end
19
+ end
20
+
21
+ attr_reader :document
22
+
23
+ def initialize(document)
24
+ @document = document
25
+ end
26
+
27
+ # Walk all content streams. Returns HiddenItem objects for each
28
+ # text-showing operation. Items with a nil reason are visible.
29
+ def detect
30
+ items = []
31
+ @document.each_indirect_object do |obj|
32
+ next unless content_stream?(obj)
33
+
34
+ page = find_page(obj)
35
+ detect_in_stream(obj, page) { |item| items << item }
36
+ end
37
+ items
38
+ end
39
+
40
+ # Returns only the hidden text items (rendering_mode_3 or alpha=0).
41
+ def hidden_only
42
+ detect.select(&:hidden?)
43
+ end
44
+
45
+ private
46
+
47
+ def content_stream?(obj)
48
+ return false unless obj.is_a?(Pdfrb::Model::Cos::Stream)
49
+ return true if obj.value[:Type] == :Page
50
+
51
+ subtype = obj.value[:Subtype]
52
+ return false if subtype == :Image
53
+
54
+ true
55
+ end
56
+
57
+ def find_page(stream_obj)
58
+ @document.each_indirect_object do |obj|
59
+ next unless obj.is_a?(Pdfrb::Model::Cos::Dictionary)
60
+ next unless obj[:Type] == :Page
61
+
62
+ contents = obj[:Contents]
63
+ next unless contents
64
+
65
+ list = if contents.is_a?(Pdfrb::Model::PdfArray)
66
+ contents.value
67
+ else
68
+ contents.is_a?(::Array) ? contents : [contents]
69
+ end
70
+
71
+ return obj if list.any? do |c|
72
+ c_obj = c.is_a?(Pdfrb::Model::Reference) ? @document.object(c) : c
73
+ c_obj == stream_obj
74
+ end
75
+ end
76
+ nil
77
+ end
78
+
79
+ def detect_in_stream(stream_obj, page, &)
80
+ bytes = stream_obj.stream
81
+ return unless bytes && !bytes.empty?
82
+
83
+ io = StringIO.new(bytes.b)
84
+ tokenizer = Pdfrb::Source::Tokenizer.new(io)
85
+ parser = Content::Parser.new(tokenizer)
86
+ collector = Collector.new(page)
87
+
88
+ parser.each_invocation do |op_class, operands|
89
+ op_name = op_class.name
90
+ case op_name
91
+ when "Tr"
92
+ collector.text_rendering_mode = operands.first.to_i if operands.first
93
+ when "ca"
94
+ collector.stroke_alpha = operands.first.to_f if operands.first
95
+ when "CA"
96
+ collector.non_stroke_alpha = operands.first.to_f if operands.first
97
+ when "Tj"
98
+ collector.show_text(operands.first)
99
+ when "TJ"
100
+ collector.show_text_array(operands.first)
101
+ when "'", "\""
102
+ collector.move_to_next_line
103
+ collector.show_text(operands.first) if operands.first
104
+ end
105
+ end
106
+
107
+ collector.items.each(&)
108
+ rescue StandardError
109
+ nil
110
+ end
111
+
112
+ # Internal: collects text-show operations with their visibility state.
113
+ class Collector
114
+ attr_reader :items
115
+
116
+ def initialize(page)
117
+ @items = []
118
+ @page = page
119
+ @text_rendering_mode = 0
120
+ @non_stroke_alpha = 1.0
121
+ @stroke_alpha = 1.0
122
+ end
123
+
124
+ def text_rendering_mode=(value)
125
+ @text_rendering_mode = value
126
+ end
127
+
128
+ def non_stroke_alpha=(value)
129
+ @non_stroke_alpha = value
130
+ end
131
+
132
+ def stroke_alpha=(value)
133
+ @stroke_alpha = value
134
+ end
135
+
136
+ def show_text(text)
137
+ return if text.nil?
138
+
139
+ reason = current_reason
140
+ @items << HiddenItem.new(text: text.to_s, reason: reason, page: @page)
141
+ end
142
+
143
+ def show_text_array(items)
144
+ return unless items.is_a?(::Array)
145
+
146
+ items.each do |item|
147
+ if item.is_a?(::String)
148
+ show_text(item)
149
+ end
150
+ # Numeric items are positioning values; ignore them
151
+ end
152
+ end
153
+
154
+ def move_to_next_line; end
155
+
156
+ private
157
+
158
+ def current_reason
159
+ return :rendering_mode_invisible if @text_rendering_mode == 3
160
+ return :alpha_zero_hidden if @non_stroke_alpha.to_f == 0.0 || @stroke_alpha.to_f == 0.0
161
+
162
+ nil
163
+ end
164
+ end
165
+ end
166
+ end
167
+ end
@@ -20,12 +20,32 @@ module Pdfrb
20
20
  end
21
21
 
22
22
  def [](name)
23
- @registry[name.to_s]
23
+ key = name.to_s
24
+ result = @registry[key]
25
+ return result if result
26
+
27
+ # Lazy-load on miss: trigger eager_load! which populates
28
+ # the registry, then retry.
29
+ eager_load! unless @eager_loaded
30
+ @registry[key]
24
31
  end
25
32
 
26
33
  def names
27
34
  @registry.keys
28
35
  end
36
+
37
+ # Force-load all operator implementations so register calls
38
+ # fire and populate the registry. Idempotent.
39
+ def eager_load!
40
+ return if @eager_loaded
41
+
42
+ Pdfrb::Content::Operators.constants.each do |c|
43
+ Pdfrb::Content::Operators.const_get(c)
44
+ rescue NameError
45
+ # Skip modules that don't resolve.
46
+ end
47
+ @eager_loaded = true
48
+ end
29
49
  end
30
50
 
31
51
  # Base class. Subclasses override +name+ and optionally
@@ -0,0 +1,36 @@
1
+ # frozen_string literal: true
2
+
3
+ module Pdfrb
4
+ module Content
5
+ module Operator
6
+ # Clipping path operators (s8.5.4). W and W* modify the current
7
+ # clipping path by intersecting it with the current path. The
8
+ # clipping path takes effect when the next painting operator or
9
+ # `n` (end path) is executed.
10
+
11
+ # `W` — modify clipping path using nonzero winding rule.
12
+ class ClipNonZero < NoArg
13
+ def self.name; "W"; end
14
+ register
15
+ end
16
+
17
+ # `W*` — modify clipping path using even-odd rule.
18
+ class ClipEvenOdd < NoArg
19
+ def self.name; "W*"; end
20
+ register
21
+ end
22
+
23
+ # `Do` — invoke an XObject (Image or Form) by resource name.
24
+ class InvokeXObject < Base
25
+ class << self
26
+ def name; "Do"; end
27
+
28
+ def serialize(_serializer, name)
29
+ "/#{name} Do\n"
30
+ end
31
+ end
32
+ register
33
+ end
34
+ end
35
+ end
36
+ end
@@ -73,6 +73,21 @@ module Pdfrb
73
73
  end
74
74
  register
75
75
  end
76
+ class NonStrokeAlpha < Base
77
+ def self.name; "ca"; end
78
+ def self.invoke(processor, alpha)
79
+ processor.update_graphics_state(non_stroke_alpha: alpha.to_f)
80
+ end
81
+ register
82
+ end
83
+
84
+ class StrokeAlpha < Base
85
+ def self.name; "CA"; end
86
+ def self.invoke(processor, alpha)
87
+ processor.update_graphics_state(stroke_alpha: alpha.to_f)
88
+ end
89
+ register
90
+ end
76
91
  end
77
92
  end
78
93
  end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pdfrb
4
+ module Content
5
+ # Operator implementation files. Each file defines one or more
6
+ # Operator::Base subclasses that register themselves on load.
7
+ # Eager loading is triggered by Operator.eager_load!.
8
+ module Operators
9
+ autoload :General, "pdfrb/content/operators/general"
10
+ autoload :Path, "pdfrb/content/operators/path"
11
+ autoload :Painting, "pdfrb/content/operators/painting"
12
+ autoload :TextState, "pdfrb/content/operators/text_state"
13
+ autoload :TextPositioning, "pdfrb/content/operators/text_positioning"
14
+ autoload :TextShowing, "pdfrb/content/operators/text_showing"
15
+ autoload :Color, "pdfrb/content/operators/color"
16
+ autoload :GraphicsStateParams, "pdfrb/content/operators/graphics_state_params"
17
+ autoload :MarkedContent, "pdfrb/content/operators/marked_content"
18
+ autoload :Clipping, "pdfrb/content/operators/clipping"
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,139 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pdfrb
4
+ module Content
5
+ module Shading
6
+ # Base shading builder. Subclasses set +shading_type+ and
7
+ # +shading_fields+. Creates a shading dict and registers it
8
+ # in the page's /Resources /Shading sub-dict.
9
+ class Base
10
+ attr_reader :color_space
11
+
12
+ class << self
13
+ def shading_type
14
+ raise NotImplementedError
15
+ end
16
+ end
17
+
18
+ def initialize(color_space: :DeviceGray)
19
+ @color_space = color_space
20
+ end
21
+
22
+ # Build the shading dictionary value (a Hash).
23
+ def to_dict
24
+ { ShadingType: self.class.shading_type,
25
+ ColorSpace: @color_space }.merge!(shading_fields)
26
+ end
27
+
28
+ # Register this shading on a page and return the resource name.
29
+ # @param document [Pdfrb::Document]
30
+ # @param page [Pdfrb::Model::Cos::Dictionary] target page.
31
+ # @return [Symbol] resource name (e.g. :Sh1)
32
+ def register_on(document, page)
33
+ shading_obj = document.add(to_dict, type: Pdfrb::Model::Cos::Dictionary)
34
+ ref = Pdfrb::Model::Reference.new(shading_obj.oid, shading_obj.gen)
35
+ resources = ensure_resources(document, page)
36
+ shading_dict = resources.value[:Shading]
37
+ if shading_dict.nil?
38
+ shading_dict = {}
39
+ resources.value[:Shading] = shading_dict
40
+ end
41
+ name = next_shading_name(shading_dict)
42
+ shading_dict[name] = ref
43
+ name
44
+ end
45
+
46
+ protected
47
+
48
+ def shading_fields
49
+ {}
50
+ end
51
+
52
+ private
53
+
54
+ def ensure_resources(document, page)
55
+ resources = page.value[:Resources]
56
+ return resources if resources.is_a?(Pdfrb::Model::Cos::Dictionary)
57
+
58
+ resources_hash = if resources.is_a?(Pdfrb::Model::Reference)
59
+ document.object(resources)&.value
60
+ else
61
+ resources
62
+ end
63
+ resources_hash ||= {}
64
+ wrapped = Pdfrb::Model::Cos::Dictionary.new(resources_hash)
65
+ page.value[:Resources] = wrapped
66
+ wrapped
67
+ end
68
+
69
+ def next_shading_name(shading_dict)
70
+ existing = shading_dict.keys.map(&:to_s).grep(/\ASh\d+\z/).map { |k| k[2..].to_i }
71
+ num = (existing.max || 0) + 1
72
+ :"Sh#{num}"
73
+ end
74
+ end
75
+
76
+ # Axial (linear) gradient. ShadingType 2.
77
+ # ISO 32000-2 §8.7.4.5.4.
78
+ class Axial < Base
79
+ attr_reader :start_point, :end_point, :function, :extend
80
+
81
+ def initialize(color_space:, start_point:, end_point:,
82
+ function:, extend: nil)
83
+ super(color_space: color_space)
84
+ @start_point = start_point
85
+ @end_point = end_point
86
+ @function = function
87
+ @extend = extend
88
+ end
89
+
90
+ class << self
91
+ def shading_type; 2; end
92
+ end
93
+
94
+ protected
95
+
96
+ def shading_fields
97
+ fields = {
98
+ Coords: Pdfrb::Model::PdfArray.new([*@start_point, *@end_point]),
99
+ Function: @function,
100
+ }
101
+ fields[:Extend] = Pdfrb::Model::PdfArray.new(@extend) if @extend
102
+ fields
103
+ end
104
+ end
105
+
106
+ # Radial gradient. ShadingType 3.
107
+ # ISO 32000-2 §8.7.4.5.3.
108
+ class Radial < Base
109
+ attr_reader :center1, :radius1, :center2, :radius2, :function, :extend
110
+
111
+ def initialize(color_space:, center1:, radius1:, center2:, radius2:,
112
+ function:, extend: nil)
113
+ super(color_space: color_space)
114
+ @center1 = center1
115
+ @radius1 = radius1
116
+ @center2 = center2
117
+ @radius2 = radius2
118
+ @function = function
119
+ @extend = extend
120
+ end
121
+
122
+ class << self
123
+ def shading_type; 3; end
124
+ end
125
+
126
+ protected
127
+
128
+ def shading_fields
129
+ fields = {
130
+ Coords: Pdfrb::Model::PdfArray.new([*@center1, @radius1, *@center2, @radius2]),
131
+ Function: @function,
132
+ }
133
+ fields[:Extend] = Pdfrb::Model::PdfArray.new(@extend) if @extend
134
+ fields
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pdfrb
4
+ module Content
5
+ # Shading patterns (ISO 32000-2 §8.7.4). Shadings define smooth
6
+ # color transitions: axial (linear) gradients, radial gradients,
7
+ # and free-form Gouraud-triangle meshes.
8
+ #
9
+ # Each shading is a dictionary with /ShadingType and type-specific
10
+ # parameters. Registered in page Resources under /Shading.
11
+ module Shading
12
+ autoload :Base, "pdfrb/content/shading/base"
13
+ autoload :Axial, "pdfrb/content/shading/base"
14
+ autoload :Radial, "pdfrb/content/shading/base"
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pdfrb
4
+ module Content
5
+ module SmartTextExtractor
6
+ module_function
7
+
8
+ def extract(document)
9
+ results = []
10
+ document.pages.each do |page|
11
+ text = extract_page_text(document, page)
12
+ results << { page: page, text: text }
13
+ end
14
+
15
+ block_given? ? yield(results) : results
16
+ end
17
+
18
+ def extract_page_text(document, page)
19
+ contents = page.value[:Contents]
20
+ return "" unless contents
21
+
22
+ ref = contents.is_a?(Pdfrb::Model::Reference) ? contents : nil
23
+ return "" unless ref
24
+
25
+ stream = document.object(ref)
26
+ return "" unless stream.is_a?(Pdfrb::Model::Cos::Stream)
27
+
28
+ data = stream.decoded_stream
29
+ return "" unless data && !data.empty?
30
+
31
+ collector = TextCollector.new(document, page)
32
+ begin
33
+ collector.process(data)
34
+ rescue StandardError
35
+ # Best effort extraction
36
+ end
37
+ collector.text
38
+ end
39
+
40
+ class TextCollector < Pdfrb::Content::Processor
41
+ attr_reader :text
42
+
43
+ def initialize(document, page)
44
+ super()
45
+ @document = document
46
+ @page = page
47
+ @text = +""
48
+ @last_y = nil
49
+ end
50
+
51
+ def show_text(str)
52
+ insert_line_break_if_needed
53
+ @text << decode_text(str)
54
+ end
55
+
56
+ def show_text_array(arr)
57
+ insert_line_break_if_needed
58
+ arr.each do |e|
59
+ @text << decode_text(e) if e.is_a?(String)
60
+ end
61
+ end
62
+
63
+ def move_text(tx, ty, **)
64
+ super
65
+ insert_line_break_if_needed
66
+ end
67
+
68
+ private
69
+
70
+ def insert_line_break_if_needed
71
+ cur_y = graphics_state.text_state.text_matrix.f
72
+ if @last_y && (cur_y - @last_y).abs > 0.001
73
+ @text << "\n"
74
+ end
75
+ @last_y = cur_y
76
+ end
77
+
78
+ def decode_text(bytes)
79
+ bytes.to_s.dup.force_encoding("UTF-8")
80
+ end
81
+ end
82
+ private_constant :TextCollector
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pdfrb
4
+ module Content
5
+ # Tiling patterns (ISO 32000-2 §8.7.3). A tiling pattern is a
6
+ # repeating cell that fills an area. Type 1 (constant spacing) and
7
+ # Type 2 (no distortion) are supported.
8
+ #
9
+ # The pattern cell is drawn via a block using the Canvas API.
10
+ # Registered in page Resources under /Pattern.
11
+ class TilingPattern
12
+ PATTERN_TYPE = 1
13
+
14
+ attr_reader :paint_type, :tiling_type, :bbox, :x_step, :y_step
15
+
16
+ # @param paint_type [Integer] 1 = colored, 2 = uncolored.
17
+ # @param tiling_type [Integer] 1 = constant, 2 = no distortion, 3 = constant spacing + no distortion.
18
+ # @param bbox [Array] bounding box of the pattern cell.
19
+ # @param x_step [Numeric] horizontal repeat distance.
20
+ # @param y_step [Numeric] vertical repeat distance.
21
+ def initialize(paint_type: 1, tiling_type: 1, bbox: [0, 0, 16, 16],
22
+ x_step: 16, y_step: 16)
23
+ @paint_type = paint_type
24
+ @tiling_type = tiling_type
25
+ @bbox = bbox
26
+ @x_step = x_step
27
+ @y_step = y_step
28
+ end
29
+
30
+ # Build the pattern stream and register it in the page's Resources.
31
+ # @param document [Pdfrb::Document]
32
+ # @param page [Pdfrb::Model::Cos::Dictionary] target page.
33
+ # @yield [canvas] block that draws the pattern cell.
34
+ # @return [Symbol] the resource name (e.g. :P1)
35
+ def register_on(document, page)
36
+ pattern_stream = document.add(
37
+ {
38
+ Type: :Pattern,
39
+ PatternType: PATTERN_TYPE,
40
+ PaintType: @paint_type,
41
+ TilingType: @tiling_type,
42
+ BBox: @bbox,
43
+ XStep: @x_step,
44
+ YStep: @y_step,
45
+ },
46
+ type: Pdfrb::Model::Cos::Stream
47
+ )
48
+
49
+ if block_given?
50
+ canvas = Pdfrb::Content::Canvas.new(pattern_stream, document: document)
51
+ yield canvas
52
+ end
53
+
54
+ ref = Pdfrb::Model::Reference.new(pattern_stream.oid, pattern_stream.gen)
55
+ register_in_resources(document, page, ref)
56
+ end
57
+
58
+ private
59
+
60
+ def register_in_resources(_document, page, ref)
61
+ resources = page.value[:Resources]
62
+ resources = {} unless resources.is_a?(::Hash)
63
+ pattern_dict = resources[:Pattern] || {}
64
+ name = next_pattern_name(pattern_dict)
65
+ pattern_dict[name] = ref
66
+ resources[:Pattern] = pattern_dict
67
+ page.value[:Resources] = resources
68
+ name
69
+ end
70
+
71
+ def next_pattern_name(pattern_dict)
72
+ existing = pattern_dict.keys.map(&:to_s).grep(/\AP\d+\z/).map { |k| k[1..].to_i }
73
+ num = (existing.max || 0) + 1
74
+ :"P#{num}"
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pdfrb
4
+ module Content
5
+ class TransformationMatrix
6
+ attr_reader :a, :b, :c, :d, :e, :f
7
+
8
+ def initialize(a = 1, b = 0, c = 0, d = 1, e = 0, f = 0)
9
+ @a = a.to_f
10
+ @b = b.to_f
11
+ @c = c.to_f
12
+ @d = d.to_f
13
+ @e = e.to_f
14
+ @f = f.to_f
15
+ freeze
16
+ end
17
+
18
+ def identity?
19
+ @a == 1 && @b.zero? && @c.zero? && @d == 1 && @e.zero? && @f.zero?
20
+ end
21
+
22
+ def multiply(other)
23
+ self.class.new(
24
+ (@a * other.a) + (@c * other.b),
25
+ (@b * other.a) + (@d * other.b),
26
+ (@a * other.c) + (@c * other.d),
27
+ (@b * other.c) + (@d * other.d),
28
+ (@a * other.e) + (@c * other.f) + @e,
29
+ (@b * other.e) + (@d * other.f) + @f,
30
+ )
31
+ end
32
+
33
+ def translate(tx, ty)
34
+ multiply(self.class.new(1, 0, 0, 1, tx, ty))
35
+ end
36
+
37
+ def scale(sx, sy = sx)
38
+ multiply(self.class.new(sx, 0, 0, sy, 0, 0))
39
+ end
40
+
41
+ def rotate(angle_radians)
42
+ cos = Math.cos(angle_radians)
43
+ sin = Math.sin(angle_radians)
44
+ multiply(self.class.new(cos, sin, -sin, cos, 0, 0))
45
+ end
46
+
47
+ def skew(fa, fb)
48
+ multiply(self.class.new(1, Math.tan(fa), Math.tan(fb), 1, 0, 0))
49
+ end
50
+
51
+ def to_a
52
+ [@a, @b, @c, @d, @e, @f]
53
+ end
54
+
55
+ def ==(other)
56
+ other.is_a?(self.class) && to_a == other.to_a
57
+ end
58
+ alias eql? ==
59
+ end
60
+ end
61
+ end