jcode/skills/html-file-formats/formats.md

171 lines
8.8 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

# 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
<script type="importmap">{ "imports": {
"js-yaml": "https://cdn.jsdelivr.net/npm/js-yaml@5.5.0/dist/js-yaml.mjs",
"pdfjs": "https://cdn.jsdelivr.net/npm/pdfjs-dist@6.4.299/build/pdf.min.mjs",
"docx": "https://cdn.jsdelivr.net/npm/docx@9.13.0/dist/index.mjs"
}}</script>
```
Classic scripts (global objects `Papa`, `XLSX`, `mammoth`, `PDFLib`, `jspdf`, `JSZip`) - put
them BEFORE your module script:
```html
<script src="https://cdn.jsdelivr.net/npm/papaparse@5.7.0/papaparse.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/mammoth@1.13.0/mammoth.browser.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/pdf-lib@1.17.1/dist/pdf-lib.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jspdf@4.2.1/dist/jspdf.umd.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jszip@3.10.2/dist/jszip.min.js"></script>
```
## Images (png / jpg / webp) - no library
```js
// load: file -> <img> -> 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 = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><circle cx="50" cy="50" r="40" fill="tomato"/></svg>`;
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 <table><tr><td>
});
// 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');
});
```