exiv2-wasm
Version:
Exiv2 in the browser via WebAssembly (read/write EXIF/IPTC/XMP).
211 lines (166 loc) • 5.63 kB
Markdown
# exiv2-wasm
> 🇰🇷 Read this in Korean: **[README.ko.md](README.ko.md)**
Use [Exiv2](https://exiv2.org/) in the browser via **WebAssembly**.
A tiny C++ wrapper (embind) exposes simple functions to **read/write EXIF / IPTC / XMP**.
Minimal deps are built for wasm (**expat, brotli dec/common, inih**); zlib comes from the Emscripten port.
## Online Demo
- [한국어 버전](https://daissue.app/exif-editor)
- [English version](https://daissue.app/en/exif-editor)
## Project structure
```
exiv2-wasm/
├─ exiv2/ # submodule (Exiv2)
├─ libexpat/ # submodule (expat)
├─ brotli/ # submodule (google/brotli)
├─ inih/ # submodule (benhoyt/inih)
├─ scripts/
│ ├─ build.ps1 # Windows PowerShell build
│ └─ build.bash # Linux/macOS bash build
├─ wrapper.cpp # embind wrapper (read/write metadata)
├─ index.html # demo UI page
├─ dist/ # build outputs: exiv2.js / exiv2.wasm
└─ (build/, deps/) # build cache / installed deps (git-ignored)
```
Clone with submodules:
```bash
git clone --recurse-submodules https://github.com/gerosyab/exiv2-wasm.git
```
## Prerequisites
### Tools
- **CMake**, **Ninja**
- **Emscripten SDK** (emcmake / emcc / em++ / emar)
**Windows (PowerShell)**
```powershell
winget install Kitware.CMake
winget install Ninja-build.Ninja
```
**Linux (Debian/Ubuntu)**
```bash
sudo apt update
sudo apt install -y cmake ninja-build python3
```
**Emscripten SDK (all OS)**
```bash
# anywhere you like
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install latest
./emsdk activate latest
# new shell(s):
source ./emsdk_env.sh # Linux/macOS
# or on Windows PowerShell:
# .\emsdk_env.ps1
```
> Re-run `emsdk_env.sh` / `emsdk_env.ps1` in each new shell.
## Build
**Windows (PowerShell)**
```powershell
cd exiv2-wasm
# one-session policy (optional)
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
# clean build
.\scripts\build.ps1 -Clean
# incremental build
.\scripts\build.ps1
```
**Linux/macOS (bash/zsh)**
```bash
cd exiv2-wasm
chmod +x scripts/build.bash
# clean build
./scripts/build.bash -c
# incremental build
./scripts/build.bash
```
**Outputs:** `dist/exiv2.js` and `dist/exiv2.wasm`
## Run the demo
```bash
# from project root
python -m http.server 8080
# open:
# http://localhost:8080/index.html
```
## JavaScript usage
**Wrapper API:**
- `read(u8: Uint8Array) -> { exif: Object, iptc: Object, xmp: Object }`
- `readTagText(u8, key: string) -> string | null`
- `readTagBytes(u8, key: string) -> Uint8Array | null`
- `writeString(u8, key: string, value: string) -> Uint8Array` (returns new buffer)
- `writeBytes(u8, key: string, data: Uint8Array) -> Uint8Array`
**Browser (CDN + `<script>` global function)**
```html
<script src="https://unpkg.com/exiv2-wasm"></script>
<script>
(async () => {
const exiv2 = await createExiv2Module();
async function fileToU8(file) {
const buf = await file.arrayBuffer();
return new Uint8Array(buf);
}
document.querySelector('#file').addEventListener('change', async (e) => {
const u8 = await fileToU8(e.target.files[0]);
const meta = exiv2.read(u8);
console.log('Camera Model:', meta.exif['Exif.Image.Model']);
});
})();
</script>
<input id="file" type="file" accept="image/*">
```
**Browser (CDN + ESM import)**
```js
<script type="module">
import { createExiv2Module } from 'https://cdn.jsdelivr.net/npm/exiv2-wasm/+esm';
const exiv2 = await createExiv2Module();
const input = document.querySelector('#file');
input.addEventListener('change', async (e) => {
const file = e.target.files[0];
const buf = new Uint8Array(await file.arrayBuffer());
const meta = exiv2.read(buf);
console.log('Camera Model:', meta.exif['Exif.Image.Model']);
});
</script>
<input id="file" type="file" accept="image/*">
```
**ESM (Node.js / Vite / webpack / Rollup)**
```
import { createExiv2Module } from 'exiv2-wasm';
const exiv2 = await createExiv2Module();
const fs = await import('fs/promises');
const u8 = new Uint8Array(await fs.readFile('image.jpg'));
const meta = exiv2.read(u8);
console.log('Model:', meta.exif['Exif.Image.Model']);
```
**CommonJS**
```js
const { createExiv2Module } = require('exiv2-wasm');
const fs = require('fs');
function fileToU8(path) {
return new Uint8Array(fs.readFileSync(path));
}
createExiv2Module().then((exiv2) => {
const u8 = fileToU8('image.jpg');
const meta = exiv2.read(u8);
console.log('Model:', meta.exif['Exif.Image.Model']);
});
```
**Common keys (examples)**
- Camera: `Exif.Image.Make`, `Exif.Image.Model`
- Exposure: `Exif.Photo.ExposureTime`, `Exif.Photo.ShutterSpeedValue`
- Aperture: `Exif.Photo.FNumber`, `Exif.Photo.ApertureValue`
- ISO: `Exif.Photo.PhotographicSensitivity` (or `Exif.Photo.ISOSpeedRatings`)
- Title/Author/Comment:
- XMP: `Xmp.dc.title`, `Xmp.dc.creator`, `Xmp.dc.description`
- EXIF: `Exif.Image.ImageDescription`, `Exif.Image.Artist`, `Exif.Photo.UserComment`
- **Windows XP* Unicode**: `Exif.Image.XPTitle`, `Exif.Image.XPAuthor`, `Exif.Image.XPComment` (UTF-16LE)
## Notes
- Exiv2 CLI build is disabled: `-DEXIV2_BUILD_EXIV2_COMMAND=OFF`
- BMFF/HEIF support enabled: `-DEXIV2_ENABLE_BMFF=ON`
- If submodules show empty after clone:
`git submodule update --init --recursive`