Packages

  • Nette
    • Application
      • Diagnostics
      • Responses
      • Routers
      • UI
    • Caching
      • Storages
    • ComponentModel
    • Config
      • Adapters
      • Extensions
    • Database
      • Diagnostics
      • Drivers
      • Reflection
      • Table
    • DI
      • Diagnostics
    • Diagnostics
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Latte
      • Macros
    • Loaders
    • Localization
    • Mail
    • Reflection
    • Security
      • Diagnostics
    • Templating
    • Utils
      • PhpGenerator
  • NetteModule
  • None
  • PHP

Classes

  • ArrayHash
  • ArrayList
  • Callback
  • DateTime53
  • Environment
  • Framework
  • FreezableObject
  • Image
  • Object
  • ObjectMixin

Interfaces

  • IFreezable

Exceptions

  • ArgumentOutOfRangeException
  • DeprecatedException
  • DirectoryNotFoundException
  • FatalErrorException
  • FileNotFoundException
  • InvalidStateException
  • IOException
  • MemberAccessException
  • NotImplementedException
  • NotSupportedException
  • StaticClassException
  • UnknownImageFileException
  • Overview
  • Package
  • Class
  • Tree
  • Deprecated
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Nette Framework (http://nette.org)
  5:  * Copyright (c) 2004 David Grudl (http://davidgrudl.com)
  6:  * @package Nette
  7:  */
  8: 
  9: 
 10: 
 11: /**
 12:  * Basic manipulation with images.
 13:  *
 14:  * <code>
 15:  * $image = Image::fromFile('nette.jpg');
 16:  * $image->resize(150, 100);
 17:  * $image->sharpen();
 18:  * $image->send();
 19:  * </code>
 20:  *
 21:  * @author     David Grudl
 22:  *
 23:  * @method void alphaBlending(bool $on)
 24:  * @method void antialias(bool $on)
 25:  * @method void arc($x, $y, $w, $h, $s, $e, $color)
 26:  * @method void char($font, $x, $y, $char, $color)
 27:  * @method void charUp($font, $x, $y, $char, $color)
 28:  * @method int colorAllocate($red, $green, $blue)
 29:  * @method int colorAllocateAlpha($red, $green, $blue, $alpha)
 30:  * @method int colorAt($x, $y)
 31:  * @method int colorClosest($red, $green, $blue)
 32:  * @method int colorClosestAlpha($red, $green, $blue, $alpha)
 33:  * @method int colorClosestHWB($red, $green, $blue)
 34:  * @method void colorDeallocate($color)
 35:  * @method int colorExact($red, $green, $blue)
 36:  * @method int colorExactAlpha($red, $green, $blue, $alpha)
 37:  * @method void colorMatch(Image $image2)
 38:  * @method int colorResolve($red, $green, $blue)
 39:  * @method int colorResolveAlpha($red, $green, $blue, $alpha)
 40:  * @method void colorSet($index, $red, $green, $blue)
 41:  * @method array colorsForIndex($index)
 42:  * @method int colorsTotal()
 43:  * @method int colorTransparent([$color])
 44:  * @method void convolution(array $matrix, float $div, float $offset)
 45:  * @method void copy(Image $src, $dstX, $dstY, $srcX, $srcY, $srcW, $srcH)
 46:  * @method void copyMerge(Image $src, $dstX, $dstY, $srcX, $srcY, $srcW, $srcH, $opacity)
 47:  * @method void copyMergeGray(Image $src, $dstX, $dstY, $srcX, $srcY, $srcW, $srcH, $opacity)
 48:  * @method void copyResampled(Image $src, $dstX, $dstY, $srcX, $srcY, $dstW, $dstH, $srcW, $srcH)
 49:  * @method void copyResized(Image $src, $dstX, $dstY, $srcX, $srcY, $dstW, $dstH, $srcW, $srcH)
 50:  * @method void dashedLine($x1, $y1, $x2, $y2, $color)
 51:  * @method void ellipse($cx, $cy, $w, $h, $color)
 52:  * @method void fill($x, $y, $color)
 53:  * @method void filledArc($cx, $cy, $w, $h, $s, $e, $color, $style)
 54:  * @method void filledEllipse($cx, $cy, $w, $h, $color)
 55:  * @method void filledPolygon(array $points, $numPoints, $color)
 56:  * @method void filledRectangle($x1, $y1, $x2, $y2, $color)
 57:  * @method void fillToBorder($x, $y, $border, $color)
 58:  * @method void filter($filtertype [, ...])
 59:  * @method int fontHeight($font)
 60:  * @method int fontWidth($font)
 61:  * @method array ftBBox($size, $angle, string $fontFile, string $text [, array $extrainfo])
 62:  * @method array ftText($size, $angle, $x, $y, $col, string $fontFile, string $text [, array $extrainfo])
 63:  * @method void gammaCorrect(float $inputgamma, float $outputgamma)
 64:  * @method int interlace([$interlace])
 65:  * @method bool isTrueColor()
 66:  * @method void layerEffect($effect)
 67:  * @method void line($x1, $y1, $x2, $y2, $color)
 68:  * @method int loadFont(string $file)
 69:  * @method void paletteCopy(Image $source)
 70:  * @method void polygon(array $points, $numPoints, $color)
 71:  * @method array psBBox(string $text, $font, $size [, $space] [, $tightness] [, float $angle])
 72:  * @method void psEncodeFont($fontIndex, string $encodingfile)
 73:  * @method void psExtendFont($fontIndex, float $extend)
 74:  * @method void psFreeFont($fontindex)
 75:  * @method resource psLoadFont(string $filename)
 76:  * @method void psSlantFont($fontIndex, float $slant)
 77:  * @method array psText(string $text, $font, $size, $color, $backgroundColor, $x, $y [, $space] [, $tightness] [, float $angle] [, $antialiasSteps])
 78:  * @method void rectangle($x1, $y1, $x2, $y2, $col)
 79:  * @method Image rotate(float $angle, $backgroundColor)
 80:  * @method void saveAlpha(bool $saveflag)
 81:  * @method void setBrush(Image $brush)
 82:  * @method void setPixel($x, $y, $color)
 83:  * @method void setStyle(array $style)
 84:  * @method void setThickness($thickness)
 85:  * @method void setTile(Image $tile)
 86:  * @method void string($font, $x, $y, string $s, $col)
 87:  * @method void stringUp($font, $x, $y, string $s, $col)
 88:  * @method void trueColorToPalette(bool $dither, $ncolors)
 89:  * @method array ttfBBox($size, $angle, string $fontfile, string $text)
 90:  * @method array ttfText($size, $angle, $x, $y, $color, string $fontfile, string $text)
 91:  * @method int types()
 92:  * @property-read int $width
 93:  * @property-read int $height
 94:  * @property-read resource $imageResource
 95:  * @package Nette
 96:  */
 97: class Image extends Object
 98: {
 99:     /** {@link resize()} only shrinks images */
100:     const SHRINK_ONLY = 1;
101: 
102:     /** {@link resize()} will ignore aspect ratio */
103:     const STRETCH = 2;
104: 
105:     /** {@link resize()} fits in given area so its dimensions are less than or equal to the required dimensions */
106:     const FIT = 0;
107: 
108:     /** {@link resize()} fills given area so its dimensions are greater than or equal to the required dimensions */
109:     const FILL = 4;
110: 
111:     /** {@link resize()} fills given area exactly */
112:     const EXACT = 8;
113: 
114:     /** @int image types {@link send()} */
115:     const JPEG = IMAGETYPE_JPEG,
116:         PNG = IMAGETYPE_PNG,
117:         GIF = IMAGETYPE_GIF;
118: 
119:     const EMPTY_GIF = "GIF89a\x01\x00\x01\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00!\xf9\x04\x01\x00\x00\x00\x00,\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02D\x01\x00;";
120: 
121:     /** @deprecated */
122:     const ENLARGE = 0;
123: 
124:     /** @var resource */
125:     private $image;
126: 
127: 
128:     /**
129:      * Returns RGB color.
130:      * @param  int  red 0..255
131:      * @param  int  green 0..255
132:      * @param  int  blue 0..255
133:      * @param  int  transparency 0..127
134:      * @return array
135:      */
136:     public static function rgb($red, $green, $blue, $transparency = 0)
137:     {
138:         return array(
139:             'red' => max(0, min(255, (int) $red)),
140:             'green' => max(0, min(255, (int) $green)),
141:             'blue' => max(0, min(255, (int) $blue)),
142:             'alpha' => max(0, min(127, (int) $transparency)),
143:         );
144:     }
145: 
146: 
147:     /**
148:      * Opens image from file.
149:      * @param  string
150:      * @param  mixed  detected image format
151:      * @return Image
152:      */
153:     public static function fromFile($file, & $format = NULL)
154:     {
155:         if (!extension_loaded('gd')) {
156:             throw new NotSupportedException("PHP extension GD is not loaded.");
157:         }
158: 
159:         $info = @getimagesize($file); // @ - files smaller than 12 bytes causes read error
160: 
161:         switch ($format = $info[2]) {
162:             case self::JPEG:
163:                 return new self(imagecreatefromjpeg($file));
164: 
165:             case self::PNG:
166:                 return new self(imagecreatefrompng($file));
167: 
168:             case self::GIF:
169:                 return new self(imagecreatefromgif($file));
170: 
171:             default:
172:                 throw new UnknownImageFileException("Unknown image type or file '$file' not found.");
173:         }
174:     }
175: 
176: 
177:     /**
178:      * Get format from the image stream in the string.
179:      * @param  string
180:      * @return mixed  detected image format
181:      */
182:     public static function getFormatFromString($s)
183:     {
184:         $types = array('image/jpeg' => self::JPEG, 'image/gif' => self::GIF, 'image/png' => self::PNG);
185:         $type = MimeTypeDetector::fromString($s);
186:         return isset($types[$type]) ? $types[$type] : NULL;
187:     }
188: 
189: 
190:     /**
191:      * Create a new image from the image stream in the string.
192:      * @param  string
193:      * @param  mixed  detected image format
194:      * @return Image
195:      */
196:     public static function fromString($s, & $format = NULL)
197:     {
198:         if (!extension_loaded('gd')) {
199:             throw new NotSupportedException("PHP extension GD is not loaded.");
200:         }
201: 
202:         $format = self::getFormatFromString($s);
203: 
204:         return new self(imagecreatefromstring($s));
205:     }
206: 
207: 
208:     /**
209:      * Creates blank image.
210:      * @param  int
211:      * @param  int
212:      * @param  array
213:      * @return Image
214:      */
215:     public static function fromBlank($width, $height, $color = NULL)
216:     {
217:         if (!extension_loaded('gd')) {
218:             throw new NotSupportedException("PHP extension GD is not loaded.");
219:         }
220: 
221:         $width = (int) $width;
222:         $height = (int) $height;
223:         if ($width < 1 || $height < 1) {
224:             throw new InvalidArgumentException('Image width and height must be greater than zero.');
225:         }
226: 
227:         $image = imagecreatetruecolor($width, $height);
228:         if (is_array($color)) {
229:             $color += array('alpha' => 0);
230:             $color = imagecolorallocatealpha($image, $color['red'], $color['green'], $color['blue'], $color['alpha']);
231:             imagealphablending($image, FALSE);
232:             imagefilledrectangle($image, 0, 0, $width - 1, $height - 1, $color);
233:             imagealphablending($image, TRUE);
234:         }
235:         return new self($image);
236:     }
237: 
238: 
239:     /**
240:      * Wraps GD image.
241:      * @param  resource
242:      */
243:     public function __construct($image)
244:     {
245:         $this->setImageResource($image);
246:         imagesavealpha($image, TRUE);
247:     }
248: 
249: 
250:     /**
251:      * Returns image width.
252:      * @return int
253:      */
254:     public function getWidth()
255:     {
256:         return imagesx($this->image);
257:     }
258: 
259: 
260:     /**
261:      * Returns image height.
262:      * @return int
263:      */
264:     public function getHeight()
265:     {
266:         return imagesy($this->image);
267:     }
268: 
269: 
270:     /**
271:      * Sets image resource.
272:      * @param  resource
273:      * @return self
274:      */
275:     protected function setImageResource($image)
276:     {
277:         if (!is_resource($image) || get_resource_type($image) !== 'gd') {
278:             throw new InvalidArgumentException('Image is not valid.');
279:         }
280:         $this->image = $image;
281:         return $this;
282:     }
283: 
284: 
285:     /**
286:      * Returns image GD resource.
287:      * @return resource
288:      */
289:     public function getImageResource()
290:     {
291:         return $this->image;
292:     }
293: 
294: 
295:     /**
296:      * Resizes image.
297:      * @param  mixed  width in pixels or percent
298:      * @param  mixed  height in pixels or percent
299:      * @param  int    flags
300:      * @return self
301:      */
302:     public function resize($width, $height, $flags = self::FIT)
303:     {
304:         if ($flags & self::EXACT) {
305:             return $this->resize($width, $height, self::FILL)->crop('50%', '50%', $width, $height);
306:         }
307: 
308:         list($newWidth, $newHeight) = self::calculateSize($this->getWidth(), $this->getHeight(), $width, $height, $flags);
309: 
310:         if ($newWidth !== $this->getWidth() || $newHeight !== $this->getHeight()) { // resize
311:             $newImage = self::fromBlank($newWidth, $newHeight, self::RGB(0, 0, 0, 127))->getImageResource();
312:             imagecopyresampled(
313:                 $newImage, $this->getImageResource(),
314:                 0, 0, 0, 0,
315:                 $newWidth, $newHeight, $this->getWidth(), $this->getHeight()
316:             );
317:             $this->image = $newImage;
318:         }
319: 
320:         if ($width < 0 || $height < 0) { // flip is processed in two steps for better quality
321:             $newImage = self::fromBlank($newWidth, $newHeight, self::RGB(0, 0, 0, 127))->getImageResource();
322:             imagecopyresampled(
323:                 $newImage, $this->getImageResource(),
324:                 0, 0, $width < 0 ? $newWidth - 1 : 0, $height < 0 ? $newHeight - 1 : 0,
325:                 $newWidth, $newHeight, $width < 0 ? -$newWidth : $newWidth, $height < 0 ? -$newHeight : $newHeight
326:             );
327:             $this->image = $newImage;
328:         }
329:         return $this;
330:     }
331: 
332: 
333:     /**
334:      * Calculates dimensions of resized image.
335:      * @param  mixed  source width
336:      * @param  mixed  source height
337:      * @param  mixed  width in pixels or percent
338:      * @param  mixed  height in pixels or percent
339:      * @param  int    flags
340:      * @return array
341:      */
342:     public static function calculateSize($srcWidth, $srcHeight, $newWidth, $newHeight, $flags = self::FIT)
343:     {
344:         if (substr($newWidth, -1) === '%') {
345:             $newWidth = round($srcWidth / 100 * abs($newWidth));
346:             $percents = TRUE;
347:         } else {
348:             $newWidth = (int) abs($newWidth);
349:         }
350: 
351:         if (substr($newHeight, -1) === '%') {
352:             $newHeight = round($srcHeight / 100 * abs($newHeight));
353:             $flags |= empty($percents) ? 0 : self::STRETCH;
354:         } else {
355:             $newHeight = (int) abs($newHeight);
356:         }
357: 
358:         if ($flags & self::STRETCH) { // non-proportional
359:             if (empty($newWidth) || empty($newHeight)) {
360:                 throw new InvalidArgumentException('For stretching must be both width and height specified.');
361:             }
362: 
363:             if ($flags & self::SHRINK_ONLY) {
364:                 $newWidth = round($srcWidth * min(1, $newWidth / $srcWidth));
365:                 $newHeight = round($srcHeight * min(1, $newHeight / $srcHeight));
366:             }
367: 
368:         } else {  // proportional
369:             if (empty($newWidth) && empty($newHeight)) {
370:                 throw new InvalidArgumentException('At least width or height must be specified.');
371:             }
372: 
373:             $scale = array();
374:             if ($newWidth > 0) { // fit width
375:                 $scale[] = $newWidth / $srcWidth;
376:             }
377: 
378:             if ($newHeight > 0) { // fit height
379:                 $scale[] = $newHeight / $srcHeight;
380:             }
381: 
382:             if ($flags & self::FILL) {
383:                 $scale = array(max($scale));
384:             }
385: 
386:             if ($flags & self::SHRINK_ONLY) {
387:                 $scale[] = 1;
388:             }
389: 
390:             $scale = min($scale);
391:             $newWidth = round($srcWidth * $scale);
392:             $newHeight = round($srcHeight * $scale);
393:         }
394: 
395:         return array(max((int) $newWidth, 1), max((int) $newHeight, 1));
396:     }
397: 
398: 
399:     /**
400:      * Crops image.
401:      * @param  mixed  x-offset in pixels or percent
402:      * @param  mixed  y-offset in pixels or percent
403:      * @param  mixed  width in pixels or percent
404:      * @param  mixed  height in pixels or percent
405:      * @return self
406:      */
407:     public function crop($left, $top, $width, $height)
408:     {
409:         list($left, $top, $width, $height) = self::calculateCutout($this->getWidth(), $this->getHeight(), $left, $top, $width, $height);
410:         $newImage = self::fromBlank($width, $height, self::RGB(0, 0, 0, 127))->getImageResource();
411:         imagecopy($newImage, $this->getImageResource(), 0, 0, $left, $top, $width, $height);
412:         $this->image = $newImage;
413:         return $this;
414:     }
415: 
416: 
417:     /**
418:      * Calculates dimensions of cutout in image.
419:      * @param  mixed  source width
420:      * @param  mixed  source height
421:      * @param  mixed  x-offset in pixels or percent
422:      * @param  mixed  y-offset in pixels or percent
423:      * @param  mixed  width in pixels or percent
424:      * @param  mixed  height in pixels or percent
425:      * @return array
426:      */
427:     public static function calculateCutout($srcWidth, $srcHeight, $left, $top, $newWidth, $newHeight)
428:     {
429:         if (substr($newWidth, -1) === '%') {
430:             $newWidth = round($srcWidth / 100 * $newWidth);
431:         }
432:         if (substr($newHeight, -1) === '%') {
433:             $newHeight = round($srcHeight / 100 * $newHeight);
434:         }
435:         if (substr($left, -1) === '%') {
436:             $left = round(($srcWidth - $newWidth) / 100 * $left);
437:         }
438:         if (substr($top, -1) === '%') {
439:             $top = round(($srcHeight - $newHeight) / 100 * $top);
440:         }
441:         if ($left < 0) {
442:             $newWidth += $left; $left = 0;
443:         }
444:         if ($top < 0) {
445:             $newHeight += $top; $top = 0;
446:         }
447:         $newWidth = min((int) $newWidth, $srcWidth - $left);
448:         $newHeight = min((int) $newHeight, $srcHeight - $top);
449:         return array($left, $top, $newWidth, $newHeight);
450:     }
451: 
452: 
453:     /**
454:      * Sharpen image.
455:      * @return self
456:      */
457:     public function sharpen()
458:     {
459:         imageconvolution($this->getImageResource(), array( // my magic numbers ;)
460:             array( -1, -1, -1 ),
461:             array( -1, 24, -1 ),
462:             array( -1, -1, -1 ),
463:         ), 16, 0);
464:         return $this;
465:     }
466: 
467: 
468:     /**
469:      * Puts another image into this image.
470:      * @param  Image
471:      * @param  mixed  x-coordinate in pixels or percent
472:      * @param  mixed  y-coordinate in pixels or percent
473:      * @param  int  opacity 0..100
474:      * @return self
475:      */
476:     public function place(Image $image, $left = 0, $top = 0, $opacity = 100)
477:     {
478:         $opacity = max(0, min(100, (int) $opacity));
479: 
480:         if (substr($left, -1) === '%') {
481:             $left = round(($this->getWidth() - $image->getWidth()) / 100 * $left);
482:         }
483: 
484:         if (substr($top, -1) === '%') {
485:             $top = round(($this->getHeight() - $image->getHeight()) / 100 * $top);
486:         }
487: 
488:         if ($opacity === 100) {
489:             imagecopy(
490:                 $this->getImageResource(), $image->getImageResource(),
491:                 $left, $top, 0, 0, $image->getWidth(), $image->getHeight()
492:             );
493: 
494:         } elseif ($opacity <> 0) {
495:             imagecopymerge(
496:                 $this->getImageResource(), $image->getImageResource(),
497:                 $left, $top, 0, 0, $image->getWidth(), $image->getHeight(),
498:                 $opacity
499:             );
500:         }
501:         return $this;
502:     }
503: 
504: 
505:     /**
506:      * Saves image to the file.
507:      * @param  string  filename
508:      * @param  int  quality 0..100 (for JPEG and PNG)
509:      * @param  int  optional image type
510:      * @return bool TRUE on success or FALSE on failure.
511:      */
512:     public function save($file = NULL, $quality = NULL, $type = NULL)
513:     {
514:         if ($type === NULL) {
515:             switch (strtolower(pathinfo($file, PATHINFO_EXTENSION))) {
516:                 case 'jpg':
517:                 case 'jpeg':
518:                     $type = self::JPEG;
519:                     break;
520:                 case 'png':
521:                     $type = self::PNG;
522:                     break;
523:                 case 'gif':
524:                     $type = self::GIF;
525:             }
526:         }
527: 
528:         switch ($type) {
529:             case self::JPEG:
530:                 $quality = $quality === NULL ? 85 : max(0, min(100, (int) $quality));
531:                 return imagejpeg($this->getImageResource(), $file, $quality);
532: 
533:             case self::PNG:
534:                 $quality = $quality === NULL ? 9 : max(0, min(9, (int) $quality));
535:                 return imagepng($this->getImageResource(), $file, $quality);
536: 
537:             case self::GIF:
538:                 return $file === NULL ? imagegif($this->getImageResource()) : imagegif($this->getImageResource(), $file); // PHP bug #44591
539: 
540:             default:
541:                 throw new InvalidArgumentException("Unsupported image type.");
542:         }
543:     }
544: 
545: 
546:     /**
547:      * Outputs image to string.
548:      * @param  int  image type
549:      * @param  int  quality 0..100 (for JPEG and PNG)
550:      * @return string
551:      */
552:     public function toString($type = self::JPEG, $quality = NULL)
553:     {
554:         ob_start();
555:         $this->save(NULL, $quality, $type);
556:         return ob_get_clean();
557:     }
558: 
559: 
560:     /**
561:      * Outputs image to string.
562:      * @return string
563:      */
564:     public function __toString()
565:     {
566:         try {
567:             return $this->toString();
568: 
569:         } catch (Exception $e) {
570:             trigger_error("Exception in " . __METHOD__ . "(): {$e->getMessage()} in {$e->getFile()}:{$e->getLine()}", E_USER_ERROR);
571:         }
572:     }
573: 
574: 
575:     /**
576:      * Outputs image to browser.
577:      * @param  int  image type
578:      * @param  int  quality 0..100 (for JPEG and PNG)
579:      * @return bool TRUE on success or FALSE on failure.
580:      */
581:     public function send($type = self::JPEG, $quality = NULL)
582:     {
583:         if ($type !== self::GIF && $type !== self::PNG && $type !== self::JPEG) {
584:             throw new InvalidArgumentException("Unsupported image type.");
585:         }
586:         header('Content-Type: ' . image_type_to_mime_type($type));
587:         return $this->save(NULL, $quality, $type);
588:     }
589: 
590: 
591:     /**
592:      * Call to undefined method.
593:      *
594:      * @param  string  method name
595:      * @param  array   arguments
596:      * @return mixed
597:      * @throws MemberAccessException
598:      */
599:     public function __call($name, $args)
600:     {
601:         $function = 'image' . $name;
602:         if (function_exists($function)) {
603:             foreach ($args as $key => $value) {
604:                 if ($value instanceof self) {
605:                     $args[$key] = $value->getImageResource();
606: 
607:                 } elseif (is_array($value) && isset($value['red'])) { // rgb
608:                     $args[$key] = imagecolorallocatealpha(
609:                         $this->getImageResource(),
610:                         $value['red'], $value['green'], $value['blue'], $value['alpha']
611:                     );
612:                 }
613:             }
614:             array_unshift($args, $this->getImageResource());
615: 
616:             $res = call_user_func_array($function, $args);
617:             return is_resource($res) && get_resource_type($res) === 'gd' ? $this->setImageResource($res) : $res;
618:         }
619: 
620:         return parent::__call($name, $args);
621:     }
622: 
623: }
624: 
625: 
626: /**
627:  * The exception that indicates invalid image file.
628:  * @package Nette
629:  */
630: class UnknownImageFileException extends Exception
631: {
632: }
633: 
Nette Framework 2.0.14 (for PHP 5.2, un-prefixed) API API documentation generated by ApiGen 2.8.0