# 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'); }); ```