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
@@ -2,26 +2,20 @@
2
2
 
3
3
  module Pdfrb
4
4
  module Content
5
- # High-level drawing API on a Stream (page /Contents or Form
6
- # XObject). The Canvas emits operators via the +Serializer+ and
7
- # appends the bytes to the Stream's raw payload.
8
- #
9
- # Methods mirror HexaPDF::Content::Canvas where the names are
10
- # intuitive; PDF-specific concepts (q/Q, marked content) are
11
- # exposed via Ruby-block syntax.
12
5
  class Canvas
13
- attr_reader :stream, :document, :serializer
6
+ attr_reader :stream, :document, :serializer, :used_fonts, :used_xobjects
14
7
 
15
8
  def initialize(stream, document: nil)
9
+ Pdfrb::Content::Operator.eager_load!
16
10
  @stream = stream
17
11
  @document = document || stream.document
18
12
  @serializer = Pdfrb::Serializer.new
13
+ @used_fonts = {}
14
+ @used_xobjects = {}
19
15
  ensure_stream_payload
20
16
  end
21
17
 
22
- # ---- State stack ----
23
-
24
- def save_graphics_state(&block)
18
+ def save_graphics_state(&)
25
19
  emit_op Pdfrb::Content::Operator::SaveGraphicsState
26
20
  if block_given?
27
21
  begin
@@ -34,44 +28,39 @@ module Pdfrb
34
28
  end
35
29
  alias with_graphics_state save_graphics_state
36
30
 
37
- # ---- Transforms ----
38
-
39
- def translate(tx, ty, &block)
40
- concat(1, 0, 0, 1, tx, ty, &block)
31
+ def translate(tx, ty, &)
32
+ concat(1, 0, 0, 1, tx, ty, &)
41
33
  end
42
34
 
43
- def scale(sx, sy = sx, &block)
44
- concat(sx, 0, 0, sy, 0, 0, &block)
35
+ def scale(sx, sy = sx, &)
36
+ concat(sx, 0, 0, sy, 0, 0, &)
45
37
  end
46
38
 
47
- def rotate(angle_in_radians, &block)
39
+ def rotate(angle_in_radians, &)
48
40
  c = Math.cos(angle_in_radians)
49
41
  s = Math.sin(angle_in_radians)
50
- concat(c, s, -s, c, 0, 0, &block)
42
+ concat(c, s, -s, c, 0, 0, &)
51
43
  end
52
44
 
53
- def concat(a, b, c, d, e, f, &block)
45
+ def concat(a, b, c, d, e, f, &)
54
46
  emit_op Pdfrb::Content::Operator::ConcatMatrix, a, b, c, d, e, f
55
47
  return self unless block_given?
56
48
 
57
49
  begin
58
50
  yield self
59
51
  ensure
60
- # Reverse by save/restore so the caller's matrix is unchanged.
61
52
  emit_op Pdfrb::Content::Operator::SaveGraphicsState
62
53
  end
63
54
  self
64
55
  end
65
56
 
66
- # ---- Path construction ----
67
-
68
57
  def move_to(x, y)
69
- emit_op Pdfrb::Content::Operator::MoveTo, x, y
58
+ emit_op(Pdfrb::Content::Operator::MoveTo, x, y)
70
59
  self
71
60
  end
72
61
 
73
62
  def line_to(x, y)
74
- emit_op Pdfrb::Content::Operator::LineTo, x, y
63
+ emit_op(Pdfrb::Content::Operator::LineTo, x, y)
75
64
  self
76
65
  end
77
66
 
@@ -79,25 +68,139 @@ module Pdfrb
79
68
  move_to(x1, y1).line_to(x2, y2)
80
69
  end
81
70
 
71
+ # Draw a connected polyline through +points+. Each point is
72
+ # [x, y]. If +close+ is true, append a close-path operator.
73
+ def polyline(points, close: false)
74
+ return self if points.empty?
75
+
76
+ first = points.first
77
+ move_to(first[0], first[1])
78
+ points.drop(1).each { |x, y| line_to(x, y) }
79
+ close_path if close
80
+ self
81
+ end
82
+
83
+ # Draw a closed polygon through +points+.
84
+ def polygon(points)
85
+ polyline(points, close: true)
86
+ end
87
+
88
+ # Approximate a circular arc from +start_angle+ to +end_angle+
89
+ # (radians) centered at (cx, cy) with +radius+. Uses 16-line
90
+ # segment approximation per full revolution.
91
+ def arc(cx, cy, radius, start_angle:, end_angle:, segments_per_rev: 16)
92
+ span = end_angle - start_angle
93
+ return self if span.abs < 1e-6
94
+
95
+ seg_count = [(segments_per_rev * span.abs / (2 * Math::PI)).ceil, 1].max
96
+ step = span / seg_count
97
+ seg_count.times do |i|
98
+ a0 = start_angle + (i * step)
99
+ a1 = a0 + step
100
+ x0 = cx + (radius * Math.cos(a0))
101
+ y0 = cy + (radius * Math.sin(a0))
102
+ x1 = cx + (radius * Math.cos(a1))
103
+ y1 = cy + (radius * Math.sin(a1))
104
+ i.zero? ? move_to(x0, y0) : line_to(x0, y0)
105
+ line_to(x1, y1)
106
+ end
107
+ self
108
+ end
109
+
110
+ # Full circle centered at (cx, cy) with +radius+.
111
+ def circle(cx, cy, radius)
112
+ arc(cx, cy, radius, start_angle: 0, end_angle: 2 * Math::PI)
113
+ close_path
114
+ end
115
+
116
+ # Ellipse centered at (cx, cy) with x/y radii (rx, ry).
117
+ # Built from a scaled circle.
118
+ def ellipse(cx, cy, rx, ry)
119
+ save_graphics_state do
120
+ translate(cx, cy)
121
+ concat(1, 0, 0, 1, 0, 0)
122
+ # Approximate ellipse by scaling a unit circle.
123
+ segments = 32
124
+ segments.times do |i|
125
+ a0 = (i / segments.to_f) * 2 * Math::PI
126
+ a1 = ((i + 1) / segments.to_f) * 2 * Math::PI
127
+ x0 = rx * Math.cos(a0)
128
+ y0 = ry * Math.sin(a0)
129
+ x1 = rx * Math.cos(a1)
130
+ y1 = ry * Math.sin(a1)
131
+ i.zero? ? move_to(x0, y0) : line_to(x0, y0)
132
+ line_to(x1, y1)
133
+ end
134
+ close_path
135
+ end
136
+ self
137
+ end
138
+
139
+ # Rectangle with rounded corners. +radius+ is the corner radius
140
+ # (a single value or 4-element [tl, tr, br, bl]).
141
+ def rounded_rectangle(x, y, width, height, radius)
142
+ radii = radius.is_a?(::Array) ? radius : [radius] * 4
143
+ tl, tr, br, bl = radii
144
+ # bottom edge
145
+ move_to(x + bl, y)
146
+ line_to(x + width - br, y)
147
+ corner(x + width - br, y, x + width, y + br)
148
+ line_to(x + width, y + height - tr)
149
+ corner(x + width, y + height - tr,
150
+ x + width - tr, y + height)
151
+ line_to(x + tl, y + height)
152
+ corner(x + tl, y + height, x, y + height - tl)
153
+ line_to(x, y + bl)
154
+ corner(x, y + bl, x + bl, y)
155
+ close_path
156
+ self
157
+ end
158
+
159
+ # Convenience alias matching HexaPDF's naming. Accepts either
160
+ # an Array (interpreted as the dash array, phase 0) or an
161
+ # [array, phase] pair.
162
+ def dash=(spec)
163
+ array, phase = case spec
164
+ when ::Array
165
+ if spec.first.is_a?(::Array)
166
+ spec
167
+ else
168
+ [spec, 0]
169
+ end
170
+ else
171
+ [[spec], 0]
172
+ end
173
+ emit_op(Pdfrb::Content::Operator::DashPattern, array, phase)
174
+ end
175
+
176
+ # Draw a quarter-circle corner from (x0, y0) to (x1, y1) by
177
+ # multi-segment linear interpolation. Adequate for typical
178
+ # radii (< 30 pts); for larger radii use curve_to directly.
179
+ def corner(x0, y0, x1, y1)
180
+ segments = 6
181
+ segments.times do |i|
182
+ t = (i + 1) / segments.to_f
183
+ line_to(x0 + ((x1 - x0) * t), y0 + ((y1 - y0) * t))
184
+ end
185
+ end
186
+
82
187
  def curve_to(c1x, c1y, c2x, c2y, x, y)
83
- emit_op Pdfrb::Content::Operator::CurveTo, c1x, c1y, c2x, c2y, x, y
188
+ emit_op(Pdfrb::Content::Operator::CurveTo, c1x, c1y, c2x, c2y, x, y)
84
189
  self
85
190
  end
86
191
 
87
192
  def rectangle(x, y, width, height)
88
- emit_op Pdfrb::Content::Operator::Rectangle, x, y, width, height
193
+ emit_op(Pdfrb::Content::Operator::Rectangle, x, y, width, height)
89
194
  self
90
195
  end
91
196
 
92
197
  def close_path
93
- emit_op Pdfrb::Content::Operator::ClosePath
198
+ emit_op(Pdfrb::Content::Operator::ClosePath)
94
199
  self
95
200
  end
96
201
 
97
- # ---- Painting ----
98
-
99
202
  def stroke
100
- emit_op Pdfrb::Content::Operator::Stroke
203
+ emit_op(Pdfrb::Content::Operator::Stroke)
101
204
  self
102
205
  end
103
206
 
@@ -105,7 +208,7 @@ module Pdfrb
105
208
  op = rule == :even_odd ?
106
209
  Pdfrb::Content::Operator::FillEvenOdd :
107
210
  Pdfrb::Content::Operator::FillNonZero
108
- emit_op op
211
+ emit_op(op)
109
212
  self
110
213
  end
111
214
 
@@ -113,16 +216,31 @@ module Pdfrb
113
216
  op = rule == :even_odd ?
114
217
  Pdfrb::Content::Operator::FillStrokeEvenOdd :
115
218
  Pdfrb::Content::Operator::FillStrokeNonZero
116
- emit_op op
219
+ emit_op(op)
117
220
  self
118
221
  end
119
222
 
120
223
  def end_path
121
- emit_op Pdfrb::Content::Operator::EndPath
224
+ emit_op(Pdfrb::Content::Operator::EndPath)
225
+ self
226
+ end
227
+
228
+ def clip
229
+ emit_op(Pdfrb::Content::Operator::ClipNonZero)
230
+ emit_op(Pdfrb::Content::Operator::EndPath)
231
+ self
232
+ end
233
+
234
+ def clip_even_odd
235
+ emit_op(Pdfrb::Content::Operator::ClipEvenOdd)
236
+ emit_op(Pdfrb::Content::Operator::EndPath)
122
237
  self
123
238
  end
124
239
 
125
- # ---- Color ----
240
+ def fill_shading(name)
241
+ append(" /#{name} sh\n")
242
+ self
243
+ end
126
244
 
127
245
  def fill_color(color)
128
246
  case color
@@ -144,69 +262,212 @@ module Pdfrb
144
262
  self
145
263
  end
146
264
 
147
- # ---- Text ----
265
+ def opacity=(alpha)
266
+ emit_op(Pdfrb::Content::Operator::ApplyExtGState,
267
+ create_ext_g_state(ca: alpha, CA: alpha))
268
+ end
269
+
270
+ def blend_mode=(mode)
271
+ emit_op(Pdfrb::Content::Operator::ApplyExtGState,
272
+ create_ext_g_state(BM: mode.to_s))
273
+ end
274
+
275
+ def with_transparency(opacity: 1.0, blend_mode: nil)
276
+ save_graphics_state
277
+ self.opacity = opacity if opacity < 1.0
278
+ self.blend_mode = blend_mode if blend_mode
279
+
280
+ begin
281
+ yield self
282
+ ensure
283
+ emit_op(Pdfrb::Content::Operator::RestoreGraphicsState)
284
+ end
285
+ self
286
+ end
287
+
288
+ def draw_image(name, at: nil, width: nil, height: nil, matrix: nil)
289
+ @used_xobjects[name] = true
290
+ save_graphics_state do
291
+ if matrix
292
+ a, b, c, d, e, f = matrix
293
+ concat(a, b, c, d, e, f)
294
+ emit_op(Pdfrb::Content::Operator::InvokeXObject, name)
295
+ else
296
+ translate(at[0], at[1])
297
+ concat(width, 0, 0, height, 0, 0)
298
+ append(" /#{name} Do\n")
299
+ end
300
+ end
301
+ self
302
+ end
303
+
304
+ def draw_image_matrix(name, a:, b:, c:, d:, e:, f:)
305
+ @used_xobjects[name] = true
306
+ save_graphics_state do
307
+ concat(a, b, c, d, e, f)
308
+ emit_op(Pdfrb::Content::Operator::InvokeXObject, name)
309
+ end
310
+ self
311
+ end
312
+
313
+ # Draw a Form XObject by resource name at (x, y) with an optional
314
+ # matrix transform. Public API for page stamping, form flattening,
315
+ # and appearance-stream embedding.
316
+ # @param name [Symbol] the resource name in /Resources /XObject.
317
+ # @param at [Array<Numeric>] x, y translation.
318
+ # @param matrix [Array<Numeric>, nil] 6-element transform matrix.
319
+ def draw_form_xobject(name, at: [0, 0], matrix: nil)
320
+ @used_xobjects[name] = true
321
+ save_graphics_state do
322
+ if matrix
323
+ a, b, c, d, e, f = matrix
324
+ concat(a, b, c, d, e, f)
325
+ elsif at
326
+ translate(at[0], at[1])
327
+ end
328
+ emit_op(Pdfrb::Content::Operator::InvokeXObject, name)
329
+ end
330
+ self
331
+ end
332
+
333
+ # Emit an inline image (BI ... ID ... EI) directly into the
334
+ # content stream. Avoids the /Resources /XObject registration
335
+ # overhead for one-shot images. The +dict+ keys are the image
336
+ # header keys (/W, /H, /CS, /BPC, /F, etc.); +data+ is the
337
+ # encoded image bytes (raw, Flate-compressed, or DCT).
338
+ #
339
+ # @param dict [Hash{Symbol=>Object}] inline image header entries
340
+ # (e.g. { W: 100, H: 100, CS: :RGB, BPC: 8, F: :DCTDecode }).
341
+ # @param data [String] raw image bytes (binary-encoded).
342
+ def inline_image(dict:, data:)
343
+ header = +"BI\n"
344
+ dict.each do |k, v|
345
+ header << @serializer.serialize(k) << " " << @serializer.serialize(v) << "\n"
346
+ end
347
+ header << "ID\n"
348
+ append(header)
349
+ append(data)
350
+ append("\nEI\n")
351
+ self
352
+ end
148
353
 
149
- # Show +text+ at (+x+, +y+) in the named font. Caller is
150
- # responsible for registering the font in the page resources
151
- # under +font_name+ before this call.
152
354
  def text(str, at:, font:, size:, char_spacing: nil, word_spacing: nil)
153
- emit_op Pdfrb::Content::Operator::BeginText
154
- emit_op Pdfrb::Content::Operator::SetTextMatrix,
155
- 1, 0, 0, 1, at[0], at[1]
156
- emit_op Pdfrb::Content::Operator::Font, font, size
157
- emit_op Pdfrb::Content::Operator::CharSpacing, char_spacing if char_spacing
158
- emit_op Pdfrb::Content::Operator::WordSpacing, word_spacing if word_spacing
159
- emit_op Pdfrb::Content::Operator::ShowText, str.to_s
160
- emit_op Pdfrb::Content::Operator::EndText
355
+ @used_fonts[font] = size
356
+ encoded = encode_for_font(str.to_s, font)
357
+ emit_op(Pdfrb::Content::Operator::BeginText)
358
+ emit_op(Pdfrb::Content::Operator::SetTextMatrix, 1, 0, 0, 1, at[0], at[1])
359
+ emit_op(Pdfrb::Content::Operator::Font, font, size)
360
+ emit_op(Pdfrb::Content::Operator::CharSpacing, char_spacing) if char_spacing
361
+ emit_op(Pdfrb::Content::Operator::WordSpacing, word_spacing) if word_spacing
362
+ emit_op(Pdfrb::Content::Operator::ShowText, encoded)
363
+ emit_op(Pdfrb::Content::Operator::EndText)
161
364
  self
162
365
  end
163
366
 
164
- # ---- Graphics-state params ----
367
+ def text_lines(lines, font:, size:, at:, leading: nil, char_spacing: nil,
368
+ word_spacing: nil)
369
+ lead = leading || (size * 1.2)
370
+ x, y = at
371
+ lines.each do |line|
372
+ text(line, at: [x, y], font: font, size: size,
373
+ char_spacing: char_spacing, word_spacing: word_spacing)
374
+ y -= lead
375
+ end
376
+ self
377
+ end
378
+
379
+ def text_rich(runs, at:)
380
+ emit_op(Pdfrb::Content::Operator::BeginText)
381
+ cx, cy = at
382
+ runs.each do |run|
383
+ @used_fonts[run[:font]] = run[:size]
384
+ emit_op(Pdfrb::Content::Operator::SetTextMatrix, 1, 0, 0, 1, cx, cy)
385
+ emit_op(Pdfrb::Content::Operator::Font, run[:font], run[:size])
386
+ fill_color(run[:color]) if run[:color]
387
+ emit_op(Pdfrb::Content::Operator::ShowText,
388
+ encode_for_font(run[:text].to_s, run[:font]))
389
+ advance = @document&.fonts&.measure_text(
390
+ run[:text], font: run[:font], size: run[:size]
391
+ ) || 0
392
+ cx += advance
393
+ end
394
+ emit_op(Pdfrb::Content::Operator::EndText)
395
+ self
396
+ end
165
397
 
166
398
  def line_width=(n)
167
- emit_op Pdfrb::Content::Operator::LineWidth, n
399
+ emit_op(Pdfrb::Content::Operator::LineWidth, n)
168
400
  end
169
401
 
170
402
  def line_cap=(n)
171
- emit_op Pdfrb::Content::Operator::LineCap, n
403
+ emit_op(Pdfrb::Content::Operator::LineCap, n)
172
404
  end
173
405
 
174
406
  def line_join=(n)
175
- emit_op Pdfrb::Content::Operator::LineJoin, n
407
+ emit_op(Pdfrb::Content::Operator::LineJoin, n)
176
408
  end
177
409
 
178
410
  def miter_limit=(n)
179
- emit_op Pdfrb::Content::Operator::MiterLimit, n
411
+ emit_op(Pdfrb::Content::Operator::MiterLimit, n)
180
412
  end
181
413
 
182
414
  def dash_pattern=(spec)
183
- if spec.is_a?(::Array)
184
- array, phase = spec
185
- else
186
- array, phase = spec, 0
187
- end
188
- emit_op Pdfrb::Content::Operator::DashPattern, array, phase
415
+ array, phase = spec.is_a?(::Array) ? spec : [spec, 0]
416
+ emit_op(Pdfrb::Content::Operator::DashPattern, array, phase)
189
417
  end
190
418
 
191
- # ---- Marked content ----
192
-
193
- def marked_content(tag, properties = nil, &block)
419
+ def marked_content(tag, properties = nil, &)
194
420
  if properties
195
- emit_op Pdfrb::Content::Operator::BeginMarkedContentWithProperties, tag, properties
421
+ emit_op(Pdfrb::Content::Operator::BeginMarkedContentWithProperties,
422
+ tag, properties)
196
423
  else
197
- emit_op Pdfrb::Content::Operator::BeginMarkedContent, tag
424
+ emit_op(Pdfrb::Content::Operator::BeginMarkedContent, tag)
198
425
  end
199
426
  return self unless block_given?
200
427
 
201
428
  begin
202
429
  yield self
203
430
  ensure
204
- emit_op Pdfrb::Content::Operator::EndMarkedContent
431
+ emit_op(Pdfrb::Content::Operator::EndMarkedContent)
205
432
  end
206
433
  self
207
434
  end
208
435
 
209
- # ---- Internal ----
436
+ def end_marked_content
437
+ emit_op(Pdfrb::Content::Operator::EndMarkedContent)
438
+ self
439
+ end
440
+
441
+ def tagged(tag, mcid: nil, **props, &)
442
+ p = props.dup
443
+ p[:MCID] = mcid if mcid
444
+ p = nil if p.empty?
445
+ marked_content(tag, p, &)
446
+ end
447
+
448
+ def artifact(type = nil, &)
449
+ if type
450
+ marked_content(:Artifact, { Type: type }, &)
451
+ else
452
+ marked_content(:Artifact, &)
453
+ end
454
+ end
455
+
456
+ def populate_resources!(page)
457
+ r = page.value[:Resources]
458
+ r = {} unless r.is_a?(::Hash)
459
+ unless @used_fonts.empty?
460
+ fd = r[:Font] || {}
461
+ @used_fonts.each_key { |n| fd[n] = fd[n] || n }
462
+ r[:Font] = fd
463
+ end
464
+ unless @used_xobjects.empty?
465
+ xd = r[:XObject] || {}
466
+ @used_xobjects.each_key { |n| xd[n] = xd[n] || n }
467
+ r[:XObject] = xd
468
+ end
469
+ page.value[:Resources] = r
470
+ end
210
471
 
211
472
  def emit_op(op_class, *operands)
212
473
  bytes = op_class.serialize(@serializer, *operands)
@@ -216,19 +477,15 @@ module Pdfrb
216
477
  private
217
478
 
218
479
  def ensure_stream_payload
219
- return if @stream.stream.is_a?(::String)
220
-
221
- @stream.stream = ""
480
+ @stream.stream = "" unless @stream.stream.is_a?(::String)
222
481
  end
223
482
 
224
483
  def append(bytes)
225
- new_payload = (@stream.stream || +"") + bytes.to_s
226
- @stream.stream = new_payload
484
+ @stream.stream = (@stream.stream || +"") + bytes.to_s
227
485
  end
228
486
 
229
487
  def emit_color_op(fill, family, rest)
230
- klass = color_op_class(family, fill)
231
- emit_op(klass, *rest)
488
+ emit_op(color_op_class(family, fill), *rest)
232
489
  end
233
490
 
234
491
  def color_op_class(family, fill)
@@ -243,6 +500,31 @@ module Pdfrb
243
500
  raise ArgumentError, "unknown color family #{family.inspect}"
244
501
  end
245
502
  end
503
+
504
+ def encode_for_font(text, font_resource)
505
+ return text.b if text.encoding == Encoding::BINARY
506
+
507
+ fonts = @document&.fonts
508
+ return text.b unless fonts&.encoding_for(font_resource)
509
+
510
+ fonts.encode_text(text, font_resource)
511
+ end
512
+
513
+ def create_ext_g_state(**fields)
514
+ @ext_g_state_counter ||= 0
515
+ @ext_g_state_counter += 1
516
+ name = :"GS#{@ext_g_state_counter}"
517
+ gs = @document.add(
518
+ { Type: :ExtGState }.merge!(fields),
519
+ type: Pdfrb::Model::Cos::Dictionary
520
+ )
521
+ r = @stream.value[:Resources] || {}
522
+ eg = r[:ExtGState] || {}
523
+ eg[name] = Pdfrb::Model::Reference.new(gs.oid, gs.gen)
524
+ r[:ExtGState] = eg
525
+ @stream.value[:Resources] = r
526
+ name
527
+ end
246
528
  end
247
529
  end
248
530
  end