Files
OpenNest/docs/nest-reports.md
T
aj 314ca2f2a6 feat(desktop): add File -> Export Nest Report command
Capture the report target and reject whole-job nesting, open progress
windows, interactive fill and busy plate actions across every view
sharing the nest; revalidate after the save dialog, capture the snapshot
synchronously on the UI thread and render it through OpenNest.Reporting.
Windows adapter tests cover enablement, guards, cancel, success and a
write failure against an existing destination (compile-only on Linux;
Windows runtime acceptance still owed).
2026-09-29 18:29:05 -04:00

150 lines
8.7 KiB
Markdown

# Nest report PDF export
Desktop: **File -> Export Nest Report...** writes `<nest-name>.report.pdf` for the
active nest's whole job through an overwrite-confirming save dialog. The command
is disabled without an open document and during a background database save, and
it refuses to run while whole-job nesting, an open progress window, interactive
fill or a busy plate action holds the nest or any second window sharing it. After
the dialog closes, the target and those conditions are revalidated; the snapshot
is then captured synchronously on the UI thread and only that detached snapshot
reaches the renderer. Export never changes the selected plate, dirty state,
timestamps or quantities, and it never touches post selection, verification or
CNC output. Opening and printing use any normal PDF viewer; the report includes
no printer controls and there are no persisted report settings or templates.
The same two calls back the command and any future integration:
```csharp
var snapshot = NestReportBuilder.Capture(nest, DateTimeOffset.Now);
NestPdfWriter.Write(snapshot, destinationPath);
```
Reporting does not select a post, generate CNC, refresh drawing quantities, change
the selected plate, or certify that a layout passed geometry/pre-post checks.
## Snapshot contract
- IDs (`R001`, ...) are local to the report. Visit non-cutoff placements in plate
and part order, then append unseen demanded drawings ordered by ordinal name and
source path. Identity is by reference: distinct same-named drawings stay distinct,
including placed drawings absent from the name-keyed drawing collection.
- Recount nested quantities from placements times sheet copies exactly once using
checked wide integers. Include unplaced demand; report shortage and extra
separately. Cached `Quantity.Nested` is not authoritative.
- Distinguish distinct layouts from physical sheets. Per-sheet quantities do not
include copies; total quantities do. Utilization uses net part area divided by
full sheet area, excluding cutoffs.
- Actual placed programs already contain rotations. Capture their material paths
with the location applied once. Preserve holes and intentional tab gaps; exclude
rapid, scribe, lead-in and lead-out paths. Keep Cut and Display contours.
- If any material contour of a part is open, show all its contours without fill.
Do not close the gap or substitute the drawing's nominal outline. Cutoffs are
separate strokes with no product ID or quantity.
- Snapshots retain values, not live drawings, parts, CNC programs or mutable
geometry. Invalid/missing/nonfinite geometry fails with drawing/plate context.
## Layout, pagination and dense-label fallback
An empty/demand-only job, a single plate, or many plates and drawings all
produce one document: a Letter portrait summary followed by one Letter
landscape section per plate, every page carrying a repeated header and
"Page X of Y". Long notes, long drawing names, many drawings and many parts
per sheet paginate naturally: MigraDoc continues the Plates/Parts tables and
each plate's part table across pages with the heading row repeated, and notes
flow as an ordinary paragraph. No row, table or note text is ever dropped or
truncated to fit a page.
Each part ID is centered on its part's pole of inaccessibility (`PolyLabel`,
the same method `PlateView`'s `LayoutPart` uses), computed on a
placement-independent quantized copy so identical parts always get the
identical label position and the label naturally clears a central hole. When
an ID cannot sit legibly inside its own material at overview scale (for
example, a cluster of tiny repeated parts), the plate gains a lettered
(rows)/numbered (columns) map grid drawn beneath the sheet, and only the
crowded cells get a zoomed, framed detail page listing that cell's real
coordinates. Detail-cell outlines use a long dash-dot stroke, distinct from
the shorter dashed scrap-cutoff stroke and the dotted grid lines. IDs are
never shrunk below 7 pt or silently dropped.
The writer still rejects, with `NotSupportedException` and before touching
the destination:
- a page header (nest name plus plate/material line) needing more than 3
wrapped lines;
- a table cell needing more than 20 wrapped lines;
- a part ID that cannot be placed legibly even in the most zoomed supported
detail view, or a plate that would need more than 24 detail views to label
every part — named with the plate, part index and ID.
Summary thumbnails and the sheet diagram are vector paths, never raster images.
Each thumbnail is a small PDFsharp page embedded by MigraDoc as a form XObject.
The diagram is drawn into a fixed-height table row reserved in MigraDoc's flow,
located after layout with `DocumentRenderer.GetRenderInfoFromPage`. Closed parts
use one even-odd filled path, so holes stay unfilled. Parts with any open contour,
and cutoffs (dashed), stroke each contour as its own path: PDFsharp's
`StartFigure` does not start a new subpath after an open figure, and would
otherwise draw a false segment across a tab gap.
A report is fully rendered to a unique temporary sibling before replacement of
its destination. A failed render or write leaves an existing report untouched and
removes temporary output. Applications should obtain overwrite consent before
calling the library. PDFsharp/MigraDoc layout and font state is process-wide;
`NestPdfWriter.Write` serializes every export behind one static lock so
concurrent calls cannot lay out text differently from a sequential export.
Advanced timing, cutting distances, pierce counts, weights, costs, gas use, and
machine/NC identity are intentionally omitted until their semantics are verified.
The PDF can be opened or printed through a normal PDF viewer; its fitted diagram
is not a dimensioned cutting drawing.
## Backend, fonts and redistribution
- Official `PDFsharp-MigraDoc` Core **6.2.4**, with `PDFsharp` **6.2.4**, targeting
net8.0. No GDI/WPF dependency or printer driver is used. MigraDoc owns flowing
document layout; PDFsharp draws vectors into its measured reserved areas.
- DejaVu Sans **2.37**, regular and bold, is bundled unmodified as assembly
resources. Source: https://dejavu-fonts.github.io/ ; binary provenance:
Ubuntu `fonts-dejavu-core` **2.37-8**. Both required faces are embedded in PDFs;
neither generation nor viewing needs a platform font installation.
- A custom resolver is installed once before fonts are created. Repeated exports
reuse it. An already-installed foreign resolver is an explicit integration
error, never silently replaced. A host that already uses PDFsharp needs resolver
composition before adding report support. MigraDoc's predefined error font is
pointed at the bundled family; its default (`Courier New`) is a platform font
the bundled resolver deliberately cannot supply.
- The initial text contract is printable ASCII/Latin-1 and line/tab separators.
Other codepoints, controls and soft hyphens fail with the field and codepoint;
there is no silent missing-glyph substitution. Broader Unicode/shaping support
is deferred rather than implied by the font's larger character inventory.
- PDFsharp/MigraDoc are MIT-licensed. The actual v6.2.4 license and the Microsoft
transitive dependency notices are in `OpenNest.Reporting/THIRD-PARTY-NOTICES.txt`.
Font permission/redistribution terms are in `OpenNest.Reporting/Fonts/LICENSE.txt`
(Bitstream Vera permission with DejaVu changes in the public domain). Preserve
both files in `ReportingNotices/` when publishing; embedded fonts do not replace
the obligation to ship their notices. Package notices are deduplicated, not
rewritten or shortened.
## Verification
```sh
dotnet test OpenNest.Tests/OpenNest.Tests.csproj --filter FullyQualifiedName~Reporting
```
The writer tests read page sizes and content streams with PDFsharp. Text
assertions extract with Poppler's `pdftotext -layout` and are skipped, not
passed, when poppler-utils is absent. For manual inspection also use `pdfinfo`,
`pdffonts` and `pdftoppm -png` (Linux verification baseline: poppler-utils
24.02.0). Confirm page sizes, quantity rows, page X of Y, embedded fonts, native
vector curves, unfilled holes and visible tab gaps with no connecting stroke.
Pixel inspection supplements geometry assertions; it is not a CNC-validation
result.
Windows compilation is not runtime acceptance. `OpenNest.WinForms.Tests/Forms/NestReportExportTests.cs`
covers enablement, busy/cancel/failure/success adapter behavior, but it only
compiles on Linux. Windows runtime acceptance still owes: exporting a real nest
(and the packaged application, so bundled fonts/notices are verified), checking
page sizes/labels/copies and embedded fonts with `pdffonts`, a tabbed part with a
hole, opening and printing the PDF, and exercising fill/nesting rejection, cancel,
overwrite and invalid-path behavior with job state and any existing destination
unchanged.