Compare commits
21 Commits
Author | SHA1 | Date | |
---|---|---|---|
7b2e8ede07 | |||
7da242ee9e | |||
d71203d2a6 | |||
064cee9921 | |||
022b25cfcd | |||
e20bd66663 | |||
6b8e22368b | |||
4304e82751 | |||
75715fde37 | |||
e779f6ea62 | |||
9280cdba50 | |||
8410d0c06d | |||
d47d85bdcf | |||
6ae45327fe | |||
464cd946dc | |||
cbff540b6e | |||
a8e6ba4c49 | |||
efc1ceaf1a | |||
c4ed19e589 | |||
13cf3eee5a | |||
24c159de37 |
@ -21,6 +21,7 @@ const doc = new Document({
|
|||||||
new Paragraph({
|
new Paragraph({
|
||||||
children: [
|
children: [
|
||||||
new ImageRun({
|
new ImageRun({
|
||||||
|
type: "jpg",
|
||||||
data: fs.readFileSync("./demo/images/image1.jpeg"),
|
data: fs.readFileSync("./demo/images/image1.jpeg"),
|
||||||
transformation: {
|
transformation: {
|
||||||
width: 100,
|
width: 100,
|
||||||
@ -37,6 +38,7 @@ const doc = new Document({
|
|||||||
new Paragraph({
|
new Paragraph({
|
||||||
children: [
|
children: [
|
||||||
new ImageRun({
|
new ImageRun({
|
||||||
|
type: "png",
|
||||||
data: fs.readFileSync("./demo/images/dog.png").toString("base64"),
|
data: fs.readFileSync("./demo/images/dog.png").toString("base64"),
|
||||||
transformation: {
|
transformation: {
|
||||||
width: 100,
|
width: 100,
|
||||||
@ -53,6 +55,7 @@ const doc = new Document({
|
|||||||
new Paragraph({
|
new Paragraph({
|
||||||
children: [
|
children: [
|
||||||
new ImageRun({
|
new ImageRun({
|
||||||
|
type: "jpg",
|
||||||
data: fs.readFileSync("./demo/images/cat.jpg"),
|
data: fs.readFileSync("./demo/images/cat.jpg"),
|
||||||
transformation: {
|
transformation: {
|
||||||
width: 100,
|
width: 100,
|
||||||
@ -73,6 +76,7 @@ const doc = new Document({
|
|||||||
new Paragraph({
|
new Paragraph({
|
||||||
children: [
|
children: [
|
||||||
new ImageRun({
|
new ImageRun({
|
||||||
|
type: "bmp",
|
||||||
data: fs.readFileSync("./demo/images/parrots.bmp"),
|
data: fs.readFileSync("./demo/images/parrots.bmp"),
|
||||||
transformation: {
|
transformation: {
|
||||||
width: 150,
|
width: 150,
|
||||||
@ -88,6 +92,7 @@ const doc = new Document({
|
|||||||
new Paragraph({
|
new Paragraph({
|
||||||
children: [
|
children: [
|
||||||
new ImageRun({
|
new ImageRun({
|
||||||
|
type: "gif",
|
||||||
data: fs.readFileSync("./demo/images/pizza.gif"),
|
data: fs.readFileSync("./demo/images/pizza.gif"),
|
||||||
transformation: {
|
transformation: {
|
||||||
width: 200,
|
width: 200,
|
||||||
@ -103,6 +108,7 @@ const doc = new Document({
|
|||||||
new Paragraph({
|
new Paragraph({
|
||||||
children: [
|
children: [
|
||||||
new ImageRun({
|
new ImageRun({
|
||||||
|
type: "gif",
|
||||||
data: fs.readFileSync("./demo/images/pizza.gif"),
|
data: fs.readFileSync("./demo/images/pizza.gif"),
|
||||||
transformation: {
|
transformation: {
|
||||||
width: 200,
|
width: 200,
|
||||||
@ -124,6 +130,7 @@ const doc = new Document({
|
|||||||
new Paragraph({
|
new Paragraph({
|
||||||
children: [
|
children: [
|
||||||
new ImageRun({
|
new ImageRun({
|
||||||
|
type: "jpg",
|
||||||
data: fs.readFileSync("./demo/images/cat.jpg"),
|
data: fs.readFileSync("./demo/images/cat.jpg"),
|
||||||
transformation: {
|
transformation: {
|
||||||
width: 200,
|
width: 200,
|
||||||
@ -143,6 +150,22 @@ const doc = new Document({
|
|||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new ImageRun({
|
||||||
|
type: "svg",
|
||||||
|
data: fs.readFileSync("./demo/images/linux-svg.svg"),
|
||||||
|
transformation: {
|
||||||
|
width: 200,
|
||||||
|
height: 200,
|
||||||
|
},
|
||||||
|
fallback: {
|
||||||
|
type: "png",
|
||||||
|
data: fs.readFileSync("./demo/images/linux-png.png"),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -21,6 +21,8 @@ import {
|
|||||||
Packer,
|
Packer,
|
||||||
Paragraph,
|
Paragraph,
|
||||||
TextRun,
|
TextRun,
|
||||||
|
MathLimitLower,
|
||||||
|
MathLimitUpper,
|
||||||
} from "docx";
|
} from "docx";
|
||||||
|
|
||||||
const doc = new Document({
|
const doc = new Document({
|
||||||
@ -316,6 +318,23 @@ const doc = new Document({
|
|||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
new Math({
|
||||||
|
children: [
|
||||||
|
new MathLimitUpper({
|
||||||
|
children: [new MathRun("x")],
|
||||||
|
limit: [new MathRun("-")],
|
||||||
|
}),
|
||||||
|
new MathRun("="),
|
||||||
|
new MathLimitLower({
|
||||||
|
children: [new MathRun("lim")],
|
||||||
|
limit: [new MathRun("x→0")],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -16,7 +16,9 @@ import {
|
|||||||
VerticalAlign,
|
VerticalAlign,
|
||||||
} from "docx";
|
} from "docx";
|
||||||
|
|
||||||
patchDocument(fs.readFileSync("demo/assets/simple-template.docx"), {
|
patchDocument({
|
||||||
|
outputType: "nodebuffer",
|
||||||
|
data: fs.readFileSync("demo/assets/simple-template.docx"),
|
||||||
patches: {
|
patches: {
|
||||||
name: {
|
name: {
|
||||||
type: PatchType.PARAGRAPH,
|
type: PatchType.PARAGRAPH,
|
||||||
@ -56,7 +58,11 @@ patchDocument(fs.readFileSync("demo/assets/simple-template.docx"), {
|
|||||||
],
|
],
|
||||||
link: "https://www.google.co.uk",
|
link: "https://www.google.co.uk",
|
||||||
}),
|
}),
|
||||||
new ImageRun({ data: fs.readFileSync("./demo/images/dog.png"), transformation: { width: 100, height: 100 } }),
|
new ImageRun({
|
||||||
|
type: "png",
|
||||||
|
data: fs.readFileSync("./demo/images/dog.png"),
|
||||||
|
transformation: { width: 100, height: 100 },
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
@ -82,7 +88,13 @@ patchDocument(fs.readFileSync("demo/assets/simple-template.docx"), {
|
|||||||
},
|
},
|
||||||
image_test: {
|
image_test: {
|
||||||
type: PatchType.PARAGRAPH,
|
type: PatchType.PARAGRAPH,
|
||||||
children: [new ImageRun({ data: fs.readFileSync("./demo/images/image1.jpeg"), transformation: { width: 100, height: 100 } })],
|
children: [
|
||||||
|
new ImageRun({
|
||||||
|
type: "jpg",
|
||||||
|
data: fs.readFileSync("./demo/images/image1.jpeg"),
|
||||||
|
transformation: { width: 100, height: 100 },
|
||||||
|
}),
|
||||||
|
],
|
||||||
},
|
},
|
||||||
table: {
|
table: {
|
||||||
type: PatchType.DOCUMENT,
|
type: PatchType.DOCUMENT,
|
||||||
|
@ -3,7 +3,9 @@
|
|||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import { patchDocument, PatchType, TextRun } from "docx";
|
import { patchDocument, PatchType, TextRun } from "docx";
|
||||||
|
|
||||||
patchDocument(fs.readFileSync("demo/assets/simple-template-2.docx"), {
|
patchDocument({
|
||||||
|
outputType: "nodebuffer",
|
||||||
|
data: fs.readFileSync("demo/assets/simple-template-2.docx"),
|
||||||
patches: {
|
patches: {
|
||||||
name: {
|
name: {
|
||||||
type: PatchType.PARAGRAPH,
|
type: PatchType.PARAGRAPH,
|
||||||
|
@ -24,7 +24,9 @@ const patches = getPatches({
|
|||||||
paragraph_replace: "Lorem ipsum paragraph",
|
paragraph_replace: "Lorem ipsum paragraph",
|
||||||
});
|
});
|
||||||
|
|
||||||
patchDocument(fs.readFileSync("demo/assets/simple-template.docx"), {
|
patchDocument({
|
||||||
|
outputType: "nodebuffer",
|
||||||
|
data: fs.readFileSync("demo/assets/simple-template.docx"),
|
||||||
patches,
|
patches,
|
||||||
}).then((doc) => {
|
}).then((doc) => {
|
||||||
fs.writeFileSync("My Document.docx", doc);
|
fs.writeFileSync("My Document.docx", doc);
|
||||||
|
@ -22,8 +22,11 @@ const patches = getPatches({
|
|||||||
"first-name": "John",
|
"first-name": "John",
|
||||||
});
|
});
|
||||||
|
|
||||||
patchDocument(fs.readFileSync("demo/assets/simple-template-3.docx"), {
|
patchDocument({
|
||||||
|
outputType: "nodebuffer",
|
||||||
|
data: fs.readFileSync("demo/assets/simple-template-3.docx"),
|
||||||
patches,
|
patches,
|
||||||
|
keepOriginalStyles: true,
|
||||||
}).then((doc) => {
|
}).then((doc) => {
|
||||||
fs.writeFileSync("My Document.docx", doc);
|
fs.writeFileSync("My Document.docx", doc);
|
||||||
});
|
});
|
||||||
|
Binary file not shown.
BIN
demo/images/linux-png.png
Normal file
BIN
demo/images/linux-png.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 71 KiB |
183
demo/images/linux-svg.svg
Normal file
183
demo/images/linux-svg.svg
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="500pt" height="600pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://web.resource.org/cc/">
|
||||||
|
|
||||||
|
<defs>
|
||||||
|
|
||||||
|
<linearGradient id="linearGradient172">
|
||||||
|
|
||||||
|
<stop style="stop-color:#3f2600;stop-opacity:0.6;" offset="0" id="stop173" />
|
||||||
|
|
||||||
|
<stop style="stop-color:#3f2600;stop-opacity:0;" offset="1" id="stop174" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="linearGradient167">
|
||||||
|
|
||||||
|
<stop style="stop-color:#ffffff;stop-opacity:0.65;" offset="0" id="stop168" />
|
||||||
|
|
||||||
|
<stop style="stop-color:#ffffff;stop-opacity:0;" offset="1" id="stop169" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="linearGradient162">
|
||||||
|
|
||||||
|
<stop style="stop-color:#ffa63f;stop-opacity:1;" offset="0" id="stop163" />
|
||||||
|
|
||||||
|
<stop style="stop-color:#ffff00;stop-opacity:1;" offset="1" id="stop164" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="linearGradient153">
|
||||||
|
|
||||||
|
<stop style="stop-color:#ffeed7;stop-opacity:1;" offset="0" id="stop154" />
|
||||||
|
|
||||||
|
<stop style="stop-color:#bdbfc2;stop-opacity:1;" offset="1" id="stop155" /></linearGradient>
|
||||||
|
|
||||||
|
<linearGradient id="linearGradient138">
|
||||||
|
|
||||||
|
<stop style="stop-color:#ffffff;stop-opacity:0.8;" offset="0" id="stop139" />
|
||||||
|
|
||||||
|
<stop style="stop-color:#ffffff;stop-opacity:0;" offset="1" id="stop140" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient xlink:href="#linearGradient138" id="linearGradient141" x1="0.47424799" y1="0.020191999" x2="0.417539" y2="0.90125799" gradientUnits="objectBoundingBox" />
|
||||||
|
|
||||||
|
<linearGradient xlink:href="#linearGradient167" id="linearGradient142" x1="0.55880702" y1="0.031192999" x2="0.553922" y2="0.94531101" gradientUnits="objectBoundingBox" />
|
||||||
|
|
||||||
|
<linearGradient xlink:href="#linearGradient167" id="linearGradient143" x1="0.46557701" y1="0.028819799" x2="0.41365999" y2="0.93366498" gradientUnits="objectBoundingBox"/>
|
||||||
|
|
||||||
|
<linearGradient xlink:href="#linearGradient167" id="linearGradient144" x1="0.70346397" y1="0.059404202" x2="0.64553201" y2="0.94063401" gradientUnits="objectBoundingBox" />
|
||||||
|
|
||||||
|
<linearGradient xlink:href="#linearGradient167" id="linearGradient145" x1="0.46741399" y1="-0.036155298" x2="0.86741799" y2="0.75857902" gradientUnits="objectBoundingBox" />
|
||||||
|
|
||||||
|
<linearGradient xlink:href="#linearGradient167" id="linearGradient146" x1="0.57152498" y1="0.023441499" x2="0.57143003" y2="0.71875" gradientUnits="objectBoundingBox" />
|
||||||
|
|
||||||
|
<linearGradient xlink:href="#linearGradient167" id="linearGradient147" x1="0.5" y1="0.0234362" x2="0.5" y2="0.8125" gradientUnits="objectBoundingBox" />
|
||||||
|
|
||||||
|
<linearGradient xlink:href="#linearGradient167" id="linearGradient148" x1="0.50799799" y1="0.37435901" x2="0.51599997" y2="0.92820501" gradientUnits="objectBoundingBox" />
|
||||||
|
|
||||||
|
<linearGradient xlink:href="#linearGradient138" id="linearGradient149" x1="0.5" y1="0.131707" x2="0.50400001" y2="0.94634098" gradientUnits="objectBoundingBox" />
|
||||||
|
|
||||||
|
<linearGradient xlink:href="#linearGradient167" id="linearGradient150" x1="-0.30509499" y1="0.099496603" x2="0.156323" y2="0.94191301" gradientUnits="objectBoundingBox" gradientTransform="matrix(-0.928523,0.283938,0.435332,0.943857,-1.91327e-7,5.49908e-8)" />
|
||||||
|
|
||||||
|
<linearGradient xlink:href="#linearGradient167" id="linearGradient151" x1="0.433979" y1="0.022184599" x2="0.487055" y2="1.02569" gradientUnits="objectBoundingBox" />
|
||||||
|
|
||||||
|
<linearGradient xlink:href="#linearGradient153" id="linearGradient152" x1="0.5" y1="0.89842999" x2="0.5" y2="0.40625" gradientUnits="objectBoundingBox" spreadMethod="reflect" />
|
||||||
|
|
||||||
|
<linearGradient xlink:href="#linearGradient153" id="linearGradient156" x1="0.43568701" y1="0.98882002" x2="0.453989" y2="0.23093501" gradientUnits="objectBoundingBox" />
|
||||||
|
|
||||||
|
<linearGradient xlink:href="#linearGradient153" id="linearGradient157" x1="0.49180499" y1="1.15284" x2="0.49482101" y2="0.41252401" gradientUnits="objectBoundingBox" />
|
||||||
|
|
||||||
|
<linearGradient xlink:href="#linearGradient153" id="linearGradient158" x1="0.51730198" y1="0.85418499" x2="0.49843901" y2="0.136172" gradientUnits="objectBoundingBox" />
|
||||||
|
|
||||||
|
<linearGradient xlink:href="#linearGradient153" id="linearGradient159" x1="0.46201" y1="0.87917101" x2="0.49215299" y2="0.096282303" gradientUnits="objectBoundingBox" />
|
||||||
|
|
||||||
|
<linearGradient xlink:href="#linearGradient162" id="linearGradient161" x1="0.50086302" y1="0.34872901" x2="0.41209599" y2="0.98558098" gradientUnits="objectBoundingBox" />
|
||||||
|
|
||||||
|
<linearGradient xlink:href="#linearGradient162" id="linearGradient165" x1="0.60399801" y1="0.51020199" x2="0.46399999" y2="0.98367399" gradientUnits="objectBoundingBox" />
|
||||||
|
|
||||||
|
<linearGradient xlink:href="#linearGradient162" id="linearGradient166" x1="0.50000501" y1="0.191616" x2="0.50800002" y2="0.97005898" gradientUnits="objectBoundingBox" />
|
||||||
|
|
||||||
|
<radialGradient xlink:href="#linearGradient172" id="radialGradient171" cx="0.5" cy="0.5" fx="0.5" fy="0.5" r="0.5" gradientUnits="objectBoundingBox" />
|
||||||
|
|
||||||
|
<radialGradient xlink:href="#linearGradient172" id="radialGradient176" />
|
||||||
|
|
||||||
|
<linearGradient xlink:href="#linearGradient153" id="linearGradient178" x1="0.94027299" y1="1.2934099" x2="0.19452" y2="-0.675295" gradientUnits="objectBoundingBox" />
|
||||||
|
|
||||||
|
<radialGradient xlink:href="#linearGradient172" id="radialGradient1399" gradientTransform="scale(1.045233,0.956725)" cx="446.77762" cy="1219.4125" fx="446.77762" fy="1219.4125" r="195.07191" gradientUnits="userSpaceOnUse" />
|
||||||
|
|
||||||
|
<linearGradient xlink:href="#linearGradient153" id="linearGradient1401" gradientUnits="userSpaceOnUse" x1="400.57785" y1="369.53015" x2="400.84448" y2="304.07886" gradientTransform="scale(0.575262,1.738339)" />
|
||||||
|
|
||||||
|
<linearGradient xlink:href="#linearGradient138" id="linearGradient1403" gradientUnits="userSpaceOnUse" x1="303.01761" y1="237.93179" x2="297.0856" y2="330.09561" gradientTransform="scale(1.116071,0.896001)" />
|
||||||
|
|
||||||
|
<linearGradient xlink:href="#linearGradient153" id="linearGradient1405" gradientUnits="userSpaceOnUse" gradientTransform="scale(0.816497,1.224744)" x1="378.93771" y1="278.60202" x2="380.27319" y2="243.91606" />
|
||||||
|
|
||||||
|
<linearGradient xlink:href="#linearGradient153" id="linearGradient1407" gradientUnits="userSpaceOnUse" x1="381.38742" y1="277.495" x2="380.5517" y2="245.68338" gradientTransform="scale(0.816497,1.224744)" />
|
||||||
|
|
||||||
|
<linearGradient xlink:href="#linearGradient167" id="linearGradient1409" gradientUnits="userSpaceOnUse" gradientTransform="scale(0.816497,1.224744)" x1="379.09573" y1="240.92712" x2="376.79556" y2="281.01636" />
|
||||||
|
|
||||||
|
<linearGradient xlink:href="#linearGradient167" id="linearGradient1411" gradientUnits="userSpaceOnUse" x1="389.63535" y1="242.28218" x2="387.06866" y2="281.32513" gradientTransform="scale(0.816497,1.224744)" />
|
||||||
|
|
||||||
|
<linearGradient xlink:href="#linearGradient153" id="linearGradient1413" gradientUnits="userSpaceOnUse" spreadMethod="reflect" x1="437.57941" y1="528.87177" x2="437.57941" y2="394.10361" gradientTransform="scale(0.812855,1.230232)" />
|
||||||
|
|
||||||
|
<linearGradient xlink:href="#linearGradient153" id="linearGradient1415" gradientUnits="userSpaceOnUse" x1="375.17325" y1="419.78485" x2="377.48541" y2="324.03815" gradientTransform="scale(0.649784,1.538974)" />
|
||||||
|
|
||||||
|
<linearGradient xlink:href="#linearGradient138" id="linearGradient1417" gradientUnits="userSpaceOnUse" x1="320.75104" y1="498.17776" x2="321.32224" y2="614.50439" gradientTransform="scale(1.074798,0.930408)" />
|
||||||
|
|
||||||
|
<linearGradient xlink:href="#linearGradient167" id="linearGradient1419" gradientUnits="userSpaceOnUse" x1="322.48257" y1="435.26761" x2="323.2514" y2="488.48251" gradientTransform="scale(1.077001,0.928504)" />
|
||||||
|
|
||||||
|
<linearGradient xlink:href="#linearGradient167" id="linearGradient1421" gradientUnits="userSpaceOnUse" x1="411.2215" y1="242.94365" x2="411.2215" y2="331.44858" gradientTransform="scale(0.571707,1.749147)" />
|
||||||
|
|
||||||
|
<linearGradient xlink:href="#linearGradient167" id="linearGradient1423" gradientUnits="userSpaceOnUse" x1="867.34546" y1="234.73897" x2="867.33453" y2="314.83911" gradientTransform="scale(0.572667,1.746214)" />
|
||||||
|
|
||||||
|
<linearGradient xlink:href="#linearGradient162" id="linearGradient1425" gradientUnits="userSpaceOnUse" x1="236.25362" y1="657.11133" x2="212.5099" y2="737.41229" gradientTransform="scale(1.011514,0.988617)" />
|
||||||
|
|
||||||
|
<linearGradient xlink:href="#linearGradient153" id="linearGradient1427" gradientUnits="userSpaceOnUse" x1="381.56607" y1="655.73102" x2="279.64313" y2="386.66583" gradientTransform="scale(1.065499,0.938527)" />
|
||||||
|
|
||||||
|
<linearGradient xlink:href="#linearGradient162" id="linearGradient1429" gradientUnits="userSpaceOnUse" x1="218.11714" y1="630.30475" x2="203.12654" y2="737.8537" gradientTransform="scale(1.009851,0.990245)" />
|
||||||
|
|
||||||
|
<linearGradient xlink:href="#linearGradient167" id="linearGradient1431" gradientUnits="userSpaceOnUse" gradientTransform="scale(1.007724,0.992335)" x1="117.88966" y1="587.23602" x2="182.24524" y2="704.73077" />
|
||||||
|
|
||||||
|
<linearGradient xlink:href="#linearGradient167" id="linearGradient1433" gradientUnits="userSpaceOnUse" x1="223.10072" y1="570.41809" x2="230.53499" y2="710.97723" gradientTransform="scale(0.999504,1.000496)" />
|
||||||
|
|
||||||
|
<linearGradient xlink:href="#linearGradient167" id="linearGradient1435" gradientUnits="userSpaceOnUse" x1="316.93988" y1="474.01779" x2="371.60889" y2="582.63507" gradientTransform="scale(1.065499,0.938527)" />
|
||||||
|
|
||||||
|
<linearGradient xlink:href="#linearGradient162" id="linearGradient1437" gradientUnits="userSpaceOnUse" x1="284.68652" y1="410.46326" x2="285.45923" y2="485.69934" gradientTransform="scale(1.218684,0.820557)" />
|
||||||
|
|
||||||
|
<linearGradient xlink:href="#linearGradient167" id="linearGradient1439" gradientUnits="userSpaceOnUse" x1="288.82358" y1="398.85422" x2="288.37628" y2="482.55939" gradientTransform="scale(1.221941,0.81837)" />
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<g id="g1369" transform="translate(-310.7524,-64.25268)">
|
||||||
|
|
||||||
|
<path transform="matrix(1.4177,0,0,0.414745,-38.7944,222.194)" d="M 670.88202 1166.6423 A 203.89551 186.63016 0 1 1 263.091,1166.6423 A 203.89551 186.63016 0 1 1 670.88202 1166.6423 z" id="path175" style="fill:url(#radialGradient1399);stroke:none;stroke-width:1pt;stroke-linecap:butt;stroke-linejoin:miter" />
|
||||||
|
|
||||||
|
<path transform="matrix(1.25,0,0,1.25,185.454,-167.505)" id="path106" d="M 223.627,632.24 C 201.239,600.017 196.873,495.256 249.114,430.81 C 275,399.892 281.604,378.345 283.645,349.417 C 285.034,316.438 260.32,217.975 353.528,210.473 C 447.934,202.941 442.864,296.133 442.321,345.448 C 441.87,387.088 472.895,410.689 494.117,443.143 C 533.396,502.773 530.074,605.443 486.718,661.015 C 431.801,730.583 384.765,700.413 353.528,702.945 C 295.035,706.147 293.101,737.336 223.627,632.24 z " style="fill:#000000;stroke:none;stroke-width:1.25;" />
|
||||||
|
|
||||||
|
<path transform="matrix(-1.67739,-2.24516e-2,-2.11236e-2,1.4709,1173.58,-293.017)" id="path113" d="M 246.571,470.864 C 234.332,483.36 202.175,539.956 251.44,576.224 C 268.809,588.857 235.063,635.719 219.435,612.532 C 191.865,570.914 210.604,505.591 227.75,482.344 C 239.402,465.857 256.98,459.668 246.571,470.864 z " style="fill:url(#linearGradient1401);stroke:none;stroke-width:0.99464899;" />
|
||||||
|
|
||||||
|
<path transform="matrix(-1.67755,0,0,1.52374,1174.62,-318.082)" id="path111" d="M 256.513,459.837 C 236.598,477.554 200.337,539.928 253.225,580.443 C 270.595,593.075 237.832,632.906 219.435,612.532 C 155.472,541.712 221.104,460.278 243.697,432.282 C 263.889,407.935 281.775,438.034 256.513,459.837 z " style="fill:#000000;stroke:#000000;stroke-width:0.97729802;" />
|
||||||
|
|
||||||
|
<path transform="matrix(1.26626,-7.13667e-2,-4.59795e-2,1.19574,202.143,-125.761)" d="M 399.56879 258.15753 A 58.37323 46.863022 0 1 1 282.82233,258.15753 A 58.37323 46.863022 0 1 1 399.56879 258.15753 z" id="path114" style="fill:url(#linearGradient1403);stroke:none;stroke-width:1.26498997;" />
|
||||||
|
|
||||||
|
<path transform="matrix(1.30445,-7.55326e-2,7.71251e-2,1.34257,144.757,-177.617)" d="M 328.86324 320.64151 A 18.087479 27.131195 0 1 1 292.68828,320.64151 A 18.087479 27.131195 0 1 1 328.86324 320.64151 z" id="path115" style="fill:url(#linearGradient1405);stroke:none;stroke-width:1.17873001;" />
|
||||||
|
|
||||||
|
<path transform="matrix(-1.81082,4.95107e-2,3.17324e-2,1.55333,1207.46,-284.777)" d="M 328.86324 320.64151 A 18.087479 27.131195 0 1 1 292.68828,320.64151 A 18.087479 27.131195 0 1 1 328.86324 320.64151 z" id="path116" style="fill:url(#linearGradient1407);stroke:none;stroke-width:0.93138498;" />
|
||||||
|
|
||||||
|
<path transform="matrix(-0.823196,-1.76123e-3,-1.82321e-2,0.852662,913.674,-37.9902)" d="M 328.86324 320.64151 A 18.087479 27.131195 0 1 1 292.68828,320.64151 A 18.087479 27.131195 0 1 1 328.86324 320.64151 z" id="path117" style="fill:#000000;stroke:none;stroke-width:1.86495996;" />
|
||||||
|
|
||||||
|
<path transform="matrix(0.59438,-7.22959e-2,6.88176e-2,0.705838,367.448,32.4186)" d="M 328.86324 320.64151 A 18.087479 27.131195 0 1 1 292.68828,320.64151 A 18.087479 27.131195 0 1 1 328.86324 320.64151 z" id="path118" style="fill:#000000;stroke:none;stroke-width:2.39814997;" />
|
||||||
|
|
||||||
|
<path transform="matrix(-0.480323,-3.6454e-2,-4.67935e-2,0.475606,813.496,87.0124)" d="M 328.86324 320.64151 A 18.087479 27.131195 0 1 1 292.68828,320.64151 A 18.087479 27.131195 0 1 1 328.86324 320.64151 z" id="path121" style="fill:url(#linearGradient1409);stroke:none;stroke-width:3.1916101;" />
|
||||||
|
|
||||||
|
<path transform="matrix(0.35691,-4.08211e-2,4.13232e-2,0.398544,449.334,114.991)" d="M 328.86324 320.64151 A 18.087479 27.131195 0 1 1 292.68828,320.64151 A 18.087479 27.131195 0 1 1 328.86324 320.64151 z" id="path122" style="fill:url(#linearGradient1411);stroke:none;stroke-width:4.12025976;" />
|
||||||
|
|
||||||
|
<path transform="matrix(1.25,0,0,1.25,185.454,-168.23)" id="path128" d="M 258.702,495.425 C 271.538,466.322 298.816,415.199 299.397,375.667 C 299.397,344.225 393.576,336.716 401.134,368.109 C 408.692,399.502 427.875,446.592 440.084,469.265 C 452.292,491.937 487.893,563.96 449.968,626.811 C 415.811,682.455 312.243,726.477 256.958,619.254 C 238.355,582.047 241.673,535.939 258.702,495.425 z " style="fill:url(#linearGradient1413);stroke:none;stroke-width:1.25;" />
|
||||||
|
|
||||||
|
<path transform="matrix(1.38936,-0.111074,0.102211,1.30214,108.413,-165.938)" id="path112" d="M 242.905,473.815 C 231.642,492.782 207.405,543.124 255.042,575.862 C 306.353,610.682 301.515,672.924 239.435,637.817 C 182.658,606.028 216.59,500.039 234.925,475.551 C 247.032,458.337 264.822,437.52 242.905,473.815 z " style="fill:url(#linearGradient1415);stroke:none;stroke-width:1.15804005;" />
|
||||||
|
|
||||||
|
<path transform="matrix(1.25,0,0,1.25,185.454,-167.505)" id="path109" d="M 256.513,449.72 C 239.048,478.228 197.136,545.533 253.225,580.443 C 328.794,626.798 307.398,673.154 238.426,631.417 C 141.317,573.153 226.601,455.801 265.557,411.079 C 310.001,360.879 274.111,420.166 256.513,449.72 z " style="fill:#000000;stroke:#000000;stroke-width:1.25;" />
|
||||||
|
|
||||||
|
<path id="path125" d="M 421.481,504.727 C 421.481,537.139 392.209,579.243 341.953,578.865 C 290.125,579.32 268.004,537.139 268.004,504.727 C 268.004,472.315 302.383,446.01 344.743,446.01 C 387.102,446.01 421.481,472.315 421.481,504.727 z " style="font-size:12px;fill:url(#linearGradient1417);stroke:none;stroke-width:1.23705006;stroke-dasharray:none" transform="matrix(1.30209,0,0,1.22525,170.042,-153.557)" />
|
||||||
|
|
||||||
|
<path id="path127" d="M 398.227,412.292 C 397.615,450.864 375.047,459.963 346.487,459.963 C 317.926,459.963 297.195,454.269 294.746,412.292 C 294.746,385.978 317.926,370.75 346.487,370.75 C 375.047,370.75 398.227,385.978 398.227,412.292 z " style="font-size:12px;fill:url(#linearGradient1419);stroke:none;stroke-width:1.38846004;stroke-dasharray:none" transform="matrix(1.1868,0,0,1.06708,210.623,-100.078)" />
|
||||||
|
|
||||||
|
<path transform="matrix(1.25,0,0,1.25,185.454,-167.505)" id="path129" d="M 234.285,456.475 C 252.001,429.479 289.3,388.111 241.262,462.288 C 202.311,523.331 226.859,562.561 239.518,573.327 C 276.045,605.889 274.484,627.676 245.913,610.533 C 184.288,573.907 197.078,512.285 234.285,456.475 z " style="fill:url(#linearGradient1421);stroke:none;stroke-width:1.25;" />
|
||||||
|
|
||||||
|
<path transform="matrix(1.25,0,0,1.25,185.454,-167.505)" id="path131" d="M 490.662,467.52 C 475.343,435.819 426.528,355.618 492.988,448.917 C 553.449,533.214 511.01,591.93 503.452,597.744 C 495.895,603.557 470.315,615.184 477.873,594.837 C 485.43,574.49 523.107,535.864 490.662,467.52 z " style="fill:url(#linearGradient1423);stroke:none;stroke-width:1.25;" />
|
||||||
|
|
||||||
|
<path transform="matrix(1.25,0,0,1.25,185.454,-167.505)" id="path132" d="M 220.915,716.921 C 180.473,695.505 121.663,721.045 143.013,662.855 C 147.289,649.617 136.638,629.847 143.594,616.929 C 151.733,601.231 169.174,604.72 179.639,594.255 C 189.957,583.364 196.498,564.606 215.683,567.513 C 234.867,570.42 247.628,593.974 261.027,622.742 C 270.91,643.38 305.968,672.406 303.677,695.5 C 300.981,731 260.65,737.69 220.915,716.921 z " style="fill:url(#linearGradient1425);stroke:#e68c3f;stroke-width:6.25;" />
|
||||||
|
|
||||||
|
<path id="path177" d="M 415.072,495.764 C 412.065,520.67 379.259,572.391 345.554,577.298 C 311.294,582.634 279.122,543.238 271.407,506.184 C 261.518,464.978 293.994,448.584 343.345,449.557 C 396.646,451.211 417.466,463.448 415.072,495.764 z " style="font-size:12px;fill:url(#linearGradient1427);stroke:none;stroke-width:2.85509992;stroke-dasharray:none" transform="matrix(0.598206,0.268584,-0.239623,0.617213,700.568,140.464)" />
|
||||||
|
|
||||||
|
<path transform="matrix(-1.1685,0.423145,0.475283,1.16478,728.343,-213.821)" id="path133" d="M 220.274,718.402 C 178.947,694.812 120.38,724.007 143.013,662.855 C 147.749,649.787 136.417,629.303 143.373,616.385 C 151.512,600.687 169.174,604.72 179.639,594.255 C 189.957,583.364 198.466,566.387 217.651,569.294 C 236.835,572.201 247.628,593.974 261.027,622.742 C 270.91,643.38 304.442,671.713 302.151,694.807 C 299.455,730.307 259.427,740.278 220.274,718.402 z " style="fill:url(#linearGradient1429);stroke:#e68c3f;stroke-width:6.25067997;" />
|
||||||
|
|
||||||
|
<path transform="matrix(-0.945096,0.343745,0.424076,0.956058,714.328,-64.342)" id="path134" d="M 216.482,675.68 C 129.951,618.177 169.174,604.72 179.639,594.255 C 189.957,583.364 198.466,566.387 217.651,569.294 C 236.835,572.201 247.628,593.974 261.027,622.742 C 270.91,643.38 304.087,671.66 302.151,694.807 C 299.535,721.917 253.961,700.294 216.482,675.68 z " style="fill:url(#linearGradient1431);stroke:none;stroke-width:1.52532005;" />
|
||||||
|
|
||||||
|
<path transform="matrix(1.00431,-5.2286e-2,-1.74e-2,1.04575,244.191,-28.4653)" id="path135" d="M 216.506,677.071 C 129.975,619.568 169.709,603.501 182.56,595.791 C 197.959,585.849 197.718,564.96 216.903,567.867 C 236.087,570.774 247.628,593.974 261.027,622.742 C 270.91,643.38 304.087,671.66 302.151,694.807 C 299.535,721.917 253.985,701.685 216.506,677.071 z " style="fill:url(#linearGradient1433);stroke:none;stroke-width:1.52532005;" />
|
||||||
|
|
||||||
|
<path id="path136" d="M 415.072,495.764 C 412.065,520.67 379.259,572.391 345.554,577.298 C 311.294,582.634 279.122,543.238 271.407,506.184 C 261.518,464.978 293.994,448.584 343.345,449.557 C 396.646,451.211 417.466,463.448 415.072,495.764 z " style="font-size:12px;fill:#000000;stroke:none;stroke-width:2.85509992;" transform="matrix(0.515584,0.215259,-0.206526,0.49467,713.3,222.559)" />
|
||||||
|
|
||||||
|
<path id="path137" d="M 415.072,495.764 C 412.065,520.67 379.259,572.391 345.554,577.298 C 311.294,582.634 279.122,543.238 271.407,506.184 C 261.518,464.978 293.994,448.584 343.345,449.557 C 396.646,451.211 417.466,463.448 415.072,495.764 z " style="font-size:12px;fill:url(#linearGradient1435);stroke:none;stroke-width:2.85509992;" transform="matrix(0.351231,0.149463,-0.128856,0.343469,724.522,318.291)" />
|
||||||
|
|
||||||
|
<path transform="matrix(1.25,0,0,1.25,185.454,-167.505)" id="path119" d="M 309.954,338.729 C 317.101,331.959 334.765,311.663 367.915,332.974 C 374.077,336.984 379.077,337.351 390.936,342.429 C 414.662,352.178 403.318,375.688 378.192,383.537 C 367.434,387.026 357.656,400.093 338.063,398.976 C 321.329,397.999 316.944,387.102 306.665,381.07 C 288.396,370.759 285.7,356.816 295.565,349.417 C 305.431,342.018 309.29,339.358 309.954,338.729 z " style="fill:url(#linearGradient1437);stroke:#e68c3f;stroke-width:3.75;" />
|
||||||
|
|
||||||
|
<path transform="matrix(1.25,0,0,1.25,185.454,-167.505)" id="path120" d="M 391.251,357.645 C 381.368,358.226 359.858,379.736 337.185,379.736 C 314.512,379.736 301.141,358.807 297.653,358.807" style="fill:none;stroke:#e68c3f;stroke-width:2.5;" />
|
||||||
|
|
||||||
|
<path transform="matrix(0.627885,0,0,0.595666,392.366,51.8173)" id="path123" d="M 309.954,338.729 C 317.101,331.959 339.645,313.381 369.542,332.401 C 375.841,336.167 382.346,340.266 392.02,345.865 C 411.182,357.613 401.691,374.543 378.734,385.255 C 368.316,389.75 351.141,399.67 338.063,398.976 C 323.53,397.568 314.128,387.577 304.496,381.07 C 286.826,368.767 287.899,358.833 296.107,350.562 C 302.312,344.883 309.29,339.358 309.954,338.729 z " style="fill:url(#linearGradient1439);stroke:none;" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 21 KiB |
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
- Simple, declarative API
|
- Simple, declarative API
|
||||||
- 80+ usage examples
|
- 80+ usage examples
|
||||||
- Battle tested, mature, 99.9%+ coverage
|
- Battle tested, mature, 100% coverage (yes, every line is tested)
|
||||||
|
|
||||||
[GitHub](https://github.com/dolanmiu/docx)
|
[GitHub](https://github.com/dolanmiu/docx)
|
||||||
[Get Started](#Welcome)
|
[Get Started](#Welcome)
|
||||||
|
@ -6,6 +6,7 @@ To create a `floating` image on top of text:
|
|||||||
|
|
||||||
```ts
|
```ts
|
||||||
const image = new ImageRun({
|
const image = new ImageRun({
|
||||||
|
type: 'gif',
|
||||||
data: fs.readFileSync("./demo/images/pizza.gif"),
|
data: fs.readFileSync("./demo/images/pizza.gif"),
|
||||||
transformation: {
|
transformation: {
|
||||||
width: 200,
|
width: 200,
|
||||||
@ -26,6 +27,7 @@ By default with no arguments, its an `inline` image:
|
|||||||
|
|
||||||
```ts
|
```ts
|
||||||
const image = new ImageRun({
|
const image = new ImageRun({
|
||||||
|
type: 'gif',
|
||||||
data: fs.readFileSync("./demo/images/pizza.gif"),
|
data: fs.readFileSync("./demo/images/pizza.gif"),
|
||||||
transformation: {
|
transformation: {
|
||||||
width: 100,
|
width: 100,
|
||||||
@ -59,6 +61,7 @@ const doc = new Document({
|
|||||||
new Paragraph({
|
new Paragraph({
|
||||||
children: [
|
children: [
|
||||||
new ImageRun({
|
new ImageRun({
|
||||||
|
type: [IMAGE_TYPE],
|
||||||
data: [IMAGE_BUFFER],
|
data: [IMAGE_BUFFER],
|
||||||
transformation: {
|
transformation: {
|
||||||
width: [IMAGE_SIZE],
|
width: [IMAGE_SIZE],
|
||||||
@ -97,6 +100,7 @@ To change the position the image to be on top of the text, simply add the `float
|
|||||||
|
|
||||||
```ts
|
```ts
|
||||||
const image = new ImageRun({
|
const image = new ImageRun({
|
||||||
|
type: 'png',
|
||||||
data: buffer,
|
data: buffer,
|
||||||
transformation: {
|
transformation: {
|
||||||
width: 903,
|
width: 903,
|
||||||
@ -115,6 +119,7 @@ const image = new ImageRun({
|
|||||||
|
|
||||||
```ts
|
```ts
|
||||||
const image = new ImageRun({
|
const image = new ImageRun({
|
||||||
|
type: 'png',
|
||||||
data: buffer,
|
data: buffer,
|
||||||
transformation: {
|
transformation: {
|
||||||
width: 903,
|
width: 903,
|
||||||
@ -180,6 +185,7 @@ For example:
|
|||||||
|
|
||||||
```ts
|
```ts
|
||||||
const image = new ImageRun({
|
const image = new ImageRun({
|
||||||
|
type: 'gif',
|
||||||
data: fs.readFileSync("./demo/images/pizza.gif"),
|
data: fs.readFileSync("./demo/images/pizza.gif"),
|
||||||
transformation: {
|
transformation: {
|
||||||
width: 200,
|
width: 200,
|
||||||
@ -228,6 +234,7 @@ For example:
|
|||||||
|
|
||||||
```ts
|
```ts
|
||||||
const image = new ImageRun({
|
const image = new ImageRun({
|
||||||
|
type: 'gif',
|
||||||
data: fs.readFileSync("./demo/images/pizza.gif"),
|
data: fs.readFileSync("./demo/images/pizza.gif"),
|
||||||
transformation: {
|
transformation: {
|
||||||
width: 200,
|
width: 200,
|
||||||
@ -258,6 +265,7 @@ Specifies common non-visual DrawingML properties. A name, title and description
|
|||||||
|
|
||||||
```ts
|
```ts
|
||||||
const image = new ImageRun({
|
const image = new ImageRun({
|
||||||
|
type: 'gif',
|
||||||
data: fs.readFileSync("./demo/images/pizza.gif"),
|
data: fs.readFileSync("./demo/images/pizza.gif"),
|
||||||
altText: {
|
altText: {
|
||||||
title: "This is an ultimate title",
|
title: "This is an ultimate title",
|
||||||
|
@ -263,3 +263,23 @@ new MathAngledBrackets({
|
|||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Limit
|
||||||
|
|
||||||
|
#### Limit Upper
|
||||||
|
|
||||||
|
```ts
|
||||||
|
new MathLimitUpper({
|
||||||
|
children: [new MathRun("x")],
|
||||||
|
limit: [new MathRun("-")],
|
||||||
|
}),
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Limit Lower
|
||||||
|
|
||||||
|
```ts
|
||||||
|
new MathLimitLower({
|
||||||
|
children: [new MathRun("lim")],
|
||||||
|
limit: [new MathRun("x→0")],
|
||||||
|
}),
|
||||||
|
```
|
||||||
|
@ -76,7 +76,7 @@ patchDocument(fs.readFileSync("My Document.docx"), {
|
|||||||
],
|
],
|
||||||
link: "https://www.google.co.uk",
|
link: "https://www.google.co.uk",
|
||||||
}),
|
}),
|
||||||
new ImageRun({ data: fs.readFileSync("./demo/images/dog.png"), transformation: { width: 100, height: 100 } }),
|
new ImageRun({ type: 'png', data: fs.readFileSync("./demo/images/dog.png"), transformation: { width: 100, height: 100 } }),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
1550
package-lock.json
generated
1550
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -80,7 +80,7 @@
|
|||||||
"eslint": "^8.23.0",
|
"eslint": "^8.23.0",
|
||||||
"eslint-plugin-functional": "^6.0.0",
|
"eslint-plugin-functional": "^6.0.0",
|
||||||
"eslint-plugin-import": "^2.26.0",
|
"eslint-plugin-import": "^2.26.0",
|
||||||
"eslint-plugin-jsdoc": "^46.2.6",
|
"eslint-plugin-jsdoc": "^48.0.2",
|
||||||
"eslint-plugin-no-null": "^1.0.2",
|
"eslint-plugin-no-null": "^1.0.2",
|
||||||
"eslint-plugin-prefer-arrow": "^1.2.3",
|
"eslint-plugin-prefer-arrow": "^1.2.3",
|
||||||
"eslint-plugin-unicorn": "^50.0.1",
|
"eslint-plugin-unicorn": "^50.0.1",
|
||||||
|
@ -12,7 +12,8 @@ describe("ImageReplacer", () => {
|
|||||||
"test {test-image.png} test",
|
"test {test-image.png} test",
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
stream: Buffer.from(""),
|
type: "png",
|
||||||
|
data: Buffer.from(""),
|
||||||
fileName: "test-image.png",
|
fileName: "test-image.png",
|
||||||
transformation: {
|
transformation: {
|
||||||
pixels: {
|
pixels: {
|
||||||
|
@ -150,12 +150,25 @@ describe("Compiler", () => {
|
|||||||
new Paragraph({
|
new Paragraph({
|
||||||
children: [
|
children: [
|
||||||
new ImageRun({
|
new ImageRun({
|
||||||
|
type: "png",
|
||||||
data: Buffer.from("", "base64"),
|
data: Buffer.from("", "base64"),
|
||||||
transformation: {
|
transformation: {
|
||||||
width: 100,
|
width: 100,
|
||||||
height: 100,
|
height: 100,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
new ImageRun({
|
||||||
|
type: "svg",
|
||||||
|
data: Buffer.from("", "base64"),
|
||||||
|
transformation: {
|
||||||
|
width: 100,
|
||||||
|
height: 100,
|
||||||
|
},
|
||||||
|
fallback: {
|
||||||
|
type: "png",
|
||||||
|
data: Buffer.from("", "base64"),
|
||||||
|
},
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
@ -165,7 +178,8 @@ describe("Compiler", () => {
|
|||||||
|
|
||||||
vi.spyOn(compiler["imageReplacer"], "getMediaData").mockReturnValue([
|
vi.spyOn(compiler["imageReplacer"], "getMediaData").mockReturnValue([
|
||||||
{
|
{
|
||||||
stream: Buffer.from(""),
|
type: "png",
|
||||||
|
data: Buffer.from(""),
|
||||||
fileName: "test",
|
fileName: "test",
|
||||||
transformation: {
|
transformation: {
|
||||||
pixels: {
|
pixels: {
|
||||||
@ -178,6 +192,36 @@ describe("Compiler", () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: "svg",
|
||||||
|
data: Buffer.from(""),
|
||||||
|
fileName: "test",
|
||||||
|
transformation: {
|
||||||
|
pixels: {
|
||||||
|
x: 100,
|
||||||
|
y: 100,
|
||||||
|
},
|
||||||
|
emus: {
|
||||||
|
x: 100,
|
||||||
|
y: 100,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fallback: {
|
||||||
|
type: "png",
|
||||||
|
data: Buffer.from(""),
|
||||||
|
fileName: "test",
|
||||||
|
transformation: {
|
||||||
|
pixels: {
|
||||||
|
x: 100,
|
||||||
|
y: 100,
|
||||||
|
},
|
||||||
|
emus: {
|
||||||
|
x: 100,
|
||||||
|
y: 100,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
compiler.compile(file);
|
compiler.compile(file);
|
||||||
|
@ -62,8 +62,13 @@ export class Compiler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const { stream, fileName } of file.Media.Array) {
|
for (const data of file.Media.Array) {
|
||||||
zip.file(`word/media/${fileName}`, stream);
|
if (data.type !== "svg") {
|
||||||
|
zip.file(`word/media/${data.fileName}`, data.data);
|
||||||
|
} else {
|
||||||
|
zip.file(`word/media/${data.fileName}`, data.data);
|
||||||
|
zip.file(`word/media/${data.fallback.fileName}`, data.fallback.data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const { data: buffer, name, fontKey } of file.FontTable.fontOptionsWithKey) {
|
for (const { data: buffer, name, fontKey } of file.FontTable.fontOptionsWithKey) {
|
||||||
|
@ -25,12 +25,13 @@ describe("ContentTypes", () => {
|
|||||||
expect(tree["Types"][3]).to.deep.equal({ Default: { _attr: { ContentType: "image/jpeg", Extension: "jpg" } } });
|
expect(tree["Types"][3]).to.deep.equal({ Default: { _attr: { ContentType: "image/jpeg", Extension: "jpg" } } });
|
||||||
expect(tree["Types"][4]).to.deep.equal({ Default: { _attr: { ContentType: "image/bmp", Extension: "bmp" } } });
|
expect(tree["Types"][4]).to.deep.equal({ Default: { _attr: { ContentType: "image/bmp", Extension: "bmp" } } });
|
||||||
expect(tree["Types"][5]).to.deep.equal({ Default: { _attr: { ContentType: "image/gif", Extension: "gif" } } });
|
expect(tree["Types"][5]).to.deep.equal({ Default: { _attr: { ContentType: "image/gif", Extension: "gif" } } });
|
||||||
expect(tree["Types"][6]).to.deep.equal({
|
expect(tree["Types"][6]).to.deep.equal({ Default: { _attr: { ContentType: "image/svg+xml", Extension: "svg" } } });
|
||||||
|
expect(tree["Types"][7]).to.deep.equal({
|
||||||
Default: { _attr: { ContentType: "application/vnd.openxmlformats-package.relationships+xml", Extension: "rels" } },
|
Default: { _attr: { ContentType: "application/vnd.openxmlformats-package.relationships+xml", Extension: "rels" } },
|
||||||
});
|
});
|
||||||
expect(tree["Types"][7]).to.deep.equal({ Default: { _attr: { ContentType: "application/xml", Extension: "xml" } } });
|
expect(tree["Types"][8]).to.deep.equal({ Default: { _attr: { ContentType: "application/xml", Extension: "xml" } } });
|
||||||
|
|
||||||
expect(tree["Types"][8]).to.deep.equal({
|
expect(tree["Types"][9]).to.deep.equal({
|
||||||
Default: {
|
Default: {
|
||||||
_attr: {
|
_attr: {
|
||||||
ContentType: "application/vnd.openxmlformats-officedocument.obfuscatedFont",
|
ContentType: "application/vnd.openxmlformats-officedocument.obfuscatedFont",
|
||||||
@ -38,7 +39,7 @@ describe("ContentTypes", () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(tree["Types"][9]).to.deep.equal({
|
expect(tree["Types"][10]).to.deep.equal({
|
||||||
Override: {
|
Override: {
|
||||||
_attr: {
|
_attr: {
|
||||||
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml",
|
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml",
|
||||||
@ -46,7 +47,7 @@ describe("ContentTypes", () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(tree["Types"][10]).to.deep.equal({
|
expect(tree["Types"][11]).to.deep.equal({
|
||||||
Override: {
|
Override: {
|
||||||
_attr: {
|
_attr: {
|
||||||
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml",
|
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml",
|
||||||
@ -54,7 +55,7 @@ describe("ContentTypes", () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(tree["Types"][11]).to.deep.equal({
|
expect(tree["Types"][12]).to.deep.equal({
|
||||||
Override: {
|
Override: {
|
||||||
_attr: {
|
_attr: {
|
||||||
ContentType: "application/vnd.openxmlformats-package.core-properties+xml",
|
ContentType: "application/vnd.openxmlformats-package.core-properties+xml",
|
||||||
@ -62,7 +63,7 @@ describe("ContentTypes", () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(tree["Types"][12]).to.deep.equal({
|
expect(tree["Types"][13]).to.deep.equal({
|
||||||
Override: {
|
Override: {
|
||||||
_attr: {
|
_attr: {
|
||||||
ContentType: "application/vnd.openxmlformats-officedocument.custom-properties+xml",
|
ContentType: "application/vnd.openxmlformats-officedocument.custom-properties+xml",
|
||||||
@ -70,7 +71,7 @@ describe("ContentTypes", () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(tree["Types"][13]).to.deep.equal({
|
expect(tree["Types"][14]).to.deep.equal({
|
||||||
Override: {
|
Override: {
|
||||||
_attr: {
|
_attr: {
|
||||||
ContentType: "application/vnd.openxmlformats-officedocument.extended-properties+xml",
|
ContentType: "application/vnd.openxmlformats-officedocument.extended-properties+xml",
|
||||||
@ -78,7 +79,7 @@ describe("ContentTypes", () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(tree["Types"][14]).to.deep.equal({
|
expect(tree["Types"][15]).to.deep.equal({
|
||||||
Override: {
|
Override: {
|
||||||
_attr: {
|
_attr: {
|
||||||
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml",
|
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml",
|
||||||
@ -86,7 +87,7 @@ describe("ContentTypes", () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(tree["Types"][15]).to.deep.equal({
|
expect(tree["Types"][16]).to.deep.equal({
|
||||||
Override: {
|
Override: {
|
||||||
_attr: {
|
_attr: {
|
||||||
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml",
|
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml",
|
||||||
@ -94,7 +95,7 @@ describe("ContentTypes", () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(tree["Types"][16]).to.deep.equal({
|
expect(tree["Types"][17]).to.deep.equal({
|
||||||
Override: {
|
Override: {
|
||||||
_attr: {
|
_attr: {
|
||||||
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml",
|
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml",
|
||||||
@ -111,7 +112,7 @@ describe("ContentTypes", () => {
|
|||||||
contentTypes.addFooter(102);
|
contentTypes.addFooter(102);
|
||||||
const tree = new Formatter().format(contentTypes);
|
const tree = new Formatter().format(contentTypes);
|
||||||
|
|
||||||
expect(tree["Types"][19]).to.deep.equal({
|
expect(tree["Types"][20]).to.deep.equal({
|
||||||
Override: {
|
Override: {
|
||||||
_attr: {
|
_attr: {
|
||||||
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml",
|
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml",
|
||||||
@ -120,7 +121,7 @@ describe("ContentTypes", () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(tree["Types"][20]).to.deep.equal({
|
expect(tree["Types"][21]).to.deep.equal({
|
||||||
Override: {
|
Override: {
|
||||||
_attr: {
|
_attr: {
|
||||||
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml",
|
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml",
|
||||||
@ -137,7 +138,7 @@ describe("ContentTypes", () => {
|
|||||||
contentTypes.addHeader(202);
|
contentTypes.addHeader(202);
|
||||||
const tree = new Formatter().format(contentTypes);
|
const tree = new Formatter().format(contentTypes);
|
||||||
|
|
||||||
expect(tree["Types"][19]).to.deep.equal({
|
expect(tree["Types"][20]).to.deep.equal({
|
||||||
Override: {
|
Override: {
|
||||||
_attr: {
|
_attr: {
|
||||||
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml",
|
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml",
|
||||||
@ -146,7 +147,7 @@ describe("ContentTypes", () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(tree["Types"][20]).to.deep.equal({
|
expect(tree["Types"][21]).to.deep.equal({
|
||||||
Override: {
|
Override: {
|
||||||
_attr: {
|
_attr: {
|
||||||
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml",
|
ContentType: "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml",
|
||||||
|
@ -18,6 +18,7 @@ export class ContentTypes extends XmlComponent {
|
|||||||
this.root.push(new Default("image/jpeg", "jpg"));
|
this.root.push(new Default("image/jpeg", "jpg"));
|
||||||
this.root.push(new Default("image/bmp", "bmp"));
|
this.root.push(new Default("image/bmp", "bmp"));
|
||||||
this.root.push(new Default("image/gif", "gif"));
|
this.root.push(new Default("image/gif", "gif"));
|
||||||
|
this.root.push(new Default("image/svg+xml", "svg"));
|
||||||
this.root.push(new Default("application/vnd.openxmlformats-package.relationships+xml", "rels"));
|
this.root.push(new Default("application/vnd.openxmlformats-package.relationships+xml", "rels"));
|
||||||
this.root.push(new Default("application/xml", "xml"));
|
this.root.push(new Default("application/xml", "xml"));
|
||||||
this.root.push(new Default("application/vnd.openxmlformats-officedocument.obfuscatedFont", "odttf"));
|
this.root.push(new Default("application/vnd.openxmlformats-officedocument.obfuscatedFont", "odttf"));
|
||||||
|
@ -3,6 +3,8 @@ import { ICompatibilityOptions } from "@file/settings/compatibility";
|
|||||||
import { FontOptions } from "@file/fonts/font-table";
|
import { FontOptions } from "@file/fonts/font-table";
|
||||||
import { StringContainer, XmlComponent } from "@file/xml-components";
|
import { StringContainer, XmlComponent } from "@file/xml-components";
|
||||||
import { dateTimeValue } from "@util/values";
|
import { dateTimeValue } from "@util/values";
|
||||||
|
import { IHyphenationOptions } from "@file/settings";
|
||||||
|
import { IFootnoteProperties } from "@file/settings/footnote-properties";
|
||||||
|
|
||||||
import { ICustomPropertyOptions } from "../custom-properties";
|
import { ICustomPropertyOptions } from "../custom-properties";
|
||||||
import { IDocumentBackgroundOptions } from "../document";
|
import { IDocumentBackgroundOptions } from "../document";
|
||||||
@ -42,6 +44,8 @@ export interface IPropertiesOptions {
|
|||||||
readonly evenAndOddHeaderAndFooters?: boolean;
|
readonly evenAndOddHeaderAndFooters?: boolean;
|
||||||
readonly defaultTabStop?: number;
|
readonly defaultTabStop?: number;
|
||||||
readonly fonts?: readonly FontOptions[];
|
readonly fonts?: readonly FontOptions[];
|
||||||
|
readonly hyphenation?: IHyphenationOptions;
|
||||||
|
readonly footnoteProperties?: IFootnoteProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
// <xs:element name="coreProperties" type="CT_CoreProperties"/>
|
// <xs:element name="coreProperties" type="CT_CoreProperties"/>
|
||||||
|
@ -5,6 +5,7 @@ import { FooterWrapper } from "@file/footer-wrapper";
|
|||||||
import { HeaderWrapper } from "@file/header-wrapper";
|
import { HeaderWrapper } from "@file/header-wrapper";
|
||||||
import { VerticalAlign, VerticalAlignElement } from "@file/vertical-align";
|
import { VerticalAlign, VerticalAlignElement } from "@file/vertical-align";
|
||||||
import { OnOffElement, XmlComponent } from "@file/xml-components";
|
import { OnOffElement, XmlComponent } from "@file/xml-components";
|
||||||
|
import { FootnoteProperties, IFootnoteProperties } from "@file/settings/footnote-properties";
|
||||||
|
|
||||||
import { HeaderFooterReference, HeaderFooterReferenceType, HeaderFooterType } from "./properties/header-footer-reference";
|
import { HeaderFooterReference, HeaderFooterReferenceType, HeaderFooterType } from "./properties/header-footer-reference";
|
||||||
import { Columns, IColumnsAttributes } from "./properties/columns";
|
import { Columns, IColumnsAttributes } from "./properties/columns";
|
||||||
@ -39,6 +40,7 @@ export interface ISectionPropertiesOptions {
|
|||||||
readonly verticalAlign?: (typeof VerticalAlign)[keyof typeof VerticalAlign];
|
readonly verticalAlign?: (typeof VerticalAlign)[keyof typeof VerticalAlign];
|
||||||
readonly column?: IColumnsAttributes;
|
readonly column?: IColumnsAttributes;
|
||||||
readonly type?: (typeof SectionType)[keyof typeof SectionType];
|
readonly type?: (typeof SectionType)[keyof typeof SectionType];
|
||||||
|
readonly footnoteProperties?: IFootnoteProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
// <xsd:complexType name="CT_SectPr">
|
// <xsd:complexType name="CT_SectPr">
|
||||||
@ -119,6 +121,7 @@ export class SectionProperties extends XmlComponent {
|
|||||||
verticalAlign,
|
verticalAlign,
|
||||||
column,
|
column,
|
||||||
type,
|
type,
|
||||||
|
footnoteProperties,
|
||||||
}: ISectionPropertiesOptions = {}) {
|
}: ISectionPropertiesOptions = {}) {
|
||||||
super("w:sectPr");
|
super("w:sectPr");
|
||||||
|
|
||||||
@ -158,6 +161,10 @@ export class SectionProperties extends XmlComponent {
|
|||||||
this.root.push(new PageTextDirection(textDirection));
|
this.root.push(new PageTextDirection(textDirection));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (footnoteProperties) {
|
||||||
|
this.root.push(new FootnoteProperties(footnoteProperties));
|
||||||
|
}
|
||||||
|
|
||||||
this.root.push(new DocumentGrid(linePitch, charSpace, gridType));
|
this.root.push(new DocumentGrid(linePitch, charSpace, gridType));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,8 +11,9 @@ import { Anchor } from "./anchor";
|
|||||||
const createAnchor = (drawingOptions: IDrawingOptions): Anchor =>
|
const createAnchor = (drawingOptions: IDrawingOptions): Anchor =>
|
||||||
new Anchor({
|
new Anchor({
|
||||||
mediaData: {
|
mediaData: {
|
||||||
|
type: "png",
|
||||||
fileName: "test.png",
|
fileName: "test.png",
|
||||||
stream: Buffer.from(""),
|
data: Buffer.from(""),
|
||||||
transformation: {
|
transformation: {
|
||||||
pixels: {
|
pixels: {
|
||||||
x: 0,
|
x: 0,
|
||||||
|
@ -11,8 +11,9 @@ const imageBase64Data = `iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAMAAAD04JH5AAACzVBMVEU
|
|||||||
const createDrawing = (drawingOptions?: IDrawingOptions): Drawing =>
|
const createDrawing = (drawingOptions?: IDrawingOptions): Drawing =>
|
||||||
new Drawing(
|
new Drawing(
|
||||||
{
|
{
|
||||||
|
type: "jpg",
|
||||||
fileName: "test.jpg",
|
fileName: "test.jpg",
|
||||||
stream: Buffer.from(imageBase64Data, "base64"),
|
data: Buffer.from(imageBase64Data, "base64"),
|
||||||
transformation: {
|
transformation: {
|
||||||
pixels: {
|
pixels: {
|
||||||
x: 100,
|
x: 100,
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
import { BuilderElement, XmlComponent } from "@file/xml-components";
|
||||||
|
import { IMediaData } from "@file/media";
|
||||||
|
|
||||||
|
const createSvgBlip = (mediaData: IMediaData): XmlComponent =>
|
||||||
|
new BuilderElement({
|
||||||
|
name: "asvg:svgBlip",
|
||||||
|
attributes: {
|
||||||
|
asvg: {
|
||||||
|
key: "xmlns:asvg",
|
||||||
|
value: "http://schemas.microsoft.com/office/drawing/2016/SVG/main",
|
||||||
|
},
|
||||||
|
embed: {
|
||||||
|
key: "r:embed",
|
||||||
|
value: `rId{${mediaData.fileName}}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const createExtention = (mediaData: IMediaData): XmlComponent =>
|
||||||
|
new BuilderElement({
|
||||||
|
name: "a:ext",
|
||||||
|
attributes: {
|
||||||
|
uri: {
|
||||||
|
key: "uri",
|
||||||
|
value: "{96DAC541-7B7A-43D3-8B79-37D633B846F1}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
children: [createSvgBlip(mediaData)],
|
||||||
|
});
|
||||||
|
|
||||||
|
export const createExtentionList = (mediaData: IMediaData): XmlComponent =>
|
||||||
|
new BuilderElement({
|
||||||
|
name: "a:extLst",
|
||||||
|
children: [createExtention(mediaData)],
|
||||||
|
});
|
@ -1,7 +1,7 @@
|
|||||||
import { IMediaData } from "@file/media";
|
import { IMediaData } from "@file/media";
|
||||||
import { XmlComponent } from "@file/xml-components";
|
import { XmlComponent } from "@file/xml-components";
|
||||||
|
|
||||||
import { Blip } from "./blip";
|
import { createBlip } from "./blip";
|
||||||
import { SourceRectangle } from "./source-rectangle";
|
import { SourceRectangle } from "./source-rectangle";
|
||||||
import { Stretch } from "./stretch";
|
import { Stretch } from "./stretch";
|
||||||
|
|
||||||
@ -9,7 +9,7 @@ export class BlipFill extends XmlComponent {
|
|||||||
public constructor(mediaData: IMediaData) {
|
public constructor(mediaData: IMediaData) {
|
||||||
super("pic:blipFill");
|
super("pic:blipFill");
|
||||||
|
|
||||||
this.root.push(new Blip(mediaData));
|
this.root.push(createBlip(mediaData));
|
||||||
this.root.push(new SourceRectangle());
|
this.root.push(new SourceRectangle());
|
||||||
this.root.push(new Stretch());
|
this.root.push(new Stretch());
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,24 @@
|
|||||||
import { IMediaData } from "@file/media";
|
import { IMediaData } from "@file/media";
|
||||||
import { XmlAttributeComponent, XmlComponent } from "@file/xml-components";
|
import { BuilderElement, XmlComponent } from "@file/xml-components";
|
||||||
|
import { createExtentionList } from "./blip-extentions";
|
||||||
|
|
||||||
class BlipAttributes extends XmlAttributeComponent<{
|
type BlipAttributes = {
|
||||||
readonly embed: string;
|
readonly embed: string;
|
||||||
readonly cstate: string;
|
readonly cstate: string;
|
||||||
}> {
|
};
|
||||||
protected readonly xmlKeys = {
|
|
||||||
embed: "r:embed",
|
|
||||||
cstate: "cstate",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Blip extends XmlComponent {
|
export const createBlip = (mediaData: IMediaData): XmlComponent =>
|
||||||
public constructor(mediaData: IMediaData) {
|
new BuilderElement<BlipAttributes>({
|
||||||
super("a:blip");
|
name: "a:blip",
|
||||||
this.root.push(
|
attributes: {
|
||||||
new BlipAttributes({
|
embed: {
|
||||||
embed: `rId{${mediaData.fileName}}`,
|
key: "r:embed",
|
||||||
cstate: "none",
|
value: `rId{${mediaData.type === "svg" ? mediaData.fallback.fileName : mediaData.fileName}}`,
|
||||||
}),
|
},
|
||||||
);
|
cstate: {
|
||||||
}
|
key: "cstate",
|
||||||
}
|
value: "none",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
children: mediaData.type === "svg" ? [createExtentionList(mediaData)] : [],
|
||||||
|
});
|
||||||
|
@ -8,8 +8,9 @@ describe("Inline", () => {
|
|||||||
const tree = new Formatter().format(
|
const tree = new Formatter().format(
|
||||||
createInline({
|
createInline({
|
||||||
mediaData: {
|
mediaData: {
|
||||||
|
type: "png",
|
||||||
fileName: "test.png",
|
fileName: "test.png",
|
||||||
stream: Buffer.from(""),
|
data: Buffer.from(""),
|
||||||
transformation: {
|
transformation: {
|
||||||
pixels: {
|
pixels: {
|
||||||
x: 0,
|
x: 0,
|
||||||
|
@ -436,7 +436,44 @@ describe("File", () => {
|
|||||||
it("should work with external styles", () => {
|
it("should work with external styles", () => {
|
||||||
const doc = new File({
|
const doc = new File({
|
||||||
sections: [],
|
sections: [],
|
||||||
externalStyles: "",
|
externalStyles: `
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<w:styles xmlns:mc="first" xmlns:r="second">
|
||||||
|
<w:docDefaults>
|
||||||
|
<w:rPrDefault>
|
||||||
|
<w:rPr>
|
||||||
|
<w:rFonts w:ascii="Arial" w:eastAsiaTheme="minorHAnsi" w:hAnsi="Arial" w:cstheme="minorHAnsi"/>
|
||||||
|
<w:lang w:val="en-US" w:eastAsia="en-US" w:bidi="ar-SA"/>
|
||||||
|
</w:rPr>
|
||||||
|
</w:rPrDefault>
|
||||||
|
<w:pPrDefault>
|
||||||
|
<w:pPr>
|
||||||
|
<w:spacing w:after="160" w:line="259" w:lineRule="auto"/>
|
||||||
|
</w:pPr>
|
||||||
|
</w:pPrDefault>
|
||||||
|
</w:docDefaults>
|
||||||
|
|
||||||
|
<w:latentStyles w:defLockedState="1" w:defUIPriority="99">
|
||||||
|
</w:latentStyles>
|
||||||
|
|
||||||
|
<w:style w:type="paragraph" w:default="1" w:styleId="Normal">
|
||||||
|
<w:name w:val="Normal"/>
|
||||||
|
<w:qFormat/>
|
||||||
|
</w:style>
|
||||||
|
|
||||||
|
<w:style w:type="paragraph" w:styleId="Heading1">
|
||||||
|
<w:name w:val="heading 1"/>
|
||||||
|
<w:basedOn w:val="Normal"/>
|
||||||
|
<w:pPr>
|
||||||
|
<w:keepNext/>
|
||||||
|
<w:keepLines/>
|
||||||
|
|
||||||
|
<w:pBdr>
|
||||||
|
<w:bottom w:val="single" w:sz="4" w:space="1" w:color="auto"/>
|
||||||
|
</w:pBdr>
|
||||||
|
</w:pPr>
|
||||||
|
</w:style>
|
||||||
|
</w:styles>`,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(doc.Styles).to.not.be.undefined;
|
expect(doc.Styles).to.not.be.undefined;
|
||||||
|
@ -80,11 +80,17 @@ export class File {
|
|||||||
trackRevisions: options.features?.trackRevisions,
|
trackRevisions: options.features?.trackRevisions,
|
||||||
updateFields: options.features?.updateFields,
|
updateFields: options.features?.updateFields,
|
||||||
defaultTabStop: options.defaultTabStop,
|
defaultTabStop: options.defaultTabStop,
|
||||||
|
hyphenation: {
|
||||||
|
autoHyphenation: options.hyphenation?.autoHyphenation,
|
||||||
|
hyphenationZone: options.hyphenation?.hyphenationZone,
|
||||||
|
consecutiveHyphenLimit: options.hyphenation?.consecutiveHyphenLimit,
|
||||||
|
doNotHyphenateCaps: options.hyphenation?.doNotHyphenateCaps,
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.media = new Media();
|
this.media = new Media();
|
||||||
|
|
||||||
if (options.externalStyles) {
|
if (options.externalStyles !== undefined) {
|
||||||
const stylesFactory = new ExternalStylesFactory();
|
const stylesFactory = new ExternalStylesFactory();
|
||||||
this.styles = stylesFactory.newInstance(options.externalStyles);
|
this.styles = stylesFactory.newInstance(options.externalStyles);
|
||||||
} else if (options.styles) {
|
} else if (options.styles) {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
export * from "./paragraph";
|
export * from "./paragraph";
|
||||||
export * from "./table";
|
export * from "./table";
|
||||||
export * from "./file";
|
export * from "./file";
|
||||||
|
export * from "./file-child";
|
||||||
export * from "./numbering";
|
export * from "./numbering";
|
||||||
export * from "./media";
|
export * from "./media";
|
||||||
export * from "./drawing";
|
export * from "./drawing";
|
||||||
|
@ -14,11 +14,25 @@ export interface IMediaDataTransformation {
|
|||||||
readonly rotation?: number;
|
readonly rotation?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IMediaData {
|
type CoreMediaData = {
|
||||||
readonly stream: Buffer | Uint8Array | ArrayBuffer;
|
|
||||||
readonly fileName: string;
|
readonly fileName: string;
|
||||||
readonly transformation: IMediaDataTransformation;
|
readonly transformation: IMediaDataTransformation;
|
||||||
}
|
readonly data: Buffer | Uint8Array | ArrayBuffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
type RegularMediaData = {
|
||||||
|
readonly type: "jpg" | "png" | "gif" | "bmp";
|
||||||
|
};
|
||||||
|
|
||||||
|
type SvgMediaData = {
|
||||||
|
readonly type: "svg";
|
||||||
|
/**
|
||||||
|
* Required in case the Word processor does not support SVG.
|
||||||
|
*/
|
||||||
|
readonly fallback: RegularMediaData & CoreMediaData;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type IMediaData = (RegularMediaData | SvgMediaData) & CoreMediaData;
|
||||||
|
|
||||||
// Needed because of: https://github.com/s-panferov/awesome-typescript-loader/issues/432
|
// Needed because of: https://github.com/s-panferov/awesome-typescript-loader/issues/432
|
||||||
/**
|
/**
|
||||||
|
@ -19,7 +19,8 @@ describe("Media", () => {
|
|||||||
const media = new Media();
|
const media = new Media();
|
||||||
|
|
||||||
media.addImage("test2.png", {
|
media.addImage("test2.png", {
|
||||||
stream: Buffer.from(""),
|
type: "png",
|
||||||
|
data: Buffer.from(""),
|
||||||
fileName: "test.png",
|
fileName: "test.png",
|
||||||
transformation: {
|
transformation: {
|
||||||
pixels: {
|
pixels: {
|
||||||
|
@ -4,6 +4,7 @@ import { Formatter } from "@export/formatter";
|
|||||||
|
|
||||||
import { AlignmentType, EmphasisMarkType, TabStopPosition } from "../paragraph";
|
import { AlignmentType, EmphasisMarkType, TabStopPosition } from "../paragraph";
|
||||||
import { UnderlineType } from "../paragraph/run/underline";
|
import { UnderlineType } from "../paragraph/run/underline";
|
||||||
|
import { HighlightColor } from "../paragraph/run/properties";
|
||||||
import { ShadingType } from "../shading";
|
import { ShadingType } from "../shading";
|
||||||
import { AbstractNumbering } from "./abstract-numbering";
|
import { AbstractNumbering } from "./abstract-numbering";
|
||||||
import { LevelFormat, LevelSuffix } from "./level";
|
import { LevelFormat, LevelSuffix } from "./level";
|
||||||
@ -2048,23 +2049,23 @@ describe("AbstractNumbering", () => {
|
|||||||
|
|
||||||
const highlightTests = [
|
const highlightTests = [
|
||||||
{
|
{
|
||||||
highlight: "005599",
|
highlight: HighlightColor.YELLOW,
|
||||||
expected: [{ "w:highlight": { _attr: { "w:val": "005599" } } }, { "w:highlightCs": { _attr: { "w:val": "005599" } } }],
|
expected: [{ "w:highlight": { _attr: { "w:val": "yellow" } } }, { "w:highlightCs": { _attr: { "w:val": "yellow" } } }],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
highlight: "005599",
|
highlight: HighlightColor.YELLOW,
|
||||||
highlightComplexScript: true,
|
highlightComplexScript: true,
|
||||||
expected: [{ "w:highlight": { _attr: { "w:val": "005599" } } }, { "w:highlightCs": { _attr: { "w:val": "005599" } } }],
|
expected: [{ "w:highlight": { _attr: { "w:val": "yellow" } } }, { "w:highlightCs": { _attr: { "w:val": "yellow" } } }],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
highlight: "005599",
|
highlight: HighlightColor.YELLOW,
|
||||||
highlightComplexScript: false,
|
highlightComplexScript: false,
|
||||||
expected: [{ "w:highlight": { _attr: { "w:val": "005599" } } }],
|
expected: [{ "w:highlight": { _attr: { "w:val": "yellow" } } }],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
highlight: "005599",
|
highlight: HighlightColor.YELLOW,
|
||||||
highlightComplexScript: "550099",
|
highlightComplexScript: "550099",
|
||||||
expected: [{ "w:highlight": { _attr: { "w:val": "005599" } } }, { "w:highlightCs": { _attr: { "w:val": "550099" } } }],
|
expected: [{ "w:highlight": { _attr: { "w:val": "yellow" } } }, { "w:highlightCs": { _attr: { "w:val": "550099" } } }],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
highlightTests.forEach(({ highlight, highlightComplexScript, expected }) => {
|
highlightTests.forEach(({ highlight, highlightComplexScript, expected }) => {
|
||||||
|
@ -6,3 +6,6 @@ export * from "./math-sub-script";
|
|||||||
export * from "./math-sum";
|
export * from "./math-sum";
|
||||||
export * from "./math-integral";
|
export * from "./math-integral";
|
||||||
export * from "./math-super-script";
|
export * from "./math-super-script";
|
||||||
|
export * from "./math-limit";
|
||||||
|
export * from "./math-limit-upper";
|
||||||
|
export * from "./math-limit-lower";
|
||||||
|
@ -22,7 +22,7 @@ describe("MathIntegral", () => {
|
|||||||
{
|
{
|
||||||
"m:limLoc": {
|
"m:limLoc": {
|
||||||
_attr: {
|
_attr: {
|
||||||
"m:val": "undOvr",
|
"m:val": "subSup",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -78,7 +78,7 @@ describe("MathIntegral", () => {
|
|||||||
{
|
{
|
||||||
"m:limLoc": {
|
"m:limLoc": {
|
||||||
_attr: {
|
_attr: {
|
||||||
"m:val": "undOvr",
|
"m:val": "subSup",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -16,7 +16,7 @@ export class MathIntegral extends XmlComponent {
|
|||||||
public constructor(options: IMathIntegralOptions) {
|
public constructor(options: IMathIntegralOptions) {
|
||||||
super("m:nary");
|
super("m:nary");
|
||||||
|
|
||||||
this.root.push(new MathNAryProperties("", !!options.superScript, !!options.subScript));
|
this.root.push(new MathNAryProperties("", !!options.superScript, !!options.subScript, "subSup"));
|
||||||
|
|
||||||
if (!!options.subScript) {
|
if (!!options.subScript) {
|
||||||
this.root.push(new MathSubScriptElement(options.subScript));
|
this.root.push(new MathSubScriptElement(options.subScript));
|
||||||
|
@ -6,9 +6,9 @@ class MathLimitLocationAttributes extends XmlAttributeComponent<{ readonly value
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class MathLimitLocation extends XmlComponent {
|
export class MathLimitLocation extends XmlComponent {
|
||||||
public constructor() {
|
public constructor(value?: string) {
|
||||||
super("m:limLoc");
|
super("m:limLoc");
|
||||||
|
|
||||||
this.root.push(new MathLimitLocationAttributes({ value: "undOvr" }));
|
this.root.push(new MathLimitLocationAttributes({ value: value || "undOvr" }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
45
src/file/paragraph/math/n-ary/math-limit-lower.spec.ts
Normal file
45
src/file/paragraph/math/n-ary/math-limit-lower.spec.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
import { Formatter } from "@export/formatter";
|
||||||
|
|
||||||
|
import { MathRun } from "../math-run";
|
||||||
|
import { MathLimitLower } from "./math-limit-lower";
|
||||||
|
|
||||||
|
describe("MathLimitLower", () => {
|
||||||
|
describe("#constructor()", () => {
|
||||||
|
it("should create a MathLimitLower with correct root key", () => {
|
||||||
|
const mathLimitLower = new MathLimitLower({
|
||||||
|
children: [new MathRun("lim")],
|
||||||
|
limit: [new MathRun("x→0")],
|
||||||
|
});
|
||||||
|
|
||||||
|
const tree = new Formatter().format(mathLimitLower);
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"m:limLow": [
|
||||||
|
{
|
||||||
|
"m:e": [
|
||||||
|
{
|
||||||
|
"m:r": [
|
||||||
|
{
|
||||||
|
"m:t": ["lim"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"m:lim": [
|
||||||
|
{
|
||||||
|
"m:r": [
|
||||||
|
{
|
||||||
|
"m:t": ["x→0"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
19
src/file/paragraph/math/n-ary/math-limit-lower.ts
Normal file
19
src/file/paragraph/math/n-ary/math-limit-lower.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// http://www.datypic.com/sc/ooxml/e-m_limLow-1.html
|
||||||
|
import { XmlComponent } from "@file/xml-components";
|
||||||
|
import { MathComponent } from "../math-component";
|
||||||
|
import { MathBase } from "./math-base";
|
||||||
|
import { MathLimit } from "./math-limit";
|
||||||
|
|
||||||
|
export interface IMathLimitLowerOptions {
|
||||||
|
readonly children: readonly MathComponent[];
|
||||||
|
readonly limit: readonly MathComponent[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MathLimitLower extends XmlComponent {
|
||||||
|
public constructor(options: IMathLimitLowerOptions) {
|
||||||
|
super("m:limLow");
|
||||||
|
|
||||||
|
this.root.push(new MathBase(options.children));
|
||||||
|
this.root.push(new MathLimit(options.limit));
|
||||||
|
}
|
||||||
|
}
|
45
src/file/paragraph/math/n-ary/math-limit-upper.spec.ts
Normal file
45
src/file/paragraph/math/n-ary/math-limit-upper.spec.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
import { Formatter } from "@export/formatter";
|
||||||
|
|
||||||
|
import { MathRun } from "../math-run";
|
||||||
|
import { MathLimitUpper } from "./math-limit-upper";
|
||||||
|
|
||||||
|
describe("MathLimitUpper", () => {
|
||||||
|
describe("#constructor()", () => {
|
||||||
|
it("should create a MathLimitUpper with correct root key", () => {
|
||||||
|
const mathLimitUpper = new MathLimitUpper({
|
||||||
|
children: [new MathRun("x")],
|
||||||
|
limit: [new MathRun("-")],
|
||||||
|
});
|
||||||
|
|
||||||
|
const tree = new Formatter().format(mathLimitUpper);
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"m:limUpp": [
|
||||||
|
{
|
||||||
|
"m:e": [
|
||||||
|
{
|
||||||
|
"m:r": [
|
||||||
|
{
|
||||||
|
"m:t": ["x"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"m:lim": [
|
||||||
|
{
|
||||||
|
"m:r": [
|
||||||
|
{
|
||||||
|
"m:t": ["-"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
19
src/file/paragraph/math/n-ary/math-limit-upper.ts
Normal file
19
src/file/paragraph/math/n-ary/math-limit-upper.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// http://www.datypic.com/sc/ooxml/e-m_limUpp-1.html
|
||||||
|
import { XmlComponent } from "@file/xml-components";
|
||||||
|
import { MathComponent } from "../math-component";
|
||||||
|
import { MathBase } from "./math-base";
|
||||||
|
import { MathLimit } from "./math-limit";
|
||||||
|
|
||||||
|
export interface IMathLimitUpperOptions {
|
||||||
|
readonly children: readonly MathComponent[];
|
||||||
|
readonly limit: readonly MathComponent[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MathLimitUpper extends XmlComponent {
|
||||||
|
public constructor(options: IMathLimitUpperOptions) {
|
||||||
|
super("m:limUpp");
|
||||||
|
|
||||||
|
this.root.push(new MathBase(options.children));
|
||||||
|
this.root.push(new MathLimit(options.limit));
|
||||||
|
}
|
||||||
|
}
|
27
src/file/paragraph/math/n-ary/math-limit.spec.ts
Normal file
27
src/file/paragraph/math/n-ary/math-limit.spec.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
import { Formatter } from "@export/formatter";
|
||||||
|
|
||||||
|
import { MathRun } from "../math-run";
|
||||||
|
import { MathLimit } from "./math-limit";
|
||||||
|
|
||||||
|
describe("MathLimit", () => {
|
||||||
|
describe("#constructor()", () => {
|
||||||
|
it("should create a MathLimit with correct root key", () => {
|
||||||
|
const mathLimit = new MathLimit([new MathRun("x→0")]);
|
||||||
|
|
||||||
|
const tree = new Formatter().format(mathLimit);
|
||||||
|
expect(tree).to.deep.equal({
|
||||||
|
"m:lim": [
|
||||||
|
{
|
||||||
|
"m:r": [
|
||||||
|
{
|
||||||
|
"m:t": ["x→0"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
13
src/file/paragraph/math/n-ary/math-limit.ts
Normal file
13
src/file/paragraph/math/n-ary/math-limit.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// http://www.datypic.com/sc/ooxml/e-m_lim-1.html
|
||||||
|
import { XmlComponent } from "@file/xml-components";
|
||||||
|
import { MathComponent } from "../math-component";
|
||||||
|
|
||||||
|
export class MathLimit extends XmlComponent {
|
||||||
|
public constructor(children: readonly MathComponent[]) {
|
||||||
|
super("m:lim");
|
||||||
|
|
||||||
|
for (const child of children) {
|
||||||
|
this.root.push(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,13 +7,13 @@ import { MathSubScriptHide } from "./math-sub-script-hide";
|
|||||||
import { MathSuperScriptHide } from "./math-super-script-hide";
|
import { MathSuperScriptHide } from "./math-super-script-hide";
|
||||||
|
|
||||||
export class MathNAryProperties extends XmlComponent {
|
export class MathNAryProperties extends XmlComponent {
|
||||||
public constructor(accent: string, hasSuperScript: boolean, hasSubScript: boolean) {
|
public constructor(accent: string, hasSuperScript: boolean, hasSubScript: boolean, limitLocationVal?: string) {
|
||||||
super("m:naryPr");
|
super("m:naryPr");
|
||||||
|
|
||||||
if (!!accent) {
|
if (!!accent) {
|
||||||
this.root.push(new MathAccentCharacter(accent));
|
this.root.push(new MathAccentCharacter(accent));
|
||||||
}
|
}
|
||||||
this.root.push(new MathLimitLocation());
|
this.root.push(new MathLimitLocation(limitLocationVal));
|
||||||
|
|
||||||
if (!hasSuperScript) {
|
if (!hasSuperScript) {
|
||||||
this.root.push(new MathSuperScriptHide());
|
this.root.push(new MathSuperScriptHide());
|
||||||
|
@ -19,6 +19,7 @@ describe("ImageRun", () => {
|
|||||||
describe("#constructor()", () => {
|
describe("#constructor()", () => {
|
||||||
it("should create with Buffer", () => {
|
it("should create with Buffer", () => {
|
||||||
const currentImageRun = new ImageRun({
|
const currentImageRun = new ImageRun({
|
||||||
|
type: "png",
|
||||||
data: Buffer.from(""),
|
data: Buffer.from(""),
|
||||||
transformation: {
|
transformation: {
|
||||||
width: 200,
|
width: 200,
|
||||||
@ -39,8 +40,7 @@ describe("ImageRun", () => {
|
|||||||
const tree = new Formatter().format(currentImageRun, {
|
const tree = new Formatter().format(currentImageRun, {
|
||||||
file: {
|
file: {
|
||||||
Media: {
|
Media: {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
addImage: vi.fn(),
|
||||||
addImage: () => {},
|
|
||||||
},
|
},
|
||||||
} as unknown as File,
|
} as unknown as File,
|
||||||
viewWrapper: {} as unknown as IViewWrapper,
|
viewWrapper: {} as unknown as IViewWrapper,
|
||||||
@ -271,6 +271,7 @@ describe("ImageRun", () => {
|
|||||||
|
|
||||||
it("should create with string", () => {
|
it("should create with string", () => {
|
||||||
const currentImageRun = new ImageRun({
|
const currentImageRun = new ImageRun({
|
||||||
|
type: "png",
|
||||||
data: "",
|
data: "",
|
||||||
transformation: {
|
transformation: {
|
||||||
width: 200,
|
width: 200,
|
||||||
@ -291,8 +292,7 @@ describe("ImageRun", () => {
|
|||||||
const tree = new Formatter().format(currentImageRun, {
|
const tree = new Formatter().format(currentImageRun, {
|
||||||
file: {
|
file: {
|
||||||
Media: {
|
Media: {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
addImage: vi.fn(),
|
||||||
addImage: () => {},
|
|
||||||
},
|
},
|
||||||
} as unknown as File,
|
} as unknown as File,
|
||||||
viewWrapper: {} as unknown as IViewWrapper,
|
viewWrapper: {} as unknown as IViewWrapper,
|
||||||
@ -522,10 +522,10 @@ describe("ImageRun", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should return UInt8Array if atob is present", () => {
|
it("should return UInt8Array if atob is present", () => {
|
||||||
// eslint-disable-next-line functional/immutable-data
|
vi.spyOn(global, "atob").mockReturnValue("atob result");
|
||||||
global.atob = () => "atob result";
|
|
||||||
|
|
||||||
const currentImageRun = new ImageRun({
|
const currentImageRun = new ImageRun({
|
||||||
|
type: "png",
|
||||||
data: "",
|
data: "",
|
||||||
transformation: {
|
transformation: {
|
||||||
width: 200,
|
width: 200,
|
||||||
@ -546,8 +546,7 @@ describe("ImageRun", () => {
|
|||||||
const tree = new Formatter().format(currentImageRun, {
|
const tree = new Formatter().format(currentImageRun, {
|
||||||
file: {
|
file: {
|
||||||
Media: {
|
Media: {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
addImage: vi.fn(),
|
||||||
addImage: () => {},
|
|
||||||
},
|
},
|
||||||
} as unknown as File,
|
} as unknown as File,
|
||||||
viewWrapper: {} as unknown as IViewWrapper,
|
viewWrapper: {} as unknown as IViewWrapper,
|
||||||
@ -775,16 +774,13 @@ describe("ImageRun", () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, functional/immutable-data
|
|
||||||
(global as any).atob = undefined;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should use data as is if its not a string", () => {
|
it("should use data as is if its not a string", () => {
|
||||||
// eslint-disable-next-line functional/immutable-data
|
vi.spyOn(global, "atob").mockReturnValue("atob result");
|
||||||
global.atob = () => "atob result";
|
|
||||||
|
|
||||||
const currentImageRun = new ImageRun({
|
const currentImageRun = new ImageRun({
|
||||||
|
type: "png",
|
||||||
data: "",
|
data: "",
|
||||||
transformation: {
|
transformation: {
|
||||||
width: 200,
|
width: 200,
|
||||||
@ -805,8 +801,7 @@ describe("ImageRun", () => {
|
|||||||
const tree = new Formatter().format(currentImageRun, {
|
const tree = new Formatter().format(currentImageRun, {
|
||||||
file: {
|
file: {
|
||||||
Media: {
|
Media: {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
addImage: vi.fn(),
|
||||||
addImage: () => {},
|
|
||||||
},
|
},
|
||||||
} as unknown as File,
|
} as unknown as File,
|
||||||
viewWrapper: {} as unknown as IViewWrapper,
|
viewWrapper: {} as unknown as IViewWrapper,
|
||||||
@ -1034,9 +1029,107 @@ describe("ImageRun", () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, functional/immutable-data
|
it("should strip base64 marker", () => {
|
||||||
(global as any).atob = undefined;
|
const spy = vi.spyOn(global, "atob").mockReturnValue("atob result");
|
||||||
|
|
||||||
|
new ImageRun({
|
||||||
|
type: "png",
|
||||||
|
data: ";base64,",
|
||||||
|
transformation: {
|
||||||
|
width: 200,
|
||||||
|
height: 200,
|
||||||
|
rotation: 45,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(spy).toBeCalledWith("");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should work with svgs", () => {
|
||||||
|
const currentImageRun = new ImageRun({
|
||||||
|
type: "svg",
|
||||||
|
data: Buffer.from(""),
|
||||||
|
transformation: {
|
||||||
|
width: 200,
|
||||||
|
height: 200,
|
||||||
|
},
|
||||||
|
fallback: {
|
||||||
|
type: "png",
|
||||||
|
data: Buffer.from(""),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const tree = new Formatter().format(currentImageRun, {
|
||||||
|
file: {
|
||||||
|
Media: {
|
||||||
|
addImage: vi.fn(),
|
||||||
|
},
|
||||||
|
} as unknown as File,
|
||||||
|
viewWrapper: {} as unknown as IViewWrapper,
|
||||||
|
stack: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree).toStrictEqual({
|
||||||
|
"w:r": [
|
||||||
|
{
|
||||||
|
"w:drawing": [
|
||||||
|
{
|
||||||
|
"wp:inline": expect.arrayContaining([
|
||||||
|
{
|
||||||
|
"a:graphic": expect.arrayContaining([
|
||||||
|
{
|
||||||
|
"a:graphicData": expect.arrayContaining([
|
||||||
|
{
|
||||||
|
"pic:pic": expect.arrayContaining([
|
||||||
|
{
|
||||||
|
"pic:blipFill": expect.arrayContaining([
|
||||||
|
{
|
||||||
|
"a:blip": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
cstate: "none",
|
||||||
|
"r:embed": "rId{test-unique-id.png}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"a:extLst": [
|
||||||
|
{
|
||||||
|
"a:ext": [
|
||||||
|
{
|
||||||
|
_attr: {
|
||||||
|
uri: "{96DAC541-7B7A-43D3-8B79-37D633B846F1}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"asvg:svgBlip": {
|
||||||
|
_attr: expect.objectContaining({
|
||||||
|
"r:embed":
|
||||||
|
"rId{test-unique-id.svg}",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -9,38 +9,102 @@ import { IMediaTransformation } from "../../media";
|
|||||||
import { IMediaData } from "../../media/data";
|
import { IMediaData } from "../../media/data";
|
||||||
import { Run } from "../run";
|
import { Run } from "../run";
|
||||||
|
|
||||||
export interface IImageOptions {
|
type CoreImageOptions = {
|
||||||
readonly data: Buffer | string | Uint8Array | ArrayBuffer;
|
|
||||||
readonly transformation: IMediaTransformation;
|
readonly transformation: IMediaTransformation;
|
||||||
readonly floating?: IFloating;
|
readonly floating?: IFloating;
|
||||||
readonly altText?: DocPropertiesOptions;
|
readonly altText?: DocPropertiesOptions;
|
||||||
readonly outline?: OutlineOptions;
|
readonly outline?: OutlineOptions;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
type RegularImageOptions = {
|
||||||
|
readonly type: "jpg" | "png" | "gif" | "bmp";
|
||||||
|
readonly data: Buffer | string | Uint8Array | ArrayBuffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
type SvgMediaOptions = {
|
||||||
|
readonly type: "svg";
|
||||||
|
readonly data: Buffer | string | Uint8Array | ArrayBuffer;
|
||||||
|
/**
|
||||||
|
* Required in case the Word processor does not support SVG.
|
||||||
|
*/
|
||||||
|
readonly fallback: RegularImageOptions;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type IImageOptions = (RegularImageOptions | SvgMediaOptions) & CoreImageOptions;
|
||||||
|
|
||||||
|
const convertDataURIToBinary = (dataURI: string): Uint8Array => {
|
||||||
|
if (typeof atob === "function") {
|
||||||
|
// https://gist.github.com/borismus/1032746
|
||||||
|
// https://github.com/mafintosh/base64-to-uint8array
|
||||||
|
const BASE64_MARKER = ";base64,";
|
||||||
|
const base64Index = dataURI.indexOf(BASE64_MARKER);
|
||||||
|
|
||||||
|
const base64IndexWithOffset = base64Index === -1 ? 0 : base64Index + BASE64_MARKER.length;
|
||||||
|
|
||||||
|
return new Uint8Array(
|
||||||
|
atob(dataURI.substring(base64IndexWithOffset))
|
||||||
|
.split("")
|
||||||
|
.map((c) => c.charCodeAt(0)),
|
||||||
|
);
|
||||||
|
/* c8 ignore next 6 */
|
||||||
|
} else {
|
||||||
|
// Not possible to test this branch in NodeJS
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
|
||||||
|
const b = require("buf" + "fer");
|
||||||
|
return new b.Buffer(dataURI, "base64");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const standardizeData = (data: string | Buffer | Uint8Array | ArrayBuffer): Buffer | Uint8Array | ArrayBuffer =>
|
||||||
|
typeof data === "string" ? convertDataURIToBinary(data) : data;
|
||||||
|
|
||||||
|
const createImageData = (options: IImageOptions, key: string): Pick<IMediaData, "data" | "fileName" | "transformation"> => ({
|
||||||
|
data: standardizeData(options.data),
|
||||||
|
fileName: key,
|
||||||
|
transformation: {
|
||||||
|
pixels: {
|
||||||
|
x: Math.round(options.transformation.width),
|
||||||
|
y: Math.round(options.transformation.height),
|
||||||
|
},
|
||||||
|
emus: {
|
||||||
|
x: Math.round(options.transformation.width * 9525),
|
||||||
|
y: Math.round(options.transformation.height * 9525),
|
||||||
|
},
|
||||||
|
flip: options.transformation.flip,
|
||||||
|
rotation: options.transformation.rotation ? options.transformation.rotation * 60000 : undefined,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
export class ImageRun extends Run {
|
export class ImageRun extends Run {
|
||||||
private readonly key = `${uniqueId()}.png`;
|
private readonly key: string;
|
||||||
|
private readonly fallbackKey = `${uniqueId()}.png`;
|
||||||
private readonly imageData: IMediaData;
|
private readonly imageData: IMediaData;
|
||||||
|
|
||||||
public constructor(options: IImageOptions) {
|
public constructor(options: IImageOptions) {
|
||||||
super({});
|
super({});
|
||||||
const newData = typeof options.data === "string" ? this.convertDataURIToBinary(options.data) : options.data;
|
|
||||||
|
|
||||||
this.imageData = {
|
this.key = `${uniqueId()}.${options.type}`;
|
||||||
stream: newData,
|
|
||||||
fileName: this.key,
|
this.imageData =
|
||||||
transformation: {
|
options.type === "svg"
|
||||||
pixels: {
|
? {
|
||||||
x: Math.round(options.transformation.width),
|
type: options.type,
|
||||||
y: Math.round(options.transformation.height),
|
...createImageData(options, this.key),
|
||||||
},
|
fallback: {
|
||||||
emus: {
|
type: options.fallback.type,
|
||||||
x: Math.round(options.transformation.width * 9525),
|
...createImageData(
|
||||||
y: Math.round(options.transformation.height * 9525),
|
{
|
||||||
},
|
...options.fallback,
|
||||||
flip: options.transformation.flip,
|
transformation: options.transformation,
|
||||||
rotation: options.transformation.rotation ? options.transformation.rotation * 60000 : undefined,
|
},
|
||||||
},
|
this.fallbackKey,
|
||||||
};
|
),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
type: options.type,
|
||||||
|
...createImageData(options, this.key),
|
||||||
|
};
|
||||||
const drawing = new Drawing(this.imageData, {
|
const drawing = new Drawing(this.imageData, {
|
||||||
floating: options.floating,
|
floating: options.floating,
|
||||||
docProperties: options.altText,
|
docProperties: options.altText,
|
||||||
@ -53,29 +117,10 @@ export class ImageRun extends Run {
|
|||||||
public prepForXml(context: IContext): IXmlableObject | undefined {
|
public prepForXml(context: IContext): IXmlableObject | undefined {
|
||||||
context.file.Media.addImage(this.key, this.imageData);
|
context.file.Media.addImage(this.key, this.imageData);
|
||||||
|
|
||||||
|
if (this.imageData.type === "svg") {
|
||||||
|
context.file.Media.addImage(this.fallbackKey, this.imageData.fallback);
|
||||||
|
}
|
||||||
|
|
||||||
return super.prepForXml(context);
|
return super.prepForXml(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
private convertDataURIToBinary(dataURI: string): Uint8Array {
|
|
||||||
if (typeof atob === "function") {
|
|
||||||
// https://gist.github.com/borismus/1032746
|
|
||||||
// https://github.com/mafintosh/base64-to-uint8array
|
|
||||||
const BASE64_MARKER = ";base64,";
|
|
||||||
const base64Index = dataURI.indexOf(BASE64_MARKER);
|
|
||||||
|
|
||||||
const base64IndexWithOffset = base64Index === -1 ? 0 : base64Index + BASE64_MARKER.length;
|
|
||||||
|
|
||||||
return new Uint8Array(
|
|
||||||
atob(dataURI.substring(base64IndexWithOffset))
|
|
||||||
.split("")
|
|
||||||
.map((c) => c.charCodeAt(0)),
|
|
||||||
);
|
|
||||||
/* c8 ignore next 6 */
|
|
||||||
} else {
|
|
||||||
// Not possible to test this branch in NodeJS
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
|
|
||||||
const b = require("buf" + "fer");
|
|
||||||
return new b.Buffer(dataURI, "base64");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,33 @@ export const TextEffect = {
|
|||||||
NONE: "none",
|
NONE: "none",
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* http://officeopenxml.com/WPtextShading.php
|
||||||
|
*
|
||||||
|
* Limit the list of supported highlight colors
|
||||||
|
*
|
||||||
|
* */
|
||||||
|
|
||||||
|
export const HighlightColor = {
|
||||||
|
BLACK: "black",
|
||||||
|
BLUE: "blue",
|
||||||
|
CYAN: "cyan",
|
||||||
|
DARK_BLUE: "darkBlue",
|
||||||
|
DARK_CYAN: "darkCyan",
|
||||||
|
DARK_GRAY: "darkGray",
|
||||||
|
DARK_GREEN: "darkGreen",
|
||||||
|
DARK_MAGENTA: "darkMagenta",
|
||||||
|
DARK_RED: "darkRed",
|
||||||
|
DARK_YELLOW: "darkYellow",
|
||||||
|
GREEN: "green",
|
||||||
|
LIGHT_GRAY: "lightGray",
|
||||||
|
MAGENTA: "magenta",
|
||||||
|
NONE: "none",
|
||||||
|
RED: "red",
|
||||||
|
WHITE: "white",
|
||||||
|
YELLOW: "yellow",
|
||||||
|
} as const;
|
||||||
|
|
||||||
/* eslint-enable */
|
/* eslint-enable */
|
||||||
|
|
||||||
export interface IRunStylePropertiesOptions {
|
export interface IRunStylePropertiesOptions {
|
||||||
@ -65,7 +92,7 @@ export interface IRunStylePropertiesOptions {
|
|||||||
readonly subScript?: boolean;
|
readonly subScript?: boolean;
|
||||||
readonly superScript?: boolean;
|
readonly superScript?: boolean;
|
||||||
readonly font?: string | IFontOptions | IFontAttributesProperties;
|
readonly font?: string | IFontOptions | IFontAttributesProperties;
|
||||||
readonly highlight?: string;
|
readonly highlight?: (typeof HighlightColor)[keyof typeof HighlightColor];
|
||||||
readonly highlightComplexScript?: boolean | string;
|
readonly highlightComplexScript?: boolean | string;
|
||||||
readonly characterSpacing?: number;
|
readonly characterSpacing?: number;
|
||||||
readonly shading?: IShadingAttributesProperties;
|
readonly shading?: IShadingAttributesProperties;
|
||||||
|
@ -23,11 +23,9 @@ export class Text extends XmlComponent {
|
|||||||
if (typeof options === "string") {
|
if (typeof options === "string") {
|
||||||
this.root.push(new TextAttributes({ space: SpaceType.PRESERVE }));
|
this.root.push(new TextAttributes({ space: SpaceType.PRESERVE }));
|
||||||
this.root.push(options);
|
this.root.push(options);
|
||||||
return this;
|
|
||||||
} else {
|
} else {
|
||||||
this.root.push(new TextAttributes({ space: options.space ?? SpaceType.DEFAULT }));
|
this.root.push(new TextAttributes({ space: options.space ?? SpaceType.DEFAULT }));
|
||||||
this.root.push(options.text);
|
this.root.push(options.text);
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import { ShadingType } from "@file/shading";
|
|||||||
import { EmphasisMarkType } from "./emphasis-mark";
|
import { EmphasisMarkType } from "./emphasis-mark";
|
||||||
import { PageNumber, Run } from "./run";
|
import { PageNumber, Run } from "./run";
|
||||||
import { UnderlineType } from "./underline";
|
import { UnderlineType } from "./underline";
|
||||||
import { TextEffect } from "./properties";
|
import { HighlightColor, TextEffect } from "./properties";
|
||||||
describe("Run", () => {
|
describe("Run", () => {
|
||||||
describe("#noProof()", () => {
|
describe("#noProof()", () => {
|
||||||
it("turns off spelling and grammar checkers for a run", () => {
|
it("turns off spelling and grammar checkers for a run", () => {
|
||||||
@ -215,18 +215,18 @@ describe("Run", () => {
|
|||||||
describe("#highlight()", () => {
|
describe("#highlight()", () => {
|
||||||
it("it should add highlight to the properties", () => {
|
it("it should add highlight to the properties", () => {
|
||||||
const run = new Run({
|
const run = new Run({
|
||||||
highlight: "005599",
|
highlight: HighlightColor.YELLOW,
|
||||||
});
|
});
|
||||||
const tree = new Formatter().format(run);
|
const tree = new Formatter().format(run);
|
||||||
expect(tree).to.deep.equal({
|
expect(tree).to.deep.equal({
|
||||||
"w:r": [
|
"w:r": [
|
||||||
{
|
{
|
||||||
"w:rPr": [
|
"w:rPr": [
|
||||||
{ "w:highlight": { _attr: { "w:val": "005599" } } },
|
{ "w:highlight": { _attr: { "w:val": "yellow" } } },
|
||||||
{
|
{
|
||||||
"w:highlightCs": {
|
"w:highlightCs": {
|
||||||
_attr: {
|
_attr: {
|
||||||
"w:val": "005599",
|
"w:val": "yellow",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
23
src/file/settings/footnote-format.ts
Normal file
23
src/file/settings/footnote-format.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { XmlAttributeComponent, XmlComponent } from "@file/xml-components";
|
||||||
|
import { NumberFormat } from "@file/shared/number-format";
|
||||||
|
|
||||||
|
export class FootnoteNumberingFormatAttributes extends XmlAttributeComponent<{
|
||||||
|
readonly numberFormat: (typeof NumberFormat)[keyof typeof NumberFormat];
|
||||||
|
}> {
|
||||||
|
protected readonly xmlKeys = {
|
||||||
|
numberFormat: "w:val",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FootnoteNumberingFormat extends XmlComponent {
|
||||||
|
public constructor(numberFormat: (typeof NumberFormat)[keyof typeof NumberFormat]) {
|
||||||
|
super("w:numFmt");
|
||||||
|
|
||||||
|
this.root.push(
|
||||||
|
new FootnoteNumberingFormatAttributes({
|
||||||
|
numberFormat,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
23
src/file/settings/footnote-positioning.ts
Normal file
23
src/file/settings/footnote-positioning.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { FootnotePositioningLocationType } from "@file/shared/footnote-properties";
|
||||||
|
import { XmlAttributeComponent, XmlComponent } from "@file/xml-components";
|
||||||
|
|
||||||
|
export class FootnotePositioningLocationAttributes extends XmlAttributeComponent<{
|
||||||
|
readonly position: FootnotePositioningLocationType;
|
||||||
|
}> {
|
||||||
|
protected readonly xmlKeys = {
|
||||||
|
position: "w:val",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FootnotePositioningLocation extends XmlComponent {
|
||||||
|
public constructor(position: FootnotePositioningLocationType) {
|
||||||
|
super("w:pos");
|
||||||
|
|
||||||
|
this.root.push(
|
||||||
|
new FootnotePositioningLocationAttributes({
|
||||||
|
position,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
36
src/file/settings/footnote-properties.ts
Normal file
36
src/file/settings/footnote-properties.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// http://www.datypic.com/sc/ooxml/e-w_footnotePr-2.html
|
||||||
|
import { NumberValueElement, XmlComponent } from "@file/xml-components";
|
||||||
|
import { FootnotePositioningLocationType, FootnoteRestartLocationType } from "@file/shared/footnote-properties";
|
||||||
|
import { NumberFormat } from "@file/shared/number-format";
|
||||||
|
import { FootnoteNumberingRestart } from "./footnote-restart";
|
||||||
|
import { FootnotePositioningLocation } from "./footnote-positioning";
|
||||||
|
import { FootnoteNumberingFormat } from "./footnote-format";
|
||||||
|
|
||||||
|
export interface IFootnoteProperties {
|
||||||
|
readonly restartLocation?: FootnoteRestartLocationType;
|
||||||
|
readonly positioningLocation?: FootnotePositioningLocationType;
|
||||||
|
readonly numberFormat?: (typeof NumberFormat)[keyof typeof NumberFormat];
|
||||||
|
readonly startingNumber?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FootnoteProperties extends XmlComponent {
|
||||||
|
public constructor(options: IFootnoteProperties) {
|
||||||
|
super("w:footnotePr");
|
||||||
|
|
||||||
|
if (options.restartLocation !== undefined) {
|
||||||
|
this.root.push(new FootnoteNumberingRestart(options.restartLocation));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.positioningLocation !== undefined) {
|
||||||
|
this.root.push(new FootnotePositioningLocation(options.positioningLocation));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.numberFormat !== undefined) {
|
||||||
|
this.root.push(new FootnoteNumberingFormat(options.numberFormat));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.startingNumber !== undefined) {
|
||||||
|
this.root.push(new NumberValueElement('w:numStart', options.startingNumber));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
src/file/settings/footnote-restart.ts
Normal file
22
src/file/settings/footnote-restart.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { FootnoteRestartLocationType } from "@file/shared/footnote-properties";
|
||||||
|
import { XmlAttributeComponent, XmlComponent } from "@file/xml-components";
|
||||||
|
|
||||||
|
export class FootnoteRestartNumberingAttributes extends XmlAttributeComponent<{
|
||||||
|
readonly restart: FootnoteRestartLocationType;
|
||||||
|
}> {
|
||||||
|
protected readonly xmlKeys = {
|
||||||
|
restart: "w:val",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FootnoteNumberingRestart extends XmlComponent {
|
||||||
|
public constructor(restart: FootnoteRestartLocationType) {
|
||||||
|
super("w:numRestart");
|
||||||
|
|
||||||
|
this.root.push(
|
||||||
|
new FootnoteRestartNumberingAttributes({
|
||||||
|
restart,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -129,6 +129,75 @@ describe("Settings", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should add autoHyphenation setting", () => {
|
||||||
|
const options = {
|
||||||
|
hyphenation: {
|
||||||
|
autoHyphenation: true,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const tree = new Formatter().format(new Settings(options));
|
||||||
|
expect(Object.keys(tree)).has.length(1);
|
||||||
|
expect(tree["w:settings"]).to.be.an("array");
|
||||||
|
expect(tree["w:settings"]).to.deep.include({
|
||||||
|
"w:autoHyphenation": {},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it("should add doNotHyphenateCaps setting", () => {
|
||||||
|
const options = {
|
||||||
|
hyphenation: {
|
||||||
|
doNotHyphenateCaps: true,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const tree = new Formatter().format(new Settings(options));
|
||||||
|
expect(Object.keys(tree)).has.length(1);
|
||||||
|
expect(tree["w:settings"]).to.be.an("array");
|
||||||
|
expect(tree["w:settings"]).to.deep.include({
|
||||||
|
"w:doNotHyphenateCaps": {},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should add hyphenationZone setting", () => {
|
||||||
|
const options = {
|
||||||
|
hyphenation: {
|
||||||
|
hyphenationZone: 200,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const tree = new Formatter().format(new Settings(options));
|
||||||
|
expect(Object.keys(tree)).has.length(1);
|
||||||
|
expect(tree["w:settings"]).to.be.an("array");
|
||||||
|
expect(tree["w:settings"]).to.deep.include({
|
||||||
|
"w:hyphenationZone": {
|
||||||
|
_attr: {
|
||||||
|
"w:val": 200,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should add consecutiveHyphenLimit setting", () => {
|
||||||
|
const options = {
|
||||||
|
hyphenation: {
|
||||||
|
consecutiveHyphenLimit: 3,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const tree = new Formatter().format(new Settings(options));
|
||||||
|
expect(Object.keys(tree)).has.length(1);
|
||||||
|
expect(tree["w:settings"]).to.be.an("array");
|
||||||
|
expect(tree["w:settings"]).to.deep.include({
|
||||||
|
"w:consecutiveHyphenLimit": {
|
||||||
|
_attr: {
|
||||||
|
"w:val": 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// TODO: Remove when deprecating compatibilityModeVersion
|
// TODO: Remove when deprecating compatibilityModeVersion
|
||||||
it("should add compatibility setting with legacy version", () => {
|
it("should add compatibility setting with legacy version", () => {
|
||||||
const settings = new Settings({
|
const settings = new Settings({
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { NumberValueElement, OnOffElement, XmlAttributeComponent, XmlComponent } from "@file/xml-components";
|
import { NumberValueElement, OnOffElement, XmlAttributeComponent, XmlComponent } from "@file/xml-components";
|
||||||
|
|
||||||
import { Compatibility, ICompatibilityOptions } from "./compatibility";
|
import { Compatibility, ICompatibilityOptions } from "./compatibility";
|
||||||
|
import { FootnoteProperties, IFootnoteProperties } from "./footnote-properties";
|
||||||
|
|
||||||
export class SettingsAttributes extends XmlAttributeComponent<{
|
export class SettingsAttributes extends XmlAttributeComponent<{
|
||||||
readonly wpc?: string;
|
readonly wpc?: string;
|
||||||
@ -153,6 +154,19 @@ export interface ISettingsOptions {
|
|||||||
readonly updateFields?: boolean;
|
readonly updateFields?: boolean;
|
||||||
readonly compatibility?: ICompatibilityOptions;
|
readonly compatibility?: ICompatibilityOptions;
|
||||||
readonly defaultTabStop?: number;
|
readonly defaultTabStop?: number;
|
||||||
|
readonly hyphenation?: IHyphenationOptions;
|
||||||
|
readonly footnoteProperties?: IFootnoteProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IHyphenationOptions {
|
||||||
|
/** Specifies whether the application automatically hyphenates words as they are typed in the document. */
|
||||||
|
readonly autoHyphenation?: boolean;
|
||||||
|
/** Specifies the minimum number of characters at the beginning of a word before a hyphen can be inserted. */
|
||||||
|
readonly hyphenationZone?: number;
|
||||||
|
/** Specifies the maximum number of consecutive lines that can end with a hyphenated word. */
|
||||||
|
readonly consecutiveHyphenLimit?: number;
|
||||||
|
/** Specifies whether to hyphenate words in all capital letters. */
|
||||||
|
readonly doNotHyphenateCaps?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Settings extends XmlComponent {
|
export class Settings extends XmlComponent {
|
||||||
@ -204,6 +218,30 @@ export class Settings extends XmlComponent {
|
|||||||
this.root.push(new NumberValueElement("w:defaultTabStop", options.defaultTabStop));
|
this.root.push(new NumberValueElement("w:defaultTabStop", options.defaultTabStop));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://c-rex.net/samples/ooxml/e1/Part4/OOXML_P4_DOCX_autoHyphenation_topic_ID0EFUMX.html
|
||||||
|
if (options.hyphenation?.autoHyphenation !== undefined) {
|
||||||
|
this.root.push(new OnOffElement("w:autoHyphenation", options.hyphenation.autoHyphenation));
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://c-rex.net/samples/ooxml/e1/Part4/OOXML_P4_DOCX_hyphenationZone_topic_ID0ERI3X.html
|
||||||
|
if (options.hyphenation?.hyphenationZone !== undefined) {
|
||||||
|
this.root.push(new NumberValueElement("w:hyphenationZone", options.hyphenation.hyphenationZone));
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://c-rex.net/samples/ooxml/e1/Part4/OOXML_P4_DOCX_consecutiveHyphenLim_topic_ID0EQ6RX.html
|
||||||
|
if (options.hyphenation?.consecutiveHyphenLimit !== undefined) {
|
||||||
|
this.root.push(new NumberValueElement("w:consecutiveHyphenLimit", options.hyphenation.consecutiveHyphenLimit));
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://c-rex.net/samples/ooxml/e1/Part4/OOXML_P4_DOCX_doNotHyphenateCaps_topic_ID0EW4XX.html
|
||||||
|
if (options.hyphenation?.doNotHyphenateCaps !== undefined) {
|
||||||
|
this.root.push(new OnOffElement("w:doNotHyphenateCaps", options.hyphenation.doNotHyphenateCaps));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.footnoteProperties !== undefined) {
|
||||||
|
this.root.push(new FootnoteProperties(options.footnoteProperties));
|
||||||
|
}
|
||||||
|
|
||||||
this.root.push(
|
this.root.push(
|
||||||
new Compatibility({
|
new Compatibility({
|
||||||
...(options.compatibility ?? {}),
|
...(options.compatibility ?? {}),
|
||||||
|
16
src/file/shared/footnote-properties.ts
Normal file
16
src/file/shared/footnote-properties.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
export const FootnoteRestartLocation = {
|
||||||
|
CONTINUOUS: "continuous",
|
||||||
|
EACH_SECTION: "eachSect",
|
||||||
|
EACH_PAGE: "eachPage",
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export type FootnoteRestartLocationType = (typeof FootnoteRestartLocation)[keyof typeof FootnoteRestartLocation]
|
||||||
|
|
||||||
|
export const FootnotePositioningLocation = {
|
||||||
|
PAGE_BOTTOM: "pageBottom",
|
||||||
|
BENEATH_TEXT: "beneathText",
|
||||||
|
SECTION_END: "sectEnd",
|
||||||
|
DOCUMENT_END: "docEnd",
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export type FootnotePositioningLocationType = (typeof FootnotePositioningLocation)[keyof typeof FootnotePositioningLocation]
|
@ -1,3 +1,4 @@
|
|||||||
export * from "./alignment";
|
export * from "./alignment";
|
||||||
export * from "./number-format";
|
export * from "./number-format";
|
||||||
export * from "./space-type";
|
export * from "./space-type";
|
||||||
|
export * from "./footnote-properties";
|
||||||
|
@ -3,6 +3,7 @@ import { describe, expect, it } from "vitest";
|
|||||||
import { Formatter } from "@export/formatter";
|
import { Formatter } from "@export/formatter";
|
||||||
import { EmphasisMarkType } from "@file/paragraph/run/emphasis-mark";
|
import { EmphasisMarkType } from "@file/paragraph/run/emphasis-mark";
|
||||||
import { UnderlineType } from "@file/paragraph/run/underline";
|
import { UnderlineType } from "@file/paragraph/run/underline";
|
||||||
|
import { HighlightColor } from "@file/paragraph/run/properties";
|
||||||
import { ShadingType } from "@file/shading";
|
import { ShadingType } from "@file/shading";
|
||||||
import { EMPTY_OBJECT } from "@file/xml-components";
|
import { EMPTY_OBJECT } from "@file/xml-components";
|
||||||
|
|
||||||
@ -728,23 +729,23 @@ describe("CharacterStyle", () => {
|
|||||||
|
|
||||||
const highlightTests = [
|
const highlightTests = [
|
||||||
{
|
{
|
||||||
highlight: "005599",
|
highlight: HighlightColor.YELLOW,
|
||||||
expected: [{ "w:highlight": { _attr: { "w:val": "005599" } } }, { "w:highlightCs": { _attr: { "w:val": "005599" } } }],
|
expected: [{ "w:highlight": { _attr: { "w:val": "yellow" } } }, { "w:highlightCs": { _attr: { "w:val": "yellow" } } }],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
highlight: "005599",
|
highlight: HighlightColor.YELLOW,
|
||||||
highlightComplexScript: true,
|
highlightComplexScript: true,
|
||||||
expected: [{ "w:highlight": { _attr: { "w:val": "005599" } } }, { "w:highlightCs": { _attr: { "w:val": "005599" } } }],
|
expected: [{ "w:highlight": { _attr: { "w:val": "yellow" } } }, { "w:highlightCs": { _attr: { "w:val": "yellow" } } }],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
highlight: "005599",
|
highlight: HighlightColor.YELLOW,
|
||||||
highlightComplexScript: false,
|
highlightComplexScript: false,
|
||||||
expected: [{ "w:highlight": { _attr: { "w:val": "005599" } } }],
|
expected: [{ "w:highlight": { _attr: { "w:val": "yellow" } } }],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
highlight: "005599",
|
highlight: HighlightColor.YELLOW,
|
||||||
highlightComplexScript: "550099",
|
highlightComplexScript: "550099",
|
||||||
expected: [{ "w:highlight": { _attr: { "w:val": "005599" } } }, { "w:highlightCs": { _attr: { "w:val": "550099" } } }],
|
expected: [{ "w:highlight": { _attr: { "w:val": "yellow" } } }, { "w:highlightCs": { _attr: { "w:val": "550099" } } }],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
highlightTests.forEach(({ highlight, highlightComplexScript, expected }) => {
|
highlightTests.forEach(({ highlight, highlightComplexScript, expected }) => {
|
||||||
|
@ -3,6 +3,7 @@ import { describe, expect, it } from "vitest";
|
|||||||
import { Formatter } from "@export/formatter";
|
import { Formatter } from "@export/formatter";
|
||||||
import { AlignmentType, EmphasisMarkType, TabStopPosition } from "@file/paragraph";
|
import { AlignmentType, EmphasisMarkType, TabStopPosition } from "@file/paragraph";
|
||||||
import { UnderlineType } from "@file/paragraph/run/underline";
|
import { UnderlineType } from "@file/paragraph/run/underline";
|
||||||
|
import { HighlightColor } from "@file/paragraph/run";
|
||||||
import { ShadingType } from "@file/shading";
|
import { ShadingType } from "@file/shading";
|
||||||
import { EMPTY_OBJECT } from "@file/xml-components";
|
import { EMPTY_OBJECT } from "@file/xml-components";
|
||||||
|
|
||||||
@ -615,23 +616,23 @@ describe("ParagraphStyle", () => {
|
|||||||
|
|
||||||
const highlightTests = [
|
const highlightTests = [
|
||||||
{
|
{
|
||||||
highlight: "005599",
|
highlight: HighlightColor.YELLOW,
|
||||||
expected: [{ "w:highlight": { _attr: { "w:val": "005599" } } }, { "w:highlightCs": { _attr: { "w:val": "005599" } } }],
|
expected: [{ "w:highlight": { _attr: { "w:val": "yellow" } } }, { "w:highlightCs": { _attr: { "w:val": "yellow" } } }],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
highlight: "005599",
|
highlight: HighlightColor.YELLOW,
|
||||||
highlightComplexScript: true,
|
highlightComplexScript: true,
|
||||||
expected: [{ "w:highlight": { _attr: { "w:val": "005599" } } }, { "w:highlightCs": { _attr: { "w:val": "005599" } } }],
|
expected: [{ "w:highlight": { _attr: { "w:val": "yellow" } } }, { "w:highlightCs": { _attr: { "w:val": "yellow" } } }],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
highlight: "005599",
|
highlight: HighlightColor.YELLOW,
|
||||||
highlightComplexScript: false,
|
highlightComplexScript: false,
|
||||||
expected: [{ "w:highlight": { _attr: { "w:val": "005599" } } }],
|
expected: [{ "w:highlight": { _attr: { "w:val": "yellow" } } }],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
highlight: "005599",
|
highlight: HighlightColor.YELLOW,
|
||||||
highlightComplexScript: "550099",
|
highlightComplexScript: "550099",
|
||||||
expected: [{ "w:highlight": { _attr: { "w:val": "005599" } } }, { "w:highlightCs": { _attr: { "w:val": "550099" } } }],
|
expected: [{ "w:highlight": { _attr: { "w:val": "yellow" } } }, { "w:highlightCs": { _attr: { "w:val": "550099" } } }],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
highlightTests.forEach(({ highlight, highlightComplexScript, expected }) => {
|
highlightTests.forEach(({ highlight, highlightComplexScript, expected }) => {
|
||||||
|
@ -218,7 +218,9 @@ describe("from-docx", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should patch the document", async () => {
|
it("should patch the document", async () => {
|
||||||
const output = await patchDocument(Buffer.from(""), {
|
const output = await patchDocument({
|
||||||
|
outputType: "uint8array",
|
||||||
|
data: Buffer.from(""),
|
||||||
patches: {
|
patches: {
|
||||||
name: {
|
name: {
|
||||||
type: PatchType.PARAGRAPH,
|
type: PatchType.PARAGRAPH,
|
||||||
@ -254,6 +256,7 @@ describe("from-docx", () => {
|
|||||||
link: "https://www.google.co.uk",
|
link: "https://www.google.co.uk",
|
||||||
}),
|
}),
|
||||||
new ImageRun({
|
new ImageRun({
|
||||||
|
type: "png",
|
||||||
data: Buffer.from(""),
|
data: Buffer.from(""),
|
||||||
transformation: { width: 100, height: 100 },
|
transformation: { width: 100, height: 100 },
|
||||||
}),
|
}),
|
||||||
@ -266,6 +269,7 @@ describe("from-docx", () => {
|
|||||||
type: PatchType.PARAGRAPH,
|
type: PatchType.PARAGRAPH,
|
||||||
children: [
|
children: [
|
||||||
new ImageRun({
|
new ImageRun({
|
||||||
|
type: "png",
|
||||||
data: Buffer.from(""),
|
data: Buffer.from(""),
|
||||||
transformation: { width: 100, height: 100 },
|
transformation: { width: 100, height: 100 },
|
||||||
}),
|
}),
|
||||||
@ -277,7 +281,9 @@ describe("from-docx", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should patch the document", async () => {
|
it("should patch the document", async () => {
|
||||||
const output = await patchDocument(Buffer.from(""), {
|
const output = await patchDocument({
|
||||||
|
outputType: "uint8array",
|
||||||
|
data: Buffer.from(""),
|
||||||
patches: {},
|
patches: {},
|
||||||
});
|
});
|
||||||
expect(output).to.not.be.undefined;
|
expect(output).to.not.be.undefined;
|
||||||
@ -303,13 +309,16 @@ describe("from-docx", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should use the relationships file rather than create one", async () => {
|
it("should use the relationships file rather than create one", async () => {
|
||||||
const output = await patchDocument(Buffer.from(""), {
|
const output = await patchDocument({
|
||||||
|
outputType: "uint8array",
|
||||||
|
data: Buffer.from(""),
|
||||||
patches: {
|
patches: {
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
image_test: {
|
image_test: {
|
||||||
type: PatchType.PARAGRAPH,
|
type: PatchType.PARAGRAPH,
|
||||||
children: [
|
children: [
|
||||||
new ImageRun({
|
new ImageRun({
|
||||||
|
type: "png",
|
||||||
data: Buffer.from(""),
|
data: Buffer.from(""),
|
||||||
transformation: { width: 100, height: 100 },
|
transformation: { width: 100, height: 100 },
|
||||||
}),
|
}),
|
||||||
@ -347,13 +356,16 @@ describe("from-docx", () => {
|
|||||||
|
|
||||||
it("should throw an error if the content types is not found", () =>
|
it("should throw an error if the content types is not found", () =>
|
||||||
expect(
|
expect(
|
||||||
patchDocument(Buffer.from(""), {
|
patchDocument({
|
||||||
|
outputType: "uint8array",
|
||||||
|
data: Buffer.from(""),
|
||||||
patches: {
|
patches: {
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
image_test: {
|
image_test: {
|
||||||
type: PatchType.PARAGRAPH,
|
type: PatchType.PARAGRAPH,
|
||||||
children: [
|
children: [
|
||||||
new ImageRun({
|
new ImageRun({
|
||||||
|
type: "png",
|
||||||
data: Buffer.from(""),
|
data: Buffer.from(""),
|
||||||
transformation: { width: 100, height: 100 },
|
transformation: { width: 100, height: 100 },
|
||||||
}),
|
}),
|
||||||
@ -384,13 +396,16 @@ describe("from-docx", () => {
|
|||||||
|
|
||||||
it("should throw an error if the content types is not found", () =>
|
it("should throw an error if the content types is not found", () =>
|
||||||
expect(
|
expect(
|
||||||
patchDocument(Buffer.from(""), {
|
patchDocument({
|
||||||
|
outputType: "uint8array",
|
||||||
|
data: Buffer.from(""),
|
||||||
patches: {
|
patches: {
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
image_test: {
|
image_test: {
|
||||||
type: PatchType.PARAGRAPH,
|
type: PatchType.PARAGRAPH,
|
||||||
children: [
|
children: [
|
||||||
new ImageRun({
|
new ImageRun({
|
||||||
|
type: "png",
|
||||||
data: Buffer.from(""),
|
data: Buffer.from(""),
|
||||||
transformation: { width: 100, height: 100 },
|
transformation: { width: 100, height: 100 },
|
||||||
}),
|
}),
|
||||||
|
@ -12,7 +12,6 @@ import { TargetModeType } from "@file/relationships/relationship/relationship";
|
|||||||
import { uniqueId } from "@util/convenience-functions";
|
import { uniqueId } from "@util/convenience-functions";
|
||||||
|
|
||||||
import { replacer } from "./replacer";
|
import { replacer } from "./replacer";
|
||||||
import { findLocationOfText } from "./traverser";
|
|
||||||
import { toJson } from "./util";
|
import { toJson } from "./util";
|
||||||
import { appendRelationship, getNextRelationshipIndex } from "./relationship-manager";
|
import { appendRelationship, getNextRelationshipIndex } from "./relationship-manager";
|
||||||
import { appendContentType } from "./content-types-manager";
|
import { appendContentType } from "./content-types-manager";
|
||||||
@ -47,14 +46,37 @@ interface IHyperlinkRelationshipAddition {
|
|||||||
|
|
||||||
export type IPatch = ParagraphPatch | FilePatch;
|
export type IPatch = ParagraphPatch | FilePatch;
|
||||||
|
|
||||||
export interface PatchDocumentOptions {
|
// From JSZip
|
||||||
|
type OutputByType = {
|
||||||
|
readonly base64: string;
|
||||||
|
// eslint-disable-next-line id-denylist
|
||||||
|
readonly string: string;
|
||||||
|
readonly text: string;
|
||||||
|
readonly binarystring: string;
|
||||||
|
readonly array: readonly number[];
|
||||||
|
readonly uint8array: Uint8Array;
|
||||||
|
readonly arraybuffer: ArrayBuffer;
|
||||||
|
readonly blob: Blob;
|
||||||
|
readonly nodebuffer: Buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PatchDocumentOutputType = keyof OutputByType;
|
||||||
|
|
||||||
|
export type PatchDocumentOptions<T extends PatchDocumentOutputType = PatchDocumentOutputType> = {
|
||||||
|
readonly outputType: T;
|
||||||
|
readonly data: InputDataType;
|
||||||
readonly patches: { readonly [key: string]: IPatch };
|
readonly patches: { readonly [key: string]: IPatch };
|
||||||
readonly keepOriginalStyles?: boolean;
|
readonly keepOriginalStyles?: boolean;
|
||||||
}
|
};
|
||||||
|
|
||||||
const imageReplacer = new ImageReplacer();
|
const imageReplacer = new ImageReplacer();
|
||||||
|
|
||||||
export const patchDocument = async (data: InputDataType, options: PatchDocumentOptions): Promise<Uint8Array> => {
|
export const patchDocument = async <T extends PatchDocumentOutputType = PatchDocumentOutputType>({
|
||||||
|
outputType,
|
||||||
|
data,
|
||||||
|
patches,
|
||||||
|
keepOriginalStyles,
|
||||||
|
}: PatchDocumentOptions<T>): Promise<OutputByType[T]> => {
|
||||||
const zipContent = await JSZip.loadAsync(data);
|
const zipContent = await JSZip.loadAsync(data);
|
||||||
const contexts = new Map<string, IContext>();
|
const contexts = new Map<string, IContext>();
|
||||||
const file = {
|
const file = {
|
||||||
@ -104,38 +126,48 @@ export const patchDocument = async (data: InputDataType, options: PatchDocumentO
|
|||||||
};
|
};
|
||||||
contexts.set(key, context);
|
contexts.set(key, context);
|
||||||
|
|
||||||
for (const [patchKey, patchValue] of Object.entries(options.patches)) {
|
for (const [patchKey, patchValue] of Object.entries(patches)) {
|
||||||
const patchText = `{{${patchKey}}}`;
|
const patchText = `{{${patchKey}}}`;
|
||||||
const renderedParagraphs = findLocationOfText(json, patchText);
|
|
||||||
// TODO: mutates json. Make it immutable
|
// TODO: mutates json. Make it immutable
|
||||||
replacer(
|
// We need to loop through to catch every occurrence of the patch text
|
||||||
json,
|
// It is possible that the patch text is in the same run
|
||||||
{
|
// This algorithm is limited to one patch per text run
|
||||||
...patchValue,
|
// Once it cannot find any more occurrences, it will throw an error, and then we break out of the loop
|
||||||
children: patchValue.children.map((element) => {
|
// https://github.com/dolanmiu/docx/issues/2267
|
||||||
// We need to replace external hyperlinks with concrete hyperlinks
|
// eslint-disable-next-line no-constant-condition
|
||||||
if (element instanceof ExternalHyperlink) {
|
while (true) {
|
||||||
const concreteHyperlink = new ConcreteHyperlink(element.options.children, uniqueId());
|
try {
|
||||||
// eslint-disable-next-line functional/immutable-data
|
replacer({
|
||||||
hyperlinkRelationshipAdditions.push({
|
json,
|
||||||
key,
|
patch: {
|
||||||
hyperlink: {
|
...patchValue,
|
||||||
id: concreteHyperlink.linkId,
|
children: patchValue.children.map((element) => {
|
||||||
link: element.options.link,
|
// We need to replace external hyperlinks with concrete hyperlinks
|
||||||
},
|
if (element instanceof ExternalHyperlink) {
|
||||||
});
|
const concreteHyperlink = new ConcreteHyperlink(element.options.children, uniqueId());
|
||||||
return concreteHyperlink;
|
// eslint-disable-next-line functional/immutable-data
|
||||||
} else {
|
hyperlinkRelationshipAdditions.push({
|
||||||
return element;
|
key,
|
||||||
}
|
hyperlink: {
|
||||||
}),
|
id: concreteHyperlink.linkId,
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
link: element.options.link,
|
||||||
} as any,
|
},
|
||||||
patchText,
|
});
|
||||||
renderedParagraphs,
|
return concreteHyperlink;
|
||||||
context,
|
} else {
|
||||||
options.keepOriginalStyles,
|
return element;
|
||||||
);
|
}
|
||||||
|
}),
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
} as any,
|
||||||
|
patchText,
|
||||||
|
context,
|
||||||
|
keepOriginalStyles,
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mediaDatas = imageReplacer.getMediaData(JSON.stringify(json), context.file.Media);
|
const mediaDatas = imageReplacer.getMediaData(JSON.stringify(json), context.file.Media);
|
||||||
@ -201,6 +233,7 @@ export const patchDocument = async (data: InputDataType, options: PatchDocumentO
|
|||||||
appendContentType(contentTypesJson, "image/jpeg", "jpg");
|
appendContentType(contentTypesJson, "image/jpeg", "jpg");
|
||||||
appendContentType(contentTypesJson, "image/bmp", "bmp");
|
appendContentType(contentTypesJson, "image/bmp", "bmp");
|
||||||
appendContentType(contentTypesJson, "image/gif", "gif");
|
appendContentType(contentTypesJson, "image/gif", "gif");
|
||||||
|
appendContentType(contentTypesJson, "image/svg+xml", "svg");
|
||||||
}
|
}
|
||||||
|
|
||||||
const zip = new JSZip();
|
const zip = new JSZip();
|
||||||
@ -215,12 +248,12 @@ export const patchDocument = async (data: InputDataType, options: PatchDocumentO
|
|||||||
zip.file(key, value);
|
zip.file(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const { stream, fileName } of file.Media.Array) {
|
for (const { data: stream, fileName } of file.Media.Array) {
|
||||||
zip.file(`word/media/${fileName}`, stream);
|
zip.file(`word/media/${fileName}`, stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
return zip.generateAsync({
|
return zip.generateAsync({
|
||||||
type: "uint8array",
|
type: outputType,
|
||||||
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||||
compression: "DEFLATE",
|
compression: "DEFLATE",
|
||||||
});
|
});
|
||||||
|
@ -27,7 +27,7 @@ describe("paragraph-token-replacer", () => {
|
|||||||
},
|
},
|
||||||
renderedParagraph: {
|
renderedParagraph: {
|
||||||
index: 0,
|
index: 0,
|
||||||
path: [0],
|
pathToParagraph: [0],
|
||||||
runs: [
|
runs: [
|
||||||
{
|
{
|
||||||
end: 4,
|
end: 4,
|
||||||
@ -128,7 +128,7 @@ describe("paragraph-token-replacer", () => {
|
|||||||
{ text: "World", parts: [{ text: "World", index: 0, start: 15, end: 19 }], index: 3, start: 15, end: 19 },
|
{ text: "World", parts: [{ text: "World", index: 0, start: 15, end: 19 }], index: 3, start: 15, end: 19 },
|
||||||
],
|
],
|
||||||
index: 0,
|
index: 0,
|
||||||
path: [0, 1, 0, 0],
|
pathToParagraph: [0, 1, 0, 0],
|
||||||
},
|
},
|
||||||
originalText: "{{name}}",
|
originalText: "{{name}}",
|
||||||
replacementText: "John",
|
replacementText: "John",
|
||||||
|
@ -8,7 +8,7 @@ import { PatchType } from "./from-docx";
|
|||||||
|
|
||||||
import { replacer } from "./replacer";
|
import { replacer } from "./replacer";
|
||||||
|
|
||||||
const MOCK_JSON = {
|
export const MOCK_JSON = {
|
||||||
elements: [
|
elements: [
|
||||||
{
|
{
|
||||||
type: "element",
|
type: "element",
|
||||||
@ -73,103 +73,60 @@ const MOCK_JSON = {
|
|||||||
|
|
||||||
describe("replacer", () => {
|
describe("replacer", () => {
|
||||||
describe("replacer", () => {
|
describe("replacer", () => {
|
||||||
it("should return the same object if nothing is added", () => {
|
it("should throw an error if nothing is added", () => {
|
||||||
const output = replacer(
|
expect(() =>
|
||||||
{
|
replacer({
|
||||||
elements: [],
|
json: {
|
||||||
},
|
elements: [],
|
||||||
{
|
},
|
||||||
type: PatchType.PARAGRAPH,
|
patch: {
|
||||||
children: [],
|
type: PatchType.PARAGRAPH,
|
||||||
},
|
children: [],
|
||||||
"hello",
|
},
|
||||||
[],
|
patchText: "hello",
|
||||||
// eslint-disable-next-line functional/prefer-readonly-type
|
// eslint-disable-next-line functional/prefer-readonly-type
|
||||||
vi.fn<[], IContext>()(),
|
context: vi.fn<[], IContext>()(),
|
||||||
);
|
}),
|
||||||
|
).toThrow();
|
||||||
expect(output).to.deep.equal({
|
|
||||||
elements: [],
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should replace paragraph type", () => {
|
it("should replace paragraph type", () => {
|
||||||
const output = replacer(
|
const output = replacer({
|
||||||
MOCK_JSON,
|
json: JSON.parse(JSON.stringify(MOCK_JSON)),
|
||||||
{
|
patch: {
|
||||||
type: PatchType.PARAGRAPH,
|
type: PatchType.PARAGRAPH,
|
||||||
children: [new TextRun("Delightful Header")],
|
children: [new TextRun("Delightful Header")],
|
||||||
},
|
},
|
||||||
"{{header_adjective}}",
|
patchText: "{{header_adjective}}",
|
||||||
[
|
context: {
|
||||||
{
|
|
||||||
text: "This is a {{header_adjective}} don’t you think?",
|
|
||||||
runs: [
|
|
||||||
{
|
|
||||||
text: "This is a {{head",
|
|
||||||
parts: [{ text: "This is a {{head", index: 0, start: 0, end: 15 }],
|
|
||||||
index: 1,
|
|
||||||
start: 0,
|
|
||||||
end: 15,
|
|
||||||
},
|
|
||||||
{ text: "er", parts: [{ text: "er", index: 0, start: 16, end: 17 }], index: 2, start: 16, end: 17 },
|
|
||||||
{
|
|
||||||
text: "_adjective}} don’t you think?",
|
|
||||||
parts: [{ text: "_adjective}} don’t you think?", index: 0, start: 18, end: 46 }],
|
|
||||||
index: 3,
|
|
||||||
start: 18,
|
|
||||||
end: 46,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
index: 0,
|
|
||||||
path: [0, 0, 0],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
{
|
|
||||||
file: {} as unknown as File,
|
file: {} as unknown as File,
|
||||||
viewWrapper: {
|
viewWrapper: {
|
||||||
Relationships: {},
|
Relationships: {},
|
||||||
} as unknown as IViewWrapper,
|
} as unknown as IViewWrapper,
|
||||||
stack: [],
|
stack: [],
|
||||||
},
|
},
|
||||||
);
|
});
|
||||||
|
|
||||||
expect(JSON.stringify(output)).to.contain("Delightful Header");
|
expect(JSON.stringify(output)).to.contain("Delightful Header");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should replace paragraph type keeping original styling if keepOriginalStyles is true", () => {
|
it("should replace paragraph type keeping original styling if keepOriginalStyles is true", () => {
|
||||||
const output = replacer(
|
const output = replacer({
|
||||||
MOCK_JSON,
|
json: JSON.parse(JSON.stringify(MOCK_JSON)),
|
||||||
{
|
patch: {
|
||||||
type: PatchType.PARAGRAPH,
|
type: PatchType.PARAGRAPH,
|
||||||
children: [new TextRun("sweet")],
|
children: [new TextRun("sweet")],
|
||||||
},
|
},
|
||||||
"{{bold}}",
|
patchText: "{{bold}}",
|
||||||
[
|
context: {
|
||||||
{
|
|
||||||
text: "What a {{bold}} text!",
|
|
||||||
runs: [
|
|
||||||
{
|
|
||||||
text: "What a {{bold}} text!",
|
|
||||||
parts: [{ text: "What a {{bold}} text!", index: 1, start: 0, end: 21 }],
|
|
||||||
index: 0,
|
|
||||||
start: 0,
|
|
||||||
end: 21,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
index: 0,
|
|
||||||
path: [0, 0, 1],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
{
|
|
||||||
file: {} as unknown as File,
|
file: {} as unknown as File,
|
||||||
viewWrapper: {
|
viewWrapper: {
|
||||||
Relationships: {},
|
Relationships: {},
|
||||||
} as unknown as IViewWrapper,
|
} as unknown as IViewWrapper,
|
||||||
stack: [],
|
stack: [],
|
||||||
},
|
},
|
||||||
true,
|
keepOriginalStyles: true,
|
||||||
);
|
});
|
||||||
|
|
||||||
expect(JSON.stringify(output)).to.contain("sweet");
|
expect(JSON.stringify(output)).to.contain("sweet");
|
||||||
expect(output.elements![0].elements![1].elements).toMatchObject([
|
expect(output.elements![0].elements![1].elements).toMatchObject([
|
||||||
@ -225,91 +182,23 @@ describe("replacer", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should replace document type", () => {
|
it("should replace document type", () => {
|
||||||
const output = replacer(
|
const output = replacer({
|
||||||
MOCK_JSON,
|
json: JSON.parse(JSON.stringify(MOCK_JSON)),
|
||||||
{
|
patch: {
|
||||||
type: PatchType.DOCUMENT,
|
type: PatchType.DOCUMENT,
|
||||||
children: [new Paragraph("Lorem ipsum paragraph")],
|
children: [new Paragraph("Lorem ipsum paragraph")],
|
||||||
},
|
},
|
||||||
"{{header_adjective}}",
|
patchText: "{{header_adjective}}",
|
||||||
[
|
context: {
|
||||||
{
|
|
||||||
text: "This is a {{header_adjective}} don’t you think?",
|
|
||||||
runs: [
|
|
||||||
{
|
|
||||||
text: "This is a {{head",
|
|
||||||
parts: [{ text: "This is a {{head", index: 0, start: 0, end: 15 }],
|
|
||||||
index: 1,
|
|
||||||
start: 0,
|
|
||||||
end: 15,
|
|
||||||
},
|
|
||||||
{ text: "er", parts: [{ text: "er", index: 0, start: 16, end: 17 }], index: 2, start: 16, end: 17 },
|
|
||||||
{
|
|
||||||
text: "_adjective}} don’t you think?",
|
|
||||||
parts: [{ text: "_adjective}} don’t you think?", index: 0, start: 18, end: 46 }],
|
|
||||||
index: 3,
|
|
||||||
start: 18,
|
|
||||||
end: 46,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
index: 0,
|
|
||||||
path: [0, 0, 0],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
{
|
|
||||||
file: {} as unknown as File,
|
file: {} as unknown as File,
|
||||||
viewWrapper: {
|
viewWrapper: {
|
||||||
Relationships: {},
|
Relationships: {},
|
||||||
} as unknown as IViewWrapper,
|
} as unknown as IViewWrapper,
|
||||||
stack: [],
|
stack: [],
|
||||||
},
|
},
|
||||||
);
|
});
|
||||||
|
|
||||||
expect(JSON.stringify(output)).to.contain("Lorem ipsum paragraph");
|
expect(JSON.stringify(output)).to.contain("Lorem ipsum paragraph");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should throw an error if the type is not supported", () => {
|
|
||||||
expect(() =>
|
|
||||||
replacer(
|
|
||||||
{},
|
|
||||||
{
|
|
||||||
type: PatchType.DOCUMENT,
|
|
||||||
children: [new Paragraph("Lorem ipsum paragraph")],
|
|
||||||
},
|
|
||||||
"{{header_adjective}}",
|
|
||||||
[
|
|
||||||
{
|
|
||||||
text: "This is a {{header_adjective}} don’t you think?",
|
|
||||||
runs: [
|
|
||||||
{
|
|
||||||
text: "This is a {{head",
|
|
||||||
parts: [{ text: "This is a {{head", index: 0, start: 0, end: 15 }],
|
|
||||||
index: 1,
|
|
||||||
start: 0,
|
|
||||||
end: 15,
|
|
||||||
},
|
|
||||||
{ text: "er", parts: [{ text: "er", index: 0, start: 16, end: 17 }], index: 2, start: 16, end: 17 },
|
|
||||||
{
|
|
||||||
text: "_adjective}} don’t you think?",
|
|
||||||
parts: [{ text: "_adjective}} don’t you think?", index: 0, start: 18, end: 46 }],
|
|
||||||
index: 3,
|
|
||||||
start: 18,
|
|
||||||
end: 46,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
index: 0,
|
|
||||||
path: [0, 0, 0],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
{
|
|
||||||
file: {} as unknown as File,
|
|
||||||
viewWrapper: {
|
|
||||||
Relationships: {},
|
|
||||||
} as unknown as IViewWrapper,
|
|
||||||
stack: [],
|
|
||||||
},
|
|
||||||
),
|
|
||||||
).to.throw();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -6,22 +6,33 @@ import { IContext, XmlComponent } from "@file/xml-components";
|
|||||||
|
|
||||||
import { IPatch, PatchType } from "./from-docx";
|
import { IPatch, PatchType } from "./from-docx";
|
||||||
import { toJson } from "./util";
|
import { toJson } from "./util";
|
||||||
import { IRenderedParagraphNode } from "./run-renderer";
|
|
||||||
import { replaceTokenInParagraphElement } from "./paragraph-token-replacer";
|
import { replaceTokenInParagraphElement } from "./paragraph-token-replacer";
|
||||||
import { findRunElementIndexWithToken, splitRunElement } from "./paragraph-split-inject";
|
import { findRunElementIndexWithToken, splitRunElement } from "./paragraph-split-inject";
|
||||||
|
import { findLocationOfText } from "./traverser";
|
||||||
|
|
||||||
const formatter = new Formatter();
|
const formatter = new Formatter();
|
||||||
|
|
||||||
const SPLIT_TOKEN = "ɵ";
|
const SPLIT_TOKEN = "ɵ";
|
||||||
|
|
||||||
export const replacer = (
|
export const replacer = ({
|
||||||
json: Element,
|
json,
|
||||||
patch: IPatch,
|
patch,
|
||||||
patchText: string,
|
patchText,
|
||||||
renderedParagraphs: readonly IRenderedParagraphNode[],
|
context,
|
||||||
context: IContext,
|
keepOriginalStyles = true,
|
||||||
keepOriginalStyles: boolean = false,
|
}: {
|
||||||
): Element => {
|
readonly json: Element;
|
||||||
|
readonly patch: IPatch;
|
||||||
|
readonly patchText: string;
|
||||||
|
readonly context: IContext;
|
||||||
|
readonly keepOriginalStyles?: boolean;
|
||||||
|
}): Element => {
|
||||||
|
const renderedParagraphs = findLocationOfText(json, patchText);
|
||||||
|
|
||||||
|
if (renderedParagraphs.length === 0) {
|
||||||
|
throw new Error(`Could not find text ${patchText}`);
|
||||||
|
}
|
||||||
|
|
||||||
for (const renderedParagraph of renderedParagraphs) {
|
for (const renderedParagraph of renderedParagraphs) {
|
||||||
const textJson = patch.children
|
const textJson = patch.children
|
||||||
// eslint-disable-next-line no-loop-func
|
// eslint-disable-next-line no-loop-func
|
||||||
@ -30,15 +41,15 @@ export const replacer = (
|
|||||||
|
|
||||||
switch (patch.type) {
|
switch (patch.type) {
|
||||||
case PatchType.DOCUMENT: {
|
case PatchType.DOCUMENT: {
|
||||||
const parentElement = goToParentElementFromPath(json, renderedParagraph.path);
|
const parentElement = goToParentElementFromPath(json, renderedParagraph.pathToParagraph);
|
||||||
const elementIndex = getLastElementIndexFromPath(renderedParagraph.path);
|
const elementIndex = getLastElementIndexFromPath(renderedParagraph.pathToParagraph);
|
||||||
// eslint-disable-next-line functional/immutable-data, prefer-destructuring
|
// eslint-disable-next-line functional/immutable-data, prefer-destructuring
|
||||||
parentElement.elements!.splice(elementIndex, 1, ...textJson);
|
parentElement.elements!.splice(elementIndex, 1, ...textJson);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PatchType.PARAGRAPH:
|
case PatchType.PARAGRAPH:
|
||||||
default: {
|
default: {
|
||||||
const paragraphElement = goToElementFromPath(json, renderedParagraph.path);
|
const paragraphElement = goToElementFromPath(json, renderedParagraph.pathToParagraph);
|
||||||
replaceTokenInParagraphElement({
|
replaceTokenInParagraphElement({
|
||||||
paragraphElement,
|
paragraphElement,
|
||||||
renderedParagraph,
|
renderedParagraph,
|
||||||
@ -87,11 +98,7 @@ const goToElementFromPath = (json: Element, path: readonly number[]): Element =>
|
|||||||
// Which we do not want to double count
|
// Which we do not want to double count
|
||||||
for (let i = 1; i < path.length; i++) {
|
for (let i = 1; i < path.length; i++) {
|
||||||
const index = path[i];
|
const index = path[i];
|
||||||
const nextElements = element.elements;
|
const nextElements = element.elements!;
|
||||||
|
|
||||||
if (!nextElements) {
|
|
||||||
throw new Error("Could not find element");
|
|
||||||
}
|
|
||||||
|
|
||||||
element = nextElements[index];
|
element = nextElements[index];
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ describe("run-renderer", () => {
|
|||||||
const output = renderParagraphNode({ element: { name: "w:p" }, index: 0, parent: undefined });
|
const output = renderParagraphNode({ element: { name: "w:p" }, index: 0, parent: undefined });
|
||||||
expect(output).to.deep.equal({
|
expect(output).to.deep.equal({
|
||||||
index: -1,
|
index: -1,
|
||||||
path: [],
|
pathToParagraph: [],
|
||||||
runs: [],
|
runs: [],
|
||||||
text: "",
|
text: "",
|
||||||
});
|
});
|
||||||
@ -39,7 +39,7 @@ describe("run-renderer", () => {
|
|||||||
});
|
});
|
||||||
expect(output).to.deep.equal({
|
expect(output).to.deep.equal({
|
||||||
index: 0,
|
index: 0,
|
||||||
path: [0],
|
pathToParagraph: [0],
|
||||||
runs: [
|
runs: [
|
||||||
{
|
{
|
||||||
end: 4,
|
end: 4,
|
||||||
@ -79,7 +79,7 @@ describe("run-renderer", () => {
|
|||||||
});
|
});
|
||||||
expect(output).to.deep.equal({
|
expect(output).to.deep.equal({
|
||||||
index: 0,
|
index: 0,
|
||||||
path: [0],
|
pathToParagraph: [0],
|
||||||
runs: [
|
runs: [
|
||||||
{
|
{
|
||||||
end: 0,
|
end: 0,
|
||||||
|
@ -6,7 +6,7 @@ export interface IRenderedParagraphNode {
|
|||||||
readonly text: string;
|
readonly text: string;
|
||||||
readonly runs: readonly IRenderedRunNode[];
|
readonly runs: readonly IRenderedRunNode[];
|
||||||
readonly index: number;
|
readonly index: number;
|
||||||
readonly path: readonly number[];
|
readonly pathToParagraph: readonly number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface StartAndEnd {
|
interface StartAndEnd {
|
||||||
@ -35,7 +35,7 @@ export const renderParagraphNode = (node: ElementWrapper): IRenderedParagraphNod
|
|||||||
text: "",
|
text: "",
|
||||||
runs: [],
|
runs: [],
|
||||||
index: -1,
|
index: -1,
|
||||||
path: [],
|
pathToParagraph: [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,8 +50,7 @@ export const renderParagraphNode = (node: ElementWrapper): IRenderedParagraphNod
|
|||||||
|
|
||||||
return renderedRunNode;
|
return renderedRunNode;
|
||||||
})
|
})
|
||||||
.filter((e) => !!e)
|
.filter((e) => !!e);
|
||||||
.map((e) => e as IRenderedRunNode);
|
|
||||||
|
|
||||||
const text = runs.reduce((acc, curr) => acc + curr.text, "");
|
const text = runs.reduce((acc, curr) => acc + curr.text, "");
|
||||||
|
|
||||||
@ -59,7 +58,7 @@ export const renderParagraphNode = (node: ElementWrapper): IRenderedParagraphNod
|
|||||||
text,
|
text,
|
||||||
runs,
|
runs,
|
||||||
index: node.index,
|
index: node.index,
|
||||||
path: buildNodePath(node),
|
pathToParagraph: buildNodePath(node),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -139,6 +139,28 @@ const MOCK_JSON = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
name: "w:p",
|
||||||
|
elements: [
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
name: "w:r",
|
||||||
|
elements: [
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
name: "w:rPr",
|
||||||
|
elements: [{ type: "element", name: "w:b", attributes: { "w:val": "1" } }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
name: "w:t",
|
||||||
|
elements: [{ type: "text", text: "What a {{bold}} text!" }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: "element",
|
type: "element",
|
||||||
name: "w:p",
|
name: "w:p",
|
||||||
@ -535,6 +557,45 @@ const MOCK_JSON = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
name: "w:p",
|
||||||
|
attributes: {
|
||||||
|
"w14:paraId": "3BE1A671",
|
||||||
|
"w14:textId": "74E856C4",
|
||||||
|
"w:rsidR": "000D38A7",
|
||||||
|
"w:rsidRDefault": "000D38A7",
|
||||||
|
},
|
||||||
|
elements: [
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
name: "w:pPr",
|
||||||
|
elements: [{ type: "element", name: "w:pStyle", attributes: { "w:val": "Header" } }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
name: "w:r",
|
||||||
|
elements: [{ type: "element", name: "w:t", elements: [{ type: "text", text: "This is a {{head" }] }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
name: "w:r",
|
||||||
|
attributes: { "w:rsidR": "004A3A99" },
|
||||||
|
elements: [{ type: "element", name: "w:t", elements: [{ type: "text", text: "er" }] }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
name: "w:r",
|
||||||
|
elements: [
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
name: "w:t",
|
||||||
|
elements: [{ type: "text", text: "_adjective}} don’t you think?" }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: "element",
|
type: "element",
|
||||||
name: "w:sectPr",
|
name: "w:sectPr",
|
||||||
@ -574,7 +635,7 @@ describe("traverser", () => {
|
|||||||
expect(output).to.deep.equal([
|
expect(output).to.deep.equal([
|
||||||
{
|
{
|
||||||
index: 1,
|
index: 1,
|
||||||
path: [0, 0, 0, 8, 2, 0, 1],
|
pathToParagraph: [0, 0, 0, 9, 2, 0, 1],
|
||||||
runs: [
|
runs: [
|
||||||
{
|
{
|
||||||
end: 18,
|
end: 18,
|
||||||
@ -595,5 +656,76 @@ describe("traverser", () => {
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should find the location of text", () => {
|
||||||
|
const output = findLocationOfText(MOCK_JSON, "{{bold}}");
|
||||||
|
|
||||||
|
expect(output).to.deep.equal([
|
||||||
|
{
|
||||||
|
text: "What a {{bold}} text!",
|
||||||
|
runs: [
|
||||||
|
{
|
||||||
|
text: "What a {{bold}} text!",
|
||||||
|
parts: [{ text: "What a {{bold}} text!", index: 1, start: 0, end: 20 }],
|
||||||
|
index: 0,
|
||||||
|
start: 0,
|
||||||
|
end: 20,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
index: 5,
|
||||||
|
pathToParagraph: [0, 0, 0, 5],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should find the location of text", () => {
|
||||||
|
const output = findLocationOfText(MOCK_JSON, "{{bold}}");
|
||||||
|
|
||||||
|
expect(output).to.deep.equal([
|
||||||
|
{
|
||||||
|
text: "What a {{bold}} text!",
|
||||||
|
runs: [
|
||||||
|
{
|
||||||
|
text: "What a {{bold}} text!",
|
||||||
|
parts: [{ text: "What a {{bold}} text!", index: 1, start: 0, end: 20 }],
|
||||||
|
index: 0,
|
||||||
|
start: 0,
|
||||||
|
end: 20,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
index: 5,
|
||||||
|
pathToParagraph: [0, 0, 0, 5],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should find the location of text", () => {
|
||||||
|
const output = findLocationOfText(MOCK_JSON, "{{header_adjective}}");
|
||||||
|
|
||||||
|
expect(output).to.deep.equal([
|
||||||
|
{
|
||||||
|
text: "This is a {{header_adjective}} don’t you think?",
|
||||||
|
runs: [
|
||||||
|
{
|
||||||
|
text: "This is a {{head",
|
||||||
|
parts: [{ text: "This is a {{head", index: 0, start: 0, end: 15 }],
|
||||||
|
index: 1,
|
||||||
|
start: 0,
|
||||||
|
end: 15,
|
||||||
|
},
|
||||||
|
{ text: "er", parts: [{ text: "er", index: 0, start: 16, end: 17 }], index: 2, start: 16, end: 17 },
|
||||||
|
{
|
||||||
|
text: "_adjective}} don’t you think?",
|
||||||
|
parts: [{ text: "_adjective}} don’t you think?", index: 0, start: 18, end: 46 }],
|
||||||
|
index: 3,
|
||||||
|
start: 18,
|
||||||
|
end: 46,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
index: 14,
|
||||||
|
pathToParagraph: [0, 0, 0, 14],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -62,10 +62,10 @@ export default defineConfig({
|
|||||||
provider: "v8",
|
provider: "v8",
|
||||||
reporter: ["text", "json", "html"],
|
reporter: ["text", "json", "html"],
|
||||||
thresholds: {
|
thresholds: {
|
||||||
statements: 99.98,
|
statements: 100,
|
||||||
branches: 99.15,
|
branches: 99.35,
|
||||||
functions: 100,
|
functions: 100,
|
||||||
lines: 99.98,
|
lines: 100,
|
||||||
},
|
},
|
||||||
exclude: [
|
exclude: [
|
||||||
...configDefaults.exclude,
|
...configDefaults.exclude,
|
||||||
|
Reference in New Issue
Block a user