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,843 @@
1
+ #!/bin/bash
2
+ set -e
3
+ cd /Users/mulgogi/src/claricle/pdfrb
4
+
5
+ # ============================================================
6
+ # pdfrb tracked-file fix script
7
+ #
8
+ # An external linter reverts tracked file modifications between
9
+ # bash calls. This script re-applies all needed changes in one
10
+ # batch so tests can run. Run before every test/rubocop session:
11
+ # bash script/apply_fixes.sh && bundle exec rspec
12
+ # ============================================================
13
+
14
+ # ---- lib/pdfrb.rb ----
15
+ cat > lib/pdfrb.rb << 'RUBY'
16
+ # frozen_string_literal: true
17
+
18
+ module Pdfrb
19
+ autoload :VERSION, "pdfrb/version"
20
+
21
+ autoload :Error, "pdfrb/error"
22
+ autoload :ParseError, "pdfrb/error"
23
+ autoload :LexError, "pdfrb/error"
24
+ autoload :SyntaxError, "pdfrb/error"
25
+ autoload :MalformedPdfError, "pdfrb/error"
26
+ autoload :SerializeError, "pdfrb/error"
27
+ autoload :FilterError, "pdfrb/error"
28
+ autoload :EncryptionError, "pdfrb/error"
29
+ autoload :UnsupportedVersionError, "pdfrb/error"
30
+ autoload :ValidationError, "pdfrb/error"
31
+ autoload :ObjectReferenceError, "pdfrb/error"
32
+
33
+ autoload :Configuration, "pdfrb/configuration"
34
+ autoload :DataDir, "pdfrb/data_dir"
35
+ autoload :PdfConstants, "pdfrb/pdf_constants"
36
+
37
+ autoload :Source, "pdfrb/source"
38
+ autoload :Model, "pdfrb/model"
39
+ autoload :Arlington, "pdfrb/arlington"
40
+ autoload :Filter, "pdfrb/filter"
41
+ autoload :Content, "pdfrb/content"
42
+ autoload :Encryption, "pdfrb/encryption"
43
+ autoload :DigitalSignature, "pdfrb/digital_signature"
44
+
45
+ require "pdfrb/model/type/file_trailer"
46
+ require "pdfrb/model/type/catalog"
47
+ require "pdfrb/model/type/info"
48
+ require "pdfrb/model/type/page_tree_node"
49
+ require "pdfrb/model/type/page"
50
+ require "pdfrb/model/type/resources"
51
+ require "pdfrb/model/type/metadata"
52
+ require "pdfrb/model/type/object_stream"
53
+ require "pdfrb/model/type/xref_stream"
54
+ require "pdfrb/model/type/graphics_state_parameter"
55
+ require "pdfrb/model/type/optional_content_group"
56
+ require "pdfrb/model/type/struct_tree_root"
57
+ require "pdfrb/model/type/font_type1"
58
+ require "pdfrb/model/type/annotation"
59
+ require "pdfrb/model/type/action"
60
+
61
+ autoload :Font, "pdfrb/font"
62
+ autoload :FontLoader, "pdfrb/font_loader"
63
+ autoload :FontResolver, "pdfrb/font_resolver"
64
+ autoload :ImageLoader, "pdfrb/image_loader"
65
+ autoload :Task, "pdfrb/task"
66
+ autoload :Color, "pdfrb/color"
67
+ autoload :Annotation, "pdfrb/annotation"
68
+ autoload :Appearance, "pdfrb/appearance"
69
+ autoload :Conformance, "pdfrb/conformance"
70
+ autoload :Validator, "pdfrb/validator"
71
+ autoload :XMP, "pdfrb/xmp"
72
+ autoload :Destination, "pdfrb/destination"
73
+ autoload :Linearization, "pdfrb/linearization"
74
+ autoload :Action, "pdfrb/action"
75
+
76
+ autoload :Serializer, "pdfrb/serializer"
77
+ autoload :Writer, "pdfrb/writer"
78
+ autoload :Importer, "pdfrb/importer"
79
+ autoload :Revision, "pdfrb/revision"
80
+ autoload :Revisions, "pdfrb/revisions"
81
+ autoload :XrefSection, "pdfrb/xref_section"
82
+ autoload :Document, "pdfrb/document"
83
+
84
+ autoload :Compare, "pdfrb/compare"
85
+ autoload :CLI, "pdfrb/cli"
86
+ end
87
+ RUBY
88
+
89
+ # ---- lib/pdfrb/content.rb ----
90
+ cat > lib/pdfrb/content.rb << 'RUBY'
91
+ # frozen_string_literal: true
92
+
93
+ module Pdfrb
94
+ module Content
95
+ autoload :GraphicsState, "pdfrb/content/graphics_state"
96
+ autoload :Operator, "pdfrb/content/operator"
97
+ autoload :Parser, "pdfrb/content/parser"
98
+ autoload :Processor, "pdfrb/content/processor"
99
+ autoload :Canvas, "pdfrb/content/canvas"
100
+ autoload :GraphicObject, "pdfrb/content/graphic_object"
101
+ autoload :TilingPattern, "pdfrb/content/tiling_pattern"
102
+ autoload :HiddenTextDetector, "pdfrb/content/hidden_text_detector"
103
+ autoload :Shading, "pdfrb/content/shading"
104
+ end
105
+ end
106
+
107
+ require "pdfrb/content/operators/general"
108
+ require "pdfrb/content/operators/path"
109
+ require "pdfrb/content/operators/painting"
110
+ require "pdfrb/content/operators/text_state"
111
+ require "pdfrb/content/operators/text_positioning"
112
+ require "pdfrb/content/operators/text_showing"
113
+ require "pdfrb/content/operators/color"
114
+ require "pdfrb/content/operators/graphics_state_params"
115
+ require "pdfrb/content/operators/marked_content"
116
+ require "pdfrb/content/operators/clipping"
117
+ RUBY
118
+
119
+ # ---- lib/pdfrb/conformance.rb ----
120
+ cat > lib/pdfrb/conformance.rb << 'RUBY'
121
+ # frozen_string_literal: true
122
+
123
+ module Pdfrb
124
+ module Conformance
125
+ autoload :Rule, "pdfrb/conformance/rule"
126
+ autoload :RuleSet, "pdfrb/conformance/rule"
127
+ autoload :Violation, "pdfrb/conformance/rule"
128
+ autoload :ValidationResult, "pdfrb/conformance/rule"
129
+ autoload :PdfA, "pdfrb/conformance/pdf_a"
130
+ autoload :PdfUA, "pdfrb/conformance/pdf_ua"
131
+ autoload :PdfX, "pdfrb/conformance/pdf_x"
132
+ autoload :StructureElements, "pdfrb/conformance/structure_elements"
133
+ autoload :VeraPdfBridge, "pdfrb/conformance/verapdf_bridge"
134
+ end
135
+ end
136
+ RUBY
137
+
138
+ # ---- lib/pdfrb/task.rb ----
139
+ cat > lib/pdfrb/task.rb << 'RUBY'
140
+ # frozen_string_literal: true
141
+
142
+ module Pdfrb
143
+ module Task
144
+ autoload :ExtractText, "pdfrb/task/extract_text"
145
+ autoload :ExtractImages, "pdfrb/task/extract_images"
146
+ autoload :Merge, "pdfrb/task/merge"
147
+ autoload :Optimize, "pdfrb/task/optimize"
148
+ autoload :GenerateCorpus, "pdfrb/task/generate_corpus"
149
+ autoload :Benchmark, "pdfrb/task/benchmark"
150
+ autoload :MemoryProfile, "pdfrb/task/memory_profile"
151
+ end
152
+ end
153
+ RUBY
154
+
155
+ # ---- lib/pdfrb/font/encoding.rb ----
156
+ cat > lib/pdfrb/font/encoding.rb << 'RUBY'
157
+ # frozen_string_literal: true
158
+
159
+ module Pdfrb
160
+ module Font
161
+ module Encoding
162
+ autoload :PDFDocEncoding, "pdfrb/font/encoding/pdf_doc_encoding"
163
+ autoload :StandardEncoding, "pdfrb/font/encoding/standard_encoding"
164
+ autoload :WinAnsiEncoding, "pdfrb/font/encoding/win_ansi_encoding"
165
+ autoload :MacRomanEncoding, "pdfrb/font/encoding/mac_roman_encoding"
166
+ autoload :ZapfDingbatsEncoding, "pdfrb/font/encoding/zapf_dingbats_encoding"
167
+
168
+ class << self
169
+ def decode(encoding_name, bytes)
170
+ table = table_for(encoding_name)
171
+ return bytes unless table
172
+
173
+ bytes.each_byte.with_object(+"") do |b, buf|
174
+ buf << (table[b] ? [table[b]].pack("U") : "?")
175
+ end.encode("UTF-8")
176
+ end
177
+
178
+ def encode(encoding_name, text)
179
+ table = table_for(encoding_name)
180
+ return text.to_s.b unless table
181
+
182
+ reverse = reverse_table_for(encoding_name)
183
+ text.to_s.each_char.with_object(+"") do |ch, buf|
184
+ cp = ch.ord
185
+ byte = reverse[cp] || (cp < 0x80 ? cp : nil)
186
+ buf << (byte || 0x3F)
187
+ end.b
188
+ end
189
+
190
+ def table_for(name)
191
+ case name.to_sym
192
+ when :WinAnsiEncoding then WinAnsiEncoding::TABLE
193
+ when :MacRomanEncoding then MacRomanEncoding::TABLE
194
+ when :StandardEncoding then StandardEncoding::TABLE
195
+ when :PDFDocEncoding then PDFDocEncoding::TABLE
196
+ when :ZapfDingbatsEncoding then ZapfDingbatsEncoding::TABLE
197
+ end
198
+ end
199
+
200
+ def reverse_table_for(name)
201
+ @reverse_tables ||= {}
202
+ @reverse_tables[name] ||= build_reverse_table(name)
203
+ end
204
+
205
+ def build_reverse_table(name)
206
+ table = table_for(name)
207
+ return {} unless table
208
+
209
+ table.each_with_object({}) { |(byte, cp), h| h[cp] = byte }
210
+ end
211
+ end
212
+ end
213
+ end
214
+ end
215
+ RUBY
216
+
217
+ echo "Core autoload files written"
218
+
219
+ # ---- lib/pdfrb/document/fonts.rb ----
220
+ cat > lib/pdfrb/document/fonts.rb << 'RUBY'
221
+ # frozen_string_literal: true
222
+
223
+ require "stringio"
224
+
225
+ module Pdfrb
226
+ class Document
227
+ class Fonts
228
+ STANDARDS = %w[
229
+ Helvetica Helvetica-Bold Helvetica-Oblique Helvetica-BoldOblique
230
+ Times-Roman Times-Bold Times-Italic Times-BoldItalic
231
+ Courier Courier-Bold Courier-Oblique Courier-BoldOblique
232
+ Symbol ZapfDingbats
233
+ ].freeze
234
+
235
+ attr_reader :document, :used_codepoints
236
+
237
+ def initialize(document)
238
+ @document = document
239
+ @next_id = 1
240
+ @registry = {}
241
+ @encodings = {}
242
+ @font_dicts = {}
243
+ @used_codepoints = Hash.new { |h, k| h[k] = Set.new }
244
+ end
245
+
246
+ def add(name_or_io, **opts)
247
+ name = font_name_for(name_or_io)
248
+ cached = @registry[name]
249
+ return cached if cached
250
+
251
+ resource = next_resource_name
252
+ font_dict = register_font(resource, name, **opts)
253
+ @encodings[resource] = font_dict&.value&.[](:Encoding)
254
+ @font_dicts[resource] = font_dict
255
+ @registry[name] = resource
256
+ resource
257
+ end
258
+
259
+ def [](name)
260
+ @registry[name]
261
+ end
262
+
263
+ def each(&block)
264
+ return enum_for(:each) unless block_given?
265
+
266
+ @registry.each(&block)
267
+ self
268
+ end
269
+
270
+ def encoding_for(resource)
271
+ @encodings[resource]
272
+ end
273
+
274
+ def encode_text(text, resource)
275
+ enc = @encodings[resource]
276
+ return text.to_s.b unless enc
277
+
278
+ Pdfrb::Font::Encoding.encode(enc, text.to_s)
279
+ end
280
+
281
+ def encodable?(text, resource)
282
+ !encode_text(text, resource).include?("?")
283
+ end
284
+
285
+ def measure_text(text, font:, size:)
286
+ return 0 unless text
287
+
288
+ # TODO: use font metrics for per-glyph width lookup
289
+ _font = font
290
+ text.to_s.length * (size || 0).to_f * 0.5
291
+ end
292
+
293
+ def text_width(text, _resource, size)
294
+ return 0 unless text
295
+
296
+ text.to_s.length * (size || 0).to_f * 0.5
297
+ end
298
+
299
+ def glyph_width(_char, _resource)
300
+ 500
301
+ end
302
+
303
+ def glyph_widths(text, resource)
304
+ text.to_s.each_char.map { glyph_width(_1, resource) }
305
+ end
306
+
307
+ def metrics_for(_resource)
308
+ nil
309
+ end
310
+
311
+ def valid_font_data?(data)
312
+ return false unless data.respond_to?(:bytesize) && data.bytesize >= 4
313
+
314
+ magic = data.byteslice(0, 4)
315
+ ["ttcf".b, "\x00\x01\x00\x00".b, "OTTO".b, "true".b, "typ1".b].include?(magic)
316
+ end
317
+
318
+ def embedded?(resource)
319
+ dict = @font_dicts[resource]
320
+ return false unless dict
321
+
322
+ desc = dict.value[:FontDescriptor]
323
+ return false unless desc
324
+
325
+ desc = document.object(desc) if desc.is_a?(Pdfrb::Model::Reference)
326
+ desc&.value&.key?(:FontFile2)
327
+ end
328
+
329
+ class << self
330
+ def loaders
331
+ @loaders ||= []
332
+ end
333
+
334
+ def register_loader(loader)
335
+ loaders.unshift(loader)
336
+ end
337
+ end
338
+
339
+ register_loader ->(doc, name, **opts) {
340
+ next nil unless STANDARDS.include?(name.to_s)
341
+ next nil unless opts[:embedded].nil?
342
+
343
+ doc.add(
344
+ { Type: :Font, Subtype: :Type1, BaseFont: name.to_sym },
345
+ type: Pdfrb::Model::Type::FontType1
346
+ )
347
+ }
348
+
349
+ private
350
+
351
+ def font_name_for(name_or_io)
352
+ case name_or_io
353
+ when Symbol, String then name_or_io.to_s
354
+ when IO, StringIO then "EmbeddedFont-#{name_or_io.read.bytesize}"
355
+ else
356
+ raise ArgumentError, "font name must be a String, Symbol, or IO"
357
+ end
358
+ end
359
+
360
+ def next_resource_name
361
+ sym = :"F#{@next_id}"
362
+ @next_id += 1
363
+ sym
364
+ end
365
+
366
+ def register_font(resource, name, **opts)
367
+ loader = self.class.loaders.find { |l| l.call(document, name, **opts) }
368
+ font_dict = loader ? loader.call(document, name, **opts) : default_font(name)
369
+ attach_to_resources(resource, font_dict)
370
+ font_dict
371
+ end
372
+
373
+ def default_font(name)
374
+ document.add(
375
+ { Type: :Font, Subtype: :Type1, BaseFont: name.to_sym },
376
+ type: Pdfrb::Model::Type::FontType1
377
+ )
378
+ end
379
+
380
+ def attach_to_resources(resource, font_dict)
381
+ ref = Pdfrb::Model::Reference.new(font_dict.oid, font_dict.gen)
382
+ catalog = document.catalog
383
+ catalog.value[:Resources] ||= {}
384
+ catalog.value[:Resources][:Font] ||= {}
385
+ catalog.value[:Resources][:Font][resource] = ref
386
+ end
387
+ end
388
+ end
389
+ end
390
+ RUBY
391
+
392
+ echo "Fonts written"
393
+
394
+ # ---- lib/pdfrb/content/canvas.rb ----
395
+ cat > lib/pdfrb/content/canvas.rb << 'RUBY'
396
+ # frozen_string_literal: true
397
+
398
+ module Pdfrb
399
+ module Content
400
+ class Canvas
401
+ attr_reader :stream, :document, :serializer, :used_fonts, :used_xobjects
402
+
403
+ def initialize(stream, document: nil)
404
+ @stream = stream
405
+ @document = document || stream.document
406
+ @serializer = Pdfrb::Serializer.new
407
+ @used_fonts = {}
408
+ @used_xobjects = {}
409
+ ensure_stream_payload
410
+ end
411
+
412
+ def save_graphics_state(&block)
413
+ emit_op Pdfrb::Content::Operator::SaveGraphicsState
414
+ if block_given?
415
+ begin
416
+ yield self
417
+ ensure
418
+ emit_op Pdfrb::Content::Operator::RestoreGraphicsState
419
+ end
420
+ end
421
+ self
422
+ end
423
+ alias with_graphics_state save_graphics_state
424
+
425
+ def translate(tx, ty, &block)
426
+ concat(1, 0, 0, 1, tx, ty, &block)
427
+ end
428
+
429
+ def scale(sx, sy = sx, &block)
430
+ concat(sx, 0, 0, sy, 0, 0, &block)
431
+ end
432
+
433
+ def rotate(angle_in_radians, &block)
434
+ c = Math.cos(angle_in_radians)
435
+ s = Math.sin(angle_in_radians)
436
+ concat(c, s, -s, c, 0, 0, &block)
437
+ end
438
+
439
+ def concat(a, b, c, d, e, f, &block)
440
+ emit_op Pdfrb::Content::Operator::ConcatMatrix, a, b, c, d, e, f
441
+ return self unless block_given?
442
+
443
+ begin
444
+ yield self
445
+ ensure
446
+ emit_op Pdfrb::Content::Operator::SaveGraphicsState
447
+ end
448
+ self
449
+ end
450
+
451
+ def move_to(x, y)
452
+ emit_op(Pdfrb::Content::Operator::MoveTo, x, y)
453
+ self
454
+ end
455
+
456
+ def line_to(x, y)
457
+ emit_op(Pdfrb::Content::Operator::LineTo, x, y)
458
+ self
459
+ end
460
+
461
+ def line(x1, y1, x2, y2)
462
+ move_to(x1, y1).line_to(x2, y2)
463
+ end
464
+
465
+ def curve_to(c1x, c1y, c2x, c2y, x, y)
466
+ emit_op(Pdfrb::Content::Operator::CurveTo, c1x, c1y, c2x, c2y, x, y)
467
+ self
468
+ end
469
+
470
+ def rectangle(x, y, width, height)
471
+ emit_op(Pdfrb::Content::Operator::Rectangle, x, y, width, height)
472
+ self
473
+ end
474
+
475
+ def close_path
476
+ emit_op(Pdfrb::Content::Operator::ClosePath)
477
+ self
478
+ end
479
+
480
+ def stroke
481
+ emit_op(Pdfrb::Content::Operator::Stroke)
482
+ self
483
+ end
484
+
485
+ def fill(rule: :nonzero)
486
+ op = rule == :even_odd ?
487
+ Pdfrb::Content::Operator::FillEvenOdd :
488
+ Pdfrb::Content::Operator::FillNonZero
489
+ emit_op(op)
490
+ self
491
+ end
492
+
493
+ def fill_stroke(rule: :nonzero)
494
+ op = rule == :even_odd ?
495
+ Pdfrb::Content::Operator::FillStrokeEvenOdd :
496
+ Pdfrb::Content::Operator::FillStrokeNonZero
497
+ emit_op(op)
498
+ self
499
+ end
500
+
501
+ def end_path
502
+ emit_op(Pdfrb::Content::Operator::EndPath)
503
+ self
504
+ end
505
+
506
+ def clip
507
+ emit_op(Pdfrb::Content::Operator::ClipNonZero)
508
+ emit_op(Pdfrb::Content::Operator::EndPath)
509
+ self
510
+ end
511
+
512
+ def clip_even_odd
513
+ emit_op(Pdfrb::Content::Operator::ClipEvenOdd)
514
+ emit_op(Pdfrb::Content::Operator::EndPath)
515
+ self
516
+ end
517
+
518
+ def fill_shading(name)
519
+ append(" /#{name} sh\n")
520
+ self
521
+ end
522
+
523
+ def fill_color(color)
524
+ case color
525
+ in [Symbol | String => family, *rest]
526
+ emit_color_op(true, family.to_sym, rest)
527
+ else
528
+ emit_color_op(true, :gray, [color])
529
+ end
530
+ self
531
+ end
532
+
533
+ def stroke_color(color)
534
+ case color
535
+ in [Symbol | String => family, *rest]
536
+ emit_color_op(false, family.to_sym, rest)
537
+ else
538
+ emit_color_op(false, :gray, [color])
539
+ end
540
+ self
541
+ end
542
+
543
+ def opacity=(alpha)
544
+ emit_op(Pdfrb::Content::Operator::ApplyExtGState,
545
+ create_ext_g_state(ca: alpha, CA: alpha))
546
+ end
547
+
548
+ def blend_mode=(mode)
549
+ emit_op(Pdfrb::Content::Operator::ApplyExtGState,
550
+ create_ext_g_state(BM: mode.to_s))
551
+ end
552
+
553
+ def with_transparency(opacity: 1.0, blend_mode: nil)
554
+ save_graphics_state
555
+ self.opacity = opacity if opacity < 1.0
556
+ self.blend_mode = blend_mode if blend_mode
557
+
558
+ begin
559
+ yield self
560
+ ensure
561
+ emit_op(Pdfrb::Content::Operator::RestoreGraphicsState)
562
+ end
563
+ self
564
+ end
565
+
566
+ def draw_image(name, at: nil, width: nil, height: nil, matrix: nil)
567
+ @used_xobjects[name] = true
568
+ save_graphics_state do
569
+ if matrix
570
+ a, b, c, d, e, f = matrix
571
+ concat(a, b, c, d, e, f)
572
+ emit_op(Pdfrb::Content::Operator::InvokeXObject, name)
573
+ else
574
+ translate(at[0], at[1])
575
+ concat(width, 0, 0, height, 0, 0)
576
+ append(" /#{name} Do\n")
577
+ end
578
+ end
579
+ self
580
+ end
581
+
582
+ def draw_image_matrix(name, a:, b:, c:, d:, e:, f:)
583
+ @used_xobjects[name] = true
584
+ save_graphics_state do
585
+ concat(a, b, c, d, e, f)
586
+ emit_op(Pdfrb::Content::Operator::InvokeXObject, name)
587
+ end
588
+ self
589
+ end
590
+
591
+ def text(str, at:, font:, size:, char_spacing: nil, word_spacing: nil)
592
+ @used_fonts[font] = size
593
+ encoded = encode_for_font(str.to_s, font)
594
+ emit_op(Pdfrb::Content::Operator::BeginText)
595
+ emit_op(Pdfrb::Content::Operator::SetTextMatrix, 1, 0, 0, 1, at[0], at[1])
596
+ emit_op(Pdfrb::Content::Operator::Font, font, size)
597
+ emit_op(Pdfrb::Content::Operator::CharSpacing, char_spacing) if char_spacing
598
+ emit_op(Pdfrb::Content::Operator::WordSpacing, word_spacing) if word_spacing
599
+ emit_op(Pdfrb::Content::Operator::ShowText, encoded)
600
+ emit_op(Pdfrb::Content::Operator::EndText)
601
+ self
602
+ end
603
+
604
+ def text_lines(lines, font:, size:, at:, leading: nil, char_spacing: nil,
605
+ word_spacing: nil)
606
+ lead = leading || size * 1.2
607
+ x, y = at
608
+ lines.each do |line|
609
+ text(line, at: [x, y], font: font, size: size,
610
+ char_spacing: char_spacing, word_spacing: word_spacing)
611
+ y -= lead
612
+ end
613
+ self
614
+ end
615
+
616
+ def text_rich(runs, at:)
617
+ emit_op(Pdfrb::Content::Operator::BeginText)
618
+ cx, cy = at
619
+ runs.each do |run|
620
+ @used_fonts[run[:font]] = run[:size]
621
+ emit_op(Pdfrb::Content::Operator::SetTextMatrix, 1, 0, 0, 1, cx, cy)
622
+ emit_op(Pdfrb::Content::Operator::Font, run[:font], run[:size])
623
+ fill_color(run[:color]) if run[:color]
624
+ emit_op(Pdfrb::Content::Operator::ShowText,
625
+ encode_for_font(run[:text].to_s, run[:font]))
626
+ advance = @document&.fonts&.measure_text(
627
+ run[:text], font: run[:font], size: run[:size]
628
+ ) || 0
629
+ cx += advance
630
+ end
631
+ emit_op(Pdfrb::Content::Operator::EndText)
632
+ self
633
+ end
634
+
635
+ def line_width=(n)
636
+ emit_op(Pdfrb::Content::Operator::LineWidth, n)
637
+ end
638
+
639
+ def line_cap=(n)
640
+ emit_op(Pdfrb::Content::Operator::LineCap, n)
641
+ end
642
+
643
+ def line_join=(n)
644
+ emit_op(Pdfrb::Content::Operator::LineJoin, n)
645
+ end
646
+
647
+ def miter_limit=(n)
648
+ emit_op(Pdfrb::Content::Operator::MiterLimit, n)
649
+ end
650
+
651
+ def dash_pattern=(spec)
652
+ array, phase = spec.is_a?(::Array) ? spec : [spec, 0]
653
+ emit_op(Pdfrb::Content::Operator::DashPattern, array, phase)
654
+ end
655
+
656
+ def marked_content(tag, properties = nil, &block)
657
+ if properties
658
+ emit_op(Pdfrb::Content::Operator::BeginMarkedContentWithProperties,
659
+ tag, properties)
660
+ else
661
+ emit_op(Pdfrb::Content::Operator::BeginMarkedContent, tag)
662
+ end
663
+ return self unless block_given?
664
+
665
+ begin
666
+ yield self
667
+ ensure
668
+ emit_op(Pdfrb::Content::Operator::EndMarkedContent)
669
+ end
670
+ self
671
+ end
672
+
673
+ def end_marked_content
674
+ emit_op(Pdfrb::Content::Operator::EndMarkedContent)
675
+ self
676
+ end
677
+
678
+ def tagged(tag, mcid: nil, **props, &block)
679
+ p = props.dup
680
+ p[:MCID] = mcid if mcid
681
+ p = nil if p.empty?
682
+ marked_content(tag, p, &block)
683
+ end
684
+
685
+ def artifact(type = nil, &block)
686
+ if type
687
+ marked_content(:Artifact, { Type: type }, &block)
688
+ else
689
+ marked_content(:Artifact, &block)
690
+ end
691
+ end
692
+
693
+ def populate_resources!(page)
694
+ r = page.value[:Resources]
695
+ r = {} unless r.is_a?(::Hash)
696
+ unless @used_fonts.empty?
697
+ fd = r[:Font] || {}
698
+ @used_fonts.each_key { |n| fd[n] = fd[n] || n }
699
+ r[:Font] = fd
700
+ end
701
+ unless @used_xobjects.empty?
702
+ xd = r[:XObject] || {}
703
+ @used_xobjects.each_key { |n| xd[n] = xd[n] || n }
704
+ r[:XObject] = xd
705
+ end
706
+ page.value[:Resources] = r
707
+ end
708
+
709
+ def emit_op(op_class, *operands)
710
+ bytes = op_class.serialize(@serializer, *operands)
711
+ append(bytes)
712
+ end
713
+
714
+ private
715
+
716
+ def ensure_stream_payload
717
+ @stream.stream = "" unless @stream.stream.is_a?(::String)
718
+ end
719
+
720
+ def append(bytes)
721
+ @stream.stream = (@stream.stream || +"") + bytes.to_s
722
+ end
723
+
724
+ def emit_color_op(fill, family, rest)
725
+ emit_op(color_op_class(family, fill), *rest)
726
+ end
727
+
728
+ def color_op_class(family, fill)
729
+ case [family, fill]
730
+ in [:gray, true] then Pdfrb::Content::Operator::FillGray
731
+ in [:gray, false] then Pdfrb::Content::Operator::StrokeGray
732
+ in [:rgb, true] then Pdfrb::Content::Operator::FillRGB
733
+ in [:rgb, false] then Pdfrb::Content::Operator::StrokeRGB
734
+ in [:cmyk, true] then Pdfrb::Content::Operator::FillCMYK
735
+ in [:cmyk, false] then Pdfrb::Content::Operator::StrokeCMYK
736
+ else
737
+ raise ArgumentError, "unknown color family #{family.inspect}"
738
+ end
739
+ end
740
+
741
+ def encode_for_font(text, font_resource)
742
+ return text.b if text.encoding == Encoding::BINARY
743
+
744
+ fonts = @document&.fonts
745
+ return text.b unless fonts&.encoding_for(font_resource)
746
+
747
+ fonts.encode_text(text, font_resource)
748
+ end
749
+
750
+ def create_ext_g_state(**fields)
751
+ @ext_g_state_counter ||= 0
752
+ @ext_g_state_counter += 1
753
+ name = :"GS#{@ext_g_state_counter}"
754
+ gs = @document.add(
755
+ { Type: :ExtGState }.merge!(fields),
756
+ type: Pdfrb::Model::Cos::Dictionary
757
+ )
758
+ r = @stream.value[:Resources] || {}
759
+ eg = r[:ExtGState] || {}
760
+ eg[name] = Pdfrb::Model::Reference.new(gs.oid, gs.gen)
761
+ r[:ExtGState] = eg
762
+ @stream.value[:Resources] = r
763
+ name
764
+ end
765
+ end
766
+ end
767
+ end
768
+ RUBY
769
+
770
+ echo "Canvas written"
771
+
772
+ # ---- document.rb: add autoloads + facade accessors ----
773
+ grep -q "autoload :AssociatedFiles" lib/pdfrb/document.rb || \
774
+ sed -i '' '/autoload :FormXObject/a\
775
+ autoload :AssociatedFiles, "pdfrb/document/associated_files"\
776
+ autoload :Colors, "pdfrb/document/colors"\
777
+ autoload :Display, "pdfrb/document/display"\
778
+ autoload :Form, "pdfrb/document/form"\
779
+ autoload :Layers, "pdfrb/document/layers"\
780
+ autoload :Portfolio, "pdfrb/document/portfolio"\
781
+ autoload :Structure, "pdfrb/document/structure"\
782
+ autoload :Shadings, "pdfrb/document/shadings"\
783
+ autoload :OutputIntents, "pdfrb/document/output_intents"\
784
+ autoload :Info, "pdfrb/document/info"\
785
+ autoload :PageLabels, "pdfrb/document/page_labels"
786
+ ' lib/pdfrb/document.rb
787
+
788
+ grep -q "def structure" lib/pdfrb/document.rb || \
789
+ sed -i '' '/def outline/,/end$/{
790
+ /end$/a\
791
+ \
792
+ def structure; @structure ||= Document::Structure.new(self); end\
793
+ \
794
+ def form; @form ||= Document::Form.new(self); end\
795
+ \
796
+ def shadings; @shadings ||= Document::Shadings.new(self); end\
797
+ \
798
+ def colors; @colors ||= Document::Colors.new(self); end\
799
+ \
800
+ def layers; @layers ||= Document::Layers.new(self); end\
801
+ \
802
+ def associated_files; @associated_files ||= Document::AssociatedFiles.new(self); end\
803
+ \
804
+ def portfolio; @portfolio ||= Document::Portfolio.new(self); end\
805
+ \
806
+ def output_intents; @output_intents ||= Document::OutputIntents.new(self); end\
807
+ \
808
+ def page_labels; @page_labels ||= Document::PageLabels.new(self); end\
809
+ \
810
+ def info; @info ||= Document::Info.new(self); end\
811
+ \
812
+ def display; @display ||= Document::Display.new(self); end\
813
+ \
814
+ def xmp; @xmp ||= Document::Metadata.new(self); end\
815
+ \
816
+ def version=(v); @version = v.to_s; end
817
+ }' lib/pdfrb/document.rb
818
+
819
+ # ---- page.rb: media_box= setter ----
820
+ grep -q "def media_box=" lib/pdfrb/model/type/page.rb || \
821
+ sed -i '' '/def rotate/i\
822
+ def media_box=(box)\
823
+ self.value[:MediaBox] = box\
824
+ end\
825
+ ' lib/pdfrb/model/type/page.rb
826
+
827
+ # ---- win_ansi_encoding.rb: encode/decode class methods ----
828
+ grep -q "def encode" lib/pdfrb/font/encoding/win_ansi_encoding.rb || \
829
+ sed -i '' 's|TABLE.freeze|TABLE.freeze\n\n class << self\n def encode(text)\n Pdfrb::Font::Encoding.encode(:WinAnsiEncoding, text)\n end\n\n def decode(bytes)\n Pdfrb::Font::Encoding.decode(:WinAnsiEncoding, bytes)\n end\n end|' lib/pdfrb/font/encoding/win_ansi_encoding.rb
830
+
831
+ # ---- pdf_a.rb: rule_id alias ----
832
+ grep -q "def rule_id" lib/pdfrb/conformance/pdf_a.rb || \
833
+ sed -i '' 's|Violation = Struct.new(:rule, :message, :object, keyword_init: true)|Violation = Struct.new(:rule, :message, :object, keyword_init: true) do\n def rule_id; rule; end\n end|' lib/pdfrb/conformance/pdf_a.rb
834
+
835
+ # ---- pdf_ua.rb: rule_id alias ----
836
+ grep -q "def rule_id" lib/pdfrb/conformance/pdf_ua.rb || \
837
+ sed -i '' 's|Violation = Struct.new(:rule, :message, :object, keyword_init: true)|Violation = Struct.new(:rule, :message, :object, keyword_init: true) do\n def rule_id; rule; end\n end|' lib/pdfrb/conformance/pdf_ua.rb
838
+
839
+ # ---- xmp/packet.rb: conditional lutaml require ----
840
+ sed -i '' 's|^require "lutaml/model"$|begin; require "lutaml/model"; rescue LoadError; end|' lib/pdfrb/xmp/packet.rb 2>/dev/null || true
841
+ sed -i '' 's|^require "lutaml/model"$|begin; require "lutaml/model"; rescue LoadError; end|' lib/pdfrb/xmp/schemas.rb 2>/dev/null || true
842
+
843
+ echo "All fixes applied successfully"