# Load / save snippets per format (browser)
All library calls below were run against the pinned versions. `pickFile(accept,
asText, cb)` and `download(name, data, type)` are the helpers from the
single-file-webapp skill (patterns.md); `download` accepts a string or a Blob.
## Libraries - copy what you need
ESM (import map):
```html
```
Classic scripts (global objects `Papa`, `XLSX`, `mammoth`, `PDFLib`, `jspdf`, `JSZip`) - put
them BEFORE your module script:
```html
```
## Images (png / jpg / webp) - no library
```js
// load: file -> -> pixels
pickFile('image/*', false, (buf, name) => {
const img = new Image();
img.onload = () => {
const c = Object.assign(document.createElement('canvas'), { width: img.width, height: img.height });
const ctx = c.getContext('2d'); ctx.drawImage(img, 0, 0);
const { data } = ctx.getImageData(0, 0, c.width, c.height); // RGBA bytes, row-major
console.log(name, img.width, 'x', img.height, 'first pixel', [...data.slice(0, 4)]);
URL.revokeObjectURL(img.src);
};
img.src = URL.createObjectURL(new Blob([buf]));
});
// save: canvas -> file
canvas.toBlob(blob => download('out.png', blob), 'image/png');
canvas.toBlob(blob => download('out.jpg', blob), 'image/jpeg', 0.9); // quality 0..1, no alpha
```
## SVG - no library (three.js for extrusion)
```js
pickFile('.svg', true, text => {
const svg = new DOMParser().parseFromString(text, 'image/svg+xml').documentElement;
if (svg.querySelector('parsererror')) throw new Error('invalid SVG');
const vb = svg.getAttribute('viewBox')?.split(/\s+|,/).map(Number); // [x, y, w, h]
const paths = [...svg.querySelectorAll('path')].map(p => p.getAttribute('d'));
// rasterize: draw into a canvas at any size
const img = new Image(); img.onload = () => ctx.drawImage(img, 0, 0, 512, 512);
img.src = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(text);
});
// save: build the markup (keep viewBox so it scales) - or serialize a DOM you built
const svgText = ``;
download('shape.svg', svgText, 'image/svg+xml');
// 3D: SVGLoader + ExtrudeGeometry -> threejs-csg skill, loaders.md
```
## STL -> threejs-csg skill, loaders.md (`STLLoader.parse(arrayBuffer)`, `STLExporter`)
## JSON -> save-load-json skill
## YAML (js-yaml 5.5.0, ESM)
```js
import { load as yamlLoad, dump as yamlDump } from 'js-yaml';
pickFile('.yaml,.yml', true, text => { const cfg = yamlLoad(text); /* plain objects/arrays */ });
download('config.yaml', yamlDump(cfg, { noRefs: true, lineWidth: 100 }), 'text/yaml');
```
`load` never executes code (safe for untrusted files). Multi-document files: `loadAll(text)`.
## CSV / TSV (papaparse 5.7.0)
```js
pickFile('.csv,.tsv,.txt', true, text => {
const { data, errors, meta } = Papa.parse(text, { header: true, dynamicTyping: true, skipEmptyLines: true });
// data: [{col: value, ...}], meta.delimiter: ',' ';' or '\t' (auto), errors: [{row, message}]
});
download('table.csv', Papa.unparse(rows), 'text/csv'); // rows: array of objects or arrays
// Excel-friendly: '' + Papa.unparse(rows, { delimiter: ';' }) (BOM + semicolon for German Excel)
```
Latin-1 files: `new TextDecoder('windows-1252').decode(arrayBuffer)` before parsing.
## XLSX / XLS (SheetJS xlsx 0.18.5)
```js
pickFile('.xlsx,.xls,.ods', false, buf => {
const wb = XLSX.read(buf, { type: 'array', cellDates: true });
for (const name of wb.SheetNames) {
const ws = wb.Sheets[name];
const rows = XLSX.utils.sheet_to_json(ws); // [{Header: value}], first row = headers
const grid = XLSX.utils.sheet_to_json(ws, { header: 1 }); // [[cell, cell, ...]] raw grid
const csv = XLSX.utils.sheet_to_csv(ws);
const cell = ws['B2']; // {v: value, t: 'n'|'s'|'d'|'b', f: 'A2*2' (formula), w: formatted text}
}
});
// save
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, XLSX.utils.json_to_sheet(rows), 'Data'); // or aoa_to_sheet([[...], [...]])
const bytes = XLSX.write(wb, { type: 'array', bookType: 'xlsx' }); // 'xls' for legacy Excel, 'csv', 'ods'
download('data.xlsx', new Blob([bytes], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }));
```
Formulas on write: `ws['C1'] = { t: 'n', f: 'A1+B1' }`. Column widths: `ws['!cols'] = [{ wch: 20 }]`.
## DOCX - read with mammoth 1.13.0, write with docx 9.13.0
```js
// read: text and simple HTML (headings, lists, tables, bold/italic, images as data URIs)
pickFile('.docx', false, async buf => {
const { value: html, messages } = await mammoth.convertToHtml({ arrayBuffer: buf });
const { value: text } = await mammoth.extractRawText({ arrayBuffer: buf });
// tables: parse the html with DOMParser and read
| }); // write import { Document, Packer, Paragraph, TextRun, HeadingLevel, Table, TableRow, TableCell, ImageRun } from 'docx'; const doc = new Document({ sections: [{ children: [ new Paragraph({ text: 'Report', heading: HeadingLevel.HEADING_1 }), new Paragraph({ children: [new TextRun('Normal '), new TextRun({ text: 'bold', bold: true })] }), new Table({ rows: [new TableRow({ children: [new TableCell({ children: [new Paragraph('cell')] })] })] }), // new Paragraph({ children: [new ImageRun({ type: 'png', data: pngArrayBuffer, transformation: { width: 300, height: 200 } })] }), ]}]}); download('report.docx', await Packer.toBlob(doc)); ``` `.doc` (Word 97-2003): no browser parser. Convert with LibreOffice via run_command (`soffice --headless --convert-to docx file.doc`) or ask for a .docx export. ## PDF - read with pdf.js 6.4.299, write with pdf-lib 1.17.1 or jsPDF 4.2.1 ```js import * as pdfjs from 'pdfjs'; pdfjs.GlobalWorkerOptions.workerSrc = 'https://cdn.jsdelivr.net/npm/pdfjs-dist@6.4.299/build/pdf.worker.min.mjs'; pickFile('.pdf', false, async buf => { const pdf = await pdfjs.getDocument({ data: new Uint8Array(buf) }).promise; // pass a COPY: pdf.js empties the buffer it gets for (let i = 1; i <= pdf.numPages; i++) { const page = await pdf.getPage(i); const text = (await page.getTextContent()).items.map(it => it.str).join(' '); // '' for scanned pages const vp = page.getViewport({ scale: 1.5 }); // render to a canvas const c = Object.assign(document.createElement('canvas'), { width: vp.width, height: vp.height }); await page.render({ canvasContext: c.getContext('2d'), viewport: vp }).promise; } }); // write (pdf-lib): exact placement, merge, fill forms, modify existing files const { PDFDocument, StandardFonts, rgb } = PDFLib; const out = await PDFDocument.create(); const page = out.addPage([595, 842]); // A4 points const font = await out.embedFont(StandardFonts.Helvetica); page.drawText('Invoice 4711', { x: 50, y: 790, size: 14, font, color: rgb(0, 0, 0) }); const png = await out.embedPng(pngBytes); page.drawImage(png, { x: 50, y: 500, width: 200, height: 150 }); download('out.pdf', new Blob([await out.save()], { type: 'application/pdf' })); // merge const merged = await PDFDocument.create(); for (const bytes of [a, b]) { const src = await PDFDocument.load(bytes); (await merged.copyPages(src, src.getPageIndices())).forEach(p => merged.addPage(p)); } // write (jsPDF): quick text/HTML-ish reports const doc = new jspdf.jsPDF(); doc.text('Hello', 20, 20); doc.addImage(canvas, 'PNG', 20, 30, 100, 60); download('report.pdf', doc.output('blob')); ``` Scanned PDFs: `getTextContent()` is empty - render the page and run OCR (tesseract.js 5: `Tesseract.recognize(canvas, 'eng')`), or say the file has no text layer. ## ZIP (jszip 3.10.2) - bundle a project with assets ```js const zip = new JSZip(); zip.file('scene.json', JSON.stringify(toData(), null, 2)); zip.folder('textures').file('wood.png', pngBlob); download('project.zip', await zip.generateAsync({ type: 'blob' })); pickFile('.zip', false, async buf => { const z = await JSZip.loadAsync(buf); const scene = JSON.parse(await z.file('scene.json').async('string')); const tex = await z.file('textures/wood.png')?.async('blob'); }); ``` |