Skip to content

Commit

Permalink
Added support for kerning. Full backwards-compatibility.
Browse files Browse the repository at this point in the history
  • Loading branch information
sorccu committed May 16, 2009
1 parent 7735962 commit f2f76b1
Show file tree
Hide file tree
Showing 4 changed files with 273 additions and 32 deletions.
8 changes: 2 additions & 6 deletions generate/lib/Cufon.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

require_once dirname(__FILE__) . DIRECTORY_SEPARATOR . 'FontForgeScript.php';
require_once dirname(__FILE__) . DIRECTORY_SEPARATOR . 'SVGFontContainer.php';
require_once dirname(__FILE__) . DIRECTORY_SEPARATOR . 'UnicodeRange.php';

class Cufon {

Expand Down Expand Up @@ -73,11 +74,7 @@ public static function generate($file, array $options)

foreach ($glyphs as $glyph)
{
// http://www.php.net/manual/en/function.ord.php#68914

$cp = unpack('N', mb_convert_encoding($glyph, 'UCS-4BE', 'UTF-8'));

$script->selectUnicode($cp[1]);
$script->selectUnicode(UnicodeRange::getCodePoint($glyph));
}
}

Expand All @@ -91,7 +88,6 @@ public static function generate($file, array $options)
$script->scaleToEm($options['emSize']);
}

$script->removeAllKerns();
$script->selectAll();
$script->verticalFlip(0);

Expand Down
126 changes: 124 additions & 2 deletions generate/lib/SVGFont.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

require_once dirname(__FILE__) . DIRECTORY_SEPARATOR . 'JSEncoder.php';
require_once dirname(__FILE__) . DIRECTORY_SEPARATOR . 'SVGFontContainer.php';
require_once dirname(__FILE__) . DIRECTORY_SEPARATOR . 'UnicodeRange.php';
require_once dirname(__FILE__) . DIRECTORY_SEPARATOR . 'VMLPath.php';

class SVGFont {
Expand Down Expand Up @@ -121,6 +122,9 @@ public function toJavaScript()
$fontJSON['face'][$key] = $this->getSanitizedFaceValue($key, (string) $val);
}

$nameIndex = array();
$charIndex = array();

foreach ($font->xpath('glyph') as $glyph)
{
if (!isset($glyph['unicode']))
Expand All @@ -147,7 +151,71 @@ public function toJavaScript()
$data->w = (int) $glyph['horiz-adv-x'];
}

$fontJSON['glyphs'][(string) $glyph['unicode']] = $data;
$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();

if ($options['kerning'])
{
$kerning = array();

foreach ($font->xpath('hkern') as $hkern)
{
$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] = (int) $hkern['k'];
}
}
}
}

$fontJSON['kerning'] = (object) $kerning;
}

$nbsp = utf8_encode(chr(0xa0));
Expand All @@ -157,7 +225,7 @@ public function toJavaScript()
$fontJSON['glyphs'][$nbsp] = $fontJSON['glyphs'][' '];
}

return self::processFont($fontJSON, $this->container->getOptions());
return self::processFont($fontJSON, $options);
}

/**
Expand Down Expand Up @@ -205,6 +273,60 @@ private static function processFont($data, $options)
json_encode($data));
}

/**
* @param string $group
* @param array $nameIndex
* @return array
*/
private static function getMatchingCharsFromGlyphNames($group, $nameIndex)
{
$matches = array();

foreach (explode(',', $group) as $g)
{
if (isset($nameIndex[$g]))
{
$matches[] = $nameIndex[$g];
}
}

return $matches;
}

/**
* @param string $unicodeRange
* @param array $charIndex
* @return array
*/
private static function getMatchingCharsFromUnicodeRange($unicodeRange, $charIndex)
{
$matches = array();

$range = new UnicodeRange($unicodeRange);

if ($range->isSimple())
{
if ($charIndex[$unicodeRange])
{
$matches[] = $unicodeRange;
}
}
else
{
reset($charIndex);

while (list($char) = each($charIndex))
{
if ($range->contains($char))
{
$matches[] = $char;
}
}
}

return $matches;
}

/**
* @param mixed $a
* @param mixed $b
Expand Down
110 changes: 110 additions & 0 deletions generate/lib/UnicodeRange.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<?php

class UnicodeRange {

/**
* @see http://www.php.net/manual/en/function.ord.php#68914
*
* @param string $char
* @return int
*/
public static function getCodePoint($char)
{
$cp = unpack('N', mb_convert_encoding($char, 'UCS-4BE', 'UTF-8'));

return $cp[1];
}

/**
* @var array
*/
private $ranges = array();

/**
* @param string $range
* @return void
*/
public function __construct($range)
{
if ($range === ',')
{
$this->ranges[] = self::solve($range);
}
else
{
foreach (explode(',', $range) as $part)
{
$this->ranges[] = self::solve($part);
}
}
}

/**
* @param string $char
* @return boolean
*/
public function contains($char)
{
$cp = self::getCodePoint($char);

foreach ($this->ranges as $range)
{
if ($cp >= $range[0] && $cp <= $range[1])
{
return true;
}
}

return false;
}

/**
* @return boolean
*/
public function isSimple()
{
return count($this->ranges) == 1 && $this->ranges[0][2];
}

/**
* @param string $range
* @return array
*/
private static function solve($range)
{
if ($range === '-')
{
$start = ord($range);

return array($start, $start, true);
}

if (strpos($range, 'U+') !== 0)
{
$start = self::getCodePoint($range);

return array($start, $start, true);
}

$stops = explode('-', $range);

$from = substr(reset($stops), 2);

if (strpos($from, '?') !== false)
{
return array(hexdec($from), hexdec(str_replace('?', 'F', $from)), false);
}

$from = hexdec($from);

if (count($stops) < 2)
{
return array($from, $from, false);
}

$to = hexdec(substr(end($stops), 2));

return array($from, $to);
}

}
Loading

0 comments on commit f2f76b1

Please sign in to comment.