icingaweb2/library/vendor/dompdf/lib/php-font-lib/classes/Glyph/OutlineSimple.php

335 lines
8.2 KiB
PHP

<?php
/**
* @package php-font-lib
* @link https://github.com/PhenX/php-font-lib
* @author Fabien Ménager <fabien.menager@gmail.com>
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
* @version $Id: Font_Table_glyf.php 46 2012-04-02 20:22:38Z fabien.menager $
*/
namespace FontLib\Glyph;
/**
* `glyf` font table.
*
* @package php-font-lib
*/
class OutlineSimple extends Outline {
const ON_CURVE = 0x01;
const X_SHORT_VECTOR = 0x02;
const Y_SHORT_VECTOR = 0x04;
const REPEAT = 0x08;
const THIS_X_IS_SAME = 0x10;
const THIS_Y_IS_SAME = 0x20;
public $instructions;
public $points;
function parseData() {
parent::parseData();
if (!$this->size) {
return;
}
$font = $this->getFont();
$noc = $this->numberOfContours;
if ($noc == 0) {
return;
}
$endPtsOfContours = $font->r(array(self::uint16, $noc));
$instructionLength = $font->readUInt16();
$this->instructions = $font->r(array(self::uint8, $instructionLength));
$count = $endPtsOfContours[$noc - 1] + 1;
// Flags
$flags = array();
for ($index = 0; $index < $count; $index++) {
$flags[$index] = $font->readUInt8();
if ($flags[$index] & self::REPEAT) {
$repeats = $font->readUInt8();
for ($i = 1; $i <= $repeats; $i++) {
$flags[$index + $i] = $flags[$index];
}
$index += $repeats;
}
}
$points = array();
foreach ($flags as $i => $flag) {
$points[$i]["onCurve"] = $flag & self::ON_CURVE;
$points[$i]["endOfContour"] = in_array($i, $endPtsOfContours);
}
// X Coords
$x = 0;
for ($i = 0; $i < $count; $i++) {
$flag = $flags[$i];
if ($flag & self::THIS_X_IS_SAME) {
if ($flag & self::X_SHORT_VECTOR) {
$x += $font->readUInt8();
}
}
else {
if ($flag & self::X_SHORT_VECTOR) {
$x -= $font->readUInt8();
}
else {
$x += $font->readInt16();
}
}
$points[$i]["x"] = $x;
}
// Y Coords
$y = 0;
for ($i = 0; $i < $count; $i++) {
$flag = $flags[$i];
if ($flag & self::THIS_Y_IS_SAME) {
if ($flag & self::Y_SHORT_VECTOR) {
$y += $font->readUInt8();
}
}
else {
if ($flag & self::Y_SHORT_VECTOR) {
$y -= $font->readUInt8();
}
else {
$y += $font->readInt16();
}
}
$points[$i]["y"] = $y;
}
$this->points = $points;
}
public function splitSVGPath($path) {
preg_match_all('/([a-z])|(-?\d+(?:\.\d+)?)/i', $path, $matches, PREG_PATTERN_ORDER);
return $matches[0];
}
public function makePoints($path) {
$path = $this->splitSVGPath($path);
$l = count($path);
$i = 0;
$points = array();
while ($i < $l) {
switch ($path[$i]) {
// moveTo
case "M":
$points[] = array(
"onCurve" => true,
"x" => $path[++$i],
"y" => $path[++$i],
"endOfContour" => false,
);
break;
// lineTo
case "L":
$points[] = array(
"onCurve" => true,
"x" => $path[++$i],
"y" => $path[++$i],
"endOfContour" => false,
);
break;
// quadraticCurveTo
case "Q":
$points[] = array(
"onCurve" => false,
"x" => $path[++$i],
"y" => $path[++$i],
"endOfContour" => false,
);
$points[] = array(
"onCurve" => true,
"x" => $path[++$i],
"y" => $path[++$i],
"endOfContour" => false,
);
break;
// closePath
/** @noinspection PhpMissingBreakStatementInspection */
case "z":
$points[count($points) - 1]["endOfContour"] = true;
default:
$i++;
break;
}
}
return $points;
}
function encode() {
if (empty($this->points)) {
return parent::encode();
}
return $this->size = $this->encodePoints($this->points);
}
public function encodePoints($points) {
$endPtsOfContours = array();
$flags = array();
$coords_x = array();
$coords_y = array();
$last_x = 0;
$last_y = 0;
$xMin = $yMin = 0xFFFF;
$xMax = $yMax = -0xFFFF;
foreach ($points as $i => $point) {
$flag = 0;
if ($point["onCurve"]) {
$flag |= self::ON_CURVE;
}
if ($point["endOfContour"]) {
$endPtsOfContours[] = $i;
}
// Simplified, we could do some optimizations
if ($point["x"] == $last_x) {
$flag |= self::THIS_X_IS_SAME;
}
else {
$x = intval($point["x"]);
$xMin = min($x, $xMin);
$xMax = max($x, $xMax);
$coords_x[] = $x - $last_x; // int16
}
// Simplified, we could do some optimizations
if ($point["y"] == $last_y) {
$flag |= self::THIS_Y_IS_SAME;
}
else {
$y = intval($point["y"]);
$yMin = min($y, $yMin);
$yMax = max($y, $yMax);
$coords_y[] = $y - $last_y; // int16
}
$flags[] = $flag;
$last_x = $point["x"];
$last_y = $point["y"];
}
$font = $this->getFont();
$l = 0;
$l += $font->writeInt16(count($endPtsOfContours)); // endPtsOfContours
$l += $font->writeFWord(isset($this->xMin) ? $this->xMin : $xMin); // xMin
$l += $font->writeFWord(isset($this->yMin) ? $this->yMin : $yMin); // yMin
$l += $font->writeFWord(isset($this->xMax) ? $this->xMax : $xMax); // xMax
$l += $font->writeFWord(isset($this->yMax) ? $this->yMax : $yMax); // yMax
// Simple glyf
$l += $font->w(array(self::uint16, count($endPtsOfContours)), $endPtsOfContours); // endPtsOfContours
$l += $font->writeUInt16(0); // instructionLength
$l += $font->w(array(self::uint8, count($flags)), $flags); // flags
$l += $font->w(array(self::int16, count($coords_x)), $coords_x); // xCoordinates
$l += $font->w(array(self::int16, count($coords_y)), $coords_y); // yCoordinates
return $l;
}
public function getSVGContours($points = null) {
$path = "";
if (!$points) {
if (empty($this->points)) {
$this->parseData();
}
$points = $this->points;
}
$length = count($points);
$firstIndex = 0;
$count = 0;
for ($i = 0; $i < $length; $i++) {
$count++;
if ($points[$i]["endOfContour"]) {
$path .= $this->getSVGPath($points, $firstIndex, $count);
$firstIndex = $i + 1;
$count = 0;
}
}
return $path;
}
protected function getSVGPath($points, $startIndex, $count) {
$offset = 0;
$path = "";
while ($offset < $count) {
$point = $points[$startIndex + $offset % $count];
$point_p1 = $points[$startIndex + ($offset + 1) % $count];
if ($offset == 0) {
$path .= "M{$point['x']},{$point['y']} ";
}
if ($point["onCurve"]) {
if ($point_p1["onCurve"]) {
$path .= "L{$point_p1['x']},{$point_p1['y']} ";
$offset++;
}
else {
$point_p2 = $points[$startIndex + ($offset + 2) % $count];
if ($point_p2["onCurve"]) {
$path .= "Q{$point_p1['x']},{$point_p1['y']},{$point_p2['x']},{$point_p2['y']} ";
}
else {
$path .= "Q{$point_p1['x']},{$point_p1['y']}," . $this->midValue($point_p1['x'], $point_p2['x']) . "," . $this->midValue($point_p1['y'], $point_p2['y']) . " ";
}
$offset += 2;
}
}
else {
if ($point_p1["onCurve"]) {
$path .= "Q{$point['x']},{$point['y']},{$point_p1['x']},{$point_p1['y']} ";
}
else {
$path .= "Q{$point['x']},{$point['y']}," . $this->midValue($point['x'], $point_p1['x']) . "," . $this->midValue($point['y'], $point_p1['y']) . " ";
}
$offset++;
}
}
$path .= "z ";
return $path;
}
function midValue($a, $b) {
return $a + ($b - $a) / 2;
}
}