feat(comments): Support comment pictures (#3032)
* feat(comments): Support comment pictures * fix(demo): Fix the image path to load the image correctly * fix(test): Update the number of files asserted in the compiler test case * feat(comments): Support comment pictures * fix(demo): Fix the image path to load the image correctly * fix(test): Update the number of files asserted in the compiler test case * style(src): Format the code and remove extra blank lines --------- Co-authored-by: Dolan <dolan_miu@hotmail.com>
This commit is contained in:
@ -1,7 +1,7 @@
|
|||||||
// Simple example to add comments to a document
|
// Simple example to add comments to a document
|
||||||
|
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import { Document, Packer, Paragraph, TextRun, CommentRangeStart, CommentRangeEnd, CommentReference } from "docx";
|
import { Document, Packer, Paragraph, TextRun, CommentRangeStart, CommentRangeEnd, CommentReference, ImageRun } from "docx";
|
||||||
|
|
||||||
const doc = new Document({
|
const doc = new Document({
|
||||||
comments: {
|
comments: {
|
||||||
@ -20,6 +20,14 @@ const doc = new Document({
|
|||||||
}),
|
}),
|
||||||
new Paragraph({
|
new Paragraph({
|
||||||
children: [
|
children: [
|
||||||
|
new ImageRun({
|
||||||
|
type: "jpg",
|
||||||
|
data: fs.readFileSync("./demo/images/cat.jpg"),
|
||||||
|
transformation: {
|
||||||
|
width: 100,
|
||||||
|
height: 100,
|
||||||
|
},
|
||||||
|
}),
|
||||||
new TextRun({
|
new TextRun({
|
||||||
text: "comment text content",
|
text: "comment text content",
|
||||||
}),
|
}),
|
||||||
|
@ -36,7 +36,7 @@ describe("Compiler", () => {
|
|||||||
const fileNames = Object.keys(zipFile.files).map((f) => zipFile.files[f].name);
|
const fileNames = Object.keys(zipFile.files).map((f) => zipFile.files[f].name);
|
||||||
|
|
||||||
expect(fileNames).is.an.instanceof(Array);
|
expect(fileNames).is.an.instanceof(Array);
|
||||||
expect(fileNames).has.length(19);
|
expect(fileNames).has.length(20);
|
||||||
expect(fileNames).to.include("word/document.xml");
|
expect(fileNames).to.include("word/document.xml");
|
||||||
expect(fileNames).to.include("word/styles.xml");
|
expect(fileNames).to.include("word/styles.xml");
|
||||||
expect(fileNames).to.include("docProps/core.xml");
|
expect(fileNames).to.include("docProps/core.xml");
|
||||||
@ -96,7 +96,7 @@ describe("Compiler", () => {
|
|||||||
const fileNames = Object.keys(zipFile.files).map((f) => zipFile.files[f].name);
|
const fileNames = Object.keys(zipFile.files).map((f) => zipFile.files[f].name);
|
||||||
|
|
||||||
expect(fileNames).is.an.instanceof(Array);
|
expect(fileNames).is.an.instanceof(Array);
|
||||||
expect(fileNames).has.length(27);
|
expect(fileNames).has.length(28);
|
||||||
|
|
||||||
expect(fileNames).to.include("word/header1.xml");
|
expect(fileNames).to.include("word/header1.xml");
|
||||||
expect(fileNames).to.include("word/_rels/header1.xml.rels");
|
expect(fileNames).to.include("word/_rels/header1.xml.rels");
|
||||||
@ -131,7 +131,7 @@ describe("Compiler", () => {
|
|||||||
const fileNames = Object.keys(zipFile.files).map((f) => zipFile.files[f].name);
|
const fileNames = Object.keys(zipFile.files).map((f) => zipFile.files[f].name);
|
||||||
|
|
||||||
expect(fileNames).is.an.instanceof(Array);
|
expect(fileNames).is.an.instanceof(Array);
|
||||||
expect(fileNames).has.length(20);
|
expect(fileNames).has.length(21);
|
||||||
|
|
||||||
expect(fileNames).to.include("word/comments.xml");
|
expect(fileNames).to.include("word/comments.xml");
|
||||||
expect(fileNames).to.include("word/commentsExtended.xml");
|
expect(fileNames).to.include("word/commentsExtended.xml");
|
||||||
@ -163,7 +163,7 @@ describe("Compiler", () => {
|
|||||||
const spy = vi.spyOn(compiler["formatter"], "format");
|
const spy = vi.spyOn(compiler["formatter"], "format");
|
||||||
|
|
||||||
compiler.compile(file);
|
compiler.compile(file);
|
||||||
expect(spy).toBeCalledTimes(15);
|
expect(spy).toBeCalledTimes(16);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should work with media datas", () => {
|
it("should work with media datas", () => {
|
||||||
|
@ -32,6 +32,7 @@ type IXmlifyedFileMapping = {
|
|||||||
readonly FootNotesRelationships: IXmlifyedFile;
|
readonly FootNotesRelationships: IXmlifyedFile;
|
||||||
readonly Settings: IXmlifyedFile;
|
readonly Settings: IXmlifyedFile;
|
||||||
readonly Comments?: IXmlifyedFile;
|
readonly Comments?: IXmlifyedFile;
|
||||||
|
readonly CommentsRelationships?: IXmlifyedFile;
|
||||||
readonly FontTable?: IXmlifyedFile;
|
readonly FontTable?: IXmlifyedFile;
|
||||||
readonly FontTableRelationships?: IXmlifyedFile;
|
readonly FontTableRelationships?: IXmlifyedFile;
|
||||||
};
|
};
|
||||||
@ -104,7 +105,28 @@ export class Compiler {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const commentRelationshipCount = file.Comments.Relationships.RelationshipCount + 1;
|
||||||
|
const commentXmlData = xml(
|
||||||
|
this.formatter.format(file.Comments, {
|
||||||
|
viewWrapper: {
|
||||||
|
View: file.Comments,
|
||||||
|
Relationships: file.Comments.Relationships,
|
||||||
|
},
|
||||||
|
file,
|
||||||
|
stack: [],
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
indent: prettify,
|
||||||
|
declaration: {
|
||||||
|
standalone: "yes",
|
||||||
|
encoding: "UTF-8",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const documentMediaDatas = this.imageReplacer.getMediaData(documentXmlData, file.Media);
|
const documentMediaDatas = this.imageReplacer.getMediaData(documentXmlData, file.Media);
|
||||||
|
const commentMediaDatas = this.imageReplacer.getMediaData(commentXmlData, file.Media);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
Relationships: {
|
Relationships: {
|
||||||
@ -450,22 +472,41 @@ export class Compiler {
|
|||||||
path: "word/settings.xml",
|
path: "word/settings.xml",
|
||||||
},
|
},
|
||||||
Comments: {
|
Comments: {
|
||||||
data: xml(
|
data: (() => {
|
||||||
this.formatter.format(file.Comments, {
|
const xmlData = this.imageReplacer.replace(commentXmlData, commentMediaDatas, commentRelationshipCount);
|
||||||
viewWrapper: file.Document,
|
const referenedXmlData = this.numberingReplacer.replace(xmlData, file.Numbering.ConcreteNumbering);
|
||||||
file,
|
return referenedXmlData;
|
||||||
stack: [],
|
})(),
|
||||||
}),
|
|
||||||
{
|
|
||||||
indent: prettify,
|
|
||||||
declaration: {
|
|
||||||
standalone: "yes",
|
|
||||||
encoding: "UTF-8",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
),
|
|
||||||
path: "word/comments.xml",
|
path: "word/comments.xml",
|
||||||
},
|
},
|
||||||
|
CommentsRelationships: {
|
||||||
|
data: (() => {
|
||||||
|
commentMediaDatas.forEach((mediaData, i) => {
|
||||||
|
file.Comments.Relationships.createRelationship(
|
||||||
|
commentRelationshipCount + i,
|
||||||
|
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
|
||||||
|
`media/${mediaData.fileName}`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return xml(
|
||||||
|
this.formatter.format(file.Comments.Relationships, {
|
||||||
|
viewWrapper: {
|
||||||
|
View: file.Comments,
|
||||||
|
Relationships: file.Comments.Relationships,
|
||||||
|
},
|
||||||
|
file,
|
||||||
|
stack: [],
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
indent: prettify,
|
||||||
|
declaration: {
|
||||||
|
encoding: "UTF-8",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
})(),
|
||||||
|
path: "word/_rels/comments.xml.rels",
|
||||||
|
},
|
||||||
FontTable: {
|
FontTable: {
|
||||||
data: xml(
|
data: xml(
|
||||||
this.formatter.format(file.FontTable.View, {
|
this.formatter.format(file.FontTable.View, {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { FileChild } from "@file/file-child";
|
import { FileChild } from "@file/file-child";
|
||||||
|
import { Relationships } from "@file/relationships";
|
||||||
import { XmlAttributeComponent, XmlComponent } from "@file/xml-components";
|
import { XmlAttributeComponent, XmlComponent } from "@file/xml-components";
|
||||||
|
|
||||||
export type ICommentOptions = {
|
export type ICommentOptions = {
|
||||||
@ -136,6 +137,8 @@ export class Comment extends XmlComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
export class Comments extends XmlComponent {
|
export class Comments extends XmlComponent {
|
||||||
|
private readonly relationships: Relationships;
|
||||||
|
|
||||||
public constructor({ children }: ICommentsOptions) {
|
public constructor({ children }: ICommentsOptions) {
|
||||||
super("w:comments");
|
super("w:comments");
|
||||||
|
|
||||||
@ -178,5 +181,11 @@ export class Comments extends XmlComponent {
|
|||||||
for (const child of children) {
|
for (const child of children) {
|
||||||
this.root.push(new Comment(child));
|
this.root.push(new Comment(child));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.relationships = new Relationships();
|
||||||
|
}
|
||||||
|
|
||||||
|
public get Relationships(): Relationships {
|
||||||
|
return this.relationships;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user