Skip to content

Commit dc61643

Browse files
tr3lanetavidis
andauthored
fix(libreoffice): don't force MS Word 97 infilter on .wps (#585)
* fix(libreoffice): don't force MS Word 97 infilter on .wps .wps is Microsoft Works, not MS Word 97/.doc. Forcing that infilter makes soffice reject a genuine Works document with "source file could not be loaded", even though the same file converts fine when no --infilter is passed at all (LibreOffice auto-detects it correctly). Fixes #582 * test: lock in wps export direction; document why null covers both sides Per review: the filter map feeds both --infilter and the --convert-to suffix, so wps: null also affects exporting TO wps. That is deliberate and behavior-preserving: LibreOffice has no Works export filter (its MS_Works filter is import-only), and bare `--convert-to wps` falls back to LibreOffice's default export filter for the extension - "MS Word 97", the exact filter this map pinned before, so export output is unchanged. Adds a regression test asserting the docx -> wps invocation. --------- Co-authored-by: tr3lane <182920672+tr3lane@users.noreply.github.com>
1 parent 393441f commit dc61643

2 files changed

Lines changed: 58 additions & 2 deletions

File tree

src/converters/libreoffice.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export const properties = {
7777

7878
type FileCategories = "text" | "calc";
7979

80-
const filters: Record<FileCategories, Record<string, string>> = {
80+
const filters: Record<FileCategories, Record<string, string | null>> = {
8181
text: {
8282
"602": "T602Document",
8383
abw: "AbiWord",
@@ -113,7 +113,20 @@ const filters: Record<FileCategories, Record<string, string>> = {
113113
txt: "Text",
114114
wn: "WriteNow",
115115
wpd: "WordPerfect",
116-
wps: "MS Word 97",
116+
// .wps is Microsoft Works, not MS Word 97/.doc - forcing the "MS Word 97"
117+
// filter makes soffice reject a genuine Works document with "source file
118+
// could not be loaded", even though it converts the same file fine with
119+
// no --infilter at all (LibreOffice auto-detects it correctly).
120+
//
121+
// null is deliberate for BOTH directions here, not just the import side:
122+
// this map feeds both --infilter (import) and the --convert-to suffix
123+
// (export). On import, null lets LibreOffice auto-detect - its Works
124+
// import filter (MS_Works, libwps-backed) is import-only, so it can only
125+
// be reached via auto-detection anyway. On export, LibreOffice has no
126+
// Works export filter at all; bare `--convert-to wps` falls back to its
127+
// default export filter for the extension, which is "MS Word 97" - the
128+
// exact filter this map pinned before, so export output is unchanged.
129+
wps: null,
117130
wpt: "MS Word 97 Vorlage",
118131
wri: "MS_Write",
119132
xhtml: "HTML (StarWriter)",

tests/converters/libreoffice.test.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,49 @@ test("uses only infilter when convertTo has no out filter (e.g., docx -> pdf)",
102102
expect(args.slice(-2)).toEqual(["out", "in.docx"]);
103103
});
104104

105+
test("does not force an infilter for wps (Microsoft Works, not MS Word 97)", async () => {
106+
// Regression test for https://github.com/C4illin/ConvertX/issues/582 -
107+
// forcing --infilter="MS Word 97" on a genuine .wps file makes soffice
108+
// reject it with "source file could not be loaded". No forced infilter
109+
// lets LibreOffice auto-detect the real format instead.
110+
await convert("in.wps", "wps", "docx", "out/out.docx", undefined, mockExecFile);
111+
112+
const { args } = requireDefined(calls[0], "Expected at least one execFile call");
113+
114+
expect(args).toEqual([
115+
"--headless",
116+
"--convert-to",
117+
"docx:MS Word 2007 XML",
118+
"--outdir",
119+
"out",
120+
"in.wps",
121+
]);
122+
expect(args.some((a) => a.startsWith("--infilter"))).toBe(false);
123+
});
124+
125+
test("does not force an outfilter for wps as an export target either", async () => {
126+
// wps shares one filter-map entry for both directions (see the comment in
127+
// libreoffice.ts) - docx's own infilter is still emitted (that describes
128+
// the real source file), but no --convert-to wps:<filter> suffix is
129+
// forced. Verified against a real soffice: this exact invocation succeeds,
130+
// with LibreOffice's default export filter for .wps ("MS Word 97" - the
131+
// same filter the map pinned before this change, so output is unchanged).
132+
// LibreOffice has no Works export filter, so no suffix could do better.
133+
await convert("in.docx", "docx", "wps", "out/out.wps", undefined, mockExecFile);
134+
135+
const { args } = requireDefined(calls[0], "Expected at least one execFile call");
136+
137+
expect(args).toEqual([
138+
"--headless",
139+
"--infilter=MS Word 2007 XML",
140+
"--convert-to",
141+
"wps",
142+
"--outdir",
143+
"out",
144+
"in.docx",
145+
]);
146+
});
147+
105148
test("strips leading './' from outdir", async () => {
106149
await convert("in.txt", "txt", "docx", "./out/out.docx", undefined, mockExecFile);
107150

0 commit comments

Comments
 (0)