{
  "schema": "leumas.docs.page/1",
  "id": "pkg:@leumas/adapter-meme",
  "slug": "adapters/adapter-meme",
  "kind": "capabilities",
  "bucket": "package",
  "title": "@leumas/adapter-meme",
  "name": "@leumas/adapter-meme",
  "eyebrow": null,
  "chip": null,
  "summary": "Meme / image-caption adapter — impact-text captions, meme templates (Drake, Distracted Boyfriend, Galaxy Brain, …), demotivational posters, free-form text, and a deep-fry filter. Text is drawn as SVG...",
  "keywords": [
    "adapter-meme",
    "meme",
    "image-caption",
    "drake",
    "distracted",
    "leumas adapter meme",
    "boyfriend",
    "demotivational"
  ],
  "audience": "both",
  "funnel": {
    "product": null,
    "cta": null
  },
  "body": "# @leumas/adapter-meme\n\nMeme / image-caption generator. Give it a source **image** (base64 or `data:` URL) and it hands\nback a PNG (base64 + data-URL) with meme text baked on.\n\nText is drawn with **no `canvas` dependency**: every tool builds an **SVG** string (font, stroke,\nword-wrap and auto-fit computed in pure JS) and hands it to **`sharp.composite()`**, which\nrasterizes the SVG over the decoded image and re-encodes to PNG.\n\n## Tools\n\n| Tool | Input | Output |\n|---|---|---|\n| `caption` | `{ image, top?, bottom?, options? }` | Classic white impact top/bottom text with black stroke, auto-fit + wrapped. |\n| `fromTemplate` | `{ template, texts, image?, options? }` | Fill a named template's zones. `texts` is an object keyed by zone key/alias, or an array in zone order. Without `image`, a flat template background is generated. |\n| `demotivational` | `{ image, title?, subtitle?, options? }` | Black poster: photo inset with a thin white keyline, serif title + subtitle below. |\n| `addText` | `{ image, text, x?, y?, size?, color?, options? }` | Free-form caption. `x`/`y` accept absolute px or a `0..1` fraction (default centered). |\n| `addBottomBar` | `{ image, text, options? }` | Reddit/Tumblr-style solid caption bar added **above** (`position:'top'`, default) or below the image. |\n| `deepFry` | `{ image, options: { level: 1..5 } }` | Crunchy over-saturated \"deep-fried\" filter (crush → resharpen → saturate → JPEG-crush). |\n| `blankTemplate` | `{ template }` | Render a template's empty background canvas. |\n| `imageInfo` | `{ image }` | Decode and report `{ width, height, format, bytes, hasAlpha }`. |\n| `listTemplates` | *(none)* | **Pure.** Catalog of templates with their zones. |\n| `describeTemplate` | `{ template }` | **Pure.** Full zone geometry for one template. |\n| `layoutMeme` | `{ template, texts }` or `{ text, width, height }` | **Pure.** Preview where captions land (fontSize + wrapped lines per zone) without rasterizing. |\n\nTemplates: `drake`, `distracted-boyfriend`, `two-buttons`, `expanding-brain`,\n`change-my-mind`, `one-does-not-simply` (see `data/templates.json`).\n\n## Usage\n\n```js\nimport meme from '@leumas/adapter-meme'; // or the registry: registry.run('meme', 'caption', args)\n\nconst src = fs.readFileSync('cat.png').toString('base64');\n\nconst out = await meme.adapters.caption({ image: src, top: 'one does not simply', bottom: 'make a meme' });\n// -> { ok:true, format:'png', base64, dataUrl, bytes, width, height }\n\nawait meme.adapters.fromTemplate({ template: 'drake', texts: { top: 'writing my own text renderer', bottom: 'building an SVG and letting sharp do it' } });\n\nmeme.adapters.listTemplates();           // pure — no image, no sharp needed\n```\n\nEach POST body maps 1:1 onto a call (one `args` object per tool).\n\n## Dependency & lazy guard\n\nThe only heavy dependency is **`sharp`** (installed at the workspace root, **shared with the `image`\npack** — not re-installed here). It is imported **lazily and guarded**: this module always *loads*\neven if the native `sharp` binary is unavailable. The image tools then return\n`{ ok:false, unavailable:true, error:'sharp not installed' }`, while the pure tools\n(`listTemplates`, `describeTemplate`, `layoutMeme`) keep working.\n\n## DRY boundary\n\n- **This pack captions/decorates raster IMAGES.** It does **not** do ASCII art or emoji art — that\n  is *text* and lives in the ascii / emoji text packs.\n- It does **not** do general image resize / format-convert / color filters — that is the **`image`**\n  pack. `meme` is the meme-specific layer (impact text, meme templates, demotivational frames,\n  deep-fry) built on top of the same shared `sharp`.\n- No `canvas`/`jimp` dependency is added: text rendering is SVG → `sharp.composite`.\n",
  "source": {
    "path": "shared/engines/adapters/domain/meme/README.md",
    "blobSha": "",
    "commit": "",
    "committedAt": "",
    "provenance": "no-git",
    "bytes": 3970,
    "hash": "9b088139b88108e5b0f926e610e6f99940799f84"
  },
  "urls": {
    "html": "/p/adapters/adapter-meme",
    "json": "/docs/adapters/adapter-meme.json",
    "md": "/docs/adapters/adapter-meme.md"
  },
  "links": {
    "composes": [],
    "usedBy": [],
    "product": [],
    "howTo": [],
    "skills": []
  },
  "exports": null
}
