reamkit 1.25.1 → 1.27.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 +12 -5
- package/dist/esm/pdf-reader/annot-draw.d.ts +65 -0
- package/dist/esm/pdf-reader/annot-draw.js +374 -0
- package/dist/esm/pdf-reader/annots.d.ts +3 -1
- package/dist/esm/pdf-reader/annots.js +18 -4
- package/dist/esm/pdf-reader/ccitt.d.ts +18 -0
- package/dist/esm/pdf-reader/ccitt.js +70 -2
- package/dist/esm/pdf-reader/cie-color.d.ts +33 -0
- package/dist/esm/pdf-reader/cie-color.js +112 -0
- package/dist/esm/pdf-reader/content.d.ts +47 -17
- package/dist/esm/pdf-reader/content.js +175 -14
- package/dist/esm/pdf-reader/display.d.ts +1 -1
- package/dist/esm/pdf-reader/display.js +21 -6
- package/dist/esm/pdf-reader/document.d.ts +6 -0
- package/dist/esm/pdf-reader/document.js +29 -1
- package/dist/esm/pdf-reader/embedded-fonts.d.ts +12 -3
- package/dist/esm/pdf-reader/embedded-fonts.js +22 -3
- package/dist/esm/pdf-reader/flow-build.d.ts +12 -4
- package/dist/esm/pdf-reader/flow-build.js +102 -8
- package/dist/esm/pdf-reader/font.js +97 -16
- package/dist/esm/pdf-reader/function.d.ts +16 -0
- package/dist/esm/pdf-reader/function.js +414 -0
- package/dist/esm/pdf-reader/glyph-names.d.ts +6 -0
- package/dist/esm/pdf-reader/glyph-names.js +408 -0
- package/dist/esm/pdf-reader/image-decode.d.ts +8 -4
- package/dist/esm/pdf-reader/image-decode.js +224 -12
- package/dist/esm/pdf-reader/images.d.ts +12 -0
- package/dist/esm/pdf-reader/images.js +109 -9
- package/dist/esm/pdf-reader/jbig2.d.ts +109 -0
- package/dist/esm/pdf-reader/jbig2.js +2606 -0
- package/dist/esm/pdf-reader/layout.d.ts +32 -0
- package/dist/esm/pdf-reader/layout.js +189 -40
- package/dist/esm/pdf-reader/lexer.d.ts +2 -0
- package/dist/esm/pdf-reader/lexer.js +4 -0
- package/dist/esm/pdf-reader/optional-content.d.ts +36 -0
- package/dist/esm/pdf-reader/optional-content.js +93 -0
- package/dist/esm/pdf-reader/reader.d.ts +5 -2
- package/dist/esm/pdf-reader/reader.js +80 -7
- package/dist/esm/pdf-reader/shading.d.ts +71 -6
- package/dist/esm/pdf-reader/shading.js +185 -15
- package/dist/esm/pdf-reader/standard-metrics.d.ts +8 -0
- package/dist/esm/pdf-reader/standard-metrics.js +18 -0
- package/dist/esm/pdf-reader/standard-widths.d.ts +20 -0
- package/dist/esm/pdf-reader/standard-widths.js +62 -0
- package/dist/esm/pdf-reader/tagged.js +204 -32
- package/dist/esm/pdf-reader/text-rules.d.ts +16 -0
- package/dist/esm/pdf-reader/text-rules.js +112 -0
- package/dist/esm/pdf-reader/text.js +78 -4
- package/dist/esm/pdf-reader/vector.d.ts +5 -0
- package/dist/esm/pdf-reader/vector.js +39 -25
- package/dist/esm/word/docx-writer.js +213 -23
- package/package.json +1 -1
|
@@ -43,10 +43,6 @@ var RASTER_MEDIA = {
|
|
|
43
43
|
ext: "jpeg",
|
|
44
44
|
contentType: "image/jpeg"
|
|
45
45
|
},
|
|
46
|
-
jpeg2000: {
|
|
47
|
-
ext: "jp2",
|
|
48
|
-
contentType: "image/jp2"
|
|
49
|
-
},
|
|
50
46
|
gif: {
|
|
51
47
|
ext: "gif",
|
|
52
48
|
contentType: "image/gif"
|
|
@@ -56,6 +52,18 @@ var RASTER_MEDIA = {
|
|
|
56
52
|
contentType: "image/bmp"
|
|
57
53
|
}
|
|
58
54
|
};
|
|
55
|
+
/**
|
|
56
|
+
* Why an image was not written, in terms of the image — for the loss report,
|
|
57
|
+
* which used to say "image bytes missing" whatever the reason, including for
|
|
58
|
+
* bytes that were all there and in a format the format does not admit.
|
|
59
|
+
*/
|
|
60
|
+
function imageRefusal(resource, state) {
|
|
61
|
+
const bytes = resource === void 0 ? void 0 : state.resources.get(resource);
|
|
62
|
+
if (!bytes) return "image bytes missing";
|
|
63
|
+
const format = detectImageFormat(bytes);
|
|
64
|
+
if (format === "jpeg2000") return "JPEG 2000 picture dropped — §15.2.14 admits no such image part, and no reader of a .docx shows one";
|
|
65
|
+
return `picture dropped — ${format ?? "an unrecognised"} is not an image part a .docx may carry`;
|
|
66
|
+
}
|
|
59
67
|
function mediaInfo(bytes) {
|
|
60
68
|
const raster = detectImageFormat(bytes);
|
|
61
69
|
if (raster) return RASTER_MEDIA[raster];
|
|
@@ -137,9 +145,7 @@ function writeDocx(flow) {
|
|
|
137
145
|
if (i === sections.length - 1) finalSectPr = sp;
|
|
138
146
|
else sectPrByClosingIndex.set(sec.endIndex - 1, sp);
|
|
139
147
|
});
|
|
140
|
-
flow.body
|
|
141
|
-
emitBlock(body, el, losses, state, docScope, sectPrByClosingIndex.get(idx));
|
|
142
|
-
});
|
|
148
|
+
emitBody(body, flow.body, losses, state, docScope, sectPrByClosingIndex);
|
|
143
149
|
if (finalSectPr) body.push(finalSectPr);
|
|
144
150
|
const documentXml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"><w:body>${body.join("")}</w:body></w:document>`;
|
|
145
151
|
const numberingPart = flow.body.some((el) => el.kind === "paragraph" && el.paragraph.properties.numbering !== void 0) && flow.numbering ? {
|
|
@@ -365,6 +371,59 @@ var docxWriter = {
|
|
|
365
371
|
supports: new Set([FEATURES.text]),
|
|
366
372
|
write: (doc) => writeDocx(doc)
|
|
367
373
|
};
|
|
374
|
+
/**
|
|
375
|
+
* The document body, with consecutive floating drawings gathered into one
|
|
376
|
+
* carrier paragraph.
|
|
377
|
+
*
|
|
378
|
+
* §20.4.2.3 — a floating drawing is placed by its anchor and not by where its
|
|
379
|
+
* paragraph lands, so the paragraph is only somewhere for the anchor to hang
|
|
380
|
+
* from. Given one paragraph EACH, a page of placed artwork is that many
|
|
381
|
+
* paragraphs of flow: 160F-2019.pdf's one-page form is 355 drawings, and Word
|
|
382
|
+
* ran it to sixteen pages of empty lines with the artwork anchored to them.
|
|
383
|
+
* Gathered, the page costs one paragraph however much is placed on it.
|
|
384
|
+
*
|
|
385
|
+
* A block that closes a section keeps its own paragraph — the section break
|
|
386
|
+
* rides that paragraph's properties, and it must be the last one in the
|
|
387
|
+
* section.
|
|
388
|
+
*/
|
|
389
|
+
function emitBody(out, blocks, losses, state, scope, sectPrByClosingIndex) {
|
|
390
|
+
let carried = [];
|
|
391
|
+
const flush = () => {
|
|
392
|
+
if (carried.length > 0) out.push(`<w:p>${carried.join("")}</w:p>`);
|
|
393
|
+
carried = [];
|
|
394
|
+
};
|
|
395
|
+
blocks.forEach((el, idx) => {
|
|
396
|
+
const closing = sectPrByClosingIndex.get(idx);
|
|
397
|
+
const anchored = closing === void 0 ? floatingDrawingRun(el, losses, state, scope) : void 0;
|
|
398
|
+
if (anchored !== void 0) {
|
|
399
|
+
carried.push(anchored);
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
flush();
|
|
403
|
+
emitBlock(out, el, losses, state, scope, closing);
|
|
404
|
+
});
|
|
405
|
+
flush();
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* The run holding one block's drawing, when that block states where on the page
|
|
409
|
+
* it goes — `undefined` for everything that flows, which the ordinary block
|
|
410
|
+
* emitter handles.
|
|
411
|
+
*/
|
|
412
|
+
function floatingDrawingRun(el, losses, state, scope) {
|
|
413
|
+
if (el.kind === "image" && el.image.float) {
|
|
414
|
+
const drawing = drawingXml(el.image.resource, el.image.width, el.image.height, el.image.altText, state, scope, el.image.float);
|
|
415
|
+
if (drawing === "") {
|
|
416
|
+
losses.push({
|
|
417
|
+
severity: "dropped",
|
|
418
|
+
feature: FEATURES.images,
|
|
419
|
+
detail: imageRefusal(el.image.resource, state)
|
|
420
|
+
});
|
|
421
|
+
return "";
|
|
422
|
+
}
|
|
423
|
+
return `<w:r>${drawing}</w:r>`;
|
|
424
|
+
}
|
|
425
|
+
if (el.kind === "shape" && el.shape.float) return `<w:r>${shapeDrawingXml(el.shape, losses, state, scope)}</w:r>`;
|
|
426
|
+
}
|
|
368
427
|
function emitBlock(out, el, losses, state, scope, closingSectPr) {
|
|
369
428
|
if (el.kind === "paragraph") {
|
|
370
429
|
out.push(paragraphXml(el.paragraph, state, scope, closingSectPr));
|
|
@@ -376,13 +435,13 @@ function emitBlock(out, el, losses, state, scope, closingSectPr) {
|
|
|
376
435
|
return;
|
|
377
436
|
}
|
|
378
437
|
if (el.kind === "image") {
|
|
379
|
-
const drawing = drawingXml(el.image.resource, el.image.width, el.image.height, el.image.altText, state, scope);
|
|
438
|
+
const drawing = drawingXml(el.image.resource, el.image.width, el.image.height, el.image.altText, state, scope, el.image.float);
|
|
380
439
|
if (drawing) out.push(`<w:p>${pPrWithSect(el.image.paragraphProperties, closingSectPr)}<w:r>${drawing}</w:r></w:p>`);
|
|
381
440
|
else {
|
|
382
441
|
losses.push({
|
|
383
442
|
severity: "dropped",
|
|
384
443
|
feature: FEATURES.images,
|
|
385
|
-
detail:
|
|
444
|
+
detail: imageRefusal(el.image.resource, state)
|
|
386
445
|
});
|
|
387
446
|
if (closingSectPr) out.push(`<w:p><w:pPr>${closingSectPr}</w:pPr></w:p>`);
|
|
388
447
|
}
|
|
@@ -425,14 +484,82 @@ function chartBlockXml(chart, state, scope, losses) {
|
|
|
425
484
|
const id = ++state.drawingSeq;
|
|
426
485
|
return `<w:drawing><wp:inline xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing"><wp:extent cx="${cx}" cy="${cy}"/><wp:docPr id="${id}" name="Chart ${id}"${chart.altText ? ` descr="${escapeAttr(chart.altText)}"` : ""}/><a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"><a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/chart"><c:chart xmlns:c="http://schemas.openxmlformats.org/drawingml/2006/chart" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" r:id="${relId}"/></a:graphicData></a:graphic></wp:inline></w:drawing>`;
|
|
427
486
|
}
|
|
428
|
-
|
|
487
|
+
/**
|
|
488
|
+
* §20.4.2.3 — the frame a drawing is placed by: `wp:anchor` where the block
|
|
489
|
+
* states where on the page it belongs, `wp:inline` where it belongs in the run
|
|
490
|
+
* of text.
|
|
491
|
+
*
|
|
492
|
+
* Everything used to be inline. The document model has carried the anchor since
|
|
493
|
+
* the reader learned to read one, and the PDF reconstruction fills it in for
|
|
494
|
+
* every line and rule it lifts off a page — and all of it was thrown away here,
|
|
495
|
+
* so a converted page came out as a column of drawings stacked in document
|
|
496
|
+
* order. S2.pdf's two colour wheels marched down the left margin as eighteen
|
|
497
|
+
* separate wedges; 160F-2019.pdf's one-page form ran to sixteen pages, because
|
|
498
|
+
* each of its 355 drawings took a paragraph of its own in the flow.
|
|
499
|
+
*
|
|
500
|
+
* The attribute list is not decoration: CT_Anchor requires `simplePos`,
|
|
501
|
+
* `relativeHeight`, `behindDoc`, `locked`, `layoutInCell` and `allowOverlap`,
|
|
502
|
+
* and the children are a SEQUENCE — position, extent, wrap, docPr, graphic —
|
|
503
|
+
* that Word refuses out of order.
|
|
504
|
+
*
|
|
505
|
+
* @param float The placement, or `undefined` for a drawing that flows.
|
|
506
|
+
* @param cx Width in EMU.
|
|
507
|
+
* @param cy Height in EMU.
|
|
508
|
+
* @param head The `wp:docPr` (and anything else) that precedes the graphic.
|
|
509
|
+
* @param graphic The `a:graphic` element the frame carries.
|
|
510
|
+
*/
|
|
511
|
+
function drawingFrame(float, cx, cy, head, graphic, state, rotation60k) {
|
|
512
|
+
const WP_NS = " xmlns:wp=\"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing\"";
|
|
513
|
+
if (!float) return `<w:drawing><wp:inline${WP_NS}><wp:extent cx="${cx}" cy="${cy}"/>${head}${graphic}</wp:inline></w:drawing>`;
|
|
514
|
+
const emu = (pt) => Number.isFinite(pt) ? Math.round((pt ?? 0) * EMU_PER_PT) : 0;
|
|
515
|
+
const d = float.wrapDist;
|
|
516
|
+
const dist = ` distT="${String(emu(d?.topPt))}" distB="${String(emu(d?.bottomPt))}" distL="${String(emu(d?.leftPt))}" distR="${String(emu(d?.rightPt))}"`;
|
|
517
|
+
const z = float.zOrder ?? ++state.drawingSeq;
|
|
518
|
+
const attrs = `${dist} simplePos="0" relativeHeight="${String(z)}" behindDoc="${float.behind ? "1" : "0"}" locked="0" layoutInCell="${float.inCell === false ? "0" : "1"}" allowOverlap="1"`;
|
|
519
|
+
const pos = (tag, p, fallback) => {
|
|
520
|
+
const from = p?.relativeFrom ?? fallback;
|
|
521
|
+
const body = p?.align !== void 0 ? `<wp:align>${p.align}</wp:align>` : `<wp:posOffset>${String(emu(p?.offsetPt))}</wp:posOffset>`;
|
|
522
|
+
return `<${tag} relativeFrom="${escapeAttr(from)}">${body}</${tag}>`;
|
|
523
|
+
};
|
|
524
|
+
const side = ` wrapText="${float.wrapSide ?? "bothSides"}"`;
|
|
525
|
+
const wrap = float.wrap === "square" ? `<wp:wrapSquare${side}/>` : float.wrap === "tight" ? `<wp:wrapTight${side}/>` : float.wrap === "through" ? `<wp:wrapThrough${side}/>` : float.wrap === "topAndBottom" || float.wrap === "notBeside" ? "<wp:wrapTopAndBottom/>" : "<wp:wrapNone/>";
|
|
526
|
+
return `<w:drawing><wp:anchor${WP_NS}${attrs}><wp:simplePos x="0" y="0"/>` + pos("wp:positionH", float.posH, "column") + pos("wp:positionV", float.posV, "paragraph") + `<wp:extent cx="${cx}" cy="${cy}"/>` + effectExtentXml(cx, cy, rotation60k) + wrap + head + graphic + "</wp:anchor></w:drawing>";
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* §20.4.2.3 `wp:effectExtent` — how far the drawing reaches PAST the extent.
|
|
530
|
+
*
|
|
531
|
+
* `wp:extent` is the shape's own size, unrotated: fdo75722-dml.docx states
|
|
532
|
+
* 1964690 × 1240790 there and repeats it in `a:ext`, then carries the turn's
|
|
533
|
+
* overhang here (l 198120, t 433705, r 199390, b 430530). Written as zeroes —
|
|
534
|
+
* which they always were — a renderer reserves the flat box for a shape that
|
|
535
|
+
* draws across the turned one, and 160F-2019.pdf's "Nature", set on its side
|
|
536
|
+
* down the middle of a column, came back lying flat across it.
|
|
537
|
+
*
|
|
538
|
+
* A box `w × h` turned by θ about its centre spans `|w·cosθ| + |h·sinθ|`
|
|
539
|
+
* across and `|w·sinθ| + |h·cosθ|` down; half of each growth hangs off each
|
|
540
|
+
* side. A turn that makes the box no wider hangs off by nothing.
|
|
541
|
+
*/
|
|
542
|
+
function effectExtentXml(cx, cy, rotation60k) {
|
|
543
|
+
const none = "<wp:effectExtent l=\"0\" t=\"0\" r=\"0\" b=\"0\"/>";
|
|
544
|
+
if (rotation60k === void 0 || !Number.isFinite(rotation60k) || rotation60k % 216e5 === 0) return none;
|
|
545
|
+
const rad = rotation60k / 6e4 * Math.PI / 180;
|
|
546
|
+
const c = Math.abs(Math.cos(rad));
|
|
547
|
+
const s = Math.abs(Math.sin(rad));
|
|
548
|
+
const overhang = (span, turned) => Math.max(0, Math.round((turned - span) / 2));
|
|
549
|
+
const h = overhang(cx, cx * c + cy * s);
|
|
550
|
+
const v = overhang(cy, cx * s + cy * c);
|
|
551
|
+
return h === 0 && v === 0 ? none : `<wp:effectExtent l="${String(h)}" t="${String(v)}" r="${String(h)}" b="${String(v)}"/>`;
|
|
552
|
+
}
|
|
553
|
+
function drawingXml(resource, widthPt, heightPt, altText, state, scope, float) {
|
|
429
554
|
if (resource === void 0) return "";
|
|
430
555
|
const relId = mediaRelId(resource, state, scope);
|
|
431
556
|
if (relId === void 0) return "";
|
|
432
557
|
const cx = Math.round(widthPt * EMU_PER_PT);
|
|
433
558
|
const cy = Math.round(heightPt * EMU_PER_PT);
|
|
434
559
|
const id = ++state.drawingSeq;
|
|
435
|
-
|
|
560
|
+
const descr = altText ? ` descr="${escapeAttr(altText)}"` : "";
|
|
561
|
+
const graphic = `<a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"><a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:pic xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:nvPicPr><pic:cNvPr id="${id}" name="Image ${id}"/><pic:cNvPicPr/></pic:nvPicPr><pic:blipFill><a:blip r:embed="${relId}"/><a:stretch><a:fillRect/></a:stretch></pic:blipFill><pic:spPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="${cx}" cy="${cy}"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom></pic:spPr></pic:pic></a:graphicData></a:graphic>`;
|
|
562
|
+
return drawingFrame(float, cx, cy, `<wp:docPr id="${id}" name="Image ${id}"${descr}/>`, graphic, state);
|
|
436
563
|
}
|
|
437
564
|
var WPS_URI = "http://schemas.microsoft.com/office/word/2010/wordprocessingShape";
|
|
438
565
|
function shapeDrawingXml(shape, losses, state, scope) {
|
|
@@ -442,7 +569,8 @@ function shapeDrawingXml(shape, losses, state, scope) {
|
|
|
442
569
|
const descr = shape.altText ? ` descr="${escapeAttr(shape.altText)}"` : "";
|
|
443
570
|
const spPr = `<wps:spPr>${xfrmXml(shape.transform, cx, cy)}${geomXml(shape.geometry)}${fillXml(shape.fill)}${shape.line ? lineXml(shape.line) : ""}</wps:spPr>`;
|
|
444
571
|
const txbx = shape.text ? txbxXml(shape.text, losses, state, scope) : "";
|
|
445
|
-
|
|
572
|
+
const graphic = `<a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"><a:graphicData uri="${WPS_URI}"><wps:wsp xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape"><wps:cNvSpPr/>` + spPr + txbx + bodyPrXml(shape.text) + "</wps:wsp></a:graphicData></a:graphic>";
|
|
573
|
+
return drawingFrame(shape.float, cx, cy, `<wp:docPr id="${id}" name="Shape ${id}"${descr}/>`, graphic, state, shape.transform?.rotation60k);
|
|
446
574
|
}
|
|
447
575
|
function xfrmXml(t, cx, cy) {
|
|
448
576
|
return `<a:xfrm${t?.rotation60k !== void 0 ? ` rot="${t.rotation60k}"` : ""}${t?.flipH ? " flipH=\"1\"" : ""}${t?.flipV ? " flipV=\"1\"" : ""}><a:off x="0" y="0"/><a:ext cx="${cx}" cy="${cy}"/></a:xfrm>`;
|
|
@@ -488,8 +616,8 @@ function geomXml(g) {
|
|
|
488
616
|
case "close": return "<a:close/>";
|
|
489
617
|
}
|
|
490
618
|
}).join("");
|
|
491
|
-
const w = c1(g.custom.pathWidth);
|
|
492
|
-
const h = c1(g.custom.pathHeight);
|
|
619
|
+
const w = Math.max(1, c1(g.custom.pathWidth));
|
|
620
|
+
const h = Math.max(1, c1(g.custom.pathHeight));
|
|
493
621
|
return `<a:custGeom><a:avLst/><a:gdLst/><a:rect l="0" t="0" r="${w}" b="${h}"/><a:pathLst><a:path w="${w}" h="${h}">${cmds}</a:path></a:pathLst></a:custGeom>`;
|
|
494
622
|
}
|
|
495
623
|
const gds = g.adjust && g.adjust.size > 0 ? [...g.adjust].map(([n, v]) => `<a:gd name="${escapeAttr(n)}" fmla="val ${v}"/>`).join("") : "";
|
|
@@ -698,19 +826,27 @@ function runXml(run, state, scope) {
|
|
|
698
826
|
}
|
|
699
827
|
function rPrXml(r) {
|
|
700
828
|
const out = [];
|
|
829
|
+
const fonts = rFontsXml(r.fontFamily);
|
|
830
|
+
if (fonts) out.push(fonts);
|
|
701
831
|
if (r.bold !== DEFAULT_RUN.bold) out.push(toggle("w:b", r.bold));
|
|
702
832
|
if (r.italic !== DEFAULT_RUN.italic) out.push(toggle("w:i", r.italic));
|
|
703
833
|
if (r.strike !== DEFAULT_RUN.strike) out.push(toggle("w:strike", r.strike));
|
|
704
|
-
if (r.underline !== DEFAULT_RUN.underline) out.push(`<w:u w:val="${r.underline}"/>`);
|
|
705
|
-
const fonts = rFontsXml(r.fontFamily);
|
|
706
|
-
if (fonts) out.push(fonts);
|
|
707
|
-
if (r.fontSizePt !== DEFAULT_RUN.fontSizePt) out.push(`<w:sz w:val="${Math.round(r.fontSizePt * 2)}"/>`);
|
|
708
834
|
if (r.colorHex !== DEFAULT_RUN.colorHex) out.push(`<w:color w:val="${r.colorHex}"/>`);
|
|
835
|
+
if (r.fontSizePt !== DEFAULT_RUN.fontSizePt) out.push(`<w:sz w:val="${Math.round(r.fontSizePt * 2)}"/>`);
|
|
836
|
+
if (r.underline !== DEFAULT_RUN.underline) out.push(underlineXml(r));
|
|
837
|
+
if (r.shadingColorHex !== void 0) out.push(runShdXml(r.shadingColorHex));
|
|
709
838
|
if (r.verticalAlign !== DEFAULT_RUN.verticalAlign) out.push(`<w:vertAlign w:val="${r.verticalAlign}"/>`);
|
|
710
839
|
if (r.rtl !== DEFAULT_RUN.rtl) out.push(toggle("w:rtl", r.rtl));
|
|
711
840
|
if (r.lang !== void 0) out.push(`<w:lang w:val="${escapeAttr(r.lang)}"/>`);
|
|
712
841
|
return out.length > 0 ? `<w:rPr>${out.join("")}</w:rPr>` : "";
|
|
713
842
|
}
|
|
843
|
+
function underlineXml(r) {
|
|
844
|
+
const color = r.underlineColorHex !== void 0 ? ` w:color="${r.underlineColorHex}"` : "";
|
|
845
|
+
return `<w:u w:val="${r.underline}"${color}/>`;
|
|
846
|
+
}
|
|
847
|
+
function runShdXml(fill) {
|
|
848
|
+
return `<w:shd w:val="clear" w:color="auto" w:fill="${fill}"/>`;
|
|
849
|
+
}
|
|
714
850
|
function pPrBody(p) {
|
|
715
851
|
const out = [];
|
|
716
852
|
if (p.numbering) out.push(`<w:numPr><w:ilvl w:val="${p.numbering.ilvl}"/><w:numId w:val="${escapeAttr(p.numbering.numId)}"/></w:numPr>`);
|
|
@@ -814,14 +950,18 @@ function rawParaPrXml(p) {
|
|
|
814
950
|
}
|
|
815
951
|
function rawRunPrXml(r) {
|
|
816
952
|
const out = [];
|
|
953
|
+
const fonts = r.fontFamily ? rawRFontsXml(r.fontFamily) : "";
|
|
954
|
+
if (fonts) out.push(fonts);
|
|
817
955
|
if (r.bold !== void 0) out.push(toggle("w:b", r.bold));
|
|
818
956
|
if (r.italic !== void 0) out.push(toggle("w:i", r.italic));
|
|
819
957
|
if (r.strike !== void 0) out.push(toggle("w:strike", r.strike));
|
|
820
|
-
if (r.underline !== void 0) out.push(`<w:u w:val="${r.underline}"/>`);
|
|
821
|
-
const fonts = r.fontFamily ? rawRFontsXml(r.fontFamily) : "";
|
|
822
|
-
if (fonts) out.push(fonts);
|
|
823
|
-
if (r.fontSizePt !== void 0) out.push(`<w:sz w:val="${Math.round(r.fontSizePt * 2)}"/>`);
|
|
824
958
|
if (r.colorHex !== void 0) out.push(`<w:color w:val="${r.colorHex}"/>`);
|
|
959
|
+
if (r.fontSizePt !== void 0) out.push(`<w:sz w:val="${Math.round(r.fontSizePt * 2)}"/>`);
|
|
960
|
+
if (r.underline !== void 0) out.push(underlineXml({
|
|
961
|
+
...r,
|
|
962
|
+
underline: r.underline
|
|
963
|
+
}));
|
|
964
|
+
if (r.shadingColorHex !== void 0) out.push(runShdXml(r.shadingColorHex));
|
|
825
965
|
if (r.verticalAlign !== void 0) out.push(`<w:vertAlign w:val="${r.verticalAlign}"/>`);
|
|
826
966
|
return out.length > 0 ? `<w:rPr>${out.join("")}</w:rPr>` : "";
|
|
827
967
|
}
|
|
@@ -859,8 +999,58 @@ function colsXml(cols) {
|
|
|
859
999
|
return `<w:cols w:num="${cols.count}" w:space="${twips(cols.spacePt)}"/>`;
|
|
860
1000
|
}
|
|
861
1001
|
var twips = (pt) => Math.round(pt * 20);
|
|
1002
|
+
/**
|
|
1003
|
+
* XML 1.0 §2.2 — the characters a document may contain at all.
|
|
1004
|
+
*
|
|
1005
|
+
* Everything below U+0020 except tab, newline and return is FORBIDDEN, and no
|
|
1006
|
+
* escape exists for them: `` is as ill-formed as the byte. A package with
|
|
1007
|
+
* one in it is not a document — LibreOffice says "source file could not be
|
|
1008
|
+
* loaded" and Word says nothing useful either.
|
|
1009
|
+
*
|
|
1010
|
+
* They reach here from the PDF reader. A subset font that states neither a
|
|
1011
|
+
* `/ToUnicode` nor an `/Encoding /Differences` says nothing about what its
|
|
1012
|
+
* codes mean, and the codes of a subset start at 1, 2, 3 — so the last-resort
|
|
1013
|
+
* Latin-1 reading turns a page of text into control characters. Reading it
|
|
1014
|
+
* better is `font.ts`'s business; what this must guarantee is that nothing the
|
|
1015
|
+
* reader believes can produce a package that will not open.
|
|
1016
|
+
*
|
|
1017
|
+
* A lone surrogate is the same case: a half of a pair is not a character.
|
|
1018
|
+
*/
|
|
1019
|
+
/**
|
|
1020
|
+
* XML 1.0 §2.2 — whether a code point may appear in a document at all.
|
|
1021
|
+
*
|
|
1022
|
+
* Everything below U+0020 except tab, newline and return is forbidden, and no
|
|
1023
|
+
* escape exists for them: `` is as ill-formed as the byte itself. So is a
|
|
1024
|
+
* lone surrogate — half a pair is not a character — and so are U+FFFE/U+FFFF.
|
|
1025
|
+
*/
|
|
1026
|
+
function xmlAllows(cp) {
|
|
1027
|
+
if (cp === 9 || cp === 10 || cp === 13) return true;
|
|
1028
|
+
if (cp < 32) return false;
|
|
1029
|
+
if (cp >= 55296 && cp <= 57343) return false;
|
|
1030
|
+
if (cp === 65534 || cp === 65535) return false;
|
|
1031
|
+
return cp <= 1114111;
|
|
1032
|
+
}
|
|
1033
|
+
/**
|
|
1034
|
+
* Escape text for XML, and drop what XML cannot carry.
|
|
1035
|
+
*
|
|
1036
|
+
* The dropping is not fussiness: a package with one control character in it is
|
|
1037
|
+
* not a document. LibreOffice answers "source file could not be loaded" and
|
|
1038
|
+
* Word refuses it too, so the whole conversion is lost over one byte.
|
|
1039
|
+
*
|
|
1040
|
+
* They reach here from the PDF reader. A subset font that states neither a
|
|
1041
|
+
* `/ToUnicode` nor an `/Encoding /Differences` says nothing about what its
|
|
1042
|
+
* codes mean, and a subset's codes start at 1, 2, 3 — so the last-resort
|
|
1043
|
+
* Latin-1 reading turns a page of prose into control characters. Reading such
|
|
1044
|
+
* a font better is `font.ts`'s business; what this guarantees is that nothing
|
|
1045
|
+
* the reader believes can produce a package that will not open.
|
|
1046
|
+
*/
|
|
862
1047
|
function escapeXml(s) {
|
|
863
|
-
|
|
1048
|
+
let out = "";
|
|
1049
|
+
for (const ch of s) {
|
|
1050
|
+
if (!xmlAllows(ch.codePointAt(0) ?? 0)) continue;
|
|
1051
|
+
out += ch === "&" ? "&" : ch === "<" ? "<" : ch === ">" ? ">" : ch;
|
|
1052
|
+
}
|
|
1053
|
+
return out;
|
|
864
1054
|
}
|
|
865
1055
|
function escapeAttr(s) {
|
|
866
1056
|
return escapeXml(s).replace(/"/g, """);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reamkit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.27.0",
|
|
4
4
|
"description": "Ream — convert DOCX, XLSX, PPTX and PDF to PDF, SVG, HTML, DOCX and XLSX, built from scratch on the ECMA-376 and ISO 32000 specifications. Parse once, convert anywhere.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Alex Krassavin <info@reamkit.dev>",
|