/** * @return string */ public function toJSON() { $font = $this->document; $fontJSON = array('w' => (int) $font['horiz-adv-x'], 'face' => array(), 'glyphs' => array()); $face = $font->xpath('font-face'); if (empty($face)) { return null; } foreach ($face[0]->attributes() as $key => $val) { $fontJSON['face'][$key] = (string) $val; } foreach ($font->xpath('glyph') as $glyph) { if (!isset($glyph['unicode'])) { continue; } if (mb_strlen($glyph['unicode'], 'utf-8') > 1) { // it's a ligature, for now we'll just ignore it continue; } $data = array(); if (isset($glyph['d'])) { $data['d'] = substr(VMLPath::fromSVG((string) $glyph['d']), 1, -2); // skip m and xe } if (isset($glyph['horiz-adv-x'])) { $data['w'] = (int) $glyph['horiz-adv-x']; } $fontJSON['glyphs'][(string) $glyph['unicode']] = $data; } return json_encode($fontJSON); }
/** * @return string */ public function toJavaScript() { $font = $this->document; $fontJSON = array('w' => (int) $font['horiz-adv-x'], 'face' => array(), 'glyphs' => array(' ' => new stdClass())); $face = $font->xpath('font-face'); if (empty($face)) { return null; } foreach ($face[0]->attributes() as $key => $val) { $fontJSON['face'][$key] = $this->getSanitizedFaceValue($key, (string) $val); } $nameIndex = array(); $charIndex = array(); foreach ($font->xpath('glyph') as $glyph) { if (!isset($glyph['unicode'])) { continue; } if (mb_strlen($glyph['unicode'], 'utf-8') > 1) { // it's a ligature, for now we'll just ignore it continue; } $data = new stdClass(); if (isset($glyph['d'])) { $data->d = substr(VMLPath::fromSVG((string) $glyph['d']), 1, -2); // skip m and xe } if (isset($glyph['horiz-adv-x'])) { $data->w = (int) $glyph['horiz-adv-x']; } $char = (string) $glyph['unicode']; if (isset($glyph['glyph-name'])) { foreach (explode(',', (string) $glyph['glyph-name']) as $glyphName) { $nameIndex[$glyphName] = $char; $charIndex[$char] = $data; } } $fontJSON['glyphs'][$char] = $data; } $options = $this->container->getOptions(); $emSize = (int) $fontJSON['face']['units-per-em']; // for some extremely weird reason FontForge sometimes pumps out // astronomical kerning values. // @todo figure out what's really wrong $kerningLimit = $emSize * 2; if ($options['kerning']) { foreach ($font->xpath('hkern') as $hkern) { $k = (int) $hkern['k']; if (abs($k) > $kerningLimit) { continue; } $firstSet = array(); $secondSet = array(); if (isset($hkern['u1'])) { $firstSet = self::getMatchingCharsFromUnicodeRange((string) $hkern['u1'], $charIndex); } if (isset($hkern['g1'])) { $firstSet = array_merge($firstSet, self::getMatchingCharsFromGlyphNames((string) $hkern['g1'], $nameIndex)); } if (isset($hkern['u2'])) { $secondSet = self::getMatchingCharsFromUnicodeRange((string) $hkern['u2'], $charIndex); } if (isset($hkern['g2'])) { $secondSet = array_merge($secondSet, self::getMatchingCharsFromGlyphNames((string) $hkern['g2'], $nameIndex)); } if (!empty($secondSet)) { foreach ($firstSet as $firstGlyph) { foreach ($secondSet as $secondGlyph) { $glyph = $fontJSON['glyphs'][$firstGlyph]; if (!isset($glyph->k)) { $glyph->k = array(); } $glyph->k[$secondGlyph] = $k; } } } } } $nbsp = utf8_encode(chr(0xa0)); if (!isset($fontJSON['glyphs'][$nbsp]) && isset($fontJSON['glyphs'][' '])) { $fontJSON['glyphs'][$nbsp] = $fontJSON['glyphs'][' ']; } return self::processFont($fontJSON, $options); }
/** * @param string $path * @return VMLPath */ public static function fromSVG($path) { $vml = new VMLPath(); $matches = array(); if (!preg_match_all('/([a-zA-Z])([0-9. \\-,]*)/', $path, $matches, PREG_SET_ORDER)) { return $vml; } $at = (object) array('x' => 0, 'y' => 0); $cp = (object) array('x' => 0, 'y' => 0); $previous = null; $zm = false; for (; $set = current($matches); next($matches)) { list($cmd, $coords) = array($set[1], array_map('floatval', preg_split('/(?:,|\\s+)/', trim($set[2])))); switch ($cmd) { case 'z': case 'Z': if (strcasecmp($previous, 'm') === 0) { $vml->pop(); } if ($zm) { $vml->pop(); } $vml->closePath(); break; case 'M': if (strcasecmp($previous, 'm') === 0) { $vml->pop(); } $vml->moveTo($at->x = $coords[0], $at->y = $coords[1]); break; case 'L': $vml->lineTo($at->x = $coords[0], $at->y = $coords[1]); break; case 'l': $vml->lineTo($at->x += $coords[0], $at->y += $coords[1]); break; case 'H': $vml->lineTo($at->x = $coords[0], $at->y); break; case 'h': $vml->lineTo($at->x += $coords[0], $at->y); break; case 'V': $vml->lineTo($at->x, $at->y = $coords[0]); break; case 'v': $vml->lineTo($at->x, $at->y += $coords[0]); break; case 'C': $vml->bezierCurveTo($coords[0], $coords[1], $cp->x = $coords[2], $cp->y = $coords[3], $at->x = $coords[4], $at->y = $coords[5]); break; case 'c': $vml->bezierCurveTo($at->x + $coords[0], $at->y + $coords[1], $cp->x = $at->x + $coords[2], $cp->y = $at->y + $coords[3], $at->x += $coords[4], $at->y += $coords[5]); break; case 'S': if (!$previous || !preg_match('/^[CcSs]$/', $previous)) { $cp->x = $at->x; $cp->y = $at->y; } $vml->bezierCurveTo($at->x + ($at->x - $cp->x), $at->y + ($at->y - $cp->y), $cp->x = $coords[0], $cp->y = $coords[1], $at->x = $coords[2], $at->y = $coords[3]); break; case 's': if (!$previous || !preg_match('/^[CcSs]$/', $previous)) { $cp->x = $at->x; $cp->y = $at->y; } $vml->bezierCurveTo($at->x + ($at->x - $cp->x), $at->y + ($at->y - $cp->y), $cp->x = $at->x + $coords[0], $cp->y = $at->y + $coords[1], $at->x += $coords[2], $at->y += $coords[3]); break; case 'Q': $vml->quadraticCurveTo($cp->x = $coords[0], $cp->y = $coords[1], $at->x = $coords[2], $at->y = $coords[3]); break; case 'q': $vml->quadraticCurveTo($cp->x = $at->x + $coords[0], $cp->y = $at->y + $coords[1], $at->x += $coords[2], $at->y += $coords[3]); break; case 'T': if (!$previous || !preg_match('/^[QqTt]$/', $previous)) { $cp->x = $at->x; $cp->y = $at->y; } $vml->quadraticCurveTo($cp->x = $at->x + ($at->x - $cp->x), $cp->y = $at->y + ($at->y - $cp->y), $at->x = $coords[0], $at->y = $coords[1]); break; case 't': if (!$previous || !preg_match('/^[QqTt]$/', $previous)) { $cp->x = $at->x; $cp->y = $at->y; } $vml->quadraticCurveTo($cp->x = $at->x + ($at->x - $cp->x), $cp->y = $at->y + ($at->y - $cp->y), $at->x += $coords[0], $at->y += $coords[1]); break; case 'A': case 'a': break; } $zm = strcasecmp($cmd, 'm') === 0 && strcasecmp($previous, 'z') === 0; $previous = $cmd; } $vml->endPath(); return $vml; }
/** * @param XMLReader $reader * @return SVGFont */ public function readFrom(XMLReader $reader) { $currentGlyphs = array(' ' => new stdClass()); $currentFace = array('font-family' => '', 'font-weight' => '', 'font-stretch' => '', 'font-style' => 'normal', 'units-per-em' => '', 'panose-1' => '', 'ascent' => '', 'descent' => '', 'bbox' => '', 'underline-thickness' => '', 'underline-position' => '', 'unicode-range' => ''); $nameIndex = array(); $charIndex = array(); do { if ($reader->nodeType == XMLReader::END_ELEMENT) { if ($reader->name === 'font') { break; } continue; } if ($reader->nodeType != XMLReader::ELEMENT) { continue; } switch ($reader->name) { case 'font': if (isset($this->id)) { // shouldn't happen but hey, who knows break 2; } $this->id = $reader->getAttribute('id'); $this->horizAdvX = (int) $reader->getAttribute('horiz-adv-x'); break; case 'font-face': foreach ($currentFace as $key => $defaultValue) { $actualValue = $this->sanitizeFaceValue($key, $reader->getAttribute($key)); $currentFace[$key] = $actualValue == null ? $defaultValue : $actualValue; } break; case 'glyph': $glyphChar = $reader->getAttribute('unicode'); if (empty($glyphChar) && $glyphChar !== '0') { break; } if (mb_strlen($glyphChar, 'utf-8') > 1) { // it's a ligature, for now we'll just ignore it break; } $glyphData = new stdClass(); $glyphName = $reader->getAttribute('glyph-name'); $glyphPath = $reader->getAttribute('d'); $glyphHorizAdvX = $reader->getAttribute('horiz-adv-x'); if ($glyphPath != null) { $glyphData->d = substr(VMLPath::fromSVG($glyphPath), 1, -2); // skip m and xe } if ($glyphHorizAdvX != null) { $glyphData->w = (int) $glyphHorizAdvX; } if ($glyphName != null) { foreach (explode(',', $glyphName) as $glyphNameAlt) { $nameIndex[$glyphNameAlt] = $glyphChar; $charIndex[$glyphChar] = $glyphData; } } $currentGlyphs[$glyphChar] = $glyphData; break; case 'hkern': if (empty($this->options['kerning'])) { break; } $kernK = (int) $reader->getAttribute('k'); $kernU1 = $reader->getAttribute('u1'); $kernG1 = $reader->getAttribute('g1'); $kernU2 = $reader->getAttribute('u2'); $kernG2 = $reader->getAttribute('g2'); $firstSet = array(); $secondSet = array(); if ($kernU1 != null) { $firstSet = self::getMatchingCharsFromUnicodeRange($kernU1, $charIndex); } if ($kernG1 != null) { $firstSet = array_merge($firstSet, self::getMatchingCharsFromGlyphNames($kernG1, $nameIndex)); } if ($kernU2 != null) { $secondSet = self::getMatchingCharsFromUnicodeRange($kernU2, $charIndex); } if ($kernG2 != null) { $secondSet = array_merge($secondSet, self::getMatchingCharsFromGlyphNames($kernG2, $nameIndex)); } if (!empty($secondSet)) { foreach ($firstSet as $firstGlyph) { foreach ($secondSet as $secondGlyph) { $glyph = $currentGlyphs[$firstGlyph]; if (!isset($glyph->k)) { $glyph->k = array(); } $glyph->k[$secondGlyph] = $kernK; } } } break; } } while ($reader->read()); $nbsp = utf8_encode(chr(0xa0)); if (!isset($currentGlyphs[$nbsp]) && isset($currentGlyphs[' '])) { $currentGlyphs[$nbsp] = $currentGlyphs[' ']; } $this->face = $currentFace; $this->glyphs = $currentGlyphs; return $this; }