diff --git a/src/__tests__/__snapshots__/images.test.ts.snap b/src/__tests__/__snapshots__/images.test.ts.snap
index 6e1d1a84..a0eb18a7 100644
--- a/src/__tests__/__snapshots__/images.test.ts.snap
+++ b/src/__tests__/__snapshots__/images.test.ts.snap
@@ -2375,3 +2375,261 @@ Object {
"_tag": "w:document",
}
`;
+
+exports[`007: can inject an image in a document that already contains images (regression test for #144) 1`] = `
+"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+
+
+ 197485
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+"
+`;
diff --git a/src/__tests__/fixtures/imageExistingMultiple.docx b/src/__tests__/fixtures/imageExistingMultiple.docx
new file mode 100644
index 00000000..ce06860d
Binary files /dev/null and b/src/__tests__/fixtures/imageExistingMultiple.docx differ
diff --git a/src/__tests__/images.test.ts b/src/__tests__/images.test.ts
index c2064b19..b63f8a9d 100644
--- a/src/__tests__/images.test.ts
+++ b/src/__tests__/images.test.ts
@@ -271,21 +271,23 @@ it('007: can inject an image in a document that already contains images (regress
const buff = await fs.promises.readFile(
path.join(__dirname, 'fixtures', 'sample.png')
);
- const report = await createReport({
- template,
- data: {
- cv: { ProfilePicture: { url: 'abc' } },
- },
- additionalJsContext: {
- getImage: () => ({
- width: 6,
- height: 6,
- data: buff,
- extension: '.png',
- }),
- },
- });
- // TODO: can only be tested by loading the result into MS Word. It will show a file corruption warning.
- fs.writeFileSync('test.docx', report);
- expect(report).toBeInstanceOf(Uint8Array);
+ expect(
+ await createReport(
+ {
+ template,
+ data: {
+ cv: { ProfilePicture: { url: 'abc' } },
+ },
+ additionalJsContext: {
+ getImage: () => ({
+ width: 6,
+ height: 6,
+ data: buff,
+ extension: '.png',
+ }),
+ },
+ },
+ 'XML'
+ )
+ ).toMatchSnapshot();
});
diff --git a/src/__tests__/unit.test.ts b/src/__tests__/unit.test.ts
index f664e0fe..df9beddd 100644
--- a/src/__tests__/unit.test.ts
+++ b/src/__tests__/unit.test.ts
@@ -1,8 +1,14 @@
import path from 'path';
import { zipLoad } from '../zip';
-import { readContentTypes, getMainDoc, getMetadata } from '../main';
+import {
+ readContentTypes,
+ getMainDoc,
+ getMetadata,
+ parseTemplate,
+} from '../main';
import fs from 'fs';
import { setDebugLogSink } from '../debug';
+import { findHighestImgId } from '../processTemplate';
if (process.env.DEBUG) setDebugLogSink(console.log);
@@ -69,3 +75,13 @@ describe('getMetadata', () => {
}
});
});
+
+describe('findHighestImgId', () => {
+ it('returns 0 when doc contains no images', async () => {
+ const template = await fs.promises.readFile(
+ path.join(__dirname, 'fixtures', 'imageExistingMultiple.docx')
+ );
+ const { jsTemplate } = await parseTemplate(template);
+ expect(findHighestImgId(jsTemplate)).toBe(3);
+ });
+});
diff --git a/src/main.ts b/src/main.ts
index b22c5b36..055de255 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -15,6 +15,7 @@ import {
walkTemplate,
getCommand,
splitCommand,
+ newContext,
} from './processTemplate';
import {
UserOptions,
@@ -37,7 +38,7 @@ const DEFAULT_LITERAL_XML_DELIMITER = '||' as const;
const CONTENT_TYPES_PATH = '[Content_Types].xml' as const;
const TEMPLATE_PATH = 'word' as const;
-async function parseTemplate(template: Buffer) {
+export async function parseTemplate(template: Buffer) {
logger.debug('Unzipping...');
const zip = await zipLoad(template);
@@ -318,7 +319,8 @@ export async function listCommands(
const prepped = preprocessTemplate(jsTemplate, opts.cmdDelimiter);
const commands: CommandSummary[] = [];
- await walkTemplate(undefined, prepped, opts, async (data, node, ctx) => {
+ const ctx = newContext(opts);
+ await walkTemplate(undefined, prepped, ctx, async (data, node, ctx) => {
const raw = getCommand(ctx.cmd, ctx.shorthands, ctx.options.fixSmartQuotes);
ctx.cmd = ''; // flush the context
const { cmdName, cmdRest: code } = splitCommand(raw);
diff --git a/src/processTemplate.ts b/src/processTemplate.ts
index 5c41b35a..482dba55 100755
--- a/src/processTemplate.ts
+++ b/src/processTemplate.ts
@@ -32,7 +32,7 @@ import {
} from './errors';
import { logger } from './debug';
-function newContext(options: CreateReportOptions): Context {
+export function newContext(options: CreateReportOptions, imageId = 0): Context {
return {
gCntIf: 0,
level: 1,
@@ -43,7 +43,7 @@ function newContext(options: CreateReportOptions): Context {
'w:p': { text: '', cmds: '', fInsertedText: false },
'w:tr': { text: '', cmds: '', fInsertedText: false },
},
- imageId: 0,
+ imageId,
images: {},
linkId: 0,
links: {},
@@ -120,17 +120,39 @@ export async function produceJsReport(
template: Node,
options: CreateReportOptions
): Promise {
- return walkTemplate(data, template, options, processCmd);
+ const highestImgId = findHighestImgId(template);
+ const ctx = newContext(options, highestImgId);
+ return walkTemplate(data, template, ctx, processCmd);
+}
+
+export function findHighestImgId(mainDoc: Node): number {
+ const doc_ids: number[] = [];
+ const search = (n: Node) => {
+ for (const c of n._children) {
+ const tag = c._fTextNode ? null : c._tag;
+ if (tag == null) continue;
+ if (tag === 'wp:docPr') {
+ if (c._fTextNode) continue;
+ const raw = c._attrs.id;
+ if (typeof raw !== 'string') continue;
+ const id = Number.parseInt(raw, 10);
+ if (Number.isSafeInteger(id)) doc_ids.push(id);
+ }
+ if (c._children.length > 0) search(c);
+ }
+ };
+ search(mainDoc);
+ if (doc_ids.length > 0) return Math.max(...doc_ids);
+ return 0;
}
export async function walkTemplate(
data: ReportData | undefined,
template: Node,
- options: CreateReportOptions,
+ ctx: Context,
processor: CommandProcessor
): Promise {
const out: Node = cloneNodeWithoutChildren(template);
- const ctx = newContext(options);
let nodeIn: Node = template;
let nodeOut: Node = out;
let move;