Skip to content
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
8 changes: 6 additions & 2 deletions packages/super-editor/src/core/DocxZipper.js
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ class DocxZipper {
return zip;
}

async updateZip({ docx, updatedDocs, originalDocxFile, media, fonts, isHeadless }) {
async updateZip({ docx, updatedDocs, originalDocxFile, media, fonts, isHeadless, compression = 'DEFLATE' }) {
// We use a different re-zip process if we have the original docx vs the docx xml metadata
let zip;

Expand All @@ -274,7 +274,11 @@ class DocxZipper {

// If we are headless we don't have 'blob' support, so export as 'nodebuffer'
const exportType = isHeadless ? 'nodebuffer' : 'blob';
return await zip.generateAsync({ type: exportType });
return await zip.generateAsync({
type: exportType,
compression,
compressionOptions: compression === 'DEFLATE' ? { level: 6 } : undefined,
});
}

/**
Expand Down
91 changes: 91 additions & 0 deletions packages/super-editor/src/core/DocxZipper.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,97 @@ describe('DocxZipper - UTF-16 XML handling', () => {
});
});

describe('DocxZipper - updateZip compression', () => {
it('uses DEFLATE compression by default', async () => {
const zipper = new DocxZipper();

const contentTypes = `<?xml version="1.0" encoding="UTF-8"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
<Default Extension="xml" ContentType="application/xml"/>
<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
</Types>`;

const documentXml = `<?xml version="1.0" encoding="UTF-8"?>
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
<w:body><w:p><w:r><w:t>${'Hello world. '.repeat(100)}</w:t></w:r></w:p></w:body>
</w:document>`;

const docx = [
{ name: '[Content_Types].xml', content: contentTypes },
{ name: 'word/document.xml', content: documentXml },
];

const result = await zipper.updateZip({
docx,
updatedDocs: {},
media: {},
fonts: {},
isHeadless: true,
});

// Verify the output is compressed by checking DEFLATE produces smaller output than STORE
const storeResult = await new DocxZipper().updateZip({
docx,
updatedDocs: {},
media: {},
fonts: {},
isHeadless: true,
compression: 'STORE',
});

expect(result.length).toBeLessThan(storeResult.length);

// Verify the compressed output is a valid zip that can be read back
const readBack = await new JSZip().loadAsync(result);
const docXml = await readBack.file('word/document.xml').async('string');
expect(docXml).toContain('Hello world.');
});

it('respects STORE compression when explicitly requested', async () => {
const zipper = new DocxZipper();

const contentTypes = `<?xml version="1.0" encoding="UTF-8"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
<Default Extension="xml" ContentType="application/xml"/>
<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
</Types>`;

const documentXml = `<?xml version="1.0" encoding="UTF-8"?>
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
<w:body><w:p><w:r><w:t>${'Hello world. '.repeat(100)}</w:t></w:r></w:p></w:body>
</w:document>`;

const docx = [
{ name: '[Content_Types].xml', content: contentTypes },
{ name: 'word/document.xml', content: documentXml },
];

const result = await zipper.updateZip({
docx,
updatedDocs: {},
media: {},
fonts: {},
isHeadless: true,
compression: 'STORE',
});

// STORE should produce output roughly the size of the uncompressed content
// (plus ZIP overhead), so it should be larger than DEFLATE
const deflateResult = await new DocxZipper().updateZip({
docx,
updatedDocs: {},
media: {},
fonts: {},
isHeadless: true,
compression: 'DEFLATE',
});

expect(result.length).toBeGreaterThan(deflateResult.length);
});
});

describe('DocxZipper - updateContentTypes', () => {
it('adds header/footer overrides for newly added parts', async () => {
const zipper = new DocxZipper();
Expand Down
7 changes: 7 additions & 0 deletions packages/super-editor/src/core/Editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ export interface SaveOptions {

/** Highlight color for fields */
fieldsHighlightColor?: string | null;

/** ZIP compression method for docx export. Defaults to 'DEFLATE'. Use 'STORE' for faster exports without compression. */
compression?: 'DEFLATE' | 'STORE';
}

/**
Expand Down Expand Up @@ -2484,6 +2487,7 @@ export class Editor extends EventEmitter<EditorEventMap> {
comments,
getUpdatedDocs = false,
fieldsHighlightColor = null,
compression,
}: {
isFinalDoc?: boolean;
commentsType?: string;
Expand All @@ -2492,6 +2496,7 @@ export class Editor extends EventEmitter<EditorEventMap> {
comments?: Comment[];
getUpdatedDocs?: boolean;
fieldsHighlightColor?: string | null;
compression?: 'DEFLATE' | 'STORE';
} = {}): Promise<Blob | ArrayBuffer | Buffer | Record<string, string> | ProseMirrorJSON | string | undefined> {
try {
// Use provided comments, or fall back to imported comments from converter
Expand Down Expand Up @@ -2623,6 +2628,7 @@ export class Editor extends EventEmitter<EditorEventMap> {
media,
fonts: this.options.fonts,
isHeadless: this.options.isHeadless,
compression,
});

return result;
Expand Down Expand Up @@ -2893,6 +2899,7 @@ export class Editor extends EventEmitter<EditorEventMap> {
commentsType: options?.commentsType,
comments: options?.comments,
fieldsHighlightColor: options?.fieldsHighlightColor,
compression: options?.compression,
});

return result as Blob | Buffer;
Expand Down