diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/WorkflowDumpCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/WorkflowDumpCommand.php
index b2bc3e5e2c8b6..3527236476519 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Command/WorkflowDumpCommand.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Command/WorkflowDumpCommand.php
@@ -17,6 +17,7 @@
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Workflow\Dumper\GraphvizDumper;
+use Symfony\Component\Workflow\Dumper\PlantUmlDumper;
use Symfony\Component\Workflow\Dumper\StateMachineGraphvizDumper;
use Symfony\Component\Workflow\Marking;
@@ -39,13 +40,15 @@ protected function configure()
new InputArgument('name', InputArgument::REQUIRED, 'A workflow name'),
new InputArgument('marking', InputArgument::IS_ARRAY, 'A marking (a list of places)'),
new InputOption('label', 'l', InputArgument::OPTIONAL, 'Labels a graph'),
+ new InputOption('dump-format', null, InputOption::VALUE_REQUIRED, 'The dump format [dot|puml]', 'dot'),
))
->setDescription('Dump a workflow')
->setHelp(<<<'EOF'
The %command.name% command dumps the graphical representation of a
-workflow in DOT format
+workflow in different formats
- %command.full_name% | dot -Tpng > workflow.png
+DOT: %command.full_name% | dot -Tpng > workflow.png
+PUML: %command.full_name% --dump-format=puml | java -jar plantuml.jar -p > workflow.png
EOF
)
@@ -59,16 +62,27 @@ protected function execute(InputInterface $input, OutputInterface $output)
{
$container = $this->getApplication()->getKernel()->getContainer();
$serviceId = $input->getArgument('name');
+
if ($container->has('workflow.'.$serviceId)) {
$workflow = $container->get('workflow.'.$serviceId);
- $dumper = new GraphvizDumper();
+ $type = 'workflow';
} elseif ($container->has('state_machine.'.$serviceId)) {
$workflow = $container->get('state_machine.'.$serviceId);
- $dumper = new StateMachineGraphvizDumper();
+ $type = 'state_machine';
} else {
throw new \InvalidArgumentException(sprintf('No service found for "workflow.%1$s" nor "state_machine.%1$s".', $serviceId));
}
+ if ('puml' === $input->getOption('dump-format')) {
+ $dumper = new PlantUmlDumper(
+ 'workflow' === $type ? PlantUmlDumper::WORKFLOW_TRANSITION : PlantUmlDumper::STATEMACHINE_TRANSITION
+ );
+ } elseif ('workflow' === $type) {
+ $dumper = new GraphvizDumper();
+ } else {
+ $dumper = new StateMachineGraphvizDumper();
+ }
+
$marking = new Marking();
foreach ($input->getArgument('marking') as $place) {
@@ -80,6 +94,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
if (null !== $label && '' !== trim($label)) {
$options = array('graph' => array('label' => $label));
}
+ $options = array_replace($options, array('name' => $serviceId, 'nofooter' => true));
$output->writeln($dumper->dump($workflow->getDefinition(), $marking, $options));
}
}
diff --git a/src/Symfony/Component/Workflow/Dumper/PlantUmlDumper.php b/src/Symfony/Component/Workflow/Dumper/PlantUmlDumper.php
new file mode 100644
index 0000000000000..02d96346174c8
--- /dev/null
+++ b/src/Symfony/Component/Workflow/Dumper/PlantUmlDumper.php
@@ -0,0 +1,172 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Workflow\Dumper;
+
+use InvalidArgumentException;
+use Symfony\Component\Workflow\Definition;
+use Symfony\Component\Workflow\Marking;
+
+/**
+ * PlantUmlDumper dumps a workflow as a PlantUML file.
+ *
+ * You can convert the generated puml file with the plantuml.jar utility (http://plantuml.com/):
+ *
+ * php bin/console workflow:dump pull_request travis --dump-format=puml | java -jar plantuml.jar -p > workflow.png
+ *
+ * @author Sébastien Morel
+ */
+class PlantUmlDumper implements DumperInterface
+{
+ private const SYMFONY_LOGO = 'sprite $sf_logo [81x20/16z] {
+hPNRaYiX24K1xwBo_tyx6-qaCtDEJ-KXLYMTLbp0HWcHZr3KRDJ8z94HG3jZn4_mijbQ2ryJoFePtXLWA_qxyGy19DpdY_10z11ZAbGjFHRwcEbcKx5-wqsV
+yIMo8StMCHKh8ZUxnEwrZiwRAUOvy1lLcPQF4lEFAjhzMd5WOAqvKflS0Enx8PbihiSYXM8ClGVAseIWTAjCgVSAcnYbQG79xKFsZ0VnDCNc7AVBoPSMcTsX
+UnrujbYjjz0NnsObkTgnmolqJD4QgGUYTQiNe8eIjtx4b6Vv8nPGpncn3NJ8Geo9W9VW2wGACm_JzgIO8A8KXr2jUBCVGEAAJSZ6JUlsNnmOzmIYti9G7bjL
+8InaHM9G40NkwTG7OxrggvNIejA8AZuqyWjOzTIKi-wwYvjeHYesSWuPiTGDN5THzkYLU4MD5r2_0PDhG7LIUG33z5HtM6CP3icyWEVOS61sD_2ZsBfJdbVA
+qM53XHDUwhY0TAwPug3OG9NonRFhO8ynF3I4unuAMDHmSrXH57V1RGvl9jafuZF9ZhqjWOEh98y0tUYGsUxkBSllIyBdT2oM5Fn2-ut-fzsq_cQNuL6Uvwqr
+knh4RrvOKzxZfLV3s0rs_R_1SdYt3VxeQ1_y2_W2
+}';
+ private const INITIAL = 'initial';
+ private const MARKED = 'marked';
+
+ const STATEMACHINE_TRANSITION = 'arrow';
+ const WORKFLOW_TRANSITION = 'square';
+ const TRANSITION_TYPES = array(self::STATEMACHINE_TRANSITION, self::WORKFLOW_TRANSITION);
+ const DEFAULT_OPTIONS = array(
+ 'skinparams' => array(
+ 'titleBorderRoundCorner' => 15,
+ 'titleBorderThickness' => 2,
+ 'state' => array(
+ 'BackgroundColor<<'.self::INITIAL.'>>' => '#87b741',
+ 'BackgroundColor<<'.self::MARKED.'>>' => '#3887C6',
+ 'BorderColor' => '#3887C6',
+ 'BorderColor<<'.self::MARKED.'>>' => 'Black',
+ 'FontColor<<'.self::MARKED.'>>' => 'White',
+ ),
+ 'agent' => array(
+ 'BackgroundColor' => '#ffffff',
+ 'BorderColor' => '#3887C6',
+ ),
+ ),
+ );
+
+ private $transitionType = self::STATEMACHINE_TRANSITION;
+
+ public function __construct(string $transitionType = null)
+ {
+ if (!in_array($transitionType, self::TRANSITION_TYPES)) {
+ throw new InvalidArgumentException("Transition type '{$transitionType}' does not exist.");
+ }
+ $this->transitionType = $transitionType;
+ }
+
+ public function dump(Definition $definition, Marking $marking = null, array $options = array()): string
+ {
+ $options = array_replace_recursive(self::DEFAULT_OPTIONS, $options);
+ $code = $this->initialize($options);
+ foreach ($definition->getPlaces() as $place) {
+ $code[] =
+ "state {$place}".
+ ($definition->getInitialPlace() === $place ? ' <<'.self::INITIAL.'>>' : '').
+ ($marking && $marking->has($place) ? ' <<'.self::MARKED.'>>' : '');
+ }
+ if ($this->isWorkflowTransitionType()) {
+ foreach ($definition->getTransitions() as $transition) {
+ $code[] = "agent {$transition->getName()}";
+ }
+ }
+ foreach ($definition->getTransitions() as $transition) {
+ foreach ($transition->getFroms() as $from) {
+ foreach ($transition->getTos() as $to) {
+ if ($this->isWorkflowTransitionType()) {
+ $lines = array(
+ "{$from} --> {$transition->getName()}",
+ "{$transition->getName()} --> {$to}",
+ );
+ foreach ($lines as $line) {
+ if (!in_array($line, $code)) {
+ $code[] = $line;
+ }
+ }
+ } else {
+ $code[] = "{$from} --> {$to}: {$transition->getName()}";
+ }
+ }
+ }
+ }
+
+ return $this->startPuml($options).$this->getLines($code).$this->endPuml($options);
+ }
+
+ private function isWorkflowTransitionType(): bool
+ {
+ return self::WORKFLOW_TRANSITION === $this->transitionType;
+ }
+
+ private function startPuml(array $options): string
+ {
+ $start = '@startuml'.PHP_EOL;
+
+ if ($this->isWorkflowTransitionType()) {
+ $start .= 'allow_mixing'.PHP_EOL;
+ }
+
+ if ($options['nofooter'] ?? false) {
+ return $start;
+ }
+
+ return $start.self::SYMFONY_LOGO.PHP_EOL;
+ }
+
+ private function endPuml(array $options): string
+ {
+ $end = PHP_EOL.'@enduml';
+ if ($options['nofooter'] ?? false) {
+ return $end;
+ }
+
+ return PHP_EOL.'footer \nGenerated by <$sf_logo> **Workflow Component** and **PlantUML**'.$end;
+ }
+
+ private function getLines(array $code): string
+ {
+ return implode(PHP_EOL, $code);
+ }
+
+ private function initialize(array $options): array
+ {
+ $code = array();
+ if (isset($options['title'])) {
+ $code[] = "title {$options['title']}";
+ }
+ if (isset($options['name'])) {
+ $code[] = "title {$options['name']}";
+ }
+ if (isset($options['skinparams']) && is_array($options['skinparams'])) {
+ foreach ($options['skinparams'] as $skinparamKey => $skinparamValue) {
+ if (!$this->isWorkflowTransitionType() && 'agent' === $skinparamKey) {
+ continue;
+ }
+ if (!is_array($skinparamValue)) {
+ $code[] = "skinparam {$skinparamKey} $skinparamValue";
+ continue;
+ }
+ $code[] = "skinparam {$skinparamKey} {";
+ foreach ($skinparamValue as $key => $value) {
+ $code[] = " {$key} $value";
+ }
+ $code[] = '}';
+ }
+ }
+
+ return $code;
+ }
+}
diff --git a/src/Symfony/Component/Workflow/Tests/Dumper/PlantUmlDumperTest.php b/src/Symfony/Component/Workflow/Tests/Dumper/PlantUmlDumperTest.php
new file mode 100644
index 0000000000000..0fae31d689224
--- /dev/null
+++ b/src/Symfony/Component/Workflow/Tests/Dumper/PlantUmlDumperTest.php
@@ -0,0 +1,97 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Workflow\Tests\Dumper;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\Workflow\Dumper\DumperInterface;
+use Symfony\Component\Workflow\Dumper\PlantUmlDumper;
+use Symfony\Component\Workflow\Marking;
+use Symfony\Component\Workflow\Tests\WorkflowBuilderTrait;
+
+class PlantUmlDumperTest extends TestCase
+{
+ use WorkflowBuilderTrait;
+
+ /**
+ * @var DumperInterface[]
+ */
+ private $dumpers;
+
+ protected function setUp()
+ {
+ $this->dumpers =
+ array(
+ PlantUmlDumper::STATEMACHINE_TRANSITION => new PlantUmlDumper(PlantUmlDumper::STATEMACHINE_TRANSITION),
+ PlantUmlDumper::WORKFLOW_TRANSITION => new PlantUmlDumper(PlantUmlDumper::WORKFLOW_TRANSITION),
+ );
+ }
+
+ /**
+ * @dataProvider provideWorkflowDefinitionWithoutMarking
+ */
+ public function testDumpWithoutMarking($definition, $expectedFileName, $title, $nofooter)
+ {
+ foreach ($this->dumpers as $transitionType => $dumper) {
+ $dump = $dumper->dump($definition, null, array('title' => $title, 'nofooter' => $nofooter));
+ // handle windows, and avoid to create more fixtures
+ $dump = str_replace(PHP_EOL, "\n", $dump.PHP_EOL);
+ $this->assertStringEqualsFile($this->getFixturePath($expectedFileName, $transitionType), $dump);
+ }
+ }
+
+ /**
+ * @dataProvider provideWorkflowDefinitionWithMarking
+ */
+ public function testDumpWithMarking($definition, $marking, $expectedFileName, $title, $footer)
+ {
+ foreach ($this->dumpers as $transitionType => $dumper) {
+ $dump = $dumper->dump($definition, $marking, array('title' => $title, 'nofooter' => $footer));
+ // handle windows, and avoid to create more fixtures
+ $dump = str_replace(PHP_EOL, "\n", $dump.PHP_EOL);
+ $this->assertStringEqualsFile($this->getFixturePath($expectedFileName, $transitionType), $dump);
+ }
+ }
+
+ public function provideWorkflowDefinitionWithoutMarking()
+ {
+ $title = 'SimpleDiagram';
+ yield array($this->createSimpleWorkflowDefinition(), 'simple-workflow-nomarking-nofooter', $title, true);
+ yield array($this->createSimpleWorkflowDefinition(), 'simple-workflow-nomarking', $title, false);
+ $title = 'ComplexDiagram';
+ yield array($this->createComplexWorkflowDefinition(), 'complex-workflow-nomarking-nofooter', $title, true);
+ yield array($this->createComplexWorkflowDefinition(), 'complex-workflow-nomarking', $title, false);
+ }
+
+ public function provideWorkflowDefinitionWithMarking()
+ {
+ $title = 'SimpleDiagram';
+ $marking = new Marking(array('b' => 1));
+ yield array(
+ $this->createSimpleWorkflowDefinition(), $marking, 'simple-workflow-marking-nofooter', $title, true,
+ );
+ yield array(
+ $this->createSimpleWorkflowDefinition(), $marking, 'simple-workflow-marking', $title, false,
+ );
+ $title = 'ComplexDiagram';
+ $marking = new Marking(array('c' => 1, 'e' => 1));
+ yield array(
+ $this->createComplexWorkflowDefinition(), $marking, 'complex-workflow-marking-nofooter', $title, true,
+ );
+ yield array(
+ $this->createComplexWorkflowDefinition(), $marking, 'complex-workflow-marking', $title, false,
+ );
+ }
+
+ private function getFixturePath($name, $transitionType)
+ {
+ return __DIR__.'/../fixtures/puml/'.$transitionType.'/'.$name.'.puml';
+ }
+}
diff --git a/src/Symfony/Component/Workflow/Tests/fixtures/puml/arrow/complex-workflow-marking-nofooter.puml b/src/Symfony/Component/Workflow/Tests/fixtures/puml/arrow/complex-workflow-marking-nofooter.puml
new file mode 100644
index 0000000000000..72a3fe4cac0de
--- /dev/null
+++ b/src/Symfony/Component/Workflow/Tests/fixtures/puml/arrow/complex-workflow-marking-nofooter.puml
@@ -0,0 +1,27 @@
+@startuml
+title ComplexDiagram
+skinparam titleBorderRoundCorner 15
+skinparam titleBorderThickness 2
+skinparam state {
+ BackgroundColor<> #87b741
+ BackgroundColor<> #3887C6
+ BorderColor #3887C6
+ BorderColor<> Black
+ FontColor<> White
+}
+state a <>
+state b
+state c <>
+state d
+state e <>
+state f
+state g
+a --> b: t1
+a --> c: t1
+b --> d: t2
+c --> d: t2
+d --> e: t3
+d --> f: t4
+e --> g: t5
+f --> g: t6
+@enduml
diff --git a/src/Symfony/Component/Workflow/Tests/fixtures/puml/arrow/complex-workflow-marking.puml b/src/Symfony/Component/Workflow/Tests/fixtures/puml/arrow/complex-workflow-marking.puml
new file mode 100644
index 0000000000000..dcb29effc98aa
--- /dev/null
+++ b/src/Symfony/Component/Workflow/Tests/fixtures/puml/arrow/complex-workflow-marking.puml
@@ -0,0 +1,36 @@
+@startuml
+sprite $sf_logo [81x20/16z] {
+hPNRaYiX24K1xwBo_tyx6-qaCtDEJ-KXLYMTLbp0HWcHZr3KRDJ8z94HG3jZn4_mijbQ2ryJoFePtXLWA_qxyGy19DpdY_10z11ZAbGjFHRwcEbcKx5-wqsV
+yIMo8StMCHKh8ZUxnEwrZiwRAUOvy1lLcPQF4lEFAjhzMd5WOAqvKflS0Enx8PbihiSYXM8ClGVAseIWTAjCgVSAcnYbQG79xKFsZ0VnDCNc7AVBoPSMcTsX
+UnrujbYjjz0NnsObkTgnmolqJD4QgGUYTQiNe8eIjtx4b6Vv8nPGpncn3NJ8Geo9W9VW2wGACm_JzgIO8A8KXr2jUBCVGEAAJSZ6JUlsNnmOzmIYti9G7bjL
+8InaHM9G40NkwTG7OxrggvNIejA8AZuqyWjOzTIKi-wwYvjeHYesSWuPiTGDN5THzkYLU4MD5r2_0PDhG7LIUG33z5HtM6CP3icyWEVOS61sD_2ZsBfJdbVA
+qM53XHDUwhY0TAwPug3OG9NonRFhO8ynF3I4unuAMDHmSrXH57V1RGvl9jafuZF9ZhqjWOEh98y0tUYGsUxkBSllIyBdT2oM5Fn2-ut-fzsq_cQNuL6Uvwqr
+knh4RrvOKzxZfLV3s0rs_R_1SdYt3VxeQ1_y2_W2
+}
+title ComplexDiagram
+skinparam titleBorderRoundCorner 15
+skinparam titleBorderThickness 2
+skinparam state {
+ BackgroundColor<> #87b741
+ BackgroundColor<> #3887C6
+ BorderColor #3887C6
+ BorderColor<> Black
+ FontColor<> White
+}
+state a <>
+state b
+state c <>
+state d
+state e <>
+state f
+state g
+a --> b: t1
+a --> c: t1
+b --> d: t2
+c --> d: t2
+d --> e: t3
+d --> f: t4
+e --> g: t5
+f --> g: t6
+footer \nGenerated by <$sf_logo> **Workflow Component** and **PlantUML**
+@enduml
diff --git a/src/Symfony/Component/Workflow/Tests/fixtures/puml/arrow/complex-workflow-nomarking-nofooter.puml b/src/Symfony/Component/Workflow/Tests/fixtures/puml/arrow/complex-workflow-nomarking-nofooter.puml
new file mode 100644
index 0000000000000..d3d9273f250de
--- /dev/null
+++ b/src/Symfony/Component/Workflow/Tests/fixtures/puml/arrow/complex-workflow-nomarking-nofooter.puml
@@ -0,0 +1,27 @@
+@startuml
+title ComplexDiagram
+skinparam titleBorderRoundCorner 15
+skinparam titleBorderThickness 2
+skinparam state {
+ BackgroundColor<> #87b741
+ BackgroundColor<> #3887C6
+ BorderColor #3887C6
+ BorderColor<> Black
+ FontColor<> White
+}
+state a <>
+state b
+state c
+state d
+state e
+state f
+state g
+a --> b: t1
+a --> c: t1
+b --> d: t2
+c --> d: t2
+d --> e: t3
+d --> f: t4
+e --> g: t5
+f --> g: t6
+@enduml
diff --git a/src/Symfony/Component/Workflow/Tests/fixtures/puml/arrow/complex-workflow-nomarking.puml b/src/Symfony/Component/Workflow/Tests/fixtures/puml/arrow/complex-workflow-nomarking.puml
new file mode 100644
index 0000000000000..14819bf97e0b6
--- /dev/null
+++ b/src/Symfony/Component/Workflow/Tests/fixtures/puml/arrow/complex-workflow-nomarking.puml
@@ -0,0 +1,36 @@
+@startuml
+sprite $sf_logo [81x20/16z] {
+hPNRaYiX24K1xwBo_tyx6-qaCtDEJ-KXLYMTLbp0HWcHZr3KRDJ8z94HG3jZn4_mijbQ2ryJoFePtXLWA_qxyGy19DpdY_10z11ZAbGjFHRwcEbcKx5-wqsV
+yIMo8StMCHKh8ZUxnEwrZiwRAUOvy1lLcPQF4lEFAjhzMd5WOAqvKflS0Enx8PbihiSYXM8ClGVAseIWTAjCgVSAcnYbQG79xKFsZ0VnDCNc7AVBoPSMcTsX
+UnrujbYjjz0NnsObkTgnmolqJD4QgGUYTQiNe8eIjtx4b6Vv8nPGpncn3NJ8Geo9W9VW2wGACm_JzgIO8A8KXr2jUBCVGEAAJSZ6JUlsNnmOzmIYti9G7bjL
+8InaHM9G40NkwTG7OxrggvNIejA8AZuqyWjOzTIKi-wwYvjeHYesSWuPiTGDN5THzkYLU4MD5r2_0PDhG7LIUG33z5HtM6CP3icyWEVOS61sD_2ZsBfJdbVA
+qM53XHDUwhY0TAwPug3OG9NonRFhO8ynF3I4unuAMDHmSrXH57V1RGvl9jafuZF9ZhqjWOEh98y0tUYGsUxkBSllIyBdT2oM5Fn2-ut-fzsq_cQNuL6Uvwqr
+knh4RrvOKzxZfLV3s0rs_R_1SdYt3VxeQ1_y2_W2
+}
+title ComplexDiagram
+skinparam titleBorderRoundCorner 15
+skinparam titleBorderThickness 2
+skinparam state {
+ BackgroundColor<> #87b741
+ BackgroundColor<> #3887C6
+ BorderColor #3887C6
+ BorderColor<> Black
+ FontColor<> White
+}
+state a <>
+state b
+state c
+state d
+state e
+state f
+state g
+a --> b: t1
+a --> c: t1
+b --> d: t2
+c --> d: t2
+d --> e: t3
+d --> f: t4
+e --> g: t5
+f --> g: t6
+footer \nGenerated by <$sf_logo> **Workflow Component** and **PlantUML**
+@enduml
diff --git a/src/Symfony/Component/Workflow/Tests/fixtures/puml/arrow/simple-workflow-marking-nofooter.puml b/src/Symfony/Component/Workflow/Tests/fixtures/puml/arrow/simple-workflow-marking-nofooter.puml
new file mode 100644
index 0000000000000..54fe1e33cb40a
--- /dev/null
+++ b/src/Symfony/Component/Workflow/Tests/fixtures/puml/arrow/simple-workflow-marking-nofooter.puml
@@ -0,0 +1,17 @@
+@startuml
+title SimpleDiagram
+skinparam titleBorderRoundCorner 15
+skinparam titleBorderThickness 2
+skinparam state {
+ BackgroundColor<> #87b741
+ BackgroundColor<> #3887C6
+ BorderColor #3887C6
+ BorderColor<> Black
+ FontColor<> White
+}
+state a <>
+state b <>
+state c
+a --> b: t1
+b --> c: t2
+@enduml
diff --git a/src/Symfony/Component/Workflow/Tests/fixtures/puml/arrow/simple-workflow-marking.puml b/src/Symfony/Component/Workflow/Tests/fixtures/puml/arrow/simple-workflow-marking.puml
new file mode 100644
index 0000000000000..3064520b3af9f
--- /dev/null
+++ b/src/Symfony/Component/Workflow/Tests/fixtures/puml/arrow/simple-workflow-marking.puml
@@ -0,0 +1,26 @@
+@startuml
+sprite $sf_logo [81x20/16z] {
+hPNRaYiX24K1xwBo_tyx6-qaCtDEJ-KXLYMTLbp0HWcHZr3KRDJ8z94HG3jZn4_mijbQ2ryJoFePtXLWA_qxyGy19DpdY_10z11ZAbGjFHRwcEbcKx5-wqsV
+yIMo8StMCHKh8ZUxnEwrZiwRAUOvy1lLcPQF4lEFAjhzMd5WOAqvKflS0Enx8PbihiSYXM8ClGVAseIWTAjCgVSAcnYbQG79xKFsZ0VnDCNc7AVBoPSMcTsX
+UnrujbYjjz0NnsObkTgnmolqJD4QgGUYTQiNe8eIjtx4b6Vv8nPGpncn3NJ8Geo9W9VW2wGACm_JzgIO8A8KXr2jUBCVGEAAJSZ6JUlsNnmOzmIYti9G7bjL
+8InaHM9G40NkwTG7OxrggvNIejA8AZuqyWjOzTIKi-wwYvjeHYesSWuPiTGDN5THzkYLU4MD5r2_0PDhG7LIUG33z5HtM6CP3icyWEVOS61sD_2ZsBfJdbVA
+qM53XHDUwhY0TAwPug3OG9NonRFhO8ynF3I4unuAMDHmSrXH57V1RGvl9jafuZF9ZhqjWOEh98y0tUYGsUxkBSllIyBdT2oM5Fn2-ut-fzsq_cQNuL6Uvwqr
+knh4RrvOKzxZfLV3s0rs_R_1SdYt3VxeQ1_y2_W2
+}
+title SimpleDiagram
+skinparam titleBorderRoundCorner 15
+skinparam titleBorderThickness 2
+skinparam state {
+ BackgroundColor<> #87b741
+ BackgroundColor<> #3887C6
+ BorderColor #3887C6
+ BorderColor<> Black
+ FontColor<> White
+}
+state a <>
+state b <>
+state c
+a --> b: t1
+b --> c: t2
+footer \nGenerated by <$sf_logo> **Workflow Component** and **PlantUML**
+@enduml
diff --git a/src/Symfony/Component/Workflow/Tests/fixtures/puml/arrow/simple-workflow-nomarking-nofooter.puml b/src/Symfony/Component/Workflow/Tests/fixtures/puml/arrow/simple-workflow-nomarking-nofooter.puml
new file mode 100644
index 0000000000000..170c9aec3322c
--- /dev/null
+++ b/src/Symfony/Component/Workflow/Tests/fixtures/puml/arrow/simple-workflow-nomarking-nofooter.puml
@@ -0,0 +1,17 @@
+@startuml
+title SimpleDiagram
+skinparam titleBorderRoundCorner 15
+skinparam titleBorderThickness 2
+skinparam state {
+ BackgroundColor<> #87b741
+ BackgroundColor<> #3887C6
+ BorderColor #3887C6
+ BorderColor<> Black
+ FontColor<> White
+}
+state a <>
+state b
+state c
+a --> b: t1
+b --> c: t2
+@enduml
diff --git a/src/Symfony/Component/Workflow/Tests/fixtures/puml/arrow/simple-workflow-nomarking.puml b/src/Symfony/Component/Workflow/Tests/fixtures/puml/arrow/simple-workflow-nomarking.puml
new file mode 100644
index 0000000000000..7d3b7c669ae9c
--- /dev/null
+++ b/src/Symfony/Component/Workflow/Tests/fixtures/puml/arrow/simple-workflow-nomarking.puml
@@ -0,0 +1,26 @@
+@startuml
+sprite $sf_logo [81x20/16z] {
+hPNRaYiX24K1xwBo_tyx6-qaCtDEJ-KXLYMTLbp0HWcHZr3KRDJ8z94HG3jZn4_mijbQ2ryJoFePtXLWA_qxyGy19DpdY_10z11ZAbGjFHRwcEbcKx5-wqsV
+yIMo8StMCHKh8ZUxnEwrZiwRAUOvy1lLcPQF4lEFAjhzMd5WOAqvKflS0Enx8PbihiSYXM8ClGVAseIWTAjCgVSAcnYbQG79xKFsZ0VnDCNc7AVBoPSMcTsX
+UnrujbYjjz0NnsObkTgnmolqJD4QgGUYTQiNe8eIjtx4b6Vv8nPGpncn3NJ8Geo9W9VW2wGACm_JzgIO8A8KXr2jUBCVGEAAJSZ6JUlsNnmOzmIYti9G7bjL
+8InaHM9G40NkwTG7OxrggvNIejA8AZuqyWjOzTIKi-wwYvjeHYesSWuPiTGDN5THzkYLU4MD5r2_0PDhG7LIUG33z5HtM6CP3icyWEVOS61sD_2ZsBfJdbVA
+qM53XHDUwhY0TAwPug3OG9NonRFhO8ynF3I4unuAMDHmSrXH57V1RGvl9jafuZF9ZhqjWOEh98y0tUYGsUxkBSllIyBdT2oM5Fn2-ut-fzsq_cQNuL6Uvwqr
+knh4RrvOKzxZfLV3s0rs_R_1SdYt3VxeQ1_y2_W2
+}
+title SimpleDiagram
+skinparam titleBorderRoundCorner 15
+skinparam titleBorderThickness 2
+skinparam state {
+ BackgroundColor<> #87b741
+ BackgroundColor<> #3887C6
+ BorderColor #3887C6
+ BorderColor<> Black
+ FontColor<> White
+}
+state a <>
+state b
+state c
+a --> b: t1
+b --> c: t2
+footer \nGenerated by <$sf_logo> **Workflow Component** and **PlantUML**
+@enduml
diff --git a/src/Symfony/Component/Workflow/Tests/fixtures/puml/square/complex-workflow-marking-nofooter.puml b/src/Symfony/Component/Workflow/Tests/fixtures/puml/square/complex-workflow-marking-nofooter.puml
new file mode 100644
index 0000000000000..9fdacade26725
--- /dev/null
+++ b/src/Symfony/Component/Workflow/Tests/fixtures/puml/square/complex-workflow-marking-nofooter.puml
@@ -0,0 +1,44 @@
+@startuml
+allow_mixing
+title ComplexDiagram
+skinparam titleBorderRoundCorner 15
+skinparam titleBorderThickness 2
+skinparam state {
+ BackgroundColor<> #87b741
+ BackgroundColor<> #3887C6
+ BorderColor #3887C6
+ BorderColor<> Black
+ FontColor<> White
+}
+skinparam agent {
+ BackgroundColor #ffffff
+ BorderColor #3887C6
+}
+state a <>
+state b
+state c <>
+state d
+state e <>
+state f
+state g
+agent t1
+agent t2
+agent t3
+agent t4
+agent t5
+agent t6
+a --> t1
+t1 --> b
+t1 --> c
+b --> t2
+t2 --> d
+c --> t2
+d --> t3
+t3 --> e
+d --> t4
+t4 --> f
+e --> t5
+t5 --> g
+f --> t6
+t6 --> g
+@enduml
diff --git a/src/Symfony/Component/Workflow/Tests/fixtures/puml/square/complex-workflow-marking.puml b/src/Symfony/Component/Workflow/Tests/fixtures/puml/square/complex-workflow-marking.puml
new file mode 100644
index 0000000000000..7384728761f24
--- /dev/null
+++ b/src/Symfony/Component/Workflow/Tests/fixtures/puml/square/complex-workflow-marking.puml
@@ -0,0 +1,53 @@
+@startuml
+allow_mixing
+sprite $sf_logo [81x20/16z] {
+hPNRaYiX24K1xwBo_tyx6-qaCtDEJ-KXLYMTLbp0HWcHZr3KRDJ8z94HG3jZn4_mijbQ2ryJoFePtXLWA_qxyGy19DpdY_10z11ZAbGjFHRwcEbcKx5-wqsV
+yIMo8StMCHKh8ZUxnEwrZiwRAUOvy1lLcPQF4lEFAjhzMd5WOAqvKflS0Enx8PbihiSYXM8ClGVAseIWTAjCgVSAcnYbQG79xKFsZ0VnDCNc7AVBoPSMcTsX
+UnrujbYjjz0NnsObkTgnmolqJD4QgGUYTQiNe8eIjtx4b6Vv8nPGpncn3NJ8Geo9W9VW2wGACm_JzgIO8A8KXr2jUBCVGEAAJSZ6JUlsNnmOzmIYti9G7bjL
+8InaHM9G40NkwTG7OxrggvNIejA8AZuqyWjOzTIKi-wwYvjeHYesSWuPiTGDN5THzkYLU4MD5r2_0PDhG7LIUG33z5HtM6CP3icyWEVOS61sD_2ZsBfJdbVA
+qM53XHDUwhY0TAwPug3OG9NonRFhO8ynF3I4unuAMDHmSrXH57V1RGvl9jafuZF9ZhqjWOEh98y0tUYGsUxkBSllIyBdT2oM5Fn2-ut-fzsq_cQNuL6Uvwqr
+knh4RrvOKzxZfLV3s0rs_R_1SdYt3VxeQ1_y2_W2
+}
+title ComplexDiagram
+skinparam titleBorderRoundCorner 15
+skinparam titleBorderThickness 2
+skinparam state {
+ BackgroundColor<> #87b741
+ BackgroundColor<> #3887C6
+ BorderColor #3887C6
+ BorderColor<> Black
+ FontColor<> White
+}
+skinparam agent {
+ BackgroundColor #ffffff
+ BorderColor #3887C6
+}
+state a <>
+state b
+state c <>
+state d
+state e <>
+state f
+state g
+agent t1
+agent t2
+agent t3
+agent t4
+agent t5
+agent t6
+a --> t1
+t1 --> b
+t1 --> c
+b --> t2
+t2 --> d
+c --> t2
+d --> t3
+t3 --> e
+d --> t4
+t4 --> f
+e --> t5
+t5 --> g
+f --> t6
+t6 --> g
+footer \nGenerated by <$sf_logo> **Workflow Component** and **PlantUML**
+@enduml
diff --git a/src/Symfony/Component/Workflow/Tests/fixtures/puml/square/complex-workflow-nomarking-nofooter.puml b/src/Symfony/Component/Workflow/Tests/fixtures/puml/square/complex-workflow-nomarking-nofooter.puml
new file mode 100644
index 0000000000000..73677f3dec852
--- /dev/null
+++ b/src/Symfony/Component/Workflow/Tests/fixtures/puml/square/complex-workflow-nomarking-nofooter.puml
@@ -0,0 +1,44 @@
+@startuml
+allow_mixing
+title ComplexDiagram
+skinparam titleBorderRoundCorner 15
+skinparam titleBorderThickness 2
+skinparam state {
+ BackgroundColor<> #87b741
+ BackgroundColor<> #3887C6
+ BorderColor #3887C6
+ BorderColor<> Black
+ FontColor<> White
+}
+skinparam agent {
+ BackgroundColor #ffffff
+ BorderColor #3887C6
+}
+state a <>
+state b
+state c
+state d
+state e
+state f
+state g
+agent t1
+agent t2
+agent t3
+agent t4
+agent t5
+agent t6
+a --> t1
+t1 --> b
+t1 --> c
+b --> t2
+t2 --> d
+c --> t2
+d --> t3
+t3 --> e
+d --> t4
+t4 --> f
+e --> t5
+t5 --> g
+f --> t6
+t6 --> g
+@enduml
diff --git a/src/Symfony/Component/Workflow/Tests/fixtures/puml/square/complex-workflow-nomarking.puml b/src/Symfony/Component/Workflow/Tests/fixtures/puml/square/complex-workflow-nomarking.puml
new file mode 100644
index 0000000000000..7bed4c7dd8d70
--- /dev/null
+++ b/src/Symfony/Component/Workflow/Tests/fixtures/puml/square/complex-workflow-nomarking.puml
@@ -0,0 +1,53 @@
+@startuml
+allow_mixing
+sprite $sf_logo [81x20/16z] {
+hPNRaYiX24K1xwBo_tyx6-qaCtDEJ-KXLYMTLbp0HWcHZr3KRDJ8z94HG3jZn4_mijbQ2ryJoFePtXLWA_qxyGy19DpdY_10z11ZAbGjFHRwcEbcKx5-wqsV
+yIMo8StMCHKh8ZUxnEwrZiwRAUOvy1lLcPQF4lEFAjhzMd5WOAqvKflS0Enx8PbihiSYXM8ClGVAseIWTAjCgVSAcnYbQG79xKFsZ0VnDCNc7AVBoPSMcTsX
+UnrujbYjjz0NnsObkTgnmolqJD4QgGUYTQiNe8eIjtx4b6Vv8nPGpncn3NJ8Geo9W9VW2wGACm_JzgIO8A8KXr2jUBCVGEAAJSZ6JUlsNnmOzmIYti9G7bjL
+8InaHM9G40NkwTG7OxrggvNIejA8AZuqyWjOzTIKi-wwYvjeHYesSWuPiTGDN5THzkYLU4MD5r2_0PDhG7LIUG33z5HtM6CP3icyWEVOS61sD_2ZsBfJdbVA
+qM53XHDUwhY0TAwPug3OG9NonRFhO8ynF3I4unuAMDHmSrXH57V1RGvl9jafuZF9ZhqjWOEh98y0tUYGsUxkBSllIyBdT2oM5Fn2-ut-fzsq_cQNuL6Uvwqr
+knh4RrvOKzxZfLV3s0rs_R_1SdYt3VxeQ1_y2_W2
+}
+title ComplexDiagram
+skinparam titleBorderRoundCorner 15
+skinparam titleBorderThickness 2
+skinparam state {
+ BackgroundColor<> #87b741
+ BackgroundColor<> #3887C6
+ BorderColor #3887C6
+ BorderColor<> Black
+ FontColor<> White
+}
+skinparam agent {
+ BackgroundColor #ffffff
+ BorderColor #3887C6
+}
+state a <>
+state b
+state c
+state d
+state e
+state f
+state g
+agent t1
+agent t2
+agent t3
+agent t4
+agent t5
+agent t6
+a --> t1
+t1 --> b
+t1 --> c
+b --> t2
+t2 --> d
+c --> t2
+d --> t3
+t3 --> e
+d --> t4
+t4 --> f
+e --> t5
+t5 --> g
+f --> t6
+t6 --> g
+footer \nGenerated by <$sf_logo> **Workflow Component** and **PlantUML**
+@enduml
diff --git a/src/Symfony/Component/Workflow/Tests/fixtures/puml/square/simple-workflow-marking-nofooter.puml b/src/Symfony/Component/Workflow/Tests/fixtures/puml/square/simple-workflow-marking-nofooter.puml
new file mode 100644
index 0000000000000..2325f95311de0
--- /dev/null
+++ b/src/Symfony/Component/Workflow/Tests/fixtures/puml/square/simple-workflow-marking-nofooter.puml
@@ -0,0 +1,26 @@
+@startuml
+allow_mixing
+title SimpleDiagram
+skinparam titleBorderRoundCorner 15
+skinparam titleBorderThickness 2
+skinparam state {
+ BackgroundColor<> #87b741
+ BackgroundColor<> #3887C6
+ BorderColor #3887C6
+ BorderColor<> Black
+ FontColor<> White
+}
+skinparam agent {
+ BackgroundColor #ffffff
+ BorderColor #3887C6
+}
+state a <>
+state b <>
+state c
+agent t1
+agent t2
+a --> t1
+t1 --> b
+b --> t2
+t2 --> c
+@enduml
diff --git a/src/Symfony/Component/Workflow/Tests/fixtures/puml/square/simple-workflow-marking.puml b/src/Symfony/Component/Workflow/Tests/fixtures/puml/square/simple-workflow-marking.puml
new file mode 100644
index 0000000000000..50ba4d7b5d7ab
--- /dev/null
+++ b/src/Symfony/Component/Workflow/Tests/fixtures/puml/square/simple-workflow-marking.puml
@@ -0,0 +1,35 @@
+@startuml
+allow_mixing
+sprite $sf_logo [81x20/16z] {
+hPNRaYiX24K1xwBo_tyx6-qaCtDEJ-KXLYMTLbp0HWcHZr3KRDJ8z94HG3jZn4_mijbQ2ryJoFePtXLWA_qxyGy19DpdY_10z11ZAbGjFHRwcEbcKx5-wqsV
+yIMo8StMCHKh8ZUxnEwrZiwRAUOvy1lLcPQF4lEFAjhzMd5WOAqvKflS0Enx8PbihiSYXM8ClGVAseIWTAjCgVSAcnYbQG79xKFsZ0VnDCNc7AVBoPSMcTsX
+UnrujbYjjz0NnsObkTgnmolqJD4QgGUYTQiNe8eIjtx4b6Vv8nPGpncn3NJ8Geo9W9VW2wGACm_JzgIO8A8KXr2jUBCVGEAAJSZ6JUlsNnmOzmIYti9G7bjL
+8InaHM9G40NkwTG7OxrggvNIejA8AZuqyWjOzTIKi-wwYvjeHYesSWuPiTGDN5THzkYLU4MD5r2_0PDhG7LIUG33z5HtM6CP3icyWEVOS61sD_2ZsBfJdbVA
+qM53XHDUwhY0TAwPug3OG9NonRFhO8ynF3I4unuAMDHmSrXH57V1RGvl9jafuZF9ZhqjWOEh98y0tUYGsUxkBSllIyBdT2oM5Fn2-ut-fzsq_cQNuL6Uvwqr
+knh4RrvOKzxZfLV3s0rs_R_1SdYt3VxeQ1_y2_W2
+}
+title SimpleDiagram
+skinparam titleBorderRoundCorner 15
+skinparam titleBorderThickness 2
+skinparam state {
+ BackgroundColor<> #87b741
+ BackgroundColor<> #3887C6
+ BorderColor #3887C6
+ BorderColor<> Black
+ FontColor<> White
+}
+skinparam agent {
+ BackgroundColor #ffffff
+ BorderColor #3887C6
+}
+state a <>
+state b <>
+state c
+agent t1
+agent t2
+a --> t1
+t1 --> b
+b --> t2
+t2 --> c
+footer \nGenerated by <$sf_logo> **Workflow Component** and **PlantUML**
+@enduml
diff --git a/src/Symfony/Component/Workflow/Tests/fixtures/puml/square/simple-workflow-nomarking-nofooter.puml b/src/Symfony/Component/Workflow/Tests/fixtures/puml/square/simple-workflow-nomarking-nofooter.puml
new file mode 100644
index 0000000000000..38b0870c9e41d
--- /dev/null
+++ b/src/Symfony/Component/Workflow/Tests/fixtures/puml/square/simple-workflow-nomarking-nofooter.puml
@@ -0,0 +1,26 @@
+@startuml
+allow_mixing
+title SimpleDiagram
+skinparam titleBorderRoundCorner 15
+skinparam titleBorderThickness 2
+skinparam state {
+ BackgroundColor<> #87b741
+ BackgroundColor<> #3887C6
+ BorderColor #3887C6
+ BorderColor<> Black
+ FontColor<> White
+}
+skinparam agent {
+ BackgroundColor #ffffff
+ BorderColor #3887C6
+}
+state a <>
+state b
+state c
+agent t1
+agent t2
+a --> t1
+t1 --> b
+b --> t2
+t2 --> c
+@enduml
diff --git a/src/Symfony/Component/Workflow/Tests/fixtures/puml/square/simple-workflow-nomarking.puml b/src/Symfony/Component/Workflow/Tests/fixtures/puml/square/simple-workflow-nomarking.puml
new file mode 100644
index 0000000000000..31e3b40061fac
--- /dev/null
+++ b/src/Symfony/Component/Workflow/Tests/fixtures/puml/square/simple-workflow-nomarking.puml
@@ -0,0 +1,35 @@
+@startuml
+allow_mixing
+sprite $sf_logo [81x20/16z] {
+hPNRaYiX24K1xwBo_tyx6-qaCtDEJ-KXLYMTLbp0HWcHZr3KRDJ8z94HG3jZn4_mijbQ2ryJoFePtXLWA_qxyGy19DpdY_10z11ZAbGjFHRwcEbcKx5-wqsV
+yIMo8StMCHKh8ZUxnEwrZiwRAUOvy1lLcPQF4lEFAjhzMd5WOAqvKflS0Enx8PbihiSYXM8ClGVAseIWTAjCgVSAcnYbQG79xKFsZ0VnDCNc7AVBoPSMcTsX
+UnrujbYjjz0NnsObkTgnmolqJD4QgGUYTQiNe8eIjtx4b6Vv8nPGpncn3NJ8Geo9W9VW2wGACm_JzgIO8A8KXr2jUBCVGEAAJSZ6JUlsNnmOzmIYti9G7bjL
+8InaHM9G40NkwTG7OxrggvNIejA8AZuqyWjOzTIKi-wwYvjeHYesSWuPiTGDN5THzkYLU4MD5r2_0PDhG7LIUG33z5HtM6CP3icyWEVOS61sD_2ZsBfJdbVA
+qM53XHDUwhY0TAwPug3OG9NonRFhO8ynF3I4unuAMDHmSrXH57V1RGvl9jafuZF9ZhqjWOEh98y0tUYGsUxkBSllIyBdT2oM5Fn2-ut-fzsq_cQNuL6Uvwqr
+knh4RrvOKzxZfLV3s0rs_R_1SdYt3VxeQ1_y2_W2
+}
+title SimpleDiagram
+skinparam titleBorderRoundCorner 15
+skinparam titleBorderThickness 2
+skinparam state {
+ BackgroundColor<> #87b741
+ BackgroundColor<> #3887C6
+ BorderColor #3887C6
+ BorderColor<> Black
+ FontColor<> White
+}
+skinparam agent {
+ BackgroundColor #ffffff
+ BorderColor #3887C6
+}
+state a <>
+state b
+state c
+agent t1
+agent t2
+a --> t1
+t1 --> b
+b --> t2
+t2 --> c
+footer \nGenerated by <$sf_logo> **Workflow Component** and **PlantUML**
+@enduml