OutlineComposite.php 7.08 KB
<?php
/**
 * @package php-font-lib
 * @link    https://github.com/dompdf/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;

/**
 * Composite glyph outline
 *
 * @package php-font-lib
 */
class OutlineComposite extends Outline {
  const ARG_1_AND_2_ARE_WORDS    = 0x0001;
  const ARGS_ARE_XY_VALUES       = 0x0002;
  const ROUND_XY_TO_GRID         = 0x0004;
  const WE_HAVE_A_SCALE          = 0x0008;
  const MORE_COMPONENTS          = 0x0020;
  const WE_HAVE_AN_X_AND_Y_SCALE = 0x0040;
  const WE_HAVE_A_TWO_BY_TWO     = 0x0080;
  const WE_HAVE_INSTRUCTIONS     = 0x0100;
  const USE_MY_METRICS           = 0x0200;
  const OVERLAP_COMPOUND         = 0x0400;

  /**
   * @var OutlineComponent[]
   */
  public $components = array();

  function getGlyphIDs() {
    if (empty($this->components)) {
      $this->parseData();
    }

    $glyphIDs = array();
    foreach ($this->components as $_component) {
      $glyphIDs[] = $_component->glyphIndex;

      $_glyph   = $this->table->data[$_component->glyphIndex];

      if ($_glyph !== $this) {
        $glyphIDs = array_merge($glyphIDs, $_glyph->getGlyphIDs());
      }
    }

    return $glyphIDs;
  }

  /*function parse() {
    //$this->parseData();
  }*/

  function parseData() {
    parent::parseData();

    $font = $this->getFont();

    do {
      $flags      = $font->readUInt16();
      $glyphIndex = $font->readUInt16();

      $a = 1.0;
      $b = 0.0;
      $c = 0.0;
      $d = 1.0;
      $e = 0.0;
      $f = 0.0;

      $point_compound  = null;
      $point_component = null;

      $instructions = null;

      if ($flags & self::ARG_1_AND_2_ARE_WORDS) {
        if ($flags & self::ARGS_ARE_XY_VALUES) {
          $e = $font->readInt16();
          $f = $font->readInt16();
        }
        else {
          $point_compound  = $font->readUInt16();
          $point_component = $font->readUInt16();
        }
      }
      else {
        if ($flags & self::ARGS_ARE_XY_VALUES) {
          $e = $font->readInt8();
          $f = $font->readInt8();
        }
        else {
          $point_compound  = $font->readUInt8();
          $point_component = $font->readUInt8();
        }
      }

      if ($flags & self::WE_HAVE_A_SCALE) {
        $a = $d = $font->readInt16();
      }
      elseif ($flags & self::WE_HAVE_AN_X_AND_Y_SCALE) {
        $a = $font->readInt16();
        $d = $font->readInt16();
      }
      elseif ($flags & self::WE_HAVE_A_TWO_BY_TWO) {
        $a = $font->readInt16();
        $b = $font->readInt16();
        $c = $font->readInt16();
        $d = $font->readInt16();
      }

      //if ($flags & self::WE_HAVE_INSTRUCTIONS) {
      //
      //}

      $component                  = new OutlineComponent();
      $component->flags           = $flags;
      $component->glyphIndex      = $glyphIndex;
      $component->a               = $a;
      $component->b               = $b;
      $component->c               = $c;
      $component->d               = $d;
      $component->e               = $e;
      $component->f               = $f;
      $component->point_compound  = $point_compound;
      $component->point_component = $point_component;
      $component->instructions    = $instructions;

      $this->components[] = $component;
    } while ($flags & self::MORE_COMPONENTS);
    if ($flags & self::WE_HAVE_INSTRUCTIONS) {
      $numInstr = $font->readUInt16();
      $instr = $font->read($numInstr);
      $this->components[count($this->components) - 1]->instructions = pack('n', $numInstr) . $instr;
    }
  }

  function encode() {
    $font = $this->getFont();

    $gids = $font->getSubset();

    $size = $font->writeInt16(-1);
    $size += $font->writeFWord($this->xMin);
    $size += $font->writeFWord($this->yMin);
    $size += $font->writeFWord($this->xMax);
    $size += $font->writeFWord($this->yMax);

    foreach ($this->components as $_i => $_component) {
      $flags = 0;
      if ($_component->point_component === null && $_component->point_compound === null) {
        $flags |= self::ARGS_ARE_XY_VALUES;

        if (abs($_component->e) > 0x7F || abs($_component->f) > 0x7F) {
          $flags |= self::ARG_1_AND_2_ARE_WORDS;
        }
      }
      elseif ($_component->point_component > 0xFF || $_component->point_compound > 0xFF) {
        $flags |= self::ARG_1_AND_2_ARE_WORDS;
      }

      if ($_component->b == 0 && $_component->c == 0) {
        if ($_component->a == $_component->d) {
          if ($_component->a != 1.0) {
            $flags |= self::WE_HAVE_A_SCALE;
          }
        }
        else {
          $flags |= self::WE_HAVE_AN_X_AND_Y_SCALE;
        }
      }
      else {
        $flags |= self::WE_HAVE_A_TWO_BY_TWO;
      }

      if ($_i < count($this->components) - 1) {
        $flags |= self::MORE_COMPONENTS;
      } elseif($_component->instructions !== null) {
        $flags |= self::WE_HAVE_INSTRUCTIONS;
      }

      $size += $font->writeUInt16($flags);

      $new_gid = array_search($_component->glyphIndex, $gids);
      $size += $font->writeUInt16($new_gid);

      if ($flags & self::ARG_1_AND_2_ARE_WORDS) {
        if ($flags & self::ARGS_ARE_XY_VALUES) {
          $size += $font->writeInt16($_component->e);
          $size += $font->writeInt16($_component->f);
        }
        else {
          $size += $font->writeUInt16($_component->point_compound);
          $size += $font->writeUInt16($_component->point_component);
        }
      }
      else {
        if ($flags & self::ARGS_ARE_XY_VALUES) {
          $size += $font->writeInt8($_component->e);
          $size += $font->writeInt8($_component->f);
        }
        else {
          $size += $font->writeUInt8($_component->point_compound);
          $size += $font->writeUInt8($_component->point_component);
        }
      }

      if ($flags & self::WE_HAVE_A_SCALE) {
        $size += $font->writeInt16($_component->a);
      }
      elseif ($flags & self::WE_HAVE_AN_X_AND_Y_SCALE) {
        $size += $font->writeInt16($_component->a);
        $size += $font->writeInt16($_component->d);
      }
      elseif ($flags & self::WE_HAVE_A_TWO_BY_TWO) {
        $size += $font->writeInt16($_component->a);
        $size += $font->writeInt16($_component->b);
        $size += $font->writeInt16($_component->c);
        $size += $font->writeInt16($_component->d);
      }
    }

    if($_component->instructions !== null) {
      $size += $font->write($_component->instructions, strlen($_component->instructions));
    }

    return $size;
  }

  public function getSVGContours() {
    $contours = array();

    /** @var \FontLib\Table\Type\glyf $glyph_data */
    $glyph_data = $this->getFont()->getTableObject("glyf");

    /** @var Outline[] $glyphs */
    $glyphs = $glyph_data->data;

    foreach ($this->components as $component) {
      $_glyph = $glyphs[$component->glyphIndex];

      if ($_glyph !== $this) {
        $contours[] = array(
          "contours"  => $_glyph->getSVGContours(),
          "transform" => $component->getMatrix(),
        );
      }
    }

    return $contours;
  }
}