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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## 0.6.5 - 2026-08-24

- Added a linear-time parser for pre-DWF-6 readable WHIP!/W2D streams such as AutoCAD R14 DWF V00.34 files.
- Added palette/background handling and core `C`, `L`, `P`, `T`, `M`, `F/f`, and `V/v` opcode support, including multiline counted point sets and correct triangle-strip triangulation.
- Added a public synthetic legacy ASCII regression fixture while keeping customer drawings out of the repository.
- Fixed strict TypeScript builds for path-command bounds calculation so both CI and the Cloudflare Pages demo build remain green.

## 0.6.4 - 2026-06-12

- Restored colorful W3D/HSF 3D rendering for eModel files whose ContentDefinition instance count does not match decoded shell geometry, avoiding incorrect ordinal material binding that could wash Robot Arm into gray.
Expand Down
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ English is the default language for the README, npm package, and online demo. Th
| npm | https://www.npmjs.com/package/dwf-viewer |
| scoped npm | https://www.npmjs.com/package/@flyfish-dev/dwf-viewer |

Current version: `0.6.4`
Current version: `0.6.5`

## Why

Expand All @@ -36,6 +36,7 @@ DWF Viewer 是面向 Web 的开源纯前端 DWF/DWFx 预览组件。它在浏览
| DWFx / OPC package | Supported |
| XPS FixedPage 2D sheets | Supported common subset with WebGL vector acceleration and Canvas text/image overlay |
| Classic binary WHIP!/W2D 2D sheets | Supported for core geometry/text/images used by Autodesk samples |
| Legacy readable ASCII WHIP!/W2D (including AutoCAD R14 DWF V00.34) | Supported for palette colors, backgrounds, core geometry, markers, fill state, and visibility |
| Textual W2D pages | Supported for smoke tests and simple sheets |
| W3D/HSF 3D eModel shell geometry | Supported: uncompressed, CS_TRIVIAL, and Edgebreaker shell meshes |
| Three.js adapter | Supported |
Expand Down Expand Up @@ -131,6 +132,12 @@ Version `0.6.3` adds a repair-and-fallback path for legacy Autodesk eModel XML m

This keeps valid 3D DWFx models from surfacing noisy `EMODEL_CONTENTDEF_PARSE_FAILED` diagnostics when only optional metadata XML is malformed.

## Legacy AutoCAD R14 ASCII DWF

Version `0.6.5` recognizes pre-DWF-6 readable WHIP!/W2D streams such as `DWF V00.34` files produced by AutoCAD R14. These files are plain opcode streams rather than DWF 6+ ZIP packages, so the loader routes them through a bounded stream parser instead of the generic line-oriented text fallback.

The supported legacy subset includes palette and background colors, `C`, `L`, `P`, `T`, `M`, `F/f`, and `V/v` opcodes, multiline counted point sets, and triangle-strip triangulation. The source background is emitted consistently across Canvas, WASM, and WebGL render paths.

## Three.js Integration

```ts
Expand Down Expand Up @@ -173,6 +180,7 @@ The demo examples are listed in `examples/manifest.json` and are intentionally d
| Example | Purpose |
|---|---|
| `blocks-and-tables.dwf` | Default demo. Binary WHIP!/W2D ePlot sample with fast loading and a clear first impression |
| `legacy-ascii-v0034.dwf` | Synthetic AutoCAD R14-style readable ASCII DWF regression covering multiline geometry, palette/background handling, triangle strips, fill state, markers, and visibility |
| `autodesk-floor-plans.dwfx` | Multi-page architectural DWFx/XPS sample, using A03 First Floor Plan for WebGL XPS, embedded font, and thin-line overview validation |
| `robot-arm.dwfx` | 3D W3D/HSF eModel with shell meshes, scene tree, materials, textures, and saved views |
| `minimal-xps.dwfx` | Small DWFx/XPS FixedPage sample |
Expand Down
23 changes: 23 additions & 0 deletions examples/legacy-ascii-v0034.dwf
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
(DWF V00.34)
(Author 'Regression fixture')
(Creator 'dwf-viewer')
(ColorMap 4
0,0,0,255 255,255,255,255 255,0,0,255 0,255,0,255)
(Background 0)
C 1
L 0,0 100,0
P 5 0,0 100,0 100,100
50,125 0,100
C 2
T 4 20,20 20,80 80,20 80,80
F
C 3
P 3 10,10 50,90 90,10
f
M 1 50,50
v
(URL 'javascript:void(0);')
L 0,0 100,100
V
L 0,100 100,100
(EndOfDWF)
13 changes: 13 additions & 0 deletions examples/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,19 @@
"maxWarnings": 0
}
},
{
"id": "legacy-ascii-v0034",
"title": "AutoCAD R14 · Legacy ASCII DWF V00.34",
"kind": "2d",
"path": "legacy-ascii-v0034.dwf",
"description": "Dependency-free regression fixture for pre-DWF-6 readable WHIP!/W2D streams, including multiline point sets, palette colors, triangle strips, fill state, visibility, and source background.",
"descriptionZh": "早期 AutoCAD 可读 ASCII DWF V00.34 回归示例,覆盖跨行点集、调色板、三角带、填充、可见性和源文件背景。",
"expected": {
"pageKind": "w2d-text",
"pages": 1,
"maxWarnings": 0
}
},
{
"id": "autodesk-floor-plans-dwfx",
"title": "Autodesk Floor Plans · 2D DWFx/XPS",
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "dwf-viewer",
"version": "0.6.4",
"version": "0.6.5",
"private": false,
"type": "module",
"description": "World's first open-source pure frontend DWF/DWFx preview component with WebGL XPS/W2D 2D rendering, W3D/HSF 3D rendering, embedded fonts, and WASM fallback.",
Expand Down
34 changes: 33 additions & 1 deletion scripts/validate-production.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,35 @@ const targets = [
{ label: 'Autodesk Floor Plans DWFx A03', path: 'examples/autodesk-floor-plans.dwfx', pageIndex: 3, kind: 'xps-fixed-page', minPages: 18, maxNonInfoDiagnostics: 0 },
{ label: 'Robot Arm 3D DWFx', path: 'examples/robot-arm.dwfx', kind: 'w3d-model', minMeshes: 30, minTriangles: 40000, minDistinctMeshColors: 24, maxNonInfoDiagnostics: 0, maxPageDiagnostics: 0 },
{ label: '2D sample DWFx', path: 'examples/minimal-xps.dwfx', kind: 'xps-fixed-page' },
{ label: 'official binary W2D DWF', path: 'examples/blocks-and-tables.dwf', kind: 'w2d-text' }
{ label: 'official binary W2D DWF', path: 'examples/blocks-and-tables.dwf', kind: 'w2d-text' },
{
label: 'AutoCAD R14 legacy ASCII DWF V00.34',
path: 'examples/legacy-ascii-v0034.dwf',
kind: 'w2d-text',
expectedPrimitives: 8,
expectedPrimitiveTypes: { polyline: 3, polygon: 3, rect: 2 },
expectedBackdrop: 'rgb(0, 0, 0)',
maxNonInfoDiagnostics: 0
}
];

let failed = false;
for (const t of targets) {
const doc = await openDwfDocument(await readFile(t.path), { fileName: t.path });
const page = doc.pageData[t.pageIndex ?? 0];
const nonInfo = (page?.diagnostics ?? []).filter(d => d.level !== 'info');
const primitiveTypes = page?.kind === 'w2d-text' ? countPrimitiveTypes(page.primitives) : undefined;
const backdrop = page?.kind === 'w2d-text' && page.primitives[0]?.type === 'rect' ? page.primitives[0].fill : undefined;
const record = {
label: t.label,
documentKind: doc.kind,
pages: doc.pageData.length,
pageIndex: t.pageIndex ?? 0,
pageKind: page?.kind,
pageName: page?.name,
primitives: page?.kind === 'w2d-text' ? page.primitives.length : undefined,
primitiveTypes,
backdrop,
meshes: page?.kind === 'w3d-model' ? page.model.meshes.length : undefined,
triangles: page?.kind === 'w3d-model' ? page.model.stats.triangleCount : undefined,
distinctMeshColors: page?.kind === 'w3d-model' ? distinctMeshColors(page) : undefined,
Expand All @@ -32,6 +46,18 @@ for (const t of targets) {
if (typeof t.minMeshes === 'number' && (!(page?.kind === 'w3d-model') || page.model.meshes.length < t.minMeshes)) failed = true;
if (typeof t.minTriangles === 'number' && (!(page?.kind === 'w3d-model') || page.model.stats.triangleCount < t.minTriangles)) failed = true;
if (typeof t.minDistinctMeshColors === 'number' && (!(page?.kind === 'w3d-model') || distinctMeshColors(page) < t.minDistinctMeshColors)) failed = true;
if (typeof t.expectedPrimitives === 'number' && (!(page?.kind === 'w2d-text') || page.primitives.length !== t.expectedPrimitives)) failed = true;
if (t.expectedPrimitiveTypes) {
if (!(page?.kind === 'w2d-text')) {
failed = true;
} else {
const actual = countPrimitiveTypes(page.primitives);
for (const [type, count] of Object.entries(t.expectedPrimitiveTypes)) {
if ((actual[type] ?? 0) !== count) failed = true;
}
}
}
if (typeof t.expectedBackdrop === 'string' && backdrop !== t.expectedBackdrop) failed = true;
if (typeof t.maxNonInfoDiagnostics === 'number' && nonInfo.length > t.maxNonInfoDiagnostics) failed = true;
if (typeof t.maxPageDiagnostics === 'number' && (page?.diagnostics?.length ?? 0) > t.maxPageDiagnostics) failed = true;
}
Expand Down Expand Up @@ -88,3 +114,9 @@ if (failed) process.exit(1);
function distinctMeshColors(page) {
return new Set(page.model.meshes.map(mesh => (mesh.color ?? []).map(v => Number(v).toFixed(4)).join(','))).size;
}

function countPrimitiveTypes(primitives) {
const counts = {};
for (const primitive of primitives) counts[primitive.type] = (counts[primitive.type] ?? 0) + 1;
return counts;
}
Loading
Loading