npe_gerber/index.js
Nick Playfair 7dd6e99ef5 pass layer names to getLayers
Update the getLayers method to take in an array of the layer filenames you expect to be in the gerber archive. This will allow the package to be used with gerbers that output different filenames for each layer then EAGLE.
2021-02-07 20:03:37 +00:00

201 lines
6.0 KiB
JavaScript

const AdmZip = require('adm-zip');
const fs = require('fs-extra');
const path = require('path');
const pcbStackup = require('pcb-stackup');
const sharp = require('sharp');
const { Readable } = require('stream');
class ImageGenerator {
constructor(folderConfig, imgConfig, layerNames) {
this.tmpDir = folderConfig.tmpDir;
this.imgDir = folderConfig.imgDir;
this.imgConfig = imgConfig;
this.layerNames = layerNames;
// Ensure that the folders exist
try {
fs.ensureDirSync(this.tmpDir);
fs.ensureDirSync(this.imgDir);
} catch (error) {
throw new Error(error);
}
}
/**
* Extracts the passed in zip file
* @param {string} fileName Name of the file to be extracted
* @param {string} tmpDir Temporary directory to extract to
* @returns {Promise} Promise object represents number of files extracted
*/
static extractArchive(fileName, tmpDir) {
// Check archive exists
try {
if (!fs.existsSync(fileName)) {
throw Error('Archive does not exist.');
}
if (!fs.existsSync(tmpDir)) {
throw Error('Temporary folder does not exist.');
}
} catch (e) {
throw new Error(e);
}
const zip = new AdmZip(fileName);
zip.extractAllTo(path.join(tmpDir, 'archive'));
}
/**
* Take in a directory of layer files and return an array of the layers files
* @param {string} dir Directory containing layer files
* @param {Array} layerNames Array of filenames for the desired layers
* @returns {Array} Array of paths to the layers files
*/
static getLayers(dir, layerNames) {
return new Promise((resolve, reject) => {
// Make sure the directory exists
if (!fs.existsSync(dir)) {
return reject(new Error('Layers folder does not exist.'));
}
// Check that the required layer files exist in source dir
let layersValid = true;
layerNames.forEach((layer) => {
if (!fs.existsSync(path.join(dir, layer))) layersValid = false;
});
if (!layersValid) return reject(new Error('Layer not found.'));
// Construct array of layers that match the supplied filenames array
const layers = layerNames.map((layerName) => ({
filename: layerName,
gerber: fs.createReadStream(path.join(dir, layerName)),
}));
return resolve(layers);
});
}
/**
* Clean up the archive folder in the specified directory
* @param {string} dir Path to a directory to clean up
*/
static cleanupFiles(dir) {
try {
const folder = path.join(dir, 'archive');
fs.emptyDirSync(folder);
} catch (err) {
throw new Error(err);
}
}
/**
* Take an archive containing gerber files, config object, temporary dir
* and output dir and create a PNG image from the gerber in the output dir
* @param {string} gerber Path to an archive file containing gerber
* @returns {Promise.<string>} Promise to return path to image
*/
gerberToImage(gerber) {
// Create output dir if it doesn't exist
try {
fs.ensureDirSync(this.imgDir, 0o644);
} catch (e) {
throw new Error(e);
}
// Check temp and output dirs exist
try {
if (!fs.existsSync(gerber)) {
throw Error('Archive does not exist.');
}
if (!fs.existsSync(this.tmpDir)) {
throw Error('Temporary folder does not exist.');
}
if (!fs.existsSync(this.imgDir)) {
throw Error('Output folder does not exist.');
}
} catch (e) {
throw new Error(e);
}
// Set filenames
const imageName = path.basename(gerber, '.zip');
const destFile = `${path.join(this.imgDir, imageName)}.png`;
return new Promise((resolve, reject) => {
ImageGenerator.extractArchive(gerber, this.tmpDir);
ImageGenerator.getLayers(
path.join(this.tmpDir, 'archive'),
this.layerNames
)
.then(pcbStackup)
.then((stackup) => {
sharp(Buffer.from(stackup.top.svg), {
density: this.imgConfig.density,
})
.resize({ width: this.imgConfig.resizeWidth })
.png({ compressionLevel: this.imgConfig.compLevel })
.toFile(destFile);
})
.then(() => {
ImageGenerator.cleanupFiles(this.tmpDir);
resolve(destFile);
})
.catch((e) => {
ImageGenerator.cleanupFiles(this.tmpDir);
reject(new Error(e));
});
});
}
/**
* Take an archive containing gerber files and return a stream containing
* a PNG image from the gerber
* @param {string} gerber Path to an archive file containing gerber
* @returns {Promise.<stream.Readable>} Promise that resolves to a PNG stream
*/
gerberToStream(gerber) {
// Check temp and output dirs exist
try {
if (!fs.existsSync(gerber)) {
throw Error('Archive does not exist.');
}
if (!fs.existsSync(this.tmpDir)) {
throw Error('Temporary folder does not exist.');
}
if (!fs.existsSync(this.imgDir)) {
throw Error('Output folder does not exist.');
}
} catch (e) {
throw new Error(e);
}
return new Promise((resolve, reject) => {
ImageGenerator.extractArchive(gerber, this.tmpDir);
ImageGenerator.getLayers(
path.join(this.tmpDir, 'archive'),
this.layerNames
)
.then(pcbStackup)
.then((stackup) => {
sharp(Buffer.from(stackup.top.svg), {
density: this.imgConfig.density,
})
.resize({ width: this.imgConfig.resizeWidth })
.png({ compressionLevel: this.imgConfig.compLevel })
.toBuffer()
.then((buffer) => {
ImageGenerator.cleanupFiles(this.tmpDir);
const stream = new Readable();
stream.push(buffer);
stream.push(null);
resolve(stream);
});
})
.catch((e) => {
ImageGenerator.cleanupFiles(this.tmpDir);
reject(new Error(e));
});
});
}
}
module.exports = {
ImageGenerator,
};