bannerstop / planar
PLANAR - Print Layout and Numerical Area Representation
Requires
- php: >=7.4
- ext-json: *
- phpdocumentor/reflection-docblock: ^5.0
- symfony/property-access: ^5.4
- symfony/property-info: ^5.4
- symfony/serializer: ^5.4
README
PLANAR is the deterministic, machine-readable specification of a print item's physical layout and constraints. It provides a standardized way to describe complex print products, including nested items, pages, bleeds, safety distances, and various overlays (marks, text, images).
Features
- Standardized Representation: Uses a set of PHP interfaces and models to represent print items accurately.
- Factory-Driven Creation: Each model has a corresponding factory to decouple instantiation from usage.
- Built-in Serialization: Seamlessly convert PLANAR objects to JSON and back using Symfony Serializer.
Installation
Install the package via Composer:
composer require bannerstop/planar
How to use it
1. Creating a Planar Instance
You can create a PLANAR representation by manually instantiating models or, preferably, by using the provided factories.
Using Factories
use Bannerstop\Planar\Contract\PlanarInterface; use Bannerstop\Planar\Factory\PlanarFactory; use Bannerstop\Planar\Factory\ItemFactory; use Bannerstop\Planar\Factory\OnlineDesignerFactory; use Bannerstop\Planar\Factory\PageFactory; use Bannerstop\Planar\Factory\BoxFactory; // Initialize Factories $planarFactory = new PlanarFactory(); $itemFactory = new ItemFactory(); $pageFactory = new PageFactory(); $boxFactory = new BoxFactory(); $onlineDesignerFactory = new OnlineDesignerFactory(); // Create a Page with bleed and safety distance $bleed = $boxFactory->create(top: 5, right: 5, bottom: 5, left: 5); // 5mm all sides $safetyDistance = $boxFactory->create(top: 10, right: 10, bottom: 10, left: 10); $page = $pageFactory->create( width: 1000, height: 2000, label: 'Front', bleed: $bleed, safetyDistance: $safetyDistance ); // Create an Item $onlineDesigner = $onlineDesignerFactory->create(enabled: true, webProductId: 123, templateId: 456); $item = $itemFactory->create( spark: 'spark_code_123', onlineDesigner: $onlineDesigner, itemNo: 1, sku: 'SKU-BANNER-1', name: 'Custom Vinyl Banner', quantity: 1, thumbnail: null, printable: true, subtotal: 49.99, currency: 'EUR', details: [], // Details pages: [$page] // Pages ); // Final Planar Object $planar = $planarFactory->create( version: PlanarInterface::VERSION, items: [$item] );
2. JSON Example
The following JSON illustrates how a complex PLANAR object is represented:
{
"version": "1.0.0",
"subject": {
"type": "order",
"origin": "atlas",
"id": "2123456"
},
"customer": {
"name": "ACME Corp",
"id": "100010"
},
"items": [
{
"itemNo": 1,
"spark": "e3b0c442-98fc-11ee-b9d1-0242ac120002",
"sku": "10001",
"name": "PVC Frontlit",
"quantity": 5,
"thumbnail": {
"url": "https://www.bannerstop.com/media/pvc-frontlit.png"
},
"printable": true,
"onlineDesigner": {
"enabled": true,
"webProductId": 123,
"templateId": 456,
"omitPagesCustomization": false
},
"subtotal": 62.82,
"currency": "EUR",
"details": [
{
"label": "Breite:",
"value": "123 cm"
},
{
"label": "Höhe:",
"value": "456 cm"
},
{
"label": "Variante:",
"value": "PVC Frontlit Premium"
},
{
"label": "Oben:",
"value": "Randverstärkung mit Ösen alle 100cm"
},
{
"label": "Unten:",
"value": "Randverstärkung mit Ösen alle 50cm"
},
{
"label": "Links:",
"value": "Hohlsaum 4cm flach"
},
{
"label": "Rechts:",
"value": "Randverstärkt"
},
{
"label": "Zubehör:",
"value": "Spannflex 50cm (30 Stk.)"
}
],
"pages": [
{
"label": "Front",
"width": 1230,
"height": 4560,
"bleed": {
"top": 10,
"right": 10,
"bottom": 10,
"left": 10
},
"safetyDistance": {
"top": 5,
"right": 5,
"bottom": 5,
"left": 100
},
"overlays": [
{
"type": "rect",
"position": {
"x": 0,
"y": 0
},
"size": {
"width": 2000,
"height": 500,
"length": null,
"diameter": null,
"angle": 0,
"stroke": null
},
"color": {
"rgb": {
"r": 0,
"g": 0,
"b": 255
},
"cmyk": {
"c": 100,
"m": 0,
"y": 0,
"k": 0
},
"spot": null
},
"fileName": null,
"overprint": false,
"renderIn3DPreview": false,
"renderOnCanvas": true,
"renderInReviewPdf": true,
"renderInPrintPdf": true
},
{
"type": "line",
"position": {
"x": 50,
"y": 50
},
"size": {
"width": null,
"height": null,
"length": 1900,
"diameter": null,
"angle": 0,
"stroke": 5
},
"color": {
"rgb": {
"r": 0,
"g": 0,
"b": 0
},
"cmyk": {
"c": 0,
"m": 0,
"y": 0,
"k": 100
},
"spot": null
},
"fileName": null,
"overprint": false,
"renderIn3DPreview": false,
"renderOnCanvas": true,
"renderInReviewPdf": true,
"renderInPrintPdf": true
},
{
"type": "line",
"position": {
"x": 1950,
"y": 450
},
"size": {
"width": null,
"height": null,
"length": 1900,
"diameter": null,
"angle": 180,
"stroke": 5
},
"color": {
"rgb": {
"r": 0,
"g": 0,
"b": 0
},
"cmyk": {
"c": 0,
"m": 0,
"y": 0,
"k": 100
},
"spot": null
},
"fileName": null,
"overprint": false,
"renderIn3DPreview": false,
"renderOnCanvas": true,
"renderInReviewPdf": true,
"renderInPrintPdf": true
},
{
"type": "circle",
"position": {
"x": 1000,
"y": 250
},
"size": {
"width": null,
"height": null,
"length": null,
"diameter": 400,
"angle": 0,
"stroke": 1
},
"color": {
"rgb": {
"r": 255,
"g": 0,
"b": 255
},
"cmyk": {
"c": 100,
"m": 100,
"y": 0,
"k": 0
},
"spot": null
},
"fileName": null,
"overprint": false,
"renderIn3DPreview": false,
"renderOnCanvas": true,
"renderInReviewPdf": true,
"renderInPrintPdf": true
},
{
"type": "rect",
"position": {
"x": 300,
"y": 100
},
"size": {
"width": 200,
"height": 200,
"length": null,
"diameter": null,
"angle": 30,
"stroke": null
},
"color": {
"rgb": {
"r": 255,
"g": 0,
"b": 0
},
"cmyk": {
"c": 0,
"m": 100,
"y": 100,
"k": 0
},
"spot": "Red Overprint"
},
"fileName": null,
"overprint": true,
"renderIn3DPreview": false,
"renderOnCanvas": true,
"renderInReviewPdf": true,
"renderInPrintPdf": true
},
{
"type": "line",
"position": {
"x": 1500,
"y": 250
},
"size": {
"width": null,
"height": null,
"length": 200,
"diameter": null,
"angle": -45,
"stroke": 5
},
"color": {
"rgb": {
"r": 255,
"g": 0,
"b": 0
},
"cmyk": {
"c": 0,
"m": 100,
"y": 100,
"k": 0
},
"spot": "Red Overprint"
},
"fileName": null,
"overprint": true,
"renderIn3DPreview": false,
"renderOnCanvas": true,
"renderInReviewPdf": true,
"renderInPrintPdf": true
},
{
"type": "line",
"position": {
"x": 1500,
"y": 250
},
"size": {
"width": null,
"height": null,
"length": 200,
"diameter": null,
"angle": 45,
"stroke": 5
},
"color": {
"rgb": {
"r": 255,
"g": 0,
"b": 0
},
"cmyk": {
"c": 0,
"m": 100,
"y": 100,
"k": 0
},
"spot": "Red Overprint"
},
"fileName": null,
"overprint": true,
"renderIn3DPreview": false,
"renderOnCanvas": true,
"renderInReviewPdf": true,
"renderInPrintPdf": true
},
{
"type": "image",
"position": {
"x": 50,
"y": 50
},
"size": {
"width": 40,
"height": 40,
"length": null,
"diameter": null,
"angle": null,
"stroke": null
},
"color": null,
"fileName": "chrome_eyelet.png",
"overprint": null,
"renderIn3DPreview": true,
"renderOnCanvas": true,
"renderInReviewPdf": true,
"renderInPrintPdf": false
}
]
}
]
}
]
}
4. Serialization and Deserialization
The package provides a high-level service for serializing and deserializing PLANAR objects.
use Bannerstop\Planar\Service\PlanarSerializerService; $serializerService = new PlanarSerializerService(); // Serialize to JSON $json = $serializerService->serialize(planar: $planar); echo $json; // Deserialize from JSON $deserializedPlanar = $serializerService->deserialize(json: $json);
5. Using Interfaces
For maximum flexibility, always type-hint against the interfaces provided in src/Contract/.
use Bannerstop\Planar\Contract\PlanarInterface; use Bannerstop\Planar\Contract\ItemInterface; function processPrintOrder(PlanarInterface $planar) { echo "PLANAR version: " . $planar->getVersion() . PHP_EOL; foreach ($planar->getItems() as $item) { if ($item->isPrintable()) { echo "Processing printable item: " . $item->getName() . PHP_EOL; } } }
Requirements
- PHP
>= 7.4 symfony/serializersymfony/property-accesssymfony/property-info
License
This project is licensed under the MIT License.