@reekon-tools/boldr-utils 1.21.1 → 1.22.0
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/annotation/canvas/AnnotationCanvasSkia.js +3 -11
- package/dist/annotation/canvas/measurementLineStyle.d.ts +3 -0
- package/dist/annotation/canvas/measurementLineStyle.js +14 -0
- package/dist/export/pdf/color.d.ts +7 -0
- package/dist/export/pdf/color.js +39 -0
- package/dist/export/pdf/emit.d.ts +37 -0
- package/dist/export/pdf/emit.js +129 -0
- package/dist/export/pdf/fonts.d.ts +38 -0
- package/dist/export/pdf/fonts.js +97 -0
- package/dist/export/pdf/index.d.ts +5 -0
- package/dist/export/pdf/index.js +5 -0
- package/dist/export/pdf/layers.d.ts +5 -0
- package/dist/export/pdf/layers.js +19 -0
- package/dist/export/pdf/marks.d.ts +20 -0
- package/dist/export/pdf/marks.js +336 -0
- package/dist/export/pdf/pageMapping.d.ts +49 -0
- package/dist/export/pdf/pageMapping.js +67 -0
- package/dist/export/pdf/renderCanvasToPdf.d.ts +2 -0
- package/dist/export/pdf/renderCanvasToPdf.js +201 -0
- package/dist/export/pdf/tileContent.d.ts +39 -0
- package/dist/export/pdf/tileContent.js +218 -0
- package/dist/export/pdf/tiles.d.ts +43 -0
- package/dist/export/pdf/tiles.js +392 -0
- package/dist/export/pdf/types.d.ts +101 -0
- package/dist/export/pdf/types.js +10 -0
- package/dist/exports.d.ts +1 -1
- package/dist/exports.js +1 -1
- package/dist/formulas/calculateFormula.d.ts +1 -1
- package/dist/formulas/calculateFormula.js +23 -10
- package/dist/formulas/evaluateFormula.d.ts +0 -10
- package/dist/formulas/evaluateFormula.js +1 -12
- package/dist/formulas/groupFormula.d.ts +43 -0
- package/dist/formulas/groupFormula.js +168 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/server.d.ts +4 -1
- package/dist/server.js +6 -1
- package/package.json +4 -1
- package/dist/canvas/AnnotationCanvas.d.ts +0 -11
- package/dist/canvas/AnnotationCanvas.js +0 -10
- package/dist/canvas/AnnotationCanvas.native.d.ts +0 -8
- package/dist/canvas/AnnotationCanvas.native.js +0 -6
- package/dist/canvas/AnnotationCanvasInner.d.ts +0 -39
- package/dist/canvas/AnnotationCanvasInner.js +0 -219
- package/dist/canvas/AnnotationCanvasInner.native.d.ts +0 -35
- package/dist/canvas/AnnotationCanvasInner.native.js +0 -138
- package/dist/canvas/AnnotationCanvasSkia.d.ts +0 -27
- package/dist/canvas/AnnotationCanvasSkia.js +0 -20
- package/dist/canvas/Tool.d.ts +0 -38
- package/dist/canvas/Tool.js +0 -1
- package/dist/canvas/elements/BackgroundImageElement.d.ts +0 -9
- package/dist/canvas/elements/BackgroundImageElement.js +0 -37
- package/dist/canvas/elements/MeasurementStampElement.d.ts +0 -13
- package/dist/canvas/elements/MeasurementStampElement.js +0 -30
- package/dist/canvas/elements/ShapeElement.d.ts +0 -7
- package/dist/canvas/elements/ShapeElement.js +0 -62
- package/dist/canvas/elements/StrokeElement.d.ts +0 -7
- package/dist/canvas/elements/StrokeElement.js +0 -18
- package/dist/canvas/measurementPicker.d.ts +0 -10
- package/dist/canvas/measurementPicker.js +0 -1
- package/dist/canvas/measurementStampOverlay.d.ts +0 -11
- package/dist/canvas/measurementStampOverlay.js +0 -1
- package/dist/canvas/pointerAdapter.d.ts +0 -3
- package/dist/canvas/pointerAdapter.js +0 -19
- package/dist/canvas/stampLayout.d.ts +0 -5
- package/dist/canvas/stampLayout.js +0 -14
- package/dist/canvas/tools/measurementStampTool.d.ts +0 -9
- package/dist/canvas/tools/measurementStampTool.js +0 -37
- package/dist/canvas/tools/panTool.d.ts +0 -5
- package/dist/canvas/tools/panTool.js +0 -25
- package/dist/canvas/tools/penTool.d.ts +0 -13
- package/dist/canvas/tools/penTool.js +0 -68
- package/dist/canvas/tools/selectTool.d.ts +0 -2
- package/dist/canvas/tools/selectTool.js +0 -182
- package/dist/canvas/useAnnotationCanvasState.d.ts +0 -54
- package/dist/canvas/useAnnotationCanvasState.js +0 -210
- package/dist/canvas/viewport.d.ts +0 -16
- package/dist/canvas/viewport.js +0 -54
- package/dist/data/AnnotationDataContext.d.ts +0 -8
- package/dist/data/AnnotationDataContext.js +0 -11
- package/dist/data/AnnotationDataProvider.d.ts +0 -65
- package/dist/data/AnnotationDataProvider.js +0 -4
- package/dist/data/InMemoryAnnotationProvider.d.ts +0 -30
- package/dist/data/InMemoryAnnotationProvider.js +0 -197
- package/dist/data/canvasPersistence.d.ts +0 -3
- package/dist/data/canvasPersistence.js +0 -26
- package/dist/data/hooks/useAnnotationCanvasDoc.d.ts +0 -33
- package/dist/data/hooks/useAnnotationCanvasDoc.js +0 -314
- package/dist/data/hooks/useAnnotationDoc.d.ts +0 -7
- package/dist/data/hooks/useAnnotationDoc.js +0 -33
- package/dist/data/hooks/useAnnotationList.d.ts +0 -7
- package/dist/data/hooks/useAnnotationList.js +0 -26
- package/dist/data/hooks/useAnnotationMutations.d.ts +0 -9
- package/dist/data/hooks/useAnnotationMutations.js +0 -11
- package/dist/hooks/useParseMeasurement.d.ts +0 -4
- package/dist/hooks/useParseMeasurement.js +0 -14
- package/dist/utils/evaluateFormula.d.ts +0 -20
- package/dist/utils/evaluateFormula.js +0 -31
|
@@ -10,17 +10,9 @@ import { BackgroundLayerElement } from './elements/BackgroundImageElement.js';
|
|
|
10
10
|
import { PlaneElement, PLANE_CHROME_COLOR, PLANE_GRID_COLOR, PLANE_GRID_OPACITY, PLANE_GRID_WIDTH, PLANE_OUTLINE_WIDTH, } from './elements/PlaneElement.js';
|
|
11
11
|
import { ShapeElement } from './elements/ShapeElement.js';
|
|
12
12
|
import { StrokeElement } from './elements/StrokeElement.js';
|
|
13
|
-
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
// a placed annotation may override them via `lineColor`/`lineWidth`/`lineCap`.
|
|
17
|
-
// The leader line defaults to the medium width preset so it reads clearly
|
|
18
|
-
// against the document; the rectangle border stays thinner (it outlines a
|
|
19
|
-
// region rather than pointing at one).
|
|
20
|
-
const MEASUREMENT_LINE_COLOR = '#0066FF';
|
|
21
|
-
const MEASUREMENT_LINE_WIDTH = 16;
|
|
22
|
-
const MEASUREMENT_RECT_WIDTH = 2;
|
|
23
|
-
// Endpoint handle color — constant selection chrome, independent of the (now
|
|
13
|
+
import { MEASUREMENT_LINE_COLOR, MEASUREMENT_LINE_WIDTH, MEASUREMENT_RECT_WIDTH, } from './measurementLineStyle.js';
|
|
14
|
+
// The measurement-line defaults live in measurementLineStyle.ts (shared with
|
|
15
|
+
// the PDF exporter). Endpoint handle color — constant selection chrome, independent of the (now
|
|
24
16
|
// editable) line color so the handles stay legible on any line color.
|
|
25
17
|
const MEASUREMENT_HANDLE_COLOR = '#0066FF';
|
|
26
18
|
// Selection bounding box for strokes/shapes (measurements get handles instead).
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Default visual constants for the measurement-annotation line and rectangle
|
|
2
|
+
// (the `placement: 'line'` / `'rectangle'` variants of PlacedMeasurementRef).
|
|
3
|
+
// Widths are in doc units, so they scale with zoom like the drawn shapes. A
|
|
4
|
+
// placed annotation may override them via `lineColor` / `lineWidth` /
|
|
5
|
+
// `lineCap`. Skia-free so the PDF exporter (export/pdf) and the Skia scene
|
|
6
|
+
// (AnnotationCanvasSkia) read ONE source — a change here re-styles both the
|
|
7
|
+
// screen and the exported page.
|
|
8
|
+
//
|
|
9
|
+
// The leader line defaults to the medium width preset so it reads clearly
|
|
10
|
+
// against the document; the rectangle border stays thinner (it outlines a
|
|
11
|
+
// region rather than pointing at one).
|
|
12
|
+
export const MEASUREMENT_LINE_COLOR = '#0066FF';
|
|
13
|
+
export const MEASUREMENT_LINE_WIDTH = 16;
|
|
14
|
+
export const MEASUREMENT_RECT_WIDTH = 2;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Hex colour parsing for the PDF emitter. The canvas stores colours as CSS hex
|
|
2
|
+
// strings (`#RGB`, `#RRGGBB`, `#RRGGBBAA`); pdf-lib wants 0..1 components and
|
|
3
|
+
// PDF carries alpha separately (an ExtGState), so the two are split here.
|
|
4
|
+
import { rgb } from 'pdf-lib';
|
|
5
|
+
const BLACK = { rgb: rgb(0, 0, 0), alpha: 1 };
|
|
6
|
+
const channel = (hex, i, width) => {
|
|
7
|
+
const raw = hex.slice(i, i + width);
|
|
8
|
+
const v = parseInt(width === 1 ? raw + raw : raw, 16);
|
|
9
|
+
return Number.isNaN(v) ? 0 : v / 255;
|
|
10
|
+
};
|
|
11
|
+
export const parseHexColor = (input) => {
|
|
12
|
+
if (!input)
|
|
13
|
+
return BLACK;
|
|
14
|
+
const hex = input.trim().replace(/^#/, '');
|
|
15
|
+
switch (hex.length) {
|
|
16
|
+
case 3:
|
|
17
|
+
return {
|
|
18
|
+
rgb: rgb(channel(hex, 0, 1), channel(hex, 1, 1), channel(hex, 2, 1)),
|
|
19
|
+
alpha: 1,
|
|
20
|
+
};
|
|
21
|
+
case 4:
|
|
22
|
+
return {
|
|
23
|
+
rgb: rgb(channel(hex, 0, 1), channel(hex, 1, 1), channel(hex, 2, 1)),
|
|
24
|
+
alpha: channel(hex, 3, 1),
|
|
25
|
+
};
|
|
26
|
+
case 6:
|
|
27
|
+
return {
|
|
28
|
+
rgb: rgb(channel(hex, 0, 2), channel(hex, 2, 2), channel(hex, 4, 2)),
|
|
29
|
+
alpha: 1,
|
|
30
|
+
};
|
|
31
|
+
case 8:
|
|
32
|
+
return {
|
|
33
|
+
rgb: rgb(channel(hex, 0, 2), channel(hex, 2, 2), channel(hex, 4, 2)),
|
|
34
|
+
alpha: channel(hex, 6, 2),
|
|
35
|
+
};
|
|
36
|
+
default:
|
|
37
|
+
return BLACK;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { LineCapStyle, type PDFName, type PDFOperator } from 'pdf-lib';
|
|
2
|
+
import type { StrokeCap, Vec2 } from '../../types/annotation.js';
|
|
3
|
+
import type { AffineMatrix } from './pageMapping.js';
|
|
4
|
+
export declare const withMatrix: (m: AffineMatrix, ops: PDFOperator[]) => PDFOperator[];
|
|
5
|
+
/** Flip back to y-up at (x, y): text and images are drawn upright inside it. */
|
|
6
|
+
export declare const uprightAt: (x: number, y: number) => AffineMatrix;
|
|
7
|
+
export declare const lineCapOf: (cap: StrokeCap | undefined) => LineCapStyle;
|
|
8
|
+
export interface StrokeStyle {
|
|
9
|
+
color: string;
|
|
10
|
+
width: number;
|
|
11
|
+
cap?: StrokeCap;
|
|
12
|
+
join?: 'round' | 'miter';
|
|
13
|
+
dash?: boolean;
|
|
14
|
+
/** ExtGState resource name carrying the element's opacity, if any. */
|
|
15
|
+
graphicsState?: PDFName;
|
|
16
|
+
}
|
|
17
|
+
/** Operators that set the stroking state for a Skia-style stroked paint. */
|
|
18
|
+
export declare const strokeStateOps: (s: StrokeStyle) => PDFOperator[];
|
|
19
|
+
export declare const fillStateOps: (color: string, graphicsState?: PDFName) => PDFOperator[];
|
|
20
|
+
/** Path construction for a polyline over `points`; no painting operator. */
|
|
21
|
+
export declare const polylinePathOps: (points: readonly Vec2[], close?: boolean) => PDFOperator[];
|
|
22
|
+
/** Same, over the flat [x, y, x, y, …] array strokes store. */
|
|
23
|
+
export declare const flatPolylinePathOps: (points: readonly number[]) => PDFOperator[];
|
|
24
|
+
export declare const circlePathOps: (cx: number, cy: number, r: number) => PDFOperator[];
|
|
25
|
+
export declare const rectPathOps: (x: number, y: number, w: number, h: number) => PDFOperator[];
|
|
26
|
+
/** Rounded rectangle path in a y-down space (radius clamped to half a side). */
|
|
27
|
+
export declare const roundedRectPathOps: (x: number, y: number, w: number, h: number, radius: number) => PDFOperator[];
|
|
28
|
+
/** Intersect the clip with an axis-aligned rect (no painting). */
|
|
29
|
+
export declare const clipRectOps: (x: number, y: number, w: number, h: number) => PDFOperator[];
|
|
30
|
+
export declare const strokeOp: () => PDFOperator;
|
|
31
|
+
export declare const fillOp: () => PDFOperator;
|
|
32
|
+
/**
|
|
33
|
+
* Draw an image XObject so it occupies the y-down rect (x, y, w, h) with its
|
|
34
|
+
* top row at `y`. PDF maps the unit square's TOP edge to the image's first
|
|
35
|
+
* row, so under a y-down CTM the image must be un-flipped locally.
|
|
36
|
+
*/
|
|
37
|
+
export declare const imageOps: (name: PDFName, x: number, y: number, w: number, h: number, graphicsState?: PDFName) => PDFOperator[];
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// Low-level PDF operator helpers for the exporter. Everything here works in
|
|
2
|
+
// the CURRENT coordinate system (whatever `cm` is in force), so the same
|
|
3
|
+
// helpers draw marks in doc space and tile chrome in a tile's design space.
|
|
4
|
+
import { LineCapStyle, LineJoinStyle, appendBezierCurve, clip, closePath, concatTransformationMatrix, degrees, drawImage, endPath, fill, lineTo, moveTo, popGraphicsState, pushGraphicsState, rectangle, setDashPattern, setFillingColor, setGraphicsState, setLineCap, setLineJoin, setLineWidth, setStrokingColor, stroke, } from 'pdf-lib';
|
|
5
|
+
import { dashIntervals, toSkiaStrokeCap, } from '../../annotation/canvas/strokeGeometry.js';
|
|
6
|
+
import { parseHexColor } from './color.js';
|
|
7
|
+
/** Bézier circle constant. */
|
|
8
|
+
const KAPPA = 0.5522847498307936;
|
|
9
|
+
export const withMatrix = (m, ops) => [
|
|
10
|
+
pushGraphicsState(),
|
|
11
|
+
concatTransformationMatrix(m[0], m[1], m[2], m[3], m[4], m[5]),
|
|
12
|
+
...ops,
|
|
13
|
+
popGraphicsState(),
|
|
14
|
+
];
|
|
15
|
+
/** Flip back to y-up at (x, y): text and images are drawn upright inside it. */
|
|
16
|
+
export const uprightAt = (x, y) => [
|
|
17
|
+
1,
|
|
18
|
+
0,
|
|
19
|
+
0,
|
|
20
|
+
-1,
|
|
21
|
+
x,
|
|
22
|
+
y,
|
|
23
|
+
];
|
|
24
|
+
export const lineCapOf = (cap) => {
|
|
25
|
+
switch (toSkiaStrokeCap(cap)) {
|
|
26
|
+
case 'butt':
|
|
27
|
+
return LineCapStyle.Butt;
|
|
28
|
+
case 'square':
|
|
29
|
+
return LineCapStyle.Projecting;
|
|
30
|
+
default:
|
|
31
|
+
return LineCapStyle.Round;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
/** Operators that set the stroking state for a Skia-style stroked paint. */
|
|
35
|
+
export const strokeStateOps = (s) => {
|
|
36
|
+
const ops = [];
|
|
37
|
+
if (s.graphicsState)
|
|
38
|
+
ops.push(setGraphicsState(s.graphicsState));
|
|
39
|
+
ops.push(setStrokingColor(parseHexColor(s.color).rgb), setLineWidth(s.width), setLineCap(lineCapOf(s.cap)), setLineJoin(s.join === 'round' ? LineJoinStyle.Round : LineJoinStyle.Miter));
|
|
40
|
+
if (s.dash) {
|
|
41
|
+
const [on, off] = dashIntervals(s.width);
|
|
42
|
+
ops.push(setDashPattern([on, off], 0));
|
|
43
|
+
}
|
|
44
|
+
return ops;
|
|
45
|
+
};
|
|
46
|
+
export const fillStateOps = (color, graphicsState) => {
|
|
47
|
+
const ops = [];
|
|
48
|
+
if (graphicsState)
|
|
49
|
+
ops.push(setGraphicsState(graphicsState));
|
|
50
|
+
ops.push(setFillingColor(parseHexColor(color).rgb));
|
|
51
|
+
return ops;
|
|
52
|
+
};
|
|
53
|
+
/** Path construction for a polyline over `points`; no painting operator. */
|
|
54
|
+
export const polylinePathOps = (points, close = false) => {
|
|
55
|
+
if (points.length === 0)
|
|
56
|
+
return [];
|
|
57
|
+
const ops = [moveTo(points[0].x, points[0].y)];
|
|
58
|
+
for (let i = 1; i < points.length; i++) {
|
|
59
|
+
ops.push(lineTo(points[i].x, points[i].y));
|
|
60
|
+
}
|
|
61
|
+
if (close)
|
|
62
|
+
ops.push(closePath());
|
|
63
|
+
return ops;
|
|
64
|
+
};
|
|
65
|
+
/** Same, over the flat [x, y, x, y, …] array strokes store. */
|
|
66
|
+
export const flatPolylinePathOps = (points) => {
|
|
67
|
+
if (points.length < 2)
|
|
68
|
+
return [];
|
|
69
|
+
const ops = [moveTo(points[0], points[1])];
|
|
70
|
+
for (let i = 2; i + 1 < points.length; i += 2) {
|
|
71
|
+
ops.push(lineTo(points[i], points[i + 1]));
|
|
72
|
+
}
|
|
73
|
+
return ops;
|
|
74
|
+
};
|
|
75
|
+
export const circlePathOps = (cx, cy, r) => {
|
|
76
|
+
const k = KAPPA * r;
|
|
77
|
+
return [
|
|
78
|
+
moveTo(cx + r, cy),
|
|
79
|
+
appendBezierCurve(cx + r, cy + k, cx + k, cy + r, cx, cy + r),
|
|
80
|
+
appendBezierCurve(cx - k, cy + r, cx - r, cy + k, cx - r, cy),
|
|
81
|
+
appendBezierCurve(cx - r, cy - k, cx - k, cy - r, cx, cy - r),
|
|
82
|
+
appendBezierCurve(cx + k, cy - r, cx + r, cy - k, cx + r, cy),
|
|
83
|
+
closePath(),
|
|
84
|
+
];
|
|
85
|
+
};
|
|
86
|
+
export const rectPathOps = (x, y, w, h) => [rectangle(x, y, w, h)];
|
|
87
|
+
/** Rounded rectangle path in a y-down space (radius clamped to half a side). */
|
|
88
|
+
export const roundedRectPathOps = (x, y, w, h, radius) => {
|
|
89
|
+
const r = Math.max(0, Math.min(radius, w / 2, h / 2));
|
|
90
|
+
if (r === 0)
|
|
91
|
+
return rectPathOps(x, y, w, h);
|
|
92
|
+
const k = KAPPA * r;
|
|
93
|
+
return [
|
|
94
|
+
moveTo(x + r, y),
|
|
95
|
+
lineTo(x + w - r, y),
|
|
96
|
+
appendBezierCurve(x + w - r + k, y, x + w, y + r - k, x + w, y + r),
|
|
97
|
+
lineTo(x + w, y + h - r),
|
|
98
|
+
appendBezierCurve(x + w, y + h - r + k, x + w - r + k, y + h, x + w - r, y + h),
|
|
99
|
+
lineTo(x + r, y + h),
|
|
100
|
+
appendBezierCurve(x + r - k, y + h, x, y + h - r + k, x, y + h - r),
|
|
101
|
+
lineTo(x, y + r),
|
|
102
|
+
appendBezierCurve(x, y + r - k, x + r - k, y, x + r, y),
|
|
103
|
+
closePath(),
|
|
104
|
+
];
|
|
105
|
+
};
|
|
106
|
+
/** Intersect the clip with an axis-aligned rect (no painting). */
|
|
107
|
+
export const clipRectOps = (x, y, w, h) => [rectangle(x, y, w, h), clip(), endPath()];
|
|
108
|
+
export const strokeOp = () => stroke();
|
|
109
|
+
export const fillOp = () => fill();
|
|
110
|
+
/**
|
|
111
|
+
* Draw an image XObject so it occupies the y-down rect (x, y, w, h) with its
|
|
112
|
+
* top row at `y`. PDF maps the unit square's TOP edge to the image's first
|
|
113
|
+
* row, so under a y-down CTM the image must be un-flipped locally.
|
|
114
|
+
*/
|
|
115
|
+
export const imageOps = (name, x, y, w, h, graphicsState) => {
|
|
116
|
+
const ops = [pushGraphicsState()];
|
|
117
|
+
if (graphicsState)
|
|
118
|
+
ops.push(setGraphicsState(graphicsState));
|
|
119
|
+
ops.push(concatTransformationMatrix(w, 0, 0, -h, x, y + h), ...drawImage(name, {
|
|
120
|
+
x: 0,
|
|
121
|
+
y: 0,
|
|
122
|
+
width: 1,
|
|
123
|
+
height: 1,
|
|
124
|
+
rotate: degrees(0),
|
|
125
|
+
xSkew: degrees(0),
|
|
126
|
+
ySkew: degrees(0),
|
|
127
|
+
}), popGraphicsState());
|
|
128
|
+
return ops;
|
|
129
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { PDFDocument, PDFFont, PDFName, PDFPage, type PDFOperator } from 'pdf-lib';
|
|
2
|
+
export type TextAlign = 'left' | 'center' | 'right';
|
|
3
|
+
export interface TextOptions {
|
|
4
|
+
align?: TextAlign;
|
|
5
|
+
/** ExtGState name carrying an opacity, if any. */
|
|
6
|
+
graphicsState?: PDFName;
|
|
7
|
+
}
|
|
8
|
+
export declare class PdfFonts {
|
|
9
|
+
private readonly primary;
|
|
10
|
+
private readonly fallback;
|
|
11
|
+
private readonly keys;
|
|
12
|
+
private constructor();
|
|
13
|
+
static create(doc: PDFDocument, primaryBytes: Uint8Array): Promise<PdfFonts>;
|
|
14
|
+
/** Whether the primary face has a glyph for every code point of `text`. */
|
|
15
|
+
private primaryCovers;
|
|
16
|
+
/** Drop the code points the standard fallback cannot encode. */
|
|
17
|
+
private fallbackUsable;
|
|
18
|
+
/**
|
|
19
|
+
* The face to draw `text` with and the text it can actually show. The
|
|
20
|
+
* primary wins when it covers every glyph; otherwise the fallback, minus
|
|
21
|
+
* anything even it cannot encode. `dropped` is true when glyphs were lost.
|
|
22
|
+
*/
|
|
23
|
+
pick(text: string): {
|
|
24
|
+
font: PDFFont;
|
|
25
|
+
text: string;
|
|
26
|
+
dropped: boolean;
|
|
27
|
+
};
|
|
28
|
+
width(text: string, size: number): number;
|
|
29
|
+
private keyFor;
|
|
30
|
+
/**
|
|
31
|
+
* Operators drawing one line of text in a y-DOWN space, with its baseline at
|
|
32
|
+
* `yBaseline` and its left / centre / right edge at `x`. Returns [] when
|
|
33
|
+
* nothing of the text is drawable.
|
|
34
|
+
*/
|
|
35
|
+
textOps(page: PDFPage, text: string, size: number, color: string, x: number, yBaseline: number, options?: TextOptions): PDFOperator[];
|
|
36
|
+
/** Largest size ≤ `max` at which `text` fits in `maxWidth`. */
|
|
37
|
+
fitSize(text: string, maxWidth: number, max: number, min?: number): number;
|
|
38
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// Text for the exporter: one embedded primary face (the apps' tile font) plus
|
|
2
|
+
// a standard-14 fallback for the glyphs it lacks. Metropolis Bold, for one,
|
|
3
|
+
// has no `°` — and an angle tile is nothing without it.
|
|
4
|
+
import { StandardFonts, degrees, drawText, } from 'pdf-lib';
|
|
5
|
+
import fontkit from '@pdf-lib/fontkit';
|
|
6
|
+
import { parseHexColor } from './color.js';
|
|
7
|
+
import { uprightAt, withMatrix } from './emit.js';
|
|
8
|
+
const codePointsOf = (text) => Array.from(text, (ch) => ch.codePointAt(0));
|
|
9
|
+
export class PdfFonts {
|
|
10
|
+
constructor(primary, fallback) {
|
|
11
|
+
this.primary = primary;
|
|
12
|
+
this.fallback = fallback;
|
|
13
|
+
this.keys = new Map();
|
|
14
|
+
}
|
|
15
|
+
static async create(doc, primaryBytes) {
|
|
16
|
+
doc.registerFontkit(fontkit);
|
|
17
|
+
// NOT subset: fontkit's CFF subsetter throws on this face ("value" argument out of
|
|
18
|
+
// bounds in CFFSubset.encode), and the whole file is ~24 KB anyway.
|
|
19
|
+
const primary = await doc.embedFont(primaryBytes, { subset: false });
|
|
20
|
+
const fallback = await doc.embedStandardFont(StandardFonts.HelveticaBold);
|
|
21
|
+
return new PdfFonts(primary, fallback);
|
|
22
|
+
}
|
|
23
|
+
/** Whether the primary face has a glyph for every code point of `text`. */
|
|
24
|
+
primaryCovers(text) {
|
|
25
|
+
const fk = this.primary.embedder?.font;
|
|
26
|
+
if (!fk?.hasGlyphForCodePoint)
|
|
27
|
+
return true;
|
|
28
|
+
return codePointsOf(text).every((cp) => cp === 0x20 || fk.hasGlyphForCodePoint(cp));
|
|
29
|
+
}
|
|
30
|
+
/** Drop the code points the standard fallback cannot encode. */
|
|
31
|
+
fallbackUsable(text) {
|
|
32
|
+
const enc = this.fallback.embedder?.encoding;
|
|
33
|
+
if (!enc?.canEncodeUnicodeCodePoint)
|
|
34
|
+
return text;
|
|
35
|
+
return Array.from(text)
|
|
36
|
+
.filter((ch) => enc.canEncodeUnicodeCodePoint(ch.codePointAt(0)))
|
|
37
|
+
.join('');
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The face to draw `text` with and the text it can actually show. The
|
|
41
|
+
* primary wins when it covers every glyph; otherwise the fallback, minus
|
|
42
|
+
* anything even it cannot encode. `dropped` is true when glyphs were lost.
|
|
43
|
+
*/
|
|
44
|
+
pick(text) {
|
|
45
|
+
if (this.primaryCovers(text)) {
|
|
46
|
+
return { font: this.primary, text, dropped: false };
|
|
47
|
+
}
|
|
48
|
+
const usable = this.fallbackUsable(text);
|
|
49
|
+
return { font: this.fallback, text: usable, dropped: usable !== text };
|
|
50
|
+
}
|
|
51
|
+
width(text, size) {
|
|
52
|
+
const p = this.pick(text);
|
|
53
|
+
return p.text ? p.font.widthOfTextAtSize(p.text, size) : 0;
|
|
54
|
+
}
|
|
55
|
+
keyFor(page, font) {
|
|
56
|
+
let keys = this.keys.get(page);
|
|
57
|
+
if (!keys) {
|
|
58
|
+
keys = {
|
|
59
|
+
primary: page.node.newFontDictionary(this.primary.name, this.primary.ref),
|
|
60
|
+
fallback: page.node.newFontDictionary(this.fallback.name, this.fallback.ref),
|
|
61
|
+
};
|
|
62
|
+
this.keys.set(page, keys);
|
|
63
|
+
}
|
|
64
|
+
return font === this.primary ? keys.primary : keys.fallback;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Operators drawing one line of text in a y-DOWN space, with its baseline at
|
|
68
|
+
* `yBaseline` and its left / centre / right edge at `x`. Returns [] when
|
|
69
|
+
* nothing of the text is drawable.
|
|
70
|
+
*/
|
|
71
|
+
textOps(page, text, size, color, x, yBaseline, options = {}) {
|
|
72
|
+
const picked = this.pick(text);
|
|
73
|
+
if (!picked.text || size <= 0)
|
|
74
|
+
return [];
|
|
75
|
+
const w = picked.font.widthOfTextAtSize(picked.text, size);
|
|
76
|
+
const align = options.align ?? 'left';
|
|
77
|
+
const left = align === 'center' ? x - w / 2 : align === 'right' ? x - w : x;
|
|
78
|
+
return withMatrix(uprightAt(left, yBaseline), drawText(picked.font.encodeText(picked.text), {
|
|
79
|
+
color: parseHexColor(color).rgb,
|
|
80
|
+
font: this.keyFor(page, picked.font),
|
|
81
|
+
size,
|
|
82
|
+
rotate: degrees(0),
|
|
83
|
+
xSkew: degrees(0),
|
|
84
|
+
ySkew: degrees(0),
|
|
85
|
+
x: 0,
|
|
86
|
+
y: 0,
|
|
87
|
+
graphicsState: options.graphicsState,
|
|
88
|
+
}));
|
|
89
|
+
}
|
|
90
|
+
/** Largest size ≤ `max` at which `text` fits in `maxWidth`. */
|
|
91
|
+
fitSize(text, maxWidth, max, min = 4) {
|
|
92
|
+
const atOne = this.width(text, 1);
|
|
93
|
+
if (atOne <= 0)
|
|
94
|
+
return max;
|
|
95
|
+
return Math.max(min, Math.min(max, maxWidth / atOne));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { renderCanvasToPdf } from './renderCanvasToPdf.js';
|
|
2
|
+
export { docToPageMatrix, pageRectsOf, normalizeRotation, rotatedBoxSize, applyMatrix, type AffineMatrix, type PageRotation, type PdfBox, } from './pageMapping.js';
|
|
3
|
+
export { resolveTileContent, measurementTileFillFor, FORMULA_COLUMN_TILE_FILL, PLANE_COMPUTED_TILE_FILL, type TileFileInfo, type TileResolutionContext, } from './tileContent.js';
|
|
4
|
+
export { tileFootprint, type TileFootprint } from './tiles.js';
|
|
5
|
+
export { PdfExportError, type EmbeddedImage, type PdfExportCalibration, type RenderCanvasToPdfInput, type RenderCanvasToPdfResult, type ResolvedTileContent, type MeasurementTileContent, type LiteralTileContent, type EmptyTileContent, type HeaderTileContent, type ObjectTileContent, type PlaneDimensionTileContent, type InstructionsTileContent, type SkippedItem, } from './types.js';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { renderCanvasToPdf } from './renderCanvasToPdf.js';
|
|
2
|
+
export { docToPageMatrix, pageRectsOf, normalizeRotation, rotatedBoxSize, applyMatrix, } from './pageMapping.js';
|
|
3
|
+
export { resolveTileContent, measurementTileFillFor, FORMULA_COLUMN_TILE_FILL, PLANE_COMPUTED_TILE_FILL, } from './tileContent.js';
|
|
4
|
+
export { tileFootprint } from './tiles.js';
|
|
5
|
+
export { PdfExportError, } from './types.js';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type PDFName, type PDFOperator } from 'pdf-lib';
|
|
2
|
+
import type { AnnotationBackgroundLayer } from '../../types/annotation.js';
|
|
3
|
+
import type { DocRect } from './pageMapping.js';
|
|
4
|
+
export declare const layerVisibleRect: (layer: AnnotationBackgroundLayer) => DocRect;
|
|
5
|
+
export declare const layerOps: (layer: AnnotationBackgroundLayer, imageName: PDFName) => PDFOperator[];
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Background image layers baked into the page: the full image placed at its
|
|
2
|
+
// doc rect, clipped to its crop window. Draw order is array order, under every
|
|
3
|
+
// mark — exactly the z-order AnnotationCanvasSkia paints.
|
|
4
|
+
import { popGraphicsState, pushGraphicsState, } from 'pdf-lib';
|
|
5
|
+
import { backgroundLayerDocRect, backgroundLayerFullDocRect, } from '../../annotation/canvas/backgroundLayers.js';
|
|
6
|
+
import { clipRectOps, imageOps } from './emit.js';
|
|
7
|
+
export const layerVisibleRect = (layer) => backgroundLayerDocRect(layer);
|
|
8
|
+
export const layerOps = (layer, imageName) => {
|
|
9
|
+
const crop = backgroundLayerDocRect(layer);
|
|
10
|
+
const full = backgroundLayerFullDocRect(layer);
|
|
11
|
+
if (crop.width <= 0 || crop.height <= 0)
|
|
12
|
+
return [];
|
|
13
|
+
return [
|
|
14
|
+
pushGraphicsState(),
|
|
15
|
+
...clipRectOps(crop.x, crop.y, crop.width, crop.height),
|
|
16
|
+
...imageOps(imageName, full.x, full.y, full.width, full.height),
|
|
17
|
+
popGraphicsState(),
|
|
18
|
+
];
|
|
19
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type PDFName, type PDFOperator, type PDFPage } from 'pdf-lib';
|
|
2
|
+
import type { AnnotationShape, AnnotationStroke, PlacedMeasurementRef } from '../../types/annotation.js';
|
|
3
|
+
import { dashIntervals } from '../../annotation/canvas/strokeGeometry.js';
|
|
4
|
+
import type { PdfFonts } from './fonts.js';
|
|
5
|
+
import { type DocRect } from './pageMapping.js';
|
|
6
|
+
import type { SkippedItem } from './types.js';
|
|
7
|
+
export interface MarkContext {
|
|
8
|
+
page: PDFPage;
|
|
9
|
+
fonts: PdfFonts;
|
|
10
|
+
/** An ExtGState carrying `alpha` (CA/ca) for this page; undefined at 1. */
|
|
11
|
+
alphaState: (alpha: number) => PDFName | undefined;
|
|
12
|
+
skipped: SkippedItem[];
|
|
13
|
+
}
|
|
14
|
+
export declare const strokeBounds: (s: AnnotationStroke) => DocRect | null;
|
|
15
|
+
export declare const strokeOps: (s: AnnotationStroke, ctx: MarkContext) => PDFOperator[];
|
|
16
|
+
export declare const shapeBounds: (shape: AnnotationShape) => DocRect | null;
|
|
17
|
+
export declare const shapeOps: (shape: AnnotationShape, ctx: MarkContext) => PDFOperator[];
|
|
18
|
+
export declare const measurementMarkBounds: (placed: PlacedMeasurementRef) => DocRect | null;
|
|
19
|
+
export declare const measurementMarkOps: (placed: PlacedMeasurementRef, ctx: MarkContext) => PDFOperator[];
|
|
20
|
+
export { dashIntervals };
|