Files
michaelschiemer/src/Framework/Svg/Builder/SvgCanvas.php
Michael Schiemer fc3d7e6357 feat(Production): Complete production deployment infrastructure
- Add comprehensive health check system with multiple endpoints
- Add Prometheus metrics endpoint
- Add production logging configurations (5 strategies)
- Add complete deployment documentation suite:
  * QUICKSTART.md - 30-minute deployment guide
  * DEPLOYMENT_CHECKLIST.md - Printable verification checklist
  * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle
  * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference
  * production-logging.md - Logging configuration guide
  * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation
  * README.md - Navigation hub
  * DEPLOYMENT_SUMMARY.md - Executive summary
- Add deployment scripts and automation
- Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment
- Update README with production-ready features

All production infrastructure is now complete and ready for deployment.
2025-10-25 19:18:37 +02:00

286 lines
6.5 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Framework\Svg\Builder;
use App\Framework\Core\ValueObjects\Dimensions;
use App\Framework\Filesystem\FileStorage;
use App\Framework\Svg\Elements\CircleElement;
use App\Framework\Svg\Elements\GroupElement;
use App\Framework\Svg\Elements\LineElement;
use App\Framework\Svg\Elements\RectElement;
use App\Framework\Svg\Elements\SvgElement;
use App\Framework\Svg\Elements\TextElement;
use App\Framework\Svg\ValueObjects\Geometry\Position;
use App\Framework\Svg\ValueObjects\Geometry\Radius;
use App\Framework\Svg\ValueObjects\Geometry\ViewBox;
use App\Framework\Svg\ValueObjects\Styling\Fill;
use App\Framework\Svg\ValueObjects\Styling\Stroke;
use App\Framework\Svg\ValueObjects\Text\TextContent;
use App\Framework\Svg\ValueObjects\Text\TextStyle;
use App\Framework\Svg\ValueObjects\Transform\Transform;
/**
* Mutable builder for creating SVG documents
* Uses fluent interface for convenient API
*/
final class SvgCanvas
{
/** @var array<SvgElement> */
private array $elements = [];
public function __construct(
private readonly Dimensions $dimensions,
private readonly ?ViewBox $viewBox = null,
private ?string $title = null,
private ?string $description = null,
private readonly ?FileStorage $fileStorage = null
) {
}
/**
* Add rectangle to canvas
*/
public function rect(
Position $position,
Dimensions $dimensions,
Fill $fill,
?Stroke $stroke = null,
?Radius $rx = null,
?Radius $ry = null,
?Transform $transform = null,
?string $id = null,
?string $class = null
): self {
$this->elements[] = new RectElement(
$position,
$dimensions,
$fill,
$stroke,
$rx,
$ry,
$transform,
$id,
$class
);
return $this;
}
/**
* Add circle to canvas
*/
public function circle(
Position $center,
Radius $radius,
Fill $fill,
?Stroke $stroke = null,
?Transform $transform = null,
?string $id = null,
?string $class = null
): self {
$this->elements[] = new CircleElement(
$center,
$radius,
$fill,
$stroke,
$transform,
$id,
$class
);
return $this;
}
/**
* Add line to canvas
*/
public function line(
Position $start,
Position $end,
Stroke $stroke,
?Transform $transform = null,
?string $id = null,
?string $class = null
): self {
$this->elements[] = new LineElement(
$start,
$end,
$stroke,
$transform,
$id,
$class
);
return $this;
}
/**
* Add text to canvas
*/
public function text(
string $content,
Position $position,
TextStyle $style,
?Transform $transform = null,
?string $id = null,
?string $class = null
): self {
$this->elements[] = new TextElement(
new TextContent($content),
$position,
$style,
$transform,
$id,
$class
);
return $this;
}
/**
* Add group to canvas
*/
public function group(
array $children,
?Transform $transform = null,
?string $id = null,
?string $class = null
): self {
$this->elements[] = new GroupElement(
$children,
$transform,
$id,
$class
);
return $this;
}
/**
* Add custom element to canvas
*/
public function element(SvgElement $element): self
{
$this->elements[] = $element;
return $this;
}
/**
* Set title for accessibility
*/
public function withTitle(string $title): self
{
$this->title = $title;
return $this;
}
/**
* Set description for accessibility
*/
public function withDescription(string $description): self
{
$this->description = $description;
return $this;
}
/**
* Get all elements
*/
public function getElements(): array
{
return $this->elements;
}
/**
* Clear all elements
*/
public function clear(): self
{
$this->elements = [];
return $this;
}
/**
* Get element count
*/
public function count(): int
{
return count($this->elements);
}
/**
* Render complete SVG document
*/
public function toSvg(): string
{
$svgAttributes = [
'width' => (string) $this->dimensions->width,
'height' => (string) $this->dimensions->height,
'xmlns' => 'http://www.w3.org/2000/svg',
];
if ($this->viewBox !== null) {
$svgAttributes['viewBox'] = $this->viewBox->toSvgAttribute();
}
$attributeString = $this->buildAttributeString($svgAttributes);
$svg = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
$svg .= "<svg {$attributeString}>\n";
// Add accessibility elements
if ($this->title !== null) {
$svg .= " <title>" . htmlspecialchars($this->title, ENT_XML1, 'UTF-8') . "</title>\n";
}
if ($this->description !== null) {
$svg .= " <desc>" . htmlspecialchars($this->description, ENT_XML1, 'UTF-8') . "</desc>\n";
}
// Add all elements
foreach ($this->elements as $element) {
$svg .= " " . $element->toSvg() . "\n";
}
$svg .= "</svg>";
return $svg;
}
/**
* Render to inline SVG (without XML declaration)
*/
public function toInlineSvg(): string
{
$svg = $this->toSvg();
// Remove XML declaration
return preg_replace('/<\?xml[^?]*\?>\s*/', '', $svg) ?? $svg;
}
/**
* Export to file using Framework's FileStorage
*/
public function toFile(string $filepath): void
{
$storage = $this->fileStorage ?? new FileStorage();
$storage->put($filepath, $this->toSvg());
}
private function buildAttributeString(array $attributes): string
{
$parts = [];
foreach ($attributes as $key => $value) {
$parts[] = sprintf('%s="%s"', $key, htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8'));
}
return implode(' ', $parts);
}
}