docs: add build commands and architecture overview to AGENTS.md

Add build/test/lint commands, architecture overview, PHP/Vue/test
code style conventions, and key project constraints. Include dompdf
config, vite HMR note, and sisyphus evidence files.
This commit is contained in:
Thorsten Bus 2026-03-02 23:03:14 +01:00
parent 149389a382
commit fa3162b2b7
8 changed files with 749 additions and 0 deletions

View file

@ -0,0 +1,8 @@
hasFill:YES
fillEnabled:NO
hasStroke:YES
strokeEnabled:NO
hasShadow:YES
shadowEnabled:NO
hasFeather:YES
featherEnabled:NO

View file

@ -0,0 +1,101 @@
TASK: Auto-select default arrangement on song match
DATE: 2026-03-02
STATUS: COMPLETE ✓
IMPLEMENTATION SUMMARY
======================
1. Modified SongMatchingService.php (app/Services/SongMatchingService.php)
- autoMatch() method (lines 34-40): Added arrangement lookup logic
- manualAssign() method (lines 65-76): Added conditional arrangement setting
2. Added 4 new tests to SongMatchingTest.php
- autoMatch setzt song_arrangement_id auf Standard-Arrangement
- autoMatch bevorzugt is_default=true Arrangement
- autoMatch nutzt erstes Arrangement wenn kein Standard vorhanden
- manualAssign setzt song_arrangement_id wenn null
- manualAssign behält bestehende song_arrangement_id bei
ARRANGEMENT SELECTION PRIORITY
==============================
1. is_default = true
2. name = 'normal'
3. first arrangement (any)
4. null (if no arrangements exist)
BEHAVIOR
========
autoMatch():
- ALWAYS sets song_arrangement_id after matching song
- Uses priority order: is_default → name='normal' → first
- Handles case where song has no arrangements (sets to null)
manualAssign():
- ONLY sets song_arrangement_id if currently null
- Preserves existing arrangement selection when reassigning song
- Uses same priority order as autoMatch()
TEST RESULTS
============
All 20 SongMatchingTest tests PASS:
✓ autoMatch ordnet Song per CCLI-ID zu
✓ autoMatch nutzt CTS-Song-ID als Fallback wenn keine CCLI passt
✓ autoMatch gibt false zurück wenn kein CCLI-ID vorhanden
✓ autoMatch gibt false zurück wenn kein passender Song in DB
✓ autoMatch überspringt bereits zugeordnete Songs
✓ autoMatch setzt song_arrangement_id auf Standard-Arrangement [NEW]
✓ autoMatch bevorzugt is_default=true Arrangement [NEW]
✓ autoMatch nutzt erstes Arrangement wenn kein Standard vorhanden [NEW]
✓ manualAssign ordnet Song manuell zu
✓ manualAssign überschreibt bestehende Zuordnung
✓ manualAssign setzt song_arrangement_id wenn null [NEW]
✓ manualAssign behält bestehende song_arrangement_id bei [NEW]
✓ requestCreation sendet E-Mail und setzt request_sent_at
✓ unassign entfernt Zuordnung
✓ POST /api/service-songs/{id}/assign ordnet Song zu
✓ POST /api/service-songs/{id}/assign validiert song_id
✓ POST /api/service-songs/{id}/request sendet Anfrage-E-Mail
✓ POST /api/service-songs/{id}/unassign entfernt Zuordnung
✓ API Endpunkte erfordern Authentifizierung
✓ API gibt 404 für nicht existierende ServiceSong
Duration: 0.47s
Tests: 20 passed (45 assertions)
CODE QUALITY
============
✓ No LSP errors in SongMatchingService.php
✓ Follows Laravel code style conventions
✓ Uses nullsafe operator (?->)
✓ Uses null coalescing (??)
✓ Proper type hints and return types
✓ Clear comments explaining logic
VERIFICATION CHECKLIST
======================
✓ autoMatch() sets song_arrangement_id to default/normal/first arrangement
✓ manualAssign() sets arrangement ONLY if currently null
✓ New tests verify auto-arrangement selection
✓ New tests verify arrangement preservation
✓ All 20 SongMatching tests pass
✓ No regressions in existing tests
✓ Code follows project conventions
✓ LSP diagnostics clean
FILES MODIFIED
==============
1. app/Services/SongMatchingService.php
- autoMatch() method: Added arrangement lookup (lines 34-40)
- manualAssign() method: Added conditional arrangement setting (lines 65-76)
2. tests/Feature/SongMatchingTest.php
- Added SongArrangement import
- Added 4 new test cases for arrangement selection
NEXT STEPS
==========
Ready for commit:
git add app/Services/SongMatchingService.php tests/Feature/SongMatchingTest.php
git commit -m "feat(songs): auto-select default arrangement on song match"

View file

@ -0,0 +1,154 @@
# Task 6: Translated Textbox Positioning - QA Evidence
## Test 1: Translated Slide Has Correct Dual Bounds
### Command
```bash
cd /Users/thorsten/AI/cts-work && php -r "
require 'vendor/autoload.php';
use ProPresenter\Parser\ProFileGenerator;
use ProPresenter\Parser\ProFileWriter;
use ProPresenter\Parser\ProFileReader;
\$song = ProFileGenerator::generate('TranslateTest',
[['name'=>'V1','color'=>[0,0,0,1],'slides'=>[['text'=>'Amazing Grace','translation'=>'Erstaunliche Gnade']]]],
[['name'=>'normal','groupNames'=>['V1']]]
);
ProFileWriter::write(\$song, '/tmp/translate-test.pro');
\$readSong = ProFileReader::read('/tmp/translate-test.pro');
\$slides = \$readSong->getSlides();
\$elements = \$slides[0]->getAllElements();
echo 'count: ' . count(\$elements) . PHP_EOL;
echo 'name0: ' . \$elements[0]->getName() . PHP_EOL;
echo 'name1: ' . \$elements[1]->getName() . PHP_EOL;
\$b0 = \$elements[0]->getGraphicsElement()->getBounds();
\$b1 = \$elements[1]->getGraphicsElement()->getBounds();
echo 'height0: ' . round(\$b0->getSize()->getHeight(), 1) . PHP_EOL;
echo 'height1: ' . round(\$b1->getSize()->getHeight(), 1) . PHP_EOL;
echo 'y0: ' . round(\$b0->getOrigin()->getY(), 3) . PHP_EOL;
echo 'y1: ' . round(\$b1->getOrigin()->getY(), 3) . PHP_EOL;
"
```
### Output
```
count: 2
name0: Orginal
name1: Deutsch
height0: 182.9
height1: 113.9
y0: 99.543
y1: 303.166
```
### Verification
✅ Element count: 2 (expected: 2)
✅ Element 0 name: "Orginal" (expected: "Orginal")
✅ Element 1 name: "Deutsch" (expected: "Deutsch")
✅ Element 0 height: 182.9px (expected: ~182.946px)
✅ Element 1 height: 113.9px (expected: ~113.889px)
✅ Element 0 Y position: 99.543 (expected: 99.543)
✅ Element 1 Y position: 303.166 (expected: 303.166)
**Result**: PASS - Translated slides have correctly positioned dual textboxes matching TestTranslated.pro reference
---
## Test 2: Non-Translated Slide Has Single Full Bounds
### Command
```bash
cd /Users/thorsten/AI/cts-work && php -r "
require 'vendor/autoload.php';
use ProPresenter\Parser\ProFileGenerator;
use ProPresenter\Parser\ProFileWriter;
use ProPresenter\Parser\ProFileReader;
\$song = ProFileGenerator::generate('NoTranslateTest',
[['name'=>'V1','color'=>[0,0,0,1],'slides'=>[['text'=>'Amazing Grace']]]],
[['name'=>'normal','groupNames'=>['V1']]]
);
ProFileWriter::write(\$song, '/tmp/no-translate-test.pro');
\$readSong = ProFileReader::read('/tmp/no-translate-test.pro');
\$slides = \$readSong->getSlides();
\$elements = \$slides[0]->getAllElements();
echo 'count: ' . count(\$elements) . PHP_EOL;
echo 'name0: ' . \$elements[0]->getName() . PHP_EOL;
\$b = \$elements[0]->getGraphicsElement()->getBounds();
echo 'height: ' . round(\$b->getSize()->getHeight(), 1) . PHP_EOL;
echo 'width: ' . round(\$b->getSize()->getWidth(), 1) . PHP_EOL;
echo 'y: ' . round(\$b->getOrigin()->getY(), 1) . PHP_EOL;
echo 'x: ' . round(\$b->getOrigin()->getX(), 1) . PHP_EOL;
"
```
### Output
```
count: 1
name0: Orginal
height: 880
width: 1620
y: 100
x: 150
```
### Verification
✅ Element count: 1 (expected: 1)
✅ Element 0 name: "Orginal" (expected: "Orginal")
✅ Height: 880px (expected: 880px)
✅ Width: 1620px (expected: 1620px)
✅ Y position: 100 (expected: 100)
✅ X position: 150 (expected: 150)
**Result**: PASS - Non-translated slides keep single full-size textbox
---
## PHPUnit Tests
### Test: testTranslatedSlideHasCorrectDualBounds
```bash
./vendor/bin/phpunit vendor/propresenter/parser/tests/ProFileGeneratorTest.php --filter testTranslatedSlideHasCorrectDualBounds
```
**Result**: OK (1 test, 7 assertions)
### Test: testNonTranslatedSlideHasSingleFullBounds
```bash
./vendor/bin/phpunit vendor/propresenter/parser/tests/ProFileGeneratorTest.php --filter testNonTranslatedSlideHasSingleFullBounds
```
**Result**: OK (1 test, 6 assertions)
### All ProFileGeneratorTest Tests
```bash
./vendor/bin/phpunit vendor/propresenter/parser/tests/ProFileGeneratorTest.php
```
**Result**: OK (14 tests, 95 assertions)
---
## Laravel Tests
### Command
```bash
php -d memory_limit=512M artisan test
```
**Result**: Tests: 203 passed (1115 assertions), Duration: 3.89s
---
## Summary
✅ All acceptance criteria met:
- [x] Two new methods created: buildOriginalBounds(), buildTranslationBounds()
- [x] Translated slides have 2 elements with different bounds (heights: ~183px, ~114px)
- [x] Non-translated slides keep single element with full bounds (1620×880)
- [x] Textbox names unchanged: "Orginal" (typo intentional), "Deutsch"
- [x] New test: translated slide has correct dual bounds
- [x] New test: non-translated slide has single full-size bounds
- [x] PHPUnit tests pass (14/14)
- [x] Laravel tests pass (203/203)
**Task Status**: COMPLETE
**Date**: 2026-03-02

View file

@ -0,0 +1,21 @@
Task 8 Manual Bundle Verification
Datum: 2026-03-02
Erzeugter Bundle-Pfad:
/var/folders/jf/qly82l3s6cg19v82rm_h0q9h0000gn/T/information-69a5fdfc20077.probundle
Befehl:
unzip -l /var/folders/jf/qly82l3s6cg19v82rm_h0q9h0000gn/T/information-69a5fdfc20077.probundle
Ausgabe:
Archive: /var/folders/jf/qly82l3s6cg19v82rm_h0q9h0000gn/T/information-69a5fdfc20077.probundle
Length Date Time Name
--------- ---------- ----- ----
1323 03-02-2026 22:15 information.pro
12 03-02-2026 22:15 manual-bundle-1.jpg
--------- -------
1335 2 files
Ergebnis:
- Enthalten: 1 .pro Datei + 1 Bilddatei
- Struktur ist flach (Root-Level), wie gefordert.

View file

@ -0,0 +1,49 @@
# Learnings: ProPresenter Generator and UI Fixes
## Task 6: Translated Textbox Positioning (2026-03-02)
### Implementation Pattern
- **Dual bounds methods**: Created `buildOriginalBounds()` and `buildTranslationBounds()` following same pattern as existing `buildBounds()`
- **Conditional logic in buildCue()**: Check for translation presence, use different bounds for each element
- **Optional parameter pattern**: Modified `buildSlideElement()` to accept `?Rect $bounds = null` parameter, defaulting to `buildBounds()` for backward compatibility
### Exact Values from TestTranslated.pro
- Original textbox: origin(150, 99.543), size(1620×182.946) — top position, ~183px tall
- Translation textbox: origin(150, 303.166), size(1620×113.889) — below, ~114px tall
- Non-translated: origin(150, 100), size(1620×880) — full height
### Testing Approach
- **PHPUnit tests**: Added two new tests to verify bounds for translated and non-translated slides
- **QA scenarios**: Used PHP CLI to generate, write, read back, and verify exact positioning values
- **Method access**: Used `getGraphicsElement()->getBounds()` to access protobuf bounds from TextElement wrapper
### Git Workflow
- **Symlinked vendor**: `vendor/propresenter/parser` is symlink to `/Users/thorsten/AI/propresenter-work/php`
- **Commit location**: Changes committed in propresenter-work repo, not cts-work
- **Branch**: propresenter-parser
### Test Results
- PHPUnit: 14/14 tests pass (95 assertions)
- Laravel: 203/203 tests pass (1115 assertions)
- QA verification: All positioning values match TestTranslated.pro reference exactly
## Task 8: .probundle Export fuer Service-Slide-Bloecke (2026-03-02)
### Export-Pattern
- Fuer ZIP-basierte Exporte ist `Storage::disk("public")->path(...)` teststabiler als harte `storage_path(...)`-Pfade, weil `Storage::fake("public")` dann direkt funktioniert.
- `.probundle` wurde als flaches ZIP umgesetzt: genau eine `.pro` plus alle Bilddateien auf Root-Ebene.
- Medienreferenzen in der `.pro` funktionieren robust mit Dateinamen (Root-Datei im Bundle) statt absoluten Pfaden.
### Controller-Integration
- Route-Parameter fuer Blocktyp (`information|moderation|sermon`) lassen sich sauber per Request-Validation nach `merge()` pruefen und liefern bei Fehlern korrekt 422 in JSON-Requests.
- Download-Response fuer temporaere Bundle-Dateien mit `deleteFileAfterSend(true)` vermeidet Temp-Muell.
### Test-Pattern
- Fuer BinaryFileResponse in Feature-Tests: Datei aus Response in eigenen Temp-Pfad kopieren und dann mit `ZipArchive` validieren.
- Vollsuite hatte einen sporadischen bestehenden Flake in `SongPdfTest`; Re-Run lief voll gruen (206/206).
## Scope-Audit Learnings (F4, 2026-03-02)
- Fuer strikte Scope-Fidelity prueft man nicht nur Dateiliste pro Commit, sondern auch unerwartete Hunks innerhalb der erwarteten Dateien (z.B. Header-Navigation oder Bulk-Delete-Features in einem Export-Task).
- Wenn ein Task auf vendor-Dateien zielt, die im Repo nicht versioniert sind, ist der Task aus Sicht des Git-Diffs nicht nachweisbar umgesetzt ("missing", auch wenn QA-Evidence existiert).
- Commit-Message-Match allein reicht nicht: Task 1/4/7/8 zeigen, dass ein passender Commit-Titel trotzdem deutliche Scope-Creep enthalten kann.

112
AGENTS.md
View file

@ -95,3 +95,115 @@ ## SongDB Import
- download: download generated .pro file from the songDB for this song
- translate: allow add a full text or an URL to the Full text, and then start an editor, that shows two columns for every slide of every group. Left the original text, right a texteditor, with the imported text - always the same line qty of text from the original is used from the given translated text. Save this as translation for this song, and mark it as `with translation`.
- UploadArea for drag'n'drop and click for upload, to upload a .pro file, a zip file with multiple .pro files, or a bunch of .pro files, which should be parsed (this module was integrated later, so show an Exception here till this was finalized) and added into the song DB.
---
## Build, Test, Lint Commands
```bash
# Setup (first time)
composer setup
# Dev server (Laravel + Vite + Queue + Logs via concurrently)
composer dev
# Build frontend
npm run build
# Run all PHP tests (clears config cache first)
composer test
# or directly:
php artisan test
# Run a single PHP test file
php artisan test tests/Feature/ServiceControllerTest.php
# Run a single test method
php artisan test --filter=test_service_kann_abgeschlossen_werden
# Run only Unit or Feature suite
php artisan test --testsuite=Unit
php artisan test --testsuite=Feature
# PHP code formatting (Laravel Pint - default Laravel preset)
./vendor/bin/pint
# Check only (no changes):
./vendor/bin/pint --test
# Run e2e tests (requires running dev server at http://cts-work.test)
npx playwright test
# Single e2e file:
npx playwright test tests/e2e/service-list.spec.ts
# Migrations
php artisan migrate
```
## Architecture Overview
```
app/
Http/Controllers/ # Inertia controllers, return Inertia::render() or JSON
Http/Requests/ # Form request validation
Http/Middleware/ # HandleInertiaRequests shares props
Models/ # Eloquent models with factories in database/factories/
Services/ # Business logic (ChurchToolsService, SongService, etc.)
Jobs/ # Queue jobs (PowerPoint conversion)
Mail/ # Mailable classes
Cts/ # CTS API spike/sync utilities
resources/js/
Pages/ # Vue page components (mapped via Inertia::render)
Components/ # Reusable Vue components
Composables/ # Vue composables (useAutoSave)
Layouts/ # AuthenticatedLayout, GuestLayout, MainLayout
tests/
Feature/ # HTTP/integration tests (class-based, PHPUnit style)
Unit/ # Unit tests
e2e/ # Playwright browser tests (TypeScript)
```
## Code Style — PHP
- **Formatter**: Laravel Pint (default Laravel preset, no custom config)
- **Indentation**: 4 spaces
- **Imports**: Fully qualified, one per line, grouped (PHP classes, then Laravel, then app)
- **Models**: Use `$fillable` array (not `$guarded`). Use `casts()` method (not `$casts` property). Relationships return typed `HasMany`/`BelongsTo`/etc.
- **Controllers**: Return type hints (`Response`, `JsonResponse`, `RedirectResponse`). Use route-model binding. Use `Inertia::render()` for page responses.
- **Migrations**: Anonymous class style: `return new class () extends Migration { ... }`
- **Error messages**: German. Flash via `->with('success', '...')`. JSON errors use `message` key.
- **Null safety**: Use nullsafe operator `?->` and null coalescing `??`
- **DB operations**: Prefer Eloquent, fall back to `DB::table()` for bulk upserts in sync code
- **SoftDeletes**: Used on `Song` model. Use `whereNull('deleted_at')` in manual queries.
## Code Style — Vue / Frontend
- **Vue 3 Composition API** only, always `<script setup>`. No Options API.
- **Props**: `defineProps({ propName: { type: Type, default: value } })`
- **Emits**: `defineEmits(['event-name'])`
- **Imports**: Use `@/` alias for `resources/js/`. Vue imports from `'vue'`, Inertia from `'@inertiajs/vue3'`.
- **Functions**: Prefer `function name() {}` declarations in components (not `const name = () => {}`)
- **Styling**: Tailwind CSS v4 utility classes inline. Scoped `<style>` only when necessary (e.g. drag-and-drop).
- **State**: `ref()` for reactive state, `computed()` for derived. Use `watch()` for side effects.
- **Routing**: Use `route('name', params)` (Ziggy) for URL generation. Use `router.post/get/delete` from Inertia.
- **Testing attributes**: Add `data-testid="..."` on interactive elements for Playwright e2e tests.
- **All user-facing text must be German** (Du-form, not Sie).
## Code Style — Tests
- **Framework**: Pest v4 (wraps PHPUnit). Feature tests are class-based extending `TestCase` with `RefreshDatabase`.
- **Naming**: `test_snake_case_german_description` (e.g. `test_service_kann_abgeschlossen_werden`)
- **Auth**: `$this->actingAs(User::factory()->create())`
- **Vite**: Call `$this->withoutVite()` before testing Inertia page renders
- **Time**: Use `Carbon::setTestNow('2026-03-01 10:00:00')` for deterministic time
- **Assertions**: `assertInertia(fn ($page) => $page->component('...')->has('...')->where('...'))` for Inertia responses
- **DB**: Tests use in-memory SQLite (configured in `phpunit.xml`)
- **e2e**: Playwright (TypeScript), `tests/e2e/`, baseURL `http://cts-work.test`, auth via `auth.setup.ts`
## Key Conventions
- **CTS API is READ-ONLY** — never write/modify data via ChurchTools API
- **Immediate persistence** — all user actions save instantly, no separate "save" button
- **German locale**`APP_LOCALE=de`, all UI text in German, Du-form
- **File uploads** — images convert to JPG 1920x1080 (maintain aspect ratio, no cropping); PPT/PPTX convert to multiple JPGs
- **Named routes** — all routes have names, use `route('name')` everywhere
- **ProPresenter (.pro) parser** — placeholder only, not yet implemented

301
config/dompdf.php Normal file
View file

@ -0,0 +1,301 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Settings
|--------------------------------------------------------------------------
|
| Set some default values. It is possible to add all defines that can be set
| in dompdf_config.inc.php. You can also override the entire config file.
|
*/
'show_warnings' => false, // Throw an Exception on warnings from dompdf
'public_path' => null, // Override the public path if needed
/*
* Dejavu Sans font is missing glyphs for converted entities, turn it off if you need to show and £.
*/
'convert_entities' => true,
'options' => [
/**
* The location of the DOMPDF font directory
*
* The location of the directory where DOMPDF will store fonts and font metrics
* Note: This directory must exist and be writable by the webserver process.
* *Please note the trailing slash.*
*
* Notes regarding fonts:
* Additional .afm font metrics can be added by executing load_font.php from command line.
*
* Only the original "Base 14 fonts" are present on all pdf viewers. Additional fonts must
* be embedded in the pdf file or the PDF may not display correctly. This can significantly
* increase file size unless font subsetting is enabled. Before embedding a font please
* review your rights under the font license.
*
* Any font specification in the source HTML is translated to the closest font available
* in the font directory.
*
* The pdf standard "Base 14 fonts" are:
* Courier, Courier-Bold, Courier-BoldOblique, Courier-Oblique,
* Helvetica, Helvetica-Bold, Helvetica-BoldOblique, Helvetica-Oblique,
* Times-Roman, Times-Bold, Times-BoldItalic, Times-Italic,
* Symbol, ZapfDingbats.
*/
'font_dir' => storage_path('fonts'), // advised by dompdf (https://github.com/dompdf/dompdf/pull/782)
/**
* The location of the DOMPDF font cache directory
*
* This directory contains the cached font metrics for the fonts used by DOMPDF.
* This directory can be the same as DOMPDF_FONT_DIR
*
* Note: This directory must exist and be writable by the webserver process.
*/
'font_cache' => storage_path('fonts'),
/**
* The location of a temporary directory.
*
* The directory specified must be writeable by the webserver process.
* The temporary directory is required to download remote images and when
* using the PDFLib back end.
*/
'temp_dir' => sys_get_temp_dir(),
/**
* ==== IMPORTANT ====
*
* dompdf's "chroot": Prevents dompdf from accessing system files or other
* files on the webserver. All local files opened by dompdf must be in a
* subdirectory of this directory. DO NOT set it to '/' since this could
* allow an attacker to use dompdf to read any files on the server. This
* should be an absolute path.
* This is only checked on command line call by dompdf.php, but not by
* direct class use like:
* $dompdf = new DOMPDF(); $dompdf->load_html($htmldata); $dompdf->render(); $pdfdata = $dompdf->output();
*/
'chroot' => realpath(base_path()),
/**
* Protocol whitelist
*
* Protocols and PHP wrappers allowed in URIs, and the validation rules
* that determine if a resouce may be loaded. Full support is not guaranteed
* for the protocols/wrappers specified
* by this array.
*
* @var array
*/
'allowed_protocols' => [
'data://' => ['rules' => []],
'file://' => ['rules' => []],
'http://' => ['rules' => []],
'https://' => ['rules' => []],
],
/**
* Operational artifact (log files, temporary files) path validation
*/
'artifactPathValidation' => null,
/**
* @var string
*/
'log_output_file' => null,
/**
* Whether to enable font subsetting or not.
*/
'enable_font_subsetting' => false,
/**
* The PDF rendering backend to use
*
* Valid settings are 'PDFLib', 'CPDF' (the bundled R&OS PDF class), 'GD' and
* 'auto'. 'auto' will look for PDFLib and use it if found, or if not it will
* fall back on CPDF. 'GD' renders PDFs to graphic files.
* {@link * Canvas_Factory} ultimately determines which rendering class to
* instantiate based on this setting.
*
* Both PDFLib & CPDF rendering backends provide sufficient rendering
* capabilities for dompdf, however additional features (e.g. object,
* image and font support, etc.) differ between backends. Please see
* {@link PDFLib_Adapter} for more information on the PDFLib backend
* and {@link CPDF_Adapter} and lib/class.pdf.php for more information
* on CPDF. Also see the documentation for each backend at the links
* below.
*
* The GD rendering backend is a little different than PDFLib and
* CPDF. Several features of CPDF and PDFLib are not supported or do
* not make any sense when creating image files. For example,
* multiple pages are not supported, nor are PDF 'objects'. Have a
* look at {@link GD_Adapter} for more information. GD support is
* experimental, so use it at your own risk.
*
* @link http://www.pdflib.com
* @link http://www.ros.co.nz/pdf
* @link http://www.php.net/image
*/
'pdf_backend' => 'CPDF',
/**
* html target media view which should be rendered into pdf.
* List of types and parsing rules for future extensions:
* http://www.w3.org/TR/REC-html40/types.html
* screen, tty, tv, projection, handheld, print, braille, aural, all
* Note: aural is deprecated in CSS 2.1 because it is replaced by speech in CSS 3.
* Note, even though the generated pdf file is intended for print output,
* the desired content might be different (e.g. screen or projection view of html file).
* Therefore allow specification of content here.
*/
'default_media_type' => 'screen',
/**
* The default paper size.
*
* North America standard is "letter"; other countries generally "a4"
*
* @see CPDF_Adapter::PAPER_SIZES for valid sizes ('letter', 'legal', 'A4', etc.)
*/
'default_paper_size' => 'a4',
/**
* The default paper orientation.
*
* The orientation of the page (portrait or landscape).
*
* @var string
*/
'default_paper_orientation' => 'portrait',
/**
* The default font family
*
* Used if no suitable fonts can be found. This must exist in the font folder.
*
* @var string
*/
'default_font' => 'serif',
/**
* Image DPI setting
*
* This setting determines the default DPI setting for images and fonts. The
* DPI may be overridden for inline images by explictly setting the
* image's width & height style attributes (i.e. if the image's native
* width is 600 pixels and you specify the image's width as 72 points,
* the image will have a DPI of 600 in the rendered PDF. The DPI of
* background images can not be overridden and is controlled entirely
* via this parameter.
*
* For the purposes of DOMPDF, pixels per inch (PPI) = dots per inch (DPI).
* If a size in html is given as px (or without unit as image size),
* this tells the corresponding size in pt.
* This adjusts the relative sizes to be similar to the rendering of the
* html page in a reference browser.
*
* In pdf, always 1 pt = 1/72 inch
*
* Rendering resolution of various browsers in px per inch:
* Windows Firefox and Internet Explorer:
* SystemControl->Display properties->FontResolution: Default:96, largefonts:120, custom:?
* Linux Firefox:
* about:config *resolution: Default:96
* (xorg screen dimension in mm and Desktop font dpi settings are ignored)
*
* Take care about extra font/image zoom factor of browser.
*
* In images, <img> size in pixel attribute, img css style, are overriding
* the real image dimension in px for rendering.
*
* @var int
*/
'dpi' => 96,
/**
* Enable embedded PHP
*
* If this setting is set to true then DOMPDF will automatically evaluate embedded PHP contained
* within <script type="text/php"> ... </script> tags.
*
* ==== IMPORTANT ==== Enabling this for documents you do not trust (e.g. arbitrary remote html pages)
* is a security risk.
* Embedded scripts are run with the same level of system access available to dompdf.
* Set this option to false (recommended) if you wish to process untrusted documents.
* This setting may increase the risk of system exploit.
* Do not change this settings without understanding the consequences.
* Additional documentation is available on the dompdf wiki at:
* https://github.com/dompdf/dompdf/wiki
*
* @var bool
*/
'enable_php' => false,
/**
* Rnable inline JavaScript
*
* If this setting is set to true then DOMPDF will automatically insert JavaScript code contained
* within <script type="text/javascript"> ... </script> tags as written into the PDF.
* NOTE: This is PDF-based JavaScript to be executed by the PDF viewer,
* not browser-based JavaScript executed by Dompdf.
*
* @var bool
*/
'enable_javascript' => true,
/**
* Enable remote file access
*
* If this setting is set to true, DOMPDF will access remote sites for
* images and CSS files as required.
*
* ==== IMPORTANT ====
* This can be a security risk, in particular in combination with isPhpEnabled and
* allowing remote html code to be passed to $dompdf = new DOMPDF(); $dompdf->load_html(...);
* This allows anonymous users to download legally doubtful internet content which on
* tracing back appears to being downloaded by your server, or allows malicious php code
* in remote html pages to be executed by your server with your account privileges.
*
* This setting may increase the risk of system exploit. Do not change
* this settings without understanding the consequences. Additional
* documentation is available on the dompdf wiki at:
* https://github.com/dompdf/dompdf/wiki
*
* @var bool
*/
'enable_remote' => false,
/**
* List of allowed remote hosts
*
* Each value of the array must be a valid hostname.
*
* This will be used to filter which resources can be loaded in combination with
* isRemoteEnabled. If enable_remote is FALSE, then this will have no effect.
*
* Leave to NULL to allow any remote host.
*
* @var array|null
*/
'allowed_remote_hosts' => null,
/**
* A ratio applied to the fonts height to be more like browsers' line height
*/
'font_height_ratio' => 1.1,
/**
* Use the HTML5 Lib parser
*
* @deprecated This feature is now always on in dompdf 2.x
*
* @var bool
*/
'enable_html5_parser' => true,
],
];

View file

@ -31,3 +31,6 @@ export default defineConfig({
},
},
});
// HMR: When using Herd, run `npm run build` to use production assets.
// The ws://localhost:5173 error occurs when dev assets are loaded without `npm run dev`.