Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Ensure necessary namespaces are in patched doc #2698

Merged
merged 6 commits into from
Jan 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@
// words - list of words to be always considered correct
"words": [
"Abjad",
"aink",
"aiueo",
"ATLEAST",
"chosung",
"clippy",
"datas",
"dcmitype",
"dcterms",
"docsify",
"dolan",
"execa",
Expand All @@ -32,6 +35,7 @@
"panose",
"rels",
"rsid",
"sdtdh",
"twip",
"twips",
"Xmlable",
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,5 @@ jobs:
uses: actions/checkout@v4
- name: Install Dependencies
run: npm ci --force
- name: Prettier
- name: CSpell
run: npm run cspell
18 changes: 7 additions & 11 deletions src/file/core-properties/properties.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { FontOptions } from "@file/fonts/font-table";
import { ICommentsOptions } from "@file/paragraph/run/comment-run";
import { IHyphenationOptions } from "@file/settings";
import { ICompatibilityOptions } from "@file/settings/compatibility";
import { StringContainer, XmlComponent } from "@file/xml-components";
import { StringContainer, XmlAttributeComponent, XmlComponent } from "@file/xml-components";
import { dateTimeValue } from "@util/values";

import { ICustomPropertyOptions } from "../custom-properties";
Expand Down Expand Up @@ -75,15 +75,7 @@ export type IPropertiesOptions = {
export class CoreProperties extends XmlComponent {
public constructor(options: Omit<IPropertiesOptions, "sections">) {
super("cp:coreProperties");
this.root.push(
new DocumentAttributes({
cp: "http://schemas.openxmlformats.org/package/2006/metadata/core-properties",
dc: "http://purl.org/dc/elements/1.1/",
dcterms: "http://purl.org/dc/terms/",
dcmitype: "http://purl.org/dc/dcmitype/",
xsi: "http://www.w3.org/2001/XMLSchema-instance",
}),
);
this.root.push(new DocumentAttributes(["cp", "dc", "dcterms", "dcmitype", "xsi"]));
if (options.title) {
this.root.push(new StringContainer("dc:title", options.title));
}
Expand All @@ -110,11 +102,15 @@ export class CoreProperties extends XmlComponent {
}
}

class TimestampElementProperties extends XmlAttributeComponent<{ readonly type: string }> {
protected readonly xmlKeys = { type: "xsi:type" };
}

class TimestampElement extends XmlComponent {
public constructor(name: string) {
super(name);
this.root.push(
new DocumentAttributes({
new TimestampElementProperties({
type: "dcterms:W3CDTF",
}),
);
Expand Down
131 changes: 51 additions & 80 deletions src/file/document/document-attributes.ts
Original file line number Diff line number Diff line change
@@ -1,89 +1,60 @@
import { XmlAttributeComponent } from "@file/xml-components";
import { AttributeMap, XmlAttributeComponent } from "@file/xml-components";

/* cSpell:disable */
export type IDocumentAttributesProperties = {
readonly wpc?: string;
readonly mc?: string;
readonly o?: string;
readonly r?: string;
readonly m?: string;
readonly v?: string;
readonly wp14?: string;
readonly wp?: string;
readonly w10?: string;
readonly w?: string;
readonly w14?: string;
readonly w15?: string;
readonly wpg?: string;
readonly wpi?: string;
readonly wne?: string;
readonly wps?: string;
readonly Ignorable?: string;
readonly cp?: string;
readonly dc?: string;
readonly dcterms?: string;
readonly dcmitype?: string;
readonly xsi?: string;
readonly type?: string;
readonly cx?: string;
readonly cx1?: string;
readonly cx2?: string;
readonly cx3?: string;
readonly cx4?: string;
readonly cx5?: string;
readonly cx6?: string;
readonly cx7?: string;
readonly cx8?: string;
readonly aink?: string;
readonly am3d?: string;
readonly w16cex?: string;
readonly w16cid?: string;
readonly w16?: string;
readonly w16sdtdh?: string;
readonly w16se?: string;
export const DocumentAttributeNamespaces = {
wpc: "http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas",
mc: "http://schemas.openxmlformats.org/markup-compatibility/2006",
o: "urn:schemas-microsoft-com:office:office",
r: "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
m: "http://schemas.openxmlformats.org/officeDocument/2006/math",
v: "urn:schemas-microsoft-com:vml",
wp14: "http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing",
wp: "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing",
w10: "urn:schemas-microsoft-com:office:word",
w: "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
w14: "http://schemas.microsoft.com/office/word/2010/wordml",
w15: "http://schemas.microsoft.com/office/word/2012/wordml",
wpg: "http://schemas.microsoft.com/office/word/2010/wordprocessingGroup",
wpi: "http://schemas.microsoft.com/office/word/2010/wordprocessingInk",
wne: "http://schemas.microsoft.com/office/word/2006/wordml",
wps: "http://schemas.microsoft.com/office/word/2010/wordprocessingShape",
cp: "http://schemas.openxmlformats.org/package/2006/metadata/core-properties",
dc: "http://purl.org/dc/elements/1.1/",
dcterms: "http://purl.org/dc/terms/",
dcmitype: "http://purl.org/dc/dcmitype/",
xsi: "http://www.w3.org/2001/XMLSchema-instance",
cx: "http://schemas.microsoft.com/office/drawing/2014/chartex",
cx1: "http://schemas.microsoft.com/office/drawing/2015/9/8/chartex",
cx2: "http://schemas.microsoft.com/office/drawing/2015/10/21/chartex",
cx3: "http://schemas.microsoft.com/office/drawing/2016/5/9/chartex",
cx4: "http://schemas.microsoft.com/office/drawing/2016/5/10/chartex",
cx5: "http://schemas.microsoft.com/office/drawing/2016/5/11/chartex",
cx6: "http://schemas.microsoft.com/office/drawing/2016/5/12/chartex",
cx7: "http://schemas.microsoft.com/office/drawing/2016/5/13/chartex",
cx8: "http://schemas.microsoft.com/office/drawing/2016/5/14/chartex",
aink: "http://schemas.microsoft.com/office/drawing/2016/ink",
am3d: "http://schemas.microsoft.com/office/drawing/2017/model3d",
w16cex: "http://schemas.microsoft.com/office/word/2018/wordml/cex",
w16cid: "http://schemas.microsoft.com/office/word/2016/wordml/cid",
w16: "http://schemas.microsoft.com/office/word/2018/wordml",
w16sdtdh: "http://schemas.microsoft.com/office/word/2020/wordml/sdtdatahash",
w16se: "http://schemas.microsoft.com/office/word/2015/wordml/symex",
};
/* cSpell:enable */

export type DocumentAttributeNamespace = keyof typeof DocumentAttributeNamespaces;

export type IDocumentAttributesProperties = Partial<Record<DocumentAttributeNamespace, string>> & {
readonly Ignorable?: string;
};

export class DocumentAttributes extends XmlAttributeComponent<IDocumentAttributesProperties> {
protected readonly xmlKeys = {
wpc: "xmlns:wpc",
mc: "xmlns:mc",
o: "xmlns:o",
r: "xmlns:r",
m: "xmlns:m",
v: "xmlns:v",
wp14: "xmlns:wp14",
wp: "xmlns:wp",
w10: "xmlns:w10",
w: "xmlns:w",
w14: "xmlns:w14",
w15: "xmlns:w15",
wpg: "xmlns:wpg",
wpi: "xmlns:wpi",
wne: "xmlns:wne",
wps: "xmlns:wps",
Ignorable: "mc:Ignorable",
cp: "xmlns:cp",
dc: "xmlns:dc",
dcterms: "xmlns:dcterms",
dcmitype: "xmlns:dcmitype",
xsi: "xmlns:xsi",
type: "xsi:type",
cx: "xmlns:cx",
cx1: "xmlns:cx1",
cx2: "xmlns:cx2",
cx3: "xmlns:cx3",
cx4: "xmlns:cx4",
cx5: "xmlns:cx5",
cx6: "xmlns:cx6",
cx7: "xmlns:cx7",
cx8: "xmlns:cx8",
aink: "xmlns:aink",
am3d: "xmlns:am3d",
w16cex: "xmlns:w16cex",
w16cid: "xmlns:w16cid",
w16: "xmlns:w16",
w16sdtdh: "xmlns:w16sdtdh",
w16se: "xmlns:w16se",
};
...Object.fromEntries(Object.keys(DocumentAttributeNamespaces).map((key) => [key, `xmlns:${key}`])),
} as AttributeMap<IDocumentAttributesProperties>;

public constructor(ns: readonly DocumentAttributeNamespace[], Ignorable?: string) {
super({ Ignorable, ...Object.fromEntries(ns.map((n) => [n, DocumentAttributeNamespaces[n]])) });
}
}
72 changes: 37 additions & 35 deletions src/file/document/document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,41 +37,43 @@ export class Document extends XmlComponent {
public constructor(options: IDocumentOptions) {
super("w:document");
this.root.push(
new DocumentAttributes({
wpc: "http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas",
mc: "http://schemas.openxmlformats.org/markup-compatibility/2006",
o: "urn:schemas-microsoft-com:office:office",
r: "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
m: "http://schemas.openxmlformats.org/officeDocument/2006/math",
v: "urn:schemas-microsoft-com:vml",
wp14: "http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing",
wp: "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing",
w10: "urn:schemas-microsoft-com:office:word",
w: "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
w14: "http://schemas.microsoft.com/office/word/2010/wordml",
w15: "http://schemas.microsoft.com/office/word/2012/wordml",
wpg: "http://schemas.microsoft.com/office/word/2010/wordprocessingGroup",
wpi: "http://schemas.microsoft.com/office/word/2010/wordprocessingInk",
wne: "http://schemas.microsoft.com/office/word/2006/wordml",
wps: "http://schemas.microsoft.com/office/word/2010/wordprocessingShape",
cx: "http://schemas.microsoft.com/office/drawing/2014/chartex",
cx1: "http://schemas.microsoft.com/office/drawing/2015/9/8/chartex",
cx2: "http://schemas.microsoft.com/office/drawing/2015/10/21/chartex",
cx3: "http://schemas.microsoft.com/office/drawing/2016/5/9/chartex",
cx4: "http://schemas.microsoft.com/office/drawing/2016/5/10/chartex",
cx5: "http://schemas.microsoft.com/office/drawing/2016/5/11/chartex",
cx6: "http://schemas.microsoft.com/office/drawing/2016/5/12/chartex",
cx7: "http://schemas.microsoft.com/office/drawing/2016/5/13/chartex",
cx8: "http://schemas.microsoft.com/office/drawing/2016/5/14/chartex",
aink: "http://schemas.microsoft.com/office/drawing/2016/ink",
am3d: "http://schemas.microsoft.com/office/drawing/2017/model3d",
w16cex: "http://schemas.microsoft.com/office/word/2018/wordml/cex",
w16cid: "http://schemas.microsoft.com/office/word/2016/wordml/cid",
w16: "http://schemas.microsoft.com/office/word/2018/wordml",
w16sdtdh: "http://schemas.microsoft.com/office/word/2020/wordml/sdtdatahash",
w16se: "http://schemas.microsoft.com/office/word/2015/wordml/symex",
Ignorable: "w14 w15 wp14",
}),
new DocumentAttributes(
[
"wpc",
"mc",
"o",
"r",
"m",
"v",
"wp14",
"wp",
"w10",
"w",
"w14",
"w15",
"wpg",
"wpi",
"wne",
"wps",
"cx",
"cx1",
"cx2",
"cx3",
"cx4",
"cx5",
"cx6",
"cx7",
"cx8",
"aink",
"am3d",
"w16cex",
"w16cid",
"w16",
"w16sdtdh",
"w16se",
],
"w14 w15 wp14",
),
);
this.body = new Body();
if (options.background) {
Expand Down
23 changes: 4 additions & 19 deletions src/file/numbering/numbering.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,25 +37,10 @@ export class Numbering extends XmlComponent {
public constructor(options: INumberingOptions) {
super("w:numbering");
this.root.push(
new DocumentAttributes({
wpc: "http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas",
mc: "http://schemas.openxmlformats.org/markup-compatibility/2006",
o: "urn:schemas-microsoft-com:office:office",
r: "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
m: "http://schemas.openxmlformats.org/officeDocument/2006/math",
v: "urn:schemas-microsoft-com:vml",
wp14: "http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing",
wp: "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing",
w10: "urn:schemas-microsoft-com:office:word",
w: "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
w14: "http://schemas.microsoft.com/office/word/2010/wordml",
w15: "http://schemas.microsoft.com/office/word/2012/wordml",
wpg: "http://schemas.microsoft.com/office/word/2010/wordprocessingGroup",
wpi: "http://schemas.microsoft.com/office/word/2010/wordprocessingInk",
wne: "http://schemas.microsoft.com/office/word/2006/wordml",
wps: "http://schemas.microsoft.com/office/word/2010/wordprocessingShape",
Ignorable: "w14 w15 wp14",
}),
new DocumentAttributes(
["wpc", "mc", "o", "r", "m", "v", "wp14", "wp", "w10", "w", "w14", "w15", "wpg", "wpi", "wne", "wps"],
"w14 w15 wp14",
),
);

const abstractNumbering = new AbstractNumbering(this.abstractNumUniqueNumericId(), [
Expand Down
4 changes: 4 additions & 0 deletions src/file/styles/external-styles-factory.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ describe("External styles factory", () => {
expect(() => new ExternalStylesFactory().newInstance(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><foo/>`)).to.throw(
"can not find styles element",
);

expect(() => new ExternalStylesFactory().newInstance(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>`)).to.throw(
"can not find styles element",
);
});

it("should parse styles elements", () => {
Expand Down
9 changes: 1 addition & 8 deletions src/file/styles/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,7 @@ export type IDefaultStylesOptions = {

export class DefaultStylesFactory {
public newInstance(options: IDefaultStylesOptions = {}): IStylesOptions {
const documentAttributes = new DocumentAttributes({
mc: "http://schemas.openxmlformats.org/markup-compatibility/2006",
r: "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
w: "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
w14: "http://schemas.microsoft.com/office/word/2010/wordml",
w15: "http://schemas.microsoft.com/office/word/2012/wordml",
Ignorable: "w14 w15",
});
const documentAttributes = new DocumentAttributes(["mc", "r", "w", "w14", "w15"], "w14 w15");
return {
initialStyles: documentAttributes,
importedStyles: [
Expand Down
2 changes: 1 addition & 1 deletion src/file/xml-components/default-attributes.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { BaseXmlComponent, IContext } from "./base";
import { IXmlAttribute, IXmlableObject } from "./xmlable-object";

type AttributeMap<T> = Record<keyof T, string>;
export type AttributeMap<T> = Record<keyof T, string>;

export type AttributeData = Record<string, boolean | number | string>;
export type AttributePayload<T> = { readonly [P in keyof T]: { readonly key: string; readonly value: T[P] } };
Expand Down
19 changes: 19 additions & 0 deletions src/patcher/from-docx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import JSZip from "jszip";
import { Element, js2xml } from "xml-js";

import { ImageReplacer } from "@export/packer/image-replacer";
import { DocumentAttributeNamespaces } from "@file/document";
import { IViewWrapper } from "@file/document-wrapper";
import { File } from "@file/file";
import { FileChild } from "@file/file-child";
Expand Down Expand Up @@ -100,6 +101,24 @@ export const patchDocument = async <T extends PatchDocumentOutputType = PatchDoc
}

const json = toJson(await value.async("text"));

if (key === "word/document.xml") {
const document = json.elements?.find((i) => i.name === "w:document");
if (document) {
// We could check all namespaces from Document, but we'll instead
// check only those that may be used by our element types.

// eslint-disable-next-line functional/immutable-data
document.attributes = document.attributes ?? {};
for (const ns of ["mc", "wp", "r", "w15", "m"] as const) {
// eslint-disable-next-line functional/immutable-data
document.attributes[`xmlns:${ns}`] = DocumentAttributeNamespaces[ns];
}
// eslint-disable-next-line functional/immutable-data
document.attributes["mc:Ignorable"] = `${document.attributes["mc:Ignorable"] || ""} w15`.trim();
}
}

if (key.startsWith("word/") && !key.endsWith(".xml.rels")) {
const context: IContext = {
file,
Expand Down
Loading