ImageOutputStrategy.php
1 <?php
2 /**
3  * wCMF - wemove Content Management Framework
4  * Copyright (C) 2005-2020 wemove digital solutions GmbH
5  *
6  * Licensed under the terms of the MIT License.
7  *
8  * See the LICENSE file distributed with this work for
9  * additional information.
10  */
11 namespace wcmf\lib\model\output;
12 
16 
17 /**
18  * ImageOutputStrategy outputs a tree of objects into an image file. It must be configured
19  * with a map that was calculated by a LayoutVisitor.
20  *
21  * @author ingo herwig <ingo@wemove.com>
22  */
24 
25  const LINETYPE_DIRECT = 0;
26  const LINETYPE_ROUTED = 1;
27 
28  protected $format = null;
29  protected $file = '';
30  protected $map = null;
31  protected $img = null;
32  protected $width = 0;
33  protected $height = 0;
34  protected $scale = null;
35  protected $border = 0;
36  protected $bgColor = null;
37  protected $txtColor = null;
38  protected $lineColor = null;
39  protected $lineType = null;
40  protected $labelDim = null;
41  protected $textPos = null;
42  protected $usemap = '';
43 
44  /**
45  * Constructor.
46  * @param $format Image format name [IMG_GIF | IMG_JPG | IMG_PNG | IMG_WBMP].
47  * @param $file The output file name.
48  * @param $map The position map provided by LayoutVisitor.
49  * @param $lineType The linetype to use [LINETYPE_DIRECT|LINETYPE_ROUTED] DEFAULT LINETYPE_DIRECT.
50  * @param $scale The image scale DEFAULT 100.
51  * @param $aspect The image aspect DEFAULT 0.5.
52  * @param $border The image border [px] DEFAULT 50.
53  * @param $usemap Name of the HTML ImageMap to write to stdout ['' means no map] DEFAULT ''.
54  */
55  public function __construct($format, $file, $map, $lineType=self::LINETYPE_DIRECT,
56  $scale=100, $aspect=0.5, $border=50, $usemap='') {
57 
58  if (!(ImageTypes() & $format)) {
59  IllegalArgumentException($format." image support is disabled.");
60  }
61  if (!is_array($map)) {
62  IllegalArgumentException("Parameter map is no array.");
63  }
64  $this->format = $format;
65  $this->file = $file;
66  $this->map = $map;
67  $this->lineType = $lineType;
68  $this->scale['x'] = $scale;
69  $this->scale['y'] = $scale/$aspect;
70  $this->border = $border;
71  $this->usemap = $usemap;
72  // define label dimensions relative to connector position
73  $this->labelDim['left'] = -10;
74  $this->labelDim['top'] = -10;
75  $this->labelDim['right'] = 80;
76  $this->labelDim['bottom'] = 20;
77  // define text position relative to connector position
78  $this->textPos['left'] = -5;
79  $this->textPos['top'] = -8;
80  }
81 
82  /**
83  * @see OutputStrategy::writeHeader
84  */
85  public function writeHeader() {
86  // calculate bounding box
87  while (list ($key, $val) = each ($this->map)) {
88  if($val->x >= $this->width) {
89  $this->width = $val->x;
90  }
91  if($val->y >= $this->height) {
92  $this->height = $val->y;
93  }
94  }
95  $this->width = $this->width * $this->scale['x'] + $this->labelDim['right'] - $this->labelDim['left'] + 2*$this->border;
96  $this->height = $this->height * $this->scale['y'] + $this->labelDim['bottom'] - $this->labelDim['top'] + 2*$this->border;
97  $this->img = ImageCreate($this->width, $this->height);
98  $this->bgColor = ImageColorAllocate($this->img, 255, 255, 255);
99  $this->txtColor = ImageColorAllocate($this->img, 0, 128, 192);
100  $this->lineColor = $this->txtColor;
101  ImageFilledRectangle($this->img, 0, 0, $this->width, $this->height, $this->bgColor);
102 
103  if ($this->usemap != '') {
104  echo "\n".'<map name="'.$this->usemap.'">'."\n";
105  }
106  }
107 
108  /**
109  * @see OutputStrategy::writeFooter
110  */
111  public function writeFooter() {
112  ImageString($this->img, 1, $this->width-350, $this->height-10, 'wemove digital solutions. '.date ("l dS of F Y h:i:s A"), $this->txtColor);
113  if ($this->format & IMG_GIF) {
114  ImageGIF($this->img, $this->file);
115  }
116  if ($this->format & IMG_PNG) {
117  ImagePNG($this->img, $this->file);
118  }
119  if ($this->format & IMG_JPEG) {
120  ImageJPEG($this->img, $this->file);
121  }
122  if ($this->format & IMG_WBMP) {
123  ImageWBMP($this->img, $this->file);
124  }
125  if ($this->usemap != '') {
126  echo "\n".'</map>'."\n";
127  }
128  }
129 
130  /**
131  * @see OutputStrategy::writeObject
132  */
133  public function writeObject(PersistentObject $obj) {
134  $oid = $obj->getOID();
135  $x = $this->map[$oid]->x * $this->scale['x'] - $this->labelDim['left'] + $this->border;
136  $y = $this->map[$oid]->y * $this->scale['y'] - $this->labelDim['top'] + $this->border;
137 
138  $statusStr = '';
139  if ($obj->getState() == PersistentObject::STATE_DIRTY) {
140  $statusStr = 'M';
141  }
142  if ($obj->getState() == PersistentObject::STATE_NEW) {
143  $statusStr = 'N';
144  }
145  if ($obj->getState() == PersistentObject::STATE_DELETED) {
146  $statusStr = 'D';
147  }
148 
149  // print label
150  ImageRectangle($this->img,
151  $x + $this->labelDim['left'],
152  $y + $this->labelDim['top'],
153  $x + $this->labelDim['right'],
154  $y + $this->labelDim['bottom'],
155  $this->txtColor);
156  // write text
157  ImageString($this->img, 1,
158  $x + $this->textPos['left'],
159  $y + $this->textPos['top'],
160  $obj->getType(),
161  $this->txtColor);
162  if (strlen($oid) > 7) {
163  $idStr = "...".subStr($oid, strlen($oid)-4, 4);
164  }
165  else {
166  $idStr = $oid;
167  }
168  ImageString($this->img, 5,
169  $x + $this->textPos['left'],
170  $y + $this->textPos['top']+14,
171  $idStr.' '.$statusStr,
172  $this->txtColor);
173 
174  // draw line
175  $parent = $obj->getParent();
176  if ($parent) {
177  $this->drawConnectionLine($parent->getOID(), $oid);
178  }
179  // print map
180  if ($this->usemap != '')
181  {
182  echo '<area shape="rect" coords="'.
183  ($x + $this->labelDim['left']).','.
184  ($y + $this->labelDim['top']).','.
185  ($x + $this->labelDim['right']).','.
186  ($y + $this->labelDim['bottom'] + 8*$this->map[$oid]->z).
187  '" onclick="javascript:if (nodeClicked) nodeClicked(\''.$obj->getOID().'\')" alt="'.$obj->getOID().'">'."\n";
188  }
189  }
190 
191  /**
192  * Draw connection line.
193  * @param $poid The parent object's object id.
194  * @param $oid The object's object id.
195  */
196  protected function drawConnectionLine($poid, $oid) {
197  list($start, $end) = $this->calculateEndPoints($poid, $oid);
198  if($this->lineType == self::LINETYPE_DIRECT) {
199  $this->drawDirectLine($start, $end);
200  }
201  else if($this->lineType == self::LINETYPE_ROUTED) {
202  $this->drawRoutedLine($start, $end);
203  }
204  }
205 
206  /**
207  * Draw direct line.
208  * @param $start The start point (Position).
209  * @param $end The end point (Position).
210  */
211  protected function drawDirectLine($start, $end) {
212  ImageLine($this->img,
213  $start->x,
214  $start->y,
215  $end->x,
216  $end->y,
217  $this->lineColor);
218  }
219 
220  /**
221  * Draw routed line.
222  * @param $start The start point (Position).
223  * @param $end The end point (Position).
224  */
225  protected function drawRoutedLine($start, $end) {
226  if ($this->map["type"] == MAPTYPE_HORIZONTAL) {
227  ImageLine($this->img,
228  $start->x,
229  $start->y,
230  $start->x,
231  $start->y-($start->y-$end->y)/2,
232  $this->lineColor);
233  ImageLine($this->img,
234  $start->x,
235  $start->y-($start->y-$end->y)/2,
236  $end->x,
237  $start->y-($start->y-$end->y)/2,
238  $this->lineColor);
239  ImageLine($this->img,
240  $end->x,
241  $start->y-($start->y-$end->y)/2,
242  $end->x,
243  $end->y,
244  $this->lineColor);
245  }
246  else {
247  ImageLine($this->img,
248  $start->x,
249  $start->y,
250  $start->x+($end->x-$start->x)/2,
251  $start->y,
252  $this->lineColor);
253  ImageLine($this->img,
254  $start->x+($end->x-$start->x)/2,
255  $start->y,
256  $start->x+($end->x-$start->x)/2,
257  $end->y,
258  $this->lineColor);
259  ImageLine($this->img,
260  $start->x+($end->x-$start->x)/2,
261  $end->y,
262  $end->x,
263  $end->y,
264  $this->lineColor);
265  }
266  }
267 
268  /**
269  * Calculate line end points.
270  * @param $poid The parent object's object id.
271  * @param $oid The object's object id.
272  * @return Array containing start and end position
273  */
274  private function calculateEndPoints($poid, $oid) {
275  // from child...
276  if ($this->map["type"] == MAPTYPE_HORIZONTAL) {
277  // connect from mid top...
278  $x1 = $this->map[$oid]->x * $this->scale['x'] + ($this->labelDim['right'] - $this->labelDim['left'])/2 + $this->border;
279  $y1 = $this->map[$oid]->y * $this->scale['y'] + $this->border - 1;
280  }
281  else {
282  // connect from mid left...
283  $x1 = $this->map[$oid]->x * $this->scale['x'] + $this->border - 1;
284  $y1 = $this->map[$oid]->y * $this->scale['y'] + ($this->labelDim['bottom'] - $this->labelDim['top'])/2 + $this->border;
285  }
286  // ...to parent
287  if ($this->map["type"] == MAPTYPE_HORIZONTAL) {
288  // ...to mid bottom
289  $x2 = $this->map[$poid]->x * $this->scale['x'] + ($this->labelDim['right'] - $this->labelDim['left'])/2 + $this->border;
290  $y2 = $this->map[$poid]->y * $this->scale['y'] + ($this->labelDim['bottom'] - $this->labelDim['top']) + $this->border + 1;
291  }
292  else {
293  // ...to mid right
294  $x2 = $this->map[$poid]->x * $this->scale['x'] + $this->labelDim['right'] - $this->labelDim['left'] + $this->border + 1;
295  $y2 = $this->map[$poid]->y * $this->scale['y'] + ($this->labelDim['bottom'] - $this->labelDim['top'])/2 + $this->border;
296  }
297  return [new Position($x1, $y1, 0), new Position($x2, $y2, 0)];
298  }
299 }
300 ?>
OutputStrategy defines the interface for classes that write an object's content to a destination (cal...
getType()
Get the type of the object.
The Position class stores a coordinate tuple for use with the LayoutVisitor.
Definition: Position.php:19
__construct($format, $file, $map, $lineType=self::LINETYPE_DIRECT, $scale=100, $aspect=0.5, $border=50, $usemap='')
Constructor.
getOID()
Get the object id of the PersistentObject.
IllegalArgumentException signals an exception in method arguments.
PersistentObject defines the interface of all persistent objects.
drawConnectionLine($poid, $oid)
Draw connection line.
getState()
Get the object's state:
ImageOutputStrategy outputs a tree of objects into an image file.