Skip to content

feat(pdf): AES-256 R6, text render modes 4-7, mesh shading types 4-7 - #44

Merged
Malcolmnixon merged 8 commits into
mainfrom
feature/pdf-r6-textclip-meshshading
Oct 10, 2026
Merged

Malcolmnixon merged 8 commits into
mainfrom
feature/pdf-r6-textclip-meshshading

Conversation

@Malcolmnixon

Copy link
Copy Markdown
Member

Pull Request

Description

Adds three PDF rendering features to DemaConsulting.CanvasNet.Pdf:

  • AES-256 /R 6 encryption (ISO 32000-2 Algorithm 2.B hardened hash), with fixtures generated
    independently via pypdf.
  • Text render modes 4-7 (text clipping).
  • Mesh shading types 4-7 (free-form and lattice triangle meshes, Coons and tensor-product
    patches), via both the sh operator and /PatternType 2 shading patterns.

The last commit adds a hand-authored fixture, shading-mesh-types-4-5-6-7.pdf, with one quadrant per
mesh type, and an end-to-end system-integration test. The type 7 interior control points are crossed
so its interior visibly differs from type 6. The fixture was checked against Chrome's PDF viewer.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Code quality improvement

Related Issues

Closes #

Pre-Submission Checklist

Before submitting this pull request, ensure you have completed the following:

Build and Test

  • Code builds successfully and all tests pass: pwsh ./build.ps1 (11,844 tests, 0 failures)
  • Code produces zero warnings

Code Quality

  • New code has appropriate XML documentation comments
  • Static analyzer warnings have been addressed

Quality Checks

Please run the following checks before submitting:

  • All linters pass: pwsh ./lint.ps1 (no errors found)

Testing

  • Added unit tests for new functionality
  • Updated existing tests if behavior changed
  • All tests follow the AAA (Arrange, Act, Assert) pattern
  • Test coverage is maintained or improved

Documentation

  • Updated README.md (if applicable)
  • Updated docs/ documentation (if applicable)
  • Added code examples for new features (if applicable)
  • Updated requirements.yaml (if applicable)

Additional Notes

build.ps1 and lint.ps1 were run on the final commit. An automated code review of the fixture
commit found no issues; no formal review has been done.

Malcolm Nixon and others added 4 commits October 9, 2026 18:17
…2.B)

Implement Algorithm 2.B for the standard security handler's validation hash
and intermediate key (user and owner variants), remove the R6 fail-closed
throw (unknown /R still throws pdf-encrypted-r-{n}), add pypdf-generated R6
fixtures and tests, and update requirements, design, verification, SysML2,
user guide, XML docs and fixtures README.
Add a hand-authored 400x400 PDF fixture painting one /ShadingType 4, 5, 6 and
7 mesh per quadrant via sh, with a system-integration test asserting corner
colors, unpainted regions, and that type 7 interior control points change the
interior relative to type 6.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings October 10, 2026 00:09

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Mesh painting can allocate a full-page temporary buffer even for a small painted region, and several feature descriptions remain inaccurate.

4 open findings
What changed in this PR

This PR extends CanvasNet’s PDF renderer with AES-256 R6 decryption, text clipping, and mesh shading.

Changes:

  • Adds R6 password hashing and decryption support.
  • Renders text clipping modes 4–7 and mesh shading types 4–7.
  • Adds tests, a mesh fixture, and supporting documentation and traceability updates.
File Description
test/​DemaConsulting.CanvasNet.Pdf.Tests/​PdfSystemIntegrationTests.cs Tests mesh rendering from a fixture.
test/​DemaConsulting.CanvasNet.Pdf.Tests/​PdfFixtures/​README.md Documents new PDF fixtures.
test/​DemaConsulting.CanvasNet.Pdf.Tests/​PdfDocumentTests.cs Tests text clipping behavior.
test/​DemaConsulting.CanvasNet.Pdf.Tests/​PdfDocumentMeshShadingTests.cs Tests mesh decoding and rendering.
test/​DemaConsulting.CanvasNet.Pdf.Tests/​PdfDocumentEncryptionTests.cs Tests R6 authentication and decryption.
src/​DemaConsulting.CanvasNet.Pdf/​PdfDocument.Text.cs Implements text clipping modes.
src/​DemaConsulting.CanvasNet.Pdf/​PdfDocument.Patterns.Tiling.cs Preserves text-clip state during tile rendering.
src/​DemaConsulting.CanvasNet.Pdf/​PdfDocument.Patterns.Shading.Mesh.cs Decodes and paints mesh shadings.
src/​DemaConsulting.CanvasNet.Pdf/​PdfDocument.Patterns.Shading.cs Dispatches mesh shadings.
src/​DemaConsulting.CanvasNet.Pdf/​PdfDocument.Patterns.cs Stores resolved mesh patterns.
src/​DemaConsulting.CanvasNet.Pdf/​PdfDocument.PathOps.cs Paints mesh-filled paths.
src/​DemaConsulting.CanvasNet.Pdf/​PdfDocument.Images.cs Preserves text-clip state in forms.
src/​DemaConsulting.CanvasNet.Pdf/​PdfDocument.GraphicsState.cs Documents clipping render modes.
src/​DemaConsulting.CanvasNet.Pdf/​PdfDocument.Fonts.Type3.cs Preserves text-clip state in Type 3 glyphs.
src/​DemaConsulting.CanvasNet.Pdf/​PdfDocument.Encryption.cs Implements R6 password hashing.
src/​DemaConsulting.CanvasNet.Pdf/​PdfDocument.cs Updates PDF API documentation.
src/​DemaConsulting.CanvasNet.Pdf/​PdfDocument.ContentStream.cs Initializes text-clip state.
src/​DemaConsulting.CanvasNet.Pdf/​NamespaceDoc.cs Updates namespace documentation.
docs/​verification/​canvas-net-pdf/​pdf-document.md Describes feature verification.
docs/​user_guide/​introduction.md Updates supported-feature guidance.
docs/​sysml2/​model/​canvas-net-pdf/​pdf-document.sysml Maps the mesh source file.
docs/​sysml2/​model/​canvas-net-pdf.sysml Updates system-level encryption description.
docs/​reqstream/​canvas-net-pdf/​pdf-document.yaml Adds feature requirements and tests.
docs/​reqstream/​canvas-net-pdf.yaml Links system requirements to new features.
docs/​design/​introduction.md Lists the mesh renderer.
docs/​design/​canvas-net-pdf/​pdf-document.md Describes the implementations.
docs/​design/​canvas-net-pdf.md Updates the PDF system design summary.
.reviewmark.yaml Includes the mesh source in review tracking.
.cspell.yaml Adds terms used by the changes.

🧠 Review effort: Lite


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/DemaConsulting.CanvasNet.Pdf/PdfDocument.Patterns.Shading.Mesh.cs Outdated
Comment thread docs/design/canvas-net-pdf.md Outdated
Comment thread src/DemaConsulting.CanvasNet.Pdf/NamespaceDoc.cs Outdated
Comment thread src/DemaConsulting.CanvasNet.Pdf/PdfDocument.cs Outdated
Address PR #44 review: rasterize meshes into a bitmap sized to the path's
device bounds instead of a full-page surface, and correct the design,
namespace and class docs on sh/W clipping, mesh types and Tr 4-7 limits.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings October 10, 2026 00:29

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Mesh input validation and clipped rendering resource use remain unresolved.

0 open findings

4 resolved since last review
Previously missed (2)

In code that hasn't changed since last review

Medium severity Validate type-4 edge flags before adding vertices

src/​DemaConsulting.CanvasNet.Pdf/​PdfDocument.Patterns.Shading.Mesh.cs:369

For a type-4 record starting with flag 0, this loop reads the flag fields of the next two vertices but never checks them. A reserved edge-flag value such as 3 in either field is therefore accepted, although the outer record path rejects values above 2 and the mesh fail-closed requirement says invalid flags must throw InvalidDataException. Validate these fields before adding their vertices, and cover a reserved flag in each position with a regression test.

Medium severity Clip temporary mesh raster bounds before allocation

src/​DemaConsulting.CanvasNet.Pdf/​PdfDocument.Patterns.Shading.Mesh.cs:549

When sh has no /BBox, BuildShadingPaintRegion passes a full-page path. This method bounds the temporary surface only by that path; it applies _gs.Clip after rasterizing. Thus a mesh drawn under a tiny W n clip can still allocate an 8192×8192 temporary surface (roughly 256 MiB) and rasterize the whole page. Restrict the temporary raster bounds to the intersection of the paint region and the active clip before allocating and painting.

🧠 Review effort: Lite

…ter to clip

Address PR #44 review: reject reserved edge flags on the 2nd/3rd vertex of a
flag-0 type 4 record, and intersect the temporary mesh raster with the active
clip's bounds (new internal ClipMask.Bounds) before allocating.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings October 10, 2026 00:49

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The combined encryption and rendering changes need human review, and an unresolved full-page mesh allocation can materially increase memory use.

0 open findings

Previously missed (1)

In code that hasn't changed since last review

Medium severity Bound mesh paint surfaces to transformed geometry

src/​DemaConsulting.CanvasNet.Pdf/​PdfDocument.Patterns.Shading.cs:242

The path-bounds optimization does not help a sh mesh with no /BBox or active clip: BuildShadingPaintRegion(null) makes a full-page path, and PaintMesh allocates a temporary Surface of those bounds before rasterizing. A small mesh on an 8192×8192 page therefore still allocates about 256 MiB per paint, even though most pixels remain transparent. Bound the temporary to the transformed mesh geometry as well as the path and clip (preserving the pattern /Background area), or avoid the full-page temporary for this case.

🧠 Review effort: Lite

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings October 10, 2026 01:28

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Mesh colors produced by nonlinear functions are interpolated in the wrong order, and feature documentation and traceability need updates.

0 open findings

Previously missed (5)

In code that hasn't changed since last review

Medium severity Map mesh samples through functions before interpolation

src/​DemaConsulting.CanvasNet.Pdf/​PdfDocument.Patterns.Shading.Mesh.cs:848

For a mesh with /Function, each vertex or patch-corner parameter must be mapped to a color before those colors are interpolated. Here the interpolated parameter is looked up instead: with endpoint parameters 0 and 1 and a nonlinear function f(t)=t², a midpoint becomes f(0.5)=0.25 rather than the midpoint of endpoint colors, 0.5. Map samples through the function when decoding their colors, then interpolate the resulting components; cover this with a nonlinear-function mesh test.

Low severity Add mesh shading tests to review paths

.reviewmark.yaml:764

This review's paths include the new mesh source, but not test/DemaConsulting.CanvasNet.Pdf.Tests/PdfDocumentMeshShadingTests.cs. Add the new 894-line test file alongside the other PdfDocument tests so ReviewMark's unit review includes the verification code.

Low severity Document unsupported shading types as outside 2–7

docs/​design/​canvas-net-pdf/​pdf-document.md:1391

This section now describes mesh shadings as supported, but the error-handling list at line 1529 still says every /ShadingType other than 2 or 3 is unsupported. Change that list to say types outside 2–7 so the design document does not contradict the new behavior.

Low severity Extend owner-password requirement to R6

docs/​reqstream/​canvas-net-pdf/​pdf-document.yaml:319

The owner-password requirement still limits the Algorithm 2.A path to R5 (line 309), even though this change adds an R6 owner-password test here and implements its Algorithm 2.B hash. Extend the requirement text to cover R5 and R6, distinguishing the R6 hash.

Low severity Add mesh shading tests to testRef

docs/​sysml2/​model/​canvas-net-pdf/​pdf-document.sysml:6

The updated sourceRef includes the mesh implementation, but the testRef immediately below omits test/DemaConsulting.CanvasNet.Pdf.Tests/PdfDocumentMeshShadingTests.cs. Add the new test file to testRef so the modeled unit's verification artifacts remain discoverable, as required by the repository's SysML artifact-location convention.

🧠 Review effort: Lite

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings October 10, 2026 02:05

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The combined cryptographic and rendering changes warrant final human validation.

0 open findings

🧠 Review effort: Lite

@Malcolmnixon
Malcolmnixon merged commit be0c74f into main Oct 10, 2026
7 checks passed
@Malcolmnixon
Malcolmnixon deleted the feature/pdf-r6-textclip-meshshading branch October 10, 2026 02:23
@Malcolmnixon

Copy link
Copy Markdown
Member Author

Thanks - I looked into the mesh /Function finding (Mesh.cs:848) and believe the current behavior is the spec-correct one. For mesh shadings (ISO 32000-1 8.7.4.5.5-8.7.4.5.8), when /Function is present the per-vertex (or per-patch-corner) value is a single parametric value t. The spec has t interpolated across the triangle/patch and the function applied to the interpolated t to obtain the color; the function is not applied at the vertices before interpolation. With f(t)=t^2 and endpoints 0 and 1 the midpoint evaluates to f(0.5)=0.25, which is the intended result; pre-mapping the vertices would make any nonlinear function (for example multi-stop ramps) collapse to a straight endpoint blend. So the order of operations is unchanged. Two valid sub-points will be addressed in a follow-up: the function is sampled through a 256-entry lookup table (adding interpolation between entries) and a nonlinear-function mesh test will be added to pin this behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants