From 28947a0caae7c7135dae8fa233efcd90a9a4eed6 Mon Sep 17 00:00:00 2001 From: peptiderepo Date: Thu, 11 Jun 2026 12:31:03 +0700 Subject: [PATCH] =?UTF-8?q?feat:=20deterministic=20image=20composer=20?= =?UTF-8?q?=E2=80=94=20OG=20+=20square=20variants,=20branding,=20baked=20c?= =?UTF-8?q?aption=20(v0.17.0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 2 (final scope) of the in-plugin editorial image pipeline (ARCHITECTURE.md #21). A deterministic local composer now runs between provider bytes and sideload: Image A yields a corner-marked featured (1200x632) + branded OG (1200x630, teal band, logo, baked caption in Poppins SemiBold) + square card (1080x1080, cream panel, Poppins Bold, teal footer with the reverse lockup); Image B gets the corner mark only. - Capability ladder: imagick (full compositing) -> wp_get_image_editor resize-only (unbranded, WARNING once) -> pass-through (WARNING once); probe cached in prautoblogger_image_compose_capability, fingerprinted by PHP version + imagick/gd presence so host flips auto-invalidate. compose() never throws; composition can never fail a publish. - Variants sideloaded with a role filename suffix; attachment meta _prautoblogger_image_role + _prautoblogger_base_attachment_id; post meta _prautoblogger_og_image_id (frozen for the rebuilt SEO stage) + _prautoblogger_square_image_id. No cost-tracker rows ($0 local stage; duration logged on the image_composer channel). - New Images settings: compose_enabled ('1'), compose_variants ('og,square'), featured_mark_enabled ('1') — read at point of use. Geometry ships as Layout defaults behind the prautoblogger_image_compose_layout filter. - Vendored brand rasters (assets/brand) + bundled OFL Poppins faces (assets/fonts) for per-environment byte-stable output (stripImage + PNG date/time chunks excluded). Validated on Hostinger PHP 8.3.30 / ImageMagick 7.1.1-43: php -l clean on all touched files, double render byte-identical (sha256), exact variant geometry. - Fix: featured/Image B alt text is the editorial caption (was the model name); model still persists in _prautoblogger_image_model. - Image_Attacher extracted from Image_Pipeline (300-line compliance); sideloader gains an optional filename suffix param (back-compat ''). - Version 0.16.0 -> 0.17.0 + CHANGELOG + ARCHITECTURE.md #21 + CONVENTIONS.md composer guide in this commit. Unit tests: layout clamp boundaries, capability ladder, cache invalidation, attacher meta-linking, pipeline integration, imagick determinism (gated). Agent-Session: engineer-prautoblogger-image-composer-20260611 --- ARCHITECTURE.md | 69 ++++ CHANGELOG.md | 48 +++ CONVENTIONS.md | 32 ++ assets/brand/logo-horizontal-reverse-128.png | Bin 0 -> 6887 bytes assets/brand/logo-horizontal-reverse-256.png | Bin 0 -> 14237 bytes assets/brand/logo-mark-small-112.png | Bin 0 -> 2956 bytes assets/brand/logo-mark-small-56.png | Bin 0 -> 1569 bytes assets/fonts/OFL.txt | 93 +++++ assets/fonts/Poppins-Bold.ttf | Bin 0 -> 155996 bytes assets/fonts/Poppins-SemiBold.ttf | Bin 0 -> 157312 bytes .../admin/class-settings-fields-extended.php | 24 ++ includes/core/class-image-attacher.php | 169 ++++++++++ includes/core/class-image-composer-canvas.php | 186 ++++++++++ includes/core/class-image-composer-editor.php | 114 +++++++ .../core/class-image-composer-imagick.php | 250 ++++++++++++++ includes/core/class-image-composer-layout.php | 173 ++++++++++ includes/core/class-image-composer.php | 298 ++++++++++++++++ .../core/class-image-media-sideloader.php | 16 +- includes/core/class-image-pipeline.php | 211 +++++------- includes/core/interface-image-composer.php | 50 +++ prautoblogger.php | 4 +- tests/unit/Core/ImageAttacherTest.php | 176 ++++++++++ tests/unit/Core/ImageComposerImagickTest.php | 104 ++++++ tests/unit/Core/ImageComposerLayoutTest.php | 144 ++++++++ tests/unit/Core/ImageComposerTest.php | 317 ++++++++++++++++++ tests/unit/Core/ImagePipelineTest.php | 66 ++++ 26 files changed, 2416 insertions(+), 128 deletions(-) create mode 100644 assets/brand/logo-horizontal-reverse-128.png create mode 100644 assets/brand/logo-horizontal-reverse-256.png create mode 100644 assets/brand/logo-mark-small-112.png create mode 100644 assets/brand/logo-mark-small-56.png create mode 100644 assets/fonts/OFL.txt create mode 100644 assets/fonts/Poppins-Bold.ttf create mode 100644 assets/fonts/Poppins-SemiBold.ttf create mode 100644 includes/core/class-image-attacher.php create mode 100644 includes/core/class-image-composer-canvas.php create mode 100644 includes/core/class-image-composer-editor.php create mode 100644 includes/core/class-image-composer-imagick.php create mode 100644 includes/core/class-image-composer-layout.php create mode 100644 includes/core/class-image-composer.php create mode 100644 includes/core/interface-image-composer.php create mode 100644 tests/unit/Core/ImageAttacherTest.php create mode 100644 tests/unit/Core/ImageComposerImagickTest.php create mode 100644 tests/unit/Core/ImageComposerLayoutTest.php create mode 100644 tests/unit/Core/ImageComposerTest.php diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index e4e4b0e..374c0fb 100755 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -75,6 +75,11 @@ PRAutoBlogger is a WordPress plugin that monitors social media (starting with Re │ │ retried once with a rule-based fallback │ │ prompt via Image_NSFW_Retry; a second block │ │ logs WARNING and publishes without that image. +│ │ v0.17.0: results then pass through the local +│ │ deterministic Image_Composer (corner mark + +│ │ branded OG/square variants for Image A; mark +│ │ only for Image B) with a capability ladder +│ │ (imagick → GD resize-only → pass-through). │ │ Sideloads to media library, logs costs. └────────────┬────────────┘ │ @@ -103,6 +108,15 @@ prautoblogger/ ├── CHANGELOG.md # Semantic versioning changelog │ ├── assets/ +│ ├── brand/ # Vendored brand PNGs rasterized from peptide-repo-brand SVGs (composer overlays) +│ │ ├── logo-mark-small-56.png # Corner mark, 1x +│ │ ├── logo-mark-small-112.png # Corner mark / OG band logo, 2x +│ │ ├── logo-horizontal-reverse-128.png # Square footer lockup, 1x +│ │ └── logo-horizontal-reverse-256.png # Square footer lockup, 2x +│ ├── fonts/ # Bundled OFL faces for deterministic text rendering +│ │ ├── Poppins-Bold.ttf # Square-card caption face +│ │ ├── Poppins-SemiBold.ttf # OG band caption face +│ │ └── OFL.txt # SIL Open Font License │ ├── css/ │ │ ├── admin.css # Admin page styles (wp-admin conventions) │ │ └── posts-widget.css # Frontend posts widget styles (uses theme CSS vars) @@ -148,6 +162,13 @@ prautoblogger/ │ │ ├── class-image-pipeline.php # Orchestrates A/B image generation (parallel via batch) │ │ ├── class-image-prompt-builder.php # Generates visual prompts from article/source data │ │ ├── class-image-template-filler.php # Fills the editorial style template's {{ topic_summary }} slot (v0.16.0) +│ │ ├── interface-image-composer.php # Contract: provider bytes → featured-first variant list (v0.17.0) +│ │ ├── class-image-composer.php # Composer orchestrator: cached capability probe + degradation ladder +│ │ ├── class-image-composer-imagick.php # Imagick renderer: corner mark, OG band, square card (WP-free) +│ │ ├── class-image-composer-canvas.php # Imagick primitives: smoke test, cover-crop, logo, deterministic PNG (WP-free) +│ │ ├── class-image-composer-editor.php # Resize-only rung via wp_get_image_editor (GD path) +│ │ ├── class-image-composer-layout.php # Geometry defaults + caption clamp (pure, WP-free) +│ │ ├── class-image-attacher.php # Sideload + cost log + variant meta-linking + caption prepend │ │ ├── class-image-media-sideloader.php # Imports images into WordPress media library │ │ ├── class-cost-tracker.php # Logs all API costs, enforces budget limits │ │ ├── class-logger.php # Structured logging singleton (error/warning/info/debug) @@ -448,6 +469,10 @@ All prefixed with `prautoblogger_`: | `prautoblogger_image_style_suffix` | DEPRECATED (v0.16.0). Former comic style suffix appended to every image prompt; no longer read by the builder. Mirrored to `prautoblogger_image_style_suffix_deprecated` for one cycle and not deleted yet | | `prautoblogger_openrouter_model_registry` | Normalized OpenRouter model list (JSON array, daily refresh, serves as durable cache) | | `prautoblogger_openrouter_model_registry_fetched_at` | Unix timestamp of last successful model registry refresh | +| `prautoblogger_image_compose_enabled` | Toggle: run the deterministic image composer (default '1'; auto-degrades, never blocks publishing) | +| `prautoblogger_image_compose_variants` | Comma list of composed variants for Image A (default 'og,square'; whitelisted at point of use) | +| `prautoblogger_image_featured_mark_enabled` | Toggle: subtle corner mark on featured images (default '1') | +| `prautoblogger_image_compose_capability` | Cached capability probe `{fingerprint, capability}`; fingerprint = PHP version + imagick/gd presence, so it auto-invalidates on host changes | ### Post Meta @@ -466,6 +491,14 @@ Stored on every PRAutoBlogger-generated post: | `_prautoblogger_editor_notes` | Chief editor's review notes | | `_prautoblogger_generated_at` | ISO 8601 timestamp of generation | | `_prautoblogger_research_sources` | JSON array of source URLs used | +| `_prautoblogger_og_image_id` | Attachment ID of the composed 1200×630 OG variant (v0.17.0; the rebuilt SEO stage emits `og:image` from this — key name frozen) | +| `_prautoblogger_square_image_id` | Attachment ID of the composed 1080×1080 square variant (v0.17.0; stored now, no consumer yet) | + +Composed-variant **attachment** meta (v0.17.0): every pipeline attachment gets +`_prautoblogger_image_role` (`featured`/`og`/`square`); OG/square variants also +get `_prautoblogger_base_attachment_id` pointing at the featured attachment +they derive from. All keys keep the `_prautoblogger_` prefix, so the uninstall +prefix-sweep purges them. --- @@ -587,6 +620,42 @@ Commit 1 of the in-plugin editorial image pipeline (brief `docs/proposals/2026-0 Provider/model are unchanged — only the prompt text changes, so production keeps emitting via Runware FLUX.1 schnell but now produces text-free editorial base images. +### #21: Deterministic PHP image composer with capability ladder (v0.17.0, Jun 2026) + +Commit 2 (and final scope) of the in-plugin editorial image pipeline (#20). Small diffusion +models cannot render clean text (2026-04-21 A/B finding), so base images are text-free — +but the one image that travels *without* its HTML caption is the social/OG share image. +A **deterministic local composer** (`PRAutoBlogger_Image_Composer` behind +`PRAutoBlogger_Image_Composer_Interface`) now runs between provider bytes and sideload: + +1. **Variants.** Image A → corner-marked featured (1200×632) + branded OG (1200×630, teal + band, brand mark, baked caption in Poppins SemiBold) + square card (1080×1080: full base + downscaled into a 1080×569 top slice — no upscaling — cream caption panel in Poppins + Bold, teal footer with the horizontal reverse lockup). Image B → corner mark only. The + variant set is config-driven (`prautoblogger_image_compose_variants`, default `og,square`; + square has no consumer yet — CEO wants it rendered + stored now). Variants are + meta-linked via `_prautoblogger_image_role`, `_prautoblogger_base_attachment_id`, + `_prautoblogger_og_image_id`, `_prautoblogger_square_image_id`. The og key is **frozen**: + the rebuilt SEO stage (pipeline-redesign §4.8) emits `og:image` from it. +2. **Capability ladder, never fatal.** Imagick is probed once (PNG format + TTF annotate + smoke test) and cached in `prautoblogger_image_compose_capability`, fingerprinted by + PHP version + imagick/gd presence so host flips (hPanel/cagefs — it has happened) + auto-invalidate the cache. Rungs: imagick → `wp_get_image_editor()` resize-only + (unbranded, square skipped because it would upscale) → pass-through. Every render is + try/caught; degradation logs one WARNING per run; the publish always proceeds with the + editable HTML caption intact. Test/ops override: `prautoblogger_image_compose_capability` + filter. +3. **Determinism.** Bundled OFL fonts (no system-font queries), vendored brand PNGs (no + runtime SVG delegate variance), `stripImage()` + PNG date/time chunks excluded + fixed + compression → byte-stable output **per environment** (not across ImageMagick versions). + Composition is local CPU, $0, logged as a duration-only stage on the `image_composer` + channel — no cost-tracker rows. +4. **Layout tuning.** Geometry/opacity are constants in + `PRAutoBlogger_Image_Composer_Layout::defaults()` behind the + `prautoblogger_image_compose_layout` filter — deliberately not settings; promote only if + the CEO asks to tune them. The caption is hard-clamped (word-boundary wrap + ellipsis, + multibyte-safe) even though the rewriter LLM is already constrained to <15 words. + ### #17: Runware as default image backend (v0.9.0, Apr 2026) After a comic-style A/B round in Apr 2026, FLUX.1 schnell via Runware was chosen as the default image backend. Cost is ~$0.0006/image vs ~$0.039/image for Gemini 2.5 Flash Image via OpenRouter (≈65× cheaper), and the looser schnell fidelity reads as editorial-cartoon style — a feature, not a bug, for our single-panel comic aesthetic. FLUX.1 dev (~$0.02/image, 28 steps) remains available as an opt-in for higher-fidelity runs. The Runware layer mirrors the OpenRouter split (interface + provider + support + pricing + batch), all under the 300-line cap. The batch class dispatches `imageInference` POSTs via `curl_multi_init/exec/select` — wall-clock time for the A/B pair drops to ≈ the slowest single image. A v0.9.0 one-time migration (`prautoblogger_migrated_default_image_v090`) flips sites still on the legacy default; explicit user selections are preserved. Cloudflare Workers AI was removed as an image provider in v0.10.0 (see ADR #16 note below). diff --git a/CHANGELOG.md b/CHANGELOG.md index c714ac4..95ac26d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,54 @@ All notable changes to PRAutoBlogger will be documented in this file. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project uses [Semantic Versioning](https://semver.org/). +## [0.17.0] - 2026-06-11 + +### Added +- **Deterministic PHP image composer (Commit 2 of the in-plugin editorial image + pipeline).** Every generated image now passes through a local, $0, + deterministic composer between the provider's bytes and the media sideloader. + Image A yields a corner-marked featured image (1200×632) plus a branded + **OG/social variant** (1200×630, teal band, brand mark, baked editorial + caption in Poppins SemiBold) and a **square card variant** (1080×1080, image + slice + cream caption panel in Poppins Bold + teal footer with the horizontal + lockup). Image B gets the corner mark only. Variants are sideloaded with a + role filename suffix and meta-linked: attachment meta + `_prautoblogger_image_role` + `_prautoblogger_base_attachment_id`, post meta + `_prautoblogger_og_image_id` + `_prautoblogger_square_image_id` (the rebuilt + SEO stage will emit `og:image` from the frozen og key). +- **Capability ladder with cached probe.** Imagick (full compositing) → + `wp_get_image_editor()` GD path (resize-only, unbranded, single WARNING) → + pass-through (single WARNING). The probe result is cached in + `prautoblogger_image_compose_capability`, fingerprinted by PHP version + + imagick/gd presence so it auto-invalidates when the host changes. Override + filter: `prautoblogger_image_compose_capability`. Composition failure can + never block a publish (per-render try/catch + pipeline safety net). +- New Images settings: `prautoblogger_image_compose_enabled` (default on), + `prautoblogger_image_compose_variants` (default `og,square`), + `prautoblogger_image_featured_mark_enabled` (default on). Geometry ships as + layout-class constants behind the `prautoblogger_image_compose_layout` + filter. All keys keep the `prautoblogger_`/`_prautoblogger_` prefixes, so + the existing uninstall prefix-sweep purges them (no uninstall.php changes). +- Vendored brand rasters (`assets/brand/`, from peptide-repo-brand SVG masters) + and bundled OFL fonts (`assets/fonts/`: Poppins Bold + Poppins SemiBold + + OFL.txt) — no system-font or runtime-SVG dependency, byte-stable output per + environment (metadata stripped, PNG date/time chunks excluded). + +### Fixed +- Featured/Image B **alt text is now the editorial caption** instead of the + generation model name (`sideload_and_log()` passed the model as `$alt_text`, + contradicting the sideloader docblock). The model keeps persisting in + `_prautoblogger_image_model`. + +### Changed +- `PRAutoBlogger_Image_Media_Sideloader::sideload_image()` / + `generate_filename()` accept an optional filename role suffix + (back-compat default `''`). +- `PRAutoBlogger_Image_Pipeline` delegates sideload/cost-log, caption + prepending, and variant persistence to the new + `PRAutoBlogger_Image_Attacher` (300-line compliance); also declares the + previously-dynamic `$trace_context` property (PHP 8.2 deprecation). + ## [0.16.0] - 2026-05-29 ### Changed diff --git a/CONVENTIONS.md b/CONVENTIONS.md index e260229..495ab6f 100644 --- a/CONVENTIONS.md +++ b/CONVENTIONS.md @@ -150,6 +150,38 @@ interface PRAutoBlogger_Image_Provider_Interface { --- +## How To: Tune the Image Composer (v0.17.0+) + +The deterministic composer (ARCHITECTURE.md #21) renders branded variants between +provider bytes and sideload. Three rules when touching it: + +1. **Geometry lives in code, not settings.** All sizes/colors/opacities come from + `PRAutoBlogger_Image_Composer_Layout::defaults()` and can be overridden via the + `prautoblogger_image_compose_layout` filter: + ```php + add_filter( 'prautoblogger_image_compose_layout', function ( array $layout ): array { + $layout['featured']['mark_opacity'] = 0.40; + return $layout; + } ); + ``` + Promote a value to a settings field only on explicit CEO request. +2. **Keep renderer classes WP-free.** `Image_Composer_Imagick`, `Image_Composer_Canvas`, + and `Image_Composer_Layout` must not call WordPress functions — they are exercised by + a standalone render harness (and the determinism test) without a WP bootstrap. Only + the orchestrator (`Image_Composer`) and the GD rung (`Image_Composer_Editor`) may + touch WP APIs. +3. **Never fatal a publish.** Any new render path needs a try/catch that degrades to + pass-through and logs at most one WARNING per run (`warn_once()`). Capability checks + go through the cached probe (`prautoblogger_image_compose_capability` option + + same-named override filter) — never re-probe per image. + +Adding a new variant role: extend `Layout::defaults()` + the renderer, whitelist the +role in `Image_Composer::configured_variants()` and in +`Image_Attacher::ROLE_POST_META` (new `_prautoblogger_{role}_image_id` post meta keeps +the uninstall prefix-sweep effective), then cover it in the unit ladder tests. + +--- + ## How To: Add a New Admin Setting 1. Define the option key in the settings array in `admin/class-admin-page.php` → `get_settings_fields()` method diff --git a/assets/brand/logo-horizontal-reverse-128.png b/assets/brand/logo-horizontal-reverse-128.png new file mode 100644 index 0000000000000000000000000000000000000000..379362ad1f41c7f3e5abf7afb66ebfec17ca0f4b GIT binary patch literal 6887 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 literal 0 HcmV?d00001 diff --git a/assets/brand/logo-mark-small-112.png b/assets/brand/logo-mark-small-112.png new file mode 100644 index 0000000000000000000000000000000000000000..fa250f4a2c577be2f7dc64919d570d1bda297042 GIT binary patch literal 2956 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 Tv63d|00000NkvXXu0mjfD2VEa literal 0 HcmV?d00001 diff --git a/assets/fonts/OFL.txt b/assets/fonts/OFL.txt new file mode 100644 index 0000000..76df3b5 --- /dev/null +++ b/assets/fonts/OFL.txt @@ -0,0 +1,93 @@ +Copyright 2020 The Poppins Project Authors (https://github.com/itfoundry/Poppins) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/assets/fonts/Poppins-Bold.ttf b/assets/fonts/Poppins-Bold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..1982f38ab21303459aa1155265052ca599fa58d1 GIT binary patch literal 155996 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 literal 0 HcmV?d00001 diff --git a/assets/fonts/Poppins-SemiBold.ttf b/assets/fonts/Poppins-SemiBold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..c30ad104723a0e6e00e54768626cb02c5fdf6aee GIT binary patch literal 157312 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 literal 0 HcmV?d00001 diff --git a/includes/admin/class-settings-fields-extended.php b/includes/admin/class-settings-fields-extended.php index 3b903a8..805687e 100644 --- a/includes/admin/class-settings-fields-extended.php +++ b/includes/admin/class-settings-fields-extended.php @@ -258,6 +258,30 @@ class PRAutoBlogger_Settings_Fields_Extended { 'default' => '1', 'description' => __( 'When the provider rejects an image prompt as NSFW, retry once with a generic fallback scene built from the article title. Disable to fail fast if the filter gets trigger-happy.', 'prautoblogger' ), ), + array( + 'id' => 'prautoblogger_image_compose_enabled', + 'label' => __( 'Compose Branded Variants', 'prautoblogger' ), + 'type' => 'toggle', + 'section' => 'prautoblogger_images', + 'default' => '1', + 'description' => __( 'Render branded social variants (teal band, logo, baked caption) from each generated image via the local deterministic composer. Auto-degrades to plain resizing or pass-through if the server lacks an image library — publishing is never blocked.', 'prautoblogger' ), + ), + array( + 'id' => 'prautoblogger_image_compose_variants', + 'label' => __( 'Composed Variant Set', 'prautoblogger' ), + 'type' => 'text', + 'section' => 'prautoblogger_images', + 'default' => 'og,square', + 'description' => __( 'Comma-separated variants to render for Image A. Supported: og (1200×630 social share), square (1080×1080 card). Unknown values are ignored.', 'prautoblogger' ), + ), + array( + 'id' => 'prautoblogger_image_featured_mark_enabled', + 'label' => __( 'Featured Image Corner Mark', 'prautoblogger' ), + 'type' => 'toggle', + 'section' => 'prautoblogger_images', + 'default' => '1', + 'description' => __( 'Overlay a small semi-transparent Peptide Repo mark in the bottom-right corner of featured images, so scraped or re-shared images carry attribution.', 'prautoblogger' ), + ), array( 'id' => 'prautoblogger_runware_model_catalog', 'label' => __( 'Runware Model Catalog', 'prautoblogger' ), diff --git a/includes/core/class-image-attacher.php b/includes/core/class-image-attacher.php new file mode 100644 index 0000000..fca1323 --- /dev/null +++ b/includes/core/class-image-attacher.php @@ -0,0 +1,169 @@ + '_prautoblogger_og_image_id', + 'square' => '_prautoblogger_square_image_id', + ); + + /** @var PRAutoBlogger_Image_Media_Sideloader Imports bytes into the media library. */ + private PRAutoBlogger_Image_Media_Sideloader $sideloader; + + /** @var PRAutoBlogger_Cost_Tracker Logs primary image generation spend. */ + private PRAutoBlogger_Cost_Tracker $cost_tracker; + + /** + * Construct with the pipeline's sideloader + cost tracker. + * + * @param PRAutoBlogger_Image_Media_Sideloader $sideloader Media importer. + * @param PRAutoBlogger_Cost_Tracker $cost_tracker Spend logger. + */ + public function __construct( PRAutoBlogger_Image_Media_Sideloader $sideloader, PRAutoBlogger_Cost_Tracker $cost_tracker ) { + $this->sideloader = $sideloader; + $this->cost_tracker = $cost_tracker; + } + + /** + * Sideload a primary image into the media library and log its cost. + * + * Side effects: media import, cost-tracker row, info log. + * + * @param array $image_data Provider result with bytes, model, cost_usd. + * @param int $post_id Post ID. + * @param string $slot Cost-tracking slot ('image_a' or 'image_b'). + * @param string $label Human-readable label for logs. + * @param string $alt_text Alt text — the editorial caption (v0.17.0 bug + * fix: previously the model name leaked here). + * + * @return int|\WP_Error Attachment ID on success. + */ + public function sideload_and_log( array $image_data, int $post_id, string $slot, string $label, string $alt_text = '' ) { + $attachment_id = $this->sideloader->sideload_image( $image_data, $post_id, $alt_text ); + + if ( is_wp_error( $attachment_id ) ) { + return $attachment_id; + } + + $this->cost_tracker->log_image_generation( + $image_data['cost_usd'], + $image_data['model'] ?? 'unknown', + $post_id, + $slot + ); + + PRAutoBlogger_Logger::instance()->info( + sprintf( '%s for post %d (att %d, $%.4f)', $label, $post_id, $attachment_id, $image_data['cost_usd'] ), + 'image_pipeline' + ); + + return $attachment_id; + } + + /** + * Sideload composed variants and meta-link them to post + base image. + * + * Each variant attachment gets `_prautoblogger_image_role` and + * `_prautoblogger_base_attachment_id`; the post gets the role-specific + * ID meta (see ROLE_POST_META). No cost-tracker entries — composition is + * a $0 local stage. A failed variant sideload logs a WARNING and is + * skipped; it never blocks the publish. + * + * @param array $variants Composed variants (role 'og'/'square' only are persisted). + * @param int $post_id Post ID. + * @param int $base_attachment_id Featured attachment the variants derive from. + * @param string $caption Editorial caption (used as variant alt text). + * @param string $model Generation model recorded on the variant attachment. + */ + public function attach_variants( array $variants, int $post_id, int $base_attachment_id, string $caption, string $model ): void { + foreach ( $variants as $variant ) { + $role = (string) ( $variant['role'] ?? '' ); + if ( ! isset( self::ROLE_POST_META[ $role ] ) ) { + continue; + } + + $attachment_id = $this->sideloader->sideload_image( + array( + 'bytes' => (string) ( $variant['bytes'] ?? '' ), + 'mime_type' => (string) ( $variant['mime_type'] ?? 'image/png' ), + 'width' => (int) ( $variant['width'] ?? 0 ), + 'height' => (int) ( $variant['height'] ?? 0 ), + 'model' => $model, + 'cost_usd' => 0.0, + 'latency_ms' => 0, + ), + $post_id, + $caption, + $role + ); + + if ( is_wp_error( $attachment_id ) ) { + PRAutoBlogger_Logger::instance()->warning( + sprintf( 'Failed to sideload %s variant for post %d: %s', $role, $post_id, $attachment_id->get_error_message() ), + 'image_composer' + ); + continue; + } + + update_post_meta( $attachment_id, '_prautoblogger_image_role', $role ); + update_post_meta( $attachment_id, '_prautoblogger_base_attachment_id', $base_attachment_id ); + update_post_meta( $post_id, self::ROLE_POST_META[ $role ], $attachment_id ); + + PRAutoBlogger_Logger::instance()->info( + sprintf( '%s variant for post %d (att %d, %dx%d)', $role, $post_id, $attachment_id, (int) $variant['width'], (int) $variant['height'] ), + 'image_composer' + ); + } + } + + /** + * Prepend the editorial caption as styled text at the top of the post. + * + * Only inserts the caption — NOT the image. The theme already displays + * the featured image, so embedding it again would cause a duplicate. + * + * @param int $post_id Post ID. + * @param int $attachment_id Featured image attachment ID (unused; kept for parity with meta storage). + * @param string $caption Caption text shown below the image. + */ + public function prepend_caption_to_post( int $post_id, int $attachment_id, string $caption ): void { + $post = get_post( $post_id ); + if ( ! $post ) { + return; + } + + // Caption-only block — the theme handles the featured image display. + $caption_html = sprintf( + '

— "%s"

', + esc_html( $caption ) + ); + + wp_update_post( + array( + 'ID' => $post_id, + 'post_content' => $caption_html . "\n\n" . $post->post_content, + ) + ); + } +} diff --git a/includes/core/class-image-composer-canvas.php b/includes/core/class-image-composer-canvas.php new file mode 100644 index 0000000..6b893d1 --- /dev/null +++ b/includes/core/class-image-composer-canvas.php @@ -0,0 +1,186 @@ +newImage( 8, 8, new \ImagickPixel( '#ffffff' ), 'png' ); + $draw = new \ImagickDraw(); + $draw->setFont( $font_path ); + $draw->setFontSize( 6 ); + $probe->annotateImage( $draw, 1, 6, 0.0, 'P' ); + $probe->clear(); + + return true; + } catch ( \Throwable $e ) { + return false; + } + } + + /** + * Load base image bytes into an Imagick object (first frame only). + * + * @param string $bytes Raw image bytes (PNG/JPEG/WebP — any readable format). + * + * @return \Imagick Loaded image. + * @throws \RuntimeException When the bytes cannot be decoded. + */ + public static function load_base( string $bytes ): \Imagick { + $image = new \Imagick(); + if ( ! $image->readImageBlob( $bytes ) ) { + throw new \RuntimeException( 'Composer could not decode base image bytes.' ); + } + $image->setIteratorIndex( 0 ); + $image = $image->coalesceImages()->current(); + $image->setImagePage( 0, 0, 0, 0 ); + + return $image; + } + + /** + * Center cover-crop an image to exact target dimensions without upscaling. + * + * Crops the largest centered region matching the target aspect ratio, + * then scales DOWN to the target. Refuses (returns false) when the source + * is too small to fill the target without upscaling. + * + * Side effects: mutates $image in place on success. + * + * @param \Imagick $image Source image (mutated). + * @param int $target_w Target width in px. + * @param int $target_h Target height in px. + * + * @return bool False when the crop would require upscaling. + */ + public static function cover_crop( \Imagick $image, int $target_w, int $target_h ): bool { + $width = $image->getImageWidth(); + $height = $image->getImageHeight(); + if ( $width < 1 || $height < 1 ) { + return false; + } + + $ratio = max( $target_w / $width, $target_h / $height ); + if ( $ratio > 1.0 ) { + return false; + } + + $src_w = min( $width, (int) round( $target_w / $ratio ) ); + $src_h = min( $height, (int) round( $target_h / $ratio ) ); + $x = (int) floor( ( $width - $src_w ) / 2 ); + $y = (int) floor( ( $height - $src_h ) / 2 ); + + $image->cropImage( $src_w, $src_h, $x, $y ); + $image->setImagePage( 0, 0, 0, 0 ); + $image->resizeImage( $target_w, $target_h, \Imagick::FILTER_LANCZOS, 1 ); + + return true; + } + + /** + * Composite a brand PNG onto the canvas at a target height and opacity. + * + * @param \Imagick $canvas Destination canvas (mutated). + * @param string $asset_path Absolute path to the brand PNG. + * @param int $target_h Logo height in px (width keeps aspect). + * @param int $x Left position on the canvas. + * @param int $y Top position on the canvas. + * @param float $opacity 1.0 = opaque, 0.55 = subtle corner mark. + * + * @return int Rendered logo width in px (useful for caption x-offsets). + * @throws \RuntimeException When the asset is missing or unreadable. + */ + public static function place_logo( \Imagick $canvas, string $asset_path, int $target_h, int $x, int $y, float $opacity = 1.0 ): int { + if ( ! is_readable( $asset_path ) ) { + throw new \RuntimeException( 'Composer brand asset missing: ' . basename( $asset_path ) ); + } + + $logo = new \Imagick( $asset_path ); + $logo->setImagePage( 0, 0, 0, 0 ); + $scale = $target_h / max( 1, $logo->getImageHeight() ); + $target_w = max( 1, (int) round( $logo->getImageWidth() * $scale ) ); + $logo->resizeImage( $target_w, $target_h, \Imagick::FILTER_LANCZOS, 1 ); + + if ( $opacity < 1.0 ) { + $logo->evaluateImage( \Imagick::EVALUATE_MULTIPLY, max( 0.0, $opacity ), \Imagick::CHANNEL_ALPHA ); + } + + $canvas->compositeImage( $logo, \Imagick::COMPOSITE_OVER, $x, $y ); + $logo->clear(); + + return $target_w; + } + + /** + * Encode a canvas as a deterministic PNG variant payload. + * + * Strips all metadata and excludes PNG date/time chunks so identical + * inputs produce byte-identical output in the same environment. + * + * Side effects: destroys the Imagick object after encoding. + * + * @param \Imagick $image Finished canvas. + * @param string $role Variant role: 'featured', 'og', or 'square'. + * + * @return array{bytes: string, mime_type: string, width: int, height: int, role: string} + */ + public static function finalize_png( \Imagick $image, string $role ): array { + $image->setImageFormat( 'png' ); + $image->setImageDepth( 8 ); + $image->stripImage(); + $image->setOption( 'png:exclude-chunks', 'date,time' ); + $image->setOption( 'png:compression-level', '9' ); + + $variant = array( + 'bytes' => $image->getImageBlob(), + 'mime_type' => 'image/png', + 'width' => $image->getImageWidth(), + 'height' => $image->getImageHeight(), + 'role' => $role, + ); + $image->clear(); + + return $variant; + } +} diff --git a/includes/core/class-image-composer-editor.php b/includes/core/class-image-composer-editor.php new file mode 100644 index 0000000..e63ebae --- /dev/null +++ b/includes/core/class-image-composer-editor.php @@ -0,0 +1,114 @@ +> $layout Layout geometry (for target dimensions). + * + * @return array + * Successfully resized variants only (possibly empty). + */ + public function compose_variants( string $bytes, array $roles, array $layout ): array { + $variants = array(); + + foreach ( $roles as $role ) { + $target_w = (int) ( $layout[ $role ]['width'] ?? 0 ); + $target_h = (int) ( $layout[ $role ]['height'] ?? 0 ); + if ( $target_w < 1 || $target_h < 1 ) { + continue; + } + + $resized = $this->cover_crop( $bytes, $target_w, $target_h ); + if ( null === $resized ) { + PRAutoBlogger_Logger::instance()->debug( + sprintf( 'Resize-only %s variant skipped (editor failure or would upscale).', $role ), + 'image_composer' + ); + continue; + } + + $resized['role'] = $role; + $variants[] = $resized; + } + + return $variants; + } + + /** + * Center-crop bytes to exact dimensions with the portable WP editor. + * + * @param string $bytes Source image bytes. + * @param int $target_w Target width in px. + * @param int $target_h Target height in px. + * + * @return array{bytes: string, mime_type: string, width: int, height: int}|null + * Null when the editor fails or cannot hit the exact size + * without upscaling. + */ + private function cover_crop( string $bytes, int $target_w, int $target_h ): ?array { + $temp = get_temp_dir() . uniqid( 'prab_compose_', true ) . '.png'; + // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents -- temp file, same pattern as the sideloader. + if ( false === file_put_contents( $temp, $bytes ) ) { + return null; + } + + try { + $editor = wp_get_image_editor( $temp ); + if ( is_wp_error( $editor ) ) { + return null; + } + if ( is_wp_error( $editor->resize( $target_w, $target_h, true ) ) ) { + return null; + } + + $size = $editor->get_size(); + if ( (int) $size['width'] !== $target_w || (int) $size['height'] !== $target_h ) { + return null; // Exact size not reachable without upscaling. + } + + if ( is_wp_error( $editor->save( $temp, 'image/png' ) ) ) { + return null; + } + // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- temp file we just wrote. + $out = file_get_contents( $temp ); + + return false === $out ? null : array( + 'bytes' => $out, + 'mime_type' => 'image/png', + 'width' => $target_w, + 'height' => $target_h, + ); + } catch ( \Throwable $e ) { + PRAutoBlogger_Logger::instance()->debug( 'WP image editor crop threw: ' . $e->getMessage(), 'image_composer' ); + return null; + } finally { + // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- best-effort temp cleanup. + @unlink( $temp ); + } + } +} diff --git a/includes/core/class-image-composer-imagick.php b/includes/core/class-image-composer-imagick.php new file mode 100644 index 0000000..50f4039 --- /dev/null +++ b/includes/core/class-image-composer-imagick.php @@ -0,0 +1,250 @@ +> Layout geometry keyed by role. */ + private array $layout; + + /** @var string Absolute path to the vendored brand PNG directory (trailing slash). */ + private string $brand_dir; + + /** @var string Absolute path to the bundled font directory (trailing slash). */ + private string $fonts_dir; + + /** + * Construct with resolved layout + asset locations. + * + * @param array> $layout Filtered layout (see Layout::defaults()). + * @param string $brand_dir Brand PNG directory, trailing slash. + * @param string $fonts_dir Font directory, trailing slash. + */ + public function __construct( array $layout, string $brand_dir, string $fonts_dir ) { + $this->layout = $layout; + $this->brand_dir = $brand_dir; + $this->fonts_dir = $fonts_dir; + } + + /** + * Featured variant: base image untouched except a subtle corner mark. + * + * @param string $bytes Base image bytes. + * + * @return array{bytes: string, mime_type: string, width: int, height: int, role: string} + * @throws \RuntimeException|\ImagickException On decode/asset failure. + */ + public function compose_featured( string $bytes ): array { + $conf = $this->layout['featured']; + $canvas = PRAutoBlogger_Image_Composer_Canvas::load_base( $bytes ); + + $mark_h = (int) $conf['mark_height']; + $inset = (int) $conf['mark_inset']; + // The mark asset is square, so rendered width equals the height. + PRAutoBlogger_Image_Composer_Canvas::place_logo( + $canvas, + $this->brand_dir . $conf['mark_asset'], + $mark_h, + $canvas->getImageWidth() - $mark_h - $inset, + $canvas->getImageHeight() - $mark_h - $inset, + (float) $conf['mark_opacity'] + ); + + return PRAutoBlogger_Image_Composer_Canvas::finalize_png( $canvas, 'featured' ); + } + + /** + * OG variant (1200×630): center-cropped base, bottom teal band with the + * brand mark and the clamped caption in white. + * + * @param string $bytes Base image bytes. + * @param string $caption Editorial caption (clamped to 2 lines). + * + * @return array{bytes: string, mime_type: string, width: int, height: int, role: string} + * @throws \RuntimeException|\ImagickException On decode/crop/asset failure. + */ + public function compose_og( string $bytes, string $caption ): array { + $conf = $this->layout['og']; + $width = (int) $conf['width']; + $height = (int) $conf['height']; + $band_h = (int) $conf['band_height']; + $band_y = $height - $band_h; + + $canvas = PRAutoBlogger_Image_Composer_Canvas::load_base( $bytes ); + if ( ! PRAutoBlogger_Image_Composer_Canvas::cover_crop( $canvas, $width, $height ) ) { + $canvas->clear(); + throw new \RuntimeException( 'OG variant would require upscaling the base image.' ); + } + + $this->draw_rect( $canvas, 0, $band_y, $width, $height, (string) $conf['band_color'] ); + + $logo_h = (int) $conf['logo_height']; + $logo_w = PRAutoBlogger_Image_Composer_Canvas::place_logo( + $canvas, + $this->brand_dir . $conf['logo_asset'], + $logo_h, + (int) $conf['logo_x'], + $band_y + (int) floor( ( $band_h - $logo_h ) / 2 ) + ); + + $lines = PRAutoBlogger_Image_Composer_Layout::clamp_caption( + $caption, + (int) $conf['caption_chars_per_line'], + (int) $conf['caption_max_lines'] + ); + $this->draw_lines( + $canvas, + $lines, + array( + 'font' => $this->fonts_dir . $conf['caption_font'], + 'size' => (float) $conf['caption_size'], + 'color' => (string) $conf['caption_color'], + 'line_height' => (float) $conf['caption_line_height'], + 'x' => (int) $conf['logo_x'] + $logo_w + (int) $conf['caption_gap'], + 'center_y' => $band_y + (int) floor( $band_h / 2 ), + 'align' => \Imagick::ALIGN_LEFT, + ) + ); + + return PRAutoBlogger_Image_Composer_Canvas::finalize_png( $canvas, 'og' ); + } + + /** + * Square variant (1080×1080, card layout, no upscaling): full base + * downscaled into the top slice, cream caption panel in Poppins Bold, + * teal footer band with the horizontal reverse lockup. + * + * @param string $bytes Base image bytes. + * @param string $caption Editorial caption (clamped to 3 lines). + * + * @return array{bytes: string, mime_type: string, width: int, height: int, role: string} + * @throws \RuntimeException|\ImagickException On decode/crop/asset failure. + */ + public function compose_square( string $bytes, string $caption ): array { + $conf = $this->layout['square']; + $width = (int) $conf['width']; + $height = (int) $conf['height']; + $slice_h = (int) $conf['slice_height']; + $footer_h = (int) $conf['footer_height']; + $footer_y = $height - $footer_h; + + $slice = PRAutoBlogger_Image_Composer_Canvas::load_base( $bytes ); + if ( ! PRAutoBlogger_Image_Composer_Canvas::cover_crop( $slice, $width, $slice_h ) ) { + $slice->clear(); + throw new \RuntimeException( 'Square variant would require upscaling the base image.' ); + } + + $canvas = new \Imagick(); + $canvas->newImage( $width, $height, new \ImagickPixel( (string) $conf['panel_color'] ), 'png' ); + $canvas->compositeImage( $slice, \Imagick::COMPOSITE_OVER, 0, 0 ); + $slice->clear(); + + $this->draw_rect( $canvas, 0, $footer_y, $width, $height, (string) $conf['footer_color'] ); + + $logo_h = (int) $conf['footer_logo_height']; + $logo = new \Imagick( $this->brand_dir . $conf['footer_logo_asset'] ); + $logo_w = (int) round( $logo->getImageWidth() * ( $logo_h / max( 1, $logo->getImageHeight() ) ) ); + $logo->clear(); + PRAutoBlogger_Image_Composer_Canvas::place_logo( + $canvas, + $this->brand_dir . $conf['footer_logo_asset'], + $logo_h, + (int) floor( ( $width - $logo_w ) / 2 ), + $footer_y + (int) floor( ( $footer_h - $logo_h ) / 2 ) + ); + + $lines = PRAutoBlogger_Image_Composer_Layout::clamp_caption( + $caption, + (int) $conf['caption_chars_per_line'], + (int) $conf['caption_max_lines'] + ); + $this->draw_lines( + $canvas, + $lines, + array( + 'font' => $this->fonts_dir . $conf['caption_font'], + 'size' => (float) $conf['caption_size'], + 'color' => (string) $conf['caption_color'], + 'line_height' => (float) $conf['caption_line_height'], + 'x' => (int) floor( $width / 2 ), + 'center_y' => $slice_h + (int) floor( ( $footer_y - $slice_h ) / 2 ), + 'align' => \Imagick::ALIGN_CENTER, + ) + ); + + return PRAutoBlogger_Image_Composer_Canvas::finalize_png( $canvas, 'square' ); + } + + /** + * Fill an axis-aligned rectangle on the canvas. + * + * @param \Imagick $canvas Destination canvas (mutated). + * @param int $x1 Left. + * @param int $y1 Top. + * @param int $x2 Right. + * @param int $y2 Bottom. + * @param string $color Hex fill color. + */ + private function draw_rect( \Imagick $canvas, int $x1, int $y1, int $x2, int $y2, string $color ): void { + $draw = new \ImagickDraw(); + $draw->setFillColor( new \ImagickPixel( $color ) ); + $draw->rectangle( (float) $x1, (float) $y1, (float) $x2, (float) $y2 ); + $canvas->drawImage( $draw ); + } + + /** + * Draw pre-clamped caption lines vertically centered around center_y. + * + * @param \Imagick $canvas Destination canvas (mutated). + * @param string[] $lines Already-clamped lines (may be empty — no-op). + * @param array{font: string, size: float, color: string, line_height: float, x: int, center_y: int, align: int} $opts Text options. + * + * @throws \RuntimeException When the bundled font file is missing. + */ + private function draw_lines( \Imagick $canvas, array $lines, array $opts ): void { + if ( empty( $lines ) ) { + return; + } + if ( ! is_readable( $opts['font'] ) ) { + throw new \RuntimeException( 'Composer font missing: ' . basename( $opts['font'] ) ); + } + + $draw = new \ImagickDraw(); + $draw->setFont( $opts['font'] ); + $draw->setFontSize( $opts['size'] ); + $draw->setFillColor( new \ImagickPixel( $opts['color'] ) ); + $draw->setTextAlignment( $opts['align'] ); + $draw->setTextAntialias( true ); + + $metrics = $canvas->queryFontMetrics( $draw, 'Hg' ); + $line_step = (int) round( $opts['size'] * $opts['line_height'] ); + $block_h = ( count( $lines ) - 1 ) * $line_step + (int) round( $metrics['ascender'] - $metrics['descender'] ); + $baseline = $opts['center_y'] - (int) floor( $block_h / 2 ) + (int) round( $metrics['ascender'] ); + + foreach ( $lines as $line ) { + $canvas->annotateImage( $draw, (float) $opts['x'], (float) $baseline, 0.0, $line ); + $baseline += $line_step; + } + } +} diff --git a/includes/core/class-image-composer-layout.php b/includes/core/class-image-composer-layout.php new file mode 100644 index 0000000..31a9f2e --- /dev/null +++ b/includes/core/class-image-composer-layout.php @@ -0,0 +1,173 @@ +> Keyed by role: featured, og, square. + */ + public static function defaults(): array { + return array( + 'featured' => array( + 'mark_asset' => 'logo-mark-small-56.png', + 'mark_height' => 28, + 'mark_inset' => 16, + 'mark_opacity' => 0.55, + ), + 'og' => array( + 'width' => 1200, + 'height' => 630, + 'band_height' => 120, + 'band_color' => self::COLOR_TEAL, + 'logo_asset' => 'logo-mark-small-112.png', + 'logo_height' => 64, + 'logo_x' => 40, + 'caption_font' => 'Poppins-SemiBold.ttf', + 'caption_size' => 32, + 'caption_color' => '#FFFFFF', + 'caption_gap' => 24, + 'caption_margin_right' => 40, + 'caption_max_lines' => 2, + 'caption_chars_per_line' => 52, + 'caption_line_height' => 1.3, + ), + 'square' => array( + 'width' => 1080, + 'height' => 1080, + 'slice_height' => 569, + 'panel_color' => self::COLOR_CREAM, + 'footer_height' => 96, + 'footer_color' => self::COLOR_TEAL, + 'footer_logo_asset' => 'logo-horizontal-reverse-128.png', + 'footer_logo_height' => 48, + 'caption_font' => 'Poppins-Bold.ttf', + 'caption_size' => 48, + 'caption_color' => self::COLOR_INK, + 'caption_max_lines' => 3, + 'caption_chars_per_line' => 36, + 'caption_side_padding' => 64, + 'caption_line_height' => 1.3, + ), + ); + } + + /** + * Clamp and word-wrap a caption to a hard per-variant budget. + * + * Whitespace is collapsed, words wrap at word boundaries, words longer + * than a full line are hard-split, and overflow beyond max_lines is cut + * with a trailing ellipsis. Multibyte-safe (mb_* functions). + * + * Side effects: none (pure). + * + * @param string $caption Raw caption text (any length, any script). + * @param int $chars_per_line Hard character budget per line (min 1). + * @param int $max_lines Maximum number of lines (min 1). + * + * @return string[] Wrapped lines; empty array when the caption is blank. + */ + public static function clamp_caption( string $caption, int $chars_per_line, int $max_lines ): array { + $chars_per_line = max( 1, $chars_per_line ); + $max_lines = max( 1, $max_lines ); + $normalized = trim( (string) preg_replace( '/\s+/u', ' ', $caption ) ); + + if ( '' === $normalized ) { + return array(); + } + + $lines = array(); + $line = ''; + + foreach ( explode( ' ', $normalized ) as $word ) { + // Hard-split words longer than a full line so they cannot overflow. + while ( mb_strlen( $word ) > $chars_per_line ) { + if ( '' !== $line ) { + $lines[] = $line; + $line = ''; + if ( count( $lines ) >= $max_lines ) { + return self::ellipsize( $lines, $chars_per_line ); + } + } + $lines[] = mb_substr( $word, 0, $chars_per_line ); + $word = mb_substr( $word, $chars_per_line ); + if ( count( $lines ) >= $max_lines ) { + return self::ellipsize( $lines, $chars_per_line ); + } + } + + $candidate = ( '' === $line ) ? $word : $line . ' ' . $word; + if ( mb_strlen( $candidate ) <= $chars_per_line ) { + $line = $candidate; + continue; + } + + $lines[] = $line; + $line = $word; + if ( count( $lines ) >= $max_lines ) { + return self::ellipsize( $lines, $chars_per_line ); + } + } + + if ( '' !== $line ) { + $lines[] = $line; + } + + return $lines; + } + + /** + * Append an ellipsis to the final line, trimming first if the line is + * already at the character budget so the result never exceeds it. + * + * @param string[] $lines Lines already at the max-line count. + * @param int $chars_per_line Hard character budget per line. + * + * @return string[] Lines with a trailing ellipsis on the last one. + */ + private static function ellipsize( array $lines, int $chars_per_line ): array { + $last_index = count( $lines ) - 1; + $last = $lines[ $last_index ]; + + if ( mb_strlen( $last ) + 1 > $chars_per_line ) { + $last = rtrim( mb_substr( $last, 0, max( 0, $chars_per_line - 1 ) ) ); + } + + $lines[ $last_index ] = $last . self::ELLIPSIS; + + return $lines; + } +} diff --git a/includes/core/class-image-composer.php b/includes/core/class-image-composer.php new file mode 100644 index 0000000..640f814 --- /dev/null +++ b/includes/core/class-image-composer.php @@ -0,0 +1,298 @@ +renderer = $renderer; + } + + /** + * Compose final variants for one generated image. See the interface for + * the full contract; this implementation never throws. + * Side effects: reads options, may cache the capability probe, logs. + * + * @param array $image_data Provider result (bytes, mime_type, width, height, ...). + * @param array $context {post_id, caption, title, slot}. + * @return array + */ + public function compose( array $image_data, array $context ): array { + $passthrough = array( $this->passthrough_variant( $image_data ) ); + + if ( '' === $passthrough[0]['bytes'] || '1' !== (string) get_option( self::OPTION_ENABLED, '1' ) ) { + return $passthrough; + } + + $started = microtime( true ); + $bytes = $passthrough[0]['bytes']; + $capability = $this->resolve_capability(); + $caption = (string) ( $context['caption'] ?? '' ); + $roles = ( 'image_a' === (string) ( $context['slot'] ?? 'image_a' ) ) + ? $this->configured_variants() + : array(); + + if ( self::CAP_IMAGICK === $capability ) { + $variants = $this->compose_via_imagick( $bytes, $caption, $roles, $passthrough[0] ); + } elseif ( self::CAP_GD === $capability ) { + $this->warn_once( 'Imagick unavailable — emitting resize-only variants without branding or caption.' ); + $editor = new PRAutoBlogger_Image_Composer_Editor(); + $variants = array_merge( $passthrough, $editor->compose_variants( $bytes, $roles, $this->layout() ) ); + } else { + $this->warn_once( 'No usable image library — base image passed through unbranded.' ); + $variants = $passthrough; + } + + PRAutoBlogger_Logger::instance()->info( + sprintf( + 'Composed %d variant(s) for post %d in %dms ($0, local, capability: %s).', + count( $variants ), + (int) ( $context['post_id'] ?? 0 ), + (int) round( ( microtime( true ) - $started ) * 1000 ), + $capability + ), + 'image_composer' + ); + + return $variants; + } + + /** + * Full compositing rung. Each render is individually guarded: a featured + * failure degrades to pass-through, a variant failure skips that variant. + * + * @param string $bytes Base image bytes. + * @param string $caption Editorial caption for OG/square. + * @param array $roles Variant roles to render ('og', 'square'). + * @param array $passthrough Pass-through featured variant (fallback). + * @return array> Featured-first variant list. + */ + private function compose_via_imagick( string $bytes, string $caption, array $roles, array $passthrough ): array { + $variants = array(); + + if ( '1' === (string) get_option( self::OPTION_MARK_ENABLED, '1' ) ) { + $featured = $this->try_render( 'featured', $bytes, $caption ); + $variants[] = null === $featured ? $passthrough : $featured; + } else { + $variants[] = $passthrough; + } + + foreach ( $roles as $role ) { + $variant = $this->try_render( $role, $bytes, $caption ); + if ( null !== $variant ) { + $variants[] = $variant; + } + } + + return $variants; + } + + /** + * Render one role via the Imagick renderer. Any throwable or malformed + * result (empty bytes) maps to one per-run WARNING + null (caller degrades). + * @param string $role 'featured', 'og', or 'square'. + * @param string $bytes Base image bytes. + * @param string $caption Editorial caption. + * @return array|null Variant payload or null on failure. + */ + private function try_render( string $role, string $bytes, string $caption ): ?array { + try { + $renderer = $this->renderer(); + if ( 'og' === $role ) { + $variant = $renderer->compose_og( $bytes, $caption ); + } elseif ( 'square' === $role ) { + $variant = $renderer->compose_square( $bytes, $caption ); + } else { + $variant = $renderer->compose_featured( $bytes ); + } + + if ( '' === (string) ( $variant['bytes'] ?? '' ) ) { + $this->warn_once( sprintf( 'Imagick render for %s variant returned no bytes — degrading.', $role ) ); + return null; + } + $variant['role'] = $role; + + return $variant; + } catch ( \Throwable $e ) { + $this->warn_once( sprintf( 'Imagick render failed for %s variant: %s', $role, $e->getMessage() ) ); + return null; + } + } + + /** + * Resolve the capability rung: cached probe + override filter. The + * same-named filter lets tests/ops force a rung (e.g. 'none'). + * + * @return string One of the CAP_* constants. + */ + private function resolve_capability(): string { + $fingerprint = md5( + PHP_VERSION . '|' . ( extension_loaded( 'imagick' ) ? 'im1' : 'im0' ) . '|' . ( extension_loaded( 'gd' ) ? 'gd1' : 'gd0' ) + ); + + $cached = get_option( self::OPTION_CAPABILITY, array() ); + if ( is_array( $cached ) && $fingerprint === ( $cached['fingerprint'] ?? '' ) && isset( $cached['capability'] ) ) { + $capability = (string) $cached['capability']; + } else { + $capability = $this->probe_capability(); + update_option( + self::OPTION_CAPABILITY, + array( + 'fingerprint' => $fingerprint, + 'capability' => $capability, + ), + false + ); + } + + $capability = (string) apply_filters( 'prautoblogger_image_compose_capability', $capability ); + + return in_array( $capability, array( self::CAP_IMAGICK, self::CAP_GD, self::CAP_NONE ), true ) + ? $capability + : self::CAP_NONE; + } + + /** + * Probe the host for the best available rung (uncached). + * @return string One of the CAP_* constants. + */ + private function probe_capability(): string { + if ( PRAutoBlogger_Image_Composer_Canvas::is_imagick_usable( $this->fonts_dir() . 'Poppins-Bold.ttf' ) ) { + return self::CAP_IMAGICK; + } + + $png_crop = array( + 'mime_type' => 'image/png', + 'methods' => array( 'resize', 'crop' ), + ); + if ( function_exists( 'wp_image_editor_supports' ) && wp_image_editor_supports( $png_crop ) ) { + return self::CAP_GD; + } + + return self::CAP_NONE; + } + + /** + * Lazily build the Imagick renderer with filtered layout + asset paths. + * @return PRAutoBlogger_Image_Composer_Imagick + */ + private function renderer(): PRAutoBlogger_Image_Composer_Imagick { + if ( null === $this->renderer ) { + $this->renderer = new PRAutoBlogger_Image_Composer_Imagick( + $this->layout(), + dirname( __DIR__, 2 ) . '/assets/brand/', + $this->fonts_dir() + ); + } + return $this->renderer; + } + + /** + * Layout geometry: pure defaults behind the documented override filter. + * @return array> + */ + private function layout(): array { + return (array) apply_filters( 'prautoblogger_image_compose_layout', PRAutoBlogger_Image_Composer_Layout::defaults() ); + } + + /** + * Bundled font directory (path-derived, no constants — unit-test safe). + * @return string Absolute path with trailing slash. + */ + private function fonts_dir(): string { + return dirname( __DIR__, 2 ) . '/assets/fonts/'; + } + + /** + * Variant roles from settings, whitelisted at point of use. + * @return string[] Subset of ['og', 'square'], deduplicated, in config order. + */ + private function configured_variants(): array { + $raw = (string) get_option( self::OPTION_VARIANTS, 'og,square' ); + $roles = array(); + foreach ( explode( ',', $raw ) as $piece ) { + $piece = strtolower( trim( $piece ) ); + if ( in_array( $piece, array( 'og', 'square' ), true ) && ! in_array( $piece, $roles, true ) ) { + $roles[] = $piece; + } + } + return $roles; + } + + /** + * Build the pass-through featured variant from the provider result. + * @param array $image_data Provider result. + * @return array{bytes: string, mime_type: string, width: int, height: int, role: string} + */ + private function passthrough_variant( array $image_data ): array { + return array( + 'bytes' => (string) ( $image_data['bytes'] ?? '' ), + 'mime_type' => (string) ( $image_data['mime_type'] ?? 'image/png' ), + 'width' => (int) ( $image_data['width'] ?? 0 ), + 'height' => (int) ( $image_data['height'] ?? 0 ), + 'role' => 'featured', + ); + } + + /** + * Log a degradation WARNING once per run (one row is enough for ops). + * @param string $message Warning text. + */ + private function warn_once( string $message ): void { + if ( self::$warned_this_run ) { + return; + } + self::$warned_this_run = true; + PRAutoBlogger_Logger::instance()->warning( $message, 'image_composer' ); + } + + /** Reset per-run state (unit tests only). */ + public static function reset_run_state(): void { + self::$warned_this_run = false; + } +} diff --git a/includes/core/class-image-media-sideloader.php b/includes/core/class-image-media-sideloader.php index 26d4605..0b92a7c 100644 --- a/includes/core/class-image-media-sideloader.php +++ b/includes/core/class-image-media-sideloader.php @@ -36,11 +36,13 @@ class PRAutoBlogger_Image_Media_Sideloader { * latency_ms: int, * } $image_data Raw image bytes + metadata from PRAutoBlogger_Image_Provider_Interface. * @param int $post_id Target post ID to attach the image to. - * @param string $alt_text Alt text for the image (from the generation prompt). + * @param string $alt_text Alt text for the image (the editorial caption). + * @param string $filename_suffix Optional role hint appended to the filename + * (e.g. 'og', 'square' for composed variants). * * @return int|\WP_Error Attachment ID on success, WP_Error on failure. */ - public function sideload_image( array $image_data, int $post_id, string $alt_text = '' ) { + public function sideload_image( array $image_data, int $post_id, string $alt_text = '', string $filename_suffix = '' ) { // WordPress media functions live in wp-admin/includes/ and are NOT // loaded automatically in cron or REST contexts. Load them if missing. // Why: WP-Cron runs as a frontend request, so admin-only files like @@ -64,7 +66,7 @@ class PRAutoBlogger_Image_Media_Sideloader { // Prepare file array for media_handle_sideload(). $file_array = array( - 'name' => $this->generate_filename( $image_data['model'], $image_data['width'], $image_data['height'] ), + 'name' => $this->generate_filename( $image_data['model'], $image_data['width'], $image_data['height'], $filename_suffix ), 'tmp_name' => $temp_file, ); @@ -135,16 +137,18 @@ class PRAutoBlogger_Image_Media_Sideloader { * @param string $model Model name (e.g., 'flux-1-schnell'). * @param int $width Image width. * @param int $height Image height. + * @param string $suffix Optional role hint (e.g. 'og', 'square'); back-compat default ''. * * @return string Sanitized filename. */ - private function generate_filename( string $model, int $width, int $height ): string { + private function generate_filename( string $model, int $width, int $height, string $suffix = '' ): string { $base = sprintf( - 'prautoblogger_%s_%dx%d_%s', + 'prautoblogger_%s_%dx%d_%s%s', sanitize_title( $model ), $width, $height, - gmdate( 'Y-m-d_H-i-s' ) + gmdate( 'Y-m-d_H-i-s' ), + '' !== $suffix ? '_' . sanitize_title( $suffix ) : '' ); return $base . '.png'; // Default to PNG — most image models output PNG. diff --git a/includes/core/class-image-pipeline.php b/includes/core/class-image-pipeline.php index a5a3343..e0e528b 100644 --- a/includes/core/class-image-pipeline.php +++ b/includes/core/class-image-pipeline.php @@ -8,13 +8,17 @@ declare(strict_types=1); * * Generates two images per article (A/B): Image A (article-driven) as featured * image, Image B (source-driven) in post meta. Each is independently fallible. + * Since v0.17.0 every result passes through the deterministic image composer + * (branded variants for A, corner mark for B) before sideloading; composition + * degrades gracefully and never blocks a publish. * * Triggered by: PRAutoBlogger_Pipeline_Runner after publisher creates the post. - * Dependencies: Image provider, Prompt builder, Sideloader, Cost tracker, Logger. + * Dependencies: Image provider, Prompt builder, Composer, Attacher, Cost tracker, Logger. * * @see core/class-pipeline-runner.php — Calls this after publisher. * @see core/class-image-prompt-builder.php — Prompt generation (scene + caption). - * @see core/class-image-media-sideloader.php — Media library integration. + * @see core/class-image-composer.php — Deterministic variant composition. + * @see core/class-image-attacher.php — Sideloading + variant meta-linking. */ class PRAutoBlogger_Image_Pipeline { @@ -34,26 +38,35 @@ class PRAutoBlogger_Image_Pipeline { private PRAutoBlogger_Image_Media_Sideloader $sideloader; /** @var PRAutoBlogger_Cost_Tracker Logs image generation spend. */ private PRAutoBlogger_Cost_Tracker $cost_tracker; + /** @var PRAutoBlogger_Image_Composer_Interface Composes branded variants pre-sideload. */ + private PRAutoBlogger_Image_Composer_Interface $composer; + /** @var PRAutoBlogger_Image_Attacher Sideloads + meta-links results and variants. */ + private PRAutoBlogger_Image_Attacher $attacher; + /** @var PRAutoBlogger_Opik_Trace_Context|null Optional tracing context. */ + private ?PRAutoBlogger_Opik_Trace_Context $trace_context; /** - * Construct with dependencies. - * - * When no provider is injected, the constructor reads the - * `prautoblogger_image_provider` setting and instantiates the - * matching concrete class ('runware' or 'openrouter'). + * Construct with dependencies. When no provider is injected, the + * `prautoblogger_image_provider` setting picks the concrete class + * ('runware' or 'openrouter'). * * @param PRAutoBlogger_Image_Provider_Interface|null $provider Optional provider override. * @param PRAutoBlogger_Cost_Tracker|null $cost_tracker Optional cost tracker. + * @param PRAutoBlogger_Opik_Trace_Context|null $trace_context Optional tracing context. + * @param PRAutoBlogger_Image_Composer_Interface|null $composer Optional composer override. */ public function __construct( ?PRAutoBlogger_Image_Provider_Interface $provider = null, ?PRAutoBlogger_Cost_Tracker $cost_tracker = null, - ?PRAutoBlogger_Opik_Trace_Context $trace_context = null + ?PRAutoBlogger_Opik_Trace_Context $trace_context = null, + ?PRAutoBlogger_Image_Composer_Interface $composer = null ) { $this->provider = $provider ?? self::create_default_provider(); $this->prompt_builder = new PRAutoBlogger_Image_Prompt_Builder( $trace_context ); $this->sideloader = new PRAutoBlogger_Image_Media_Sideloader(); $this->cost_tracker = $cost_tracker ?? new PRAutoBlogger_Cost_Tracker(); + $this->composer = $composer ?? new PRAutoBlogger_Image_Composer(); + $this->attacher = new PRAutoBlogger_Image_Attacher( $this->sideloader, $this->cost_tracker ); $this->trace_context = $trace_context; } @@ -149,12 +162,14 @@ class PRAutoBlogger_Image_Pipeline { ->retry_blocked_slots( $post_id, $batch_requests, $batch_results, $captions, $article_data, $source_data ); } + $title = (string) ( $article_data['post_title'] ?? $article_data['suggested_title'] ?? '' ); + // Process Image A result. - $this->process_image_a( $post_id, $batch_results, $captions, $result ); + $this->process_slot( 'image_a', $post_id, $batch_results, $captions, $title, $result ); // Process Image B result (if requested). if ( isset( $batch_results['image_b'] ) ) { - $this->process_image_b( $post_id, $batch_results, $captions, $result ); + $this->process_slot( 'image_b', $post_id, $batch_results, $captions, $title, $result ); } elseif ( null === $source_data || empty( $source_data ) ) { PRAutoBlogger_Logger::instance()->info( sprintf( 'Image B skipped for post %d: no source data.', $post_id ), @@ -166,142 +181,98 @@ class PRAutoBlogger_Image_Pipeline { } /** - * Process Image A batch result: sideload, set featured image, insert caption. + * Process one slot's batch result: compose, sideload, link to the post. * - * @param int $post_id Post ID. - * @param array $batch_results Keyed results from generate_image_batch(). - * @param array $captions Keyed captions from prompt builder. - * @param array $result Pipeline result array (modified by reference). + * Image A becomes the featured image (role meta, caption prepend, and + * composed OG/square variants meta-linked); Image B is stored in post + * meta with a corner mark only. Alt text is the editorial caption + * (v0.17.0 fix — the model name used to leak here). + * + * @param string $slot 'image_a' or 'image_b'. + * @param int $post_id Post ID. + * @param array $batch_results Keyed results from generate_image_batch(). + * @param array $captions Keyed captions from prompt builder. + * @param string $title Post title (composer context). + * @param array $result Pipeline result array (modified by reference). */ - private function process_image_a( int $post_id, array $batch_results, array $captions, array &$result ): void { - $image_data = $batch_results['image_a'] ?? null; + private function process_slot( string $slot, int $post_id, array $batch_results, array $captions, string $title, array &$result ): void { + $label = 'image_a' === $slot ? 'Image A' : 'Image B'; + $image_data = $batch_results[ $slot ] ?? null; + if ( ! $image_data || isset( $image_data['error'] ) ) { - $err_msg = $image_data['error'] ?? 'Image A missing from batch results.'; + $err_msg = $image_data['error'] ?? sprintf( '%s missing from batch results.', $label ); $result['errors'][] = $err_msg; // Log at WARNING so the path is visible even if the outer catch in the // caller doesn't fire. See thread image-mime-bug Issue 2. PRAutoBlogger_Logger::instance()->warning( - sprintf( 'Image A generation failed for post %d: %s', $post_id, $err_msg ), + sprintf( '%s generation failed for post %d: %s', $label, $post_id, $err_msg ), 'image_pipeline' ); return; } - $attachment_id = $this->sideload_and_log( $image_data, $post_id, 'image_a', 'Image A' ); + $caption = (string) ( $captions[ $slot ] ?? '' ); + $variants = $this->compose_variants( $image_data, $post_id, $caption, $title, $slot ); + $featured = array_merge( $image_data, array_shift( $variants ) ); + + $attachment_id = $this->attacher->sideload_and_log( $featured, $post_id, $slot, $label, $caption ); if ( is_wp_error( $attachment_id ) ) { $result['errors'][] = $attachment_id->get_error_message(); return; } - $result['image_a_id'] = $attachment_id; - $result['cost_usd'] += $image_data['cost_usd']; - set_post_thumbnail( $post_id, $attachment_id ); + $result[ $slot . '_id' ] = $attachment_id; + $result['cost_usd'] += $image_data['cost_usd']; - if ( '' !== ( $captions['image_a'] ?? '' ) ) { - update_post_meta( $attachment_id, '_prautoblogger_image_caption', $captions['image_a'] ); - $this->prepend_caption_to_post( $post_id, $attachment_id, $captions['image_a'] ); + if ( '' !== $caption ) { + update_post_meta( $attachment_id, '_prautoblogger_image_caption', $caption ); + } + + if ( 'image_a' === $slot ) { + set_post_thumbnail( $post_id, $attachment_id ); + update_post_meta( $attachment_id, '_prautoblogger_image_role', 'featured' ); + if ( '' !== $caption ) { + $this->attacher->prepend_caption_to_post( $post_id, $attachment_id, $caption ); + } + $this->attacher->attach_variants( $variants, $post_id, $attachment_id, $caption, (string) ( $image_data['model'] ?? 'unknown' ) ); + } else { + update_post_meta( $post_id, '_prautoblogger_image_b_id', $attachment_id ); } } /** - * Process Image B batch result: sideload, store meta. + * Run the composer with a hard safety net: any failure (composer bugs + * included — its own internals already degrade) falls back to passing + * the base image through, so composition can never fail a publish. * - * @param int $post_id Post ID. - * @param array $batch_results Keyed results from generate_image_batch(). - * @param array $captions Keyed captions from prompt builder. - * @param array $result Pipeline result array (modified by reference). + * @param array $image_data Provider result for one slot. + * @param int $post_id Post ID (composer context). + * @param string $caption Editorial caption (composer context). + * @param string $title Post title (composer context). + * @param string $slot 'image_a' or 'image_b'. + * @return array> Featured-first variant list (never empty). */ - private function process_image_b( int $post_id, array $batch_results, array $captions, array &$result ): void { - $image_data = $batch_results['image_b'] ?? null; - if ( ! $image_data || isset( $image_data['error'] ) ) { - $err_msg = $image_data['error'] ?? 'Image B missing from batch results.'; - $result['errors'][] = $err_msg; - // Log at WARNING so the path is visible even if the outer catch in the - // caller doesn't fire. See thread image-mime-bug Issue 2. - PRAutoBlogger_Logger::instance()->warning( - sprintf( 'Image B generation failed for post %d: %s', $post_id, $err_msg ), - 'image_pipeline' + private function compose_variants( array $image_data, int $post_id, string $caption, string $title, string $slot ): array { + try { + $variants = $this->composer->compose( + $image_data, + array( + 'post_id' => $post_id, + 'caption' => $caption, + 'title' => $title, + 'slot' => $slot, + ) + ); + if ( ! empty( $variants ) ) { + return $variants; + } + } catch ( \Throwable $e ) { + PRAutoBlogger_Logger::instance()->warning( + 'Image composition failed, publishing original image: ' . $e->getMessage(), + 'image_composer' ); - return; } - $attachment_id = $this->sideload_and_log( $image_data, $post_id, 'image_b', 'Image B' ); - if ( is_wp_error( $attachment_id ) ) { - $result['errors'][] = $attachment_id->get_error_message(); - return; - } - - $result['image_b_id'] = $attachment_id; - $result['cost_usd'] += $image_data['cost_usd']; - update_post_meta( $post_id, '_prautoblogger_image_b_id', $attachment_id ); - - if ( '' !== ( $captions['image_b'] ?? '' ) ) { - update_post_meta( $attachment_id, '_prautoblogger_image_caption', $captions['image_b'] ); - } - } - - /** - * Sideload image bytes into the media library and log the cost. - * - * @param array $image_data Provider result with bytes, model, cost_usd. - * @param int $post_id Post ID. - * @param string $slot Cost-tracking slot ('image_a' or 'image_b'). - * @param string $label Human-readable label for logs. - * @return int|\WP_Error Attachment ID on success. - */ - private function sideload_and_log( array $image_data, int $post_id, string $slot, string $label ) { - $attachment_id = $this->sideloader->sideload_image( - $image_data, - $post_id, - substr( $image_data['model'] ?? 'image', 0, 100 ) - ); - - if ( is_wp_error( $attachment_id ) ) { - return $attachment_id; - } - - $this->cost_tracker->log_image_generation( - $image_data['cost_usd'], - $image_data['model'] ?? 'unknown', - $post_id, - $slot - ); - - PRAutoBlogger_Logger::instance()->info( - sprintf( '%s for post %d (att %d, $%.4f)', $label, $post_id, $attachment_id, $image_data['cost_usd'] ), - 'image_pipeline' - ); - - return $attachment_id; - } - - /** - * Prepend the comic caption as styled text at the top of the post content. - * - * Only inserts the caption — NOT the image. The theme already displays the - * featured image, so embedding it again would cause a duplicate. - * - * @param int $post_id Post ID. - * @param int $attachment_id Featured image attachment ID (for meta storage only). - * @param string $caption Caption text (the comic punchline). - */ - private function prepend_caption_to_post( int $post_id, int $attachment_id, string $caption ): void { - $post = get_post( $post_id ); - if ( ! $post ) { - return; - } - - // Caption-only block — the theme handles the featured image display. - $caption_html = sprintf( - '

— "%s"

', - esc_html( $caption ) - ); - - wp_update_post( - array( - 'ID' => $post_id, - 'post_content' => $caption_html . "\n\n" . $post->post_content, - ) - ); + return array( array_merge( $image_data, array( 'role' => 'featured' ) ) ); } } diff --git a/includes/core/interface-image-composer.php b/includes/core/interface-image-composer.php new file mode 100644 index 0000000..bee9b58 --- /dev/null +++ b/includes/core/interface-image-composer.php @@ -0,0 +1,50 @@ + + * Non-empty list; index 0 always carries role 'featured'. + */ + public function compose( array $image_data, array $context ): array; +} diff --git a/prautoblogger.php b/prautoblogger.php index 1db73bb..3b8c519 100644 --- a/prautoblogger.php +++ b/prautoblogger.php @@ -9,7 +9,7 @@ * Plugin Name: PRAutoBlogger * Plugin URI: https://peptiderepo.com/prautoblogger * Description: Monitors social media for trending topics, generates SEO-friendly blog posts using AI, and publishes them on a daily schedule with full cost tracking and self-improvement metrics. - * Version: 0.16.0 + * Version: 0.17.0 * Requires at least: 6.0 * Requires PHP: 7.4 * Author: PeptideRepo @@ -35,7 +35,7 @@ if ( ! defined( 'ABSPATH' ) ) { | Defined here so every file in the plugin can reference paths, versions, | and limits without magic strings. */ -define( 'PRAUTOBLOGGER_VERSION', '0.16.0' ); +define( 'PRAUTOBLOGGER_VERSION', '0.17.0' ); define( 'PRAUTOBLOGGER_DB_VERSION', '1.1.0' ); define( 'PRAUTOBLOGGER_PLUGIN_FILE', __FILE__ ); define( 'PRAUTOBLOGGER_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); diff --git a/tests/unit/Core/ImageAttacherTest.php b/tests/unit/Core/ImageAttacherTest.php new file mode 100644 index 0000000..0824d53 --- /dev/null +++ b/tests/unit/Core/ImageAttacherTest.php @@ -0,0 +1,176 @@ +alias( + static function ( $thing ) { + return $thing instanceof \WP_Error; + } + ); + $this->meta_calls = []; + Functions\when( 'update_post_meta' )->alias( + function ( $object_id, $key, $value ) { + $this->meta_calls[] = [ $object_id, $key, $value ]; + return true; + } + ); + $this->stub_get_option( [] ); + } + + /** Helper: find a captured meta value by object id + key. */ + private function meta_value( int $object_id, string $key ) { + foreach ( $this->meta_calls as $call ) { + if ( $call[0] === $object_id && $call[1] === $key ) { + return $call[2]; + } + } + return null; + } + + /** Helper: primary image fixture. */ + private function image_data(): array { + return [ + 'bytes' => 'featured_bytes', + 'mime_type' => 'image/png', + 'width' => 1200, + 'height' => 632, + 'model' => 'flux-1-schnell', + 'cost_usd' => 0.0006, + 'latency_ms' => 1500, + ]; + } + + /** + * sideload_and_log() forwards the caption as alt text (bug fix: the model + * name used to leak into alt text) and logs the generation cost once. + */ + public function test_sideload_and_log_forwards_caption_as_alt_and_logs_cost(): void { + $sideloader = $this->createMock( \PRAutoBlogger_Image_Media_Sideloader::class ); + $sideloader->expects( $this->once() ) + ->method( 'sideload_image' ) + ->with( $this->image_data(), 7, 'Collagen fragments may aid joint repair' ) + ->willReturn( 42 ); + + $cost_tracker = $this->createMock( \PRAutoBlogger_Cost_Tracker::class ); + $cost_tracker->expects( $this->once() ) + ->method( 'log_image_generation' ) + ->with( 0.0006, 'flux-1-schnell', 7, 'image_a' ); + + $attacher = new \PRAutoBlogger_Image_Attacher( $sideloader, $cost_tracker ); + $result = $attacher->sideload_and_log( $this->image_data(), 7, 'image_a', 'Image A', 'Collagen fragments may aid joint repair' ); + + $this->assertSame( 42, $result ); + } + + /** + * A failed primary sideload returns the WP_Error untouched and logs no cost. + */ + public function test_sideload_and_log_propagates_error_without_cost_row(): void { + $error = new \WP_Error( 'sideload_failed', 'disk full' ); + $sideloader = $this->createMock( \PRAutoBlogger_Image_Media_Sideloader::class ); + $sideloader->method( 'sideload_image' )->willReturn( $error ); + + $cost_tracker = $this->createMock( \PRAutoBlogger_Cost_Tracker::class ); + $cost_tracker->expects( $this->never() )->method( 'log_image_generation' ); + + $attacher = new \PRAutoBlogger_Image_Attacher( $sideloader, $cost_tracker ); + + $this->assertSame( $error, $attacher->sideload_and_log( $this->image_data(), 7, 'image_a', 'Image A', 'cap' ) ); + } + + /** + * attach_variants() sideloads og + square with the role as filename + * suffix and the caption as alt text, writes role/base attachment meta + * and the role-specific post meta, and never logs composition cost. + */ + public function test_attach_variants_meta_links_og_and_square(): void { + $variants = [ + [ + 'bytes' => 'og_bytes', + 'mime_type' => 'image/png', + 'width' => 1200, + 'height' => 630, + 'role' => 'og', + ], + [ + 'bytes' => 'square_bytes', + 'mime_type' => 'image/png', + 'width' => 1080, + 'height' => 1080, + 'role' => 'square', + ], + ]; + + $returned_ids = [ 'og' => 43, 'square' => 44 ]; + $sideloader = $this->createMock( \PRAutoBlogger_Image_Media_Sideloader::class ); + $sideloader->expects( $this->exactly( 2 ) ) + ->method( 'sideload_image' ) + ->willReturnCallback( + function ( array $image_data, int $post_id, string $alt, string $suffix ) use ( $returned_ids ) { + $this->assertSame( 7, $post_id ); + $this->assertSame( 'A short caption', $alt ); + $this->assertSame( 0.0, $image_data['cost_usd'] ); + $this->assertSame( 'flux-1-schnell', $image_data['model'] ); + $this->assertArrayHasKey( $suffix, $returned_ids, 'filename suffix must be the role' ); + return $returned_ids[ $suffix ]; + } + ); + + $cost_tracker = $this->createMock( \PRAutoBlogger_Cost_Tracker::class ); + $cost_tracker->expects( $this->never() )->method( 'log_image_generation' ); + + $attacher = new \PRAutoBlogger_Image_Attacher( $sideloader, $cost_tracker ); + $attacher->attach_variants( $variants, 7, 42, 'A short caption', 'flux-1-schnell' ); + + $this->assertSame( 'og', $this->meta_value( 43, '_prautoblogger_image_role' ) ); + $this->assertSame( 42, $this->meta_value( 43, '_prautoblogger_base_attachment_id' ) ); + $this->assertSame( 43, $this->meta_value( 7, '_prautoblogger_og_image_id' ) ); + + $this->assertSame( 'square', $this->meta_value( 44, '_prautoblogger_image_role' ) ); + $this->assertSame( 42, $this->meta_value( 44, '_prautoblogger_base_attachment_id' ) ); + $this->assertSame( 44, $this->meta_value( 7, '_prautoblogger_square_image_id' ) ); + } + + /** + * Unknown roles (e.g. a stray featured entry) are ignored, and a failed + * variant sideload is skipped without meta writes or exceptions. + */ + public function test_attach_variants_skips_unknown_roles_and_failed_sideloads(): void { + $variants = [ + [ 'bytes' => 'f', 'mime_type' => 'image/png', 'width' => 1, 'height' => 1, 'role' => 'featured' ], + [ 'bytes' => 'o', 'mime_type' => 'image/png', 'width' => 1200, 'height' => 630, 'role' => 'og' ], + ]; + + $sideloader = $this->createMock( \PRAutoBlogger_Image_Media_Sideloader::class ); + $sideloader->expects( $this->once() ) // Only og — featured is not a variant role here. + ->method( 'sideload_image' ) + ->willReturn( new \WP_Error( 'sideload_failed', 'nope' ) ); + + $cost_tracker = $this->createMock( \PRAutoBlogger_Cost_Tracker::class ); + + $attacher = new \PRAutoBlogger_Image_Attacher( $sideloader, $cost_tracker ); + $attacher->attach_variants( $variants, 7, 42, 'cap', 'model' ); + + $this->assertSame( [], $this->meta_calls ); + } +} diff --git a/tests/unit/Core/ImageComposerImagickTest.php b/tests/unit/Core/ImageComposerImagickTest.php new file mode 100644 index 0000000..cf26007 --- /dev/null +++ b/tests/unit/Core/ImageComposerImagickTest.php @@ -0,0 +1,104 @@ +markTestSkipped( 'ext-imagick not available — renderer covered by the server-side harness.' ); + } + } + + /** Helper: renderer wired to the real bundled assets. */ + private function renderer(): \PRAutoBlogger_Image_Composer_Imagick { + $root = dirname( __DIR__, 3 ); + + return new \PRAutoBlogger_Image_Composer_Imagick( + \PRAutoBlogger_Image_Composer_Layout::defaults(), + $root . '/assets/brand/', + $root . '/assets/fonts/' + ); + } + + /** Helper: deterministic 1200×632 fixture base image (gradient + disc). */ + private function fixture_bytes(): string { + $image = new \Imagick(); + $image->newPseudoImage( 1200, 632, 'gradient:#1B8A92-#FAFAF7' ); + $draw = new \ImagickDraw(); + $draw->setFillColor( new \ImagickPixel( '#FF8A3D' ) ); + $draw->circle( 600, 316, 600, 470 ); + $image->drawImage( $draw ); + $image->setImageFormat( 'png' ); + + return $image->getImageBlob(); + } + + /** + * Determinism: rendering the same input twice yields byte-identical + * variants (metadata stripped, PNG date/time chunks excluded). + */ + public function test_same_input_renders_byte_identical_output(): void { + $bytes = $this->fixture_bytes(); + $caption = 'Collagen fragments may aid joint repair'; + + $first = $this->renderer()->compose_og( $bytes, $caption ); + $second = $this->renderer()->compose_og( $bytes, $caption ); + $this->assertSame( hash( 'sha256', $first['bytes'] ), hash( 'sha256', $second['bytes'] ), 'OG render must be byte-stable' ); + + $first = $this->renderer()->compose_square( $bytes, $caption ); + $second = $this->renderer()->compose_square( $bytes, $caption ); + $this->assertSame( hash( 'sha256', $first['bytes'] ), hash( 'sha256', $second['bytes'] ), 'Square render must be byte-stable' ); + + $first = $this->renderer()->compose_featured( $bytes ); + $second = $this->renderer()->compose_featured( $bytes ); + $this->assertSame( hash( 'sha256', $first['bytes'] ), hash( 'sha256', $second['bytes'] ), 'Featured render must be byte-stable' ); + } + + /** + * Variant geometry matches the spec: featured keeps base dimensions, + * OG is exactly 1200×630, square exactly 1080×1080, all PNG. + */ + public function test_variant_dimensions_and_mime(): void { + $bytes = $this->fixture_bytes(); + $caption = 'Collagen fragments may aid joint repair'; + + $featured = $this->renderer()->compose_featured( $bytes ); + $this->assertSame( [ 1200, 632, 'image/png', 'featured' ], [ $featured['width'], $featured['height'], $featured['mime_type'], $featured['role'] ] ); + + $og = $this->renderer()->compose_og( $bytes, $caption ); + $this->assertSame( [ 1200, 630, 'image/png', 'og' ], [ $og['width'], $og['height'], $og['mime_type'], $og['role'] ] ); + + $square = $this->renderer()->compose_square( $bytes, $caption ); + $this->assertSame( [ 1080, 1080, 'image/png', 'square' ], [ $square['width'], $square['height'], $square['mime_type'], $square['role'] ] ); + } + + /** + * Output PNG bytes embed no date text chunks or tIME chunk, so a render + * is stable across wall-clock time, not just within one process. (We + * scan the raw bytes: Imagick synthesizes `date:*` image properties at + * read time even for chunk-free PNGs, so getImageProperties() can't + * distinguish embedded dates from synthesized ones.) + */ + public function test_output_contains_no_date_chunks(): void { + $og = $this->renderer()->compose_og( $this->fixture_bytes(), 'Short caption' ); + + $this->assertFalse( strpos( $og['bytes'], 'date:create' ), 'PNG must not embed a date:create text chunk' ); + $this->assertFalse( strpos( $og['bytes'], 'date:modify' ), 'PNG must not embed a date:modify text chunk' ); + $this->assertFalse( strpos( $og['bytes'], 'tIME' ), 'PNG must not embed a tIME chunk' ); + } +} diff --git a/tests/unit/Core/ImageComposerLayoutTest.php b/tests/unit/Core/ImageComposerLayoutTest.php new file mode 100644 index 0000000..1d92ef7 --- /dev/null +++ b/tests/unit/Core/ImageComposerLayoutTest.php @@ -0,0 +1,144 @@ +assertSame( [], \PRAutoBlogger_Image_Composer_Layout::clamp_caption( '', 52, 2 ) ); + $this->assertSame( [], \PRAutoBlogger_Image_Composer_Layout::clamp_caption( " \n\t ", 52, 2 ) ); + } + + /** + * A single short word stays on one line, untouched. + */ + public function test_single_word_caption(): void { + $this->assertSame( + [ 'Collagen' ], + \PRAutoBlogger_Image_Composer_Layout::clamp_caption( 'Collagen', 52, 2 ) + ); + } + + /** + * A caption exactly at the per-line budget fills the line with no + * ellipsis and no spill. + */ + public function test_caption_exactly_at_budget_is_not_truncated(): void { + $caption = 'abcde fghij'; // 11 chars, budget 11. + $lines = \PRAutoBlogger_Image_Composer_Layout::clamp_caption( $caption, 11, 1 ); + + $this->assertSame( [ 'abcde fghij' ], $lines ); + } + + /** + * A caption exactly filling max_lines is kept in full (boundary: the + * ellipsis only appears when content is actually dropped). + */ + public function test_caption_exactly_filling_max_lines_keeps_all_words(): void { + $lines = \PRAutoBlogger_Image_Composer_Layout::clamp_caption( 'aa bb cc dd', 5, 2 ); + + $this->assertSame( [ 'aa bb', 'cc dd' ], $lines ); + } + + /** + * 3× the budget gets cut at max_lines with a trailing ellipsis, and no + * line ever exceeds the per-line budget. + */ + public function test_overflowing_caption_is_ellipsized_within_budget(): void { + $caption = 'Collagen fragments may aid joint repair according to early trials in athletes'; + $lines = \PRAutoBlogger_Image_Composer_Layout::clamp_caption( $caption, 20, 2 ); + + $this->assertCount( 2, $lines ); + $this->assertStringEndsWith( \PRAutoBlogger_Image_Composer_Layout::ELLIPSIS, $lines[1] ); + foreach ( $lines as $line ) { + $this->assertLessThanOrEqual( 20, mb_strlen( $line ) ); + } + } + + /** + * Words wrap at word boundaries — a word never straddles two lines when + * it fits on the next one. + */ + public function test_wraps_at_word_boundaries(): void { + $lines = \PRAutoBlogger_Image_Composer_Layout::clamp_caption( 'peptide bonds form chains', 13, 3 ); + + $this->assertSame( [ 'peptide bonds', 'form chains' ], $lines ); + } + + /** + * A single word longer than a full line is hard-split instead of + * overflowing the canvas. + */ + public function test_overlong_single_word_is_hard_split(): void { + $lines = \PRAutoBlogger_Image_Composer_Layout::clamp_caption( 'abcdefghij', 4, 3 ); + + $this->assertSame( [ 'abcd', 'efgh', 'ij' ], $lines ); + } + + /** + * Multibyte captions are measured in characters, not bytes, and the + * ellipsis path keeps each line within the budget. + */ + public function test_multibyte_caption_counts_characters_not_bytes(): void { + $lines = \PRAutoBlogger_Image_Composer_Layout::clamp_caption( 'ペプチドは結合組織を強化する可能性がある', 6, 2 ); + + $this->assertCount( 2, $lines ); + foreach ( $lines as $line ) { + $this->assertLessThanOrEqual( 6, mb_strlen( $line ) ); + } + $this->assertStringEndsWith( \PRAutoBlogger_Image_Composer_Layout::ELLIPSIS, $lines[1] ); + } + + /** + * Interior whitespace runs (newlines, tabs, doubles) collapse to single + * spaces before wrapping. + */ + public function test_whitespace_is_collapsed_before_wrapping(): void { + $lines = \PRAutoBlogger_Image_Composer_Layout::clamp_caption( "joint \n\t repair", 20, 2 ); + + $this->assertSame( [ 'joint repair' ], $lines ); + } + + /** + * Layout defaults carry every key the renderers consume, with the + * spec'd headline geometry (band 120, square slice 569, footer 96). + */ + public function test_defaults_shape_matches_renderer_contract(): void { + $defaults = \PRAutoBlogger_Image_Composer_Layout::defaults(); + + $this->assertSame( [ 'featured', 'og', 'square' ], array_keys( $defaults ) ); + + $this->assertSame( 28, $defaults['featured']['mark_height'] ); + $this->assertSame( 0.55, $defaults['featured']['mark_opacity'] ); + + $this->assertSame( 1200, $defaults['og']['width'] ); + $this->assertSame( 630, $defaults['og']['height'] ); + $this->assertSame( 120, $defaults['og']['band_height'] ); + $this->assertSame( 2, $defaults['og']['caption_max_lines'] ); + + $this->assertSame( 1080, $defaults['square']['width'] ); + $this->assertSame( 1080, $defaults['square']['height'] ); + $this->assertSame( 569, $defaults['square']['slice_height'] ); + $this->assertSame( 96, $defaults['square']['footer_height'] ); + $this->assertSame( 3, $defaults['square']['caption_max_lines'] ); + + foreach ( [ 'og', 'square' ] as $role ) { + foreach ( [ 'caption_font', 'caption_size', 'caption_color', 'caption_chars_per_line', 'caption_line_height' ] as $key ) { + $this->assertArrayHasKey( $key, $defaults[ $role ], "$role missing $key" ); + } + } + } +} diff --git a/tests/unit/Core/ImageComposerTest.php b/tests/unit/Core/ImageComposerTest.php new file mode 100644 index 0000000..0842126 --- /dev/null +++ b/tests/unit/Core/ImageComposerTest.php @@ -0,0 +1,317 @@ + 'raw_base_bytes', + 'mime_type' => 'image/png', + 'width' => 1200, + 'height' => 632, + 'model' => 'flux-1-schnell', + 'cost_usd' => 0.0006, + 'latency_ms' => 1500, + ]; + + protected function setUp(): void { + parent::setUp(); + \PRAutoBlogger_Image_Composer::reset_run_state(); + Functions\when( 'update_option' )->justReturn( true ); + $this->stub_get_option( [] ); // All composer options fall back to defaults. + } + + protected function tearDown(): void { + \PRAutoBlogger_Image_Composer::reset_run_state(); + parent::tearDown(); + } + + /** Helper: composer context for Image A. */ + private function context( string $slot = 'image_a' ): array { + return [ + 'post_id' => 7, + 'caption' => 'Collagen fragments may aid joint repair', + 'title' => 'Collagen Peptides Explained', + 'slot' => $slot, + ]; + } + + /** Helper: a fake rendered variant payload. */ + private function variant( string $role, int $w, int $h ): array { + return [ + 'bytes' => "rendered_{$role}", + 'mime_type' => 'image/png', + 'width' => $w, + 'height' => $h, + 'role' => $role, + ]; + } + + /** + * Compose disabled in settings → single pass-through featured variant, + * renderer untouched. + */ + public function test_disabled_setting_returns_passthrough_only(): void { + $this->stub_get_option( [ \PRAutoBlogger_Image_Composer::OPTION_ENABLED => '0' ] ); + + $renderer = $this->createMock( \PRAutoBlogger_Image_Composer_Imagick::class ); + $renderer->expects( $this->never() )->method( 'compose_featured' ); + $renderer->expects( $this->never() )->method( 'compose_og' ); + + $composer = new \PRAutoBlogger_Image_Composer( $renderer ); + $variants = $composer->compose( $this->image_data, $this->context() ); + + $this->assertCount( 1, $variants ); + $this->assertSame( 'featured', $variants[0]['role'] ); + $this->assertSame( 'raw_base_bytes', $variants[0]['bytes'] ); + } + + /** + * Empty provider bytes short-circuit to pass-through (no probe, no render). + */ + public function test_empty_bytes_return_passthrough(): void { + $composer = new \PRAutoBlogger_Image_Composer( + $this->createMock( \PRAutoBlogger_Image_Composer_Imagick::class ) + ); + $variants = $composer->compose( [ 'bytes' => '' ], $this->context() ); + + $this->assertCount( 1, $variants ); + $this->assertSame( 'featured', $variants[0]['role'] ); + } + + /** + * Ladder rung 1 (Imagick "present" via filter + mocked renderer): + * Image A yields marked featured + og + square, featured first, with the + * caption forwarded to both text-bearing variants. + */ + public function test_imagick_rung_renders_featured_plus_variants(): void { + Filters\expectApplied( 'prautoblogger_image_compose_capability' )->andReturn( 'imagick' ); + + $caption = $this->context()['caption']; + $renderer = $this->createMock( \PRAutoBlogger_Image_Composer_Imagick::class ); + $renderer->expects( $this->once() )->method( 'compose_featured' ) + ->with( 'raw_base_bytes' )->willReturn( $this->variant( 'featured', 1200, 632 ) ); + $renderer->expects( $this->once() )->method( 'compose_og' ) + ->with( 'raw_base_bytes', $caption )->willReturn( $this->variant( 'og', 1200, 630 ) ); + $renderer->expects( $this->once() )->method( 'compose_square' ) + ->with( 'raw_base_bytes', $caption )->willReturn( $this->variant( 'square', 1080, 1080 ) ); + + $composer = new \PRAutoBlogger_Image_Composer( $renderer ); + $variants = $composer->compose( $this->image_data, $this->context() ); + + $this->assertSame( [ 'featured', 'og', 'square' ], array_column( $variants, 'role' ) ); + $this->assertSame( 'rendered_featured', $variants[0]['bytes'] ); + } + + /** + * Featured corner mark toggled off → featured stays the untouched base, + * but social variants still render. + */ + public function test_mark_disabled_passes_featured_through_but_keeps_variants(): void { + Filters\expectApplied( 'prautoblogger_image_compose_capability' )->andReturn( 'imagick' ); + $this->stub_get_option( [ \PRAutoBlogger_Image_Composer::OPTION_MARK_ENABLED => '0' ] ); + + $renderer = $this->createMock( \PRAutoBlogger_Image_Composer_Imagick::class ); + $renderer->expects( $this->never() )->method( 'compose_featured' ); + $renderer->method( 'compose_og' )->willReturn( $this->variant( 'og', 1200, 630 ) ); + $renderer->method( 'compose_square' )->willReturn( $this->variant( 'square', 1080, 1080 ) ); + + $composer = new \PRAutoBlogger_Image_Composer( $renderer ); + $variants = $composer->compose( $this->image_data, $this->context() ); + + $this->assertSame( 'raw_base_bytes', $variants[0]['bytes'] ); + $this->assertSame( [ 'featured', 'og', 'square' ], array_column( $variants, 'role' ) ); + } + + /** + * Image B slot gets the corner mark only — no og/square renders ever. + */ + public function test_image_b_slot_yields_mark_only(): void { + Filters\expectApplied( 'prautoblogger_image_compose_capability' )->andReturn( 'imagick' ); + + $renderer = $this->createMock( \PRAutoBlogger_Image_Composer_Imagick::class ); + $renderer->expects( $this->once() )->method( 'compose_featured' ) + ->willReturn( $this->variant( 'featured', 1200, 632 ) ); + $renderer->expects( $this->never() )->method( 'compose_og' ); + $renderer->expects( $this->never() )->method( 'compose_square' ); + + $composer = new \PRAutoBlogger_Image_Composer( $renderer ); + $variants = $composer->compose( $this->image_data, $this->context( 'image_b' ) ); + + $this->assertCount( 1, $variants ); + $this->assertSame( 'featured', $variants[0]['role'] ); + } + + /** + * The variant option is whitelisted at point of use: unknown tokens are + * dropped, duplicates deduped, config order preserved. + */ + public function test_variant_config_is_whitelisted_and_ordered(): void { + Filters\expectApplied( 'prautoblogger_image_compose_capability' )->andReturn( 'imagick' ); + $this->stub_get_option( + [ \PRAutoBlogger_Image_Composer::OPTION_VARIANTS => ' square, banana ,og, og' ] + ); + + $renderer = $this->createMock( \PRAutoBlogger_Image_Composer_Imagick::class ); + $renderer->method( 'compose_featured' )->willReturn( $this->variant( 'featured', 1200, 632 ) ); + $renderer->expects( $this->once() )->method( 'compose_square' ) + ->willReturn( $this->variant( 'square', 1080, 1080 ) ); + $renderer->expects( $this->once() )->method( 'compose_og' ) + ->willReturn( $this->variant( 'og', 1200, 630 ) ); + + $composer = new \PRAutoBlogger_Image_Composer( $renderer ); + $variants = $composer->compose( $this->image_data, $this->context() ); + + $this->assertSame( [ 'featured', 'square', 'og' ], array_column( $variants, 'role' ) ); + } + + /** + * Acceptance: with every render throwing (e.g. probe lied / Imagick + * flipped mid-run), compose() still returns the publishable pass-through + * featured variant and never lets the exception escape. + */ + public function test_renderer_failure_degrades_to_passthrough(): void { + Filters\expectApplied( 'prautoblogger_image_compose_capability' )->andReturn( 'imagick' ); + + $renderer = $this->createMock( \PRAutoBlogger_Image_Composer_Imagick::class ); + $renderer->method( 'compose_featured' )->willThrowException( new \RuntimeException( 'no imagick after all' ) ); + $renderer->method( 'compose_og' )->willThrowException( new \RuntimeException( 'no imagick after all' ) ); + $renderer->method( 'compose_square' )->willThrowException( new \RuntimeException( 'no imagick after all' ) ); + + $composer = new \PRAutoBlogger_Image_Composer( $renderer ); + $variants = $composer->compose( $this->image_data, $this->context() ); + + $this->assertCount( 1, $variants ); + $this->assertSame( 'featured', $variants[0]['role'] ); + $this->assertSame( 'raw_base_bytes', $variants[0]['bytes'] ); + } + + /** + * Ladder rung 3 forced via the override filter: pass-through, no renderer + * involvement, publish data intact. + */ + public function test_capability_none_passes_through(): void { + Filters\expectApplied( 'prautoblogger_image_compose_capability' )->andReturn( 'none' ); + + $renderer = $this->createMock( \PRAutoBlogger_Image_Composer_Imagick::class ); + $renderer->expects( $this->never() )->method( 'compose_featured' ); + + $composer = new \PRAutoBlogger_Image_Composer( $renderer ); + $variants = $composer->compose( $this->image_data, $this->context() ); + + $this->assertCount( 1, $variants ); + $this->assertSame( 'raw_base_bytes', $variants[0]['bytes'] ); + $this->assertSame( 1200, $variants[0]['width'] ); + $this->assertSame( 632, $variants[0]['height'] ); + } + + /** + * A garbage filter return is coerced to 'none' (pass-through), never an + * undefined rung. + */ + public function test_unknown_capability_value_is_treated_as_none(): void { + Filters\expectApplied( 'prautoblogger_image_compose_capability' )->andReturn( 'webgpu' ); + + $composer = new \PRAutoBlogger_Image_Composer( + $this->createMock( \PRAutoBlogger_Image_Composer_Imagick::class ) + ); + $variants = $composer->compose( $this->image_data, $this->context() ); + + $this->assertCount( 1, $variants ); + $this->assertSame( 'featured', $variants[0]['role'] ); + } + + /** + * A cached probe with a matching environment fingerprint is reused — + * no re-probe, no option write. + */ + public function test_matching_capability_cache_skips_probe_write(): void { + $fingerprint = md5( + PHP_VERSION . '|' . ( extension_loaded( 'imagick' ) ? 'im1' : 'im0' ) . '|' . ( extension_loaded( 'gd' ) ? 'gd1' : 'gd0' ) + ); + $this->stub_get_option( + [ + \PRAutoBlogger_Image_Composer::OPTION_CAPABILITY => [ + 'fingerprint' => $fingerprint, + 'capability' => 'none', + ], + ] + ); + Functions\expect( 'update_option' )->never(); + + $composer = new \PRAutoBlogger_Image_Composer( + $this->createMock( \PRAutoBlogger_Image_Composer_Imagick::class ) + ); + $variants = $composer->compose( $this->image_data, $this->context() ); + + $this->assertSame( 'featured', $variants[0]['role'] ); + } + + /** + * A stale fingerprint (host changed) triggers a fresh probe and a cache + * rewrite — the auto-invalidation the spec requires. + */ + public function test_stale_capability_cache_reprobes_and_rewrites(): void { + $this->stub_get_option( + [ + \PRAutoBlogger_Image_Composer::OPTION_CAPABILITY => [ + 'fingerprint' => 'old-host-fingerprint', + 'capability' => 'imagick', + ], + ] + ); + Functions\expect( 'update_option' ) + ->once() + ->with( + \PRAutoBlogger_Image_Composer::OPTION_CAPABILITY, + \Mockery::on( + static function ( $value ): bool { + return is_array( $value ) + && isset( $value['fingerprint'], $value['capability'] ) + && 'old-host-fingerprint' !== $value['fingerprint']; + } + ), + false + ) + ->andReturn( true ); + + $composer = new \PRAutoBlogger_Image_Composer( + $this->createMock( \PRAutoBlogger_Image_Composer_Imagick::class ) + ); + $variants = $composer->compose( $this->image_data, $this->context() ); + + $this->assertSame( 'featured', $variants[0]['role'] ); + } + + /** + * QA note for the uninstall contract: every new option key keeps the + * `prautoblogger_` prefix so uninstall.php's prefix-sweep purges it. + * (Per spec, uninstall.php itself is intentionally untouched.) + */ + public function test_new_option_keys_keep_uninstall_sweep_prefix(): void { + $keys = [ + \PRAutoBlogger_Image_Composer::OPTION_ENABLED, + \PRAutoBlogger_Image_Composer::OPTION_VARIANTS, + \PRAutoBlogger_Image_Composer::OPTION_MARK_ENABLED, + \PRAutoBlogger_Image_Composer::OPTION_CAPABILITY, + ]; + + foreach ( $keys as $key ) { + $this->assertStringStartsWith( 'prautoblogger_', $key ); + } + } +} diff --git a/tests/unit/Core/ImagePipelineTest.php b/tests/unit/Core/ImagePipelineTest.php index 043fd19..3bbd49b 100644 --- a/tests/unit/Core/ImagePipelineTest.php +++ b/tests/unit/Core/ImagePipelineTest.php @@ -18,6 +18,11 @@ class ImagePipelineTest extends BaseTestCase { protected function setUp(): void { parent::setUp(); + // v0.17.0: the composer caches its capability probe via update_option + // and keeps a once-per-run warning flag that must not leak between tests. + \PRAutoBlogger_Image_Composer::reset_run_state(); + Functions\when( 'update_option' )->justReturn( true ); + // Mock WordPress functions used by the pipeline and its dependencies. Functions\when( 'is_wp_error' )->alias( function ( $thing ) { return $thing instanceof \WP_Error; @@ -346,6 +351,67 @@ class ImagePipelineTest extends BaseTestCase { $this->assertNotEmpty( $result['errors'] ); } + /** + * v0.17.0: composed OG/square variants are sideloaded and meta-linked — + * role + base-attachment meta on the variant attachments, role-specific + * image ID meta on the post, and the featured attachment tagged with its + * role. Composition adds no cost-tracker rows beyond the primary image. + */ + public function test_composed_variants_are_sideloaded_and_meta_linked(): void { + $image_result = [ + 'bytes' => 'fake_image_data', + 'mime_type' => 'image/png', + 'width' => 1200, + 'height' => 632, + 'model' => 'test-model', + 'cost_usd' => 0.05, + 'latency_ms' => 2000, + ]; + + $provider = $this->createMock( \PRAutoBlogger_Image_Provider_Interface::class ); + $provider->method( 'estimate_cost' )->willReturn( 0.05 ); + $provider->method( 'generate_image_batch' )->willReturn( [ 'image_a' => $image_result ] ); + + $cost_tracker = $this->createMock( \PRAutoBlogger_Cost_Tracker::class ); + $cost_tracker->method( 'would_exceed_budget' )->willReturn( false ); + // Exactly one cost row: the primary image. Variants are $0 local renders. + $cost_tracker->expects( $this->exactly( 1 ) )->method( 'log_image_generation' ); + + $composer = new class() implements \PRAutoBlogger_Image_Composer_Interface { + public function compose( array $image_data, array $context ): array { + return [ + [ 'bytes' => 'marked', 'mime_type' => 'image/png', 'width' => 1200, 'height' => 632, 'role' => 'featured' ], + [ 'bytes' => 'og_png', 'mime_type' => 'image/png', 'width' => 1200, 'height' => 630, 'role' => 'og' ], + [ 'bytes' => 'sq_png', 'mime_type' => 'image/png', 'width' => 1080, 'height' => 1080, 'role' => 'square' ], + ]; + } + }; + + Functions\when( 'get_temp_dir' )->justReturn( '/tmp/' ); + $next_id = 41; + Functions\when( 'media_handle_sideload' )->alias( function () use ( &$next_id ) { + return ++$next_id; // 42 featured, 43 og, 44 square. + } ); + + $meta_calls = []; + Functions\when( 'update_post_meta' )->alias( function ( $object_id, $key, $value ) use ( &$meta_calls ) { + $meta_calls[] = [ $object_id, $key, $value ]; + return true; + } ); + + $pipeline = new \PRAutoBlogger_Image_Pipeline( $provider, $cost_tracker, null, $composer ); + $result = $pipeline->generate_and_attach_images( 1, [ 'post_title' => 'Test' ], null ); + + $this->assertSame( 42, $result['image_a_id'] ); + $this->assertContains( [ 42, '_prautoblogger_image_role', 'featured' ], $meta_calls ); + $this->assertContains( [ 43, '_prautoblogger_image_role', 'og' ], $meta_calls ); + $this->assertContains( [ 43, '_prautoblogger_base_attachment_id', 42 ], $meta_calls ); + $this->assertContains( [ 1, '_prautoblogger_og_image_id', 43 ], $meta_calls ); + $this->assertContains( [ 44, '_prautoblogger_image_role', 'square' ], $meta_calls ); + $this->assertContains( [ 44, '_prautoblogger_base_attachment_id', 42 ], $meta_calls ); + $this->assertContains( [ 1, '_prautoblogger_square_image_id', 44 ], $meta_calls ); + } + /** * Test graceful handling when one image in the batch returns an error. */