emf-converter 3.5.1 → 4.1.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/README.md +4 -2
- package/dist/index.js +293 -74
- package/dist/index.mjs +293 -74
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -206,7 +206,7 @@ Everything below is verified against output painted by Windows itself; `src/gdi-
|
|
|
206
206
|
|
|
207
207
|
### Supported records
|
|
208
208
|
|
|
209
|
-
- **EMF**:
|
|
209
|
+
- **EMF**: Record playback includes logical palettes (`PALETTEINDEX`, `DIBPALETTEINDEX`, `PALETTERGB`, `DIB_PAL_COLORS`), `EMR_ALPHABLEND` (Windows' exact integer blend), `EMR_TRANSPARENTBLT`, `EMR_MASKBLT`, `EMR_PLGBLT`, `EMR_SETDIBITSTODEVICE`, `EMR_GRADIENTFILL` (rectangles and triangles), `EMR_FILLRGN` / `EMR_FRAMERGN` / `EMR_INVERTRGN` / `EMR_PAINTRGN`, `EMR_EXTFLOODFILL`, `EMR_ANGLEARC`, `EMR_POLYDRAW(16)`, `EMR_FLATTENPATH` / `EMR_WIDENPATH` / `EMR_ABORTPATH`, alongside shapes, paths, `EMR_EXTTEXTOUTA/W`, `EMR_POLYTEXTOUTA/W`, `EMR_SMALLTEXTOUT`, text justification, blits, clipping and transforms. Colour space, ICM, OpenGL, escape and font-driver records are consumed without effect, as on a Windows display.
|
|
210
210
|
- **EMF+**: every record in MS-EMFPLUS (Beziers, cardinal curves, regions, containers, save/restore, compositing mode, rendering origin, text contrast, `StrokeFillPath`, the terminal-server `SetTSGraphics` / `SetTSClip`, `MultiFormat*` played as GDI+ plays them), with 32-bit, compressed 16-bit and relative point data, and every object type (solid, hatch, texture and gradient brushes; pens with caps, joins, dash styles, dash caps, compound lines and custom line caps; paths, regions, bitmap and metafile images, fonts, string formats, image attributes). Several encodings follow what GDI+ actually does where it differs from MS-EMFPLUS (relative points, `StrokeFillPath`, `SetTSGraphics`, `SetTSClip`).
|
|
211
211
|
- **WMF**: META_ANIMATEPALETTE, META_ARC, META_BITBLT, META_CHORD, META_CREATEBITMAP, META_CREATEBITMAPINDIRECT, META_CREATEBRUSH, META_CREATEBRUSHINDIRECT, META_CREATEFONTINDIRECT, META_CREATEPALETTE, META_CREATEPATTERNBRUSH, META_CREATEPENINDIRECT, META_CREATEREGION, META_DELETEOBJECT, META_DIBBITBLT, META_DIBCREATEPATTERNBRUSH, META_DIBSTRETCHBLT, META_ELLIPSE, META_EOF, META_ESCAPE, META_EXCLUDECLIPRECT, META_EXTFLOODFILL, META_EXTTEXTOUT, META_FILLREGION, META_FLOODFILL, META_FRAMEREGION, META_INTERSECTCLIPRECT, META_INVERTREGION, META_LINETO, META_MOVETO, META_OFFSETCLIPRGN, META_OFFSETVIEWPORTORG, META_OFFSETWINDOWORG, META_PAINTREGION, META_PATBLT, META_PIE, META_POLYGON, META_POLYLINE, META_POLYPOLYGON, META_REALIZEPALETTE, META_RECTANGLE, META_RESIZEPALETTE, META_RESTOREDC, META_ROUNDRECT, META_SAVEDC, META_SCALEVIEWPORTEXT, META_SCALEWINDOWEXT, META_SELECTCLIPREGION, META_SELECTOBJECT, META_SELECTPALETTE, META_SETBKCOLOR, META_SETBKMODE, META_SETDIBTODEV, META_SETLAYOUT, META_SETMAPMODE, META_SETMAPPERFLAGS, META_SETPALENTRIES, META_SETPIXEL, META_SETPOLYFILLMODE, META_SETRELABS, META_SETROP2, META_SETSTRETCHBLTMODE, META_SETTEXTALIGN, META_SETTEXTCHAREXTRA, META_SETTEXTCOLOR, META_SETTEXTJUSTIFICATION, META_SETVIEWPORTEXT, META_SETVIEWPORTORG, META_SETWINDOWEXT, META_SETWINDOWORG, META_STRETCHBLT, META_STRETCHDIB, META_TEXTOUT. Where Windows no longer plays a record the way MS-WMF describes it (Win16 device bitmaps in META_BITBLT / META_STRETCHBLT, META_CREATEPATTERNBRUSH, banded META_SETDIBTODEV), the converter follows Windows.
|
|
212
212
|
|
|
@@ -214,7 +214,9 @@ Everything below is verified against output painted by Windows itself; `src/gdi-
|
|
|
214
214
|
|
|
215
215
|
Everything is measured against output painted by Windows itself; `src/gdi-parity.fixture.test.ts` holds the exact per-fixture bounds.
|
|
216
216
|
|
|
217
|
-
- **
|
|
217
|
+
- **Colour adjustment and image effects**: `EMR_SETCOLORADJUSTMENT` is read but not yet applied, and the `HALFTONE` stretch mode is not bit-exact. EMF+ image effects (`SerializableObject`: blur, sharpen, colour matrix and the like) are not applied, so the image is drawn without the effect.
|
|
218
|
+
- **Pen transforms**: EMF+ pen transforms support uniform scaling with rotation and translation; nonuniform scaling and skew remain unsupported.
|
|
219
|
+
- **Text**: ANSI, multi-string and small-text records now render; the Windows text fixtures retain a few glyph-edge differences and a `PolyTextOut` C1 control-glyph difference (under 0.1% of pixels; bounds in `src/emf-text-records.fixture.test.ts`). ANSI decoding uses the host's `TextDecoder` for common Windows code pages; unsupported encodings (including Johab and OEM on standard runtimes) fall back to Windows-1252. Vertical `ETO_PDY` advances require the `fonts` engine. Without `fonts`, SVG text measurements use deterministic estimates, so precise justification requires supplying the matching fonts.
|
|
218
220
|
- **Wide pens and paths**: flat-capped GDI pens 7 px and wider can differ by a few pixels at round joins, and dashed wide Bezier curves follow `WidenPath` (which Windows' direct drawing does not quite match); at most 0.2% of pixels on the fixtures. `EMR_WIDENPATH` does not reproduce the extra inner join triangles GDI's own `WidenPath` emits (visible only when the widened outline is itself stroked). EMF+ 1-pixel antialiased lines can differ by one antialiasing sample at their ends, some closed widened outlines by one sample along an edge, and Inset or compound pens on closed figures are approximate.
|
|
219
221
|
- **GM_COMPATIBLE recordings**: EMF files do not record the graphics mode, and Windows plays RoundRect, Arc, Chord, Pie and null-pen Ellipse records back differently from how a GM_COMPATIBLE application drew them on screen; the converter follows Windows' playback.
|
|
220
222
|
- **Small EMF+ residuals**: rotated `HighQualityBicubic` `DrawImage` edge pixels (0.14%), one-level differences at exact half-level `Blend` knots, and a few pixels of a metafile nested in `DrawImage` under a scale.
|
package/dist/index.js
CHANGED
|
@@ -493,6 +493,23 @@ function totalGlyphAdvance(dx) {
|
|
|
493
493
|
}
|
|
494
494
|
return sum;
|
|
495
495
|
}
|
|
496
|
+
function applyTextJustification(advances, characters, extra, count) {
|
|
497
|
+
const result = advances.slice();
|
|
498
|
+
if (extra === 0 || count <= 0) {
|
|
499
|
+
return result;
|
|
500
|
+
}
|
|
501
|
+
let seen = 0;
|
|
502
|
+
for (let i = 0; i < result.length && i < characters.length; i++) {
|
|
503
|
+
if (characters[i] !== 32) {
|
|
504
|
+
continue;
|
|
505
|
+
}
|
|
506
|
+
const before = Math.round(seen * extra / count);
|
|
507
|
+
seen++;
|
|
508
|
+
const after = Math.round(seen * extra / count);
|
|
509
|
+
result[i] += after - before;
|
|
510
|
+
}
|
|
511
|
+
return result;
|
|
512
|
+
}
|
|
496
513
|
function alignmentStartOffset(totalWidth, align) {
|
|
497
514
|
if (align === "center") {
|
|
498
515
|
return -totalWidth / 2;
|
|
@@ -629,8 +646,11 @@ var EMR_PAINTRGN = 74;
|
|
|
629
646
|
var EMR_MASKBLT = 78;
|
|
630
647
|
var EMR_PLGBLT = 79;
|
|
631
648
|
var EMR_SETDIBITSTODEVICE = 80;
|
|
649
|
+
var EMR_EXTTEXTOUTA = 83;
|
|
632
650
|
var EMR_POLYPOLYLINE16 = 90;
|
|
633
651
|
var EMR_POLYDRAW16 = 92;
|
|
652
|
+
var EMR_POLYTEXTOUTA = 96;
|
|
653
|
+
var EMR_POLYTEXTOUTW = 97;
|
|
634
654
|
var EMR_CREATECOLORSPACE = 99;
|
|
635
655
|
var EMR_SETCOLORSPACE = 100;
|
|
636
656
|
var EMR_DELETECOLORSPACE = 101;
|
|
@@ -639,6 +659,7 @@ var EMR_GLSBOUNDEDRECORD = 103;
|
|
|
639
659
|
var EMR_PIXELFORMAT = 104;
|
|
640
660
|
var EMR_DRAWESCAPE = 105;
|
|
641
661
|
var EMR_EXTESCAPE = 106;
|
|
662
|
+
var EMR_SMALLTEXTOUT = 108;
|
|
642
663
|
var EMR_FORCEUFIMAPPING = 109;
|
|
643
664
|
var EMR_NAMEDESCAPE = 110;
|
|
644
665
|
var EMR_COLORCORRECTPALETTE = 111;
|
|
@@ -13102,7 +13123,7 @@ function parseEmfPlusPenObject(view, dataOff, recDataSize, textureCache, cacheKe
|
|
|
13102
13123
|
o += 4;
|
|
13103
13124
|
return v;
|
|
13104
13125
|
};
|
|
13105
|
-
const pen = { kind: "plus-pen", color: "rgba(0,0,0,1)", width: penWidth
|
|
13126
|
+
const pen = { kind: "plus-pen", color: "rgba(0,0,0,1)", width: penWidth, dashStyle: 0 };
|
|
13106
13127
|
if (penFlags & PEN_TRANSFORM) {
|
|
13107
13128
|
if (o + 24 <= end) {
|
|
13108
13129
|
pen.transform = [0, 4, 8, 12, 16, 20].map((k) => view.getFloat32(o + k, true));
|
|
@@ -14938,19 +14959,7 @@ var SvgContext = class {
|
|
|
14938
14959
|
}
|
|
14939
14960
|
}
|
|
14940
14961
|
// ---- text ---------------------------------------------------------------
|
|
14941
|
-
measurer() {
|
|
14942
|
-
if (this.measureCtx === void 0) {
|
|
14943
|
-
const temp = createTempCanvas(1, 1);
|
|
14944
|
-
this.measureCtx = temp && !isSoftwareRaster(temp.canvas) ? temp.ctx : null;
|
|
14945
|
-
}
|
|
14946
|
-
return this.measureCtx;
|
|
14947
|
-
}
|
|
14948
14962
|
measureText(text) {
|
|
14949
|
-
const real = this.shadow ?? this.measurer();
|
|
14950
|
-
if (real) {
|
|
14951
|
-
real.font = this.state.font;
|
|
14952
|
-
return real.measureText(text);
|
|
14953
|
-
}
|
|
14954
14963
|
const size = parseFont(this.state.font).size;
|
|
14955
14964
|
return { width: estimateTextWidth(text, size) };
|
|
14956
14965
|
}
|
|
@@ -20955,10 +20964,109 @@ function handleEmfGdiBlendBlitRecord(rCtx, recType, offset, dataOff, recSize) {
|
|
|
20955
20964
|
}
|
|
20956
20965
|
}
|
|
20957
20966
|
|
|
20967
|
+
// src/emf-ansi.ts
|
|
20968
|
+
var CP1252_HIGH2 = [
|
|
20969
|
+
8364,
|
|
20970
|
+
129,
|
|
20971
|
+
8218,
|
|
20972
|
+
402,
|
|
20973
|
+
8222,
|
|
20974
|
+
8230,
|
|
20975
|
+
8224,
|
|
20976
|
+
8225,
|
|
20977
|
+
710,
|
|
20978
|
+
8240,
|
|
20979
|
+
352,
|
|
20980
|
+
8249,
|
|
20981
|
+
338,
|
|
20982
|
+
141,
|
|
20983
|
+
381,
|
|
20984
|
+
143,
|
|
20985
|
+
144,
|
|
20986
|
+
8216,
|
|
20987
|
+
8217,
|
|
20988
|
+
8220,
|
|
20989
|
+
8221,
|
|
20990
|
+
8226,
|
|
20991
|
+
8211,
|
|
20992
|
+
8212,
|
|
20993
|
+
732,
|
|
20994
|
+
8482,
|
|
20995
|
+
353,
|
|
20996
|
+
8250,
|
|
20997
|
+
339,
|
|
20998
|
+
157,
|
|
20999
|
+
382,
|
|
21000
|
+
376
|
|
21001
|
+
];
|
|
21002
|
+
function ansiToCode(b, charSet) {
|
|
21003
|
+
return charSet === 2 ? b : b >= 128 && b <= 159 ? CP1252_HIGH2[b - 128] : b;
|
|
21004
|
+
}
|
|
21005
|
+
var CHARSET_ENCODING = {
|
|
21006
|
+
0: "windows-1252",
|
|
21007
|
+
1: "windows-1252",
|
|
21008
|
+
128: "shift_jis",
|
|
21009
|
+
129: "euc-kr",
|
|
21010
|
+
130: "johab",
|
|
21011
|
+
134: "gbk",
|
|
21012
|
+
136: "big5",
|
|
21013
|
+
161: "windows-1253",
|
|
21014
|
+
162: "windows-1254",
|
|
21015
|
+
163: "windows-1258",
|
|
21016
|
+
177: "windows-1255",
|
|
21017
|
+
178: "windows-1256",
|
|
21018
|
+
186: "windows-1257",
|
|
21019
|
+
204: "windows-1251",
|
|
21020
|
+
222: "windows-874",
|
|
21021
|
+
238: "windows-1250",
|
|
21022
|
+
255: "ibm437"
|
|
21023
|
+
};
|
|
21024
|
+
function decodeAnsiRecord(bytes, charSet) {
|
|
21025
|
+
if (charSet === 2) {
|
|
21026
|
+
return { codes: [...bytes], byteLengths: bytes.map(() => 1) };
|
|
21027
|
+
}
|
|
21028
|
+
const label = CHARSET_ENCODING[charSet] ?? "windows-1252";
|
|
21029
|
+
try {
|
|
21030
|
+
const decoder = new TextDecoder(label, { fatal: true });
|
|
21031
|
+
const replacementDecoder = new TextDecoder(label);
|
|
21032
|
+
const source = new Uint8Array(bytes);
|
|
21033
|
+
const codes = [];
|
|
21034
|
+
const byteLengths = [];
|
|
21035
|
+
let i = 0;
|
|
21036
|
+
while (i < bytes.length) {
|
|
21037
|
+
let decoded = null;
|
|
21038
|
+
let consumed = 1;
|
|
21039
|
+
for (let n = 1; n <= Math.min(4, bytes.length - i); n++) {
|
|
21040
|
+
try {
|
|
21041
|
+
const candidate = decoder.decode(source.subarray(i, i + n));
|
|
21042
|
+
if (candidate.length > 0) {
|
|
21043
|
+
decoded = candidate;
|
|
21044
|
+
consumed = n;
|
|
21045
|
+
break;
|
|
21046
|
+
}
|
|
21047
|
+
} catch {
|
|
21048
|
+
}
|
|
21049
|
+
}
|
|
21050
|
+
if (decoded === null) {
|
|
21051
|
+
decoded = replacementDecoder.decode(source.subarray(i, i + 1));
|
|
21052
|
+
}
|
|
21053
|
+
for (let j = 0; j < decoded.length; j++) {
|
|
21054
|
+
codes.push(decoded.charCodeAt(j));
|
|
21055
|
+
byteLengths.push(j === decoded.length - 1 ? consumed : 0);
|
|
21056
|
+
}
|
|
21057
|
+
i += consumed;
|
|
21058
|
+
}
|
|
21059
|
+
return { codes, byteLengths };
|
|
21060
|
+
} catch {
|
|
21061
|
+
return { codes: bytes.map((b) => ansiToCode(b, charSet)), byteLengths: bytes.map(() => 1) };
|
|
21062
|
+
}
|
|
21063
|
+
}
|
|
21064
|
+
|
|
20958
21065
|
// src/gdi-text-render.ts
|
|
20959
21066
|
var ETO_OPAQUE = 2;
|
|
20960
21067
|
var ETO_CLIPPED = 4;
|
|
20961
21068
|
var ETO_GLYPH_INDEX = 16;
|
|
21069
|
+
var ETO_IGNORELANGUAGE = 4096;
|
|
20962
21070
|
var ETO_PDY = 8192;
|
|
20963
21071
|
var GRAY_INV_GAMMA = 0.43;
|
|
20964
21072
|
var GRAY_BLACK_ON_WHITE = [255, 255, 240, 233, 225, 217, 208, 199, 189, 178, 167, 154, 140, 124, 104, 77, 0];
|
|
@@ -21039,15 +21147,24 @@ function fillDeviceQuad(ctx, pts, color, clip) {
|
|
|
21039
21147
|
ctx.fill();
|
|
21040
21148
|
ctx.restore();
|
|
21041
21149
|
}
|
|
21150
|
+
function isC1Control(code) {
|
|
21151
|
+
return code >= 128 && code <= 159;
|
|
21152
|
+
}
|
|
21153
|
+
function suppressC1Glyph(run, index) {
|
|
21154
|
+
return !run.glyphIndices && !(run.options & ETO_IGNORELANGUAGE) && isC1Control(run.codes[index]);
|
|
21155
|
+
}
|
|
21042
21156
|
function layoutGdiRun(font, run) {
|
|
21043
21157
|
const n = run.codes.length;
|
|
21044
21158
|
const glyphs = run.codes.map((c) => run.glyphIndices ? c : font.glyphIndex(c));
|
|
21045
|
-
|
|
21159
|
+
let adv = [];
|
|
21046
21160
|
for (let i = 0; i < n; i++) {
|
|
21047
21161
|
adv.push(
|
|
21048
|
-
run.dx && i < run.dx.length ? run.dx[i] : run.matrix ? font.rotatedAdvance(glyphs[i]) : font.advance(glyphs[i])
|
|
21162
|
+
suppressC1Glyph(run, i) && !run.dx ? 0 : run.dx && i < run.dx.length ? run.dx[i] : run.matrix ? font.rotatedAdvance(glyphs[i]) : font.advance(glyphs[i])
|
|
21049
21163
|
);
|
|
21050
21164
|
}
|
|
21165
|
+
if (!run.dx && run.textJustification && !run.glyphIndices) {
|
|
21166
|
+
adv = applyTextJustification(adv, run.codes, run.textJustification.extra, run.textJustification.count);
|
|
21167
|
+
}
|
|
21051
21168
|
const advY = [];
|
|
21052
21169
|
for (let i = 0; i < n; i++) {
|
|
21053
21170
|
advY.push(run.dy && i < run.dy.length ? run.dy[i] : 0);
|
|
@@ -21090,9 +21207,11 @@ function layoutGdiRun(font, run) {
|
|
|
21090
21207
|
const subX = font.gridFit ? 0 : Math.round((origin.x - ox) * 64);
|
|
21091
21208
|
const oy = Math.round(origin.y);
|
|
21092
21209
|
origins.push({ x: ox + subX / 64, y: oy, along, down });
|
|
21093
|
-
|
|
21094
|
-
|
|
21095
|
-
|
|
21210
|
+
if (!suppressC1Glyph(run, i)) {
|
|
21211
|
+
const g = font.glyph(glyphs[i], gm, subX);
|
|
21212
|
+
if (g.bitmap) {
|
|
21213
|
+
placed.push({ x: ox + g.bitmap.left, y: oy - g.bitmap.top, bitmap: g.bitmap });
|
|
21214
|
+
}
|
|
21096
21215
|
}
|
|
21097
21216
|
along += adv[i];
|
|
21098
21217
|
down += advY[i];
|
|
@@ -21177,21 +21296,25 @@ function paintGdiTextRun(ctx, font, run, fontFamilyMap) {
|
|
|
21177
21296
|
}
|
|
21178
21297
|
function emitSvgRun(ctx, font, run, glyphs, origins, fontFamilyMap) {
|
|
21179
21298
|
let text = "";
|
|
21299
|
+
const visibleOrigins = [];
|
|
21180
21300
|
for (let i = 0; i < origins.length; i++) {
|
|
21181
21301
|
const code = run.glyphIndices ? font.charForGlyph(glyphs[i]) : run.codes[i];
|
|
21302
|
+
if (!run.glyphIndices && !(run.options & ETO_IGNORELANGUAGE) && isC1Control(code)) continue;
|
|
21182
21303
|
text += String.fromCharCode(code);
|
|
21304
|
+
visibleOrigins.push(origins[i]);
|
|
21183
21305
|
}
|
|
21306
|
+
if (text.length === 0) return;
|
|
21184
21307
|
const m = run.matrix;
|
|
21185
21308
|
const ttf = font.ttf;
|
|
21186
21309
|
const bold = font.syntheticBold || ttf.weightClass >= 600;
|
|
21187
21310
|
const italic = font.syntheticItalic || (ttf.fsSelection & 1) !== 0 || (ttf.macStyle & 2) !== 0;
|
|
21188
|
-
const first =
|
|
21311
|
+
const first = visibleOrigins[0];
|
|
21189
21312
|
ctx.fillGlyphRun({
|
|
21190
21313
|
text,
|
|
21191
21314
|
// Upright runs: device positions. Rotated runs: offsets along/across
|
|
21192
21315
|
// the baseline from the first glyph, under the frame matrix.
|
|
21193
|
-
xs:
|
|
21194
|
-
ys:
|
|
21316
|
+
xs: visibleOrigins.map((o) => m ? o.along - first.along : o.x),
|
|
21317
|
+
ys: visibleOrigins.map((o) => m ? o.down - first.down : o.y),
|
|
21195
21318
|
matrix: m ? [m[0], m[1], m[2], m[3], first.x, first.y] : null,
|
|
21196
21319
|
scaleX: font.ppemX !== font.ppem ? font.ppemX / font.ppem : 1,
|
|
21197
21320
|
fontFamily: mapFontFamily(ttf.family, fontFamilyMap),
|
|
@@ -21362,6 +21485,7 @@ function drawGdiTextCall(ctx, fonts, state, call) {
|
|
|
21362
21485
|
y: ref.y,
|
|
21363
21486
|
dx: call.dx ? call.dx.map((v) => v * advanceScale) : null,
|
|
21364
21487
|
dy: call.dy ? call.dy.map((v) => -v * fontScale) : null,
|
|
21488
|
+
textJustification: call.textJustification ? { ...call.textJustification, extra: call.textJustification.extra * advanceScale } : void 0,
|
|
21365
21489
|
textAlign: state.textAlign,
|
|
21366
21490
|
textColor: state.textColor,
|
|
21367
21491
|
bkColor: state.bkColor,
|
|
@@ -21498,11 +21622,11 @@ function readDxArray(view, offset, dataOff, nChars, viewEnd) {
|
|
|
21498
21622
|
}
|
|
21499
21623
|
const dx = [];
|
|
21500
21624
|
for (let i = 0; i < nChars; i++) {
|
|
21501
|
-
dx.push(view.
|
|
21625
|
+
dx.push(view.getInt32(start + i * 4, true));
|
|
21502
21626
|
}
|
|
21503
21627
|
return dx;
|
|
21504
21628
|
}
|
|
21505
|
-
function drawWithFontEngine(rCtx, offset, dataOff, nChars, offString) {
|
|
21629
|
+
function drawWithFontEngine(rCtx, offset, dataOff, nChars, offString, viewEnd) {
|
|
21506
21630
|
const { ctx, view, state } = rCtx;
|
|
21507
21631
|
const fonts = rCtx.fonts;
|
|
21508
21632
|
if (!fonts) {
|
|
@@ -21514,7 +21638,7 @@ function drawWithFontEngine(rCtx, offset, dataOff, nChars, offString) {
|
|
|
21514
21638
|
codes.push(view.getUint16(offset + offString + i * 2, true));
|
|
21515
21639
|
}
|
|
21516
21640
|
const pdy = (options & ETO_PDY) !== 0;
|
|
21517
|
-
const raw = readDxArray(view, offset, dataOff, pdy ? nChars * 2 : nChars,
|
|
21641
|
+
const raw = readDxArray(view, offset, dataOff, pdy ? nChars * 2 : nChars, viewEnd);
|
|
21518
21642
|
let dx = raw;
|
|
21519
21643
|
let dy = null;
|
|
21520
21644
|
if (raw && pdy) {
|
|
@@ -21539,7 +21663,8 @@ function drawWithFontEngine(rCtx, offset, dataOff, nChars, offString) {
|
|
|
21539
21663
|
},
|
|
21540
21664
|
dx,
|
|
21541
21665
|
dy,
|
|
21542
|
-
matrix: gdiDeviceMatrix(rCtx)
|
|
21666
|
+
matrix: gdiDeviceMatrix(rCtx),
|
|
21667
|
+
textJustification: state.textJustification
|
|
21543
21668
|
});
|
|
21544
21669
|
if (!adv) {
|
|
21545
21670
|
return false;
|
|
@@ -21551,12 +21676,12 @@ function drawWithFontEngine(rCtx, offset, dataOff, nChars, offString) {
|
|
|
21551
21676
|
return true;
|
|
21552
21677
|
}
|
|
21553
21678
|
function horizontalAlign(textAlign) {
|
|
21679
|
+
if ((textAlign & 6) === 6) {
|
|
21680
|
+
return "center";
|
|
21681
|
+
}
|
|
21554
21682
|
if (textAlign & 2) {
|
|
21555
21683
|
return "right";
|
|
21556
21684
|
}
|
|
21557
|
-
if (textAlign & 6) {
|
|
21558
|
-
return "center";
|
|
21559
|
-
}
|
|
21560
21685
|
return "left";
|
|
21561
21686
|
}
|
|
21562
21687
|
function verticalBaseline(textAlign) {
|
|
@@ -21598,11 +21723,11 @@ function handleExtTextOutW(rCtx, offset, dataOff, recSize) {
|
|
|
21598
21723
|
const refY = view.getInt32(dataOff + 32, true);
|
|
21599
21724
|
const nChars = view.getUint32(dataOff + 36, true);
|
|
21600
21725
|
const offString = view.getUint32(dataOff + 40, true);
|
|
21601
|
-
const viewEnd = view.byteLength;
|
|
21602
|
-
if (nChars === 0 || offString === 0 ||
|
|
21726
|
+
const viewEnd = Math.min(view.byteLength, offset + recSize);
|
|
21727
|
+
if (nChars === 0 || offString === 0 || nChars > Math.floor((viewEnd - offset - offString) / 2)) {
|
|
21603
21728
|
return true;
|
|
21604
21729
|
}
|
|
21605
|
-
if (drawWithFontEngine(rCtx, offset, dataOff, nChars, offString)) {
|
|
21730
|
+
if (drawWithFontEngine(rCtx, offset, dataOff, nChars, offString, viewEnd)) {
|
|
21606
21731
|
return true;
|
|
21607
21732
|
}
|
|
21608
21733
|
const text = readUtf16LE(view, offset + offString, nChars);
|
|
@@ -21617,8 +21742,16 @@ function handleExtTextOutW(rCtx, offset, dataOff, recSize) {
|
|
|
21617
21742
|
const align = horizontalAlign(state.textAlign);
|
|
21618
21743
|
ctx.textBaseline = verticalBaseline(state.textAlign);
|
|
21619
21744
|
ctx.textAlign = align === "center" ? "center" : align === "right" ? "right" : "left";
|
|
21620
|
-
const
|
|
21621
|
-
const
|
|
21745
|
+
const pdy = (view.getUint32(dataOff + 44, true) & ETO_PDY) !== 0;
|
|
21746
|
+
const dxRaw = readDxArray(view, offset, dataOff, pdy ? nChars * 2 : nChars, viewEnd);
|
|
21747
|
+
const dxLogical = dxRaw && pdy ? dxRaw.filter((_, i) => i % 2 === 0) : dxRaw;
|
|
21748
|
+
let dxDevice = dxLogical ? dxLogical.map((v) => advanceScale !== null ? v * advanceScale : gmw(rCtx, v)) : null;
|
|
21749
|
+
if (!dxDevice && state.textJustification && !pdy) {
|
|
21750
|
+
const units = text.split("");
|
|
21751
|
+
const natural = units.map((ch) => ctx.measureText(ch).width);
|
|
21752
|
+
const scale = advanceScale ?? gmw(rCtx, 1);
|
|
21753
|
+
dxDevice = applyTextJustification(natural, units.map((ch) => ch.charCodeAt(0)), state.textJustification.extra * scale, state.textJustification.count);
|
|
21754
|
+
}
|
|
21622
21755
|
if (dxDevice) {
|
|
21623
21756
|
ctx.textAlign = "left";
|
|
21624
21757
|
}
|
|
@@ -21651,10 +21784,118 @@ function handleExtTextOutW(rCtx, offset, dataOff, recSize) {
|
|
|
21651
21784
|
}
|
|
21652
21785
|
return true;
|
|
21653
21786
|
}
|
|
21787
|
+
function drawTextCall(rCtx, x, y, codes, options, rect, dx) {
|
|
21788
|
+
const n = codes.length;
|
|
21789
|
+
const offString = 76;
|
|
21790
|
+
const offDx = offString + n * 2 + 3 & -4;
|
|
21791
|
+
const dxCount = dx ? dx.length : 0;
|
|
21792
|
+
const size = offDx + dxCount * 4;
|
|
21793
|
+
const view = new DataView(new ArrayBuffer(size));
|
|
21794
|
+
view.setUint32(44, n, true);
|
|
21795
|
+
view.setUint32(48, offString, true);
|
|
21796
|
+
view.setUint32(52, options, true);
|
|
21797
|
+
view.setInt32(36, x, true);
|
|
21798
|
+
view.setInt32(40, y, true);
|
|
21799
|
+
view.setInt32(56, rect[0], true);
|
|
21800
|
+
view.setInt32(60, rect[1], true);
|
|
21801
|
+
view.setInt32(64, rect[2], true);
|
|
21802
|
+
view.setInt32(68, rect[3], true);
|
|
21803
|
+
view.setUint32(72, dx ? offDx : 0, true);
|
|
21804
|
+
for (let i = 0; i < n; i++) view.setUint16(offString + i * 2, codes[i], true);
|
|
21805
|
+
if (dx) for (let i = 0; i < dxCount; i++) view.setInt32(offDx + i * 4, dx[i], true);
|
|
21806
|
+
const child = { ...rCtx, view };
|
|
21807
|
+
handleExtTextOutW(child, 0, 8, size);
|
|
21808
|
+
}
|
|
21809
|
+
function readAnsiRecord(view, start, count, charSet) {
|
|
21810
|
+
return decodeAnsiRecord(Array.from({ length: count }, (_, i) => view.getUint8(start + i)), charSet);
|
|
21811
|
+
}
|
|
21812
|
+
function collapseAnsiDx(dx, byteLengths, pdy) {
|
|
21813
|
+
if (!dx) return null;
|
|
21814
|
+
const result = [];
|
|
21815
|
+
let at = 0;
|
|
21816
|
+
for (const bytes of byteLengths) {
|
|
21817
|
+
let x = 0, y = 0;
|
|
21818
|
+
for (let i = 0; i < bytes; i++) {
|
|
21819
|
+
x += dx[at + i * (pdy ? 2 : 1)] ?? 0;
|
|
21820
|
+
if (pdy) y += dx[at + i * 2 + 1] ?? 0;
|
|
21821
|
+
}
|
|
21822
|
+
result.push(x);
|
|
21823
|
+
if (pdy) result.push(y);
|
|
21824
|
+
at += bytes * (pdy ? 2 : 1);
|
|
21825
|
+
}
|
|
21826
|
+
return result;
|
|
21827
|
+
}
|
|
21828
|
+
function handlePolyText(rCtx, offset, dataOff, recSize, wide) {
|
|
21829
|
+
const { view, state } = rCtx;
|
|
21830
|
+
const end = Math.min(view.byteLength, offset + recSize);
|
|
21831
|
+
if (recSize < 40) return true;
|
|
21832
|
+
const count = view.getUint32(dataOff + 28, true);
|
|
21833
|
+
const arrayStart = dataOff + 32;
|
|
21834
|
+
if (count > Math.floor((end - arrayStart) / 40)) return true;
|
|
21835
|
+
const charSet = state.fontDetails?.charSet ?? 1;
|
|
21836
|
+
for (let i = 0; i < count; i++) {
|
|
21837
|
+
const e = arrayStart + i * 40;
|
|
21838
|
+
const x = view.getInt32(e, true), y = view.getInt32(e + 4, true);
|
|
21839
|
+
const n = view.getUint32(e + 8, true), offString = view.getUint32(e + 12, true);
|
|
21840
|
+
const options = view.getUint32(e + 16, true);
|
|
21841
|
+
const rect = [view.getInt32(e + 20, true), view.getInt32(e + 24, true), view.getInt32(e + 28, true), view.getInt32(e + 32, true)];
|
|
21842
|
+
const offDx = view.getUint32(e + 36, true);
|
|
21843
|
+
const unit = wide ? 2 : 1;
|
|
21844
|
+
if (!offString || offString > end - offset || n > Math.floor((end - (offset + offString)) / unit)) continue;
|
|
21845
|
+
const ansi = wide ? null : readAnsiRecord(view, offset + offString, n, charSet);
|
|
21846
|
+
const codes = wide ? Array.from({ length: n }, (_, j) => view.getUint16(offset + offString + j * 2, true)) : ansi.codes;
|
|
21847
|
+
let dx = null;
|
|
21848
|
+
const pdy = (options & ETO_PDY) !== 0;
|
|
21849
|
+
const dxCount = n * (pdy ? 2 : 1);
|
|
21850
|
+
if (offDx && offDx <= end - offset && dxCount <= Math.floor((end - (offset + offDx)) / 4)) {
|
|
21851
|
+
dx = Array.from({ length: dxCount }, (_, j) => view.getInt32(offset + offDx + j * 4, true));
|
|
21852
|
+
if (!wide) dx = collapseAnsiDx(dx, ansi.byteLengths, pdy);
|
|
21853
|
+
}
|
|
21854
|
+
drawTextCall(rCtx, x, y, codes, options, rect, dx);
|
|
21855
|
+
}
|
|
21856
|
+
return true;
|
|
21857
|
+
}
|
|
21858
|
+
function handleSmallTextOut(rCtx, offset, dataOff, recSize) {
|
|
21859
|
+
const { view } = rCtx;
|
|
21860
|
+
const end = Math.min(view.byteLength, offset + recSize);
|
|
21861
|
+
if (recSize < 36) return true;
|
|
21862
|
+
const x = view.getInt32(dataOff, true), y = view.getInt32(dataOff + 4, true);
|
|
21863
|
+
const n = view.getUint32(dataOff + 8, true), options = view.getUint32(dataOff + 12, true);
|
|
21864
|
+
const small = (options & 512) !== 0, noRect = (options & 256) !== 0;
|
|
21865
|
+
const base = dataOff + 28 + (noRect ? 0 : 16);
|
|
21866
|
+
if (base > end || n > Math.floor((end - base) / (small ? 1 : 2))) return true;
|
|
21867
|
+
const rect = noRect ? [0, 0, 0, 0] : [view.getInt32(dataOff + 28, true), view.getInt32(dataOff + 32, true), view.getInt32(dataOff + 36, true), view.getInt32(dataOff + 40, true)];
|
|
21868
|
+
const codes = small ? Array.from({ length: n }, (_, i) => view.getUint8(base + i)) : Array.from({ length: n }, (_, i) => view.getUint16(base + i * 2, true));
|
|
21869
|
+
drawTextCall(rCtx, x, y, codes, options, rect, null);
|
|
21870
|
+
return true;
|
|
21871
|
+
}
|
|
21654
21872
|
function handleEmfGdiDrawTextRecord(rCtx, recType, offset, dataOff, recSize) {
|
|
21655
21873
|
if (recType === EMR_EXTTEXTOUTW) {
|
|
21656
21874
|
return handleExtTextOutW(rCtx, offset, dataOff, recSize);
|
|
21657
21875
|
}
|
|
21876
|
+
if (recType === EMR_EXTTEXTOUTA || recType === EMR_POLYTEXTOUTA || recType === EMR_POLYTEXTOUTW || recType === EMR_SMALLTEXTOUT) {
|
|
21877
|
+
if (recType === EMR_POLYTEXTOUTA || recType === EMR_POLYTEXTOUTW) return handlePolyText(rCtx, offset, dataOff, recSize, recType === EMR_POLYTEXTOUTW);
|
|
21878
|
+
if (recType === EMR_SMALLTEXTOUT) return handleSmallTextOut(rCtx, offset, dataOff, recSize);
|
|
21879
|
+
const { view, state } = rCtx;
|
|
21880
|
+
const end = Math.min(view.byteLength, offset + recSize);
|
|
21881
|
+
if (recSize < 76) return true;
|
|
21882
|
+
const x = view.getInt32(dataOff + 28, true), y = view.getInt32(dataOff + 32, true);
|
|
21883
|
+
const n = view.getUint32(dataOff + 36, true), strOff = view.getUint32(dataOff + 40, true), options = view.getUint32(dataOff + 44, true);
|
|
21884
|
+
if (!strOff || strOff > end - offset) return true;
|
|
21885
|
+
if (n > end - (offset + strOff)) return true;
|
|
21886
|
+
const ansi = readAnsiRecord(view, offset + strOff, n, state.fontDetails?.charSet ?? 1);
|
|
21887
|
+
const rect = [view.getInt32(dataOff + 48, true), view.getInt32(dataOff + 52, true), view.getInt32(dataOff + 56, true), view.getInt32(dataOff + 60, true)];
|
|
21888
|
+
const offDx = view.getUint32(dataOff + 64, true);
|
|
21889
|
+
let dx = null;
|
|
21890
|
+
const pdy = (options & ETO_PDY) !== 0;
|
|
21891
|
+
const dxCount = n * (pdy ? 2 : 1);
|
|
21892
|
+
if (offDx && offDx <= end - offset && dxCount <= Math.floor((end - (offset + offDx)) / 4)) {
|
|
21893
|
+
dx = Array.from({ length: dxCount }, (_, i) => view.getInt32(offset + offDx + i * 4, true));
|
|
21894
|
+
dx = collapseAnsiDx(dx, ansi.byteLengths, pdy);
|
|
21895
|
+
}
|
|
21896
|
+
drawTextCall(rCtx, x, y, ansi.codes, options, rect, dx);
|
|
21897
|
+
return true;
|
|
21898
|
+
}
|
|
21658
21899
|
return false;
|
|
21659
21900
|
}
|
|
21660
21901
|
|
|
@@ -23667,7 +23908,7 @@ function penDashArray(pen) {
|
|
|
23667
23908
|
return pattern.map((v) => v * w);
|
|
23668
23909
|
}
|
|
23669
23910
|
function applyPlusPenStyle(ctx, pen, widthScale = 1) {
|
|
23670
|
-
ctx.lineWidth = pen.width * widthScale;
|
|
23911
|
+
ctx.lineWidth = (pen.width || 1) * widthScale;
|
|
23671
23912
|
const dashes = penDashArray(pen);
|
|
23672
23913
|
ctx.lineCap = canvasLineCap(dashes.length > 0 ? pen.dashCap ?? 0 : pen.startCap ?? pen.endCap);
|
|
23673
23914
|
ctx.lineJoin = canvasLineJoin(pen.lineJoin);
|
|
@@ -23679,6 +23920,7 @@ function applyPlusPenStyle(ctx, pen, widthScale = 1) {
|
|
|
23679
23920
|
}
|
|
23680
23921
|
function strokePlusGeometry(rCtx, pen, buildPath, points, closedFigure = false) {
|
|
23681
23922
|
const { ctx } = rCtx;
|
|
23923
|
+
pen = applySimilarityPenTransform(pen);
|
|
23682
23924
|
const inset = !!pen && pen.alignment === 1 && closedFigure;
|
|
23683
23925
|
const widthScale = inset ? 2 : 1;
|
|
23684
23926
|
const stroke = (c) => {
|
|
@@ -23740,6 +23982,22 @@ function strokePlusGeometry(rCtx, pen, buildPath, points, closedFigure = false)
|
|
|
23740
23982
|
applyPlusWorldTransform(rCtx);
|
|
23741
23983
|
stroke(ctx);
|
|
23742
23984
|
}
|
|
23985
|
+
function applySimilarityPenTransform(pen) {
|
|
23986
|
+
if (!pen?.transform) {
|
|
23987
|
+
return pen;
|
|
23988
|
+
}
|
|
23989
|
+
if (isIdentity2(pen.transform)) {
|
|
23990
|
+
return { ...pen, transform: null };
|
|
23991
|
+
}
|
|
23992
|
+
const [a, b, c, d] = pen.transform;
|
|
23993
|
+
const sx = Math.hypot(a, b);
|
|
23994
|
+
const sy = Math.hypot(c, d);
|
|
23995
|
+
const dot = a * c + b * d;
|
|
23996
|
+
if (!Number.isFinite(sx + sy + dot) || !(sx > 0) || Math.abs(sx - sy) > 1e-6 * sx || Math.abs(dot) > 1e-6 * sx * sy) {
|
|
23997
|
+
return pen;
|
|
23998
|
+
}
|
|
23999
|
+
return { ...pen, width: pen.width === 0 ? 1 : pen.width * sx, transform: null };
|
|
24000
|
+
}
|
|
23743
24001
|
function strokeGdiplus(rCtx, pen, sampler, buildPath, device, size, mode, closedFigure) {
|
|
23744
24002
|
if (pen.transform && !isIdentity2(pen.transform)) {
|
|
23745
24003
|
return false;
|
|
@@ -24153,6 +24411,7 @@ function drawPlusStringWithEngine(rCtx, font, text, layoutX, layoutY, paint, ali
|
|
|
24153
24411
|
dx,
|
|
24154
24412
|
dy: null,
|
|
24155
24413
|
textAlign: 24,
|
|
24414
|
+
options: 0,
|
|
24156
24415
|
matrix: null});
|
|
24157
24416
|
if (!cov) {
|
|
24158
24417
|
return true;
|
|
@@ -26867,46 +27126,6 @@ function penIsNull(p) {
|
|
|
26867
27126
|
}
|
|
26868
27127
|
|
|
26869
27128
|
// src/wmf-text.ts
|
|
26870
|
-
var CP1252_HIGH2 = [
|
|
26871
|
-
8364,
|
|
26872
|
-
129,
|
|
26873
|
-
8218,
|
|
26874
|
-
402,
|
|
26875
|
-
8222,
|
|
26876
|
-
8230,
|
|
26877
|
-
8224,
|
|
26878
|
-
8225,
|
|
26879
|
-
710,
|
|
26880
|
-
8240,
|
|
26881
|
-
352,
|
|
26882
|
-
8249,
|
|
26883
|
-
338,
|
|
26884
|
-
141,
|
|
26885
|
-
381,
|
|
26886
|
-
143,
|
|
26887
|
-
144,
|
|
26888
|
-
8216,
|
|
26889
|
-
8217,
|
|
26890
|
-
8220,
|
|
26891
|
-
8221,
|
|
26892
|
-
8226,
|
|
26893
|
-
8211,
|
|
26894
|
-
8212,
|
|
26895
|
-
732,
|
|
26896
|
-
8482,
|
|
26897
|
-
353,
|
|
26898
|
-
8250,
|
|
26899
|
-
339,
|
|
26900
|
-
157,
|
|
26901
|
-
382,
|
|
26902
|
-
376
|
|
26903
|
-
];
|
|
26904
|
-
function ansiToCode(b, charSet) {
|
|
26905
|
-
if (charSet === 2) {
|
|
26906
|
-
return b;
|
|
26907
|
-
}
|
|
26908
|
-
return b >= 128 && b <= 159 ? CP1252_HIGH2[b - 128] : b;
|
|
26909
|
-
}
|
|
26910
27129
|
function fontAdvances(p, codes) {
|
|
26911
27130
|
const fonts = p.rCtx.fonts;
|
|
26912
27131
|
if (!fonts) {
|
package/dist/index.mjs
CHANGED
|
@@ -491,6 +491,23 @@ function totalGlyphAdvance(dx) {
|
|
|
491
491
|
}
|
|
492
492
|
return sum;
|
|
493
493
|
}
|
|
494
|
+
function applyTextJustification(advances, characters, extra, count) {
|
|
495
|
+
const result = advances.slice();
|
|
496
|
+
if (extra === 0 || count <= 0) {
|
|
497
|
+
return result;
|
|
498
|
+
}
|
|
499
|
+
let seen = 0;
|
|
500
|
+
for (let i = 0; i < result.length && i < characters.length; i++) {
|
|
501
|
+
if (characters[i] !== 32) {
|
|
502
|
+
continue;
|
|
503
|
+
}
|
|
504
|
+
const before = Math.round(seen * extra / count);
|
|
505
|
+
seen++;
|
|
506
|
+
const after = Math.round(seen * extra / count);
|
|
507
|
+
result[i] += after - before;
|
|
508
|
+
}
|
|
509
|
+
return result;
|
|
510
|
+
}
|
|
494
511
|
function alignmentStartOffset(totalWidth, align) {
|
|
495
512
|
if (align === "center") {
|
|
496
513
|
return -totalWidth / 2;
|
|
@@ -627,8 +644,11 @@ var EMR_PAINTRGN = 74;
|
|
|
627
644
|
var EMR_MASKBLT = 78;
|
|
628
645
|
var EMR_PLGBLT = 79;
|
|
629
646
|
var EMR_SETDIBITSTODEVICE = 80;
|
|
647
|
+
var EMR_EXTTEXTOUTA = 83;
|
|
630
648
|
var EMR_POLYPOLYLINE16 = 90;
|
|
631
649
|
var EMR_POLYDRAW16 = 92;
|
|
650
|
+
var EMR_POLYTEXTOUTA = 96;
|
|
651
|
+
var EMR_POLYTEXTOUTW = 97;
|
|
632
652
|
var EMR_CREATECOLORSPACE = 99;
|
|
633
653
|
var EMR_SETCOLORSPACE = 100;
|
|
634
654
|
var EMR_DELETECOLORSPACE = 101;
|
|
@@ -637,6 +657,7 @@ var EMR_GLSBOUNDEDRECORD = 103;
|
|
|
637
657
|
var EMR_PIXELFORMAT = 104;
|
|
638
658
|
var EMR_DRAWESCAPE = 105;
|
|
639
659
|
var EMR_EXTESCAPE = 106;
|
|
660
|
+
var EMR_SMALLTEXTOUT = 108;
|
|
640
661
|
var EMR_FORCEUFIMAPPING = 109;
|
|
641
662
|
var EMR_NAMEDESCAPE = 110;
|
|
642
663
|
var EMR_COLORCORRECTPALETTE = 111;
|
|
@@ -13100,7 +13121,7 @@ function parseEmfPlusPenObject(view, dataOff, recDataSize, textureCache, cacheKe
|
|
|
13100
13121
|
o += 4;
|
|
13101
13122
|
return v;
|
|
13102
13123
|
};
|
|
13103
|
-
const pen = { kind: "plus-pen", color: "rgba(0,0,0,1)", width: penWidth
|
|
13124
|
+
const pen = { kind: "plus-pen", color: "rgba(0,0,0,1)", width: penWidth, dashStyle: 0 };
|
|
13104
13125
|
if (penFlags & PEN_TRANSFORM) {
|
|
13105
13126
|
if (o + 24 <= end) {
|
|
13106
13127
|
pen.transform = [0, 4, 8, 12, 16, 20].map((k) => view.getFloat32(o + k, true));
|
|
@@ -14936,19 +14957,7 @@ var SvgContext = class {
|
|
|
14936
14957
|
}
|
|
14937
14958
|
}
|
|
14938
14959
|
// ---- text ---------------------------------------------------------------
|
|
14939
|
-
measurer() {
|
|
14940
|
-
if (this.measureCtx === void 0) {
|
|
14941
|
-
const temp = createTempCanvas(1, 1);
|
|
14942
|
-
this.measureCtx = temp && !isSoftwareRaster(temp.canvas) ? temp.ctx : null;
|
|
14943
|
-
}
|
|
14944
|
-
return this.measureCtx;
|
|
14945
|
-
}
|
|
14946
14960
|
measureText(text) {
|
|
14947
|
-
const real = this.shadow ?? this.measurer();
|
|
14948
|
-
if (real) {
|
|
14949
|
-
real.font = this.state.font;
|
|
14950
|
-
return real.measureText(text);
|
|
14951
|
-
}
|
|
14952
14961
|
const size = parseFont(this.state.font).size;
|
|
14953
14962
|
return { width: estimateTextWidth(text, size) };
|
|
14954
14963
|
}
|
|
@@ -20953,10 +20962,109 @@ function handleEmfGdiBlendBlitRecord(rCtx, recType, offset, dataOff, recSize) {
|
|
|
20953
20962
|
}
|
|
20954
20963
|
}
|
|
20955
20964
|
|
|
20965
|
+
// src/emf-ansi.ts
|
|
20966
|
+
var CP1252_HIGH2 = [
|
|
20967
|
+
8364,
|
|
20968
|
+
129,
|
|
20969
|
+
8218,
|
|
20970
|
+
402,
|
|
20971
|
+
8222,
|
|
20972
|
+
8230,
|
|
20973
|
+
8224,
|
|
20974
|
+
8225,
|
|
20975
|
+
710,
|
|
20976
|
+
8240,
|
|
20977
|
+
352,
|
|
20978
|
+
8249,
|
|
20979
|
+
338,
|
|
20980
|
+
141,
|
|
20981
|
+
381,
|
|
20982
|
+
143,
|
|
20983
|
+
144,
|
|
20984
|
+
8216,
|
|
20985
|
+
8217,
|
|
20986
|
+
8220,
|
|
20987
|
+
8221,
|
|
20988
|
+
8226,
|
|
20989
|
+
8211,
|
|
20990
|
+
8212,
|
|
20991
|
+
732,
|
|
20992
|
+
8482,
|
|
20993
|
+
353,
|
|
20994
|
+
8250,
|
|
20995
|
+
339,
|
|
20996
|
+
157,
|
|
20997
|
+
382,
|
|
20998
|
+
376
|
|
20999
|
+
];
|
|
21000
|
+
function ansiToCode(b, charSet) {
|
|
21001
|
+
return charSet === 2 ? b : b >= 128 && b <= 159 ? CP1252_HIGH2[b - 128] : b;
|
|
21002
|
+
}
|
|
21003
|
+
var CHARSET_ENCODING = {
|
|
21004
|
+
0: "windows-1252",
|
|
21005
|
+
1: "windows-1252",
|
|
21006
|
+
128: "shift_jis",
|
|
21007
|
+
129: "euc-kr",
|
|
21008
|
+
130: "johab",
|
|
21009
|
+
134: "gbk",
|
|
21010
|
+
136: "big5",
|
|
21011
|
+
161: "windows-1253",
|
|
21012
|
+
162: "windows-1254",
|
|
21013
|
+
163: "windows-1258",
|
|
21014
|
+
177: "windows-1255",
|
|
21015
|
+
178: "windows-1256",
|
|
21016
|
+
186: "windows-1257",
|
|
21017
|
+
204: "windows-1251",
|
|
21018
|
+
222: "windows-874",
|
|
21019
|
+
238: "windows-1250",
|
|
21020
|
+
255: "ibm437"
|
|
21021
|
+
};
|
|
21022
|
+
function decodeAnsiRecord(bytes, charSet) {
|
|
21023
|
+
if (charSet === 2) {
|
|
21024
|
+
return { codes: [...bytes], byteLengths: bytes.map(() => 1) };
|
|
21025
|
+
}
|
|
21026
|
+
const label = CHARSET_ENCODING[charSet] ?? "windows-1252";
|
|
21027
|
+
try {
|
|
21028
|
+
const decoder = new TextDecoder(label, { fatal: true });
|
|
21029
|
+
const replacementDecoder = new TextDecoder(label);
|
|
21030
|
+
const source = new Uint8Array(bytes);
|
|
21031
|
+
const codes = [];
|
|
21032
|
+
const byteLengths = [];
|
|
21033
|
+
let i = 0;
|
|
21034
|
+
while (i < bytes.length) {
|
|
21035
|
+
let decoded = null;
|
|
21036
|
+
let consumed = 1;
|
|
21037
|
+
for (let n = 1; n <= Math.min(4, bytes.length - i); n++) {
|
|
21038
|
+
try {
|
|
21039
|
+
const candidate = decoder.decode(source.subarray(i, i + n));
|
|
21040
|
+
if (candidate.length > 0) {
|
|
21041
|
+
decoded = candidate;
|
|
21042
|
+
consumed = n;
|
|
21043
|
+
break;
|
|
21044
|
+
}
|
|
21045
|
+
} catch {
|
|
21046
|
+
}
|
|
21047
|
+
}
|
|
21048
|
+
if (decoded === null) {
|
|
21049
|
+
decoded = replacementDecoder.decode(source.subarray(i, i + 1));
|
|
21050
|
+
}
|
|
21051
|
+
for (let j = 0; j < decoded.length; j++) {
|
|
21052
|
+
codes.push(decoded.charCodeAt(j));
|
|
21053
|
+
byteLengths.push(j === decoded.length - 1 ? consumed : 0);
|
|
21054
|
+
}
|
|
21055
|
+
i += consumed;
|
|
21056
|
+
}
|
|
21057
|
+
return { codes, byteLengths };
|
|
21058
|
+
} catch {
|
|
21059
|
+
return { codes: bytes.map((b) => ansiToCode(b, charSet)), byteLengths: bytes.map(() => 1) };
|
|
21060
|
+
}
|
|
21061
|
+
}
|
|
21062
|
+
|
|
20956
21063
|
// src/gdi-text-render.ts
|
|
20957
21064
|
var ETO_OPAQUE = 2;
|
|
20958
21065
|
var ETO_CLIPPED = 4;
|
|
20959
21066
|
var ETO_GLYPH_INDEX = 16;
|
|
21067
|
+
var ETO_IGNORELANGUAGE = 4096;
|
|
20960
21068
|
var ETO_PDY = 8192;
|
|
20961
21069
|
var GRAY_INV_GAMMA = 0.43;
|
|
20962
21070
|
var GRAY_BLACK_ON_WHITE = [255, 255, 240, 233, 225, 217, 208, 199, 189, 178, 167, 154, 140, 124, 104, 77, 0];
|
|
@@ -21037,15 +21145,24 @@ function fillDeviceQuad(ctx, pts, color, clip) {
|
|
|
21037
21145
|
ctx.fill();
|
|
21038
21146
|
ctx.restore();
|
|
21039
21147
|
}
|
|
21148
|
+
function isC1Control(code) {
|
|
21149
|
+
return code >= 128 && code <= 159;
|
|
21150
|
+
}
|
|
21151
|
+
function suppressC1Glyph(run, index) {
|
|
21152
|
+
return !run.glyphIndices && !(run.options & ETO_IGNORELANGUAGE) && isC1Control(run.codes[index]);
|
|
21153
|
+
}
|
|
21040
21154
|
function layoutGdiRun(font, run) {
|
|
21041
21155
|
const n = run.codes.length;
|
|
21042
21156
|
const glyphs = run.codes.map((c) => run.glyphIndices ? c : font.glyphIndex(c));
|
|
21043
|
-
|
|
21157
|
+
let adv = [];
|
|
21044
21158
|
for (let i = 0; i < n; i++) {
|
|
21045
21159
|
adv.push(
|
|
21046
|
-
run.dx && i < run.dx.length ? run.dx[i] : run.matrix ? font.rotatedAdvance(glyphs[i]) : font.advance(glyphs[i])
|
|
21160
|
+
suppressC1Glyph(run, i) && !run.dx ? 0 : run.dx && i < run.dx.length ? run.dx[i] : run.matrix ? font.rotatedAdvance(glyphs[i]) : font.advance(glyphs[i])
|
|
21047
21161
|
);
|
|
21048
21162
|
}
|
|
21163
|
+
if (!run.dx && run.textJustification && !run.glyphIndices) {
|
|
21164
|
+
adv = applyTextJustification(adv, run.codes, run.textJustification.extra, run.textJustification.count);
|
|
21165
|
+
}
|
|
21049
21166
|
const advY = [];
|
|
21050
21167
|
for (let i = 0; i < n; i++) {
|
|
21051
21168
|
advY.push(run.dy && i < run.dy.length ? run.dy[i] : 0);
|
|
@@ -21088,9 +21205,11 @@ function layoutGdiRun(font, run) {
|
|
|
21088
21205
|
const subX = font.gridFit ? 0 : Math.round((origin.x - ox) * 64);
|
|
21089
21206
|
const oy = Math.round(origin.y);
|
|
21090
21207
|
origins.push({ x: ox + subX / 64, y: oy, along, down });
|
|
21091
|
-
|
|
21092
|
-
|
|
21093
|
-
|
|
21208
|
+
if (!suppressC1Glyph(run, i)) {
|
|
21209
|
+
const g = font.glyph(glyphs[i], gm, subX);
|
|
21210
|
+
if (g.bitmap) {
|
|
21211
|
+
placed.push({ x: ox + g.bitmap.left, y: oy - g.bitmap.top, bitmap: g.bitmap });
|
|
21212
|
+
}
|
|
21094
21213
|
}
|
|
21095
21214
|
along += adv[i];
|
|
21096
21215
|
down += advY[i];
|
|
@@ -21175,21 +21294,25 @@ function paintGdiTextRun(ctx, font, run, fontFamilyMap) {
|
|
|
21175
21294
|
}
|
|
21176
21295
|
function emitSvgRun(ctx, font, run, glyphs, origins, fontFamilyMap) {
|
|
21177
21296
|
let text = "";
|
|
21297
|
+
const visibleOrigins = [];
|
|
21178
21298
|
for (let i = 0; i < origins.length; i++) {
|
|
21179
21299
|
const code = run.glyphIndices ? font.charForGlyph(glyphs[i]) : run.codes[i];
|
|
21300
|
+
if (!run.glyphIndices && !(run.options & ETO_IGNORELANGUAGE) && isC1Control(code)) continue;
|
|
21180
21301
|
text += String.fromCharCode(code);
|
|
21302
|
+
visibleOrigins.push(origins[i]);
|
|
21181
21303
|
}
|
|
21304
|
+
if (text.length === 0) return;
|
|
21182
21305
|
const m = run.matrix;
|
|
21183
21306
|
const ttf = font.ttf;
|
|
21184
21307
|
const bold = font.syntheticBold || ttf.weightClass >= 600;
|
|
21185
21308
|
const italic = font.syntheticItalic || (ttf.fsSelection & 1) !== 0 || (ttf.macStyle & 2) !== 0;
|
|
21186
|
-
const first =
|
|
21309
|
+
const first = visibleOrigins[0];
|
|
21187
21310
|
ctx.fillGlyphRun({
|
|
21188
21311
|
text,
|
|
21189
21312
|
// Upright runs: device positions. Rotated runs: offsets along/across
|
|
21190
21313
|
// the baseline from the first glyph, under the frame matrix.
|
|
21191
|
-
xs:
|
|
21192
|
-
ys:
|
|
21314
|
+
xs: visibleOrigins.map((o) => m ? o.along - first.along : o.x),
|
|
21315
|
+
ys: visibleOrigins.map((o) => m ? o.down - first.down : o.y),
|
|
21193
21316
|
matrix: m ? [m[0], m[1], m[2], m[3], first.x, first.y] : null,
|
|
21194
21317
|
scaleX: font.ppemX !== font.ppem ? font.ppemX / font.ppem : 1,
|
|
21195
21318
|
fontFamily: mapFontFamily(ttf.family, fontFamilyMap),
|
|
@@ -21360,6 +21483,7 @@ function drawGdiTextCall(ctx, fonts, state, call) {
|
|
|
21360
21483
|
y: ref.y,
|
|
21361
21484
|
dx: call.dx ? call.dx.map((v) => v * advanceScale) : null,
|
|
21362
21485
|
dy: call.dy ? call.dy.map((v) => -v * fontScale) : null,
|
|
21486
|
+
textJustification: call.textJustification ? { ...call.textJustification, extra: call.textJustification.extra * advanceScale } : void 0,
|
|
21363
21487
|
textAlign: state.textAlign,
|
|
21364
21488
|
textColor: state.textColor,
|
|
21365
21489
|
bkColor: state.bkColor,
|
|
@@ -21496,11 +21620,11 @@ function readDxArray(view, offset, dataOff, nChars, viewEnd) {
|
|
|
21496
21620
|
}
|
|
21497
21621
|
const dx = [];
|
|
21498
21622
|
for (let i = 0; i < nChars; i++) {
|
|
21499
|
-
dx.push(view.
|
|
21623
|
+
dx.push(view.getInt32(start + i * 4, true));
|
|
21500
21624
|
}
|
|
21501
21625
|
return dx;
|
|
21502
21626
|
}
|
|
21503
|
-
function drawWithFontEngine(rCtx, offset, dataOff, nChars, offString) {
|
|
21627
|
+
function drawWithFontEngine(rCtx, offset, dataOff, nChars, offString, viewEnd) {
|
|
21504
21628
|
const { ctx, view, state } = rCtx;
|
|
21505
21629
|
const fonts = rCtx.fonts;
|
|
21506
21630
|
if (!fonts) {
|
|
@@ -21512,7 +21636,7 @@ function drawWithFontEngine(rCtx, offset, dataOff, nChars, offString) {
|
|
|
21512
21636
|
codes.push(view.getUint16(offset + offString + i * 2, true));
|
|
21513
21637
|
}
|
|
21514
21638
|
const pdy = (options & ETO_PDY) !== 0;
|
|
21515
|
-
const raw = readDxArray(view, offset, dataOff, pdy ? nChars * 2 : nChars,
|
|
21639
|
+
const raw = readDxArray(view, offset, dataOff, pdy ? nChars * 2 : nChars, viewEnd);
|
|
21516
21640
|
let dx = raw;
|
|
21517
21641
|
let dy = null;
|
|
21518
21642
|
if (raw && pdy) {
|
|
@@ -21537,7 +21661,8 @@ function drawWithFontEngine(rCtx, offset, dataOff, nChars, offString) {
|
|
|
21537
21661
|
},
|
|
21538
21662
|
dx,
|
|
21539
21663
|
dy,
|
|
21540
|
-
matrix: gdiDeviceMatrix(rCtx)
|
|
21664
|
+
matrix: gdiDeviceMatrix(rCtx),
|
|
21665
|
+
textJustification: state.textJustification
|
|
21541
21666
|
});
|
|
21542
21667
|
if (!adv) {
|
|
21543
21668
|
return false;
|
|
@@ -21549,12 +21674,12 @@ function drawWithFontEngine(rCtx, offset, dataOff, nChars, offString) {
|
|
|
21549
21674
|
return true;
|
|
21550
21675
|
}
|
|
21551
21676
|
function horizontalAlign(textAlign) {
|
|
21677
|
+
if ((textAlign & 6) === 6) {
|
|
21678
|
+
return "center";
|
|
21679
|
+
}
|
|
21552
21680
|
if (textAlign & 2) {
|
|
21553
21681
|
return "right";
|
|
21554
21682
|
}
|
|
21555
|
-
if (textAlign & 6) {
|
|
21556
|
-
return "center";
|
|
21557
|
-
}
|
|
21558
21683
|
return "left";
|
|
21559
21684
|
}
|
|
21560
21685
|
function verticalBaseline(textAlign) {
|
|
@@ -21596,11 +21721,11 @@ function handleExtTextOutW(rCtx, offset, dataOff, recSize) {
|
|
|
21596
21721
|
const refY = view.getInt32(dataOff + 32, true);
|
|
21597
21722
|
const nChars = view.getUint32(dataOff + 36, true);
|
|
21598
21723
|
const offString = view.getUint32(dataOff + 40, true);
|
|
21599
|
-
const viewEnd = view.byteLength;
|
|
21600
|
-
if (nChars === 0 || offString === 0 ||
|
|
21724
|
+
const viewEnd = Math.min(view.byteLength, offset + recSize);
|
|
21725
|
+
if (nChars === 0 || offString === 0 || nChars > Math.floor((viewEnd - offset - offString) / 2)) {
|
|
21601
21726
|
return true;
|
|
21602
21727
|
}
|
|
21603
|
-
if (drawWithFontEngine(rCtx, offset, dataOff, nChars, offString)) {
|
|
21728
|
+
if (drawWithFontEngine(rCtx, offset, dataOff, nChars, offString, viewEnd)) {
|
|
21604
21729
|
return true;
|
|
21605
21730
|
}
|
|
21606
21731
|
const text = readUtf16LE(view, offset + offString, nChars);
|
|
@@ -21615,8 +21740,16 @@ function handleExtTextOutW(rCtx, offset, dataOff, recSize) {
|
|
|
21615
21740
|
const align = horizontalAlign(state.textAlign);
|
|
21616
21741
|
ctx.textBaseline = verticalBaseline(state.textAlign);
|
|
21617
21742
|
ctx.textAlign = align === "center" ? "center" : align === "right" ? "right" : "left";
|
|
21618
|
-
const
|
|
21619
|
-
const
|
|
21743
|
+
const pdy = (view.getUint32(dataOff + 44, true) & ETO_PDY) !== 0;
|
|
21744
|
+
const dxRaw = readDxArray(view, offset, dataOff, pdy ? nChars * 2 : nChars, viewEnd);
|
|
21745
|
+
const dxLogical = dxRaw && pdy ? dxRaw.filter((_, i) => i % 2 === 0) : dxRaw;
|
|
21746
|
+
let dxDevice = dxLogical ? dxLogical.map((v) => advanceScale !== null ? v * advanceScale : gmw(rCtx, v)) : null;
|
|
21747
|
+
if (!dxDevice && state.textJustification && !pdy) {
|
|
21748
|
+
const units = text.split("");
|
|
21749
|
+
const natural = units.map((ch) => ctx.measureText(ch).width);
|
|
21750
|
+
const scale = advanceScale ?? gmw(rCtx, 1);
|
|
21751
|
+
dxDevice = applyTextJustification(natural, units.map((ch) => ch.charCodeAt(0)), state.textJustification.extra * scale, state.textJustification.count);
|
|
21752
|
+
}
|
|
21620
21753
|
if (dxDevice) {
|
|
21621
21754
|
ctx.textAlign = "left";
|
|
21622
21755
|
}
|
|
@@ -21649,10 +21782,118 @@ function handleExtTextOutW(rCtx, offset, dataOff, recSize) {
|
|
|
21649
21782
|
}
|
|
21650
21783
|
return true;
|
|
21651
21784
|
}
|
|
21785
|
+
function drawTextCall(rCtx, x, y, codes, options, rect, dx) {
|
|
21786
|
+
const n = codes.length;
|
|
21787
|
+
const offString = 76;
|
|
21788
|
+
const offDx = offString + n * 2 + 3 & -4;
|
|
21789
|
+
const dxCount = dx ? dx.length : 0;
|
|
21790
|
+
const size = offDx + dxCount * 4;
|
|
21791
|
+
const view = new DataView(new ArrayBuffer(size));
|
|
21792
|
+
view.setUint32(44, n, true);
|
|
21793
|
+
view.setUint32(48, offString, true);
|
|
21794
|
+
view.setUint32(52, options, true);
|
|
21795
|
+
view.setInt32(36, x, true);
|
|
21796
|
+
view.setInt32(40, y, true);
|
|
21797
|
+
view.setInt32(56, rect[0], true);
|
|
21798
|
+
view.setInt32(60, rect[1], true);
|
|
21799
|
+
view.setInt32(64, rect[2], true);
|
|
21800
|
+
view.setInt32(68, rect[3], true);
|
|
21801
|
+
view.setUint32(72, dx ? offDx : 0, true);
|
|
21802
|
+
for (let i = 0; i < n; i++) view.setUint16(offString + i * 2, codes[i], true);
|
|
21803
|
+
if (dx) for (let i = 0; i < dxCount; i++) view.setInt32(offDx + i * 4, dx[i], true);
|
|
21804
|
+
const child = { ...rCtx, view };
|
|
21805
|
+
handleExtTextOutW(child, 0, 8, size);
|
|
21806
|
+
}
|
|
21807
|
+
function readAnsiRecord(view, start, count, charSet) {
|
|
21808
|
+
return decodeAnsiRecord(Array.from({ length: count }, (_, i) => view.getUint8(start + i)), charSet);
|
|
21809
|
+
}
|
|
21810
|
+
function collapseAnsiDx(dx, byteLengths, pdy) {
|
|
21811
|
+
if (!dx) return null;
|
|
21812
|
+
const result = [];
|
|
21813
|
+
let at = 0;
|
|
21814
|
+
for (const bytes of byteLengths) {
|
|
21815
|
+
let x = 0, y = 0;
|
|
21816
|
+
for (let i = 0; i < bytes; i++) {
|
|
21817
|
+
x += dx[at + i * (pdy ? 2 : 1)] ?? 0;
|
|
21818
|
+
if (pdy) y += dx[at + i * 2 + 1] ?? 0;
|
|
21819
|
+
}
|
|
21820
|
+
result.push(x);
|
|
21821
|
+
if (pdy) result.push(y);
|
|
21822
|
+
at += bytes * (pdy ? 2 : 1);
|
|
21823
|
+
}
|
|
21824
|
+
return result;
|
|
21825
|
+
}
|
|
21826
|
+
function handlePolyText(rCtx, offset, dataOff, recSize, wide) {
|
|
21827
|
+
const { view, state } = rCtx;
|
|
21828
|
+
const end = Math.min(view.byteLength, offset + recSize);
|
|
21829
|
+
if (recSize < 40) return true;
|
|
21830
|
+
const count = view.getUint32(dataOff + 28, true);
|
|
21831
|
+
const arrayStart = dataOff + 32;
|
|
21832
|
+
if (count > Math.floor((end - arrayStart) / 40)) return true;
|
|
21833
|
+
const charSet = state.fontDetails?.charSet ?? 1;
|
|
21834
|
+
for (let i = 0; i < count; i++) {
|
|
21835
|
+
const e = arrayStart + i * 40;
|
|
21836
|
+
const x = view.getInt32(e, true), y = view.getInt32(e + 4, true);
|
|
21837
|
+
const n = view.getUint32(e + 8, true), offString = view.getUint32(e + 12, true);
|
|
21838
|
+
const options = view.getUint32(e + 16, true);
|
|
21839
|
+
const rect = [view.getInt32(e + 20, true), view.getInt32(e + 24, true), view.getInt32(e + 28, true), view.getInt32(e + 32, true)];
|
|
21840
|
+
const offDx = view.getUint32(e + 36, true);
|
|
21841
|
+
const unit = wide ? 2 : 1;
|
|
21842
|
+
if (!offString || offString > end - offset || n > Math.floor((end - (offset + offString)) / unit)) continue;
|
|
21843
|
+
const ansi = wide ? null : readAnsiRecord(view, offset + offString, n, charSet);
|
|
21844
|
+
const codes = wide ? Array.from({ length: n }, (_, j) => view.getUint16(offset + offString + j * 2, true)) : ansi.codes;
|
|
21845
|
+
let dx = null;
|
|
21846
|
+
const pdy = (options & ETO_PDY) !== 0;
|
|
21847
|
+
const dxCount = n * (pdy ? 2 : 1);
|
|
21848
|
+
if (offDx && offDx <= end - offset && dxCount <= Math.floor((end - (offset + offDx)) / 4)) {
|
|
21849
|
+
dx = Array.from({ length: dxCount }, (_, j) => view.getInt32(offset + offDx + j * 4, true));
|
|
21850
|
+
if (!wide) dx = collapseAnsiDx(dx, ansi.byteLengths, pdy);
|
|
21851
|
+
}
|
|
21852
|
+
drawTextCall(rCtx, x, y, codes, options, rect, dx);
|
|
21853
|
+
}
|
|
21854
|
+
return true;
|
|
21855
|
+
}
|
|
21856
|
+
function handleSmallTextOut(rCtx, offset, dataOff, recSize) {
|
|
21857
|
+
const { view } = rCtx;
|
|
21858
|
+
const end = Math.min(view.byteLength, offset + recSize);
|
|
21859
|
+
if (recSize < 36) return true;
|
|
21860
|
+
const x = view.getInt32(dataOff, true), y = view.getInt32(dataOff + 4, true);
|
|
21861
|
+
const n = view.getUint32(dataOff + 8, true), options = view.getUint32(dataOff + 12, true);
|
|
21862
|
+
const small = (options & 512) !== 0, noRect = (options & 256) !== 0;
|
|
21863
|
+
const base = dataOff + 28 + (noRect ? 0 : 16);
|
|
21864
|
+
if (base > end || n > Math.floor((end - base) / (small ? 1 : 2))) return true;
|
|
21865
|
+
const rect = noRect ? [0, 0, 0, 0] : [view.getInt32(dataOff + 28, true), view.getInt32(dataOff + 32, true), view.getInt32(dataOff + 36, true), view.getInt32(dataOff + 40, true)];
|
|
21866
|
+
const codes = small ? Array.from({ length: n }, (_, i) => view.getUint8(base + i)) : Array.from({ length: n }, (_, i) => view.getUint16(base + i * 2, true));
|
|
21867
|
+
drawTextCall(rCtx, x, y, codes, options, rect, null);
|
|
21868
|
+
return true;
|
|
21869
|
+
}
|
|
21652
21870
|
function handleEmfGdiDrawTextRecord(rCtx, recType, offset, dataOff, recSize) {
|
|
21653
21871
|
if (recType === EMR_EXTTEXTOUTW) {
|
|
21654
21872
|
return handleExtTextOutW(rCtx, offset, dataOff, recSize);
|
|
21655
21873
|
}
|
|
21874
|
+
if (recType === EMR_EXTTEXTOUTA || recType === EMR_POLYTEXTOUTA || recType === EMR_POLYTEXTOUTW || recType === EMR_SMALLTEXTOUT) {
|
|
21875
|
+
if (recType === EMR_POLYTEXTOUTA || recType === EMR_POLYTEXTOUTW) return handlePolyText(rCtx, offset, dataOff, recSize, recType === EMR_POLYTEXTOUTW);
|
|
21876
|
+
if (recType === EMR_SMALLTEXTOUT) return handleSmallTextOut(rCtx, offset, dataOff, recSize);
|
|
21877
|
+
const { view, state } = rCtx;
|
|
21878
|
+
const end = Math.min(view.byteLength, offset + recSize);
|
|
21879
|
+
if (recSize < 76) return true;
|
|
21880
|
+
const x = view.getInt32(dataOff + 28, true), y = view.getInt32(dataOff + 32, true);
|
|
21881
|
+
const n = view.getUint32(dataOff + 36, true), strOff = view.getUint32(dataOff + 40, true), options = view.getUint32(dataOff + 44, true);
|
|
21882
|
+
if (!strOff || strOff > end - offset) return true;
|
|
21883
|
+
if (n > end - (offset + strOff)) return true;
|
|
21884
|
+
const ansi = readAnsiRecord(view, offset + strOff, n, state.fontDetails?.charSet ?? 1);
|
|
21885
|
+
const rect = [view.getInt32(dataOff + 48, true), view.getInt32(dataOff + 52, true), view.getInt32(dataOff + 56, true), view.getInt32(dataOff + 60, true)];
|
|
21886
|
+
const offDx = view.getUint32(dataOff + 64, true);
|
|
21887
|
+
let dx = null;
|
|
21888
|
+
const pdy = (options & ETO_PDY) !== 0;
|
|
21889
|
+
const dxCount = n * (pdy ? 2 : 1);
|
|
21890
|
+
if (offDx && offDx <= end - offset && dxCount <= Math.floor((end - (offset + offDx)) / 4)) {
|
|
21891
|
+
dx = Array.from({ length: dxCount }, (_, i) => view.getInt32(offset + offDx + i * 4, true));
|
|
21892
|
+
dx = collapseAnsiDx(dx, ansi.byteLengths, pdy);
|
|
21893
|
+
}
|
|
21894
|
+
drawTextCall(rCtx, x, y, ansi.codes, options, rect, dx);
|
|
21895
|
+
return true;
|
|
21896
|
+
}
|
|
21656
21897
|
return false;
|
|
21657
21898
|
}
|
|
21658
21899
|
|
|
@@ -23665,7 +23906,7 @@ function penDashArray(pen) {
|
|
|
23665
23906
|
return pattern.map((v) => v * w);
|
|
23666
23907
|
}
|
|
23667
23908
|
function applyPlusPenStyle(ctx, pen, widthScale = 1) {
|
|
23668
|
-
ctx.lineWidth = pen.width * widthScale;
|
|
23909
|
+
ctx.lineWidth = (pen.width || 1) * widthScale;
|
|
23669
23910
|
const dashes = penDashArray(pen);
|
|
23670
23911
|
ctx.lineCap = canvasLineCap(dashes.length > 0 ? pen.dashCap ?? 0 : pen.startCap ?? pen.endCap);
|
|
23671
23912
|
ctx.lineJoin = canvasLineJoin(pen.lineJoin);
|
|
@@ -23677,6 +23918,7 @@ function applyPlusPenStyle(ctx, pen, widthScale = 1) {
|
|
|
23677
23918
|
}
|
|
23678
23919
|
function strokePlusGeometry(rCtx, pen, buildPath, points, closedFigure = false) {
|
|
23679
23920
|
const { ctx } = rCtx;
|
|
23921
|
+
pen = applySimilarityPenTransform(pen);
|
|
23680
23922
|
const inset = !!pen && pen.alignment === 1 && closedFigure;
|
|
23681
23923
|
const widthScale = inset ? 2 : 1;
|
|
23682
23924
|
const stroke = (c) => {
|
|
@@ -23738,6 +23980,22 @@ function strokePlusGeometry(rCtx, pen, buildPath, points, closedFigure = false)
|
|
|
23738
23980
|
applyPlusWorldTransform(rCtx);
|
|
23739
23981
|
stroke(ctx);
|
|
23740
23982
|
}
|
|
23983
|
+
function applySimilarityPenTransform(pen) {
|
|
23984
|
+
if (!pen?.transform) {
|
|
23985
|
+
return pen;
|
|
23986
|
+
}
|
|
23987
|
+
if (isIdentity2(pen.transform)) {
|
|
23988
|
+
return { ...pen, transform: null };
|
|
23989
|
+
}
|
|
23990
|
+
const [a, b, c, d] = pen.transform;
|
|
23991
|
+
const sx = Math.hypot(a, b);
|
|
23992
|
+
const sy = Math.hypot(c, d);
|
|
23993
|
+
const dot = a * c + b * d;
|
|
23994
|
+
if (!Number.isFinite(sx + sy + dot) || !(sx > 0) || Math.abs(sx - sy) > 1e-6 * sx || Math.abs(dot) > 1e-6 * sx * sy) {
|
|
23995
|
+
return pen;
|
|
23996
|
+
}
|
|
23997
|
+
return { ...pen, width: pen.width === 0 ? 1 : pen.width * sx, transform: null };
|
|
23998
|
+
}
|
|
23741
23999
|
function strokeGdiplus(rCtx, pen, sampler, buildPath, device, size, mode, closedFigure) {
|
|
23742
24000
|
if (pen.transform && !isIdentity2(pen.transform)) {
|
|
23743
24001
|
return false;
|
|
@@ -24151,6 +24409,7 @@ function drawPlusStringWithEngine(rCtx, font, text, layoutX, layoutY, paint, ali
|
|
|
24151
24409
|
dx,
|
|
24152
24410
|
dy: null,
|
|
24153
24411
|
textAlign: 24,
|
|
24412
|
+
options: 0,
|
|
24154
24413
|
matrix: null});
|
|
24155
24414
|
if (!cov) {
|
|
24156
24415
|
return true;
|
|
@@ -26865,46 +27124,6 @@ function penIsNull(p) {
|
|
|
26865
27124
|
}
|
|
26866
27125
|
|
|
26867
27126
|
// src/wmf-text.ts
|
|
26868
|
-
var CP1252_HIGH2 = [
|
|
26869
|
-
8364,
|
|
26870
|
-
129,
|
|
26871
|
-
8218,
|
|
26872
|
-
402,
|
|
26873
|
-
8222,
|
|
26874
|
-
8230,
|
|
26875
|
-
8224,
|
|
26876
|
-
8225,
|
|
26877
|
-
710,
|
|
26878
|
-
8240,
|
|
26879
|
-
352,
|
|
26880
|
-
8249,
|
|
26881
|
-
338,
|
|
26882
|
-
141,
|
|
26883
|
-
381,
|
|
26884
|
-
143,
|
|
26885
|
-
144,
|
|
26886
|
-
8216,
|
|
26887
|
-
8217,
|
|
26888
|
-
8220,
|
|
26889
|
-
8221,
|
|
26890
|
-
8226,
|
|
26891
|
-
8211,
|
|
26892
|
-
8212,
|
|
26893
|
-
732,
|
|
26894
|
-
8482,
|
|
26895
|
-
353,
|
|
26896
|
-
8250,
|
|
26897
|
-
339,
|
|
26898
|
-
157,
|
|
26899
|
-
382,
|
|
26900
|
-
376
|
|
26901
|
-
];
|
|
26902
|
-
function ansiToCode(b, charSet) {
|
|
26903
|
-
if (charSet === 2) {
|
|
26904
|
-
return b;
|
|
26905
|
-
}
|
|
26906
|
-
return b >= 128 && b <= 159 ? CP1252_HIGH2[b - 128] : b;
|
|
26907
|
-
}
|
|
26908
27127
|
function fontAdvances(p, codes) {
|
|
26909
27128
|
const fonts = p.rCtx.fonts;
|
|
26910
27129
|
if (!fonts) {
|