spotpdf 0.7.0__tar.gz
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.
- spotpdf-0.7.0/CHANGELOG.md +287 -0
- spotpdf-0.7.0/CONTRIBUTING.md +195 -0
- spotpdf-0.7.0/LICENSE +21 -0
- spotpdf-0.7.0/MANIFEST.in +32 -0
- spotpdf-0.7.0/PKG-INFO +525 -0
- spotpdf-0.7.0/README.md +495 -0
- spotpdf-0.7.0/SECURITY.md +67 -0
- spotpdf-0.7.0/SUPPORT.md +57 -0
- spotpdf-0.7.0/corpus/manifest.toml +104 -0
- spotpdf-0.7.0/docs/architecture.md +265 -0
- spotpdf-0.7.0/docs/compatibility.md +386 -0
- spotpdf-0.7.0/docs/images/demo-after.png +0 -0
- spotpdf-0.7.0/docs/images/demo-alternate.png +0 -0
- spotpdf-0.7.0/docs/images/demo-before.png +0 -0
- spotpdf-0.7.0/docs/images/demo-convert.svg +26 -0
- spotpdf-0.7.0/docs/images/demo-rename.svg +27 -0
- spotpdf-0.7.0/docs/images/generation.json +63 -0
- spotpdf-0.7.0/docs/json-output.md +346 -0
- spotpdf-0.7.0/docs/performance.md +76 -0
- spotpdf-0.7.0/docs/processing-budgets.md +155 -0
- spotpdf-0.7.0/docs/public-corpus.md +63 -0
- spotpdf-0.7.0/docs/python-api.md +152 -0
- spotpdf-0.7.0/docs/releasing.md +201 -0
- spotpdf-0.7.0/examples/create_demo_pdf.py +137 -0
- spotpdf-0.7.0/pyproject.toml +56 -0
- spotpdf-0.7.0/scripts/benchmark_inventory.py +240 -0
- spotpdf-0.7.0/scripts/check_distribution.py +255 -0
- spotpdf-0.7.0/scripts/check_public_corpus.py +40 -0
- spotpdf-0.7.0/scripts/check_pypi_readme.py +366 -0
- spotpdf-0.7.0/scripts/check_python_size.py +26 -0
- spotpdf-0.7.0/scripts/check_release.py +567 -0
- spotpdf-0.7.0/scripts/check_repository.py +334 -0
- spotpdf-0.7.0/scripts/create_docs_images.py +570 -0
- spotpdf-0.7.0/scripts/docs_image_check.py +229 -0
- spotpdf-0.7.0/scripts/public_corpus.py +460 -0
- spotpdf-0.7.0/scripts/release_readme.py +260 -0
- spotpdf-0.7.0/scripts/smoke_distributions.py +299 -0
- spotpdf-0.7.0/setup.cfg +4 -0
- spotpdf-0.7.0/src/spotpdf/__init__.py +54 -0
- spotpdf-0.7.0/src/spotpdf/__main__.py +3 -0
- spotpdf-0.7.0/src/spotpdf/alternate.py +146 -0
- spotpdf-0.7.0/src/spotpdf/alternate_plan.py +286 -0
- spotpdf-0.7.0/src/spotpdf/alternate_validation.py +272 -0
- spotpdf-0.7.0/src/spotpdf/budget_content.py +123 -0
- spotpdf-0.7.0/src/spotpdf/budget_graph.py +83 -0
- spotpdf-0.7.0/src/spotpdf/budget_preflight.py +60 -0
- spotpdf-0.7.0/src/spotpdf/cli.py +128 -0
- spotpdf-0.7.0/src/spotpdf/cli_dry_run.py +31 -0
- spotpdf-0.7.0/src/spotpdf/cli_limits.py +90 -0
- spotpdf-0.7.0/src/spotpdf/cli_output.py +477 -0
- spotpdf-0.7.0/src/spotpdf/cli_parser.py +254 -0
- spotpdf-0.7.0/src/spotpdf/cmyk.py +144 -0
- spotpdf-0.7.0/src/spotpdf/colors.py +148 -0
- spotpdf-0.7.0/src/spotpdf/content.py +401 -0
- spotpdf-0.7.0/src/spotpdf/content_support.py +105 -0
- spotpdf-0.7.0/src/spotpdf/convert.py +143 -0
- spotpdf-0.7.0/src/spotpdf/convert_aliases.py +303 -0
- spotpdf-0.7.0/src/spotpdf/convert_content.py +583 -0
- spotpdf-0.7.0/src/spotpdf/convert_operators.py +67 -0
- spotpdf-0.7.0/src/spotpdf/convert_plan.py +128 -0
- spotpdf-0.7.0/src/spotpdf/convert_resource_contexts.py +307 -0
- spotpdf-0.7.0/src/spotpdf/convert_resources.py +121 -0
- spotpdf-0.7.0/src/spotpdf/convert_state.py +64 -0
- spotpdf-0.7.0/src/spotpdf/convert_stream_owners.py +243 -0
- spotpdf-0.7.0/src/spotpdf/convert_streams.py +426 -0
- spotpdf-0.7.0/src/spotpdf/document.py +543 -0
- spotpdf-0.7.0/src/spotpdf/inspection.py +44 -0
- spotpdf-0.7.0/src/spotpdf/inventory.py +527 -0
- spotpdf-0.7.0/src/spotpdf/inventory_content.py +600 -0
- spotpdf-0.7.0/src/spotpdf/inventory_graph.py +159 -0
- spotpdf-0.7.0/src/spotpdf/inventory_hazards.py +359 -0
- spotpdf-0.7.0/src/spotpdf/inventory_prepress.py +168 -0
- spotpdf-0.7.0/src/spotpdf/inventory_usage.py +65 -0
- spotpdf-0.7.0/src/spotpdf/inventory_values.py +128 -0
- spotpdf-0.7.0/src/spotpdf/limits.py +103 -0
- spotpdf-0.7.0/src/spotpdf/metadata_fingerprint.py +165 -0
- spotpdf-0.7.0/src/spotpdf/model.py +264 -0
- spotpdf-0.7.0/src/spotpdf/mutation_verification.py +139 -0
- spotpdf-0.7.0/src/spotpdf/objects.py +47 -0
- spotpdf-0.7.0/src/spotpdf/publication.py +206 -0
- spotpdf-0.7.0/src/spotpdf/py.typed +0 -0
- spotpdf-0.7.0/src/spotpdf/removal_resources.py +228 -0
- spotpdf-0.7.0/src/spotpdf/rename.py +236 -0
- spotpdf-0.7.0/src/spotpdf/rename_hazards.py +261 -0
- spotpdf-0.7.0/src/spotpdf/rename_plan.py +595 -0
- spotpdf-0.7.0/src/spotpdf/rename_request.py +74 -0
- spotpdf-0.7.0/src/spotpdf/rename_slots.py +412 -0
- spotpdf-0.7.0/src/spotpdf/rename_structures.py +320 -0
- spotpdf-0.7.0/src/spotpdf/resource_owners.py +62 -0
- spotpdf-0.7.0/src/spotpdf/scan.py +324 -0
- spotpdf-0.7.0/src/spotpdf/separation_targets.py +233 -0
- spotpdf-0.7.0/src/spotpdf/trailer_semantics.py +30 -0
- spotpdf-0.7.0/src/spotpdf.egg-info/PKG-INFO +525 -0
- spotpdf-0.7.0/src/spotpdf.egg-info/SOURCES.txt +142 -0
- spotpdf-0.7.0/src/spotpdf.egg-info/dependency_links.txt +1 -0
- spotpdf-0.7.0/src/spotpdf.egg-info/entry_points.txt +2 -0
- spotpdf-0.7.0/src/spotpdf.egg-info/requires.txt +1 -0
- spotpdf-0.7.0/src/spotpdf.egg-info/top_level.txt +1 -0
- spotpdf-0.7.0/tests/cli_json_helpers.py +215 -0
- spotpdf-0.7.0/tests/conversion_fixtures.py +66 -0
- spotpdf-0.7.0/tests/processing_limit_fixtures.py +96 -0
- spotpdf-0.7.0/tests/test_alternate_safety.py +308 -0
- spotpdf-0.7.0/tests/test_cli_dry_run.py +487 -0
- spotpdf-0.7.0/tests/test_cli_json.py +297 -0
- spotpdf-0.7.0/tests/test_cli_json_errors.py +350 -0
- spotpdf-0.7.0/tests/test_contributor_docs.py +54 -0
- spotpdf-0.7.0/tests/test_convert.py +224 -0
- spotpdf-0.7.0/tests/test_convert_alias_dependencies.py +162 -0
- spotpdf-0.7.0/tests/test_convert_content.py +267 -0
- spotpdf-0.7.0/tests/test_convert_content_matrix.py +277 -0
- spotpdf-0.7.0/tests/test_convert_forms.py +263 -0
- spotpdf-0.7.0/tests/test_convert_render.py +136 -0
- spotpdf-0.7.0/tests/test_convert_resource_contexts.py +174 -0
- spotpdf-0.7.0/tests/test_convert_safety.py +573 -0
- spotpdf-0.7.0/tests/test_convert_stream_owners.py +424 -0
- spotpdf-0.7.0/tests/test_convert_verification.py +83 -0
- spotpdf-0.7.0/tests/test_demo.py +32 -0
- spotpdf-0.7.0/tests/test_distribution_check.py +225 -0
- spotpdf-0.7.0/tests/test_docs_image_check.py +262 -0
- spotpdf-0.7.0/tests/test_inventory.py +438 -0
- spotpdf-0.7.0/tests/test_inventory_edges.py +293 -0
- spotpdf-0.7.0/tests/test_inventory_performance.py +148 -0
- spotpdf-0.7.0/tests/test_inventory_usage.py +565 -0
- spotpdf-0.7.0/tests/test_metadata_fingerprint.py +157 -0
- spotpdf-0.7.0/tests/test_processing_limits.py +599 -0
- spotpdf-0.7.0/tests/test_public_api.py +394 -0
- spotpdf-0.7.0/tests/test_public_corpus.py +38 -0
- spotpdf-0.7.0/tests/test_pypi_readme.py +267 -0
- spotpdf-0.7.0/tests/test_release_checks.py +551 -0
- spotpdf-0.7.0/tests/test_release_readme.py +175 -0
- spotpdf-0.7.0/tests/test_release_workflow.py +119 -0
- spotpdf-0.7.0/tests/test_removal_resource_contexts.py +494 -0
- spotpdf-0.7.0/tests/test_rename.py +591 -0
- spotpdf-0.7.0/tests/test_rename_malformed_edges.py +243 -0
- spotpdf-0.7.0/tests/test_rename_render.py +169 -0
- spotpdf-0.7.0/tests/test_rename_safety.py +420 -0
- spotpdf-0.7.0/tests/test_rename_semantics.py +560 -0
- spotpdf-0.7.0/tests/test_rename_structures.py +259 -0
- spotpdf-0.7.0/tests/test_rename_verification.py +286 -0
- spotpdf-0.7.0/tests/test_repository_checks.py +278 -0
- spotpdf-0.7.0/tests/test_safety.py +148 -0
- spotpdf-0.7.0/tests/test_set_alternate.py +457 -0
- spotpdf-0.7.0/tests/test_spotpdf.py +499 -0
- spotpdf-0.7.0/tests/test_troubleshooting_docs.py +129 -0
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable user-visible changes are documented here. The project follows
|
|
4
|
+
[Semantic Versioning](https://semver.org/).
|
|
5
|
+
|
|
6
|
+
## [Unreleased]
|
|
7
|
+
|
|
8
|
+
## [0.7.0] - 2026-08-30
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Automated repository hygiene checks reject tracked PDFs and broken local
|
|
13
|
+
documentation file targets, while CI regenerates every synthetic documentation
|
|
14
|
+
image, verifies an exact source-fingerprint manifest and SVG output, and uses
|
|
15
|
+
bounded PNG drift checks before packaging.
|
|
16
|
+
- CodeQL analysis for Python runs on pull requests, `main`, a weekly schedule,
|
|
17
|
+
and manual dispatch with read-only contents plus the required code-scanning
|
|
18
|
+
write permission.
|
|
19
|
+
- A support-routing guide and structured usage-question form distinguish
|
|
20
|
+
questions, reproducible bugs, feature requests, and private vulnerability
|
|
21
|
+
reports while keeping confidential PDFs and sensitive output data out of
|
|
22
|
+
project channels.
|
|
23
|
+
- A tokenless PyPI Trusted Publishing path uploads the already tested wheel and
|
|
24
|
+
source archive only after the corresponding GitHub Release is immutable.
|
|
25
|
+
- Release validation now rejects repository-relative or stale-tag README links
|
|
26
|
+
that would break on PyPI. A fail-closed PyPI Markdown gate renders the exact
|
|
27
|
+
long description packaged in both distributions and requires them to match;
|
|
28
|
+
bounded archive parsing rejects resource floods and ambiguous metadata paths.
|
|
29
|
+
`twine check --strict` remains responsible for metadata and warning checks.
|
|
30
|
+
- Every mutating CLI command now accepts `--dry-run` instead of an output path.
|
|
31
|
+
The mode executes the complete rewrite, serialization, strict reopen, and
|
|
32
|
+
semantic verification pipeline, then discards the verified temporary PDF.
|
|
33
|
+
Text identifies the dry run; JSON adds `dry_run: true` and omits `output`
|
|
34
|
+
without changing normal mutation result shapes.
|
|
35
|
+
- Wheel and source-archive Core Metadata now expose the canonical Support and
|
|
36
|
+
Security channels. The package gate requires each link exactly once in both
|
|
37
|
+
distributions before publication.
|
|
38
|
+
- Contributor setup now names Poppler's `pdftoppm` prerequisite, provides
|
|
39
|
+
macOS and Debian/Ubuntu installation commands, adds POSIX and PowerShell
|
|
40
|
+
verification, and distinguishes Poppler-only documentation/rename rendering
|
|
41
|
+
from qpdf/Ghostscript convert-render and release-corpus requirements.
|
|
42
|
+
- An actionable troubleshooting guide maps common fail-closed errors to causes
|
|
43
|
+
and safe next steps without treating `--force`, higher budgets, or PDF-object
|
|
44
|
+
patching as ways to bypass unsupported semantics and document restrictions.
|
|
45
|
+
- The seven supported Python operations, their core result records, limits,
|
|
46
|
+
colorant roles/kinds, and controlled errors now have one canonical package-root
|
|
47
|
+
import surface. All path arguments consistently accept strings and path-like
|
|
48
|
+
objects, and built distributions declare and verify PEP 561 inline typing.
|
|
49
|
+
- Stable-release validation now binds both the bug-report and usage-question
|
|
50
|
+
version placeholders to the package version.
|
|
51
|
+
|
|
52
|
+
### Security
|
|
53
|
+
|
|
54
|
+
- Spot removal now plans every target resource-alias deletion only inside a
|
|
55
|
+
proven Page/Form content-resource context. Genuine but uninvoked Forms are
|
|
56
|
+
analyzed across every effective resource scope before selected aliases can be
|
|
57
|
+
removed. Retained color-space dependencies, Form inline images, selected
|
|
58
|
+
targets reachable through non-content trailer roots, private resource
|
|
59
|
+
lookalikes, and resource containers with non-content owners fail closed
|
|
60
|
+
without replacing an existing destination.
|
|
61
|
+
- The PyPI publisher is isolated in a two-step, tag-only environment job with
|
|
62
|
+
only `id-token: write`; it cannot check out or rebuild repository code and
|
|
63
|
+
uses no stored package-index credential.
|
|
64
|
+
- Read-only CI checkouts no longer retain Git credentials beyond checkout.
|
|
65
|
+
- Dry-run success is emitted only after private temporary storage has been
|
|
66
|
+
cleaned up. Handled mutation failures remove the private output; a filesystem
|
|
67
|
+
cleanup failure becomes an I/O error before any success record is emitted.
|
|
68
|
+
|
|
69
|
+
## [0.6.0] - 2026-08-30
|
|
70
|
+
|
|
71
|
+
### Added
|
|
72
|
+
|
|
73
|
+
- Schema-versioned JSON output for all six input-processing commands through
|
|
74
|
+
`--format json`, with one deterministic command-specific result on stdout or
|
|
75
|
+
one classified error on stderr.
|
|
76
|
+
- Stable machine error codes for usage, budgets, unsupported spot semantics,
|
|
77
|
+
validation, PDF parsing, I/O, invalid values, invariant failures, and nesting
|
|
78
|
+
limits. Budget failures include the metric, public field, observed value,
|
|
79
|
+
limit, and matching CLI option.
|
|
80
|
+
- A normative JSON contract with Enfocus Switch, shell, and CI recipes plus
|
|
81
|
+
subprocess coverage for every command, empty results, Unicode and controls,
|
|
82
|
+
parser failures, native decode failures, and output atomicity.
|
|
83
|
+
|
|
84
|
+
### Changed
|
|
85
|
+
|
|
86
|
+
- Invalid command-line arguments now exit with `64`, leaving exit `2`
|
|
87
|
+
exclusively for a successful `check` result whose requested name is present.
|
|
88
|
+
- Stable-release validation now also rejects stale development-only claims and
|
|
89
|
+
mismatched `spotpdf_version` examples in the README and normative JSON,
|
|
90
|
+
processing-budget, and security documentation.
|
|
91
|
+
|
|
92
|
+
### Compatibility
|
|
93
|
+
|
|
94
|
+
- Human-readable text remains the default; successful command output and
|
|
95
|
+
runtime-error wording remain unchanged. Help now documents `--format`, usage
|
|
96
|
+
errors move from exit `2` to `64`, successful mutations remain exit `0`, and
|
|
97
|
+
`check` remains exit `0` for absent and `2` for present. Help and version
|
|
98
|
+
output remain text in JSON mode.
|
|
99
|
+
|
|
100
|
+
## [0.5.0] - 2026-08-30
|
|
101
|
+
|
|
102
|
+
spotpdf 0.5.0 adds fail-closed conversion of one supported named
|
|
103
|
+
`/Separation` to explicit DeviceCMYK vector or text paint. The operator supplies
|
|
104
|
+
the CMYK recipe; spotpdf never guesses it from the existing alternate preview.
|
|
105
|
+
|
|
106
|
+
```console
|
|
107
|
+
spotpdf convert input.pdf --spot "Varnish" \
|
|
108
|
+
--to-cmyk 0,80,100,0 -o output.pdf
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Added
|
|
112
|
+
|
|
113
|
+
- `convert` CLI command and `convert_spot_to_cmyk` library API for replacing a
|
|
114
|
+
strictly supported named Separation with explicit DeviceCMYK vector/text
|
|
115
|
+
paint from an operator-supplied process recipe.
|
|
116
|
+
- Stateful tint conversion across independent fill/stroke state, balanced
|
|
117
|
+
graphics-state scopes, text rendering modes, and compatible nested Forms.
|
|
118
|
+
- A synthetic qpdf/Poppler/Ghostscript conversion oracle, generated conversion
|
|
119
|
+
walkthrough, and seventh pinned public-corpus case.
|
|
120
|
+
- Immutable public `ProcessingLimits` configuration and five positive-integer
|
|
121
|
+
CLI overrides for input bytes, pages, reachable graph entries, decoded
|
|
122
|
+
page/Form content bytes, and content operators.
|
|
123
|
+
- A normative processing-budget guide with exact inclusive counter semantics,
|
|
124
|
+
library examples, default rationale, and isolation boundaries.
|
|
125
|
+
|
|
126
|
+
### Safety
|
|
127
|
+
|
|
128
|
+
- Conversion builds and verifies a complete precomputed resource/stream plan,
|
|
129
|
+
requires the target plate to be absent after strict reopen, and fingerprints
|
|
130
|
+
every unplanned document semantic before atomic publication.
|
|
131
|
+
- A resource-scope-aware preflight proves that deleting target aliases cannot
|
|
132
|
+
leave nested color spaces, images, shadings, transparency groups, or Tiling
|
|
133
|
+
Pattern content with stale references.
|
|
134
|
+
- Explicit process conversion fails closed for target-related DeviceN,
|
|
135
|
+
Type 5 halftones, OPI and other prepress dependencies, images and their
|
|
136
|
+
alternates, patterns, shadings, Type 3 fonts, annotation appearances,
|
|
137
|
+
transparency, effective overprint, effective `/DefaultCMYK`, ambiguous
|
|
138
|
+
resource/Form contexts, and malformed or unsupported paint state.
|
|
139
|
+
- Unknown content operators outside PDF `BX`/`EX` compatibility sections are
|
|
140
|
+
rejected before conversion so vendor extensions cannot hide paint or graphics
|
|
141
|
+
state changes from the conversion plan. Standard operators are also checked
|
|
142
|
+
against page/Form versus text-object context, and ExtGState font selections
|
|
143
|
+
participate in the Type 3 refusal.
|
|
144
|
+
- A stream-owner preflight refuses page/Form cross-role aliases, non-content
|
|
145
|
+
aliases such as attachments and metadata, and externally referenced
|
|
146
|
+
`/Contents` Array members. Only direct `/XObject` slots in proven Page/Form
|
|
147
|
+
resource contexts authorize Form writes; external StructTree MCR `/Stm`
|
|
148
|
+
associations and private lookalike structures fail closed.
|
|
149
|
+
- Every input-processing subcommand now performs one fixed-order source budget
|
|
150
|
+
preflight before analysis or mutation. Any overrun publishes no output,
|
|
151
|
+
preserves an existing `--force` destination, and leaves no created private
|
|
152
|
+
temporary candidate behind.
|
|
153
|
+
- Reachable graph entries and content operators are counted incrementally rather
|
|
154
|
+
than materializing complete graph-edge or instruction lists solely for the
|
|
155
|
+
budget check. Application ceilings remain separate from qpdf safeguards and
|
|
156
|
+
external CPU/RAM/time isolation.
|
|
157
|
+
- Native qpdf warnings remain collected and fail closed but are no longer
|
|
158
|
+
duplicated ahead of the CLI's single user-facing error line.
|
|
159
|
+
|
|
160
|
+
## [0.4.0] - 2026-08-30
|
|
161
|
+
|
|
162
|
+
### Added
|
|
163
|
+
|
|
164
|
+
- `set-alternate` command for replacing every matching Separation's composite
|
|
165
|
+
fallback with one linear DeviceCMYK FunctionType 2 preview while preserving
|
|
166
|
+
the spot plate and all paint operands.
|
|
167
|
+
- A real veraPDF public-corpus case and synthetic before/after render for
|
|
168
|
+
alternate-preview changes.
|
|
169
|
+
- A reproducible synthetic 64/128-spot inventory benchmark with deterministic
|
|
170
|
+
page/Form parse counts, timings, and Python heap measurements.
|
|
171
|
+
|
|
172
|
+
### Changed
|
|
173
|
+
|
|
174
|
+
- Read-only inventory now attributes structural hazards in one traversal and
|
|
175
|
+
interprets every reached page or compatible Form stream once, eliminating the
|
|
176
|
+
previous colorant-by-stream parse multiplier while preserving per-colorant
|
|
177
|
+
status and paint counters.
|
|
178
|
+
|
|
179
|
+
### Fixed
|
|
180
|
+
|
|
181
|
+
- Shared Forms with direct resource dictionaries now use stable owner-bound
|
|
182
|
+
identities instead of transient pikepdf wrapper IDs, and compatible shared
|
|
183
|
+
Form use is attributed to every calling page without recounting its paint.
|
|
184
|
+
- Cached changes from nested shared Forms now propagate through every enclosing
|
|
185
|
+
Form, so removal reports every affected calling page even when the inner Form
|
|
186
|
+
was first reached through another path.
|
|
187
|
+
- Inline images are recognized with current pikepdf objects. Page aliases and
|
|
188
|
+
Form streams that would require rewriting now fail during the dry run rather
|
|
189
|
+
than reaching an unsafe apply pass.
|
|
190
|
+
- Page-tree `/Parent` back-links and shared resource-hazard subtrees are handled
|
|
191
|
+
without repeated cross-expansion, keeping inventory linear as pages, Forms,
|
|
192
|
+
and aliases grow while preserving non-page `/Parent` reachability.
|
|
193
|
+
|
|
194
|
+
### Safety
|
|
195
|
+
|
|
196
|
+
- Alternate-preview changes reject ambiguous roles, reserved/process names,
|
|
197
|
+
target-related DeviceN/NChannel use, malformed target name fields, signed or
|
|
198
|
+
restricted inputs, and unsafe output aliases before publication.
|
|
199
|
+
- In-memory and post-save checks bind inventory, content streams, the complete
|
|
200
|
+
document graph, and every requested preview definition to the planned change.
|
|
201
|
+
- Release metadata validation now binds each version's comparison link to the
|
|
202
|
+
immediately preceding dated release, and the maintainer checklist names every
|
|
203
|
+
local lock, metadata, size, corpus, benchmark, and artifact gate.
|
|
204
|
+
|
|
205
|
+
## [0.3.0] - 2026-08-30
|
|
206
|
+
|
|
207
|
+
### Added
|
|
208
|
+
|
|
209
|
+
- Atomic `rename` command for exact, case-sensitive spot-plate aliasing without
|
|
210
|
+
changing alternate color spaces, tint transforms, tint operands, resource
|
|
211
|
+
aliases, or content streams.
|
|
212
|
+
- Rename support for structurally consistent DeviceN/NChannel spot components,
|
|
213
|
+
`/Colorants`, `/MixingHints`, page `/SeparationInfo`, and normal PrinterMark
|
|
214
|
+
appearances.
|
|
215
|
+
- Role-aware DeviceN/NChannel inventory for arbitrary process component names,
|
|
216
|
+
canonical CMYK components, spot components, `/All`, and `/None`.
|
|
217
|
+
- Stable object identities and human-readable locations for every reachable
|
|
218
|
+
Separation and DeviceN definition.
|
|
219
|
+
- Exact-name dependency inventory for NChannel process/colorant/mixing-hint
|
|
220
|
+
entries, page `/SeparationInfo`, printer-mark colorants, and TrapNet
|
|
221
|
+
`/SeparationColorNames`.
|
|
222
|
+
- A pinned six-file public prepress release corpus spanning painted
|
|
223
|
+
Separations, DeviceCMYK and DeviceRGB alternates, DeviceN dependencies,
|
|
224
|
+
arbitrary NChannel process components, and multi-page definitions.
|
|
225
|
+
- Reproducible synthetic removal and rename documentation images.
|
|
226
|
+
- Automated GitHub release assets, SHA-256 checksums, and build-provenance
|
|
227
|
+
attestations after the full test and public-corpus gates pass.
|
|
228
|
+
|
|
229
|
+
### Fixed
|
|
230
|
+
|
|
231
|
+
- Rename now accepts pikepdf's storage-only XMP packet reserialization in valid
|
|
232
|
+
PDF/A files while still rejecting RDF values, namespaces, comments, packet
|
|
233
|
+
identity/mutability, malformed wrapper grammar, and unrelated metadata
|
|
234
|
+
changes.
|
|
235
|
+
|
|
236
|
+
### Safety
|
|
237
|
+
|
|
238
|
+
- Rename rejects missing or ambiguous sources, existing destinations, process
|
|
239
|
+
and reserved names, malformed name relationships, and unsupported TrapNet,
|
|
240
|
+
type 5 halftone, OPI, and PrinterMark rollover/down dependencies before any
|
|
241
|
+
output is published.
|
|
242
|
+
- Rename output is reopened and semantically verified before atomic replacement;
|
|
243
|
+
failed `--force` processing preserves the existing destination.
|
|
244
|
+
- Rename verification binds alternate spaces, tint transforms, content streams,
|
|
245
|
+
and dependency payloads to their semantic locations and rejects any change
|
|
246
|
+
beyond the planned name slots.
|
|
247
|
+
- Target-related DeviceN/NChannel, Process, MixingHints, Colorants, and
|
|
248
|
+
SeparationInfo relationships are validated before mutation.
|
|
249
|
+
- `remove --all` no longer treats custom-named NChannel process components as
|
|
250
|
+
spots.
|
|
251
|
+
- Removal fails closed when a selected name remains in a supported exact-name
|
|
252
|
+
prepress dependency, including pre-separated page metadata.
|
|
253
|
+
- The release build backend is exactly pinned and locked; unexpected release
|
|
254
|
+
filenames, symlinks, extra assets, version drift, and checksum changes fail
|
|
255
|
+
before publication.
|
|
256
|
+
|
|
257
|
+
## [0.2.1] - 2026-08-29
|
|
258
|
+
|
|
259
|
+
First public beta release.
|
|
260
|
+
|
|
261
|
+
### Added
|
|
262
|
+
|
|
263
|
+
- `list`, `check`, exact-name `remove`, and atomic `remove --all` commands.
|
|
264
|
+
- Removal of supported vector fills, strokes, combined paint, and text paint.
|
|
265
|
+
- Nested Form processing with context and nesting safeguards.
|
|
266
|
+
- A synthetic demo generator, before/after screenshots, and runtime-generated
|
|
267
|
+
test PDFs.
|
|
268
|
+
|
|
269
|
+
### Safety
|
|
270
|
+
|
|
271
|
+
- Preserve process `/Cyan`, `/Magenta`, `/Yellow`, and `/Black` in `--all`.
|
|
272
|
+
- Preserve reserved `/All` and `/None` separations.
|
|
273
|
+
- Refuse signed, encrypted, restricted, malformed, and unsupported mutations.
|
|
274
|
+
- Refuse unresolved color-space and pattern resources.
|
|
275
|
+
- Reject output symlinks instead of following them under `--force`.
|
|
276
|
+
- Traverse deep PDF object graphs iteratively and reject excessive Form nesting
|
|
277
|
+
without a traceback or partial output.
|
|
278
|
+
- Verify saved output before atomic replacement and preserve an existing forced
|
|
279
|
+
destination whenever processing fails.
|
|
280
|
+
|
|
281
|
+
[Unreleased]: https://github.com/Hyperrick/spotpdf/compare/v0.7.0...HEAD
|
|
282
|
+
[0.7.0]: https://github.com/Hyperrick/spotpdf/compare/v0.6.0...v0.7.0
|
|
283
|
+
[0.6.0]: https://github.com/Hyperrick/spotpdf/compare/v0.5.0...v0.6.0
|
|
284
|
+
[0.5.0]: https://github.com/Hyperrick/spotpdf/compare/v0.4.0...v0.5.0
|
|
285
|
+
[0.4.0]: https://github.com/Hyperrick/spotpdf/compare/v0.3.0...v0.4.0
|
|
286
|
+
[0.3.0]: https://github.com/Hyperrick/spotpdf/compare/v0.2.1...v0.3.0
|
|
287
|
+
[0.2.1]: https://github.com/Hyperrick/spotpdf/releases/tag/v0.2.1
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# Contributing to spotpdf
|
|
2
|
+
|
|
3
|
+
Thank you for helping make PDF spot-color processing safer and easier to audit.
|
|
4
|
+
|
|
5
|
+
## Before opening an issue
|
|
6
|
+
|
|
7
|
+
- Search existing issues.
|
|
8
|
+
- Use the route described in [SUPPORT.md](SUPPORT.md) for usage questions, bugs,
|
|
9
|
+
feature requests, and private security reports.
|
|
10
|
+
- Include the `spotpdf`, Python, and operating-system versions.
|
|
11
|
+
- Include the exact command, stdout, stderr, expected result, and actual result.
|
|
12
|
+
- Say whether the PDF is signed, encrypted, or modification-restricted.
|
|
13
|
+
- Prefer a minimal PDF generated from code.
|
|
14
|
+
|
|
15
|
+
Never upload a confidential customer or production PDF to a public issue. If a
|
|
16
|
+
reproducer cannot be shared safely, describe its structure or create a synthetic
|
|
17
|
+
equivalent.
|
|
18
|
+
|
|
19
|
+
Security vulnerabilities belong in a private report as described in
|
|
20
|
+
[SECURITY.md](SECURITY.md), not a public issue.
|
|
21
|
+
|
|
22
|
+
## Development setup
|
|
23
|
+
|
|
24
|
+
The documentation-image gate and rename render comparison require Poppler's
|
|
25
|
+
`pdftoppm` executable. Install Poppler before running the pull-request
|
|
26
|
+
checklist. On macOS with Homebrew:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
brew install poppler
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
On Debian or Ubuntu:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
sudo apt-get update
|
|
36
|
+
sudo apt-get install --yes poppler-utils
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
On other platforms, install a current [Poppler](https://poppler.freedesktop.org/)
|
|
40
|
+
build and put `pdftoppm` on `PATH`. In a POSIX shell, confirm the executable is
|
|
41
|
+
discoverable and print its version:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
command -v pdftoppm
|
|
45
|
+
pdftoppm -v
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
In Windows PowerShell, run the equivalent checks:
|
|
49
|
+
|
|
50
|
+
```powershell
|
|
51
|
+
Get-Command pdftoppm -ErrorAction Stop
|
|
52
|
+
pdftoppm -v
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Poppler alone covers the documentation-image gate and rename render comparison.
|
|
56
|
+
The convert render/plate comparison also requires qpdf and Ghostscript;
|
|
57
|
+
pull-request CI runs it in the Linux Python 3.13 test job. The maintainer-only
|
|
58
|
+
public corpus gate uses the same three tools and runs for releases. Its complete
|
|
59
|
+
setup remains in
|
|
60
|
+
[docs/public-corpus.md](docs/public-corpus.md#run-locally).
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
git clone https://github.com/Hyperrick/spotpdf.git
|
|
64
|
+
cd spotpdf
|
|
65
|
+
uv sync --locked --dev
|
|
66
|
+
uv run python -m unittest discover -s tests -v
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Before submitting a pull request, run:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
uv sync --locked --dev --group release
|
|
73
|
+
uv lock --check
|
|
74
|
+
uv run ruff check .
|
|
75
|
+
uv run ruff format --check .
|
|
76
|
+
uv run python scripts/check_python_size.py
|
|
77
|
+
uv run python scripts/check_repository.py
|
|
78
|
+
uv run python scripts/create_docs_images.py
|
|
79
|
+
uv run python scripts/create_docs_images.py --check
|
|
80
|
+
uv run python -m unittest discover -s tests -v
|
|
81
|
+
uv run python scripts/benchmark_inventory.py --runs 3
|
|
82
|
+
artifact_dir="$(mktemp -d)"
|
|
83
|
+
uv build --no-build-isolation --out-dir "$artifact_dir"
|
|
84
|
+
uv run --no-sync python scripts/check_pypi_readme.py \
|
|
85
|
+
"$artifact_dir"/*.whl "$artifact_dir"/*.tar.gz
|
|
86
|
+
uv run --no-sync twine check --strict \
|
|
87
|
+
"$artifact_dir"/*.whl "$artifact_dir"/*.tar.gz
|
|
88
|
+
uv run python scripts/check_distribution.py "$artifact_dir"
|
|
89
|
+
uv run python scripts/smoke_distributions.py "$artifact_dir"
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The distribution check validates both archive contents and the canonical
|
|
93
|
+
Support and Security links embedded in each archive's Core Metadata.
|
|
94
|
+
|
|
95
|
+
Maintainers should also run the hash-pinned public corpus before a release:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
uv run python scripts/check_public_corpus.py
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
See [docs/public-corpus.md](docs/public-corpus.md) for system dependencies,
|
|
102
|
+
attribution, licenses, and offline reproduction.
|
|
103
|
+
|
|
104
|
+
The inventory benchmark creates 64- and 128-spot PDFs only in a temporary
|
|
105
|
+
directory. Its page/Form parse counts are deterministic; timing and Python heap
|
|
106
|
+
measurements are diagnostic. See [docs/performance.md](docs/performance.md).
|
|
107
|
+
|
|
108
|
+
Documentation image checks regenerate all five synthetic visuals in a private
|
|
109
|
+
temporary directory. A deterministic SHA-256 manifest binds them to the demo,
|
|
110
|
+
locked environment, generator, and current spotpdf source. SVGs and that
|
|
111
|
+
manifest must match byte for byte. PNGs use bounded decoding and calibrated
|
|
112
|
+
pixel-drift limits so harmless Poppler antialiasing differences between
|
|
113
|
+
operating systems pass while meaningful visual changes fail: at most 2.5% of
|
|
114
|
+
pixels may have an RGB channel delta above 16, and the mean absolute channel
|
|
115
|
+
delta may not exceed 2.0.
|
|
116
|
+
|
|
117
|
+
## Processing budget changes
|
|
118
|
+
|
|
119
|
+
Any counter, default, or preflight-order change must include:
|
|
120
|
+
|
|
121
|
+
- an exact-limit success test and a limit-plus-one failure test;
|
|
122
|
+
- small runtime-generated fixtures, including compressed content when decoded
|
|
123
|
+
size is involved;
|
|
124
|
+
- atomicity coverage proving every mutating command preserves an existing
|
|
125
|
+
`--force` destination and leaves no private temporary file;
|
|
126
|
+
- platform-stable assertions on counts and structured errors, not wall-clock,
|
|
127
|
+
RSS, compression ratios, or allocator behavior; and
|
|
128
|
+
- synchronized README, processing-budget, security, architecture, and changelog
|
|
129
|
+
documentation.
|
|
130
|
+
|
|
131
|
+
Keep limit configuration immutable and inject it per public library call. Do
|
|
132
|
+
not store per-job overrides or usage in mutable CLI/module globals. New limits
|
|
133
|
+
must document what they include, what they exclude, when they are checked, and
|
|
134
|
+
which native or operating-system work can occur before the overrun is observed.
|
|
135
|
+
|
|
136
|
+
## JSON CLI contract changes
|
|
137
|
+
|
|
138
|
+
Treat `spotpdf.cli/v1` as a public automation API:
|
|
139
|
+
|
|
140
|
+
- add command fields through explicit serializers, never `dataclasses.asdict()`;
|
|
141
|
+
- keep PDF names and paths exact while sorting every set-derived array;
|
|
142
|
+
- add subprocess tests for exit code, stdout/stderr purity, canonical JSON,
|
|
143
|
+
Unicode and control characters, and the default text output;
|
|
144
|
+
- document every new result field or stable error code in
|
|
145
|
+
[JSON output and automation](docs/json-output.md) and `CHANGELOG.md`; and
|
|
146
|
+
- introduce a new schema identifier before removing, renaming, changing the
|
|
147
|
+
type of, or changing the meaning of an existing field.
|
|
148
|
+
|
|
149
|
+
Human-readable text remains the default. A JSON change must not weaken the
|
|
150
|
+
same atomic-output and fail-closed guarantees exercised by text mode.
|
|
151
|
+
|
|
152
|
+
## PDF fixtures
|
|
153
|
+
|
|
154
|
+
Tests should build the smallest possible PDF with `pikepdf` inside a temporary
|
|
155
|
+
directory. Generated fixtures must not survive the test run.
|
|
156
|
+
|
|
157
|
+
If a third-party fixture is essential, prefer a commit-pinned external corpus
|
|
158
|
+
case over vendoring it. Document all of the following in the pull request:
|
|
159
|
+
|
|
160
|
+
- canonical source URL;
|
|
161
|
+
- author or project;
|
|
162
|
+
- exact license and redistribution permission;
|
|
163
|
+
- why a synthetic fixture cannot exercise the same behavior.
|
|
164
|
+
|
|
165
|
+
Do not add customer files, real print jobs, or PDFs with personal information.
|
|
166
|
+
The repository and distribution checks reject all tracked `*.pdf` files by
|
|
167
|
+
default, including mixed-case extensions. Repository-relative Markdown
|
|
168
|
+
destinations and repository-relative targets in HTML `href`, `src`, and
|
|
169
|
+
`srcset` attributes may only point to tracked files; ignored local files are
|
|
170
|
+
deliberately not accepted. External URLs and URL fragments are outside this
|
|
171
|
+
check's scope, but the file path before a fragment is still validated. The
|
|
172
|
+
package README has a stricter release gate: repository content must use absolute
|
|
173
|
+
URLs bound to the exact release tag so links and images also work on PyPI.
|
|
174
|
+
Contribution, support, security-policy, and release-process routes are the
|
|
175
|
+
deliberate live-policy exceptions.
|
|
176
|
+
|
|
177
|
+
## Design rules
|
|
178
|
+
|
|
179
|
+
- Keep mutations atomic and fail closed on unsupported semantics.
|
|
180
|
+
- Add a regression test before or with every bug fix.
|
|
181
|
+
- Keep CLI behavior and exit codes backward compatible unless the change is
|
|
182
|
+
explicitly documented.
|
|
183
|
+
- Keep modules focused and Python files under 600 lines.
|
|
184
|
+
- Update `CHANGELOG.md` for user-visible behavior.
|
|
185
|
+
- Preserve vector data; do not introduce implicit rasterization.
|
|
186
|
+
|
|
187
|
+
See [AGENTS.md](AGENTS.md) for the complete organization guidelines and
|
|
188
|
+
[docs/architecture.md](docs/architecture.md) for current boundaries.
|
|
189
|
+
|
|
190
|
+
## Pull requests
|
|
191
|
+
|
|
192
|
+
Keep each pull request narrowly scoped. Describe compatibility impact, include
|
|
193
|
+
tests and documentation, and disclose the origin and license of every fixture.
|
|
194
|
+
By contributing, you agree that your contribution is licensed under the
|
|
195
|
+
project's MIT License.
|
spotpdf-0.7.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 spotpdf contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
include CHANGELOG.md
|
|
2
|
+
include CONTRIBUTING.md
|
|
3
|
+
include LICENSE
|
|
4
|
+
include README.md
|
|
5
|
+
include SECURITY.md
|
|
6
|
+
include SUPPORT.md
|
|
7
|
+
include src/spotpdf/py.typed
|
|
8
|
+
include docs/architecture.md
|
|
9
|
+
include docs/compatibility.md
|
|
10
|
+
include docs/json-output.md
|
|
11
|
+
include docs/performance.md
|
|
12
|
+
include docs/processing-budgets.md
|
|
13
|
+
include docs/python-api.md
|
|
14
|
+
include docs/public-corpus.md
|
|
15
|
+
include docs/releasing.md
|
|
16
|
+
include docs/images/demo-after.png
|
|
17
|
+
include docs/images/demo-alternate.png
|
|
18
|
+
include docs/images/demo-before.png
|
|
19
|
+
include docs/images/demo-convert.svg
|
|
20
|
+
include docs/images/demo-rename.svg
|
|
21
|
+
include docs/images/generation.json
|
|
22
|
+
recursive-include corpus *.toml
|
|
23
|
+
recursive-include examples *.py
|
|
24
|
+
recursive-include scripts *.py
|
|
25
|
+
recursive-include tests *.py
|
|
26
|
+
global-exclude *.pdf
|
|
27
|
+
global-exclude .DS_Store
|
|
28
|
+
global-exclude *.py[cod]
|
|
29
|
+
prune .venv
|
|
30
|
+
prune in
|
|
31
|
+
prune out
|
|
32
|
+
prune tmp
|