Compare commits
No commits in common. "main" and "modernise" have entirely different histories.
12
.gitignore
vendored
12
.gitignore
vendored
@ -75,12 +75,22 @@ typings/
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and *not* Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
@ -97,4 +107,4 @@ typings/
|
||||
gerber/
|
||||
hello.txt
|
||||
test/tmp/*
|
||||
test/arduino/*
|
||||
test/archiveTest
|
196
dist/index.js
vendored
196
dist/index.js
vendored
@ -1,196 +0,0 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ImageGenerator = void 0;
|
||||
//Modules
|
||||
const adm_zip_1 = __importDefault(require("adm-zip"));
|
||||
const fs_extra_1 = require("fs-extra");
|
||||
const path_1 = __importDefault(require("path"));
|
||||
const pcb_stackup_1 = __importDefault(require("pcb-stackup"));
|
||||
const sharp_1 = __importDefault(require("sharp"));
|
||||
const node_stream_1 = require("node:stream");
|
||||
const node_fs_1 = require("node:fs");
|
||||
//Class definition
|
||||
class ImageGenerator {
|
||||
constructor(folderConfig, imgConfig, layerNames) {
|
||||
this.folderConfig = folderConfig;
|
||||
this.imgConfig = imgConfig;
|
||||
this.layerNames = layerNames;
|
||||
//Ensure folders exist
|
||||
if (!validFolder(folderConfig.tmpDir, true))
|
||||
throw new Error('Temp directory is invalid');
|
||||
if (!validFolder(folderConfig.imgDir, true))
|
||||
throw new Error('Image directory is invalid');
|
||||
}
|
||||
//Layer promise
|
||||
getLayers(dir, layerNames) {
|
||||
//Check correct number of layers and folder exists
|
||||
layerNames.forEach((layerName) => {
|
||||
if (!(0, node_fs_1.existsSync)(path_1.default.join(dir, layerName))) {
|
||||
throw `Missing layer: ${layerName}`;
|
||||
}
|
||||
});
|
||||
if (!(0, node_fs_1.existsSync)(dir)) {
|
||||
throw new Error('Folder not there');
|
||||
}
|
||||
//Return layer promise
|
||||
const layersPromise = new Promise(function (resolve, reject) {
|
||||
const layers = layerNames.map((layerName) => ({
|
||||
filename: layerName,
|
||||
gerber: (0, node_fs_1.createReadStream)(path_1.default.join(dir, layerName)),
|
||||
}));
|
||||
if (layers.length === layerNames.length) {
|
||||
resolve(layers);
|
||||
}
|
||||
else {
|
||||
reject('Invalid layer count');
|
||||
}
|
||||
});
|
||||
return layersPromise;
|
||||
}
|
||||
//Clean up the archive folder in the specified directory
|
||||
static cleanupFiles(dir) {
|
||||
try {
|
||||
const folder = path_1.default.join(dir, 'archive');
|
||||
(0, fs_extra_1.emptyDirSync)(folder);
|
||||
}
|
||||
catch (error) {
|
||||
if (error instanceof Error) {
|
||||
console.error(error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
// * 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 {
|
||||
(0, fs_extra_1.ensureDirSync)(this.folderConfig.imgDir);
|
||||
}
|
||||
catch (error) {
|
||||
if (error instanceof Error) {
|
||||
console.error(error.message);
|
||||
}
|
||||
}
|
||||
// Check temp and output dirs exist
|
||||
if (!(0, node_fs_1.existsSync)(gerber)) {
|
||||
throw Error('Archive does not exist.');
|
||||
}
|
||||
if (!(0, node_fs_1.existsSync)(this.folderConfig.tmpDir)) {
|
||||
throw Error('Temporary folder does not exist.');
|
||||
}
|
||||
if (!(0, node_fs_1.existsSync)(this.folderConfig.imgDir)) {
|
||||
throw Error('Output folder does not exist.');
|
||||
}
|
||||
// Set filenames
|
||||
//Use the filename of the gerber zip to determine the output png filename
|
||||
const imageName = path_1.default.basename(gerber, '.zip');
|
||||
const destFile = `${path_1.default.join(this.folderConfig.imgDir, imageName)}.png`;
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this.layerNames) {
|
||||
throw new Error('You must supply an array of layer names.');
|
||||
}
|
||||
//Extract the passed in zip file
|
||||
extractArchive(gerber, this.folderConfig.tmpDir);
|
||||
this.getLayers(path_1.default.join(this.folderConfig.tmpDir, 'archive'), this.layerNames)
|
||||
.then(pcb_stackup_1.default)
|
||||
.then((stackup) => {
|
||||
(0, sharp_1.default)(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.folderConfig.tmpDir);
|
||||
resolve(destFile);
|
||||
})
|
||||
.catch((e) => {
|
||||
ImageGenerator.cleanupFiles(this.folderConfig.tmpDir);
|
||||
reject(new Error(e));
|
||||
});
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Take an archive containing gerber files and return a stream containing
|
||||
* a PNG image from the gerber */
|
||||
gerberToStream(gerber) {
|
||||
// Check temp and output dirs exist
|
||||
if (!(0, node_fs_1.existsSync)(gerber)) {
|
||||
throw Error('Archive does not exist.');
|
||||
}
|
||||
if (!(0, node_fs_1.existsSync)(this.folderConfig.tmpDir)) {
|
||||
throw Error('Temporary folder does not exist.');
|
||||
}
|
||||
if (!(0, node_fs_1.existsSync)(this.folderConfig.imgDir)) {
|
||||
throw Error('Output folder does not exist.');
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
extractArchive(gerber, this.folderConfig.tmpDir);
|
||||
if (!this.layerNames)
|
||||
throw new Error('No layers provided');
|
||||
this.getLayers(path_1.default.join(this.folderConfig.tmpDir, 'archive'), this.layerNames)
|
||||
.then(pcb_stackup_1.default)
|
||||
.then((stackup) => {
|
||||
(0, sharp_1.default)(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.folderConfig.tmpDir);
|
||||
const stream = new node_stream_1.Readable();
|
||||
stream.push(buffer);
|
||||
stream.push(null);
|
||||
resolve(stream);
|
||||
});
|
||||
})
|
||||
.catch((e) => {
|
||||
ImageGenerator.cleanupFiles(this.folderConfig.tmpDir);
|
||||
reject(new Error(e));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.ImageGenerator = ImageGenerator;
|
||||
//File methods
|
||||
//Check that a folder exists and is writeable
|
||||
function validFolder(dir, checkPerms) {
|
||||
if (!(0, node_fs_1.existsSync)(dir)) {
|
||||
throw Error('Folder does not exist.');
|
||||
}
|
||||
//Check folder permissions, will throw error if not readable or writeable
|
||||
if (checkPerms) {
|
||||
(0, node_fs_1.accessSync)(dir, node_fs_1.constants.R_OK | node_fs_1.constants.W_OK);
|
||||
(0, node_fs_1.accessSync)(dir, node_fs_1.constants.R_OK | node_fs_1.constants.W_OK);
|
||||
}
|
||||
//All checks passed
|
||||
return true;
|
||||
}
|
||||
function extractArchive(fileName, outputDir) {
|
||||
//Check archive exists
|
||||
if (!(0, node_fs_1.existsSync)(fileName)) {
|
||||
throw Error('Archive does not exist.');
|
||||
}
|
||||
//Check output dir is valid
|
||||
if (!validFolder(outputDir, true))
|
||||
throw new Error('Output directory is not valid');
|
||||
//Attempt to extract archive
|
||||
const zip = new adm_zip_1.default(fileName);
|
||||
try {
|
||||
zip.extractAllTo(path_1.default.join(outputDir, 'archive'));
|
||||
}
|
||||
catch (error) {
|
||||
if (error instanceof Error) {
|
||||
console.error(error.message);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return zip.getEntries().length;
|
||||
}
|
@ -1,18 +1,17 @@
|
||||
// @ts-check
|
||||
import js from '@eslint/js';
|
||||
import globals from 'globals';
|
||||
import { defineConfig, globalIgnores } from 'eslint/config';
|
||||
|
||||
import eslint from '@eslint/js';
|
||||
import tseslint from 'typescript-eslint';
|
||||
|
||||
export default tseslint.config(
|
||||
eslint.configs.recommended,
|
||||
tseslint.configs.recommended,
|
||||
export default defineConfig([
|
||||
globalIgnores(['test/**/*', 'node_modules/**/*']),
|
||||
{
|
||||
ignores: [
|
||||
'dist/**',
|
||||
'test/**',
|
||||
'node_modules/**',
|
||||
'webpack.dev.js',
|
||||
'webpack.prod.js',
|
||||
],
|
||||
files: ['**/*.{js,mjs,cjs}'],
|
||||
plugins: { js },
|
||||
extends: ['js/recommended'],
|
||||
},
|
||||
);
|
||||
{ files: ['**/*.js'], languageOptions: { sourceType: 'commonjs' } },
|
||||
{
|
||||
files: ['**/*.{js,mjs,cjs}'],
|
||||
languageOptions: { globals: globals.browser },
|
||||
},
|
||||
]);
|
||||
|
256
index.js
Normal file
256
index.js
Normal file
@ -0,0 +1,256 @@
|
||||
//Modules
|
||||
const AdmZip = require('adm-zip');
|
||||
const { emptyDirSync } = require('fs-extra');
|
||||
const path = require('path');
|
||||
const pcbStackup = require('pcb-stackup');
|
||||
const sharp = require('sharp');
|
||||
const { Readable } = require('node:stream');
|
||||
const { Buffer } = require('node:buffer');
|
||||
const {
|
||||
existsSync,
|
||||
accessSync,
|
||||
createReadStream,
|
||||
mkdirSync,
|
||||
chmodSync,
|
||||
constants,
|
||||
} = require('node:fs');
|
||||
|
||||
//ensureDirSync method
|
||||
function ensureDirSync(directory) {
|
||||
if (!existsSync(directory)) {
|
||||
mkdirSync(directory, { recursive: true });
|
||||
chmodSync(directory, 0o644);
|
||||
}
|
||||
}
|
||||
|
||||
//Class definition
|
||||
class ImageGenerator {
|
||||
constructor(folderConfig, imgConfig, layerNames) {
|
||||
this.tmpDir = folderConfig.tmpDir;
|
||||
this.imgDir = folderConfig.imgDir;
|
||||
this.imgConfig = imgConfig;
|
||||
this.layerNames = layerNames;
|
||||
|
||||
//Ensure folders exist
|
||||
try {
|
||||
if (!existsSync(this.tmpDir)) throw 'Temp dir does not exist';
|
||||
if (!existsSync(this.imgDir)) throw 'Image dir does not exist';
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
|
||||
//Check folder permissions
|
||||
try {
|
||||
accessSync(this.tmpDir, constants.R_OK | constants.W_OK);
|
||||
accessSync(this.imgDir, constants.R_OK | constants.W_OK);
|
||||
} 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 {number} Number of objects contained in the archive
|
||||
*/
|
||||
static extractArchive(fileName, tmpDir) {
|
||||
// Check archive exists
|
||||
try {
|
||||
if (!existsSync(fileName)) {
|
||||
throw Error('Archive does not exist.');
|
||||
}
|
||||
if (!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'));
|
||||
|
||||
return zip.getEntries().length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporary test method zip file
|
||||
* @param {string} fileName Name of the file to be extracted
|
||||
* @param {string} tmpDir Temporary directory to extract to
|
||||
* @returns {number} Number of objects contained in the archive
|
||||
*/
|
||||
static testArchive(fileName, tmpDir) {
|
||||
// Check archive exists
|
||||
try {
|
||||
if (!existsSync(fileName)) {
|
||||
throw Error('Archive does not exist.');
|
||||
}
|
||||
if (!existsSync(tmpDir)) {
|
||||
throw Error('Temporary folder does not exist.');
|
||||
}
|
||||
} catch (e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
try {
|
||||
const zip = new AdmZip(fileName);
|
||||
return zip.getEntries().length;
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 (!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 (!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: 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');
|
||||
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);
|
||||
ensureDirSync(this.imgDir);
|
||||
} catch (e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
|
||||
// Check temp and output dirs exist
|
||||
try {
|
||||
if (!existsSync(gerber)) {
|
||||
throw Error('Archive does not exist.');
|
||||
}
|
||||
if (!existsSync(this.tmpDir)) {
|
||||
throw Error('Temporary folder does not exist.');
|
||||
}
|
||||
if (!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 (!existsSync(gerber)) {
|
||||
throw Error('Archive does not exist.');
|
||||
}
|
||||
if (!existsSync(this.tmpDir)) {
|
||||
throw Error('Temporary folder does not exist.');
|
||||
}
|
||||
if (!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,
|
||||
};
|
1093
package-lock.json
generated
1093
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
45
package.json
45
package.json
@ -1,13 +1,10 @@
|
||||
{
|
||||
"name": "@nplayfair/npe_gerber",
|
||||
"version": "1.0.2",
|
||||
"version": "0.2.0",
|
||||
"description": "Create a PCB image from gerber files",
|
||||
"main": "dist/index.js",
|
||||
"types": "types/npe_gerber.d.ts",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "tsc --build",
|
||||
"test": "NODE_ENV=test PORT=7788 jest",
|
||||
"test:watch": "npm run test -- --watchAll",
|
||||
"test": "jest",
|
||||
"lint": "eslint ."
|
||||
},
|
||||
"repository": {
|
||||
@ -25,48 +22,18 @@
|
||||
"image",
|
||||
"gerber"
|
||||
],
|
||||
"jest": {
|
||||
"verbose": true,
|
||||
"modulePathIgnorePatterns": [
|
||||
"<rootDir>/node_modules"
|
||||
],
|
||||
"roots": [
|
||||
"<rootDir>/test"
|
||||
],
|
||||
"transform": {
|
||||
"^.+\\.tsx?$": "ts-jest"
|
||||
},
|
||||
"testEnvironment": "node",
|
||||
"testPathIgnorePatterns": [
|
||||
"/node_modules"
|
||||
],
|
||||
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$",
|
||||
"moduleFileExtensions": [
|
||||
"ts",
|
||||
"tsx",
|
||||
"js",
|
||||
"jsx",
|
||||
"json",
|
||||
"node"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"adm-zip": "^0.5.16",
|
||||
"fs-extra": "^11.3.0",
|
||||
"jszip": "^3.10.1",
|
||||
"pcb-stackup": "^4.2.8",
|
||||
"sharp": "^0.34.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.29.0",
|
||||
"@types/adm-zip": "^0.5.7",
|
||||
"@types/fs-extra": "^11.0.4",
|
||||
"@types/jest": "^29.5.14",
|
||||
"eslint": "^9.29.0",
|
||||
"globals": "^16.2.0",
|
||||
"jest": "^30.0.0",
|
||||
"prettier": "^3.5.3",
|
||||
"ts-jest": "^29.4.0",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint": "^8.34.0"
|
||||
"prettier": "^3.5.3"
|
||||
}
|
||||
}
|
||||
|
219
src/index.ts
219
src/index.ts
@ -1,219 +0,0 @@
|
||||
//Modules
|
||||
import AdmZip from 'adm-zip';
|
||||
import { emptyDirSync, ensureDirSync } from 'fs-extra';
|
||||
import path from 'path';
|
||||
import pcbStackup from 'pcb-stackup';
|
||||
import sharp from 'sharp';
|
||||
import { Readable } from 'node:stream';
|
||||
import { existsSync, createReadStream, accessSync, constants } from 'node:fs';
|
||||
|
||||
//Class definition
|
||||
export class ImageGenerator {
|
||||
constructor(
|
||||
public folderConfig: FolderConfig,
|
||||
public imgConfig: ImageConfig,
|
||||
public layerNames: string[],
|
||||
) {
|
||||
//Ensure folders exist
|
||||
if (!this.validFolder(folderConfig.tmpDir, true))
|
||||
throw new Error('Temp directory is invalid');
|
||||
|
||||
if (!this.validFolder(folderConfig.imgDir, true))
|
||||
throw new Error('Image directory is invalid');
|
||||
}
|
||||
|
||||
//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
|
||||
public gerberToImage(gerber: string) {
|
||||
//Check gerber archive exists
|
||||
if (!existsSync(gerber)) {
|
||||
throw Error('Archive does not exist.');
|
||||
}
|
||||
|
||||
// Create output dir if it doesn't exist
|
||||
try {
|
||||
ensureDirSync(this.folderConfig.imgDir);
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Set filenames
|
||||
//Use the filename of the gerber zip to determine the output png filename
|
||||
const imageName = path.basename(gerber, '.zip');
|
||||
const destFile = `${path.join(this.folderConfig.imgDir, imageName)}.png`;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this.layerNames) {
|
||||
reject('You must supply an array of layer names.');
|
||||
}
|
||||
//Extract the passed in zip file
|
||||
this.extractArchive(gerber, this.folderConfig.tmpDir);
|
||||
//Check all layers present
|
||||
this.layerNames.forEach((layerName) => {
|
||||
if (
|
||||
!existsSync(path.join(this.folderConfig.tmpDir, 'archive', layerName))
|
||||
) {
|
||||
this.cleanupFiles(this.folderConfig.tmpDir);
|
||||
reject(`Missing layer: ${layerName}`);
|
||||
}
|
||||
});
|
||||
this.getLayers(
|
||||
path.join(this.folderConfig.tmpDir, 'archive'),
|
||||
this.layerNames,
|
||||
)
|
||||
.then(pcbStackup)
|
||||
.then((stackup) => {
|
||||
sharp(Buffer.from(stackup.top.svg as ArrayLike<number>), {
|
||||
density: this.imgConfig.density,
|
||||
})
|
||||
.resize({ width: this.imgConfig.resizeWidth })
|
||||
.png({ compressionLevel: this.imgConfig.compLevel })
|
||||
.toFile(destFile);
|
||||
})
|
||||
.then(() => {
|
||||
this.cleanupFiles(this.folderConfig.tmpDir);
|
||||
resolve(destFile);
|
||||
})
|
||||
.catch((e) => {
|
||||
this.cleanupFiles(this.folderConfig.tmpDir);
|
||||
reject(new Error(e));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Take an archive containing gerber files and return a stream containing
|
||||
* a PNG image from the gerber */
|
||||
|
||||
gerberToStream(gerber: string) {
|
||||
// Check gerber archive exists
|
||||
if (!existsSync(gerber)) {
|
||||
throw Error('Archive does not exist.');
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.extractArchive(gerber, this.folderConfig.tmpDir);
|
||||
if (!this.layerNames) throw new Error('No layers provided');
|
||||
//Check all layers present
|
||||
this.layerNames.forEach((layerName) => {
|
||||
if (
|
||||
!existsSync(path.join(this.folderConfig.tmpDir, 'archive', layerName))
|
||||
) {
|
||||
this.cleanupFiles(this.folderConfig.tmpDir);
|
||||
reject(`Missing layer: ${layerName}`);
|
||||
}
|
||||
});
|
||||
this.getLayers(
|
||||
path.join(this.folderConfig.tmpDir, 'archive'),
|
||||
this.layerNames,
|
||||
)
|
||||
.then(pcbStackup)
|
||||
.then((stackup) => {
|
||||
sharp(Buffer.from(stackup.top.svg as ArrayLike<number>), {
|
||||
density: this.imgConfig.density,
|
||||
})
|
||||
.resize({ width: this.imgConfig.resizeWidth })
|
||||
.png({ compressionLevel: this.imgConfig.compLevel })
|
||||
.toBuffer()
|
||||
.then((buffer) => {
|
||||
this.cleanupFiles(this.folderConfig.tmpDir);
|
||||
const stream = new Readable();
|
||||
stream.push(buffer);
|
||||
stream.push(null);
|
||||
resolve(stream);
|
||||
});
|
||||
})
|
||||
.catch((e) => {
|
||||
this.cleanupFiles(this.folderConfig.tmpDir);
|
||||
reject(new Error(e));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//Layer methods
|
||||
//Returns promise that resolves to array of Layers
|
||||
private getLayers(dir: string, layerNames: string[]): Promise<Layers[]> {
|
||||
//Check correct number of layers and folder exists
|
||||
// try {
|
||||
// layerNames.forEach((layerName) => {
|
||||
// if (!existsSync(path.join(dir, layerName))) {
|
||||
// this.cleanupFiles(dir);
|
||||
// throw new Error(`Missing layer: ${layerName}`);
|
||||
// }
|
||||
// });
|
||||
// } catch (error: unknown) {
|
||||
// if (error instanceof Error) {
|
||||
// {
|
||||
// console.error(error.message);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
//Return layer promise
|
||||
const layersPromise = new Promise<Layers[]>(function (resolve, reject) {
|
||||
const layers: Layers[] = layerNames.map((layerName: string) => ({
|
||||
filename: layerName,
|
||||
gerber: createReadStream(path.join(dir, layerName)),
|
||||
}));
|
||||
if (layers.length === layerNames.length) {
|
||||
resolve(layers);
|
||||
} else {
|
||||
reject(new Error('Invalid layer count'));
|
||||
}
|
||||
});
|
||||
return layersPromise;
|
||||
}
|
||||
|
||||
//File methods
|
||||
//Check that a folder exists and is writeable
|
||||
private validFolder(dir: string, checkPerms?: boolean): boolean {
|
||||
if (!existsSync(dir)) {
|
||||
throw Error('Folder does not exist.');
|
||||
}
|
||||
//Check folder permissions, will throw error if not readable or writeable
|
||||
if (checkPerms) {
|
||||
accessSync(dir, constants.R_OK | constants.W_OK);
|
||||
accessSync(dir, constants.R_OK | constants.W_OK);
|
||||
}
|
||||
|
||||
//All checks passed
|
||||
return true;
|
||||
}
|
||||
|
||||
private extractArchive(fileName: string, outputDir: string): number {
|
||||
//Check archive exists
|
||||
if (!existsSync(fileName)) {
|
||||
throw Error('Archive does not exist.');
|
||||
}
|
||||
|
||||
//Check output dir is valid
|
||||
if (!this.validFolder(outputDir, true))
|
||||
throw new Error('Output directory is not valid');
|
||||
|
||||
//Attempt to extract archive
|
||||
const zip = new AdmZip(fileName);
|
||||
try {
|
||||
zip.extractAllTo(path.join(outputDir, 'archive'));
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof Error) {
|
||||
console.error(error.message);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return zip.getEntries().length;
|
||||
}
|
||||
|
||||
//Clean up the archive folder in the specified directory
|
||||
private cleanupFiles(dir: string): void {
|
||||
try {
|
||||
const folder = path.join(dir, 'archive');
|
||||
emptyDirSync(folder);
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof Error) {
|
||||
console.error(error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
214
test/index.test.js
Normal file
214
test/index.test.js
Normal file
@ -0,0 +1,214 @@
|
||||
const path = require('path');
|
||||
const { readdirSync, ReadStream } = require('node:fs');
|
||||
const { Readable } = require('node:stream');
|
||||
const { ImageGenerator } = require('../index.js');
|
||||
require('../index.js');
|
||||
|
||||
const testGerber = path.join(__dirname, 'Arduino-Pro-Mini.zip');
|
||||
const incompleteGerber = path.join(__dirname, 'incomplete.zip');
|
||||
const testLayers = path.join(__dirname, 'layers');
|
||||
const emptyFolder = path.join(__dirname, 'layers', 'Empty');
|
||||
const archiveTestFolder = path.join(__dirname, 'archiveTest');
|
||||
const folderConfig = {
|
||||
tmpDir: path.join(__dirname, 'tmp'),
|
||||
imgDir: path.join(__dirname, 'tmp'),
|
||||
};
|
||||
const noTempConfig = {
|
||||
tmpDir: emptyFolder,
|
||||
imgDir: path.join(__dirname, 'tmp'),
|
||||
};
|
||||
const tmpNotExist = {
|
||||
tmpDir: path.join(__dirname, 'InvalidFolderName'),
|
||||
imgDir: path.join(__dirname, 'tmp'),
|
||||
};
|
||||
const imgNotExist = {
|
||||
tmpDir: path.join(__dirname, 'tmp'),
|
||||
imgDir: path.join(__dirname, 'InvalidFolderName'),
|
||||
};
|
||||
const tmpBadPerms = {
|
||||
tmpDir: path.join(__dirname, 'badPerms'),
|
||||
imgDir: path.join(__dirname, 'tmp'),
|
||||
};
|
||||
const imgBadPerms = {
|
||||
tmpDir: path.join(__dirname, 'tmp'),
|
||||
imgDir: path.join(__dirname, 'badPerms'),
|
||||
};
|
||||
const noImageConfig = {
|
||||
tmpDir: path.join(__dirname, 'tmp'),
|
||||
imgDir: emptyFolder,
|
||||
};
|
||||
const imgConfig = {
|
||||
resizeWidth: 600,
|
||||
density: 1000,
|
||||
compLevel: 1,
|
||||
};
|
||||
const layerNames = [
|
||||
'CAMOutputs/DrillFiles/drills.xln',
|
||||
'CAMOutputs/GerberFiles/copper_top.gbr',
|
||||
'CAMOutputs/GerberFiles/silkscreen_top.gbr',
|
||||
'CAMOutputs/GerberFiles/soldermask_top.gbr',
|
||||
'CAMOutputs/GerberFiles/solderpaste_top.gbr',
|
||||
'CAMOutputs/GerberFiles/profile.gbr',
|
||||
];
|
||||
|
||||
const fileProc = new ImageGenerator(folderConfig, imgConfig, layerNames);
|
||||
const fileProcNoTemp = new ImageGenerator(noTempConfig, imgConfig, layerNames);
|
||||
const fileProcNoImage = new ImageGenerator(
|
||||
noImageConfig,
|
||||
imgConfig,
|
||||
layerNames,
|
||||
);
|
||||
|
||||
/**************
|
||||
* Tests
|
||||
***************/
|
||||
|
||||
// Test constructor
|
||||
describe('Creating an ImageGenerator object', () => {
|
||||
const imgGen = new ImageGenerator(folderConfig, imgConfig);
|
||||
test('should create a valid object when passed the correct files and configuration', () => {
|
||||
expect(imgGen).toBeInstanceOf(ImageGenerator);
|
||||
});
|
||||
// Image processing configuration
|
||||
test('image width should be 600', () => {
|
||||
expect(imgGen.imgConfig.resizeWidth).toBe(600);
|
||||
});
|
||||
test('image density should be 1000', () => {
|
||||
expect(imgGen.imgConfig.density).toBe(1000);
|
||||
});
|
||||
test('image compression level should be 1', () => {
|
||||
expect(imgGen.imgConfig.compLevel).toBe(1);
|
||||
});
|
||||
test('folders should be the ones specified in the folder config parameter', () => {
|
||||
expect(imgGen.tmpDir).toBe(path.join(__dirname, 'tmp'));
|
||||
expect(imgGen.imgDir).toBe(path.join(__dirname, 'tmp'));
|
||||
});
|
||||
});
|
||||
|
||||
// Testing folder config
|
||||
describe('Passing in', () => {
|
||||
test('a non-existent tmp folder should throw error', () => {
|
||||
expect(() => {
|
||||
new ImageGenerator(tmpNotExist, imgConfig);
|
||||
}).toThrow();
|
||||
});
|
||||
test('a tmp folder with invalid permissions should throw error', () => {
|
||||
expect(() => {
|
||||
new ImageGenerator(tmpBadPerms, imgConfig);
|
||||
}).toThrow();
|
||||
});
|
||||
test('a non-existent img folder should throw error', () => {
|
||||
expect(() => {
|
||||
new ImageGenerator(imgNotExist, imgConfig);
|
||||
}).toThrow();
|
||||
});
|
||||
test('an img folder with invalid permissions should throw error', () => {
|
||||
expect(() => {
|
||||
new ImageGenerator(imgBadPerms, imgConfig);
|
||||
}).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
// Testing static methods
|
||||
//Layer methods
|
||||
describe('Getting layers', () => {
|
||||
test('should return a promise of array layers', () => {
|
||||
expect.assertions(1);
|
||||
return ImageGenerator.getLayers(testLayers, layerNames).then((data) => {
|
||||
expect(data).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
filename: expect.any(String),
|
||||
gerber: expect.any(ReadStream),
|
||||
}),
|
||||
]),
|
||||
);
|
||||
});
|
||||
});
|
||||
test('should reject promise with error if the layers folder is not valid', () => {
|
||||
expect.assertions(1);
|
||||
return expect(
|
||||
ImageGenerator.getLayers('./invalid_folder', layerNames),
|
||||
).rejects.toThrow(new Error('Layers folder does not exist.'));
|
||||
});
|
||||
test('should reject promise with error if there is not the correct number of layers', () => {
|
||||
expect.assertions(1);
|
||||
return expect(
|
||||
ImageGenerator.getLayers(emptyFolder, layerNames),
|
||||
).rejects.toThrow(new Error('Layer not found.'));
|
||||
});
|
||||
});
|
||||
|
||||
//Archive methods
|
||||
describe('When extracting an archive', () => {
|
||||
test('a non-existent archive should throw an error', () => {
|
||||
expect(() =>
|
||||
ImageGenerator.extractArchive('invalid.zip', folderConfig.tmpDir),
|
||||
).toThrow();
|
||||
});
|
||||
test('if the temp dir does not exist it should throw an error', () => {
|
||||
expect(() =>
|
||||
ImageGenerator.extractArchive(testGerber, './invalid_dir'),
|
||||
).toThrow(Error);
|
||||
});
|
||||
test('it should load the archive and return the number of files extracted', () => {
|
||||
expect(() => {
|
||||
ImageGenerator.testArchive(testGerber, archiveTestFolder);
|
||||
}).not.toThrow();
|
||||
expect(ImageGenerator.testArchive(testGerber, archiveTestFolder)).toEqual(
|
||||
12,
|
||||
);
|
||||
});
|
||||
test('it should extract archive and all files should be present', () => {
|
||||
expect(ImageGenerator.testArchive(testGerber, archiveTestFolder)).toEqual(
|
||||
12,
|
||||
);
|
||||
ImageGenerator.extractArchive(testGerber, archiveTestFolder);
|
||||
const dirents = readdirSync(archiveTestFolder, {
|
||||
recursive: true,
|
||||
withFileTypes: true,
|
||||
});
|
||||
const numOutputFiles = dirents.filter((dirent) => dirent.isFile());
|
||||
expect(numOutputFiles).toHaveLength(12);
|
||||
});
|
||||
});
|
||||
|
||||
//Gerber methods
|
||||
describe('Converting a gerber to an image', () => {
|
||||
test('temp dir not existing should throw an error', () => {
|
||||
expect(() =>
|
||||
fileProcNoTemp
|
||||
.gerberToImage(testGerber)
|
||||
.toThrow(new Error('Temporary folder does not exist.')),
|
||||
);
|
||||
});
|
||||
test('output dir not existing should throw an error', () => {
|
||||
expect(() =>
|
||||
fileProcNoImage
|
||||
.gerberToImage(testGerber)
|
||||
.toThrow(new Error('Output folder does not exist.')),
|
||||
);
|
||||
});
|
||||
test('invalid archive file should throw an error', () => {
|
||||
expect(() =>
|
||||
fileProc
|
||||
.gerberToImage('invalid.zip')
|
||||
.toThrow(new Error('Archive does not exist.')),
|
||||
);
|
||||
});
|
||||
test('an archive with incomplete set of layers should throw an error', () => {
|
||||
expect(() => fileProc.gerberToImage(incompleteGerber).toThrow(Error));
|
||||
});
|
||||
test('gerber archive should resolve promise and return a filename of an image', () => {
|
||||
expect.assertions(1);
|
||||
return expect(fileProc.gerberToImage(testGerber)).resolves.toEqual(
|
||||
expect.stringContaining('Arduino-Pro-Mini.png'),
|
||||
);
|
||||
});
|
||||
test('Gerber archive should resolve promise and return a png stream', () => {
|
||||
expect.assertions(1);
|
||||
return expect(fileProc.gerberToStream(testGerber)).resolves.toBeInstanceOf(
|
||||
Readable,
|
||||
);
|
||||
});
|
||||
});
|
@ -1,154 +0,0 @@
|
||||
import path from 'path';
|
||||
import { readdirSync } from 'node:fs';
|
||||
import { emptyDirSync } from 'fs-extra';
|
||||
import { Readable } from 'node:stream';
|
||||
import { ImageGenerator } from '../src/index';
|
||||
|
||||
//Sample data
|
||||
const arduinoGerber = path.join(__dirname, 'Arduino-Pro-Mini.zip');
|
||||
const incompleteGerber = path.join(__dirname, 'incomplete.zip');
|
||||
|
||||
//Correct folder configuration
|
||||
const folderConfig = {
|
||||
tmpDir: path.join(__dirname, 'tmp'),
|
||||
imgDir: path.join(__dirname, 'tmp'),
|
||||
};
|
||||
//Folder configuration with non-existent tmpDir
|
||||
const tmpNotExist = {
|
||||
tmpDir: path.join(__dirname, 'InvalidFolderName'),
|
||||
imgDir: path.join(__dirname, 'tmp'),
|
||||
};
|
||||
//Folder configuration with non-existent imgDir
|
||||
const imgNotExist = {
|
||||
tmpDir: path.join(__dirname, 'tmp'),
|
||||
imgDir: path.join(__dirname, 'InvalidFolderName'),
|
||||
};
|
||||
//Folder configuration with bad permissions on tmpDir
|
||||
const tmpBadPerms = {
|
||||
tmpDir: path.join(__dirname, 'badPerms'),
|
||||
imgDir: path.join(__dirname, 'tmp'),
|
||||
};
|
||||
//Folder configuration with bad permissions on imgDir
|
||||
const imgBadPerms = {
|
||||
tmpDir: path.join(__dirname, 'tmp'),
|
||||
imgDir: path.join(__dirname, 'badPerms'),
|
||||
};
|
||||
//Correct folder configuration
|
||||
const arduinoConfig = {
|
||||
tmpDir: path.join(__dirname, 'tmp'),
|
||||
imgDir: path.join(__dirname, 'arduino'),
|
||||
};
|
||||
|
||||
//Valid image configuration object
|
||||
const imgConfig = {
|
||||
resizeWidth: 600,
|
||||
density: 1000,
|
||||
compLevel: 1,
|
||||
};
|
||||
|
||||
//Valid array of layer names
|
||||
const layerNames = [
|
||||
'CAMOutputs/DrillFiles/drills.xln',
|
||||
'CAMOutputs/GerberFiles/copper_top.gbr',
|
||||
'CAMOutputs/GerberFiles/silkscreen_top.gbr',
|
||||
'CAMOutputs/GerberFiles/soldermask_top.gbr',
|
||||
'CAMOutputs/GerberFiles/solderpaste_top.gbr',
|
||||
'CAMOutputs/GerberFiles/profile.gbr',
|
||||
];
|
||||
|
||||
//===== Tests =====
|
||||
|
||||
//Setup
|
||||
beforeAll(() => {
|
||||
return emptyDirSync(folderConfig.tmpDir);
|
||||
});
|
||||
|
||||
// Test constructor
|
||||
describe('Creating an ImageGenerator object', () => {
|
||||
const imgGen = new ImageGenerator(folderConfig, imgConfig, layerNames);
|
||||
test('should create a valid object when passed the correct files and configuration', () => {
|
||||
expect(imgGen).toBeInstanceOf(ImageGenerator);
|
||||
});
|
||||
// Image processing configuration
|
||||
test('image width should be 600', () => {
|
||||
expect(imgGen.imgConfig.resizeWidth).toBe(600);
|
||||
});
|
||||
test('image density should be 1000', () => {
|
||||
expect(imgGen.imgConfig.density).toBe(1000);
|
||||
});
|
||||
test('image compression level should be 1', () => {
|
||||
expect(imgGen.imgConfig.compLevel).toBe(1);
|
||||
});
|
||||
test('folders should be the ones specified in the folder config parameter', () => {
|
||||
expect(imgGen.folderConfig.tmpDir).toBe(path.join(__dirname, 'tmp'));
|
||||
expect(imgGen.folderConfig.imgDir).toBe(path.join(__dirname, 'tmp'));
|
||||
});
|
||||
test('Layers should match layerNames sample array', () => {
|
||||
expect(imgGen.layerNames).toBe(layerNames);
|
||||
});
|
||||
afterAll(() => {
|
||||
return emptyDirSync(folderConfig.tmpDir);
|
||||
});
|
||||
});
|
||||
|
||||
//Test invalid folder configs
|
||||
describe('Attempting to create ImageGenerator object with', () => {
|
||||
afterAll(() => {
|
||||
return emptyDirSync(path.join(__dirname, 'tmp'));
|
||||
});
|
||||
test('non-existent temp folder should throw error', () => {
|
||||
expect(() => {
|
||||
const badGen = new ImageGenerator(tmpNotExist, imgConfig, layerNames);
|
||||
}).toThrow();
|
||||
});
|
||||
test('non-existent image folder should throw error', () => {
|
||||
expect(() => {
|
||||
const badGen = new ImageGenerator(imgNotExist, imgConfig, layerNames);
|
||||
}).toThrow();
|
||||
});
|
||||
test('temp folder with bad permissions should throw error', () => {
|
||||
expect(() => {
|
||||
const badGen = new ImageGenerator(tmpBadPerms, imgConfig, layerNames);
|
||||
}).toThrow();
|
||||
});
|
||||
test('image folder with bad permissions should throw error', () => {
|
||||
expect(() => {
|
||||
const badGen = new ImageGenerator(imgBadPerms, imgConfig, layerNames);
|
||||
}).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
//Create image from Arduino Gerber
|
||||
describe('Create image from Arduino gerber', () => {
|
||||
beforeAll(() => {
|
||||
return emptyDirSync(path.join(__dirname, 'arduino'));
|
||||
});
|
||||
beforeEach(() => {
|
||||
return emptyDirSync(folderConfig.tmpDir);
|
||||
});
|
||||
const arduinoGen = new ImageGenerator(arduinoConfig, imgConfig, layerNames);
|
||||
test('should create a valid object when passed the correct files and configuration', () => {
|
||||
expect(arduinoGen).toBeInstanceOf(ImageGenerator);
|
||||
});
|
||||
test('invalid archive file should throw an error', () => {
|
||||
expect(() => arduinoGen.gerberToImage('invalid.zip')).toThrow();
|
||||
});
|
||||
test('arduino archive should resolve promise and return a filename of an image', () => {
|
||||
expect.assertions(1);
|
||||
return expect(arduinoGen.gerberToImage(arduinoGerber)).resolves.toEqual(
|
||||
expect.stringContaining('Arduino-Pro-Mini.png'),
|
||||
);
|
||||
});
|
||||
test('arduino archive should resolve promise and return a png stream', () => {
|
||||
expect.assertions(1);
|
||||
return expect(
|
||||
arduinoGen.gerberToStream(arduinoGerber),
|
||||
).resolves.toBeInstanceOf(Readable);
|
||||
});
|
||||
test('incomplete archive file should throw an error', () => {
|
||||
expect.assertions(1);
|
||||
return expect(arduinoGen.gerberToImage(incompleteGerber)).rejects.toContain(
|
||||
'Missing',
|
||||
);
|
||||
});
|
||||
});
|
77
test/layers/CAMOutputs/DrillFiles/drills.xln
Normal file
77
test/layers/CAMOutputs/DrillFiles/drills.xln
Normal file
@ -0,0 +1,77 @@
|
||||
M48
|
||||
;GenerationSoftware,Autodesk,EAGLE,9.6.2*%
|
||||
;CreationDate,2021-02-01T16:14:31Z*%
|
||||
FMAT,2
|
||||
ICI,OFF
|
||||
METRIC,TZ,000.000
|
||||
T2C0.508
|
||||
T1C1.016
|
||||
%
|
||||
G90
|
||||
M71
|
||||
T1
|
||||
X16510Y24130
|
||||
X13589Y7493
|
||||
X2540Y31750
|
||||
X5080Y31750
|
||||
X7620Y31750
|
||||
X10160Y31750
|
||||
X12700Y31750
|
||||
X15240Y31750
|
||||
X13589Y20447
|
||||
X13589Y17907
|
||||
X1270Y29210
|
||||
X1270Y26670
|
||||
X1270Y24130
|
||||
X1270Y21590
|
||||
X1270Y19050
|
||||
X1270Y16510
|
||||
X1270Y13970
|
||||
X1270Y11430
|
||||
X1270Y8890
|
||||
X1270Y6350
|
||||
X1270Y3810
|
||||
X1270Y1270
|
||||
X16510Y1270
|
||||
X16510Y3810
|
||||
X16510Y6350
|
||||
X16510Y8890
|
||||
X16510Y11430
|
||||
X16510Y13970
|
||||
X16510Y16510
|
||||
X16510Y29210
|
||||
X16510Y26670
|
||||
X13589Y10033
|
||||
X16510Y21590
|
||||
X16510Y19050
|
||||
T2
|
||||
X12192Y13970
|
||||
X2794Y2159
|
||||
X8890Y15621
|
||||
X9525Y16891
|
||||
X9322Y19812
|
||||
X15367Y15240
|
||||
X14046Y27280
|
||||
X11176Y26162
|
||||
X6350Y26416
|
||||
X6629Y22200
|
||||
X7366Y11557
|
||||
X8001Y12700
|
||||
X11303Y14986
|
||||
X8890Y4572
|
||||
X5969Y3048
|
||||
X10414Y11557
|
||||
X11811Y5842
|
||||
X8153Y29261
|
||||
X6401Y24130
|
||||
X11405Y24130
|
||||
X3683Y26035
|
||||
X7722Y16942
|
||||
X14757Y25400
|
||||
X6350Y14478
|
||||
X10414Y20447
|
||||
X5105Y29286
|
||||
X8890Y2667
|
||||
X9652Y14478
|
||||
X11430Y12700
|
||||
M30
|
0
test/layers/CAMOutputs/Empty/dummy.file
Normal file
0
test/layers/CAMOutputs/Empty/dummy.file
Normal file
44
test/layers/CAMOutputs/GerberFiles/Other.gbr
Normal file
44
test/layers/CAMOutputs/GerberFiles/Other.gbr
Normal file
@ -0,0 +1,44 @@
|
||||
G04 EAGLE Gerber RS-274X export*
|
||||
G75*
|
||||
%MOMM*%
|
||||
%FSLAX34Y34*%
|
||||
%LPD*%
|
||||
%INVias*%
|
||||
%IPPOS*%
|
||||
%AMOC8*
|
||||
5,1,8,0,0,1.08239X$1,22.5*%
|
||||
G01*
|
||||
%ADD10C,1.016000*%
|
||||
|
||||
|
||||
D10*
|
||||
X114300Y127000D03*
|
||||
X96520Y144780D03*
|
||||
X88900Y26670D03*
|
||||
X51054Y292862D03*
|
||||
X104140Y204470D03*
|
||||
X63500Y144780D03*
|
||||
X147574Y254000D03*
|
||||
X77216Y169418D03*
|
||||
X36830Y260350D03*
|
||||
X114046Y241300D03*
|
||||
X64008Y241300D03*
|
||||
X81534Y292608D03*
|
||||
X118110Y58420D03*
|
||||
X104140Y115570D03*
|
||||
X59690Y30480D03*
|
||||
X88900Y45720D03*
|
||||
X113030Y149860D03*
|
||||
X80010Y127000D03*
|
||||
X73660Y115570D03*
|
||||
X66294Y221996D03*
|
||||
X63500Y264160D03*
|
||||
X111760Y261620D03*
|
||||
X140462Y272796D03*
|
||||
X153670Y152400D03*
|
||||
X93218Y198120D03*
|
||||
X95250Y168910D03*
|
||||
X88900Y156210D03*
|
||||
X27940Y21590D03*
|
||||
X121920Y139700D03*
|
||||
M02*
|
3538
test/layers/CAMOutputs/GerberFiles/copper_bottom.gbr
Normal file
3538
test/layers/CAMOutputs/GerberFiles/copper_bottom.gbr
Normal file
File diff suppressed because it is too large
Load Diff
5635
test/layers/CAMOutputs/GerberFiles/copper_top.gbr
Normal file
5635
test/layers/CAMOutputs/GerberFiles/copper_top.gbr
Normal file
File diff suppressed because it is too large
Load Diff
24
test/layers/CAMOutputs/GerberFiles/gerber_job.gbrjob
Normal file
24
test/layers/CAMOutputs/GerberFiles/gerber_job.gbrjob
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"Header": {
|
||||
"Comment": "All values are metric (mm)",
|
||||
"CreationDate": "2021-02-01T16:14:31Z",
|
||||
"GenerationSoftware": {
|
||||
"Application": "EAGLE",
|
||||
"Vendor": "Autodesk",
|
||||
"Version": "9.6.2"
|
||||
},
|
||||
"Part": "Single"
|
||||
},
|
||||
"Overall": {
|
||||
"BoardThickness": 1.57,
|
||||
"LayerNumber": 2,
|
||||
"Name": {
|
||||
"ProjectId": "Arduino-Pro-Mini"
|
||||
},
|
||||
"Owner": "nick nick <nick@chaosemerald.co.uk>",
|
||||
"Size": {
|
||||
"X": 17.78,
|
||||
"Y": 33.02
|
||||
}
|
||||
}
|
||||
}
|
20
test/layers/CAMOutputs/GerberFiles/profile.gbr
Normal file
20
test/layers/CAMOutputs/GerberFiles/profile.gbr
Normal file
@ -0,0 +1,20 @@
|
||||
G04 EAGLE Gerber RS-274X export*
|
||||
G75*
|
||||
%MOMM*%
|
||||
%FSLAX34Y34*%
|
||||
%LPD*%
|
||||
%IN*%
|
||||
%IPPOS*%
|
||||
%AMOC8*
|
||||
5,1,8,0,0,1.08239X$1,22.5*%
|
||||
G01*
|
||||
%ADD10C,0.254000*%
|
||||
|
||||
|
||||
D10*
|
||||
X0Y0D02*
|
||||
X177800Y0D01*
|
||||
X177800Y330200D01*
|
||||
X0Y330200D01*
|
||||
X0Y0D01*
|
||||
M02*
|
3945
test/layers/CAMOutputs/GerberFiles/silkscreen_bottom.gbr
Normal file
3945
test/layers/CAMOutputs/GerberFiles/silkscreen_bottom.gbr
Normal file
File diff suppressed because it is too large
Load Diff
3517
test/layers/CAMOutputs/GerberFiles/silkscreen_top.gbr
Normal file
3517
test/layers/CAMOutputs/GerberFiles/silkscreen_top.gbr
Normal file
File diff suppressed because it is too large
Load Diff
56
test/layers/CAMOutputs/GerberFiles/soldermask_bottom.gbr
Normal file
56
test/layers/CAMOutputs/GerberFiles/soldermask_bottom.gbr
Normal file
@ -0,0 +1,56 @@
|
||||
G04 EAGLE Gerber RS-274X export*
|
||||
G75*
|
||||
%MOMM*%
|
||||
%FSLAX34Y34*%
|
||||
%LPD*%
|
||||
%INSoldermask Bottom*%
|
||||
%IPPOS*%
|
||||
%AMOC8*
|
||||
5,1,8,0,0,1.08239X$1,22.5*%
|
||||
G01*
|
||||
%ADD10C,2.082800*%
|
||||
%ADD11R,1.203200X1.303200*%
|
||||
|
||||
|
||||
D10*
|
||||
X165100Y292100D03*
|
||||
X165100Y266700D03*
|
||||
X165100Y241300D03*
|
||||
X165100Y215900D03*
|
||||
X165100Y190500D03*
|
||||
X165100Y165100D03*
|
||||
X165100Y139700D03*
|
||||
X165100Y114300D03*
|
||||
X165100Y88900D03*
|
||||
X165100Y63500D03*
|
||||
X165100Y38100D03*
|
||||
X165100Y12700D03*
|
||||
X12700Y12700D03*
|
||||
X12700Y38100D03*
|
||||
X12700Y63500D03*
|
||||
X12700Y88900D03*
|
||||
X12700Y114300D03*
|
||||
X12700Y139700D03*
|
||||
X12700Y165100D03*
|
||||
X12700Y190500D03*
|
||||
X12700Y215900D03*
|
||||
X12700Y241300D03*
|
||||
X12700Y266700D03*
|
||||
X12700Y292100D03*
|
||||
X135890Y179070D03*
|
||||
X135890Y204470D03*
|
||||
X152400Y317500D03*
|
||||
X127000Y317500D03*
|
||||
X101600Y317500D03*
|
||||
X76200Y317500D03*
|
||||
X50800Y317500D03*
|
||||
X25400Y317500D03*
|
||||
D11*
|
||||
X129794Y223148D03*
|
||||
X129794Y240148D03*
|
||||
X142494Y223148D03*
|
||||
X142494Y240148D03*
|
||||
D10*
|
||||
X135890Y74930D03*
|
||||
X135890Y100330D03*
|
||||
M02*
|
376
test/layers/CAMOutputs/GerberFiles/soldermask_top.gbr
Normal file
376
test/layers/CAMOutputs/GerberFiles/soldermask_top.gbr
Normal file
@ -0,0 +1,376 @@
|
||||
G04 EAGLE Gerber RS-274X export*
|
||||
G75*
|
||||
%MOMM*%
|
||||
%FSLAX34Y34*%
|
||||
%LPD*%
|
||||
%INSoldermask Top*%
|
||||
%IPPOS*%
|
||||
%AMOC8*
|
||||
5,1,8,0,0,1.08239X$1,22.5*%
|
||||
G01*
|
||||
%ADD10R,1.303200X1.203200*%
|
||||
%ADD11R,1.803200X1.603200*%
|
||||
%ADD12C,0.505344*%
|
||||
%ADD13R,1.203200X1.303200*%
|
||||
%ADD14R,1.403200X0.753200*%
|
||||
%ADD15C,2.082800*%
|
||||
%ADD16R,1.473200X0.762000*%
|
||||
%ADD17R,0.762000X1.473200*%
|
||||
%ADD18C,0.838200*%
|
||||
%ADD19R,1.473200X0.838200*%
|
||||
%ADD20R,1.727200X0.965200*%
|
||||
%ADD21C,0.653200*%
|
||||
%ADD22R,1.534159X3.495041*%
|
||||
|
||||
|
||||
D10*
|
||||
X80382Y204216D03*
|
||||
X63382Y204216D03*
|
||||
D11*
|
||||
X46990Y274350D03*
|
||||
X46990Y246350D03*
|
||||
D10*
|
||||
X97400Y266700D03*
|
||||
X80400Y266700D03*
|
||||
D12*
|
||||
X94434Y222184D02*
|
||||
X94434Y215204D01*
|
||||
X94434Y222184D02*
|
||||
X101414Y222184D01*
|
||||
X101414Y215204D01*
|
||||
X94434Y215204D01*
|
||||
X94434Y220004D02*
|
||||
X101414Y220004D01*
|
||||
X76894Y222184D02*
|
||||
X76894Y215204D01*
|
||||
X76894Y222184D02*
|
||||
X83874Y222184D01*
|
||||
X83874Y215204D01*
|
||||
X76894Y215204D01*
|
||||
X76894Y220004D02*
|
||||
X83874Y220004D01*
|
||||
D13*
|
||||
X45466Y43824D03*
|
||||
X45466Y60824D03*
|
||||
D10*
|
||||
X98670Y72390D03*
|
||||
X81670Y72390D03*
|
||||
D13*
|
||||
X45466Y12074D03*
|
||||
X45466Y29074D03*
|
||||
X137160Y57522D03*
|
||||
X137160Y40522D03*
|
||||
D12*
|
||||
X133670Y11992D02*
|
||||
X140650Y11992D01*
|
||||
X140650Y5012D01*
|
||||
X133670Y5012D01*
|
||||
X133670Y11992D01*
|
||||
X133670Y9812D02*
|
||||
X140650Y9812D01*
|
||||
X140650Y29532D02*
|
||||
X133670Y29532D01*
|
||||
X140650Y29532D02*
|
||||
X140650Y22552D01*
|
||||
X133670Y22552D01*
|
||||
X133670Y29532D01*
|
||||
X133670Y27352D02*
|
||||
X140650Y27352D01*
|
||||
D14*
|
||||
X101901Y232308D03*
|
||||
X101901Y241808D03*
|
||||
X101901Y251308D03*
|
||||
X75899Y251308D03*
|
||||
X75899Y232308D03*
|
||||
D11*
|
||||
X132080Y227300D03*
|
||||
X132080Y255300D03*
|
||||
D15*
|
||||
X165100Y292100D03*
|
||||
X165100Y266700D03*
|
||||
X165100Y241300D03*
|
||||
X165100Y215900D03*
|
||||
X165100Y190500D03*
|
||||
X165100Y165100D03*
|
||||
X165100Y139700D03*
|
||||
X165100Y114300D03*
|
||||
X165100Y88900D03*
|
||||
X165100Y63500D03*
|
||||
X165100Y38100D03*
|
||||
X165100Y12700D03*
|
||||
X12700Y12700D03*
|
||||
X12700Y38100D03*
|
||||
X12700Y63500D03*
|
||||
X12700Y88900D03*
|
||||
X12700Y114300D03*
|
||||
X12700Y139700D03*
|
||||
X12700Y165100D03*
|
||||
X12700Y190500D03*
|
||||
X12700Y215900D03*
|
||||
X12700Y241300D03*
|
||||
X12700Y266700D03*
|
||||
X12700Y292100D03*
|
||||
X135890Y179070D03*
|
||||
X135890Y204470D03*
|
||||
D16*
|
||||
G36*
|
||||
X80860Y181950D02*
|
||||
X70444Y192366D01*
|
||||
X75832Y197754D01*
|
||||
X86248Y187338D01*
|
||||
X80860Y181950D01*
|
||||
G37*
|
||||
G36*
|
||||
X75203Y176293D02*
|
||||
X64787Y186709D01*
|
||||
X70175Y192097D01*
|
||||
X80591Y181681D01*
|
||||
X75203Y176293D01*
|
||||
G37*
|
||||
G36*
|
||||
X69546Y170637D02*
|
||||
X59130Y181053D01*
|
||||
X64518Y186441D01*
|
||||
X74934Y176025D01*
|
||||
X69546Y170637D01*
|
||||
G37*
|
||||
G36*
|
||||
X63889Y164980D02*
|
||||
X53473Y175396D01*
|
||||
X58861Y180784D01*
|
||||
X69277Y170368D01*
|
||||
X63889Y164980D01*
|
||||
G37*
|
||||
G36*
|
||||
X58232Y159323D02*
|
||||
X47816Y169739D01*
|
||||
X53204Y175127D01*
|
||||
X63620Y164711D01*
|
||||
X58232Y159323D01*
|
||||
G37*
|
||||
G36*
|
||||
X52575Y153666D02*
|
||||
X42159Y164082D01*
|
||||
X47547Y169470D01*
|
||||
X57963Y159054D01*
|
||||
X52575Y153666D01*
|
||||
G37*
|
||||
G36*
|
||||
X46919Y148009D02*
|
||||
X36503Y158425D01*
|
||||
X41891Y163813D01*
|
||||
X52307Y153397D01*
|
||||
X46919Y148009D01*
|
||||
G37*
|
||||
G36*
|
||||
X41262Y142352D02*
|
||||
X30846Y152768D01*
|
||||
X36234Y158156D01*
|
||||
X46650Y147740D01*
|
||||
X41262Y142352D01*
|
||||
G37*
|
||||
D17*
|
||||
G36*
|
||||
X36234Y121244D02*
|
||||
X30846Y126632D01*
|
||||
X41262Y137048D01*
|
||||
X46650Y131660D01*
|
||||
X36234Y121244D01*
|
||||
G37*
|
||||
G36*
|
||||
X41891Y115587D02*
|
||||
X36503Y120975D01*
|
||||
X46919Y131391D01*
|
||||
X52307Y126003D01*
|
||||
X41891Y115587D01*
|
||||
G37*
|
||||
G36*
|
||||
X47547Y109930D02*
|
||||
X42159Y115318D01*
|
||||
X52575Y125734D01*
|
||||
X57963Y120346D01*
|
||||
X47547Y109930D01*
|
||||
G37*
|
||||
G36*
|
||||
X53204Y104273D02*
|
||||
X47816Y109661D01*
|
||||
X58232Y120077D01*
|
||||
X63620Y114689D01*
|
||||
X53204Y104273D01*
|
||||
G37*
|
||||
G36*
|
||||
X58861Y98616D02*
|
||||
X53473Y104004D01*
|
||||
X63889Y114420D01*
|
||||
X69277Y109032D01*
|
||||
X58861Y98616D01*
|
||||
G37*
|
||||
G36*
|
||||
X64518Y92959D02*
|
||||
X59130Y98347D01*
|
||||
X69546Y108763D01*
|
||||
X74934Y103375D01*
|
||||
X64518Y92959D01*
|
||||
G37*
|
||||
G36*
|
||||
X70175Y87303D02*
|
||||
X64787Y92691D01*
|
||||
X75203Y103107D01*
|
||||
X80591Y97719D01*
|
||||
X70175Y87303D01*
|
||||
G37*
|
||||
G36*
|
||||
X75832Y81646D02*
|
||||
X70444Y87034D01*
|
||||
X80860Y97450D01*
|
||||
X86248Y92062D01*
|
||||
X75832Y81646D01*
|
||||
G37*
|
||||
D16*
|
||||
G36*
|
||||
X101968Y81646D02*
|
||||
X91552Y92062D01*
|
||||
X96940Y97450D01*
|
||||
X107356Y87034D01*
|
||||
X101968Y81646D01*
|
||||
G37*
|
||||
G36*
|
||||
X107625Y87303D02*
|
||||
X97209Y97719D01*
|
||||
X102597Y103107D01*
|
||||
X113013Y92691D01*
|
||||
X107625Y87303D01*
|
||||
G37*
|
||||
G36*
|
||||
X113282Y92959D02*
|
||||
X102866Y103375D01*
|
||||
X108254Y108763D01*
|
||||
X118670Y98347D01*
|
||||
X113282Y92959D01*
|
||||
G37*
|
||||
G36*
|
||||
X118939Y98616D02*
|
||||
X108523Y109032D01*
|
||||
X113911Y114420D01*
|
||||
X124327Y104004D01*
|
||||
X118939Y98616D01*
|
||||
G37*
|
||||
G36*
|
||||
X124596Y104273D02*
|
||||
X114180Y114689D01*
|
||||
X119568Y120077D01*
|
||||
X129984Y109661D01*
|
||||
X124596Y104273D01*
|
||||
G37*
|
||||
G36*
|
||||
X130253Y109930D02*
|
||||
X119837Y120346D01*
|
||||
X125225Y125734D01*
|
||||
X135641Y115318D01*
|
||||
X130253Y109930D01*
|
||||
G37*
|
||||
G36*
|
||||
X135909Y115587D02*
|
||||
X125493Y126003D01*
|
||||
X130881Y131391D01*
|
||||
X141297Y120975D01*
|
||||
X135909Y115587D01*
|
||||
G37*
|
||||
G36*
|
||||
X141566Y121244D02*
|
||||
X131150Y131660D01*
|
||||
X136538Y137048D01*
|
||||
X146954Y126632D01*
|
||||
X141566Y121244D01*
|
||||
G37*
|
||||
D17*
|
||||
G36*
|
||||
X136538Y142352D02*
|
||||
X131150Y147740D01*
|
||||
X141566Y158156D01*
|
||||
X146954Y152768D01*
|
||||
X136538Y142352D01*
|
||||
G37*
|
||||
G36*
|
||||
X130881Y148009D02*
|
||||
X125493Y153397D01*
|
||||
X135909Y163813D01*
|
||||
X141297Y158425D01*
|
||||
X130881Y148009D01*
|
||||
G37*
|
||||
G36*
|
||||
X125225Y153666D02*
|
||||
X119837Y159054D01*
|
||||
X130253Y169470D01*
|
||||
X135641Y164082D01*
|
||||
X125225Y153666D01*
|
||||
G37*
|
||||
G36*
|
||||
X119568Y159323D02*
|
||||
X114180Y164711D01*
|
||||
X124596Y175127D01*
|
||||
X129984Y169739D01*
|
||||
X119568Y159323D01*
|
||||
G37*
|
||||
G36*
|
||||
X113911Y164980D02*
|
||||
X108523Y170368D01*
|
||||
X118939Y180784D01*
|
||||
X124327Y175396D01*
|
||||
X113911Y164980D01*
|
||||
G37*
|
||||
G36*
|
||||
X108254Y170637D02*
|
||||
X102866Y176025D01*
|
||||
X113282Y186441D01*
|
||||
X118670Y181053D01*
|
||||
X108254Y170637D01*
|
||||
G37*
|
||||
G36*
|
||||
X102597Y176293D02*
|
||||
X97209Y181681D01*
|
||||
X107625Y192097D01*
|
||||
X113013Y186709D01*
|
||||
X102597Y176293D01*
|
||||
G37*
|
||||
G36*
|
||||
X96940Y181950D02*
|
||||
X91552Y187338D01*
|
||||
X101968Y197754D01*
|
||||
X107356Y192366D01*
|
||||
X96940Y181950D01*
|
||||
G37*
|
||||
D15*
|
||||
X152400Y317500D03*
|
||||
X127000Y317500D03*
|
||||
X101600Y317500D03*
|
||||
X76200Y317500D03*
|
||||
X50800Y317500D03*
|
||||
X25400Y317500D03*
|
||||
D10*
|
||||
X136516Y292100D03*
|
||||
X119516Y292100D03*
|
||||
D18*
|
||||
X39370Y292100D03*
|
||||
X124460Y25400D03*
|
||||
D19*
|
||||
X46990Y226369D03*
|
||||
X46990Y218131D03*
|
||||
D20*
|
||||
X118110Y7620D03*
|
||||
X62230Y7620D03*
|
||||
X118110Y45720D03*
|
||||
X62230Y45720D03*
|
||||
D15*
|
||||
X135890Y74930D03*
|
||||
X135890Y100330D03*
|
||||
D21*
|
||||
X49693Y178246D02*
|
||||
X35143Y178246D01*
|
||||
X35143Y190246D02*
|
||||
X49693Y190246D01*
|
||||
X49693Y202246D02*
|
||||
X35143Y202246D01*
|
||||
D22*
|
||||
X42443Y190221D03*
|
||||
M02*
|
19
test/layers/CAMOutputs/GerberFiles/solderpaste_bottom.gbr
Normal file
19
test/layers/CAMOutputs/GerberFiles/solderpaste_bottom.gbr
Normal file
@ -0,0 +1,19 @@
|
||||
G04 EAGLE Gerber RS-274X export*
|
||||
G75*
|
||||
%MOMM*%
|
||||
%FSLAX34Y34*%
|
||||
%LPD*%
|
||||
%INSolderpaste Bottom*%
|
||||
%IPPOS*%
|
||||
%AMOC8*
|
||||
5,1,8,0,0,1.08239X$1,22.5*%
|
||||
G01*
|
||||
%ADD10R,1.000000X1.100000*%
|
||||
|
||||
|
||||
D10*
|
||||
X129794Y223148D03*
|
||||
X129794Y240148D03*
|
||||
X142494Y223148D03*
|
||||
X142494Y240148D03*
|
||||
M02*
|
341
test/layers/CAMOutputs/GerberFiles/solderpaste_top.gbr
Normal file
341
test/layers/CAMOutputs/GerberFiles/solderpaste_top.gbr
Normal file
@ -0,0 +1,341 @@
|
||||
G04 EAGLE Gerber RS-274X export*
|
||||
G75*
|
||||
%MOMM*%
|
||||
%FSLAX34Y34*%
|
||||
%LPD*%
|
||||
%INSolderpaste Top*%
|
||||
%IPPOS*%
|
||||
%AMOC8*
|
||||
5,1,8,0,0,1.08239X$1,22.5*%
|
||||
G01*
|
||||
%ADD10R,1.100000X1.000000*%
|
||||
%ADD11R,1.600000X1.400000*%
|
||||
%ADD12C,0.300000*%
|
||||
%ADD13R,1.000000X1.100000*%
|
||||
%ADD14R,1.200000X0.550000*%
|
||||
%ADD15R,1.270000X0.558800*%
|
||||
%ADD16R,0.558800X1.270000*%
|
||||
%ADD17R,1.270000X0.635000*%
|
||||
%ADD18R,2.286000X2.438400*%
|
||||
%ADD19R,1.524000X0.762000*%
|
||||
%ADD20C,0.450000*%
|
||||
|
||||
|
||||
D10*
|
||||
X80382Y204216D03*
|
||||
X63382Y204216D03*
|
||||
D11*
|
||||
X46990Y274350D03*
|
||||
X46990Y246350D03*
|
||||
D10*
|
||||
X97400Y266700D03*
|
||||
X80400Y266700D03*
|
||||
D12*
|
||||
X94424Y222194D02*
|
||||
X94424Y215194D01*
|
||||
X94424Y222194D02*
|
||||
X101424Y222194D01*
|
||||
X101424Y215194D01*
|
||||
X94424Y215194D01*
|
||||
X94424Y218044D02*
|
||||
X101424Y218044D01*
|
||||
X101424Y220894D02*
|
||||
X94424Y220894D01*
|
||||
X76884Y222194D02*
|
||||
X76884Y215194D01*
|
||||
X76884Y222194D02*
|
||||
X83884Y222194D01*
|
||||
X83884Y215194D01*
|
||||
X76884Y215194D01*
|
||||
X76884Y218044D02*
|
||||
X83884Y218044D01*
|
||||
X83884Y220894D02*
|
||||
X76884Y220894D01*
|
||||
D13*
|
||||
X45466Y43824D03*
|
||||
X45466Y60824D03*
|
||||
D10*
|
||||
X98670Y72390D03*
|
||||
X81670Y72390D03*
|
||||
D13*
|
||||
X45466Y12074D03*
|
||||
X45466Y29074D03*
|
||||
X137160Y57522D03*
|
||||
X137160Y40522D03*
|
||||
D12*
|
||||
X133660Y12002D02*
|
||||
X140660Y12002D01*
|
||||
X140660Y5002D01*
|
||||
X133660Y5002D01*
|
||||
X133660Y12002D01*
|
||||
X133660Y7852D02*
|
||||
X140660Y7852D01*
|
||||
X140660Y10702D02*
|
||||
X133660Y10702D01*
|
||||
X133660Y29542D02*
|
||||
X140660Y29542D01*
|
||||
X140660Y22542D01*
|
||||
X133660Y22542D01*
|
||||
X133660Y29542D01*
|
||||
X133660Y25392D02*
|
||||
X140660Y25392D01*
|
||||
X140660Y28242D02*
|
||||
X133660Y28242D01*
|
||||
D14*
|
||||
X101901Y232308D03*
|
||||
X101901Y241808D03*
|
||||
X101901Y251308D03*
|
||||
X75899Y251308D03*
|
||||
X75899Y232308D03*
|
||||
D11*
|
||||
X132080Y227300D03*
|
||||
X132080Y255300D03*
|
||||
D15*
|
||||
G36*
|
||||
X80860Y183387D02*
|
||||
X71881Y192366D01*
|
||||
X75832Y196317D01*
|
||||
X84811Y187338D01*
|
||||
X80860Y183387D01*
|
||||
G37*
|
||||
G36*
|
||||
X75203Y177730D02*
|
||||
X66224Y186709D01*
|
||||
X70175Y190660D01*
|
||||
X79154Y181681D01*
|
||||
X75203Y177730D01*
|
||||
G37*
|
||||
G36*
|
||||
X69546Y172074D02*
|
||||
X60567Y181053D01*
|
||||
X64518Y185004D01*
|
||||
X73497Y176025D01*
|
||||
X69546Y172074D01*
|
||||
G37*
|
||||
G36*
|
||||
X63889Y166417D02*
|
||||
X54910Y175396D01*
|
||||
X58861Y179347D01*
|
||||
X67840Y170368D01*
|
||||
X63889Y166417D01*
|
||||
G37*
|
||||
G36*
|
||||
X58232Y160760D02*
|
||||
X49253Y169739D01*
|
||||
X53204Y173690D01*
|
||||
X62183Y164711D01*
|
||||
X58232Y160760D01*
|
||||
G37*
|
||||
G36*
|
||||
X52575Y155103D02*
|
||||
X43596Y164082D01*
|
||||
X47547Y168033D01*
|
||||
X56526Y159054D01*
|
||||
X52575Y155103D01*
|
||||
G37*
|
||||
G36*
|
||||
X46919Y149446D02*
|
||||
X37940Y158425D01*
|
||||
X41891Y162376D01*
|
||||
X50870Y153397D01*
|
||||
X46919Y149446D01*
|
||||
G37*
|
||||
G36*
|
||||
X41262Y143789D02*
|
||||
X32283Y152768D01*
|
||||
X36234Y156719D01*
|
||||
X45213Y147740D01*
|
||||
X41262Y143789D01*
|
||||
G37*
|
||||
D16*
|
||||
G36*
|
||||
X36234Y122681D02*
|
||||
X32283Y126632D01*
|
||||
X41262Y135611D01*
|
||||
X45213Y131660D01*
|
||||
X36234Y122681D01*
|
||||
G37*
|
||||
G36*
|
||||
X41891Y117024D02*
|
||||
X37940Y120975D01*
|
||||
X46919Y129954D01*
|
||||
X50870Y126003D01*
|
||||
X41891Y117024D01*
|
||||
G37*
|
||||
G36*
|
||||
X47547Y111367D02*
|
||||
X43596Y115318D01*
|
||||
X52575Y124297D01*
|
||||
X56526Y120346D01*
|
||||
X47547Y111367D01*
|
||||
G37*
|
||||
G36*
|
||||
X53204Y105710D02*
|
||||
X49253Y109661D01*
|
||||
X58232Y118640D01*
|
||||
X62183Y114689D01*
|
||||
X53204Y105710D01*
|
||||
G37*
|
||||
G36*
|
||||
X58861Y100053D02*
|
||||
X54910Y104004D01*
|
||||
X63889Y112983D01*
|
||||
X67840Y109032D01*
|
||||
X58861Y100053D01*
|
||||
G37*
|
||||
G36*
|
||||
X64518Y94396D02*
|
||||
X60567Y98347D01*
|
||||
X69546Y107326D01*
|
||||
X73497Y103375D01*
|
||||
X64518Y94396D01*
|
||||
G37*
|
||||
G36*
|
||||
X70175Y88740D02*
|
||||
X66224Y92691D01*
|
||||
X75203Y101670D01*
|
||||
X79154Y97719D01*
|
||||
X70175Y88740D01*
|
||||
G37*
|
||||
G36*
|
||||
X75832Y83083D02*
|
||||
X71881Y87034D01*
|
||||
X80860Y96013D01*
|
||||
X84811Y92062D01*
|
||||
X75832Y83083D01*
|
||||
G37*
|
||||
D15*
|
||||
G36*
|
||||
X101968Y83083D02*
|
||||
X92989Y92062D01*
|
||||
X96940Y96013D01*
|
||||
X105919Y87034D01*
|
||||
X101968Y83083D01*
|
||||
G37*
|
||||
G36*
|
||||
X107625Y88740D02*
|
||||
X98646Y97719D01*
|
||||
X102597Y101670D01*
|
||||
X111576Y92691D01*
|
||||
X107625Y88740D01*
|
||||
G37*
|
||||
G36*
|
||||
X113282Y94396D02*
|
||||
X104303Y103375D01*
|
||||
X108254Y107326D01*
|
||||
X117233Y98347D01*
|
||||
X113282Y94396D01*
|
||||
G37*
|
||||
G36*
|
||||
X118939Y100053D02*
|
||||
X109960Y109032D01*
|
||||
X113911Y112983D01*
|
||||
X122890Y104004D01*
|
||||
X118939Y100053D01*
|
||||
G37*
|
||||
G36*
|
||||
X124596Y105710D02*
|
||||
X115617Y114689D01*
|
||||
X119568Y118640D01*
|
||||
X128547Y109661D01*
|
||||
X124596Y105710D01*
|
||||
G37*
|
||||
G36*
|
||||
X130253Y111367D02*
|
||||
X121274Y120346D01*
|
||||
X125225Y124297D01*
|
||||
X134204Y115318D01*
|
||||
X130253Y111367D01*
|
||||
G37*
|
||||
G36*
|
||||
X135909Y117024D02*
|
||||
X126930Y126003D01*
|
||||
X130881Y129954D01*
|
||||
X139860Y120975D01*
|
||||
X135909Y117024D01*
|
||||
G37*
|
||||
G36*
|
||||
X141566Y122681D02*
|
||||
X132587Y131660D01*
|
||||
X136538Y135611D01*
|
||||
X145517Y126632D01*
|
||||
X141566Y122681D01*
|
||||
G37*
|
||||
D16*
|
||||
G36*
|
||||
X136538Y143789D02*
|
||||
X132587Y147740D01*
|
||||
X141566Y156719D01*
|
||||
X145517Y152768D01*
|
||||
X136538Y143789D01*
|
||||
G37*
|
||||
G36*
|
||||
X130881Y149446D02*
|
||||
X126930Y153397D01*
|
||||
X135909Y162376D01*
|
||||
X139860Y158425D01*
|
||||
X130881Y149446D01*
|
||||
G37*
|
||||
G36*
|
||||
X125225Y155103D02*
|
||||
X121274Y159054D01*
|
||||
X130253Y168033D01*
|
||||
X134204Y164082D01*
|
||||
X125225Y155103D01*
|
||||
G37*
|
||||
G36*
|
||||
X119568Y160760D02*
|
||||
X115617Y164711D01*
|
||||
X124596Y173690D01*
|
||||
X128547Y169739D01*
|
||||
X119568Y160760D01*
|
||||
G37*
|
||||
G36*
|
||||
X113911Y166417D02*
|
||||
X109960Y170368D01*
|
||||
X118939Y179347D01*
|
||||
X122890Y175396D01*
|
||||
X113911Y166417D01*
|
||||
G37*
|
||||
G36*
|
||||
X108254Y172074D02*
|
||||
X104303Y176025D01*
|
||||
X113282Y185004D01*
|
||||
X117233Y181053D01*
|
||||
X108254Y172074D01*
|
||||
G37*
|
||||
G36*
|
||||
X102597Y177730D02*
|
||||
X98646Y181681D01*
|
||||
X107625Y190660D01*
|
||||
X111576Y186709D01*
|
||||
X102597Y177730D01*
|
||||
G37*
|
||||
G36*
|
||||
X96940Y183387D02*
|
||||
X92989Y187338D01*
|
||||
X101968Y196317D01*
|
||||
X105919Y192366D01*
|
||||
X96940Y183387D01*
|
||||
G37*
|
||||
D10*
|
||||
X136516Y292100D03*
|
||||
X119516Y292100D03*
|
||||
D17*
|
||||
X46990Y226369D03*
|
||||
X46990Y218131D03*
|
||||
D18*
|
||||
X46990Y222250D03*
|
||||
D19*
|
||||
X118110Y7620D03*
|
||||
X62230Y7620D03*
|
||||
X118110Y45720D03*
|
||||
X62230Y45720D03*
|
||||
D20*
|
||||
X49693Y178246D02*
|
||||
X35143Y178246D01*
|
||||
X35143Y190246D02*
|
||||
X49693Y190246D01*
|
||||
X49693Y202246D02*
|
||||
X35143Y202246D01*
|
||||
M02*
|
115
tsconfig.json
115
tsconfig.json
@ -1,115 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig to read more about this file */
|
||||
|
||||
/* Projects */
|
||||
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
|
||||
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
|
||||
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
|
||||
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||
|
||||
/* Language and Environment */
|
||||
"target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
|
||||
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
||||
// "libReplacement": true, /* Enable lib replacement. */
|
||||
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
|
||||
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
||||
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
|
||||
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
|
||||
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
||||
|
||||
/* Modules */
|
||||
"module": "commonjs" /* Specify what module code is generated. */,
|
||||
// "rootDir": "./", /* Specify the root folder within your source files. */
|
||||
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
|
||||
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
|
||||
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
|
||||
// "rewriteRelativeImportExtensions": true, /* Rewrite '.ts', '.tsx', '.mts', and '.cts' file extensions in relative import paths to their JavaScript equivalent in output files. */
|
||||
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
|
||||
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
|
||||
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
|
||||
// "noUncheckedSideEffectImports": true, /* Check side effect imports. */
|
||||
// "resolveJsonModule": true, /* Enable importing .json files. */
|
||||
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
|
||||
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
||||
|
||||
/* JavaScript Support */
|
||||
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
|
||||
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
||||
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
|
||||
|
||||
/* Emit */
|
||||
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
||||
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
||||
"outDir": "./dist" /* Specify an output folder for all emitted files. */,
|
||||
// "removeComments": true, /* Disable emitting comments. */
|
||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
||||
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
||||
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
||||
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
|
||||
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
|
||||
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
||||
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
|
||||
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
||||
|
||||
/* Interop Constraints */
|
||||
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
||||
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
|
||||
// "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */
|
||||
// "erasableSyntaxOnly": true, /* Do not allow runtime constructs that are not part of ECMAScript. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
|
||||
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
|
||||
|
||||
/* Type Checking */
|
||||
"strict": true /* Enable all strict type-checking options. */,
|
||||
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
|
||||
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
|
||||
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
||||
// "strictBuiltinIteratorReturn": true, /* Built-in iterators are instantiated with a 'TReturn' type of 'undefined' instead of 'any'. */
|
||||
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
|
||||
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
|
||||
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
||||
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
|
||||
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
|
||||
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
||||
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
||||
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
|
||||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
||||
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
|
||||
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
||||
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
||||
|
||||
/* Completeness */
|
||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
},
|
||||
"include": ["src/**/*", "types/npe_gerber.d.ts"],
|
||||
"exclude": ["node_modules", "dist/**/*", "test/**/*"]
|
||||
}
|
23
types/npe_gerber.d.ts
vendored
23
types/npe_gerber.d.ts
vendored
@ -1,23 +0,0 @@
|
||||
interface ImageConfig {
|
||||
resizeWidth: number;
|
||||
density: number;
|
||||
compLevel: number;
|
||||
}
|
||||
|
||||
interface FolderConfig {
|
||||
tmpDir: string;
|
||||
imgDir: string;
|
||||
}
|
||||
|
||||
interface ZipExtractor {
|
||||
extractArchive(fileName: string, tmpDir: string): number;
|
||||
}
|
||||
|
||||
interface Layers {
|
||||
filename: string;
|
||||
gerber: ReadStream;
|
||||
}
|
||||
|
||||
interface LayerGenerator {
|
||||
getLayers(dir: string, layerNames: string[]): Promise<Layers[]>;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user