@embedpdf/core-geometry 3.0.0-next.14 → 3.0.0-next.16

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.
package/dist/index.d.cts CHANGED
@@ -1,13 +1,24 @@
1
+ //#region src/page-space.d.ts
2
+ /**
3
+ * Page space: the unrotated page's own frame, origin at the top-left of the
4
+ * visible page, y down, in points (the
5
+ * page's UserUnit is applied by the view, not here). The engine gives and
6
+ * takes every position in this space.
7
+ */
8
+ type PagePoint = PointIn<'page'>;
9
+ type PageRect = RectIn<'page'>;
10
+ type PageQuad = QuadIn<'page'>;
11
+ //#endregion
1
12
  //#region src/index.d.ts
2
13
  /**
3
14
  * @embedpdf/core-geometry — the viewer's pure 2D coordinate primitives.
4
15
  *
5
16
  * The bottom of the pyramid: stage-core (layout), plugin-stage (`pageToWorld`),
6
- * and every framework adapter (`toContentPoint` hit-testing) all sit on these.
17
+ * and every framework adapter (`toPagePoint` hit-testing) all sit on these.
7
18
  * Zero dependencies, DOM-free, serializable — Rust-portable.
8
19
  *
9
20
  * The page-rotation transforms are the reason this package exists: the same
10
- * quarter-turn maps content↔box in BOTH directions, and that one rule must not
21
+ * quarter-turn maps content↔box in both directions, and that one rule must not
11
22
  * be hand-written (and drift, or be bug-fixed in only one of four adapters).
12
23
  */
13
24
  interface Point {
@@ -19,13 +30,9 @@ interface Size {
19
30
  height: number;
20
31
  }
21
32
  /**
22
- * A rectangle in the VIEWER's coordinates: top-left origin, y-down, PDF-point
23
- * scale. What every viewer API speaks — `stage.reveal`, selection and search
24
- * rects, render boxes.
25
- *
26
- * The rule: shown on screen → `Rect`; stored in the file → the engine's y-up
27
- * `PdfRect` (`{left, bottom, right, top}`). The shapes are incompatible on
28
- * purpose — passing one where the other goes doesn't compile.
33
+ * A rectangle in page space: top-left origin, y-down, in points. What every
34
+ * viewer and engine API speaks — annotation rects, `stage.reveal`, selection
35
+ * and search rects, render boxes.
29
36
  */
30
37
  interface Rect {
31
38
  x: number;
@@ -45,10 +52,21 @@ declare function boundsOfRects(rects: readonly Rect[]): Rect | null;
45
52
  * without a null branch. Space-agnostic like {@link boundsOfRects}: view-px
46
53
  * viewport ∩ footprint, page-point visibility ∩ tile footprints, ….
47
54
  */
48
- declare function intersectRects(a: Rect, b: Rect): Rect;
55
+ declare function intersectRects(left: Rect, right: Rect): Rect;
56
+ /** The axis-aligned rect spanned by two corners, in any order — a drag box. */
57
+ declare function rectFromCorners(from: Point, to: Point): Rect;
58
+ /** Whether a rect contains a point, edges inclusive. Space-agnostic. */
59
+ declare function rectContains(rect: Rect, point: Point): boolean;
60
+ /** Positive-area overlap of two rects — touching edges do not count. */
61
+ declare function rectsOverlap(left: Rect, right: Rect): boolean;
62
+ /**
63
+ * The largest rect of `content`'s proportions inside `box`, centred in it:
64
+ * CSS `object-fit: contain`. `box` itself when `content` has no area.
65
+ */
66
+ declare function containedRect(content: Size, box: Rect): Rect;
49
67
  /**
50
68
  * Quarter-turn display rotation, degrees clockwise. The viewer-side notion of a
51
- * page's on-screen rotation — the TOTAL = (document /Rotate + any view
69
+ * page's on-screen rotation — the total = (document /Rotate + any view
52
70
  * rotation), resolved by the shell. Structurally identical to the engine's
53
71
  * `PageRotation`, so the two interoperate without this package depending on the
54
72
  * engine.
@@ -70,118 +88,117 @@ declare const NO_FRAME: PageFrame;
70
88
  /** True for the quarter-turns that swap a page's width and height. */
71
89
  declare function isQuarterTurn(rotation: PageRotation): boolean;
72
90
  /**
73
- * Compose two quarter-turn rotations (mod 360). THE way the shell resolves a
74
- * page's TOTAL display rotation — `addRotations(page /Rotate, view rotation)`
91
+ * Compose two quarter-turn rotations (mod 360). The way the shell resolves a
92
+ * page's total display rotation — `addRotations(page /Rotate, view rotation)`
75
93
  * — so the wrap arithmetic is never hand-written (and drift) per call site.
76
94
  */
77
- declare function addRotations(a: PageRotation, b: PageRotation): PageRotation;
95
+ declare function addRotations(left: PageRotation, right: PageRotation): PageRotation;
78
96
  /**
79
97
  * Snap a view-px value to the device pixel grid: round to a whole device pixel,
80
- * back to view px. Used for a page's screen POSITION so a CSS-rotated page lands
98
+ * back to view px. Used for a page's screen position so a CSS-rotated page lands
81
99
  * on the grid (no sub-pixel anti-aliased fringe). The shell never hand-rounds.
82
100
  */
83
101
  declare function snapToDevice(value: number, dpr: number): number;
84
102
  /** A page's on-screen footprint: width and height swapped for quarter-turns. */
85
103
  declare function displaySize(content: Size, rotation: PageRotation): Size;
86
104
  /**
87
- * The engine's `kind:'width'` height rule, replicated EXACTLY so the viewer can
105
+ * The engine's `kind:'width'` height rule, replicated exactly so the viewer can
88
106
  * predict the rendered bitmap's height up front — the page box is sized to it,
89
107
  * so the box matches the bitmap with no reflow and no 1px seam.
90
108
  *
91
109
  * Mirrors `PageRenderReader.resolveDeviceSize` (engine-services): we render the
92
- * page UN-rotated (CSS applies the rotation), so there is no width/height swap
93
- * here — `pageSize` is the page's own points. MUST stay in lockstep with that
110
+ * page un-rotated (CSS applies the rotation), so there is no width/height swap
111
+ * here — `pageSize` is the page's own points. Must stay in lockstep with that
94
112
  * function; the round-trip test in the render plugin asserts they agree.
95
113
  */
96
114
  declare function deviceHeightForWidth(pageSize: Size, deviceWidth: number): number;
97
115
  /**
98
- * The single per-page bridge between the viewer's three coordinate spaces. NB:
99
- * these are all VIEWER spaces — top-left origin, y-down. They are NOT the
100
- * engine's PDF user space (`Pdf*`, bottom-left origin, y-up); the engine→content
101
- * hop (the y-flip + crop offset) is a separate matrix (`pageGeometry`).
116
+ * The single per-page bridge between the viewer's three coordinate spaces, all
117
+ * top-left origin, y-down. Page space is the engine's own, so engine values
118
+ * need no hop of their own.
102
119
  *
103
- * content space page-local points, top-left, y-down — un-rotated page frame
120
+ * page space page-local points, top-left, y-down — un-rotated page frame
104
121
  * view space platform logical px (web: CSS px) — layout, DOM, pointer events
105
122
  * device space physical pixels — the rendered bitmap
106
123
  *
107
- * PAGE-LOCAL by design: it knows this page's own box, NOT where the page sits in
124
+ * Page-local by design: it knows this page's own box, not where the page sits in
108
125
  * the scene. Placement (camera/layout) stays on `VisiblePage.{x,y}` and positions
109
- * the page container; the transform converts everything INSIDE that container. So
126
+ * the page container; the transform converts everything inside that container. So
110
127
  * it is camera/pan-invariant — it only changes on zoom / rotation / contentScale
111
128
  * / dpr, which makes it cheap to memoize.
112
129
  *
113
- * Every plugin and framework adapter consumes THIS — never `x * scale`, never
130
+ * Every plugin and framework adapter consumes this — never `x * scale`, never
114
131
  * `pageToWorld()∘toScreen()`, never `* dpr`. New plugins (annotations, search,
115
132
  * forms) do no coordinate math; new platforms (iOS/Android) inject a different
116
133
  * `scale` and reuse all of it.
117
134
  */
118
135
  interface PageTransform {
119
136
  /**
120
- * The page's TOTAL display rotation (document /Rotate + any view rotation) —
137
+ * The page's total display rotation (document /Rotate + any view rotation) —
121
138
  * the same value the transform's matrices were built from, exposed so
122
139
  * consumers that need the quarter-turn itself (an upright-placement rule, a
123
- * pointer sample) read it HERE instead of re-deriving it from the matrix.
140
+ * pointer sample) read it here instead of re-deriving it from the matrix.
124
141
  */
125
142
  readonly rotation: PageRotation;
126
- /** Display footprint in VIEW px (device-snapped) — the page container's size.
143
+ /** Display footprint in view px (device-snapped) — the page container's size.
127
144
  * Width↔height already swapped for quarter-turns. */
128
145
  readonly viewWidth: number;
129
146
  readonly viewHeight: number;
130
- /** The UN-rotated content box in VIEW px — the size of the wrapper the bitmap
131
- * and content-space overlays live in (before the wrapper's CSS rotation). */
147
+ /** The un-rotated content box in view px — the size of the wrapper the bitmap
148
+ * and page-space overlays live in (before the wrapper's CSS rotation). */
132
149
  readonly contentWidth: number;
133
150
  readonly contentHeight: number;
134
- /** Render the (un-rotated) bitmap at EXACTLY this — `viewport: {kind:'width', width: deviceWidth}`.
151
+ /** Render the (un-rotated) bitmap at exactly this — `viewport: {kind:'width', width: deviceWidth}`.
135
152
  * Integer device px; `deviceHeight` is the engine's exact derived height. */
136
153
  readonly deviceWidth: number;
137
154
  readonly deviceHeight: number;
138
155
  /** Device px per PDF point (uniform). */
139
156
  readonly renderScale: number;
140
- /** VIEW px per PDF point (uniform, device-snapped) — the one factor for
157
+ /** View px per PDF point (uniform, device-snapped) — the one factor for
141
158
  * screen-constant chrome: content units = CSS px ÷ `viewScale`. */
142
159
  readonly viewScale: number;
143
160
  /**
144
- * View px per PDF point at 100% ZOOM — the page's physical baseline:
161
+ * View px per PDF point at 100% zoom — the page's physical baseline:
145
162
  * `viewUnitsPerPoint × userUnit` (web: 96/72 × `/UserUnit`, so 100% maps to
146
- * inches on screen, Acrobat-style). What "100%" MEANS for this page.
163
+ * inches on screen, Acrobat-style). What "100%" means for this page.
147
164
  */
148
165
  readonly baseScale: number;
149
166
  /**
150
- * The page's zoom RELATIVE to its 100% baseline (`viewScale / baseScale`,
151
- * dimensionless; exactly 1 at 100% for any `userUnit`). THE number
167
+ * The page's zoom relative to its 100% baseline (`viewScale / baseScale`,
168
+ * dimensionless; exactly 1 at 100% for any `userUnit`). The number
152
169
  * zoom-relative policies consume — e.g. the `/F` NoZoom exemption clamps
153
170
  * its body to `zoom ≤ 1`. Distinct from `viewScale` (a units conversion).
154
171
  */
155
172
  readonly zoom: number;
156
173
  /**
157
174
  * Content point → this page's pixels (the un-rotated content wrapper's local
158
- * px). THE overlay author's converter: elements inside the wrapper ride its
159
- * rotation, so they place in content points and only scale here. Convert at
175
+ * px). The overlay author's converter: elements inside the wrapper ride its
176
+ * rotation, so they place in page points and only scale here. Convert at
160
177
  * the last moment — pixels are never stored.
161
178
  */
162
- toPixels(p: Point): Point;
163
- /** Inverse of {@link toPixels}: wrapper-local px → content point (a pointer
179
+ toPixels(point: Point): Point;
180
+ /** Inverse of {@link toPixels}: wrapper-local px → page point (a pointer
164
181
  * event inside the wrapper back to the viewer's coordinates). */
165
- fromPixels(p: Point): Point;
166
- /** Content point → page-local view px in the DISPLAY box (rotation applied). For
182
+ fromPixels(point: Point): Point;
183
+ /** Content point → page-local view px in the display box (rotation applied). For
167
184
  * footprint-space layers (outside the wrapper). */
168
- contentToView(p: Point): Point;
185
+ pageToView(point: Point): Point;
169
186
  /** Content rect → its axis-aligned view-px rect (exact for quarter-turns). */
170
- contentToViewRect(r: Rect): Rect;
171
- /** Inverse — display-box view px (box-local) → content point. Hit-testing. */
172
- viewToContent(p: Point): Point;
173
- /** Inverse of {@link contentToViewRect}: display-box view-px rect → its content
187
+ pageToViewRect(rect: Rect): Rect;
188
+ /** Inverse — display-box view px (box-local) → page point. Hit-testing. */
189
+ viewToPage(point: Point): Point;
190
+ /** Inverse of {@link pageToViewRect}: display-box view-px rect → its content
174
191
  * AABB (exact for quarter-turns). The visibility primitive — projecting a
175
- * viewport rect into page points is THIS, never per-adapter corner math. */
176
- viewToContentRect(r: Rect): Rect;
177
- /** CSS `matrix()` mapping content space → this page's display box. Drop it on
192
+ * viewport rect into page points is this, never per-adapter corner math. */
193
+ viewToPageRect(rect: Rect): Rect;
194
+ /** CSS `matrix()` mapping page space → this page's display box. Drop it on
178
195
  * a footprint-space layer (`transform`, `transform-origin: 0 0`) and place
179
- * children in content points — rotation + scale handled for free. */
196
+ * children in page points — rotation + scale handled for free. */
180
197
  readonly cssMatrix: string;
181
198
  }
182
199
  /**
183
200
  * Build a page's transform from its intrinsic size + the platform/view scale.
184
- * `scale` is VIEW px per PDF point (the shell composes it: `viewUnitsPerPoint ×
201
+ * `scale` is view px per PDF point (the shell composes it: `viewUnitsPerPoint ×
185
202
  * contentScale × zoom`); `dpr` is device px per view px. Device dimensions snap
186
203
  * to the grid (crisp, no rotation fringe) and follow the engine's width rule
187
204
  * (box == bitmap, no reflow).
@@ -203,15 +220,22 @@ declare function pageTransform(input: {
203
220
  dpr: number;
204
221
  }): PageTransform;
205
222
  /**
206
- * The coordinate spaces a viewer touches. `pdf` is the engine's PDF user space
207
- * (y-up, origin at the crop box bottom-left); the rest are viewer-local, y-down.
223
+ * Whether two transforms place things on the page the same: the same scale, zoom and turn, and
224
+ * the same content box. A Stage builds a new transform on every frame, also for a plain scroll,
225
+ * which moves the page but changes nothing inside it; a layer that compares with this redraws
226
+ * only when what it draws in the page's pixels moved.
208
227
  */
209
- type Space = 'pdf' | 'content' | 'view' | 'screen';
228
+ declare function samePagePlacement(left: Pick<PageTransform, 'viewScale' | 'zoom' | 'rotation' | 'contentWidth' | 'contentHeight'>, right: Pick<PageTransform, 'viewScale' | 'zoom' | 'rotation' | 'contentWidth' | 'contentHeight'>): boolean;
210
229
  /**
211
- * Phantom space brand. It is OPTIONAL so plain `{ x, y }` / `{ x, y, w, h }`
212
- * literals stay assignable day-to-day (terse), but a value EXPLICITLY tagged
230
+ * The coordinate spaces a viewer touches, all y-down. `content` is the
231
+ * engine's page space: points from the top-left of the visible page.
232
+ */
233
+ type Space = 'page' | 'view' | 'screen';
234
+ /**
235
+ * Phantom space brand. It is optional so plain `{ x, y }` / `{ x, y, w, h }`
236
+ * literals stay assignable day-to-day (terse), but a value explicitly tagged
213
237
  * with the wrong space is rejected — and `Mat2D<From, To>` enforces the hop at
214
- * the matrix, so you cannot `applyRect` a `pdf` rect with a `content→view`
238
+ * the matrix, so you cannot `applyRect` a `view` rect with a `content→view`
215
239
  * matrix. The brand is type-only; it carries no runtime field.
216
240
  */
217
241
  interface SpaceBrand<S extends Space> {
@@ -222,23 +246,29 @@ type PointIn<S extends Space> = Point & SpaceBrand<S>;
222
246
  /**
223
247
  * An axis-aligned rect tagged with its space, as min-corner + positive extent:
224
248
  * `(x, y)` is the corner with the smallest coordinates and `(x+width, y+height)`
225
- * the largest. In `pdf` space (y-up) that corner is bottom-left (`x=left`,
226
- * `y=bottom`); in the y-down viewer spaces it is top-left.
249
+ * the largest: in every space here, the top-left.
227
250
  */
228
251
  type RectIn<S extends Space> = Rect & SpaceBrand<S>;
229
252
  /**
230
- * Four POSITIONAL points (no corner semantics) — the viewer twin of the engine's
231
- * `PdfQuad`. Use this (via {@link applyQuad}) for rotatable/skewed content where
232
- * an axis-aligned rect would lie.
253
+ * A quad: four corners named in the text's own upright frame, the viewer twin
254
+ * of the engine's `PageQuad`. `upper` is the ascent side and `lower` the
255
+ * baseline side; `left` to `right` runs along the frame's x axis. The names
256
+ * are neither screen directions nor reading order: text turned 180° has its
257
+ * `upperLeft` at the bottom right, and right-to-left text still runs from
258
+ * `left` to `right`. In y-down page space an upright quad has `upper*` at the
259
+ * smaller y. Use it (via {@link applyQuad}) for text and any content that can
260
+ * turn or skew, where an axis-aligned rect would lie.
233
261
  */
234
262
  interface Quad {
235
- p1: Point;
236
- p2: Point;
237
- p3: Point;
238
- p4: Point;
263
+ upperLeft: Point;
264
+ upperRight: Point;
265
+ lowerLeft: Point;
266
+ lowerRight: Point;
239
267
  }
240
268
  /** A quad tagged with its coordinate space. */
241
269
  type QuadIn<S extends Space> = Quad & SpaceBrand<S>;
270
+ /** Four points as a polygon ring, for `<polygon>`, fills and hit tests. */
271
+ type QuadRing = [Point, Point, Point, Point];
242
272
  /**
243
273
  * A 2D affine transform from space `From` to space `To`, stored as the six
244
274
  * numbers `[a, b, c, d, e, f]`. The phantom `From`/`To` make the compiler track
@@ -252,154 +282,75 @@ type Mat2D<From extends Space = Space, To extends Space = Space> = readonly [num
252
282
  /** The identity transform within a single space. */
253
283
  declare function identity<S extends Space>(): Mat2D<S, S>;
254
284
  /**
255
- * Compose two matrices: `compose(m, n)` applies `n` first, then `m` (matrix
256
- * product `m · n`). The shared middle space `B` must line up, so the type system
257
- * rejects composing mismatched hops.
285
+ * Compose two matrices: `compose(matrix, inner)` applies `inner` first, then
286
+ * `matrix` (matrix product `matrix · inner`). The shared middle space `B` must
287
+ * line up, so the type system rejects composing mismatched hops.
258
288
  */
259
- declare function compose<A extends Space, B extends Space, C extends Space>(m: Mat2D<B, C>, n: Mat2D<A, B>): Mat2D<A, C>;
260
- /** Invert a transform — `invert(pdfToView)` is `viewToPdf`, i.e. hit-testing for free. */
261
- declare function invert<F extends Space, T extends Space>(m: Mat2D<F, T>): Mat2D<T, F>;
289
+ declare function compose<A extends Space, B extends Space, C extends Space>(matrix: Mat2D<B, C>, inner: Mat2D<A, B>): Mat2D<A, C>;
290
+ /** Invert a transform — `invert(pageToView)` is `viewToPage`, i.e. hit-testing for free. */
291
+ declare function invert<F extends Space, T extends Space>(matrix: Mat2D<F, T>): Mat2D<T, F>;
262
292
  /** Apply a transform to a point. */
263
- declare function applyPoint<F extends Space, T extends Space>(m: Mat2D<F, T>, p: PointIn<F>): PointIn<T>;
293
+ declare function applyPoint<F extends Space, T extends Space>(matrix: Mat2D<F, T>, point: PointIn<F>): PointIn<T>;
264
294
  /**
265
295
  * Apply a transform to a rect, returning the axis-aligned bounding box of the
266
- * four transformed corners. EXACT for translate/scale/quarter-turn (every page
296
+ * four transformed corners. Exact for translate/scale/quarter-turn (every page
267
297
  * transform `pageGeometry` produces); lossy under skew — use {@link applyQuad}
268
298
  * for content that can be rotated off-axis.
269
299
  */
270
- declare function applyRect<F extends Space, T extends Space>(m: Mat2D<F, T>, r: RectIn<F>): RectIn<T>;
271
- /** Apply a transform to a quad, preserving orientation (maps the four points). */
272
- declare function applyQuad<F extends Space, T extends Space>(m: Mat2D<F, T>, q: QuadIn<F>): QuadIn<T>;
300
+ declare function applyRect<F extends Space, T extends Space>(matrix: Mat2D<F, T>, rect: RectIn<F>): RectIn<T>;
301
+ /** Apply a transform to a quad: each corner moves and keeps its name. */
302
+ declare function applyQuad<F extends Space, T extends Space>(matrix: Mat2D<F, T>, quad: QuadIn<F>): QuadIn<T>;
273
303
  /**
274
- * A `Mat2D` IS a CSS `matrix()` — the six numbers are identical. Drop the result
304
+ * A `Mat2D` is a CSS `matrix()` — the six numbers are identical. Drop the result
275
305
  * on a footprint-space layer (`transform`, `transform-origin: 0 0`) and place
276
306
  * children in the source space; the rotation + scale are handled for free.
277
307
  */
278
- declare function matrixToCss(m: Mat2D): string;
308
+ declare function matrixToCss(matrix: Mat2D): string;
279
309
  /** Translate within a space. */
280
310
  declare function translate<S extends Space>(tx: number, ty: number): Mat2D<S, S>;
281
311
  /** Scale about the space origin. */
282
312
  declare function scale<S extends Space>(sx: number, sy: number): Mat2D<S, S>;
283
- /** Rotate about the space origin (radians; clockwise in y-down content space). */
313
+ /** Rotate about the space origin (radians; clockwise in y-down page space). */
284
314
  declare function rotate<S extends Space>(rad: number): Mat2D<S, S>;
285
315
  /** Rotate about an arbitrary pivot: `T(c) · R(rad) · T(-c)`. */
286
- declare function rotateAbout<S extends Space>(c: PointIn<S>, rad: number): Mat2D<S, S>;
316
+ declare function rotateAbout<S extends Space>(point: PointIn<S>, rad: number): Mat2D<S, S>;
287
317
  /** Scale about an arbitrary anchor: `T(a) · S(sx, sy) · T(-a)`. */
288
- declare function scaleAbout<S extends Space>(a: PointIn<S>, sx: number, sy: number): Mat2D<S, S>;
318
+ declare function scaleAbout<S extends Space>(point: PointIn<S>, sx: number, sy: number): Mat2D<S, S>;
289
319
  /** The rotation angle (radians) encoded in a matrix — `atan2(b, a)`. Positive is
290
- * clockwise in y-down content space (the inverse of {@link rotate}). */
291
- declare function angleOf(m: Mat2D): number;
320
+ * clockwise in y-down page space (the inverse of {@link rotate}). */
321
+ declare function angleOf(matrix: Mat2D): number;
292
322
  /**
293
- * The page's scale + integer quarter-turn as a `Mat2D`: a content point maps to
294
- * its place in the rotated DISPLAY box. `scale` is output units per content
295
- * point; `boxW`/`boxH` are the UN-rotated content's extents in those same output
323
+ * The page's scale + integer quarter-turn as a `Mat2D`: a page point maps to
324
+ * its place in the rotated display box. `scale` is output units per content
325
+ * point; `boxW`/`boxH` are the un-rotated content's extents in those same output
296
326
  * units, and the offsets keep the footprint in the box's positive quadrant
297
327
  * (origin at the box top-left).
298
328
  *
299
- * THE single encoding of the quarter-turn — `pageTransform` (view px),
329
+ * The single encoding of the quarter-turn — `pageTransform` (view px),
300
330
  * `pageGeometry` (the `content→view` hop), and `pageToWorld` (world units) all
301
331
  * build on this one function, so the rotation can never drift between them.
302
332
  */
303
333
  declare function rotateScaleMatrix(scale: number, boxW: number, boxH: number, rotation: PageRotation): Mat2D;
334
+ /** An upright quad over a rect (y-down: upper = smaller y). */
335
+ declare function quadFromRect(rect: Rect): Quad;
336
+ /** The four corners: upper-left, upper-right, lower-left, lower-right. */
337
+ declare function quadCorners(quad: Quad): [Point, Point, Point, Point];
338
+ /** The corners as a polygon ring: upper-left, upper-right, lower-right, lower-left. */
339
+ declare function quadRing(quad: Quad): QuadRing;
304
340
  /**
305
- * The per-page geometry input. Structurally a subset of the engine's
306
- * `PageLayout` (so a layout can be passed straight in), but defined here to keep
307
- * this package dependency-free — the same reason {@link PageRotation} is a
308
- * structural twin of the engine's rotation.
309
- */
310
- interface PageGeometryInput {
311
- /** Effective crop box in PDF user space (y-up edges); origin preserved, so
312
- * `left`/`bottom` may be non-zero or negative. */
313
- crop: {
314
- left: number;
315
- bottom: number;
316
- right: number;
317
- top: number;
318
- };
319
- /** Persistent page rotation (the document's `/Rotate`). */
320
- rotation: PageRotation;
321
- /** PDF `userUnit` (§14.11.6): scales PDF points into the layout. 1 for most docs. */
322
- userUnit: number;
323
- }
324
- /** The matrices bridging one page's spaces — every other matrix composes from these. */
325
- interface PageGeometry {
326
- /** PDF user space (y-up, crop-relative) → content space (y-down, crop top-left, unscaled points). */
327
- readonly pdfToContent: Mat2D<'pdf', 'content'>;
328
- /** Content → view: scale (zoom × userUnit) + the page's quarter-turn rotation. */
329
- readonly contentToView: Mat2D<'content', 'view'>;
330
- /** PDF → view, precomposed. */
331
- readonly pdfToView: Mat2D<'pdf', 'view'>;
332
- /** View → PDF, the inverse — hit-testing for free. */
333
- readonly viewToPdf: Mat2D<'view', 'pdf'>;
334
- }
335
- /**
336
- * PDF user space (y-up, origin = crop bottom-left) → content space (y-down,
337
- * origin = crop top-left, unscaled points). THE single encoding of the engine→
338
- * content y-flip + crop offset: `pageGeometry` composes its `pdfToContent` from
339
- * this, and pure annotation geometry (content-space editing) reuses it too, so
340
- * the rule can never be hand-written twice and drift.
341
- */
342
- declare function pdfToContentMatrix(crop: {
343
- left: number;
344
- top: number;
345
- }): Mat2D<'pdf', 'content'>;
346
- /**
347
- * Build a page's space matrices from its crop box, rotation, and `userUnit` plus
348
- * the viewer `zoom`. This is the ONE function that knows the y-flip, crop origin,
349
- * `userUnit`, and rotation; everything else composes from its output. The engine
350
- * never emits these — they bake in viewer state (zoom, later scroll/dpr) — so the
351
- * boundary stays clean: engine owns `Pdf*` truth, the viewer composes the rest.
352
- */
353
- declare function pageGeometry(input: PageGeometryInput, zoom: number): PageGeometry;
354
- interface TextQuad {
355
- upperStart: Point;
356
- upperEnd: Point;
357
- lowerStart: Point;
358
- lowerEnd: Point;
359
- }
360
- /** A TextQuad tagged with its coordinate space. */
361
- type TextQuadIn<S extends Space> = TextQuad & SpaceBrand<S>;
362
- /** Axis-aligned TextQuad over a rect (y-down: upper = smaller y). */
363
- declare function textQuadFromRect(r: Rect): TextQuad;
364
- /** The four corners in slot order: upper-start, upper-end, lower-start, lower-end. */
365
- declare function textQuadPoints(t: TextQuad): [Point, Point, Point, Point];
366
- /** The corners as a polygon RING (US → UE → LE → LS) — for `<polygon>`/fills. */
367
- declare function textQuadRing(t: TextQuad): [Point, Point, Point, Point];
368
- /**
369
- * One SIDE edge of the cell — the start (`US → LS`) or end (`UE → LE`) edge,
370
- * ASCENT CORNER FIRST. This is the segment a caret or a selection handle
341
+ * One side edge of the cell, the left (`UL → LL`) or right (`UR → LR`) one,
342
+ * ascent corner first. This is the segment a caret or a selection handle
371
343
  * occupies: its length is the glyph's ink height in the text's own frame (not
372
344
  * the AABB height, which grows with tilt), and its direction carries the
373
- * text's rotation. Which side is the selection's LEADING edge is a reading
345
+ * text's rotation. Which side is the selection's leading edge is a reading
374
346
  * -order question — decide it with `advance`, not with geometry.
375
347
  */
376
- declare function textQuadEdge(t: TextQuad, side: 'start' | 'end'): [Point, Point];
377
- /** Corner-wise equality — an AABB comparison would call a quad that ROTATED
348
+ declare function quadEdge(quad: Quad, side: 'left' | 'right'): [Point, Point];
349
+ /** Corner-wise equality — an AABB comparison would call a quad that rotated
378
350
  * in place "unchanged"; corners cannot. (Change-detection for quad consumers.) */
379
- declare function textQuadEquals(a: TextQuad, b: TextQuad): boolean;
380
- /** Axis-aligned bounds of a TextQuad. */
381
- declare function textQuadBounds(t: TextQuad): Rect;
382
- /** Map a TextQuad through an affine transform (corner semantics ride along). */
383
- declare function applyTextQuad<F extends Space, T extends Space>(m: Mat2D<F, T>, t: TextQuadIn<F>): TextQuadIn<T>;
384
- /** Serialize to the positional zigzag convention (`p1..p4` = US, UE, LS, LE). */
385
- declare function positionalQuad(t: TextQuad): Quad;
386
- /**
387
- * Interpret an arbitrary positional quad (an imported `/QuadPoints` entry,
388
- * already in content space) as a TextQuad — a NORMALIZER, not a cast:
389
- *
390
- * 1. the de-facto zigzag order (US, UE, LS, LE) passes through untouched —
391
- * this covers every well-formed writer, rotated quads included;
392
- * 2. the common ring order (US, UE, LE, LS) is recognized and repaired;
393
- * 3. anything else gets a deterministic geometric labeling: corners sorted
394
- * into a ring around their centroid, "upper" = the edge whose midpoint
395
- * sits highest on screen (smallest y). The 180° "which side is up"
396
- * ambiguity is unresolvable from geometry alone — convention decides.
397
- *
398
- * Drawing code downstream (`markupScene`) reads corner SEMANTICS, so a
399
- * mislabeled import can shift an underline to the wrong edge but can never
400
- * crash or self-intersect.
401
- */
402
- declare function normalizeQuad(q: Quad): TextQuad;
351
+ declare function quadEquals(left: Quad, right: Quad): boolean;
352
+ /** Axis-aligned bounds of a quad. */
353
+ declare function quadBounds(quad: Quad): Rect;
403
354
  //#endregion
404
- export { Mat2D, NO_FRAME, PageFrame, PageGeometry, PageGeometryInput, PageRotation, PageTransform, Point, PointIn, Quad, QuadIn, Rect, RectIn, Size, Space, TextQuad, TextQuadIn, addRotations, angleOf, applyPoint, applyQuad, applyRect, applyTextQuad, boundsOfRects, compose, deviceHeightForWidth, displaySize, identity, intersectRects, invert, isQuarterTurn, matrixToCss, normalizeQuad, pageGeometry, pageTransform, pdfToContentMatrix, positionalQuad, rotate, rotateAbout, rotateScaleMatrix, scale, scaleAbout, snapToDevice, textQuadBounds, textQuadEdge, textQuadEquals, textQuadFromRect, textQuadPoints, textQuadRing, translate };
355
+ export { Mat2D, NO_FRAME, PageFrame, PagePoint, PageQuad, PageRect, PageRotation, PageTransform, Point, PointIn, Quad, QuadIn, QuadRing, Rect, RectIn, Size, Space, addRotations, angleOf, applyPoint, applyQuad, applyRect, boundsOfRects, compose, containedRect, deviceHeightForWidth, displaySize, identity, intersectRects, invert, isQuarterTurn, matrixToCss, pageTransform, quadBounds, quadCorners, quadEdge, quadEquals, quadFromRect, quadRing, rectContains, rectFromCorners, rectsOverlap, rotate, rotateAbout, rotateScaleMatrix, samePagePlacement, scale, scaleAbout, snapToDevice, translate };
405
356
  //# sourceMappingURL=index.d.cts.map