67 lines
4.6 KiB
Markdown
67 lines
4.6 KiB
Markdown
---
|
|
name: html-file-formats
|
|
description: >
|
|
Load, inspect and save files inside a single-file HTML app (browser, no
|
|
server): images (png/jpg/webp), svg, stl, json, yaml, csv, xls/xlsx, docx,
|
|
pdf and zip - which pinned CDN library to use, how to read the file the user
|
|
picks, extract text/tables/pixels/geometry from it, and generate a download
|
|
of your own data in that format. Use whenever a web app must open or export
|
|
one of these formats; for .doc or other formats without a browser parser it
|
|
says what to do instead.
|
|
---
|
|
|
|
# File formats in a browser app
|
|
|
|
Everything runs in the page: the user picks a file (`<input type=file>`), you
|
|
parse it in JS, and you save by generating bytes and triggering a download.
|
|
Nothing is written to disk by the app itself - "save" always means download.
|
|
Verified library versions and calls: [formats.md](./formats.md). Copy the
|
|
import map / script tags from there; pin versions, never `@latest`.
|
|
|
|
| Format | Load (file -> data) | Save (data -> file) | Library | Notes |
|
|
|---|---|---|---|---|
|
|
| png / jpg / webp | `Image` + `<canvas>` -> pixels, size | `canvas.toBlob(cb, 'image/png')` | none | jpg has no alpha; `toBlob('image/jpeg', 0.9)` |
|
|
| svg | `DOMParser` -> DOM (paths, sizes); rasterize via `Image` | serialize DOM / build string | none; three.js `SVGLoader` for extrusion | keep `viewBox`; CSS inside `<style>` |
|
|
| stl | `STLLoader.parse(ArrayBuffer)` -> geometry | `STLExporter` | three.js addons | details: threejs-csg skill, loaders.md |
|
|
| json | `JSON.parse(text)` | `JSON.stringify(data, null, 2)` | none | full recipe: save-load-json skill |
|
|
| yaml | `yaml.load(text)` | `yaml.dump(obj)` | js-yaml 5.5.0 (ESM) | `load` is safe (no code execution) |
|
|
| csv / tsv | `Papa.parse(text, {header, dynamicTyping})` | `Papa.unparse(rows)` | papaparse 5.7.0 | delimiter auto-detected; `encoding` for Latin-1 |
|
|
| xlsx / xls | `XLSX.read(ArrayBuffer)` -> sheets -> json/csv | `XLSX.write(wb, {bookType})` | xlsx 0.18.5 (SheetJS) | reads legacy `.xls` too; formulas come as values + `f` |
|
|
| docx | `mammoth.convertToHtml({arrayBuffer})` / `extractRawText` | `docx` 9.13.0 `Packer.toBlob` | mammoth 1.13.0, docx 9.13.0 | mammoth: text, headings, tables, images; no layout |
|
|
| doc (legacy) | no browser parser | - | - | convert first: `soffice --headless --convert-to docx x.doc` (run_command), or ask for .docx |
|
|
| pdf | pdf.js `getTextContent()` per page, render page to canvas | pdf-lib (precise, merge/modify) or jsPDF (quick text/images) | pdfjs-dist 6.4.299, pdf-lib 1.17.1, jspdf 4.2.1 | scanned PDFs have no text layer: render + OCR (tesseract.js) |
|
|
| zip | `JSZip.loadAsync(ArrayBuffer)` | `zip.generateAsync({type:'blob'})` | jszip 3.10.2 | bundle a project: json + assets |
|
|
|
|
## Workflow
|
|
1. Pick the row, copy its library line from formats.md into the page (import
|
|
map entry or `<script>` tag) and the load/save snippet.
|
|
2. Read the file as the right type: **text** for json/yaml/csv/svg,
|
|
**ArrayBuffer** for everything binary (images, stl, xlsx, docx, pdf, zip).
|
|
Helpers `pickFile`/`download` are in the single-file-webapp skill.
|
|
3. Parse into plain data first (rows, records, geometry, text per page), THEN
|
|
build app objects from it through the app's normal constructor. Never keep
|
|
the library's document object as app state.
|
|
4. Save = build from app state -> library -> `Blob` -> `download(name, blob)`.
|
|
The button handler calls one `exportX()` function that RETURNS the Blob, so
|
|
the browser-test skill can test it (`h.download('#btn')`).
|
|
5. Test with real files: put small fixtures in `fixtures/` (a 2-row csv, a
|
|
3-cell xlsx, a one-page pdf), drive the real buttons with `h.upload`,
|
|
assert on the parsed data; then round-trip your own export through your
|
|
own importer.
|
|
|
|
## Pitfalls
|
|
- `FileReader.readAsText` guesses UTF-8; pass `'windows-1252'` for old CSV/TXT
|
|
exports from Excel, or detect the BOM.
|
|
- pdf.js **transfers** the ArrayBuffer you give it (it becomes empty). Pass
|
|
`new Uint8Array(bytes)` or `bytes.slice()` if you need the bytes afterwards
|
|
(e.g. to merge with pdf-lib).
|
|
- pdf.js needs its worker: set `GlobalWorkerOptions.workerSrc` to the CDN
|
|
worker URL of the SAME version, or every call fails with a worker error.
|
|
- SheetJS dates: `cellDates: true` on read, else dates arrive as serial numbers.
|
|
- Big files: parse in a `Worker` or at least `await` in chunks (`Papa.parse`
|
|
has `step`/`chunk` callbacks); the UI freezes above a few MB otherwise.
|
|
- `canvas.toBlob` on an image from another origin throws (tainted canvas):
|
|
images must come from the user's file or the same origin.
|
|
- Binary formats never go through `readAsText`; a docx/xlsx/pdf opened as text
|
|
is garbage and the library will say "corrupt file".
|