wpd-codec 2.1.5 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -197,6 +197,7 @@ What the corpus settles:
197
197
  - **Two readings the specification does not settle, both chosen deliberately.** The Form function states its desired width and its desired length as two independent fields and its orientation as a third, and says nothing about whether the pair is written before or after the rotation — so a landscape form's dimensions go through exactly as written and the flag is reported through `wpd/landscape-orientation-unmapped` rather than rotated on this package's own inference. And a Table Column's `[width]` is the one horizontal dimension in the format the SDK does not tag `(WPU)`, so it is read as WordPerfect Units on the strength of the two gutter fields immediately after it, which are.
198
198
  - **Reading a document is not rendering one.** The corpus check confirms that each file reads, that its text is prose, and that the structures above are recovered; it does not compare the result against what WordPerfect itself would display, which would need a reference renderer this family does not have.
199
199
  - **The document-area's own file-size bound.** The header's file-size field is honoured only when self-consistent, because the SDK itself warns that a third-party writer failing to update it is a common real-world defect whose symptom is a document reading back blank.
200
+ - **A partially-shaded cell fill's own compositing formula.** A cell whose foreground and background colours are blended at a shading percentage other than 0 or 255 now resolves to a real two-colour `pattern` `ContentCellFill` (ExaDev/documents.js#1024) rather than being flattened to its background colour alone, but the mirrored SDK pages document the RGBS byte layout, not how the shading byte composites the pair — so the background shade byte is read as that layer's own opacity and the foreground's implied coverage is snapped to the nearest `percentN` step `ContentCellPatternTypeSchema` defines, a derivation rather than a cited value. Reported through `wpd/cell-fill-blended`.
200
201
 
201
202
  ## Conventions
202
203
 
package/dist/read.cjs CHANGED
@@ -125,11 +125,11 @@ function readCellAttributes(state, nonDeletable, sink) {
125
125
  const row = require_stream_table.findEmbeddedSubfunction(subfunctions, 128);
126
126
  const cellSpanning = spanning === void 0 ? void 0 : require_stream_table.readCellSpanning(spanning);
127
127
  const cellFill = fill === void 0 ? void 0 : require_stream_table.readCellFill(fill);
128
- if (cellFill?.blended === true) reportOnce(state, sink, require_diagnostics.WpdDiagnosticCodes.CellFillBlended, "A cell is filled with a shaded blend of two colours; its background colour is used and the blend is not reproduced.");
128
+ if (cellFill?.blended === true) reportOnce(state, sink, require_diagnostics.WpdDiagnosticCodes.CellFillBlended, "A cell is filled with a shaded blend of two colours, resolved to a 'pattern' fill whose density is this reader's own best-effort derivation, not a value confirmed against a specification.");
129
129
  return {
130
130
  attributes: {
131
131
  alignment: information === void 0 ? void 0 : require_stream_table.readCellInformation(information)?.alignment,
132
- background: cellFill?.background,
132
+ background: cellFill?.fill,
133
133
  columnSpan: cellSpanning?.columnSpan ?? NO_SPAN,
134
134
  rowSpan: cellSpanning?.rowSpan ?? NO_SPAN,
135
135
  covered: cellSpanning?.coveredFromLeft === true || cellSpanning?.coveredFromAbove === true
@@ -149,10 +149,7 @@ function closeCell(state, table, attributes) {
149
149
  blocks,
150
150
  ...attributes.columnSpan > NO_SPAN ? { colSpan: attributes.columnSpan } : {},
151
151
  ...attributes.rowSpan > NO_SPAN ? { rowSpan: attributes.rowSpan } : {},
152
- ...attributes.background === void 0 ? {} : { background: {
153
- kind: "solid",
154
- color: attributes.background
155
- } }
152
+ ...attributes.background === void 0 ? {} : { background: attributes.background }
156
153
  };
157
154
  table.cells.push(cell);
158
155
  }
package/dist/read.js CHANGED
@@ -124,11 +124,11 @@ function readCellAttributes(state, nonDeletable, sink) {
124
124
  const row = findEmbeddedSubfunction(subfunctions, 128);
125
125
  const cellSpanning = spanning === void 0 ? void 0 : readCellSpanning(spanning);
126
126
  const cellFill = fill === void 0 ? void 0 : readCellFill(fill);
127
- if (cellFill?.blended === true) reportOnce(state, sink, WpdDiagnosticCodes.CellFillBlended, "A cell is filled with a shaded blend of two colours; its background colour is used and the blend is not reproduced.");
127
+ if (cellFill?.blended === true) reportOnce(state, sink, WpdDiagnosticCodes.CellFillBlended, "A cell is filled with a shaded blend of two colours, resolved to a 'pattern' fill whose density is this reader's own best-effort derivation, not a value confirmed against a specification.");
128
128
  return {
129
129
  attributes: {
130
130
  alignment: information === void 0 ? void 0 : readCellInformation(information)?.alignment,
131
- background: cellFill?.background,
131
+ background: cellFill?.fill,
132
132
  columnSpan: cellSpanning?.columnSpan ?? NO_SPAN,
133
133
  rowSpan: cellSpanning?.rowSpan ?? NO_SPAN,
134
134
  covered: cellSpanning?.coveredFromLeft === true || cellSpanning?.coveredFromAbove === true
@@ -148,10 +148,7 @@ function closeCell(state, table, attributes) {
148
148
  blocks,
149
149
  ...attributes.columnSpan > NO_SPAN ? { colSpan: attributes.columnSpan } : {},
150
150
  ...attributes.rowSpan > NO_SPAN ? { rowSpan: attributes.rowSpan } : {},
151
- ...attributes.background === void 0 ? {} : { background: {
152
- kind: "solid",
153
- color: attributes.background
154
- } }
151
+ ...attributes.background === void 0 ? {} : { background: attributes.background }
155
152
  };
156
153
  table.cells.push(cell);
157
154
  }
@@ -1,4 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_errors = require("../errors.cjs");
2
3
  const require_bytes_view = require("../bytes/view.cjs");
3
4
  const require_stream_units = require("./units.cjs");
4
5
  //#region src/stream/table.ts
@@ -123,6 +124,34 @@ function readCellSpanning(data) {
123
124
  const COLOR_COMPONENT_MAX = 255;
124
125
  const RGBS_SIZE = 4;
125
126
  const FULL_SHADE = 255;
127
+ const PERCENT_STEPS = [
128
+ [5, "percent5"],
129
+ [10, "percent10"],
130
+ [12, "percent12"],
131
+ [15, "percent15"],
132
+ [20, "percent20"],
133
+ [25, "percent25"],
134
+ [30, "percent30"],
135
+ [35, "percent35"],
136
+ [37, "percent37"],
137
+ [40, "percent40"],
138
+ [45, "percent45"],
139
+ [50, "percent50"],
140
+ [55, "percent55"],
141
+ [60, "percent60"],
142
+ [62, "percent62"],
143
+ [65, "percent65"],
144
+ [70, "percent70"],
145
+ [75, "percent75"],
146
+ [80, "percent80"],
147
+ [85, "percent85"],
148
+ [87, "percent87"],
149
+ [90, "percent90"],
150
+ [95, "percent95"]
151
+ ];
152
+ function nearestPercentType(percent) {
153
+ return PERCENT_STEPS.reduce((best, step) => Math.abs(step[0] - percent) < Math.abs(best[0] - percent) ? step : best)[1];
154
+ }
126
155
  function colorAt(data, offset) {
127
156
  const r = data[offset];
128
157
  const g = data[offset + 1];
@@ -138,9 +167,23 @@ function readCellFill(data) {
138
167
  const background = colorAt(data, RGBS_SIZE);
139
168
  if (background === void 0) return;
140
169
  const backgroundShade = data[7];
170
+ if (backgroundShade === void 0 || backgroundShade === FULL_SHADE) return {
171
+ fill: {
172
+ kind: "solid",
173
+ color: background
174
+ },
175
+ blended: false
176
+ };
177
+ const foreground = colorAt(data, 0);
178
+ if (foreground === void 0) throw new require_errors.WpdFormatError("Cell fill has a readable background colour but an unreadable foreground colour, which the RGBS pair's own contiguous layout should make impossible.");
141
179
  return {
142
- background,
143
- blended: backgroundShade !== void 0 && backgroundShade !== FULL_SHADE
180
+ fill: {
181
+ kind: "pattern",
182
+ patternType: nearestPercentType(100 - backgroundShade / COLOR_COMPONENT_MAX * 100),
183
+ foregroundColor: foreground,
184
+ backgroundColor: background
185
+ },
186
+ blended: true
144
187
  };
145
188
  }
146
189
  //#endregion
@@ -1,4 +1,4 @@
1
- import { Alignment, Color } from "document-schema.js";
1
+ import { Alignment, ContentCellFill } from "document-schema.js";
2
2
  //#region src/stream/table.d.ts
3
3
  declare const CHARACTER_TABLE_DEFINITION = 42;
4
4
  declare const CHARACTER_DEFINE_TABLE_END = 43;
@@ -35,7 +35,7 @@ interface WpdCellSpanning {
35
35
  }
36
36
  declare function readCellSpanning(data: Uint8Array): WpdCellSpanning | undefined;
37
37
  interface WpdCellFill {
38
- readonly background: Color;
38
+ readonly fill: ContentCellFill;
39
39
  readonly blended: boolean;
40
40
  }
41
41
  declare function readCellFill(data: Uint8Array): WpdCellFill | undefined;
@@ -1,4 +1,4 @@
1
- import { Alignment, Color } from "document-schema.js";
1
+ import { Alignment, ContentCellFill } from "document-schema.js";
2
2
  //#region src/stream/table.d.ts
3
3
  declare const CHARACTER_TABLE_DEFINITION = 42;
4
4
  declare const CHARACTER_DEFINE_TABLE_END = 43;
@@ -35,7 +35,7 @@ interface WpdCellSpanning {
35
35
  }
36
36
  declare function readCellSpanning(data: Uint8Array): WpdCellSpanning | undefined;
37
37
  interface WpdCellFill {
38
- readonly background: Color;
38
+ readonly fill: ContentCellFill;
39
39
  readonly blended: boolean;
40
40
  }
41
41
  declare function readCellFill(data: Uint8Array): WpdCellFill | undefined;
@@ -1,3 +1,4 @@
1
+ import { WpdFormatError } from "../errors.js";
1
2
  import { uint16At } from "../bytes/view.js";
2
3
  import { pointsFromWpu } from "./units.js";
3
4
  //#region src/stream/table.ts
@@ -122,6 +123,34 @@ function readCellSpanning(data) {
122
123
  const COLOR_COMPONENT_MAX = 255;
123
124
  const RGBS_SIZE = 4;
124
125
  const FULL_SHADE = 255;
126
+ const PERCENT_STEPS = [
127
+ [5, "percent5"],
128
+ [10, "percent10"],
129
+ [12, "percent12"],
130
+ [15, "percent15"],
131
+ [20, "percent20"],
132
+ [25, "percent25"],
133
+ [30, "percent30"],
134
+ [35, "percent35"],
135
+ [37, "percent37"],
136
+ [40, "percent40"],
137
+ [45, "percent45"],
138
+ [50, "percent50"],
139
+ [55, "percent55"],
140
+ [60, "percent60"],
141
+ [62, "percent62"],
142
+ [65, "percent65"],
143
+ [70, "percent70"],
144
+ [75, "percent75"],
145
+ [80, "percent80"],
146
+ [85, "percent85"],
147
+ [87, "percent87"],
148
+ [90, "percent90"],
149
+ [95, "percent95"]
150
+ ];
151
+ function nearestPercentType(percent) {
152
+ return PERCENT_STEPS.reduce((best, step) => Math.abs(step[0] - percent) < Math.abs(best[0] - percent) ? step : best)[1];
153
+ }
125
154
  function colorAt(data, offset) {
126
155
  const r = data[offset];
127
156
  const g = data[offset + 1];
@@ -137,9 +166,23 @@ function readCellFill(data) {
137
166
  const background = colorAt(data, RGBS_SIZE);
138
167
  if (background === void 0) return;
139
168
  const backgroundShade = data[7];
169
+ if (backgroundShade === void 0 || backgroundShade === FULL_SHADE) return {
170
+ fill: {
171
+ kind: "solid",
172
+ color: background
173
+ },
174
+ blended: false
175
+ };
176
+ const foreground = colorAt(data, 0);
177
+ if (foreground === void 0) throw new WpdFormatError("Cell fill has a readable background colour but an unreadable foreground colour, which the RGBS pair's own contiguous layout should make impossible.");
140
178
  return {
141
- background,
142
- blended: backgroundShade !== void 0 && backgroundShade !== FULL_SHADE
179
+ fill: {
180
+ kind: "pattern",
181
+ patternType: nearestPercentType(100 - backgroundShade / COLOR_COMPONENT_MAX * 100),
182
+ foregroundColor: foreground,
183
+ backgroundColor: background
184
+ },
185
+ blended: true
143
186
  };
144
187
  }
145
188
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wpd-codec",
3
- "version": "2.1.5",
3
+ "version": "3.0.1",
4
4
  "description": "Hand-written read-only WordPerfect 6.x-X6 (.wpd) reader over document-schema.js's ContentDocument",
5
5
  "type": "module",
6
6
  "repository": {
@@ -80,8 +80,8 @@
80
80
  "license": "MIT",
81
81
  "packageManager": "pnpm@11.6.0",
82
82
  "dependencies": {
83
- "archive-codec": "^1.6.4",
84
- "document-schema.js": "^6.2.3",
83
+ "archive-codec": "^1.6.5",
84
+ "document-schema.js": "^7.0.0",
85
85
  "zod": "^4.4.3"
86
86
  },
87
87
  "devDependencies": {