Skip to content

Commit b0ad10f

Browse files
committed
feat: add DWG-to-DXF conversion support in javascript api
1 parent c472cb6 commit b0ad10f

10 files changed

Lines changed: 259 additions & 77 deletions

File tree

bindings/javascript/embind/binding_func.cpp

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,16 @@
66
#include "dwg.h"
77
#include "common.h"
88
#include "binding_common.h"
9+
#ifdef __cplusplus
10+
extern "C" {
11+
#endif
12+
#ifndef DISABLE_DXF
13+
#include "bits.h"
14+
#include "out_dxf.h"
15+
#endif
16+
#ifdef __cplusplus
17+
}
18+
#endif
919

1020
using namespace emscripten;
1121

@@ -27,14 +37,68 @@ emscripten::val dwg_read_file_wrapper(const std::string& filename) {
2737
return result;
2838
}
2939

40+
int dwg_write_dxf_wrapper(
41+
const std::string& input_filename,
42+
const std::string& output_filename) {
43+
#ifdef DISABLE_DXF
44+
(void)input_filename;
45+
(void)output_filename;
46+
return DWG_ERR_NOTYETSUPPORTED;
47+
#else
48+
if (input_filename == output_filename)
49+
return DWG_ERR_IOERROR;
50+
51+
Dwg_Data dwg = {};
52+
Bit_Chain dat = {};
53+
54+
int error = dwg_read_file(input_filename.c_str(), &dwg);
55+
if (error >= DWG_ERR_CRITICAL)
56+
{
57+
dwg_free(&dwg);
58+
return error;
59+
}
60+
61+
dat.version = dwg.header.version;
62+
dat.from_version = dwg.header.from_version;
63+
dat.fh = fopen(output_filename.c_str(), "wb");
64+
if (!dat.fh)
65+
{
66+
dwg_free(&dwg);
67+
return DWG_ERR_IOERROR;
68+
}
69+
70+
error = dwg_write_dxf(&dat, &dwg);
71+
fclose(dat.fh);
72+
dwg_free(&dwg);
73+
return error;
74+
#endif
75+
}
76+
3077
// emscripten::val dxf_read_file_wrapper(const std::string& filename) {
31-
// Dwg_Data* dwg = new Dwg_Data();
78+
// emscripten::val result = emscripten::val::object();
79+
// #if defined(DISABLE_DXF) || !defined(USE_WRITE)
80+
// (void)filename;
81+
// result.set("error", DWG_ERR_NOTYETSUPPORTED);
82+
// result.set("data", static_cast<uintptr_t>(0));
83+
// return result;
84+
// #else
85+
// Dwg_Data* dwg = new Dwg_Data();
3286
// int error = dxf_read_file(filename.c_str(), dwg);
87+
// if (error < DWG_ERR_CRITICAL)
88+
// {
89+
// for (BITCODE_BL i = 0; i < dwg->num_object_refs; i++)
90+
// {
91+
// Dwg_Object *restrict obj
92+
// = dwg_resolve_handle (dwg, dwg->object_ref[i]->absolute_ref);
93+
// dwg->object_ref[i]->obj = obj;
94+
// }
95+
// dwg->dirty_refs = 0;
96+
// }
3397

34-
// emscripten::val result = emscripten::val::object();
3598
// result.set("error", error);
3699
// result.set("data", reinterpret_cast<uintptr_t>(dwg));
37100
// return result;
101+
// #endif
38102
// }
39103

40104
// emscripten::val dwg_write_file_wrapper(
@@ -843,6 +907,7 @@ uintptr_t dwg_absref_get_object_wrapper(
843907

844908
EMSCRIPTEN_BINDINGS(libredwg_api) {
845909
DEFINE_FUNC(dwg_read_file);
910+
DEFINE_FUNC(dwg_write_dxf);
846911
// DEFINE_FUNC(dxf_read_file);
847912
// DEFINE_FUNC(dwg_write_file);
848913
DEFINE_FUNC(dwg_get_version_type);
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>Convert Dwg to DXF</title>
7+
<style>
8+
#status:empty {
9+
height: 1em;
10+
}
11+
</style>
12+
</head>
13+
<body>
14+
<h1>Convert Dwg to DXF</h1>
15+
<input type="file" id="fileInput" accept=".dwg" />
16+
<button id="download-dxf-btn" disabled>Download DXF</button>
17+
<p id="status"></p>
18+
<script type="module" src="dist/libredwg-web.js" defer></script>
19+
<script type="module">
20+
import { LibreDwg } from './dist/libredwg-web.js'
21+
22+
const libredwg = await LibreDwg.create()
23+
const fileInput = document.getElementById('fileInput')
24+
const downloadButton = document.getElementById('download-dxf-btn')
25+
const status = document.getElementById('status')
26+
27+
let dxfContent = null
28+
let outputFileName = 'drawing.dxf'
29+
30+
const resetResult = () => {
31+
dxfContent = null
32+
outputFileName = 'drawing.dxf'
33+
downloadButton.disabled = true
34+
}
35+
36+
const getOutputFileName = (inputFileName) => {
37+
const dotIndex = inputFileName.lastIndexOf('.')
38+
if (dotIndex <= 0) {
39+
return `${inputFileName}.dxf`
40+
}
41+
return `${inputFileName.slice(0, dotIndex)}.dxf`
42+
}
43+
44+
downloadButton.addEventListener('click', () => {
45+
if (!dxfContent) {
46+
alert('No DXF file to download!')
47+
return
48+
}
49+
50+
const blob = new Blob([dxfContent], { type: 'application/dxf' })
51+
const url = URL.createObjectURL(blob)
52+
const a = document.createElement('a')
53+
a.href = url
54+
a.download = outputFileName
55+
document.body.appendChild(a)
56+
a.click()
57+
document.body.removeChild(a)
58+
URL.revokeObjectURL(url)
59+
})
60+
61+
fileInput.addEventListener('change', function(event) {
62+
const file = event.target.files[0]
63+
resetResult()
64+
65+
if (!file) {
66+
status.textContent = 'No file selected'
67+
return
68+
}
69+
70+
status.textContent = 'Converting...'
71+
72+
const reader = new FileReader()
73+
reader.onload = function(e) {
74+
const fileContent = e.target.result
75+
try {
76+
const converted = libredwg.dwg_write_dxf(fileContent)
77+
if (!converted) {
78+
status.textContent = 'Failed to convert DWG to DXF.'
79+
return
80+
}
81+
82+
dxfContent = converted
83+
outputFileName = getOutputFileName(file.name)
84+
downloadButton.disabled = false
85+
status.textContent = `Converted successfully: ${outputFileName} (${converted.length} bytes)`
86+
} catch (error) {
87+
status.textContent = 'Failed to convert DWG to DXF.'
88+
console.error('Failed to convert dwg file to dxf: ', error)
89+
}
90+
}
91+
92+
reader.readAsArrayBuffer(file)
93+
})
94+
</script>
95+
</body>
96+
</html>

bindings/javascript/examples/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ <h1>Examples</h1>
1212
<li><a href="2_raw_wasm_usage.html" target="_blank">Raw Liredwg-Web WebAssembly API Usage</a></li>
1313
<li><a href="3_convert_to_svg.html" target="_blank">Convert Dwg to SVG</a></li>
1414
<li><a href="4_extract_dwg_thumbnail.html" target="_blank">Extract Thumbnail Image from Dwg file</a></li>
15+
<li><a href="5_convert_to_dxf.html" target="_blank">Convert Dwg to DXF</a></li>
1516
</ol>
1617
</body>
1718
</html>

bindings/javascript/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"description": "A DWG/DXF JavaScript parser based on libredwg",
44
"license": "GPL-3.0",
55
"private": false,
6-
"version": "0.6.10",
6+
"version": "0.7.0",
77
"author": "MLight Lee <mlight.lee@outlook.com>",
88
"type": "module",
99
"repository": {
@@ -28,7 +28,7 @@
2828
"demo": "live-server examples --entry-file=index.html",
2929
"doc": "typedoc",
3030
"build": "tsc && vite build && cp -rf wasm examples/ && cp -rf dist examples/",
31-
"build:prepare": "mkdir -p ../../build-wasm && cd ../../build-wasm && emconfigure ../configure CFLAGS=\"-O2 -sUSE_ZLIB=1\" CC=emcc --enable-release --disable-docs --disable-write --disable-python --disable-bindings --disable-shared --disable-json --disable-dxf --enable-partial --enable-experimental",
31+
"build:prepare": "mkdir -p ../../build-wasm && cd ../../build-wasm && emconfigure ../configure CFLAGS=\"-O2 -sUSE_ZLIB=1\" CC=emcc --enable-release --disable-docs --disable-write --disable-python --disable-bindings --disable-shared --disable-json --enable-partial --enable-experimental",
3232
"build:obj": "cd ../../build-wasm && emmake make",
3333
"build:wasm": "cd ../../build-wasm && emcc ../bindings/javascript/embind/*.cpp src/*.o -O2 -lembind -std=c++17 -Isrc -I../src -I../include -o libredwg-web.js -s ALLOW_MEMORY_GROWTH=1 -s EXPORT_ES6=1 -s MODULARIZE=1 -s EXPORT_NAME=\"createModule\" -sEXPORTED_RUNTIME_METHODS=FS,ENV,ccall,cwrap,UTF8ToString,stringToNewUTF8,setValue --emit-tsd libredwg-web.d.ts",
3434
"clean": "rm -rf ../../build-wasm ./docs ./dist ./lib",

bindings/javascript/src/libredwg.ts

Lines changed: 52 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -89,20 +89,25 @@ export class LibreDwg {
8989
})
9090
}
9191

92-
dwg_read_data(fileContent: string | ArrayBuffer, fileType: number) {
93-
if (fileType == Dwg_File_Type.DWG) {
94-
const fileName = 'tmp.dwg'
95-
this.wasmInstance.FS.writeFile(
96-
fileName,
97-
new Uint8Array(fileContent as ArrayBuffer)
98-
)
99-
const result = this.wasmInstance.dwg_read_file(fileName)
100-
if (result.error != 0) {
101-
console.log('Open dwg file with error code: ', result.error)
102-
}
103-
this.wasmInstance.FS.unlink(fileName)
104-
return result.data as Dwg_Data_Ptr
105-
}
92+
dwg_read_data(fileContent: string | ArrayBuffer, fileType: number) {
93+
if (fileType == Dwg_File_Type.DWG) {
94+
const fileName = 'tmp.dwg'
95+
try {
96+
this.wasmInstance.FS.writeFile(
97+
fileName,
98+
new Uint8Array(fileContent as ArrayBuffer)
99+
)
100+
const result = this.wasmInstance.dwg_read_file(fileName)
101+
if (result.error != 0) {
102+
console.log('Open dwg file with error code: ', result.error)
103+
}
104+
return result.data as Dwg_Data_Ptr
105+
} finally {
106+
if (this.wasmInstance.FS.analyzePath(fileName, false).exists) {
107+
this.wasmInstance.FS.unlink(fileName)
108+
}
109+
}
110+
}
106111
// else if (fileType == Dwg_File_Type.DXF) {
107112
// const fileName = "tmp.dxf";
108113
// this.wasmInstance.FS.writeFile(fileName, new Uint8Array(fileContent as ArrayBuffer));
@@ -115,6 +120,39 @@ export class LibreDwg {
115120
// }
116121
}
117122

123+
/**
124+
* Converts DWG file content to DXF file content.
125+
* @param fileContent DWG file content.
126+
* @returns Returns DXF file content if conversion succeeds. Otherwise returns null.
127+
*/
128+
dwg_write_dxf(fileContent: string | ArrayBuffer): Uint8Array | null {
129+
const inputFileName = 'tmp.dwg'
130+
const outputFileName = 'tmp.dxf'
131+
132+
try {
133+
this.wasmInstance.FS.writeFile(
134+
inputFileName,
135+
new Uint8Array(fileContent as ArrayBuffer)
136+
)
137+
const error = this.wasmInstance.dwg_write_dxf(
138+
inputFileName,
139+
outputFileName
140+
)
141+
if (error != 0) {
142+
console.log('Convert dwg to dxf with error code: ', error)
143+
return null
144+
}
145+
return this.wasmInstance.FS.readFile(outputFileName) as Uint8Array
146+
} finally {
147+
if (this.wasmInstance.FS.analyzePath(inputFileName, false).exists) {
148+
this.wasmInstance.FS.unlink(inputFileName)
149+
}
150+
if (this.wasmInstance.FS.analyzePath(outputFileName, false).exists) {
151+
this.wasmInstance.FS.unlink(outputFileName)
152+
}
153+
}
154+
}
155+
118156
/**
119157
* Gets the version of the dwg.
120158
* @param data Pointer to Dwg_Data instance.

bindings/javascript/wasm/libredwg-web.d.ts

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ declare namespace RuntimeExports {
1010
export let currentPath: string;
1111
export let initialized: boolean;
1212
export let ignorePermissions: boolean;
13+
export { ErrnoError };
1314
export let filesystems: any;
1415
export let syncFSRequests: number;
15-
export { ErrnoError };
16+
export let readFiles: {};
1617
export { FSStream };
1718
export { FSNode };
1819
export function lookupPath(path: any, opts?: {}): {
@@ -113,10 +114,11 @@ declare namespace RuntimeExports {
113114
export function llseek(stream: any, offset: any, whence: any): any;
114115
export function read(stream: any, buffer: any, offset: any, length: any, position: any): any;
115116
export function write(stream: any, buffer: any, offset: any, length: any, position: any, canOwn: any): any;
117+
export function allocate(stream: any, offset: any, length: any): void;
116118
export function mmap(stream: any, length: any, position: any, prot: any, flags: any): any;
117119
export function msync(stream: any, buffer: any, offset: any, length: any, mmapFlags: any): any;
118120
export function ioctl(stream: any, cmd: any, arg: any): any;
119-
export function readFile(path: any, opts?: {}): Uint8Array<any>;
121+
export function readFile(path: any, opts?: {}): any;
120122
export function writeFile(path: any, data: any, opts?: {}): void;
121123
export function cwd(): any;
122124
export function chdir(path: any): void;
@@ -150,10 +152,10 @@ declare namespace RuntimeExports {
150152
/**
151153
* @param {string|null=} returnType
152154
* @param {Array=} argTypes
153-
* @param {Array=} args
155+
* @param {Arguments|Array=} args
154156
* @param {Object=} opts
155157
*/
156-
function ccall(ident: any, returnType?: (string | null) | undefined, argTypes?: any[] | undefined, args?: any[] | undefined, opts?: any | undefined): any;
158+
function ccall(ident: any, returnType?: (string | null) | undefined, argTypes?: any[] | undefined, args?: (Arguments | any[]) | undefined, opts?: any | undefined): any;
157159
/**
158160
* @param {string=} returnType
159161
* @param {Array=} argTypes
@@ -169,18 +171,31 @@ declare namespace RuntimeExports {
169171
* maximum number of bytes to read. You can omit this parameter to scan the
170172
* string until the first 0 byte. If maxBytesToRead is passed, and the string
171173
* at [ptr, ptr+maxBytesToReadr[ contains a null byte in the middle, then the
172-
* string will cut short at that byte index.
173-
* @param {boolean=} ignoreNul - If true, the function will not stop on a NUL character.
174+
* string will cut short at that byte index (i.e. maxBytesToRead will not
175+
* produce a string of exact length [ptr, ptr+maxBytesToRead[) N.B. mixing
176+
* frequent uses of UTF8ToString() with and without maxBytesToRead may throw
177+
* JS JIT optimizations off, so it is worth to consider consistently using one
174178
* @return {string}
175179
*/
176-
function UTF8ToString(ptr: number, maxBytesToRead?: number | undefined, ignoreNul?: boolean | undefined): string;
180+
function UTF8ToString(ptr: number, maxBytesToRead?: number | undefined): string;
177181
function stringToNewUTF8(str: any): any;
178182
/**
179183
* @param {number} ptr
180184
* @param {number} value
181185
* @param {string} type
182186
*/
183187
function setValue(ptr: number, value: number, type?: string): void;
188+
let HEAPF32: any;
189+
let HEAPF64: any;
190+
let HEAP_DATA_VIEW: any;
191+
let HEAP8: any;
192+
let HEAPU8: any;
193+
let HEAP16: any;
194+
let HEAPU16: any;
195+
let HEAP32: any;
196+
let HEAPU32: any;
197+
let HEAP64: any;
198+
let HEAPU64: any;
184199
}
185200
declare class ErrnoError {
186201
constructor(errno: any);
@@ -232,8 +247,6 @@ export interface ClassHandle {
232247
delete(): void;
233248
deleteLater(): this;
234249
isDeleted(): boolean;
235-
// @ts-ignore - If targeting lower than ESNext, this symbol might not exist.
236-
[Symbol.dispose](): void;
237250
clone(): this;
238251
}
239252
export interface Dwg_Version_TypeValue<T extends number> {
@@ -426,6 +439,7 @@ interface EmbindModule {
426439
dwg_dynapi_entity_set_value(_0: number, _1: EmbindString, _2: EmbindString, _3: number, _4: boolean): boolean;
427440
dwg_dynapi_common_set_value(_0: number, _1: EmbindString, _2: number, _3: boolean): boolean;
428441
dwg_dynapi_handle_name(_0: number, _1: number, _2: number): string;
442+
dwg_write_dxf(_0: EmbindString, _1: EmbindString): number;
429443
dwg_find_tablehandle(_0: number, _1: EmbindString, _2: EmbindString): number;
430444
dwg_find_tablehandle_index(_0: number, _1: number, _2: EmbindString): number;
431445
dwg_handle_name(_0: number, _1: EmbindString, _2: number): string;

bindings/javascript/wasm/libredwg-web.js

Lines changed: 15 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
1.99 MB
Binary file not shown.

0 commit comments

Comments
 (0)