Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • tracespace
  • main
2 results

Target

Select target project
No results found
Select Git revision
  • main
1 result
Show changes

Commits on Source 29

21 files
+ 6453
1746
Compare changes
  • Side-by-side
  • Inline

Files

+2 −1
Original line number Original line Diff line number Diff line
@@ -6,12 +6,13 @@ yarn-debug.log*
yarn-error.log*
yarn-error.log*
pnpm-debug.log*
pnpm-debug.log*
lerna-debug.log*
lerna-debug.log*
dev


node_modules
node_modules
dist-ssr
dist-ssr
*.local
*.local


pckage-lock.json
package-lock.json


# Editor directories and files
# Editor directories and files
.vscode/*
.vscode/*
+2 −2
Original line number Original line Diff line number Diff line
# gerber2png
# gerber2img


https://quentinbolsee.pages.cba.mit.edu/gerber2png/index.html
https://quentinbolsee.pages.cba.mit.edu/gerber2img/


A basic website to consistently convert Gerber files to aligned PNG images.
A basic website to consistently convert Gerber files to aligned PNG images.
+1 −0
Original line number Original line Diff line number Diff line
body{font-family:Arial,Helvetica,sans-serif}input{max-width:30%}p{color:#000}h3{margin-left:4px;margin-top:20px;margin-bottom:4px}h1{margin-top:4px;margin-bottom:8px}p.lock{cursor:default;margin:0}p#dropText{font-size:100px}.container{display:flex}button{border:none}button.interface{background-color:#c8c8c8;padding:4px;border-radius:6px;margin:4px;width:28%;min-width:80px}button.interface:hover{background-color:#a0a0a0;cursor:pointer}button.lock{background:none;border:none;margin:0;padding:0;cursor:pointer}div,h2{margin:4px}.info{background-color:#ddd;padding:5px;border-radius:8px}.full-flex{flex:1}.panel{display:flex;flex-direction:column;padding:4px}.settings{width:35vh;margin:4px;display:flex;border-radius:6px;border:1px solid rgb(0,0,0);flex-direction:column}#previewSVG{width:100%;height:100%}div.SVGbox{border:1px solid rgb(0,0,0)}div.preview{width:65vh;height:65vh;margin:4px;border-radius:6px;background-size:100% 100%;background-image:url(https://quentinbolsee.pages.cba.mit.edu/gerber2img/assets/background-f04e5df7.svg);border:1px solid rgb(0,0,0);display:flex;align-items:center;justify-content:center}div#dropZone{background-color:#b4b4b499;position:fixed;top:0;left:0;width:100%;height:100%;z-index:999;visibility:hidden;display:flex;align-items:center;justify-content:center}footer{position:fixed;left:0;bottom:0;height:30px;width:100%;font-size:12px}

dist/assets/index-c5b4e48b.css

deleted100644 → 0
+0 −1
Original line number Original line Diff line number Diff line
body{font-family:Arial,Helvetica,sans-serif}input{max-width:30%}p{color:#000}h3{margin-left:4px;margin-top:20px;margin-bottom:4px}p.lock{cursor:default;margin:0}p#dropText{font-size:100px}.container{display:flex}button{border:none}button.interface{background-color:#c8c8c8;padding:4px;border-radius:6px;margin:4px;width:28%;min-width:80px}button.interface:hover{background-color:#a0a0a0;cursor:pointer}button.lock{background:none;border:none;margin:0;padding:0;cursor:pointer}div,h2{margin:4px}.info{background-color:#ddd;padding:5px;border-radius:8px}.full-flex{flex:1}.panel{display:flex;flex-direction:column;padding:4px}.settings{width:35vh;height:65vh;margin:4px;display:flex;border-radius:6px;border:1px solid rgb(0,0,0);flex-direction:column}#previewSVG{width:100%;height:100%}div.SVGbox{border:1px solid rgb(0,0,0)}div.preview{width:65vh;height:65vh;margin:4px;border-radius:6px;background-color:#fff;border:1px solid rgb(0,0,0);display:flex;align-items:center;justify-content:center}div#dropZone{background-color:#b4b4b499;position:fixed;top:0;left:0;width:100%;height:100%;z-index:999;visibility:hidden;display:flex;align-items:center;justify-content:center}
+27 −12
Original line number Original line Diff line number Diff line
@@ -2,17 +2,19 @@
<!DOCTYPE html>
<!DOCTYPE html>
<html>
<html>
<head>
<head>
	<link rel="icon" type="image/x-icon" href="images/logo.png">
	<link rel="icon" type="image/x-icon" href="https://quentinbolsee.pages.cba.mit.edu/gerber2img/assets/logo-a252ba3f.png">
 	
 	
	<meta charset="utf-8">
	<meta charset="utf-8">
	<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
	<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
	<meta http-equiv="Pragma" content="no-cache" />
	<meta http-equiv="Pragma" content="no-cache" />
	<meta http-equiv="Expires" content="-1" />
	<meta http-equiv="Expires" content="-1" />
  <script type="module" crossorigin src="https://quentinbolsee.pages.cba.mit.edu/gerber2png/assets/main-e1885530.js"></script>
  <script type="module" crossorigin src="https://quentinbolsee.pages.cba.mit.edu/gerber2img/assets/main-128b0f6a.js"></script>
  <link rel="stylesheet" href="https://quentinbolsee.pages.cba.mit.edu/gerber2png/assets/index-c5b4e48b.css">
  <link rel="stylesheet" href="https://quentinbolsee.pages.cba.mit.edu/gerber2img/assets/index-591a036e.css">
</head>
</head>
<body style="background-color: rgb(255, 255, 255);">
<body style="background-color: rgb(255, 255, 255);">
	<h1>gerber2png</h1>
	<h1>gerber2img</h1>
	<p>Drag and drop one or multiple Gerber files; lock origin and dimensions to preserve alignment between successive uploads.</p>
	<p>For through-holes and vias, make sure to export drill file (.drl) as metric.</p>
	<div id="dropZone"><p id="dropText"></p></div>
	<div id="dropZone"><p id="dropText"></p></div>
	<input type="file" id="fileInput" multiple hidden></input>
	<input type="file" id="fileInput" multiple hidden></input>
	<canvas id="canvas" hidden></canvas>
	<canvas id="canvas" hidden></canvas>
@@ -25,7 +27,7 @@
			<div>
			<div>
				<!-- <button class="interface" id="loadButton">Load file(s)...</button></input> -->
				<!-- <button class="interface" id="loadButton">Load file(s)...</button></input> -->
				<button class="interface" id="downloadRenderButton">Download render</button>
				<button class="interface" id="downloadRenderButton">Download render</button>
			<button class="interface" id="downloadlayersButton">Download layers</button>
				<!-- <button class="interface" id="downloadlayersButton">Download layers</button> -->
			</div>
			</div>
		</div>
		</div>
		<div class="panel">
		<div class="panel">
@@ -34,12 +36,22 @@
				<h3>Rendering</h3>
				<h3>Rendering</h3>


				<div>
				<div>
					<input type="checkbox" id="settingsFill" checked=checked>
					<input type="checkbox" id="settingsFill" checked="checked">
					<label for="settingsFill">Fill edge cut</label>
					<label for="settingsFill">Fill edge cut</label>
				</div>
				</div>


				<div>
				<div>
					<input type="checkbox" id="settingsAsSVG" checked=unchecked>
					<input type="checkbox" id="settingsBW">
					<label for="settingsBW">Black and white</label>
				</div>

				<div>
					<input type="checkbox" id="settingsTransparent">
					<label for="settingsTransparent">Transparent background</label>
				</div>

				<div>
					<input type="checkbox" id="settingsAsSVG">
					<label for="settingsAsSVG">Download as SVG</label>
					<label for="settingsAsSVG">Download as SVG</label>
				</div>
				</div>


@@ -70,13 +82,16 @@
				<h3>Margins [mm]</h3>
				<h3>Margins [mm]</h3>
				<div>
				<div>
					<label for="settingsMarginX">x:</label>
					<label for="settingsMarginX">x:</label>
					<input id="settingsMarginX" step=0.1 min=0 type="number" value=3>
					<input id="settingsMarginX" step=0.1 min=0 type="number" value=2>
					<label for="settingsMarginY">y:</label>
					<label for="settingsMarginY">y:</label>
					<input id="settingsMarginY" step=0.1 min=0 type="number" value=3>
					<input id="settingsMarginY" step=0.1 min=0 type="number" value=2>
				</div>
				</div>
			</div>
			</div>
		</div>
		</div>
	</div>
	</div>
	
	
	<footer>
		<img src="https://quentinbolsee.pages.cba.mit.edu/gerber2img/assets/logo-a252ba3f.png" height="24" style="margin-left:4px; margin-right:4px;"></img>Quentin Bolsée and Jake Read, MIT Center for Bits and Atoms, 2023
	</footer>
</body>
</body>
</html>
</html>

img/background.svg

0 → 100644
+2842 −0

File added.

Preview size limit exceeded, changes collapsed.

+25 −10
Original line number Original line Diff line number Diff line
@@ -2,7 +2,7 @@
<!DOCTYPE html>
<!DOCTYPE html>
<html>
<html>
<head>
<head>
	<link rel="icon" type="image/x-icon" href="images/logo.png">
	<link rel="icon" type="image/x-icon" href="img/logo.png">
 	<link rel="stylesheet" type="text/css" href="src/style.css">
 	<link rel="stylesheet" type="text/css" href="src/style.css">
	<meta charset="utf-8">
	<meta charset="utf-8">
	<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
	<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
@@ -10,7 +10,9 @@
	<meta http-equiv="Expires" content="-1" />
	<meta http-equiv="Expires" content="-1" />
</head>
</head>
<body style="background-color: rgb(255, 255, 255);">
<body style="background-color: rgb(255, 255, 255);">
	<h1>gerber2png</h1>
	<h1>gerber2img</h1>
	<p>Drag and drop one or multiple Gerber files; lock origin and dimensions to preserve alignment between successive uploads.</p>
	<p>For through-holes and vias, make sure to export drill file (.drl) as metric.</p>
	<div id="dropZone"><p id="dropText"></p></div>
	<div id="dropZone"><p id="dropText"></p></div>
	<input type="file" id="fileInput" multiple hidden></input>
	<input type="file" id="fileInput" multiple hidden></input>
	<canvas id="canvas" hidden></canvas>
	<canvas id="canvas" hidden></canvas>
@@ -23,7 +25,7 @@
			<div>
			<div>
				<!-- <button class="interface" id="loadButton">Load file(s)...</button></input> -->
				<!-- <button class="interface" id="loadButton">Load file(s)...</button></input> -->
				<button class="interface" id="downloadRenderButton">Download render</button>
				<button class="interface" id="downloadRenderButton">Download render</button>
			<button class="interface" id="downloadlayersButton">Download layers</button>
				<!-- <button class="interface" id="downloadlayersButton">Download layers</button> -->
			</div>
			</div>
		</div>
		</div>
		<div class="panel">
		<div class="panel">
@@ -32,12 +34,22 @@
				<h3>Rendering</h3>
				<h3>Rendering</h3>


				<div>
				<div>
					<input type="checkbox" id="settingsFill" checked=checked>
					<input type="checkbox" id="settingsFill" checked="checked">
					<label for="settingsFill">Fill edge cut</label>
					<label for="settingsFill">Fill edge cut</label>
				</div>
				</div>


				<div>
				<div>
					<input type="checkbox" id="settingsAsSVG" checked=unchecked>
					<input type="checkbox" id="settingsBW">
					<label for="settingsBW">Black and white</label>
				</div>

				<div>
					<input type="checkbox" id="settingsTransparent">
					<label for="settingsTransparent">Transparent background</label>
				</div>

				<div>
					<input type="checkbox" id="settingsAsSVG">
					<label for="settingsAsSVG">Download as SVG</label>
					<label for="settingsAsSVG">Download as SVG</label>
				</div>
				</div>


@@ -68,13 +80,16 @@
				<h3>Margins [mm]</h3>
				<h3>Margins [mm]</h3>
				<div>
				<div>
					<label for="settingsMarginX">x:</label>
					<label for="settingsMarginX">x:</label>
					<input id="settingsMarginX" step=0.1 min=0 type="number" value=3>
					<input id="settingsMarginX" step=0.1 min=0 type="number" value=2>
					<label for="settingsMarginY">y:</label>
					<label for="settingsMarginY">y:</label>
					<input id="settingsMarginY" step=0.1 min=0 type="number" value=3>
					<input id="settingsMarginY" step=0.1 min=0 type="number" value=2>
				</div>
				</div>
			</div>
			</div>
		</div>
		</div>
	</div>
	</div>
	<script type="module" src="/src/interface.js"></script>
	<script type="module" src="/src/interface.js"></script>
	<footer>
		<img src="img/logo.png" height="24" style="margin-left:4px; margin-right:4px;"></img>Quentin Bolsée and Jake Read, MIT Center for Bits and Atoms, 2023
	</footer>
</body>
</body>
</html>
</html>
+77 −0
Original line number Original line Diff line number Diff line
# png-metadata
library to read and write PNG Metadata on NodeJS and Browser

w3 PNG Chunks specification: https://www.w3.org/TR/PNG-Chunks.html

The Metadata in PNG files: https://dev.exiv2.org/projects/exiv2/wiki/The_Metadata_in_PNG_files
 
 
 ### example with buffers
```javascript
  function loadFileAsBlob(url){
    return new Promise((resolve, reject) => {
      var xhr = new XMLHttpRequest();
      xhr.open('GET', url, true);
      xhr.responseType = 'blob';
      xhr.onload = function(e) {
        if (this.status === 200) {
          resolve(this.response);
          // myBlob is now the blob that the object URL pointed to.
        }else{
          reject(this.response);
        }
      };
      xhr.send();
    })
  };

  //Browser
  const blob = await loadFileAsBlob('1000ppcm.png');
  const buffer = await blob.arrayBuffer();

  //NodeJS
  const buffer = fs.readFileSync('1000ppcm.png')

  //read metadata
  metadata = readMetadata(buffer);
```

### metadata format

image with 300 dpi

```javascript
{
  "pHYs": { 
    "x": 30000,
    "y": 30000,
    "units": RESOLUTION_UNITS.INCHES
  },
  "tEXt": {
    "Title":            "Short (one line) title or caption for image",
    "Author":           "Name of image's creator",
    "Description":      "Description of image (possibly long)",
    "Copyright":        "Copyright notice",
    "Software":         "Software used to create the image",
    "Disclaimer":       "Legal disclaimer",
    "Warning":          "Warning of nature of content",
    "Source":           "Device used to create the image",
    "Comment":          "Miscellaneous comment"
  }
}
```

### writing metadata

```javascript
writeMetadata(buffer,metadata);
```

### save image from canvas

```javascript
canvas.toBlob(blob => {
    let newBlob = fabric.util.png.writeMetadataB(blob, metadata);
    saveAs(newBlob, title);
});
```
+445 −0
Original line number Original line Diff line number Diff line
/**
 * library to read and write PNG Metadata
 *
 * References:
 * w3 PNG Chunks specification: https://www.w3.org/TR/PNG-Chunks.html
 * The Metadata in PNG files: https://dev.exiv2.org/projects/exiv2/wiki/The_Metadata_in_PNG_files
 */

let crc32 = require('crc-32')


// Used for fast-ish conversion between uint8s and uint32s/int32s.
// Also required in order to remain agnostic for both Node Buffers and
// Uint8Arrays.
let uint8 = new Uint8Array(4)
let int32 = new Int32Array(uint8.buffer)
let uint32 = new Uint32Array(uint8.buffer)

const RESOLUTION_UNITS = {UNDEFINED: 0, METERS: 1, INCHES: 2};

/**
 * https://github.com/aheckmann/sliced
 * An Array.prototype.slice.call(arguments) alternative
 * @param {Object} args something with a length
 * @param {Number} slice
 * @param {Number} sliceEnd
 * @api public
 */
function sliced (args, slice, sliceEnd) {
	var ret = [];
	var len = args.length;

	if (0 === len) return ret;

	var start = slice < 0
		? Math.max(0, slice + len)
		: slice || 0;

	if (sliceEnd !== undefined) {
		len = sliceEnd < 0
			? sliceEnd + len
			: sliceEnd
	}

	while (len-- > start) {
		ret[len - start] = args[len];
	}

	return ret;
}

/**
 * https://github.com/hughsk/png-chunk-text
 * Returns a chunk object containing the metadata for a given key and value:
 * @param keyword
 * @param content
 * @param chunkName
 * @returns {{data: Uint8Array, name: 'tEXt'}}
 */
function textEncode (keyword, content , chunkName = 'tEXt') {
	keyword = String(keyword)
	content = String(content)

	if (content.length && (!/^[\x00-\xFF]+$/.test(keyword) || !/^[\x00-\xFF]+$/.test(content))) {
		throw new Error('Only Latin-1 characters are permitted in PNG tEXt chunks. You might want to consider base64 encoding and/or zEXt compression')
	}

	if (keyword.length >= 80) {
		throw new Error('Keyword "' + keyword + '" is longer than the 79-character limit imposed by the PNG specification')
	}

	let totalSize = keyword.length + content.length + 1
	let output = new Uint8Array(totalSize)
	let idx = 0
	let code

	for (let i = 0; i < keyword.length; i++) {
		if (!(code = keyword.charCodeAt(i))) {
			throw new Error('0x00 character is not permitted in tEXt keywords')
		}

		output[idx++] = code
	}

	output[idx++] = 0

	for (let j = 0; j < content.length; j++) {
		if (!(code = content.charCodeAt(j))) {
			throw new Error('0x00 character is not permitted in tEXt content')
		}

		output[idx++] = code
	}

	return {
		name: chunkName,
		data: output
	}
}

/**
 * https://github.com/hughsk/png-chunk-text
 * Reads a Uint8Array or Node.js Buffer instance containing a tEXt PNG chunk's data and returns its keyword/text:
 * @param data
 * @returns {{text: string, keyword: string}}
 */
function textDecode (data) {
	if (data.data && data.name) {
		data = data.data
	}

	let naming = true
	let text = ''
	let name = ''

	for (let i = 0; i < data.length; i++) {
		let code = data[i]

		if (naming) {
			if (code) {
				name += String.fromCharCode(code)
			} else {
				naming = false
			}
		} else {
			if (code) {
				text += String.fromCharCode(code)
			} else {
				throw new Error('Invalid NULL character found. 0x00 character is not permitted in tEXt content')
			}
		}
	}

	return {
		keyword: name,
		text: text
	}
}

/**
 * https://github.com/hughsk/png-chunks-extract
 * Extract the data chunks from a PNG file.
 * Useful for reading the metadata of a PNG image, or as the base of a more complete PNG parser.
 * Takes the raw image file data as a Uint8Array or Node.js Buffer, and returns an array of chunks. Each chunk has a name and data buffer:
 * @param data {Uint8Array}
 * @returns {[{name: String, data: Uint8Array}]}
 */
function extractChunks (data) {
	if (data[0] !== 0x89) throw new Error('Invalid .png file header')
	if (data[1] !== 0x50) throw new Error('Invalid .png file header')
	if (data[2] !== 0x4E) throw new Error('Invalid .png file header')
	if (data[3] !== 0x47) throw new Error('Invalid .png file header')
	if (data[4] !== 0x0D) throw new Error('Invalid .png file header: possibly caused by DOS-Unix line ending conversion?')
	if (data[5] !== 0x0A) throw new Error('Invalid .png file header: possibly caused by DOS-Unix line ending conversion?')
	if (data[6] !== 0x1A) throw new Error('Invalid .png file header')
	if (data[7] !== 0x0A) throw new Error('Invalid .png file header: possibly caused by DOS-Unix line ending conversion?')

	let ended = false
	let chunks = []
	let idx = 8

	while (idx < data.length) {
		// Read the length of the current chunk,
		// which is stored as a Uint32.
		uint8[3] = data[idx++]
		uint8[2] = data[idx++]
		uint8[1] = data[idx++]
		uint8[0] = data[idx++]

		// Chunk includes name/type for CRC check (see below).
		let length = uint32[0] + 4
		let chunk = new Uint8Array(length)
		chunk[0] = data[idx++]
		chunk[1] = data[idx++]
		chunk[2] = data[idx++]
		chunk[3] = data[idx++]

		// Get the name in ASCII for identification.
		let name = (
			String.fromCharCode(chunk[0]) +
			String.fromCharCode(chunk[1]) +
			String.fromCharCode(chunk[2]) +
			String.fromCharCode(chunk[3])
		)

		// The IHDR header MUST come first.
		if (!chunks.length && name !== 'IHDR') {
			throw new Error('IHDR header missing')
		}

		// The IEND header marks the end of the file,
		// so on discovering it break out of the loop.
		if (name === 'IEND') {
			ended = true
			chunks.push({
				name: name,
				data: new Uint8Array(0)
			})

			break
		}

		// Read the contents of the chunk out of the main buffer.
		for (let i = 4; i < length; i++) {
			chunk[i] = data[idx++]
		}

		// Read out the CRC value for comparison.
		// It's stored as an Int32.
		uint8[3] = data[idx++]
		uint8[2] = data[idx++]
		uint8[1] = data[idx++]
		uint8[0] = data[idx++]

		let crcActual = int32[0]
		let crcExpect = crc32.buf(chunk)
		if (crcExpect !== crcActual) {
			throw new Error(
				'CRC values for ' + name + ' header do not match, PNG file is likely corrupted'
			)
		}

		// The chunk data is now copied to remove the 4 preceding
		// bytes used for the chunk name/type.
		let chunkData = new Uint8Array(chunk.buffer.slice(4))

		chunks.push({
			name: name,
			data: chunkData
		})
	}

	if (!ended) {
		throw new Error('.png file ended prematurely: no IEND header was found')
	}

	return chunks
}

/**
 * https://github.com/hughsk/png-chunks-encode
 * Return a fresh PNG buffer given a set of PNG chunks. Useful in combination with png-chunks-encode to easily modify or add to the data of a PNG file.
 * Takes an array of chunks, each with a name and data:
 * @param chunks {[{name: String, data: Uint8Array}]}
 * @returns {Uint8Array}
 */
function encodeChunks (chunks) {
	let totalSize = 8
	let idx = totalSize
	let i

	for (i = 0; i < chunks.length; i++) {
		totalSize += chunks[i].data.length
		totalSize += 12
	}

	let output = new Uint8Array(totalSize)

	output[0] = 0x89
	output[1] = 0x50
	output[2] = 0x4E
	output[3] = 0x47
	output[4] = 0x0D
	output[5] = 0x0A
	output[6] = 0x1A
	output[7] = 0x0A

	for (i = 0; i < chunks.length; i++) {
		let chunk = chunks[i]
		let name = chunk.name
		let data = chunk.data
		let size = data.length
		let nameChars = [
			name.charCodeAt(0),
			name.charCodeAt(1),
			name.charCodeAt(2),
			name.charCodeAt(3)
		]

		uint32[0] = size
		output[idx++] = uint8[3]
		output[idx++] = uint8[2]
		output[idx++] = uint8[1]
		output[idx++] = uint8[0]

		output[idx++] = nameChars[0]
		output[idx++] = nameChars[1]
		output[idx++] = nameChars[2]
		output[idx++] = nameChars[3]

		for (let j = 0; j < size;) {
			output[idx++] = data[j++]
		}

		let crcCheck = nameChars.concat(sliced(data));
		int32[0] = crc32.buf(crcCheck)
		output[idx++] = uint8[3]
		output[idx++] = uint8[2]
		output[idx++] = uint8[1]
		output[idx++] = uint8[0]
	}

	return output
}

/**
 * read 4 bytes number from UInt8Array.
 * @param uint8array
 * @param offset
 * @returns {number}
 */
function readUint32 (uint8array,offset) {
	let byte1, byte2, byte3, byte4;
	byte1 = uint8array[offset++];
	byte2 = uint8array[offset++];
	byte3 = uint8array[offset++];
	byte4 = uint8array[offset];
	return  0 | (byte1 << 24) | (byte2 << 16) | (byte3 << 8) | byte4;
}

/**
 * write 4 bytes number to UInt8Array.
 * @param uint8array
 * @param num
 * @param offset
 */
function writeUInt32(uint8array, num, offset){
	uint8array[offset] = (num & 0xff000000) >> 24;
	uint8array[offset + 1] = (num & 0x00ff0000) >> 16;
	uint8array[offset + 2] = (num & 0x0000ff00) >> 8;
	uint8array[offset + 3] = (num & 0x000000ff);
}

/**
 * Get object with PNG metadata. only tEXt and pHYs chunks are parsed
 * @param buffer {Buffer}
 * @returns {{tEXt: {keyword: value}, pHYs: {x: number, y: number, units: RESOLUTION_UNITS}, [string]: true}}
 */
function readMetadata(buffer){
	let result = {};
	const chunks = extractChunks(buffer);
	chunks.forEach( chunk => {
		switch(chunk.name){
			case 'tEXt':
				if (!result.tEXt) {
					result.tEXt = {};
				}
				let textChunk = textDecode(chunk.data);
				result.tEXt[textChunk.keyword] = textChunk.text;
				break
			case 'pHYs':
				result.pHYs = {
					// Pixels per unit, X axis: 4 bytes (unsigned integer)
					"x": readUint32(chunk.data, 0),
					// Pixels per unit, Y axis: 4 bytes (unsigned integer)
					"y":  readUint32(chunk.data, 4),
					"unit": chunk.data[8],
				}
				break
			case 'gAMA':
			case 'cHRM':
			case 'sRGB':
			case 'IHDR':
			case 'iCCP':
			default:
				result[chunk.name] = true;
		}
	})
	return result;
}

/**
 * create new Buffer with metadata. only tEXt and pHYs chunks are supported.
 * @param buffer {Buffer}
 * @param metadata {{tEXt: {keyword: value}, pHYs: {x: number, y: number, units: RESOLUTION_UNITS}}}
 * @returns {Buffer}
 */
function writeMetadata(buffer,metadata){
	const chunks = extractChunks(buffer);
	insertMetadata(chunks,metadata);
	return new Buffer.from(encodeChunks(chunks))
}

/**
 * the same. but for Blobs
 * @param blob {Blob}
 * @param metadata {{tEXt: {keyword: value}, pHYs: {x: number, y: number, units: RESOLUTION_UNITS}}}
 * @returns {Promise<Blob>} new blob
 */
export async function writeMetadataB(blob,metadata){
	let arrayBuffer = await blob.arrayBuffer();
	let uint8Array = new Uint8Array(arrayBuffer);
	let newBuffer = await writeMetadata(uint8Array,metadata);
	return new Blob([newBuffer], {type : blob.type});
}

function insertMetadata(chunks,metadata){
	if(metadata.clear){
		for(let i = chunks.length - 1; i--;){
			switch(chunks[i].name){
				case 'IHDR':
				case 'IDAT':
				case 'IEND':
					break;
				default:
					chunks.splice(i,1);
			}
		}
	}
	if(metadata.tEXt){
		for(var keyword in metadata.tEXt){
			chunks.splice(-1, 0, textEncode(keyword, metadata.tEXt[keyword]))
		}
	}
	if(metadata.pHYs){
		const data = new Uint8Array(9);
		writeUInt32(data,metadata.pHYs.x,0)
		writeUInt32(data,metadata.pHYs.y,4)
		data[8] = metadata.pHYs.units; // inches

		let pHYs = chunks.find(chunk => chunk.name === "pHYs");
		if(pHYs){
			pHYs.data = data;
		}
		else{
			chunks.splice(1, 0, {name: "pHYs",data: data})
		}
	}
    console.log(metadata)
    console.log("DONE")
}



// module.exports = {
// 	RESOLUTION_UNITS: RESOLUTION_UNITS,
// 	insertMetadata: insertMetadata,
// 	readMetadata: readMetadata,
// 	writeMetadata: writeMetadata,
// 	writeMetadataB: writeMetadataB,
// 	textEncode: textEncode,
// 	textDecode: textDecode,
// 	extractChunks: extractChunks,
// 	encodeChunks: encodeChunks
// };
+9 −0
Original line number Original line Diff line number Diff line
{
  "name": "png-metadata",
  "version": "1.0.0",
  "description": "library to read and write PNG Metadata",
  "main": "index.js",
  "dependencies": {
    "crc-32": "^1.2.0"
  }
}

package-lock.json

deleted100644 → 0
+0 −1625

File deleted.

Preview size limit exceeded, changes collapsed.

+4 −3
Original line number Original line Diff line number Diff line
@@ -12,14 +12,15 @@
    "buffer": "^6.0.0",
    "buffer": "^6.0.0",
    "events": "^3.0.0",
    "events": "^3.0.0",
    "gerber-to-svg": "^4.2.8",
    "gerber-to-svg": "^4.2.8",
    "save-svg-as-png": "^1.4.0",
    "browserify-zlib": "^0.2.0",
    "browserify-zlib": "^0.2.0",
    "process": "^0.11.10",
    "process": "^0.11.10",
    "stream-browserify": "^3.0.0",
    "stream-browserify": "^3.0.0",
    "util": "^0.12.5"
    "util": "^0.12.5",
    "png-metadata": "file:lib/png-metadata"
  },
  },
  "devDependencies": {
  "devDependencies": {
    "typescript": "^5.0.2",
    "typescript": "^5.0.2",
    "vite": "^4.4.11"
    "vite": "^4.4.11",
    "vite-require": "^0.2.3"
  }
  }
}
}
+140 −66

File changed.

Preview size limit exceeded, changes collapsed.

+16 −2
Original line number Original line Diff line number Diff line
@@ -16,6 +16,11 @@ h3 {
    margin-bottom: 4px;
    margin-bottom: 4px;
}
}


h1 {
    margin-top: 4px;
    margin-bottom: 8px;
}

p.lock {
p.lock {
    cursor: default;
    cursor: default;
    margin: 0;
    margin: 0;
@@ -92,7 +97,6 @@ h2 {


.settings {
.settings {
    width: 35vh;
    width: 35vh;
    height: 65vh;
    margin: 4px;
    margin: 4px;
    display: flex;
    display: flex;
    border-radius: 6px;
    border-radius: 6px;
@@ -114,7 +118,8 @@ div.preview {
    height: 65vh;
    height: 65vh;
    margin: 4px;
    margin: 4px;
    border-radius: 6px;
    border-radius: 6px;
    background-color: rgb(255, 255, 255);
    background-size: 100% 100%;
    background-image: url("/img/background.svg");
    border: 1px solid rgb(0, 0, 0);
    border: 1px solid rgb(0, 0, 0);
    display: flex;
    display: flex;
    align-items: center;
    align-items: center;
@@ -134,3 +139,12 @@ div#dropZone {
  align-items: center;
  align-items: center;
  justify-content: center;
  justify-content: center;
}
}

footer {
   position:fixed;
   left: 0px;
   bottom: 0px;
   height: 30px;
   width: 100%;
   font-size: 12px;
}
+1 −1
Original line number Original line Diff line number Diff line
@@ -19,5 +19,5 @@
    "noUnusedParameters": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true
    "noFallthroughCasesInSwitch": true
  },
  },
  "include": ["src"]
  "include": ["src", "img"]
}
}
+5 −1
Original line number Original line Diff line number Diff line
import { resolve } from 'path'
import { resolve } from 'path'
import { defineConfig } from 'vite'
import { defineConfig } from 'vite'
import { viteRequire } from 'vite-require'


export default defineConfig({
export default defineConfig({
    resolve: {
    resolve: {
@@ -10,7 +11,7 @@ export default defineConfig({
        util: 'util'
        util: 'util'
      }
      }
    },
    },
    base: "https://quentinbolsee.pages.cba.mit.edu/gerber2png/",
    base: "https://quentinbolsee.pages.cba.mit.edu/gerber2img/",
    build: {
    build: {
        rollupOptions: {
        rollupOptions: {
            input: {
            input: {
@@ -19,4 +20,7 @@ export default defineConfig({
            },
            },
        },
        },
    },
    },
    plugins: [
      viteRequire()
    ]
})
})