pdfrb 0.3.0 → 0.7.1

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 (422) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/CHANGELOG.md +51 -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 +61 -0
  23. data/data/pdfrb/afm/Courier-Bold.afm +342 -342
  24. data/data/pdfrb/afm/Courier-BoldOblique.afm +342 -342
  25. data/data/pdfrb/afm/Courier-Oblique.afm +342 -342
  26. data/data/pdfrb/afm/Courier.afm +342 -342
  27. data/data/pdfrb/afm/Helvetica-Bold.afm +2827 -2827
  28. data/data/pdfrb/afm/Helvetica-BoldOblique.afm +2827 -2827
  29. data/data/pdfrb/afm/Helvetica-Oblique.afm +3051 -3051
  30. data/data/pdfrb/afm/Helvetica.afm +3051 -3051
  31. data/data/pdfrb/afm/Symbol.afm +213 -213
  32. data/data/pdfrb/afm/Times-Bold.afm +2588 -2588
  33. data/data/pdfrb/afm/Times-BoldItalic.afm +2384 -2384
  34. data/data/pdfrb/afm/Times-Italic.afm +2667 -2667
  35. data/data/pdfrb/afm/Times-Roman.afm +2419 -2419
  36. data/data/pdfrb/afm/ZapfDingbats.afm +225 -225
  37. data/lib/pdfrb/action/base.rb +21 -0
  38. data/lib/pdfrb/action/types.rb +178 -0
  39. data/lib/pdfrb/action.rb +55 -0
  40. data/lib/pdfrb/annotation/base.rb +79 -0
  41. data/lib/pdfrb/annotation/types.rb +222 -0
  42. data/lib/pdfrb/annotation.rb +78 -0
  43. data/lib/pdfrb/appearance/generator.rb +216 -0
  44. data/lib/pdfrb/appearance.rb +11 -0
  45. data/lib/pdfrb/arlington/object_definition.rb +2 -2
  46. data/lib/pdfrb/arlington/predicate/evaluator.rb +1 -1
  47. data/lib/pdfrb/arlington/predicate/functions.rb +25 -9
  48. data/lib/pdfrb/arlington/predicate.rb +1 -5
  49. data/lib/pdfrb/cli/batch.rb +18 -0
  50. data/lib/pdfrb/cli/debug_info.rb +18 -0
  51. data/lib/pdfrb/cli/files.rb +16 -0
  52. data/lib/pdfrb/cli/fonts.rb +16 -0
  53. data/lib/pdfrb/cli/form.rb +18 -0
  54. data/lib/pdfrb/cli/image2pdf.rb +18 -0
  55. data/lib/pdfrb/cli/images.rb +34 -0
  56. data/lib/pdfrb/cli/info.rb +20 -0
  57. data/lib/pdfrb/cli/inspect.rb +18 -0
  58. data/lib/pdfrb/cli/merge.rb +18 -0
  59. data/lib/pdfrb/cli/modify.rb +18 -0
  60. data/lib/pdfrb/cli/optimize.rb +18 -0
  61. data/lib/pdfrb/cli/split.rb +21 -0
  62. data/lib/pdfrb/cli/watermark.rb +18 -0
  63. data/lib/pdfrb/cli.rb +291 -7
  64. data/lib/pdfrb/color/color_space.rb +259 -0
  65. data/lib/pdfrb/color/icc_profile.rb +90 -0
  66. data/lib/pdfrb/color.rb +12 -0
  67. data/lib/pdfrb/compare/comparator.rb +244 -0
  68. data/lib/pdfrb/compare/report.rb +78 -0
  69. data/lib/pdfrb/compare.rb +42 -0
  70. data/lib/pdfrb/composer.rb +161 -0
  71. data/lib/pdfrb/conformance/pdf_a.rb +317 -56
  72. data/lib/pdfrb/conformance/pdf_ua.rb +186 -29
  73. data/lib/pdfrb/conformance/pdf_x.rb +247 -0
  74. data/lib/pdfrb/conformance/rule.rb +78 -0
  75. data/lib/pdfrb/conformance/structure_elements.rb +174 -0
  76. data/lib/pdfrb/conformance/verapdf_bridge.rb +92 -0
  77. data/lib/pdfrb/conformance.rb +7 -3
  78. data/lib/pdfrb/content/canvas.rb +354 -72
  79. data/lib/pdfrb/content/color_space.rb +157 -0
  80. data/lib/pdfrb/content/hidden_text_detector.rb +167 -0
  81. data/lib/pdfrb/content/operator.rb +21 -1
  82. data/lib/pdfrb/content/operators/clipping.rb +36 -0
  83. data/lib/pdfrb/content/operators/graphics_state_params.rb +15 -0
  84. data/lib/pdfrb/content/operators/inline_image.rb +53 -0
  85. data/lib/pdfrb/content/operators.rb +22 -0
  86. data/lib/pdfrb/content/shading/base.rb +139 -0
  87. data/lib/pdfrb/content/shading.rb +17 -0
  88. data/lib/pdfrb/content/smart_text_extractor.rb +85 -0
  89. data/lib/pdfrb/content/tiling_pattern.rb +78 -0
  90. data/lib/pdfrb/content/transformation_matrix.rb +61 -0
  91. data/lib/pdfrb/content.rb +7 -26
  92. data/lib/pdfrb/destination/fit.rb +185 -0
  93. data/lib/pdfrb/destination.rb +13 -0
  94. data/lib/pdfrb/digital_signature/cms_handler.rb +22 -0
  95. data/lib/pdfrb/digital_signature/signing.rb +107 -11
  96. data/lib/pdfrb/digital_signature/timestamp_handler.rb +45 -0
  97. data/lib/pdfrb/digital_signature/verification.rb +88 -9
  98. data/lib/pdfrb/digital_signature/verification_result.rb +14 -0
  99. data/lib/pdfrb/digital_signature.rb +26 -2
  100. data/lib/pdfrb/document/annotations.rb +120 -3
  101. data/lib/pdfrb/document/associated_files.rb +104 -0
  102. data/lib/pdfrb/document/colors.rb +95 -0
  103. data/lib/pdfrb/document/destinations.rb +127 -5
  104. data/lib/pdfrb/document/display.rb +84 -0
  105. data/lib/pdfrb/document/fonts.rb +417 -45
  106. data/lib/pdfrb/document/form.rb +338 -0
  107. data/lib/pdfrb/document/graphics_state.rb +91 -0
  108. data/lib/pdfrb/document/images.rb +4 -4
  109. data/lib/pdfrb/document/info.rb +102 -0
  110. data/lib/pdfrb/document/layers.rb +62 -0
  111. data/lib/pdfrb/document/metadata.rb +12 -5
  112. data/lib/pdfrb/document/output_intents.rb +91 -0
  113. data/lib/pdfrb/document/page_labels.rb +80 -0
  114. data/lib/pdfrb/document/pages.rb +68 -19
  115. data/lib/pdfrb/document/portfolio.rb +119 -0
  116. data/lib/pdfrb/document/shadings.rb +159 -0
  117. data/lib/pdfrb/document/stamps.rb +68 -0
  118. data/lib/pdfrb/document/structure.rb +242 -0
  119. data/lib/pdfrb/document.rb +73 -4
  120. data/lib/pdfrb/encryption/identity.rb +13 -0
  121. data/lib/pdfrb/encryption/password_verification.rb +53 -8
  122. data/lib/pdfrb/encryption/security_handler.rb +8 -5
  123. data/lib/pdfrb/encryption/standard_security_handler.rb +154 -81
  124. data/lib/pdfrb/encryption.rb +17 -0
  125. data/lib/pdfrb/error.rb +2 -2
  126. data/lib/pdfrb/filter/brotli_decode.rb +32 -0
  127. data/lib/pdfrb/filter/ccitt_fax_decode.rb +12 -0
  128. data/lib/pdfrb/filter/crypt.rb +2 -2
  129. data/lib/pdfrb/filter/dct_decode.rb +18 -0
  130. data/lib/pdfrb/filter/flate_decode.rb +4 -18
  131. data/lib/pdfrb/filter/jbig2_decode.rb +12 -0
  132. data/lib/pdfrb/filter/jpx_decode.rb +18 -0
  133. data/lib/pdfrb/filter/lzw_decode.rb +3 -2
  134. data/lib/pdfrb/filter/png_predictor.rb +94 -0
  135. data/lib/pdfrb/filter.rb +26 -13
  136. data/lib/pdfrb/font/afm_parser.rb +50 -26
  137. data/lib/pdfrb/font/cmap/writer.rb +9 -0
  138. data/lib/pdfrb/font/encoding/win_ansi_encoding.rb +17 -0
  139. data/lib/pdfrb/font/encoding.rb +32 -3
  140. data/lib/pdfrb/font/metrics_helper.rb +121 -0
  141. data/lib/pdfrb/font/true_type/file.rb +10 -1
  142. data/lib/pdfrb/font/true_type/glyf.rb +82 -0
  143. data/lib/pdfrb/font/true_type/kern.rb +58 -0
  144. data/lib/pdfrb/font/true_type/loca.rb +42 -0
  145. data/lib/pdfrb/font/true_type/maxp.rb +44 -0
  146. data/lib/pdfrb/font/true_type/name.rb +53 -0
  147. data/lib/pdfrb/font/true_type/post.rb +93 -0
  148. data/lib/pdfrb/font/true_type/subsetter.rb +3 -5
  149. data/lib/pdfrb/font/true_type/wrapper.rb +102 -0
  150. data/lib/pdfrb/font/true_type.rb +7 -0
  151. data/lib/pdfrb/font.rb +2 -0
  152. data/lib/pdfrb/font_loader/from_configuration.rb +19 -0
  153. data/lib/pdfrb/font_loader/from_file.rb +20 -0
  154. data/lib/pdfrb/font_loader/standard14.rb +20 -0
  155. data/lib/pdfrb/font_loader/variant_from_name.rb +36 -0
  156. data/lib/pdfrb/font_loader.rb +4 -0
  157. data/lib/pdfrb/font_resolver.rb +138 -0
  158. data/lib/pdfrb/image/audit.rb +94 -0
  159. data/lib/pdfrb/image/downsampler.rb +124 -0
  160. data/lib/pdfrb/image.rb +13 -0
  161. data/lib/pdfrb/image_loader/jpeg.rb +39 -58
  162. data/lib/pdfrb/image_loader/png.rb +82 -251
  163. data/lib/pdfrb/layout/bidi.rb +198 -0
  164. data/lib/pdfrb/layout/box.rb +49 -0
  165. data/lib/pdfrb/layout/box_fitter.rb +54 -0
  166. data/lib/pdfrb/layout/column_box.rb +71 -0
  167. data/lib/pdfrb/layout/container_box.rb +45 -0
  168. data/lib/pdfrb/layout/frame.rb +67 -0
  169. data/lib/pdfrb/layout/image_box.rb +44 -0
  170. data/lib/pdfrb/layout/inline_box.rb +25 -0
  171. data/lib/pdfrb/layout/line.rb +55 -0
  172. data/lib/pdfrb/layout/list_box.rb +87 -0
  173. data/lib/pdfrb/layout/numeric_refinements.rb +43 -0
  174. data/lib/pdfrb/layout/page_style.rb +65 -0
  175. data/lib/pdfrb/layout/style.rb +62 -0
  176. data/lib/pdfrb/layout/table_box.rb +66 -0
  177. data/lib/pdfrb/layout/text_box.rb +38 -0
  178. data/lib/pdfrb/layout/text_fragment.rb +45 -0
  179. data/lib/pdfrb/layout/text_layouter.rb +123 -0
  180. data/lib/pdfrb/layout.rb +28 -0
  181. data/lib/pdfrb/linearization/hint_stream.rb +93 -0
  182. data/lib/pdfrb/linearization/writer.rb +259 -0
  183. data/lib/pdfrb/linearization.rb +21 -0
  184. data/lib/pdfrb/model/cos/dictionary.rb +12 -0
  185. data/lib/pdfrb/model/pdf_array.rb +2 -2
  186. data/lib/pdfrb/model/type/action.rb +42 -0
  187. data/lib/pdfrb/model/type/action_go_to_3d_view.rb +14 -0
  188. data/lib/pdfrb/model/type/action_go_to_dp.rb +16 -0
  189. data/lib/pdfrb/model/type/action_go_to_r.rb +26 -0
  190. data/lib/pdfrb/model/type/action_goto.rb +34 -0
  191. data/lib/pdfrb/model/type/action_hide.rb +26 -0
  192. data/lib/pdfrb/model/type/action_import_data.rb +18 -0
  193. data/lib/pdfrb/model/type/action_java_script.rb +18 -0
  194. data/lib/pdfrb/model/type/action_launch.rb +22 -0
  195. data/lib/pdfrb/model/type/action_movie.rb +22 -0
  196. data/lib/pdfrb/model/type/action_named.rb +23 -0
  197. data/lib/pdfrb/model/type/action_nop.rb +12 -0
  198. data/lib/pdfrb/model/type/action_rendition.rb +28 -0
  199. data/lib/pdfrb/model/type/action_reset_form.rb +22 -0
  200. data/lib/pdfrb/model/type/action_rich_media_execute.rb +17 -0
  201. data/lib/pdfrb/model/type/action_set_ocg_state.rb +32 -0
  202. data/lib/pdfrb/model/type/action_set_state.rb +14 -0
  203. data/lib/pdfrb/model/type/action_sound_action.rb +23 -0
  204. data/lib/pdfrb/model/type/action_submit_form.rb +64 -0
  205. data/lib/pdfrb/model/type/action_thread.rb +23 -0
  206. data/lib/pdfrb/model/type/action_trans.rb +30 -0
  207. data/lib/pdfrb/model/type/action_uri.rb +30 -0
  208. data/lib/pdfrb/model/type/af_embedded_file.rb +11 -0
  209. data/lib/pdfrb/model/type/af_file_specification.rb +34 -0
  210. data/lib/pdfrb/model/type/annotation.rb +49 -10
  211. data/lib/pdfrb/model/type/appearance.rb +41 -0
  212. data/lib/pdfrb/model/type/appearance_characteristics.rb +36 -0
  213. data/lib/pdfrb/model/type/appearance_generator.rb +22 -0
  214. data/lib/pdfrb/model/type/appearance_trap_net.rb +84 -0
  215. data/lib/pdfrb/model/type/border_effect.rb +25 -0
  216. data/lib/pdfrb/model/type/border_styling.rb +38 -0
  217. data/lib/pdfrb/model/type/box_color_info.rb +21 -0
  218. data/lib/pdfrb/model/type/box_style.rb +19 -0
  219. data/lib/pdfrb/model/type/button.rb +17 -0
  220. data/lib/pdfrb/model/type/cal_gray.rb +17 -0
  221. data/lib/pdfrb/model/type/cal_rgb.rb +22 -0
  222. data/lib/pdfrb/model/type/caret_annotation.rb +21 -0
  223. data/lib/pdfrb/model/type/catalog.rb +99 -16
  224. data/lib/pdfrb/model/type/choice.rb +21 -0
  225. data/lib/pdfrb/model/type/cid_font.rb +52 -1
  226. data/lib/pdfrb/model/type/cid_font_descriptor_metrics.rb +28 -0
  227. data/lib/pdfrb/model/type/cid_system_info.rb +28 -0
  228. data/lib/pdfrb/model/type/circle_annotation.rb +20 -0
  229. data/lib/pdfrb/model/type/collection.rb +27 -0
  230. data/lib/pdfrb/model/type/collection_field.rb +24 -0
  231. data/lib/pdfrb/model/type/collection_item.rb +19 -0
  232. data/lib/pdfrb/model/type/collection_schema.rb +25 -0
  233. data/lib/pdfrb/model/type/collection_sort.rb +13 -0
  234. data/lib/pdfrb/model/type/crypt_filter.rb +58 -0
  235. data/lib/pdfrb/model/type/device_n.rb +37 -0
  236. data/lib/pdfrb/model/type/device_n_mixing_hints.rb +17 -0
  237. data/lib/pdfrb/model/type/device_n_process.rb +13 -0
  238. data/lib/pdfrb/model/type/doc_mdp_transform_parameters.rb +21 -0
  239. data/lib/pdfrb/model/type/document_security_store.rb +55 -0
  240. data/lib/pdfrb/model/type/embedded_file.rb +19 -0
  241. data/lib/pdfrb/model/type/embedded_file_parameter.rb +48 -0
  242. data/lib/pdfrb/model/type/encryption_public_key.rb +38 -0
  243. data/lib/pdfrb/model/type/encryption_standard.rb +68 -0
  244. data/lib/pdfrb/model/type/field_mdp_transform_parameters.rb +26 -0
  245. data/lib/pdfrb/model/type/file_attachment_annotation.rb +29 -0
  246. data/lib/pdfrb/model/type/file_spec_ef.rb +22 -0
  247. data/lib/pdfrb/model/type/file_spec_rf.rb +21 -0
  248. data/lib/pdfrb/model/type/file_specification.rb +43 -0
  249. data/lib/pdfrb/model/type/file_trailer.rb +54 -0
  250. data/lib/pdfrb/model/type/font.rb +73 -2
  251. data/lib/pdfrb/model/type/font_cid_type0.rb +17 -0
  252. data/lib/pdfrb/model/type/font_cid_type2.rb +17 -0
  253. data/lib/pdfrb/model/type/font_descriptor.rb +47 -0
  254. data/lib/pdfrb/model/type/font_descriptor_cid_type0.rb +15 -0
  255. data/lib/pdfrb/model/type/font_descriptor_cid_type2.rb +15 -0
  256. data/lib/pdfrb/model/type/font_encoding.rb +47 -0
  257. data/lib/pdfrb/model/type/font_file.rb +28 -0
  258. data/lib/pdfrb/model/type/font_file2.rb +32 -0
  259. data/lib/pdfrb/model/type/font_file3.rb +23 -0
  260. data/lib/pdfrb/model/type/font_multiple_master.rb +60 -0
  261. data/lib/pdfrb/model/type/font_true_type.rb +1 -1
  262. data/lib/pdfrb/model/type/font_type0.rb +1 -1
  263. data/lib/pdfrb/model/type/font_type1.rb +1 -1
  264. data/lib/pdfrb/model/type/font_type3.rb +1 -1
  265. data/lib/pdfrb/model/type/form_field.rb +73 -0
  266. data/lib/pdfrb/model/type/free_text_annotation.rb +37 -0
  267. data/lib/pdfrb/model/type/function.rb +42 -0
  268. data/lib/pdfrb/model/type/function_exponential.rb +31 -0
  269. data/lib/pdfrb/model/type/function_postscript.rb +25 -0
  270. data/lib/pdfrb/model/type/function_sampled.rb +55 -0
  271. data/lib/pdfrb/model/type/function_stitching.rb +33 -0
  272. data/lib/pdfrb/model/type/generic_appearance.rb +40 -0
  273. data/lib/pdfrb/model/type/graphics_state_parameter.rb +69 -0
  274. data/lib/pdfrb/model/type/halftone.rb +21 -0
  275. data/lib/pdfrb/model/type/halftone_type1.rb +17 -0
  276. data/lib/pdfrb/model/type/halftone_type10.rb +16 -0
  277. data/lib/pdfrb/model/type/halftone_type16.rb +11 -0
  278. data/lib/pdfrb/model/type/halftone_type5.rb +24 -0
  279. data/lib/pdfrb/model/type/halftone_type6.rb +16 -0
  280. data/lib/pdfrb/model/type/highlight_annotation.rb +11 -0
  281. data/lib/pdfrb/model/type/icc_based_color_space.rb +24 -0
  282. data/lib/pdfrb/model/type/icon_fit.rb +31 -0
  283. data/lib/pdfrb/model/type/indexed.rb +32 -0
  284. data/lib/pdfrb/model/type/info.rb +44 -0
  285. data/lib/pdfrb/model/type/ink_annotation.rb +29 -0
  286. data/lib/pdfrb/model/type/interactive_form.rb +36 -0
  287. data/lib/pdfrb/model/type/interior_color.rb +12 -0
  288. data/lib/pdfrb/model/type/jbig2_globals.rb +15 -0
  289. data/lib/pdfrb/model/type/lab.rb +27 -0
  290. data/lib/pdfrb/model/type/line_annotation.rb +44 -0
  291. data/lib/pdfrb/model/type/line_ending_styling.rb +39 -0
  292. data/lib/pdfrb/model/type/link_annotation.rb +41 -0
  293. data/lib/pdfrb/model/type/link_appearance.rb +9 -0
  294. data/lib/pdfrb/model/type/mark_information.rb +23 -0
  295. data/lib/pdfrb/model/type/marked_content_reference.rb +24 -0
  296. data/lib/pdfrb/model/type/markup_annotation.rb +36 -0
  297. data/lib/pdfrb/model/type/mdp_dict.rb +28 -0
  298. data/lib/pdfrb/model/type/measure.rb +39 -0
  299. data/lib/pdfrb/model/type/media_clip.rb +28 -0
  300. data/lib/pdfrb/model/type/media_offset.rb +106 -0
  301. data/lib/pdfrb/model/type/metadata.rb +27 -3
  302. data/lib/pdfrb/model/type/misc_helpers.rb +75 -0
  303. data/lib/pdfrb/model/type/movie.rb +28 -0
  304. data/lib/pdfrb/model/type/names.rb +51 -0
  305. data/lib/pdfrb/model/type/namespace.rb +27 -0
  306. data/lib/pdfrb/model/type/object_reference.rb +31 -0
  307. data/lib/pdfrb/model/type/object_stream.rb +38 -0
  308. data/lib/pdfrb/model/type/opt_content_ext.rb +94 -0
  309. data/lib/pdfrb/model/type/opt_content_page_element.rb +18 -0
  310. data/lib/pdfrb/model/type/optional_content_config.rb +56 -0
  311. data/lib/pdfrb/model/type/optional_content_group.rb +35 -10
  312. data/lib/pdfrb/model/type/optional_content_membership.rb +36 -2
  313. data/lib/pdfrb/model/type/optional_content_properties.rb +16 -1
  314. data/lib/pdfrb/model/type/outline.rb +40 -6
  315. data/lib/pdfrb/model/type/outline_item.rb +45 -0
  316. data/lib/pdfrb/model/type/output_intent.rb +30 -0
  317. data/lib/pdfrb/model/type/page.rb +127 -0
  318. data/lib/pdfrb/model/type/page_label.rb +68 -0
  319. data/lib/pdfrb/model/type/page_piece_info.rb +58 -0
  320. data/lib/pdfrb/model/type/page_tree_node.rb +51 -16
  321. data/lib/pdfrb/model/type/page_tree_node_root.rb +15 -0
  322. data/lib/pdfrb/model/type/pattern.rb +16 -0
  323. data/lib/pdfrb/model/type/pattern_shading.rb +25 -0
  324. data/lib/pdfrb/model/type/pattern_tiling.rb +25 -0
  325. data/lib/pdfrb/model/type/polygon_annotation.rb +29 -0
  326. data/lib/pdfrb/model/type/polygon_polyline.rb +14 -0
  327. data/lib/pdfrb/model/type/polyline_annotation.rb +29 -0
  328. data/lib/pdfrb/model/type/popup_annotation.rb +20 -0
  329. data/lib/pdfrb/model/type/printer_mark_annotation.rb +13 -0
  330. data/lib/pdfrb/model/type/redact_annotation.rb +28 -0
  331. data/lib/pdfrb/model/type/rendition.rb +31 -0
  332. data/lib/pdfrb/model/type/resources.rb +59 -14
  333. data/lib/pdfrb/model/type/rich_media_activation.rb +24 -0
  334. data/lib/pdfrb/model/type/rich_media_animation.rb +25 -0
  335. data/lib/pdfrb/model/type/rich_media_annotation.rb +18 -0
  336. data/lib/pdfrb/model/type/rich_media_command.rb +19 -0
  337. data/lib/pdfrb/model/type/rich_media_configuration.rb +32 -0
  338. data/lib/pdfrb/model/type/rich_media_content.rb +28 -0
  339. data/lib/pdfrb/model/type/rich_media_cue_point.rb +18 -0
  340. data/lib/pdfrb/model/type/rich_media_deactivation.rb +17 -0
  341. data/lib/pdfrb/model/type/rich_media_instance.rb +33 -0
  342. data/lib/pdfrb/model/type/rich_media_presentation.rb +29 -0
  343. data/lib/pdfrb/model/type/rich_media_settings.rb +33 -0
  344. data/lib/pdfrb/model/type/rich_media_window.rb +20 -0
  345. data/lib/pdfrb/model/type/screen_annotation.rb +19 -0
  346. data/lib/pdfrb/model/type/separation.rb +25 -0
  347. data/lib/pdfrb/model/type/sig_field_lock.rb +25 -0
  348. data/lib/pdfrb/model/type/sig_field_seed_value.rb +25 -0
  349. data/lib/pdfrb/model/type/signature.rb +65 -0
  350. data/lib/pdfrb/model/type/signature_build.rb +86 -0
  351. data/lib/pdfrb/model/type/signature_field.rb +17 -0
  352. data/lib/pdfrb/model/type/software_identifier.rb +19 -0
  353. data/lib/pdfrb/model/type/sound.rb +38 -0
  354. data/lib/pdfrb/model/type/sound_annotation.rb +21 -0
  355. data/lib/pdfrb/model/type/square_annotation.rb +24 -0
  356. data/lib/pdfrb/model/type/square_circle.rb +13 -0
  357. data/lib/pdfrb/model/type/squiggly_annotation.rb +11 -0
  358. data/lib/pdfrb/model/type/stamp_annotation.rb +19 -0
  359. data/lib/pdfrb/model/type/strikeout_annotation.rb +11 -0
  360. data/lib/pdfrb/model/type/struct_elem.rb +75 -0
  361. data/lib/pdfrb/model/type/struct_tree_root.rb +34 -0
  362. data/lib/pdfrb/model/type/structure_attributes.rb +44 -0
  363. data/lib/pdfrb/model/type/structure_element_kid.rb +33 -0
  364. data/lib/pdfrb/model/type/text_annotation.rb +25 -0
  365. data/lib/pdfrb/model/type/text_appearance.rb +9 -0
  366. data/lib/pdfrb/model/type/text_field.rb +21 -0
  367. data/lib/pdfrb/model/type/text_markup_annotation.rb +30 -0
  368. data/lib/pdfrb/model/type/three_d_activation.rb +32 -0
  369. data/lib/pdfrb/model/type/three_d_animation_style.rb +24 -0
  370. data/lib/pdfrb/model/type/three_d_background.rb +28 -0
  371. data/lib/pdfrb/model/type/three_d_cross_section.rb +25 -0
  372. data/lib/pdfrb/model/type/three_d_lighting_scheme.rb +26 -0
  373. data/lib/pdfrb/model/type/three_d_measure.rb +20 -0
  374. data/lib/pdfrb/model/type/three_d_measure_3dc.rb +23 -0
  375. data/lib/pdfrb/model/type/three_d_measure_ad3.rb +14 -0
  376. data/lib/pdfrb/model/type/three_d_measure_ld3.rb +14 -0
  377. data/lib/pdfrb/model/type/three_d_measure_pd3.rb +20 -0
  378. data/lib/pdfrb/model/type/three_d_measure_rd3.rb +14 -0
  379. data/lib/pdfrb/model/type/three_d_node.rb +37 -0
  380. data/lib/pdfrb/model/type/three_d_reference.rb +21 -0
  381. data/lib/pdfrb/model/type/three_d_render_mode.rb +34 -0
  382. data/lib/pdfrb/model/type/three_d_stream.rb +48 -0
  383. data/lib/pdfrb/model/type/three_d_units.rb +18 -0
  384. data/lib/pdfrb/model/type/three_d_view.rb +40 -0
  385. data/lib/pdfrb/model/type/to_unicode_cmap_stream.rb +23 -0
  386. data/lib/pdfrb/model/type/transition.rb +68 -0
  387. data/lib/pdfrb/model/type/underline_annotation.rb +11 -0
  388. data/lib/pdfrb/model/type/ur_transform_parameters.rb +33 -0
  389. data/lib/pdfrb/model/type/uri_dict.rb +18 -0
  390. data/lib/pdfrb/model/type/url_alias.rb +19 -0
  391. data/lib/pdfrb/model/type/variable_text_field.rb +47 -0
  392. data/lib/pdfrb/model/type/viewer_preferences.rb +42 -0
  393. data/lib/pdfrb/model/type/watermark_annotation.rb +13 -0
  394. data/lib/pdfrb/model/type/widget_annotation.rb +48 -0
  395. data/lib/pdfrb/model/type/widget_appearance.rb +9 -0
  396. data/lib/pdfrb/model/type/xobject_form.rb +41 -0
  397. data/lib/pdfrb/model/type/xobject_image.rb +56 -0
  398. data/lib/pdfrb/model/type/xref_stream.rb +37 -0
  399. data/lib/pdfrb/model/type.rb +289 -6
  400. data/lib/pdfrb/serializer.rb +2 -2
  401. data/lib/pdfrb/source/tokenizer.rb +0 -2
  402. data/lib/pdfrb/task/benchmark.rb +83 -0
  403. data/lib/pdfrb/task/extract_text.rb +1 -1
  404. data/lib/pdfrb/task/generate_corpus.rb +130 -0
  405. data/lib/pdfrb/task/memory_profile.rb +84 -0
  406. data/lib/pdfrb/task/optimize.rb +97 -7
  407. data/lib/pdfrb/task/regenerate_appearances.rb +98 -0
  408. data/lib/pdfrb/task.rb +4 -3
  409. data/lib/pdfrb/test_utils.rb +112 -0
  410. data/lib/pdfrb/validator.rb +82 -0
  411. data/lib/pdfrb/version.rb +1 -1
  412. data/lib/pdfrb/writer.rb +117 -27
  413. data/lib/pdfrb/xmp/packet.rb +122 -0
  414. data/lib/pdfrb/xmp/schemas.rb +96 -0
  415. data/lib/pdfrb/xmp.rb +44 -0
  416. data/lib/pdfrb/xref_section.rb +2 -2
  417. data/lib/pdfrb.rb +24 -54
  418. data/pdfrb.gemspec +45 -0
  419. data/script/apply_fixes.sh +843 -0
  420. data/script/make_jpeg_fixture.rb +53 -0
  421. data/script/make_png_fixture.rb +25 -0
  422. metadata +319 -4
@@ -0,0 +1,157 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pdfrb
4
+ module Content
5
+ module ColorSpace
6
+ REGISTRY = {} # rubocop:disable Style/MutableConstant
7
+
8
+ class << self
9
+ def register(name, klass)
10
+ REGISTRY[name.to_sym] = klass
11
+ end
12
+
13
+ def resolve(name, _document = nil)
14
+ REGISTRY[name.to_sym]
15
+ end
16
+
17
+ def families
18
+ REGISTRY.keys
19
+ end
20
+ end
21
+
22
+ class DeviceGray
23
+ def self.family; :DeviceGray; end
24
+ def self.components; 1; end
25
+ def self.default; [0.0]; end
26
+ end
27
+ register :DeviceGray, DeviceGray
28
+
29
+ class DeviceRGB
30
+ def self.family; :DeviceRGB; end
31
+ def self.components; 3; end
32
+ def self.default; [0.0, 0.0, 0.0]; end
33
+ end
34
+ register :DeviceRGB, DeviceRGB
35
+
36
+ class DeviceCMYK
37
+ def self.family; :DeviceCMYK; end
38
+ def self.components; 4; end
39
+ def self.default; [0.0, 0.0, 0.0, 1.0]; end
40
+ end
41
+ register :DeviceCMYK, DeviceCMYK
42
+
43
+ class CalGray
44
+ attr_reader :white_point, :gamma
45
+
46
+ def initialize(white_point:, gamma: 1.0)
47
+ @white_point = white_point
48
+ @gamma = gamma
49
+ end
50
+
51
+ def family; :CalGray; end
52
+ def components; 1; end
53
+ end
54
+ register :CalGray, CalGray
55
+
56
+ class CalRGB
57
+ attr_reader :white_point, :black_point, :gamma
58
+
59
+ def initialize(white_point:, black_point: [0, 0, 0], gamma: [1, 1, 1])
60
+ @white_point = white_point
61
+ @black_point = black_point
62
+ @gamma = gamma
63
+ end
64
+
65
+ def family; :CalRGB; end
66
+ def components; 3; end
67
+ end
68
+ register :CalRGB, CalRGB
69
+
70
+ class Lab
71
+ attr_reader :white_point, :black_point, :range
72
+
73
+ def initialize(white_point:, black_point: [0, 0, 0], range: [-100, 100, -100, 100])
74
+ @white_point = white_point
75
+ @black_point = black_point
76
+ @range = range
77
+ end
78
+
79
+ def family; :Lab; end
80
+ def components; 3; end
81
+ end
82
+ register :Lab, Lab
83
+
84
+ class ICCBased
85
+ attr_reader :stream_ref, :alternate, :range, :n
86
+
87
+ def initialize(stream_ref:, n:, alternate: nil, range: nil)
88
+ @stream_ref = stream_ref
89
+ @n = n
90
+ @alternate = alternate
91
+ @range = range
92
+ end
93
+
94
+ def family; :ICCBased; end
95
+ def components; @n; end
96
+ end
97
+ register :ICCBased, ICCBased
98
+
99
+ class Indexed
100
+ attr_reader :base, :hival, :lookup
101
+
102
+ def initialize(base:, hival:, lookup:)
103
+ @base = base
104
+ @hival = hival
105
+ @lookup = lookup
106
+ end
107
+
108
+ def family; :Indexed; end
109
+ def components; 1; end
110
+ end
111
+ register :Indexed, Indexed
112
+
113
+ class Separation
114
+ attr_reader :name, :alternate_space, :tint_transform
115
+
116
+ def initialize(name:, alternate_space:, tint_transform:)
117
+ @name = name
118
+ @alternate_space = alternate_space
119
+ @tint_transform = tint_transform
120
+ end
121
+
122
+ def family; :Separation; end
123
+ def components; 1; end
124
+ end
125
+ register :Separation, Separation
126
+
127
+ class DeviceN
128
+ attr_reader :names, :alternate_space, :tint_transform
129
+
130
+ def initialize(names:, alternate_space:, tint_transform:)
131
+ @names = names
132
+ @alternate_space = alternate_space
133
+ @tint_transform = tint_transform
134
+ end
135
+
136
+ def family; :DeviceN; end
137
+
138
+ def components
139
+ (@names || []).length
140
+ end
141
+ end
142
+ register :DeviceN, DeviceN
143
+
144
+ class Pattern
145
+ attr_reader :base_space
146
+
147
+ def initialize(base_space: nil)
148
+ @base_space = base_space
149
+ end
150
+
151
+ def family; :Pattern; end
152
+ def components; 0; end
153
+ end
154
+ register :Pattern, Pattern
155
+ end
156
+ end
157
+ end
@@ -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,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pdfrb
4
+ module Content
5
+ module Operator
6
+ # Inline image operators (s8.9.7). Inline images embed raw image
7
+ # data directly in the content stream, framed by BI ... ID ...
8
+ # EI. They're an alternative to /XObject /Image for one-shot
9
+ # images: smaller overhead per image, no /Resources entry
10
+ # needed, but the bytes live in the content stream so they
11
+ # can't be reused.
12
+ #
13
+ # The serialise path emits the BI dict, ID marker, raw bytes,
14
+ # and EI marker. The invoke path is intentionally a no-op; the
15
+ # content Parser handles BI/ID/EI as a single bounded block
16
+ # rather than via the operator dispatch table.
17
+ class BeginInlineImage < Base
18
+ class << self
19
+ def name; "BI"; end
20
+
21
+ def serialize(serializer, **dict)
22
+ buf = +"BI\n"
23
+ dict.each do |k, v|
24
+ buf << serializer.serialize(k) << " " << serializer.serialize(v) << "\n"
25
+ end
26
+ buf << "ID\n"
27
+ buf
28
+ end
29
+
30
+ def invoke(_processor, *_operands); end
31
+ register
32
+ end
33
+ end
34
+
35
+ class EndInlineImage < Base
36
+ class << self
37
+ def name; "EI"; end
38
+
39
+ # The byte payload sits between ID and EI. The serializer
40
+ # for inline images writes the payload directly via
41
+ # Canvas#inline_image; this method is here so the registry
42
+ # knows about EI.
43
+ def serialize(_serializer, *_operands)
44
+ "EI\n"
45
+ end
46
+
47
+ def invoke(_processor, *_operands); end
48
+ register
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,22 @@
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
+ autoload :InlineImage, "pdfrb/content/operators/inline_image"
20
+ end
21
+ end
22
+ 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