aspose-pdf-foss-for-python 0.1.0__py3-none-any.whl

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 (265) hide show
  1. aspose_pdf/__init__.py +367 -0
  2. aspose_pdf/_compat_surface.py +142 -0
  3. aspose_pdf/_export_files.py +108 -0
  4. aspose_pdf/_pending_signatures.py +222 -0
  5. aspose_pdf/_version.py +17 -0
  6. aspose_pdf/actions.py +273 -0
  7. aspose_pdf/annotations/__init__.py +1614 -0
  8. aspose_pdf/annotations/pdf3d.py +70 -0
  9. aspose_pdf/attachments.py +62 -0
  10. aspose_pdf/auto_links.py +375 -0
  11. aspose_pdf/block_extraction.py +223 -0
  12. aspose_pdf/cgm.py +7 -0
  13. aspose_pdf/clustering/__init__.py +13 -0
  14. aspose_pdf/clustering/hierarchical_agglomerative_clustering.py +151 -0
  15. aspose_pdf/color.py +241 -0
  16. aspose_pdf/destinations.py +164 -0
  17. aspose_pdf/document.py +5884 -0
  18. aspose_pdf/document_fonts.py +150 -0
  19. aspose_pdf/drawing.py +13 -0
  20. aspose_pdf/engine/__init__.py +1 -0
  21. aspose_pdf/engine/agl.py +243 -0
  22. aspose_pdf/engine/annotation_cleanup.py +407 -0
  23. aspose_pdf/engine/annotation_import.py +503 -0
  24. aspose_pdf/engine/annotation_json.py +313 -0
  25. aspose_pdf/engine/annotation_xfdf.py +405 -0
  26. aspose_pdf/engine/annotation_xfdf_import.py +389 -0
  27. aspose_pdf/engine/appearance.py +1361 -0
  28. aspose_pdf/engine/attachment_zip.py +306 -0
  29. aspose_pdf/engine/attachment_zip_import.py +387 -0
  30. aspose_pdf/engine/auto_crop.py +101 -0
  31. aspose_pdf/engine/auto_tag.py +1451 -0
  32. aspose_pdf/engine/blank_pages.py +198 -0
  33. aspose_pdf/engine/blank_split.py +73 -0
  34. aspose_pdf/engine/bookmark_json.py +714 -0
  35. aspose_pdf/engine/bookmark_split.py +167 -0
  36. aspose_pdf/engine/ccitt.py +457 -0
  37. aspose_pdf/engine/ccitt_tables.py +231 -0
  38. aspose_pdf/engine/cert_chain.py +306 -0
  39. aspose_pdf/engine/cff_outlines.py +928 -0
  40. aspose_pdf/engine/cms.py +562 -0
  41. aspose_pdf/engine/comparison_html.py +435 -0
  42. aspose_pdf/engine/conformance.py +2748 -0
  43. aspose_pdf/engine/contact_sheet.py +215 -0
  44. aspose_pdf/engine/content_authoring.py +1347 -0
  45. aspose_pdf/engine/content_bounds.py +67 -0
  46. aspose_pdf/engine/content_isolation.py +180 -0
  47. aspose_pdf/engine/content_stream_parser.py +2318 -0
  48. aspose_pdf/engine/cos.py +416 -0
  49. aspose_pdf/engine/data/__init__.py +81 -0
  50. aspose_pdf/engine/data/agl/LICENSE-ADOBE-AGL.txt +28 -0
  51. aspose_pdf/engine/data/agl/LICENSE-REPORTLAB.txt +31 -0
  52. aspose_pdf/engine/data/agl/README.md +44 -0
  53. aspose_pdf/engine/data/agl/glyphlist.json.zlib +0 -0
  54. aspose_pdf/engine/data/cmaps/LICENSE-ADOBE-CMAP.txt +28 -0
  55. aspose_pdf/engine/data/cmaps/LICENSE-ADOBE-MAPPING.txt +28 -0
  56. aspose_pdf/engine/data/cmaps/README.md +51 -0
  57. aspose_pdf/engine/data/cmaps/predefined_cmaps_CNS1.json.zlib +0 -0
  58. aspose_pdf/engine/data/cmaps/predefined_cmaps_GB1.json.zlib +0 -0
  59. aspose_pdf/engine/data/cmaps/predefined_cmaps_Japan1.json.zlib +0 -0
  60. aspose_pdf/engine/data/cmaps/predefined_cmaps_Korea1.json.zlib +0 -0
  61. aspose_pdf/engine/data/cmaps/predefined_cmaps_index.json.zlib +0 -0
  62. aspose_pdf/engine/data/fonts/LICENSE-DEJAVU.txt +187 -0
  63. aspose_pdf/engine/data/fonts/LICENSE-OFL.txt +102 -0
  64. aspose_pdf/engine/data/fonts/README.md +47 -0
  65. aspose_pdf/engine/data/fonts/dingbats.ttf.zlib +0 -0
  66. aspose_pdf/engine/data/fonts/mono-bold.ttf.zlib +0 -0
  67. aspose_pdf/engine/data/fonts/mono-bolditalic.ttf.zlib +0 -0
  68. aspose_pdf/engine/data/fonts/mono-italic.ttf.zlib +0 -0
  69. aspose_pdf/engine/data/fonts/mono-regular.ttf.zlib +0 -0
  70. aspose_pdf/engine/data/fonts/sans-bold.ttf.zlib +0 -0
  71. aspose_pdf/engine/data/fonts/sans-bolditalic.ttf.zlib +0 -0
  72. aspose_pdf/engine/data/fonts/sans-italic.ttf.zlib +0 -0
  73. aspose_pdf/engine/data/fonts/sans-regular.ttf.zlib +0 -0
  74. aspose_pdf/engine/data/fonts/serif-bold.ttf.zlib +0 -0
  75. aspose_pdf/engine/data/fonts/serif-bolditalic.ttf.zlib +0 -0
  76. aspose_pdf/engine/data/fonts/serif-italic.ttf.zlib +0 -0
  77. aspose_pdf/engine/data/fonts/serif-regular.ttf.zlib +0 -0
  78. aspose_pdf/engine/data/fonts/symbol.ttf.zlib +0 -0
  79. aspose_pdf/engine/data/number.py +78 -0
  80. aspose_pdf/engine/data/types.py +156 -0
  81. aspose_pdf/engine/data/xmp.py +1458 -0
  82. aspose_pdf/engine/dct.py +854 -0
  83. aspose_pdf/engine/dss.py +658 -0
  84. aspose_pdf/engine/duplicate_pages.py +67 -0
  85. aspose_pdf/engine/encryption.py +967 -0
  86. aspose_pdf/engine/field_appearance.py +310 -0
  87. aspose_pdf/engine/file_output.py +98 -0
  88. aspose_pdf/engine/filters.py +958 -0
  89. aspose_pdf/engine/flatten.py +388 -0
  90. aspose_pdf/engine/font_authoring.py +609 -0
  91. aspose_pdf/engine/font_inventory.py +211 -0
  92. aspose_pdf/engine/font_resolver.py +829 -0
  93. aspose_pdf/engine/font_subset.py +545 -0
  94. aspose_pdf/engine/font_subset_cff.py +963 -0
  95. aspose_pdf/engine/font_subset_type1.py +114 -0
  96. aspose_pdf/engine/font_variations.py +159 -0
  97. aspose_pdf/engine/form_batch.py +134 -0
  98. aspose_pdf/engine/form_csv.py +267 -0
  99. aspose_pdf/engine/form_csv_export.py +131 -0
  100. aspose_pdf/engine/form_data.py +364 -0
  101. aspose_pdf/engine/form_field_edit.py +306 -0
  102. aspose_pdf/engine/form_fields.py +253 -0
  103. aspose_pdf/engine/form_json.py +89 -0
  104. aspose_pdf/engine/form_schema.py +246 -0
  105. aspose_pdf/engine/form_validation.py +395 -0
  106. aspose_pdf/engine/glyph_outlines.py +465 -0
  107. aspose_pdf/engine/graphics_absorb.py +1061 -0
  108. aspose_pdf/engine/grid_split.py +80 -0
  109. aspose_pdf/engine/gvar.py +467 -0
  110. aspose_pdf/engine/headings.py +273 -0
  111. aspose_pdf/engine/image_export.py +875 -0
  112. aspose_pdf/engine/image_import.py +219 -0
  113. aspose_pdf/engine/image_resample.py +116 -0
  114. aspose_pdf/engine/image_resolution.py +376 -0
  115. aspose_pdf/engine/image_zip.py +339 -0
  116. aspose_pdf/engine/imposition.py +233 -0
  117. aspose_pdf/engine/incremental_update.py +477 -0
  118. aspose_pdf/engine/inline_image.py +337 -0
  119. aspose_pdf/engine/interleave.py +188 -0
  120. aspose_pdf/engine/io.py +53 -0
  121. aspose_pdf/engine/jbig2.py +186 -0
  122. aspose_pdf/engine/jpeg2000.py +2124 -0
  123. aspose_pdf/engine/jpeg_encoder.py +706 -0
  124. aspose_pdf/engine/jpx.py +211 -0
  125. aspose_pdf/engine/link_extraction.py +378 -0
  126. aspose_pdf/engine/link_urls.py +252 -0
  127. aspose_pdf/engine/link_validation.py +555 -0
  128. aspose_pdf/engine/lists.py +307 -0
  129. aspose_pdf/engine/logging.py +18 -0
  130. aspose_pdf/engine/merge_bookmarks.py +180 -0
  131. aspose_pdf/engine/optional_content.py +1323 -0
  132. aspose_pdf/engine/page_count_split.py +60 -0
  133. aspose_pdf/engine/page_labels.py +295 -0
  134. aspose_pdf/engine/page_zip.py +219 -0
  135. aspose_pdf/engine/parser_exceptions.py +125 -0
  136. aspose_pdf/engine/pdf_matrix.py +90 -0
  137. aspose_pdf/engine/pdf_parser_cos.py +1611 -0
  138. aspose_pdf/engine/pdf_writer_cos.py +544 -0
  139. aspose_pdf/engine/predefined_cmaps.py +635 -0
  140. aspose_pdf/engine/primitives/__init__.py +5 -0
  141. aspose_pdf/engine/primitives/matrix.py +31 -0
  142. aspose_pdf/engine/pubsec.py +898 -0
  143. aspose_pdf/engine/rasterized_copy.py +171 -0
  144. aspose_pdf/engine/rasterizer.py +5483 -0
  145. aspose_pdf/engine/redaction.py +140 -0
  146. aspose_pdf/engine/revocation.py +495 -0
  147. aspose_pdf/engine/rich_text.py +415 -0
  148. aspose_pdf/engine/sanitize.py +412 -0
  149. aspose_pdf/engine/sfnt.py +247 -0
  150. aspose_pdf/engine/shading.py +2317 -0
  151. aspose_pdf/engine/sign_field.py +689 -0
  152. aspose_pdf/engine/signature_appearance.py +302 -0
  153. aspose_pdf/engine/signature_profiles.py +206 -0
  154. aspose_pdf/engine/signature_revisions.py +476 -0
  155. aspose_pdf/engine/signature_validator.py +313 -0
  156. aspose_pdf/engine/signed_changes.py +208 -0
  157. aspose_pdf/engine/signing.py +288 -0
  158. aspose_pdf/engine/simple_pdf.py +21416 -0
  159. aspose_pdf/engine/spread_split.py +330 -0
  160. aspose_pdf/engine/stamps.py +414 -0
  161. aspose_pdf/engine/std_font_data.py +201 -0
  162. aspose_pdf/engine/std_fonts.py +97 -0
  163. aspose_pdf/engine/std_metrics.py +364 -0
  164. aspose_pdf/engine/stream_output.py +22 -0
  165. aspose_pdf/engine/stroke.py +228 -0
  166. aspose_pdf/engine/svg_export.py +1162 -0
  167. aspose_pdf/engine/symbol_encodings.py +260 -0
  168. aspose_pdf/engine/tables.py +420 -0
  169. aspose_pdf/engine/text_blocks.py +467 -0
  170. aspose_pdf/engine/text_comparison.py +146 -0
  171. aspose_pdf/engine/text_edit.py +1936 -0
  172. aspose_pdf/engine/text_export.py +758 -0
  173. aspose_pdf/engine/text_faces.py +275 -0
  174. aspose_pdf/engine/text_highlight.py +197 -0
  175. aspose_pdf/engine/text_layout.py +574 -0
  176. aspose_pdf/engine/text_locate.py +423 -0
  177. aspose_pdf/engine/text_metrics.py +86 -0
  178. aspose_pdf/engine/text_outlines.py +150 -0
  179. aspose_pdf/engine/text_region.py +476 -0
  180. aspose_pdf/engine/text_selection.py +53 -0
  181. aspose_pdf/engine/text_split.py +141 -0
  182. aspose_pdf/engine/tiff_import.py +130 -0
  183. aspose_pdf/engine/timestamp.py +327 -0
  184. aspose_pdf/engine/toc.py +407 -0
  185. aspose_pdf/engine/type1_outlines.py +471 -0
  186. aspose_pdf/engine/viewer_preferences.py +275 -0
  187. aspose_pdf/engine/visual_comparison.py +318 -0
  188. aspose_pdf/engine/webp_encoder.py +107 -0
  189. aspose_pdf/engine/woff.py +302 -0
  190. aspose_pdf/engine/woff2.py +674 -0
  191. aspose_pdf/engine/word_extraction.py +235 -0
  192. aspose_pdf/exceptions.py +148 -0
  193. aspose_pdf/facades.py +1569 -0
  194. aspose_pdf/font_registry.py +149 -0
  195. aspose_pdf/font_repository.py +311 -0
  196. aspose_pdf/font_substitution.py +115 -0
  197. aspose_pdf/forms.py +1688 -0
  198. aspose_pdf/generated/__init__.py +3 -0
  199. aspose_pdf/generated/document.py +14 -0
  200. aspose_pdf/generated/forms.py +12 -0
  201. aspose_pdf/generated/pdfa.py +12 -0
  202. aspose_pdf/generated/security.py +33 -0
  203. aspose_pdf/geometry.py +149 -0
  204. aspose_pdf/graphics.py +256 -0
  205. aspose_pdf/html.py +117 -0
  206. aspose_pdf/image_resolution.py +45 -0
  207. aspose_pdf/images.py +524 -0
  208. aspose_pdf/interactive.py +435 -0
  209. aspose_pdf/latex.py +14 -0
  210. aspose_pdf/layers.py +482 -0
  211. aspose_pdf/license_management.py +65 -0
  212. aspose_pdf/link_extraction.py +52 -0
  213. aspose_pdf/link_validation.py +29 -0
  214. aspose_pdf/lists.py +365 -0
  215. aspose_pdf/load_limits.py +173 -0
  216. aspose_pdf/load_options.py +63 -0
  217. aspose_pdf/logical_structure.py +34 -0
  218. aspose_pdf/lowcode.py +1598 -0
  219. aspose_pdf/markdown.py +38 -0
  220. aspose_pdf/ofd.py +7 -0
  221. aspose_pdf/optimization.py +51 -0
  222. aspose_pdf/outlines.py +271 -0
  223. aspose_pdf/page_labels.py +169 -0
  224. aspose_pdf/page_ranges.py +90 -0
  225. aspose_pdf/page_size.py +220 -0
  226. aspose_pdf/pages.py +1883 -0
  227. aspose_pdf/paths.py +306 -0
  228. aspose_pdf/pdf_consts.py +19 -0
  229. aspose_pdf/pdfa.py +285 -0
  230. aspose_pdf/pdfua.py +194 -0
  231. aspose_pdf/pdfx.py +267 -0
  232. aspose_pdf/permissions.py +340 -0
  233. aspose_pdf/presentation.py +170 -0
  234. aspose_pdf/primitives.py +22 -0
  235. aspose_pdf/printing.py +41 -0
  236. aspose_pdf/py.typed +0 -0
  237. aspose_pdf/recipients.py +78 -0
  238. aspose_pdf/sanitization.py +90 -0
  239. aspose_pdf/save_format.py +25 -0
  240. aspose_pdf/save_options.py +25 -0
  241. aspose_pdf/security/__init__.py +160 -0
  242. aspose_pdf/security/impl/__init__.py +1 -0
  243. aspose_pdf/security/impl/digests/__init__.py +1 -0
  244. aspose_pdf/signature.py +385 -0
  245. aspose_pdf/signature_appearance.py +263 -0
  246. aspose_pdf/stamps.py +563 -0
  247. aspose_pdf/svg.py +41 -0
  248. aspose_pdf/table_extraction.py +156 -0
  249. aspose_pdf/tables.py +272 -0
  250. aspose_pdf/tagged.py +254 -0
  251. aspose_pdf/text.py +762 -0
  252. aspose_pdf/text_block.py +286 -0
  253. aspose_pdf/text_comparison.py +31 -0
  254. aspose_pdf/text_layout.py +100 -0
  255. aspose_pdf/utils.py +74 -0
  256. aspose_pdf/validation.py +286 -0
  257. aspose_pdf/viewer_preferences.py +375 -0
  258. aspose_pdf/visual_comparison.py +53 -0
  259. aspose_pdf/visualization.py +118 -0
  260. aspose_pdf/xmp.py +75 -0
  261. aspose_pdf_foss_for_python-0.1.0.dist-info/METADATA +2233 -0
  262. aspose_pdf_foss_for_python-0.1.0.dist-info/RECORD +265 -0
  263. aspose_pdf_foss_for_python-0.1.0.dist-info/WHEEL +5 -0
  264. aspose_pdf_foss_for_python-0.1.0.dist-info/licenses/LICENSE +21 -0
  265. aspose_pdf_foss_for_python-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,222 @@
1
+ """What ``Document.sign``, ``add_ltv`` and ``add_document_timestamp`` leave for ``save``.
2
+
3
+ A signature covers bytes, so it can only be made once the document has been
4
+ serialized: each call records what to do, and ``Document.save`` does it, in call
5
+ order, to the bytes it is about to write -- each as an incremental revision, so
6
+ an earlier signature in the same save stays valid under a later one. The
7
+ arguments are checked when the call is made, so a mistake surfaces there rather
8
+ than halfway through a save that may already have asked a timestamp authority
9
+ for a token.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ from collections.abc import Iterable
15
+ from dataclasses import dataclass
16
+ from typing import Any
17
+
18
+ from cryptography import x509
19
+ from cryptography.hazmat.primitives import serialization
20
+ from cryptography.hazmat.primitives.asymmetric import ec, ed448, ed25519, rsa
21
+ from cryptography.x509 import ocsp
22
+ from cryptography.x509.oid import NameOID
23
+
24
+ from aspose_pdf.engine.dss import DssMaterial, add_document_timestamp, enable_ltv
25
+ from aspose_pdf.engine.sign_field import sign_field
26
+ from aspose_pdf.exceptions import PdfValidationException
27
+ from aspose_pdf.validation import CertificationLevel
28
+
29
+ _KEY_TYPES = (
30
+ rsa.RSAPrivateKey,
31
+ ec.EllipticCurvePrivateKey,
32
+ ed25519.Ed25519PrivateKey,
33
+ ed448.Ed448PrivateKey,
34
+ )
35
+ # PKCS#7 (``adbe.pkcs7.detached``) goes through ``cryptography``'s builder,
36
+ # which signs with RSA and EC keys only; the CAdES builder also takes Ed25519.
37
+ _PKCS7_KEY_TYPES = (rsa.RSAPrivateKey, ec.EllipticCurvePrivateKey)
38
+
39
+
40
+ def _public_key_der(key: Any) -> bytes:
41
+ return key.public_bytes(
42
+ serialization.Encoding.DER, serialization.PublicFormat.SubjectPublicKeyInfo
43
+ )
44
+
45
+
46
+ def _check_pair(certificate: Any, private_key: Any, role: str) -> None:
47
+ if not isinstance(certificate, x509.Certificate):
48
+ raise TypeError(f"The {role} certificate must be a cryptography x509.Certificate")
49
+ if not isinstance(private_key, _KEY_TYPES):
50
+ raise TypeError(f"The {role} private key must be an RSA, EC or Ed25519 private key")
51
+ # A key that is not the certificate's makes a signature that no validator
52
+ # accepts; nothing further along notices, so it is caught here.
53
+ if _public_key_der(private_key.public_key()) != _public_key_der(certificate.public_key()):
54
+ raise PdfValidationException(f"The {role} private key does not belong to its certificate")
55
+
56
+
57
+ def certificate_common_name(certificate: Any) -> str | None:
58
+ """The name to show for *certificate*'s holder, or ``None``.
59
+
60
+ A visible signature says who signed, and the certificate is the one place
61
+ that already knows. The subject's **common name** is what every other
62
+ producer shows; an organization certificate that has none falls back to
63
+ its organization name, and a certificate with neither is left for the
64
+ caller's own ``signer_name``.
65
+ """
66
+ for oid in (NameOID.COMMON_NAME, NameOID.ORGANIZATION_NAME):
67
+ for attribute in certificate.subject.get_attributes_for_oid(oid):
68
+ value = attribute.value
69
+ if isinstance(value, bytes): # a non-UTF-8 directory string
70
+ value = value.decode("utf-8", "replace")
71
+ text = str(value).strip()
72
+ if text:
73
+ return text
74
+ return None
75
+
76
+
77
+ def check_signer(certificate: Any, private_key: Any, *, pades: bool) -> None:
78
+ """Refuse a certificate and key that could not make a verifiable signature."""
79
+ _check_pair(certificate, private_key, "signer")
80
+ if isinstance(private_key, ed448.Ed448PrivateKey):
81
+ # RFC 8419: Ed448 in CMS digests with SHAKE256, not produced here.
82
+ raise PdfValidationException(
83
+ "Ed448 keys are not supported for signing: an Ed448 signature "
84
+ "digests with SHAKE256 (RFC 8419), which this signer does not produce"
85
+ )
86
+ if not pades and not isinstance(private_key, _PKCS7_KEY_TYPES):
87
+ raise PdfValidationException(
88
+ f"A {type(private_key).__name__} signs only with pades=True: the "
89
+ "adbe.pkcs7.detached encoder takes RSA and EC keys"
90
+ )
91
+
92
+
93
+ def check_timestamp_source(url: Any, authority: Any) -> tuple[Any, Any] | None:
94
+ """Validate a timestamp URL or local authority; return the authority as a pair."""
95
+ if url is not None and authority is not None:
96
+ raise PdfValidationException(
97
+ "Give a timestamp_url or a timestamp_authority, not both"
98
+ )
99
+ if url is not None and (not isinstance(url, str) or not url):
100
+ raise TypeError("timestamp_url must be a non-empty string")
101
+ if authority is None:
102
+ return None
103
+ try:
104
+ certificate, private_key = authority
105
+ except (TypeError, ValueError):
106
+ raise TypeError(
107
+ "timestamp_authority must be a (certificate, private_key) pair"
108
+ ) from None
109
+ _check_pair(certificate, private_key, "timestamp authority")
110
+ # The local authority signs its tokens with RSA PKCS#1 v1.5.
111
+ if not isinstance(private_key, rsa.RSAPrivateKey):
112
+ raise PdfValidationException("A local timestamp authority needs an RSA key")
113
+ return certificate, private_key
114
+
115
+
116
+ def certification_level(value: Any) -> int | None:
117
+ """The DocMDP ``/P`` a ``certify`` argument asks for, or ``None``."""
118
+ if value is None or value is CertificationLevel.NOT_CERTIFIED:
119
+ return None
120
+ if isinstance(value, CertificationLevel):
121
+ return value.value
122
+ if isinstance(value, bool) or not isinstance(value, int) or value not in (1, 2, 3):
123
+ raise PdfValidationException(
124
+ "certify must be a CertificationLevel or a DocMDP level 1, 2 or 3"
125
+ )
126
+ return value
127
+
128
+
129
+ def der_items(values: Iterable[Any], kind: str) -> list[bytes]:
130
+ """DER bytes for each certificate, CRL or OCSP response in *values*."""
131
+ if isinstance(values, (bytes, bytearray)):
132
+ raise TypeError(f"{kind} must be a sequence of items, not one bytes value")
133
+ out: list[bytes] = []
134
+ for value in values:
135
+ if isinstance(value, (bytes, bytearray)):
136
+ out.append(bytes(value))
137
+ elif isinstance(
138
+ value,
139
+ (x509.Certificate, x509.CertificateRevocationList, ocsp.OCSPResponse),
140
+ ):
141
+ out.append(value.public_bytes(serialization.Encoding.DER))
142
+ else:
143
+ raise TypeError(
144
+ f"{kind} items must be DER bytes or cryptography objects, "
145
+ f"not {type(value).__name__}"
146
+ )
147
+ return out
148
+
149
+
150
+ @dataclass(frozen=True)
151
+ class PendingSignature:
152
+ """A signature to put into field *field* when the document is saved."""
153
+
154
+ field: str
155
+ certificate: Any
156
+ private_key: Any
157
+ extra_certificates: tuple[Any, ...]
158
+ reason: str | None
159
+ location: str | None
160
+ contact: str | None
161
+ signer_name: str | None
162
+ pades: bool
163
+ timestamp_url: str | None
164
+ timestamp_authority: tuple[Any, Any] | None
165
+ timestamp_timeout: float
166
+ certify: int | None
167
+ #: How the signature is drawn, or ``None`` for an invisible one. It is not
168
+ #: used here: the appearance is written into the widget before the document
169
+ #: is serialized, so the signature covers it (see
170
+ #: ``Document._flush_signature_appearances``).
171
+ appearance: Any = None
172
+
173
+ def apply(self, data: bytes, *, encryption: Any, limits: Any) -> bytes:
174
+ return sign_field(
175
+ data,
176
+ self.field,
177
+ self.certificate,
178
+ self.private_key,
179
+ pades=self.pades,
180
+ reason=self.reason,
181
+ location=self.location,
182
+ contact=self.contact,
183
+ signer_name=self.signer_name,
184
+ extra_certs=list(self.extra_certificates) or None,
185
+ tsa=self.timestamp_authority,
186
+ timestamp_url=self.timestamp_url,
187
+ timestamp_timeout=self.timestamp_timeout,
188
+ certify_permissions=self.certify,
189
+ limits=limits,
190
+ encryption=encryption,
191
+ )
192
+
193
+
194
+ @dataclass(frozen=True)
195
+ class PendingLtv:
196
+ """A ``/DSS`` to add, or extend, when the document is saved."""
197
+
198
+ material: DssMaterial
199
+
200
+ def apply(self, data: bytes, *, encryption: Any, limits: Any) -> bytes:
201
+ return enable_ltv(data, extra=self.material, encryption=encryption)
202
+
203
+
204
+ @dataclass(frozen=True)
205
+ class PendingDocumentTimestamp:
206
+ """A document timestamp (``ETSI.RFC3161``) to append when the document is saved."""
207
+
208
+ timestamp_url: str | None
209
+ timestamp_authority: tuple[Any, Any] | None
210
+ timeout: float
211
+
212
+ def apply(self, data: bytes, *, encryption: Any, limits: Any) -> bytes:
213
+ return add_document_timestamp(
214
+ data,
215
+ tsa=self.timestamp_authority,
216
+ timestamp_url=self.timestamp_url,
217
+ timeout=self.timeout,
218
+ encryption=encryption,
219
+ )
220
+
221
+
222
+ PendingOperation = PendingSignature | PendingLtv | PendingDocumentTimestamp
aspose_pdf/_version.py ADDED
@@ -0,0 +1,17 @@
1
+ """Version helpers for the ``aspose_pdf`` package."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from importlib.metadata import PackageNotFoundError, version
6
+
7
+ __release_version__ = "0.1.0"
8
+
9
+
10
+ def _detect_installed_version() -> str:
11
+ try:
12
+ return version("aspose-pdf-foss-for-python")
13
+ except PackageNotFoundError:
14
+ return __release_version__
15
+
16
+
17
+ __version__ = _detect_installed_version()
aspose_pdf/actions.py ADDED
@@ -0,0 +1,273 @@
1
+ """What a document, a page or a field does when something happens to it.
2
+
3
+ ISO 32000-1 12.6.3: an *additional-actions* dictionary (``/AA``) maps a trigger
4
+ to an action. The triggers are different for each kind of owner, and naming one
5
+ that the owner has no trigger for is a mistake nothing would ever report -- a
6
+ viewer simply never fires it -- so each collection knows its own::
7
+
8
+ field.actions["validate"] = JavaScriptAction("if (event.value < 0) ...")
9
+ page.actions["open"] = JavaScriptAction("app.alert('page one')")
10
+ document.actions["will_print"] = JavaScriptAction("...")
11
+
12
+ The actions themselves are the ones in :mod:`aspose_pdf.interactive`.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ from collections.abc import Iterator
18
+ from typing import Any
19
+
20
+ from aspose_pdf.exceptions import PdfValidationException
21
+
22
+ __all__ = [
23
+ "DOCUMENT_TRIGGERS",
24
+ "FIELD_TRIGGERS",
25
+ "PAGE_TRIGGERS",
26
+ "ActionCollection",
27
+ "JavaScriptCollection",
28
+ ]
29
+
30
+ #: A form field's triggers (Table 196), and the widget's own (Table 195).
31
+ #: ``/K``, ``/F``, ``/V`` and ``/C`` belong on the field, the rest on its widget,
32
+ #: which is where a viewer looks for each -- the collection writes whichever
33
+ #: dictionary the trigger belongs to.
34
+ FIELD_TRIGGERS: dict[str, str] = {
35
+ "keystroke": "K",
36
+ "format": "F",
37
+ "validate": "V",
38
+ "calculate": "C",
39
+ "enter": "E",
40
+ "exit": "X",
41
+ "mouse_down": "D",
42
+ "mouse_up": "U",
43
+ "focus": "Fo",
44
+ "blur": "Bl",
45
+ "page_open": "PO",
46
+ "page_close": "PC",
47
+ "page_visible": "PV",
48
+ "page_invisible": "PI",
49
+ }
50
+
51
+ #: The triggers that belong on the *field* rather than on its widget.
52
+ FIELD_ONLY_TRIGGERS = frozenset({"keystroke", "format", "validate", "calculate"})
53
+
54
+ #: A page's triggers (Table 194).
55
+ PAGE_TRIGGERS: dict[str, str] = {"open": "O", "close": "C"}
56
+
57
+ #: The document's triggers (Table 197), on the catalog.
58
+ DOCUMENT_TRIGGERS: dict[str, str] = {
59
+ "will_close": "WC",
60
+ "will_save": "WS",
61
+ "did_save": "DS",
62
+ "will_print": "WP",
63
+ "did_print": "DP",
64
+ }
65
+
66
+
67
+ class ActionCollection:
68
+ """A mapping of trigger name to :class:`~aspose_pdf.interactive.Action`.
69
+
70
+ Reads go to the document each time, so the collection is never a stale copy.
71
+ Assigning ``None``, or deleting a key, removes the trigger; an action this API
72
+ has no class for is read as ``None`` and left in the file as it is, the way
73
+ every other typed view in the package treats one.
74
+ """
75
+
76
+ __slots__ = ("_read", "_triggers", "_what", "_write")
77
+
78
+ def __init__(
79
+ self,
80
+ triggers: dict[str, str],
81
+ read: Any,
82
+ write: Any,
83
+ what: str,
84
+ ) -> None:
85
+ self._triggers = triggers
86
+ self._read = read
87
+ self._write = write
88
+ self._what = what
89
+
90
+ def _key(self, trigger: str) -> str:
91
+ """The ``/AA`` entry *trigger* names, or a refusal listing the ones there are."""
92
+ if not isinstance(trigger, str):
93
+ raise PdfValidationException(f"A {self._what} trigger is named by a string.")
94
+ key = trigger.strip().lower().replace("-", "_").replace(" ", "_")
95
+ if key in self._triggers:
96
+ return self._triggers[key]
97
+ # The PDF key itself is accepted, so code that knows the standard can say
98
+ # what it means in the standard's own terms.
99
+ if trigger in self._triggers.values():
100
+ return trigger
101
+ raise PdfValidationException(
102
+ f"{trigger!r} is not a {self._what} trigger; use one of: "
103
+ + ", ".join(sorted(self._triggers))
104
+ )
105
+
106
+ def _name_of(self, key: str) -> str:
107
+ """The friendly name of a ``/AA`` entry, or the entry itself."""
108
+ for name, entry in self._triggers.items():
109
+ if entry == key:
110
+ return name
111
+ return key
112
+
113
+ def __len__(self) -> int:
114
+ return len(self._read())
115
+
116
+ def __iter__(self) -> Iterator[str]:
117
+ return iter(sorted(self._name_of(key) for key in self._read()))
118
+
119
+ def __contains__(self, trigger: object) -> bool:
120
+ if not isinstance(trigger, str):
121
+ return False
122
+ try:
123
+ return self._key(trigger) in self._read()
124
+ except PdfValidationException:
125
+ return False
126
+
127
+ def __getitem__(self, trigger: str) -> Any:
128
+ key = self._key(trigger)
129
+ actions = self._read()
130
+ if key not in actions:
131
+ raise KeyError(trigger)
132
+ return actions[key]
133
+
134
+ def get(self, trigger: str, default: Any = None) -> Any:
135
+ """The action *trigger* fires, or *default* when it has none.
136
+
137
+ A trigger that *is* set but whose action this API has no class for reads
138
+ as ``None`` rather than as *default*: the action is there, and it is only
139
+ this typed view that cannot describe it.
140
+ """
141
+ try:
142
+ return self[trigger]
143
+ except KeyError:
144
+ return default
145
+
146
+ def __setitem__(self, trigger: str, action: Any) -> None:
147
+ self._write(self._key(trigger), action)
148
+
149
+ def __delitem__(self, trigger: str) -> None:
150
+ key = self._key(trigger)
151
+ if key not in self._read():
152
+ raise KeyError(trigger)
153
+ self._write(key, None)
154
+
155
+ def set(self, trigger: str, action: Any) -> None:
156
+ """Set *trigger* to fire *action*; ``None`` removes it."""
157
+ self[trigger] = action
158
+
159
+ def remove(self, trigger: str) -> bool:
160
+ """Remove *trigger*; ``True`` when it had an action."""
161
+ key = self._key(trigger)
162
+ if key not in self._read():
163
+ return False
164
+ self._write(key, None)
165
+ return True
166
+
167
+ def clear(self) -> None:
168
+ """Remove every trigger."""
169
+ for key in list(self._read()):
170
+ self._write(key, None)
171
+
172
+ def keys(self) -> list[str]:
173
+ return list(self)
174
+
175
+ def values(self) -> list[Any]:
176
+ return [self[name] for name in self]
177
+
178
+ def items(self) -> list[tuple[str, Any]]:
179
+ return [(name, self[name]) for name in self]
180
+
181
+ @property
182
+ def triggers(self) -> tuple[str, ...]:
183
+ """Every trigger this owner has, whether it is set or not."""
184
+ return tuple(sorted(self._triggers))
185
+
186
+ def __repr__(self) -> str:
187
+ return f"ActionCollection({self._what}, {self.keys()!r})"
188
+
189
+
190
+ class JavaScriptCollection:
191
+ """The document's own scripts, as a mapping of name to source.
192
+
193
+ ISO 32000-1 12.6.4.17: these live in the catalog's ``/Names /JavaScript`` name
194
+ tree, and a viewer runs them when the document opens -- in **key order**, so
195
+ the names are what decides which runs first. This is where the functions a
196
+ field's ``/AA`` script calls are defined.
197
+
198
+ Reads go to the document each time. A name is encoded with latin-1, the one
199
+ codec that round-trips a name tree's byte keys unchanged.
200
+ """
201
+
202
+ __slots__ = ("_document",)
203
+
204
+ def __init__(self, document: Any) -> None:
205
+ self._document = document
206
+
207
+ def _engine(self) -> Any:
208
+ self._document._ensure_not_disposed()
209
+ engine = self._document._engine_pdf
210
+ if engine is None:
211
+ from aspose_pdf.exceptions import AsposePdfException
212
+
213
+ raise AsposePdfException("No document loaded")
214
+ return engine
215
+
216
+ def _scripts(self) -> dict[str, str]:
217
+ return self._engine().document_javascript()
218
+
219
+ def __len__(self) -> int:
220
+ return len(self._scripts())
221
+
222
+ def __iter__(self) -> Iterator[str]:
223
+ """The names, in the order a viewer runs them: key order."""
224
+ return iter(sorted(self._scripts()))
225
+
226
+ def __contains__(self, name: object) -> bool:
227
+ return isinstance(name, str) and name in self._scripts()
228
+
229
+ def __getitem__(self, name: str) -> str:
230
+ scripts = self._scripts()
231
+ if name not in scripts:
232
+ raise KeyError(name)
233
+ return scripts[name]
234
+
235
+ def get(self, name: str, default: Any = None) -> Any:
236
+ """The script *name* holds, or *default*."""
237
+ return self._scripts().get(name, default)
238
+
239
+ def __setitem__(self, name: str, script: str) -> None:
240
+ self.add(name, script)
241
+
242
+ def add(self, name: str, script: str) -> None:
243
+ """Define (or replace) the script *name*."""
244
+ if not isinstance(script, str):
245
+ raise PdfValidationException("A document script is source text.")
246
+ self._engine().set_document_javascript(name, script)
247
+
248
+ def __delitem__(self, name: str) -> None:
249
+ if not self.remove(name):
250
+ raise KeyError(name)
251
+
252
+ def remove(self, name: str) -> bool:
253
+ """Remove the script *name*; ``True`` when there was one."""
254
+ if name not in self._scripts():
255
+ return False
256
+ self._engine().set_document_javascript(name, None)
257
+ return True
258
+
259
+ def clear(self) -> None:
260
+ """Remove every document-level script."""
261
+ self._engine().clear_document_javascript()
262
+
263
+ def keys(self) -> list[str]:
264
+ return list(self)
265
+
266
+ def values(self) -> list[str]:
267
+ return [self[name] for name in self]
268
+
269
+ def items(self) -> list[tuple[str, str]]:
270
+ return [(name, self[name]) for name in self]
271
+
272
+ def __repr__(self) -> str:
273
+ return f"JavaScriptCollection({self.keys()!r})"