bannerstop/planar

PLANAR - Print Layout and Numerical Area Representation

Maintainers

Package info

github.com/bannerstop/planar

pkg:composer/bannerstop/planar

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

1.0.0 2026-03-25 11:53 UTC

This package is auto-updated.

Last update: 2026-03-25 12:03:15 UTC


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/serializer
  • symfony/property-access
  • symfony/property-info

License

This project is licensed under the MIT License.