freecad-preview-extractor 0.1.1 → 0.1.2

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
@@ -48,7 +48,7 @@ fcxtc filename.FCStd
48
48
 
49
49
  ### Generate preview with FreeCAD before extraction (requires FreeCAD and desktop environment)
50
50
 
51
- Adding `--fit` argument will trigger `src/isofit.FCMacro` FreeCAD macros for each file,
51
+ Adding `--fit` argument will trigger `src/isometric-fit.FCMacro` FreeCAD macros for each file,
52
52
  the macros sets model to the isometric view, does "fit into view", and saves the file.
53
53
 
54
54
  ```bash
@@ -80,7 +80,7 @@ test-model.FCStd
80
80
 
81
81
  The `--fit` option will:
82
82
  1. Open each FreeCAD file with FreeCAD
83
- 2. Run the `isofit.FCMacro` macro to set Isometric View and Fit All
83
+ 2. Run the `isometric-fit.FCMacro` macro to set Isometric View and Fit All
84
84
  3. Save the file with the updated preview
85
85
  4. Extract the preview image as usual
86
86
 
@@ -152,7 +152,7 @@ Key files of the project (not including all files):
152
152
 
153
153
  - `src/index.js` - Main CLI entry point
154
154
  - `src/extract-png-from-fcstd.js` - Thumbnail extraction logic
155
- - `src/isofit.FCMacro` - FreeCAD macro for isometric view and fit (used by `--fit` option)
155
+ - `src/isometric-fit.FCMacro` - FreeCAD macro for isometric view and fit (used by `--fit` option)
156
156
  - `tests/` - Unit tests for ignore functionality
157
157
 
158
158
  ## License
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "freecad-preview-extractor",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Script extracting freecad previews",
5
5
  "keywords": [
6
6
  "FreeCAD"
@@ -87,9 +87,10 @@ export async function extractThumbnailFromFCStd(zipFilePath, outputPath) {
87
87
  /**
88
88
  * Process a single .FCStd file to extract its preview
89
89
  * @param {string} filePath - Path to the .FCStd file
90
+ * @param {string} [customOutputPath] - Optional custom path for the output PNG
90
91
  * @returns {Promise<boolean>} True if processing was successful
91
92
  */
92
- export async function processSingleFile(filePath) {
93
+ export async function processSingleFile(filePath, customOutputPath = null) {
93
94
  try {
94
95
  // Verify file exists
95
96
  if (!fs.existsSync(filePath)) {
@@ -107,7 +108,7 @@ export async function processSingleFile(filePath) {
107
108
  // Prepare output path - same directory, same name but with .png extension
108
109
  const dir = path.dirname(filePath);
109
110
  const baseName = path.basename(filePath, path.extname(filePath));
110
- const pngPath = path.join(dir, `${baseName}-preview.png`);
111
+ const pngPath = customOutputPath || path.join(dir, `${baseName}-preview.png`);
111
112
 
112
113
  // Try to extract thumbnail from the file
113
114
  await extractThumbnailFromFCStd(filePath, pngPath);
@@ -0,0 +1,47 @@
1
+ import path from "path";
2
+ import { fileURLToPath } from "url";
3
+ import { spawn } from "child_process";
4
+
5
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
+
7
+ // Run FreeCAD with isometric fit macro to generate preview
8
+ async function runFreeCADIsometricFit(fcstdFile) {
9
+ const macroPath = path.join(__dirname, "isometric-fit.FCMacro");
10
+
11
+ return new Promise((resolve, reject) => {
12
+ console.log(
13
+ `🔧 Running FreeCAD with Isometric, Fit All macro on ${fcstdFile}...`,
14
+ );
15
+
16
+ const freecad = spawn("freecad", [fcstdFile, macroPath]);
17
+
18
+ freecad.stdout.on("data", (data) => {
19
+ // Log FreeCAD output if needed for debugging
20
+ // console.log(`FreeCAD: ${data}`);
21
+ });
22
+
23
+ freecad.stderr.on("data", (data) => {
24
+ // Log errors but don't fail immediately
25
+ // console.error(`FreeCAD stderr: ${data}`);
26
+ });
27
+
28
+ freecad.on("close", (code) => {
29
+ if (code === 0) {
30
+ console.log(`✅ FreeCAD processing complete for ${fcstdFile}`);
31
+ resolve();
32
+ } else {
33
+ reject(new Error(`FreeCAD exited with code ${code}`));
34
+ }
35
+ });
36
+
37
+ freecad.on("error", (err) => {
38
+ reject(
39
+ new Error(
40
+ `Failed to start FreeCAD: ${err.message}. Make sure FreeCAD is installed and available in PATH.`,
41
+ ),
42
+ );
43
+ });
44
+ });
45
+ }
46
+
47
+ export { runFreeCADIsometricFit };
package/src/index.js CHANGED
@@ -8,7 +8,8 @@ import {
8
8
  extractThumbnailFromFCStd,
9
9
  } from "./extract-png-from-fcstd.js";
10
10
  import { loadIgnoreConfig, filterIgnoredFiles } from "./ignore-utils.js";
11
- import { readFileSync } from "fs";
11
+ import { runFreeCADIsometricFit } from "./fit-utils.js";
12
+ import fs from "fs";
12
13
 
13
14
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
14
15
 
@@ -17,46 +18,6 @@ function getRootDir(cwd = process.cwd()) {
17
18
  return cwd;
18
19
  }
19
20
 
20
- // Run FreeCAD with isofit macro to generate preview
21
- async function runFreeCADIsofit(fcstdFile) {
22
- const macroPath = path.join(__dirname, "isofit.FCMacro");
23
-
24
- return new Promise((resolve, reject) => {
25
- console.log(
26
- `🔧 Running FreeCAD with Isometric, Fit All macro on ${fcstdFile}...`,
27
- );
28
-
29
- const freecad = spawn("freecad", [fcstdFile, macroPath]);
30
-
31
- freecad.stdout.on("data", (data) => {
32
- // Log FreeCAD output if needed for debugging
33
- // console.log(`FreeCAD: ${data}`);
34
- });
35
-
36
- freecad.stderr.on("data", (data) => {
37
- // Log errors but don't fail immediately
38
- // console.error(`FreeCAD stderr: ${data}`);
39
- });
40
-
41
- freecad.on("close", (code) => {
42
- if (code === 0) {
43
- console.log(`✅ FreeCAD processing complete for ${fcstdFile}`);
44
- resolve();
45
- } else {
46
- reject(new Error(`FreeCAD exited with code ${code}`));
47
- }
48
- });
49
-
50
- freecad.on("error", (err) => {
51
- reject(
52
- new Error(
53
- `Failed to start FreeCAD: ${err.message}. Make sure FreeCAD is installed and available in PATH.`,
54
- ),
55
- );
56
- });
57
- });
58
- }
59
-
60
21
  // Process all .FCStd files in current directory and subdirectories
61
22
  async function processAllFiles(runFit = false, ignoreConfig = null) {
62
23
  try {
@@ -95,10 +56,21 @@ async function processAllFiles(runFit = false, ignoreConfig = null) {
95
56
  let failureCount = 0;
96
57
 
97
58
  for (const file of fcstdFiles) {
59
+ let fileToProcess = file;
60
+ let tempFile = null;
61
+
98
62
  try {
99
- // If --fit flag is set, run FreeCAD with isofit macro first
63
+ // If --fit flag is set, run FreeCAD with isometric-fit macro first
100
64
  if (runFit) {
101
- await runFreeCADIsofit(file);
65
+ const ext = path.extname(file);
66
+ const dir = path.dirname(file);
67
+ const baseName = path.basename(file, ext);
68
+ tempFile = path.join(dir, `${baseName}_temp_${Date.now()}${ext}`);
69
+
70
+ fs.copyFileSync(file, tempFile);
71
+ fileToProcess = tempFile;
72
+
73
+ await runFreeCADIsometricFit(fileToProcess);
102
74
  }
103
75
 
104
76
  // Prepare output path - same directory, same name but with .png extension
@@ -107,11 +79,20 @@ async function processAllFiles(runFit = false, ignoreConfig = null) {
107
79
  const pngPath = path.join(dir, `${baseName}-preview.png`);
108
80
 
109
81
  // Try to extract thumbnail from the file
110
- await extractThumbnailFromFCStd(file, pngPath);
82
+ await extractThumbnailFromFCStd(fileToProcess, pngPath);
111
83
  successCount++;
112
84
  } catch (err) {
113
85
  console.log(`❌ Error processing ${file}: ${err.message}`);
114
86
  failureCount++;
87
+ } finally {
88
+ // Clean up temp file
89
+ if (tempFile && fs.existsSync(tempFile)) {
90
+ try {
91
+ fs.unlinkSync(tempFile);
92
+ } catch (e) {
93
+ console.log(`⚠️ Could not delete temp file ${tempFile}: ${e.message}`);
94
+ }
95
+ }
115
96
  }
116
97
  processedCount++;
117
98
  console.log(`📊 Progress: ${processedCount}/${fcstdFiles.length} (${successCount} succeeded, ${failureCount} failed)`);
@@ -136,7 +117,7 @@ async function main() {
136
117
 
137
118
  // Check for --version or --help flags
138
119
  if (args.includes("--version") || args.includes("-v")) {
139
- const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url)));
120
+ const pkg = JSON.parse(fs.readFileSync(new URL("../package.json", import.meta.url)));
140
121
  console.log(pkg.version);
141
122
  process.exit(0);
142
123
  }
@@ -184,7 +165,7 @@ Examples:
184
165
  // No arguments - process all files
185
166
  if (runFit) {
186
167
  console.log(
187
- "🔍 Running FreeCAD isofit and extracting images from all FreeCAD files in current directory...",
168
+ "🔍 Running FreeCAD isometric-fit and extracting images from all FreeCAD files in current directory...",
188
169
  );
189
170
  } else {
190
171
  console.log(
@@ -201,19 +182,47 @@ Examples:
201
182
  } else {
202
183
  // Process specific file(s)
203
184
  const file = fileArgs[0];
185
+ let fileToProcess = file;
186
+ let tempFile = null;
187
+ let customOutputPath = null;
188
+
189
+ try {
190
+ // For single file mode, ignore config is not applied
191
+ if (runFit) {
192
+ const ext = path.extname(file);
193
+ const dir = path.dirname(file);
194
+ const baseName = path.basename(file, ext);
195
+ tempFile = path.join(dir, `${baseName}_temp_${Date.now()}${ext}`);
204
196
 
205
- // For single file mode, ignore config is not applied
206
- if (runFit) {
207
- console.log(`🔧 Running FreeCAD isofit on: ${file}`);
208
- await runFreeCADIsofit(file);
209
- }
197
+ fs.copyFileSync(file, tempFile);
198
+ fileToProcess = tempFile;
199
+
200
+ // Set output path based on original file
201
+ customOutputPath = path.join(dir, `${baseName}-preview.png`);
210
202
 
211
- console.log(`🔍 Extracting image from: ${file}`);
212
- await processSingleFile(file);
203
+ console.log(`🔧 Running FreeCAD isometric-fit on temp file: ${path.basename(tempFile)}`);
204
+ await runFreeCADIsometricFit(fileToProcess);
205
+ }
206
+
207
+ console.log(`🔍 Extracting image from: ${fileToProcess}`);
208
+ await processSingleFile(fileToProcess, customOutputPath);
209
+ } catch (err) {
210
+ console.error(`❌ Error processing ${file}:`, err);
211
+ process.exitCode = 1;
212
+ } finally {
213
+ // Clean up temp file
214
+ if (tempFile && fs.existsSync(tempFile)) {
215
+ try {
216
+ fs.unlinkSync(tempFile);
217
+ } catch (e) {
218
+ console.warn(`⚠️ Failed to delete temp file: ${e.message}`);
219
+ }
220
+ }
221
+ }
213
222
  }
214
223
  }
215
224
 
216
- export { processAllFiles, runFreeCADIsofit };
225
+ export { processAllFiles };
217
226
  export { loadIgnoreConfig, filterIgnoredFiles } from "./ignore-utils.js";
218
227
 
219
228
  // Run the script
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
- # Macro Begin: isofit.FCMacro +++++++++++++++++++++++++++++++++++++++++++++++++
3
+ # Macro Begin: isometric-fit.FCMacro +++++++++++++++++++++++++++++++++++++++++++++++++
4
4
  import FreeCAD
5
5
 
6
6
  try:
@@ -12,7 +12,7 @@ try:
12
12
  Gui.SendMsgToActiveView("Save")
13
13
  Gui.getMainWindow().close()
14
14
  except Exception as e:
15
- print("Error in isofit macro: {}".format(e))
15
+ print("Error in isometric-fit macro: {}".format(e))
16
16
  Gui.getMainWindow().close()
17
17
 
18
- # Macro End: isofit.FCMacro +++++++++++++++++++++++++++++++++++++++++++++++++
18
+ # Macro End: isometric-fit.FCMacro +++++++++++++++++++++++++++++++++++++++++++++++++