Skip to content

CAD → PDF: one page per paper-space layout, through its viewports (#172) - #471

Merged
KnifeLemon merged 7 commits into
KnifeLemon:mainfrom
cre8ive-marketing:feature/cad-layout-pages
Oct 23, 2026
Merged

KnifeLemon merged 7 commits into
KnifeLemon:mainfrom
cre8ive-marketing:feature/cad-layout-pages

Conversation

@cre8ive-marketing

Copy link
Copy Markdown
Collaborator

What does this change?

#172, the first step you described there: CAD → PDF with one page per paper-space layout, following each layout's viewports. Detecting title-block frames in model space isn't part of it.

Before: a DWG/DXF became one PDF page of model space. A drawing with an empty model space got its active layout instead, and viewports weren't followed.

Now (PDF only):

  • A drawing whose layouts have something on them becomes a PDF with one page per layout, in tab order. The empty default layout is skipped, so drawings without layouts are unchanged.
  • Each page holds the layout's own drawing (title block, notes) and, inside each viewport, model space at the viewport's scale (height ÷ view height) around its view centre, clipped to the viewport frame.
  • Each page is fitted to the layout's drawing and viewport frames, like the one-page PDF (A3 / A4 / Letter from the preset).

How:

  • CadSceneBuilder.BuildLayouts builds a LayoutSheet per layout: its paper-space scene plus its ViewportViews (frame, view centre, scale). It skips the viewport that is the sheet itself (RepresentsPaper).
  • CadRenderer.WritePdf(sheets, model, …) draws model space once per viewport through PageLayout.Through (same page, the viewport's scale and centre) inside a ClipRect, then draws the layout on top.
  • Model space is built once and reused for every viewport.

Unchanged: SVG and images stay one picture of model space. DWG ↔ DXF is untouched.

Not followed yet (written in docs/ENGINES.md):

  • twisted views (drawn untwisted);
  • layers frozen per viewport;
  • non-rectangular (clipped) viewports;
  • the layout's plot settings: paper size and plot scale. Pages are fitted like before.

No settings change, no new strings, no new dependency. Only ACadSharp and SkiaSharp, which were already used.

How was it tested?

  • dotnet format --verify-no-changes
  • dotnet test with FFmpeg 9.0.2 and rhwp 0.8.7: Platform 48, Core 501, Cli 27, Setup 149, Engines 1596, App 847 pass, none fail.
  • New CadLayoutTests, with a drawing built in the test: a square and, far away, a circle in model space; two layouts, each with a border and one viewport ("Plan" at 1:1 on the square, "Detail" at 2:1 on the circle).
    • Sheets come out in tab order with the right frame and scale (2.0 for 2:1). CadBuilder.Sample(), which has only the default layout, gets no sheets.
    • Through the job queue: the PDF has 2 pages. On both, PDFium finds model-space lines inside the viewport and none outside the frame.
    • PNG output is still one picture.

Screenshots

Page 1 (Plan, 1:1) and page 2 (Detail, 2:1) of the test drawing's PDF:

Page 1 Page 2

Checklist

  • New UI strings are in all src/Filee.App/Assets/i18n/*.json files (none added)
  • New third-party code or binaries are listed in THIRD-PARTY-NOTICES.md (none)

Addresses #172 (one page per layout). Frame detection in model space can stay open there.

…ifeLemon#172)

A drawing with layouts becomes a PDF with one page per layout that has something on it, in tab order: model space is drawn through each viewport at its scale, clipped to the frame, under the layout's own drawing. Images and SVG stay one picture of model space; drawings without layouts are unchanged.

@KnifeLemon KnifeLemon left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thanks @cre8ive-marketing, this is the first step from #172 and it reads well: one page per layout in tab order, the sheet's own viewport skipped, model space built once and clipped to each viewport, images and SVG unchanged, and the limits written in ENGINES.md. Two things before it goes in:

  1. The view centre is in display coordinates. Viewport.ViewCenter is DCS (DXF 12/22), and DCS has its origin at the view target (ViewTarget, DXF 17/27/37, in WCS). For a view from the top, the model point in the middle of the viewport is ViewTarget + ViewCenter. Most drawings have the target at 0,0,0, which is why the test passes, but a drawing with another target shows the wrong part of model space. Please add the target, and a test with a non-zero one.
  2. Drawings made only in model space. AutoCAD gives a layout a viewport the first time its tab is opened, so many model-space-only drawings have a Layout1 that holds just that viewport, with the view it had back then. Those used to give one page of the whole model space; now they give the Layout1 page, which shows only part of the drawing if it grew afterwards. Could you keep the single model-space page when no layout has anything of its own besides viewports (no border, title block or notes), with a test for it? A layout with a title block keeps the new behaviour.

Smaller:

  • A viewport that's switched off (ViewportStatusFlags.ViewportOff) shows nothing in AutoCAD and could be skipped.
  • A viewport looking from another direction (ViewDirection not 0,0,1) is drawn from the top; worth adding to the "Not followed" list in ENGINES.md.

Thanks!

…re off

Review of KnifeLemon#471:
- The model point at a viewport's centre is ViewTarget + ViewCenter
  (DCS has its origin at the target), with a test using a non-zero one.
- When no layout has anything of its own besides viewports, the PDF
  stays one page of all of model space, as before: AutoCAD's Layout1
  with only its first viewport no longer cuts the drawing.
- Viewports that are off are skipped.
- ENGINES.md: views from another direction are drawn from the top.
@cre8ive-marketing

Copy link
Copy Markdown
Collaborator Author

Thanks @KnifeLemon, all four are done in the latest commit:

  1. View target: the model point at a viewport's centre is now ViewTarget + ViewCenter. The "Detail" sheet in the test now uses a target of (900, 0) with the centre 100 to its right, and the test checks that it lands on (1000, 0).
  2. Drawings made only in model space: when no layout has anything of its own besides viewports, the PDF stays one page of the whole of model space. A layout with a border or title block keeps one page per layout. New test: a Layout1 with only a viewport gives a single page.
  3. Viewports that are switched off (ViewportOff) are skipped. There's a test for it.
  4. ENGINES.md: the "Not followed" list now has views from another direction (drawn from the top). It also notes that viewports that are off are skipped, and the rule for drawings made only in model space.

All 39 CAD tests pass locally.

KnifeLemon and others added 2 commits October 23, 2026 19:31
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When model space has nothing visible, the one-picture scene falls back to
the active layout's paper space. The per-layout PDF passed that scene as
model space, so the layout's own border and notes were drawn a second time
inside every viewport. Use an empty model scene in that case, with a test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cre8ive-marketing

Copy link
Copy Markdown
Collaborator Author

The linux-arm64 portable job failed twice on 80ea676, in PresetAvailabilityTests (Saving_a_slice… the first time, Saving_a_chip… on the rerun). Both times OpenMain found a main window an earlier test had left open. None of the CAD code is involved.

I merged current main (e6f9bdb), which brings in #492's single-main-window fix, in case that's what was racing. Locally, PresetAvailabilityTests, ToolbarPalette (44) and the CAD tests (40) pass on the merge. If it shows up again on arm64, it's probably the toolbar tests from #463 running slowly there, not this PR.

@cre8ive-marketing

Copy link
Copy Markdown
Collaborator Author

Update: the build job on e6f9bdb failed in ToolbarPaletteViewTests.Unusable_blocks_add_but_allows_edit, with the same "main window left open" assert. main fails the same way, and #498 fixes it, so I'll merge main again once #498 is in rather than touch those tests here.

@KnifeLemon
KnifeLemon merged commit 4746365 into KnifeLemon:main Oct 23, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New features

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants