@zebrash/core 0.1.3 → 0.1.4
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/assets/fonts-browser.d.ts +2 -0
- package/dist/assets/fonts-browser.d.ts.map +1 -1
- package/dist/assets/fonts-browser.js +6 -0
- package/dist/assets/fonts.d.ts +3 -6
- package/dist/assets/fonts.d.ts.map +1 -1
- package/dist/assets/fonts.js +15 -5
- package/dist/assets/index.d.ts +1 -1
- package/dist/assets/index.d.ts.map +1 -1
- package/dist/assets/index.js +1 -1
- package/dist/drawer-options.d.ts +20 -2
- package/dist/drawer-options.d.ts.map +1 -1
- package/dist/drawer-options.js +3 -1
- package/dist/drawer.d.ts +27 -0
- package/dist/drawer.d.ts.map +1 -1
- package/dist/drawer.js +96 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/svg/emitter.d.ts +76 -0
- package/dist/svg/emitter.d.ts.map +1 -0
- package/dist/svg/emitter.js +207 -0
- package/dist/svg/font_embed.d.ts +12 -0
- package/dist/svg/font_embed.d.ts.map +1 -0
- package/dist/svg/font_embed.js +61 -0
- package/dist/svg-drawers/barcode_128.d.ts +3 -0
- package/dist/svg-drawers/barcode_128.d.ts.map +1 -0
- package/dist/svg-drawers/barcode_128.js +74 -0
- package/dist/svg-drawers/barcode_2of5.d.ts +3 -0
- package/dist/svg-drawers/barcode_2of5.d.ts.map +1 -0
- package/dist/svg-drawers/barcode_2of5.js +36 -0
- package/dist/svg-drawers/barcode_39.d.ts +3 -0
- package/dist/svg-drawers/barcode_39.d.ts.map +1 -0
- package/dist/svg-drawers/barcode_39.js +34 -0
- package/dist/svg-drawers/barcode_aztec.d.ts +3 -0
- package/dist/svg-drawers/barcode_aztec.d.ts.map +1 -0
- package/dist/svg-drawers/barcode_aztec.js +48 -0
- package/dist/svg-drawers/barcode_datamatrix.d.ts +3 -0
- package/dist/svg-drawers/barcode_datamatrix.d.ts.map +1 -0
- package/dist/svg-drawers/barcode_datamatrix.js +44 -0
- package/dist/svg-drawers/barcode_ean13.d.ts +3 -0
- package/dist/svg-drawers/barcode_ean13.d.ts.map +1 -0
- package/dist/svg-drawers/barcode_ean13.js +76 -0
- package/dist/svg-drawers/barcode_paint_svg.d.ts +35 -0
- package/dist/svg-drawers/barcode_paint_svg.d.ts.map +1 -0
- package/dist/svg-drawers/barcode_paint_svg.js +144 -0
- package/dist/svg-drawers/barcode_pdf417.d.ts +3 -0
- package/dist/svg-drawers/barcode_pdf417.d.ts.map +1 -0
- package/dist/svg-drawers/barcode_pdf417.js +43 -0
- package/dist/svg-drawers/barcode_qr.d.ts +3 -0
- package/dist/svg-drawers/barcode_qr.d.ts.map +1 -0
- package/dist/svg-drawers/barcode_qr.js +39 -0
- package/dist/svg-drawers/graphic_box.d.ts +3 -0
- package/dist/svg-drawers/graphic_box.d.ts.map +1 -0
- package/dist/svg-drawers/graphic_box.js +64 -0
- package/dist/svg-drawers/graphic_circle.d.ts +3 -0
- package/dist/svg-drawers/graphic_circle.d.ts.map +1 -0
- package/dist/svg-drawers/graphic_circle.js +19 -0
- package/dist/svg-drawers/graphic_diagonal_line.d.ts +3 -0
- package/dist/svg-drawers/graphic_diagonal_line.d.ts.map +1 -0
- package/dist/svg-drawers/graphic_diagonal_line.js +36 -0
- package/dist/svg-drawers/graphic_field.d.ts +3 -0
- package/dist/svg-drawers/graphic_field.d.ts.map +1 -0
- package/dist/svg-drawers/graphic_field.js +72 -0
- package/dist/svg-drawers/index.d.ts +10 -0
- package/dist/svg-drawers/index.d.ts.map +1 -0
- package/dist/svg-drawers/index.js +41 -0
- package/dist/svg-drawers/maxicode.d.ts +3 -0
- package/dist/svg-drawers/maxicode.d.ts.map +1 -0
- package/dist/svg-drawers/maxicode.js +87 -0
- package/dist/svg-drawers/svg_element_drawer.d.ts +13 -0
- package/dist/svg-drawers/svg_element_drawer.d.ts.map +1 -0
- package/dist/svg-drawers/svg_element_drawer.js +2 -0
- package/dist/svg-drawers/text_field.d.ts +3 -0
- package/dist/svg-drawers/text_field.d.ts.map +1 -0
- package/dist/svg-drawers/text_field.js +264 -0
- package/dist/svg-drawers/transform.d.ts +20 -0
- package/dist/svg-drawers/transform.d.ts.map +1 -0
- package/dist/svg-drawers/transform.js +52 -0
- package/package.json +1 -1
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
// SVG analogue of `drawers/text_field.ts`. This is the most complex drawer
|
|
2
|
+
// — orientation, font scaling, word-wrap, anchor positioning all matter.
|
|
3
|
+
//
|
|
4
|
+
// Approach: measure on an offscreen canvas (so we get the same widths as the
|
|
5
|
+
// PNG path, and word-wrap lands on identical line breaks), but emit `<text>`
|
|
6
|
+
// elements for the actual rendering. Pre-anchor the x/y so we always emit
|
|
7
|
+
// with `text-anchor="start"` — that sidesteps any rasteriser-specific
|
|
8
|
+
// interpretation of SVG's anchor / dominant-baseline interactions.
|
|
9
|
+
import { FONT_FAMILY_DEJAVU_MONO, FONT_FAMILY_DEJAVU_MONO_BOLD, FONT_FAMILY_HELVETICA_BOLD, FONT_FAMILY_ZPL_GS, registerEmbeddedFonts, } from "../assets/index.js";
|
|
10
|
+
import { FieldAlignmentRight, FieldOrientation90, FieldOrientation180, FieldOrientation270, TextAlignmentCenter, TextAlignmentJustified, TextAlignmentRight, } from "../elements/index.js";
|
|
11
|
+
import { platform } from "../platform.js";
|
|
12
|
+
import { rotateAbout, scaleAbout } from "./transform.js";
|
|
13
|
+
const FONT_KEY_BY_FAMILY = {
|
|
14
|
+
[FONT_FAMILY_HELVETICA_BOLD]: "HelveticaBold",
|
|
15
|
+
[FONT_FAMILY_DEJAVU_MONO]: "DejavuSansMono",
|
|
16
|
+
[FONT_FAMILY_DEJAVU_MONO_BOLD]: "DejavuSansMonoBold",
|
|
17
|
+
[FONT_FAMILY_ZPL_GS]: "ZplGS",
|
|
18
|
+
};
|
|
19
|
+
function isTextField(value) {
|
|
20
|
+
return (typeof value === "object" &&
|
|
21
|
+
value !== null &&
|
|
22
|
+
value._kind === "TextField");
|
|
23
|
+
}
|
|
24
|
+
let measureCtx = null;
|
|
25
|
+
function getMeasureCtx() {
|
|
26
|
+
if (measureCtx === null) {
|
|
27
|
+
const canvas = platform.createCanvas(1, 1);
|
|
28
|
+
const ctx = canvas.getContext("2d");
|
|
29
|
+
if (ctx === null) {
|
|
30
|
+
throw new Error("zebrash: failed to acquire 2D context for SVG text measurement");
|
|
31
|
+
}
|
|
32
|
+
measureCtx = ctx;
|
|
33
|
+
}
|
|
34
|
+
return measureCtx;
|
|
35
|
+
}
|
|
36
|
+
export function newTextFieldSvgDrawer() {
|
|
37
|
+
return {
|
|
38
|
+
async draw(emitter, element, _options, state) {
|
|
39
|
+
if (!isTextField(element))
|
|
40
|
+
return;
|
|
41
|
+
// Same lazy font setup as the canvas drawer — needed because the
|
|
42
|
+
// measurement canvas (offscreen, but real) still uses the platform's
|
|
43
|
+
// font registry.
|
|
44
|
+
await registerEmbeddedFonts();
|
|
45
|
+
if (element.font.customFont && !element.font.customFontFamily) {
|
|
46
|
+
await platform.registerFont(element.font.customFont.data, element.font.customFont.name);
|
|
47
|
+
element.font.customFontFamily = element.font.customFont.name;
|
|
48
|
+
}
|
|
49
|
+
const text = adjustTextField(element);
|
|
50
|
+
const scaleX = getFontScaleX(text.font);
|
|
51
|
+
const family = getFontFamily(text.font);
|
|
52
|
+
const fontKey = FONT_KEY_BY_FAMILY[family];
|
|
53
|
+
const ctx = getMeasureCtx();
|
|
54
|
+
ctx.font = `${text.font.height}px "${family}"`;
|
|
55
|
+
ctx.textBaseline = "alphabetic";
|
|
56
|
+
ctx.textAlign = "left";
|
|
57
|
+
const { width: rawW, fontHeight } = measure(ctx, text.text);
|
|
58
|
+
const w = rawW * scaleX;
|
|
59
|
+
const h = fontHeight;
|
|
60
|
+
const [x, y] = getTextTopLeftPos(text, w, h, state);
|
|
61
|
+
state.updateAutomaticTextPosition(text, w);
|
|
62
|
+
const [ax, ay] = getTextAxAy(text);
|
|
63
|
+
emitter.save();
|
|
64
|
+
try {
|
|
65
|
+
rotateAbout(emitter, text.font.orientation, x, y);
|
|
66
|
+
if (scaleX !== 1.0) {
|
|
67
|
+
scaleAbout(emitter, scaleX, 1, x, y);
|
|
68
|
+
}
|
|
69
|
+
if (text.block) {
|
|
70
|
+
const maxWidth = text.block.maxWidth / scaleX;
|
|
71
|
+
const lineSpacing = h === 0 ? 1 : 1 + text.block.lineSpacing / h;
|
|
72
|
+
drawStringWrapped(emitter, ctx, text.text, x, y - h, ax, ay, maxWidth, lineSpacing, text.block.alignment, h, family, fontKey, text.font.height);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
drawStringAnchored(emitter, ctx, text.text, x, y, ax, ay, family, fontKey, text.font.height);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
finally {
|
|
79
|
+
emitter.restore();
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
// Helpers — same shape as the canvas drawer, but rendering through the emitter.
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
function adjustTextField(text) {
|
|
88
|
+
if (text.font.name === "B") {
|
|
89
|
+
return { ...text, text: text.text.toUpperCase() };
|
|
90
|
+
}
|
|
91
|
+
return text;
|
|
92
|
+
}
|
|
93
|
+
function getFontFamily(font) {
|
|
94
|
+
if (font.customFontFamily)
|
|
95
|
+
return font.customFontFamily;
|
|
96
|
+
switch (font.name) {
|
|
97
|
+
case "0":
|
|
98
|
+
return FONT_FAMILY_HELVETICA_BOLD;
|
|
99
|
+
case "B":
|
|
100
|
+
return FONT_FAMILY_DEJAVU_MONO_BOLD;
|
|
101
|
+
case "GS":
|
|
102
|
+
return FONT_FAMILY_ZPL_GS;
|
|
103
|
+
default:
|
|
104
|
+
return FONT_FAMILY_DEJAVU_MONO;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function getFontScaleX(font) {
|
|
108
|
+
if (font.height !== 0) {
|
|
109
|
+
return (widthToHeightRatio(font) * font.width) / font.height;
|
|
110
|
+
}
|
|
111
|
+
return 1.0;
|
|
112
|
+
}
|
|
113
|
+
function widthToHeightRatio(font) {
|
|
114
|
+
if (font.name === "0" || font.name === "GS" || font.customFontFamily) {
|
|
115
|
+
return 1.0;
|
|
116
|
+
}
|
|
117
|
+
return 2.0;
|
|
118
|
+
}
|
|
119
|
+
function getTextTopLeftPos(text, w, h, state) {
|
|
120
|
+
const [x, y] = state.getTextPosition(text);
|
|
121
|
+
let lines = 1;
|
|
122
|
+
let spacing = 0;
|
|
123
|
+
let blockW = w;
|
|
124
|
+
if (text.block) {
|
|
125
|
+
lines = Math.max(text.block.maxLines, 1);
|
|
126
|
+
spacing = text.block.lineSpacing;
|
|
127
|
+
blockW = text.block.maxWidth;
|
|
128
|
+
}
|
|
129
|
+
if (!text.position.calculateFromBottom) {
|
|
130
|
+
switch (text.font.orientation) {
|
|
131
|
+
case FieldOrientation90:
|
|
132
|
+
return [x + h / 4, y];
|
|
133
|
+
case FieldOrientation180:
|
|
134
|
+
return [x + blockW, y + h / 4];
|
|
135
|
+
case FieldOrientation270:
|
|
136
|
+
return [x + (3 * h) / 4, y + blockW];
|
|
137
|
+
default:
|
|
138
|
+
return [x, y + (3 * h) / 4];
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
const offset = (lines - 1) * (h + spacing);
|
|
142
|
+
switch (text.font.orientation) {
|
|
143
|
+
case FieldOrientation90:
|
|
144
|
+
return [x + offset, y];
|
|
145
|
+
case FieldOrientation180:
|
|
146
|
+
return [x, y + offset];
|
|
147
|
+
case FieldOrientation270:
|
|
148
|
+
return [x - offset, y];
|
|
149
|
+
default:
|
|
150
|
+
return [x, y - offset];
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
function getTextAxAy(text) {
|
|
154
|
+
return [text.alignment === FieldAlignmentRight ? 1 : 0, 0];
|
|
155
|
+
}
|
|
156
|
+
function measure(ctx, s) {
|
|
157
|
+
const m = ctx.measureText(s);
|
|
158
|
+
return {
|
|
159
|
+
width: m.width,
|
|
160
|
+
fontHeight: m.fontBoundingBoxAscent + m.fontBoundingBoxDescent,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Mirrors the canvas drawer's `drawStringAnchored`. We pre-compute the
|
|
165
|
+
* effective x/y (same arithmetic the canvas applies via fillText offsets)
|
|
166
|
+
* and emit a single `<text>` with `text-anchor="start"`.
|
|
167
|
+
*/
|
|
168
|
+
function drawStringAnchored(emitter, ctx, s, x, y, ax, ay, family, fontKey, fontSize) {
|
|
169
|
+
const { width, fontHeight } = measure(ctx, s);
|
|
170
|
+
emitter.text(x - ax * width, y + ay * fontHeight, s, {
|
|
171
|
+
fontFamily: family,
|
|
172
|
+
fontKey,
|
|
173
|
+
fontSize,
|
|
174
|
+
anchor: "start",
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Mirrors the canvas drawer's `drawStringWrapped` + `drawStringJustified`.
|
|
179
|
+
*/
|
|
180
|
+
function drawStringWrapped(emitter, ctx, s, x, y, ax, ay, width, lineSpacing, align, fontHeight, family, fontKey, fontSize) {
|
|
181
|
+
const lines = wordWrap(ctx, s, width);
|
|
182
|
+
let totalH = lines.length * fontHeight * lineSpacing;
|
|
183
|
+
totalH -= (lineSpacing - 1) * fontHeight;
|
|
184
|
+
let cx = x - ax * width;
|
|
185
|
+
let cy = y - ay * totalH;
|
|
186
|
+
let lineAx = ax;
|
|
187
|
+
switch (align) {
|
|
188
|
+
case TextAlignmentCenter:
|
|
189
|
+
lineAx = 0.5;
|
|
190
|
+
cx += width / 2;
|
|
191
|
+
break;
|
|
192
|
+
case TextAlignmentRight:
|
|
193
|
+
lineAx = 1;
|
|
194
|
+
cx += width;
|
|
195
|
+
break;
|
|
196
|
+
default:
|
|
197
|
+
lineAx = 0;
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
const lineAy = 1;
|
|
201
|
+
const lastLine = lines.length - 1;
|
|
202
|
+
for (let i = 0; i < lines.length; i++) {
|
|
203
|
+
const line = lines[i] ?? "";
|
|
204
|
+
if (align === TextAlignmentJustified && i < lastLine) {
|
|
205
|
+
drawStringJustified(emitter, ctx, line, cx, cy, lineAx, lineAy, width, fontHeight, family, fontKey, fontSize);
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
drawStringAnchored(emitter, ctx, line, cx, cy, lineAx, lineAy, family, fontKey, fontSize);
|
|
209
|
+
}
|
|
210
|
+
cy += fontHeight * lineSpacing;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
function drawStringJustified(emitter, ctx, line, x, y, ax, ay, maxWidth, fontHeight, family, fontKey, fontSize) {
|
|
214
|
+
const words = splitFields(line);
|
|
215
|
+
if (words.length === 0)
|
|
216
|
+
return;
|
|
217
|
+
const wordsWidth = Array.from({ length: words.length }, () => 0);
|
|
218
|
+
let totalWordWidth = 0;
|
|
219
|
+
for (let i = 0; i < words.length; i++) {
|
|
220
|
+
const w = ctx.measureText(words[i] ?? "").width;
|
|
221
|
+
wordsWidth[i] = w;
|
|
222
|
+
totalWordWidth += w;
|
|
223
|
+
}
|
|
224
|
+
const spaceCount = words.length - 1;
|
|
225
|
+
let spaceWidth = 0;
|
|
226
|
+
if (spaceCount > 0) {
|
|
227
|
+
spaceWidth = (maxWidth - totalWordWidth) / spaceCount;
|
|
228
|
+
if (spaceWidth < 0)
|
|
229
|
+
spaceWidth = fontHeight * 0.3;
|
|
230
|
+
}
|
|
231
|
+
let cx = x;
|
|
232
|
+
for (let i = 0; i < words.length; i++) {
|
|
233
|
+
drawStringAnchored(emitter, ctx, words[i] ?? "", cx, y, ax, ay, family, fontKey, fontSize);
|
|
234
|
+
cx += (wordsWidth[i] ?? 0) + spaceWidth;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
function splitFields(s) {
|
|
238
|
+
return s.split(/\s+/u).filter((w) => w.length > 0);
|
|
239
|
+
}
|
|
240
|
+
function wordWrap(ctx, s, width) {
|
|
241
|
+
const result = [];
|
|
242
|
+
for (const paragraph of s.split("\n")) {
|
|
243
|
+
const words = splitFields(paragraph);
|
|
244
|
+
if (words.length === 0) {
|
|
245
|
+
result.push("");
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
let current = words[0] ?? "";
|
|
249
|
+
for (let i = 1; i < words.length; i++) {
|
|
250
|
+
const word = words[i] ?? "";
|
|
251
|
+
const candidate = `${current} ${word}`;
|
|
252
|
+
if (ctx.measureText(candidate).width > width) {
|
|
253
|
+
result.push(current);
|
|
254
|
+
current = word;
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
current = candidate;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
result.push(current);
|
|
261
|
+
}
|
|
262
|
+
return result;
|
|
263
|
+
}
|
|
264
|
+
//# sourceMappingURL=text_field.js.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { SvgEmitter } from "../svg/emitter.ts";
|
|
2
|
+
import { type FieldOrientation, type LabelPosition } from "../elements/index.ts";
|
|
3
|
+
interface RotatableImage {
|
|
4
|
+
width: number;
|
|
5
|
+
height: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Apply the same rotation transform `rotateImage` applies on a canvas, but
|
|
9
|
+
* via the emitter's group stack. The matching `restore()` closes every
|
|
10
|
+
* `<g>` opened here.
|
|
11
|
+
*/
|
|
12
|
+
export declare function rotateImage(emitter: SvgEmitter, img: RotatableImage, pos: LabelPosition, orientation: FieldOrientation): void;
|
|
13
|
+
/** Width-and-height variant matching `rotateForOrientation` on the canvas side. */
|
|
14
|
+
export declare function rotateForOrientation(emitter: SvgEmitter, width: number, height: number, pos: LabelPosition, orientation: FieldOrientation): void;
|
|
15
|
+
/** SVG analogue of `rotateAbout` — pure rotation about (x, y). */
|
|
16
|
+
export declare function rotateAbout(emitter: SvgEmitter, orientation: FieldOrientation, x: number, y: number): void;
|
|
17
|
+
/** SVG analogue of `scaleAbout` — non-uniform scale about (x, y). */
|
|
18
|
+
export declare function scaleAbout(emitter: SvgEmitter, sx: number, sy: number, x: number, y: number): void;
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=transform.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../../src/svg-drawers/transform.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD,OAAO,EACL,KAAK,gBAAgB,EAIrB,KAAK,aAAa,EAEnB,MAAM,sBAAsB,CAAC;AAE9B,UAAU,cAAc;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,UAAU,EACnB,GAAG,EAAE,cAAc,EACnB,GAAG,EAAE,aAAa,EAClB,WAAW,EAAE,gBAAgB,GAC5B,IAAI,CAwBN;AAED,mFAAmF;AACnF,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,UAAU,EACnB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,aAAa,EAClB,WAAW,EAAE,gBAAgB,GAC5B,IAAI,CAEN;AAED,kEAAkE;AAClE,wBAAgB,WAAW,CACzB,OAAO,EAAE,UAAU,EACnB,WAAW,EAAE,gBAAgB,EAC7B,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,GACR,IAAI,CAON;AAED,qEAAqE;AACrE,wBAAgB,UAAU,CACxB,OAAO,EAAE,UAAU,EACnB,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,EACV,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,GACR,IAAI,CAKN"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { FieldOrientation90, FieldOrientation180, FieldOrientation270, getFieldOrientationDegrees, } from "../elements/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Apply the same rotation transform `rotateImage` applies on a canvas, but
|
|
4
|
+
* via the emitter's group stack. The matching `restore()` closes every
|
|
5
|
+
* `<g>` opened here.
|
|
6
|
+
*/
|
|
7
|
+
export function rotateImage(emitter, img, pos, orientation) {
|
|
8
|
+
const degrees = getFieldOrientationDegrees(orientation);
|
|
9
|
+
if (degrees === 0)
|
|
10
|
+
return;
|
|
11
|
+
const radians = (degrees * Math.PI) / 180;
|
|
12
|
+
const { width, height } = img;
|
|
13
|
+
emitter.translate(pos.x, pos.y);
|
|
14
|
+
emitter.rotate(radians);
|
|
15
|
+
emitter.translate(-pos.x, -pos.y);
|
|
16
|
+
switch (orientation) {
|
|
17
|
+
case FieldOrientation90:
|
|
18
|
+
emitter.translate(0, -height);
|
|
19
|
+
break;
|
|
20
|
+
case FieldOrientation180:
|
|
21
|
+
emitter.translate(-width, -height);
|
|
22
|
+
break;
|
|
23
|
+
case FieldOrientation270:
|
|
24
|
+
emitter.translate(-width, 0);
|
|
25
|
+
break;
|
|
26
|
+
default:
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/** Width-and-height variant matching `rotateForOrientation` on the canvas side. */
|
|
31
|
+
export function rotateForOrientation(emitter, width, height, pos, orientation) {
|
|
32
|
+
rotateImage(emitter, { width, height }, pos, orientation);
|
|
33
|
+
}
|
|
34
|
+
/** SVG analogue of `rotateAbout` — pure rotation about (x, y). */
|
|
35
|
+
export function rotateAbout(emitter, orientation, x, y) {
|
|
36
|
+
const degrees = getFieldOrientationDegrees(orientation);
|
|
37
|
+
if (degrees === 0)
|
|
38
|
+
return;
|
|
39
|
+
const radians = (degrees * Math.PI) / 180;
|
|
40
|
+
emitter.translate(x, y);
|
|
41
|
+
emitter.rotate(radians);
|
|
42
|
+
emitter.translate(-x, -y);
|
|
43
|
+
}
|
|
44
|
+
/** SVG analogue of `scaleAbout` — non-uniform scale about (x, y). */
|
|
45
|
+
export function scaleAbout(emitter, sx, sy, x, y) {
|
|
46
|
+
if (sx === 1 && sy === 1)
|
|
47
|
+
return;
|
|
48
|
+
emitter.translate(x, y);
|
|
49
|
+
emitter.scale(sx, sy);
|
|
50
|
+
emitter.translate(-x, -y);
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=transform.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zebrash/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Engine for the @zebrash family — pure-TS ZPL parser + renderer. Internal package; consume @zebrash/node or @zebrash/browser.",
|
|
5
5
|
"homepage": "https://github.com/Fountain-Bio/zebrash-ts#readme",
|
|
6
6
|
"bugs": "https://github.com/Fountain-Bio/zebrash-ts/issues",
|