--- pr: 161 title: "fix(draw-image): use density 72 for 1024x1024 PNG output" --- ## Что сделано Фикс размера PNG draw-image (issue #159) — три изменения: 1. **density 96 → 72** — `.opencode/draw-image/render.mjs:36`: `sharp(Buffer.from(svg), { density: 72 })`. При density:96 SVG 1024×1024 (по `viewBox`, 1in=72pt) рендерился как 1024 × 96/72 = 1365px. При density:72 → 1024 × 72/72 = 1024px ровно. 2. **Реальные assertions размеров PNG** — пустые проверки заменены на decode PNG IHDR chunk (width bytes 16-19, height bytes 20-23, big-endian uint32): - `tests/e2e.test.ts` `assertPng1024` — добавлены `expect(width).toBe(1024)` + `expect(height).toBe(1024)`. - `tests/render.integration.test.ts` тест `"renders valid PNG 1024x1024"` — добавлены width/height assertions (были только magic bytes). - `tests/render.optional.integration.test.ts` `assertPng` — добавлены width/height assertions. 3. **Регенерация assets/cover.png** — удалены старые `assets/cover.png` + `cover.meta.json`, перерендерены через `draw-image` tool (template=cover, icon=opencode, subtitle). Проверено через `sharp().metadata()`: 1024×1024 (было 1365×1365). Тесты: `npx vitest run` в `.opencode/draw-image/` → 55/55 passed (5 прогонов подряд стабильно). Все обновлённые assertions реально проверяют 1024×1024 через IHDR decode. ## Почему `density: 96` — историческое значение, дававшее 33% оверсайз (1365 вместо 1024). PNG cover используется в README и slaid098.dev showcase — 1365px ломал layout и не соответствовал контракту `meta.size: 1024` в `cli.ts:78`. Пустые assertions (только magic bytes, без width/height) маскировали баг — тесты «проходили» на 1365×1365, хотя в имени функции буквально `assertPng1024`. Density 72 — canonical значение для SVG-to-PNG при `viewBox` в pt (1pt = 1/72in), дающее pixel-perfect 1024×1024. ## Pending — ## Watch out - **Существующий race condition на `.tmp-render.svg`** — `cli.ts:64` пишет shared temp SVG в `__dirname`. При параллельных CLI-вызовах (vitest concurrent) или leftover от ручных прогонов → `render.mjs` падает с `Input Buffer is empty`. Это **отдельный баг**, заведён issue #160 (вне scope #159). На стабильных прогонах (clean tmp) не проявляется. Не чинить в этом PR. - Density-фикс **меняет хэш** всех PNG (`computeHash` не зависит от density, но `generatedAt` и пиксели differ) → все cached PNG будут перерендерены при следующем вызове CLI (meta.json hash не меняется, но если файл удалён — re-render). `cover.meta.json` перегенерирован. - `buf.readUInt32BE(16)` / `buf.readUInt32BE(20)` — PNG IHDR fixed offset (8 signature + 4 length + 4 "IHDR" = 16 для width, 20 для height). Работает только для стандартных PNG (sharp всегда пишет IHDR первым chunk'ом).