Errors
The stable codes a refused import or export carries, and how to ask before writing.
DocxImportError and DocxExportError carry a stable code; their messages may be reworded.
Both classes are exported from the root entry as well.
import { DocxImportError, importDocx } from "@portone/docx-editor/core";
try {
importDocx(source);
} catch (error) {
if (error instanceof DocxImportError) {
console.error(error.code);
}
throw error;
}Import codes
| Code | Why the document was refused |
|---|---|
no-xml-parser | No xmlParser was passed and there is no DOMParser global. See Server environments. |
not-a-docx | The bytes are not a readable zip container, or its entries are not a DOCX package's. |
too-large | The package inflates beyond the import limits. |
missing-part | The package has no main document part. |
missing-body | The main part has no body. |
malformed-xml | The XML cannot be parsed, declares a DTD, or is inconsistent. |
unsupported-conformance | The package is ECMA-376 Strict; the editor reads and writes Transitional. |
unsupported-content | The document holds markup that could not be written back unchanged, including a main part whose root does not bind the w prefix to the Transitional namespace. |
not-a-docx and too-large are usually the person's to fix; the others describe the file.
Checking before export
exportProblems(doc, session) reports the reasons exportDocx would refuse the document, in the
order the writer would raise them:
import { exportDocx, exportProblems } from "@portone/docx-editor/core";
const problems = exportProblems(doc, session);
if (problems.length > 0) {
console.warn(problems[0].code, problems[0].reason.kind);
} else {
const bytes = exportDocx(doc, session);
}Each problem carries the code and message the DocxExportError would carry, a reason saying
which situation was met, and pos when the problem stands at a place in the document.
The message is an English one-liner for a developer and may be reworded; switch on code and
reason.kind instead.
reason.kind | code | What it names |
|---|---|---|
unreadable-preserved-xml | malformed-xml | Preserved markup holding a bookmark that could not be parsed. |
unnamed-bookmark | malformed-xml | A bookmark marker, "start" or "end", carrying no id to pair it by. |
unmatched-bookmark | malformed-xml | The bookmark id whose partner is gone, and which marker is left standing. |
repeated-bookmark-start | malformed-xml | A bookmark id started twice. |
unwritable-part-root | malformed-xml | The part whose root element the writer cannot rewrite around. |
conflicting-part-prefix | unsupported-content | The path of a part a change is written into, and the prefix its root binds to a namespace of its own. |
vertical-merge-past-table | invalid-table | A vertical merge reaching past the last row of its table. |
lost-preserved-xml | lost-original | The node written from its original XML alone, which it no longer holds. |
preserved-from-another-document | lost-original | A placeholder pasted in from another open document, and the sessionId it was opened in. |
duplicate-preserved-block | unsupported-content | The node standing in two places. |
undefined-list | unsupported-content | The numId of a list nothing defines. |
unwritten-story-change | unsupported-content | The story whose change - "added", "edited" or "removed" - nothing writes back. |
story-id-not-a-number | unsupported-content | The story added under an id no entry can be identified by. |
missing-content-types | missing-content-types | The part the writer would add to a package with no content types part to declare it in. |
Every reason but unwritable-part-root, conflicting-part-prefix and missing-content-types
carries a story: the side story the content stands in, or null for the body. A story has a
kind (comment, footnote, endnote, header or footer) and an id: a note's number, a
comment's id, or a header or footer part's path. A part is media, numbering, comments,
commentsExtended, people, commentsIds, commentsExtensible, footnotes or endnotes. A
path is where the part sits in the package.
That is enough to say what a person has to put back:
import type { ExportProblem } from "@portone/docx-editor/core";
function whatToRevert({ reason }: ExportProblem): string {
switch (reason.kind) {
case "unwritten-story-change":
return `Revert ${reason.story.kind} ${reason.story.id} to save this file.`;
case "duplicate-preserved-block":
return "A block was copied. Delete the copy to save this file.";
case "undefined-list":
return "Remove the list from this paragraph to save this file.";
default:
return "This document cannot be saved as it stands.";
}
}pos is a position in the body: the block or marker itself, or, for a footnote or an endnote, the
first reference to that note, since the note's text stands nowhere in the body. It is absent for a
problem of the package, for content of a header, footer or comment story, and for a note nothing
refers to; reason.story then says which story to open.
exportDocx throws the first entry of the same list, and the DocxExportError carries that entry
as problem, so a catch reads the same reason and pos without asking again.
An empty list does not guarantee that writing will succeed: a DocxExportError the list did not
predict is thrown under the same codes with no problem, for a main part that binds the
relationship prefix r to another namespace while a link is written (unsupported-content), for a
plugin that wrote an invalid node or attr, or for a bug in the editor.
Both functions need an XML parser, and are refused with the import code no-xml-parser without
one.
An export failure leaves the editor state untouched.
Inside an editor, canExport asks the same question of the
state.
In the React component
When import fails, the component renders renderImportError or its built-in panel in place of the
editor; only DocxImportError is rendered, and anything else is thrown for an error boundary.
downloadDocx returns blocked with the problems above rather than throwing, while exportBytes
throws DocxExportError; a refusal the list did not predict is thrown by both.