Skip to content

Commit fedc4a5

Browse files
committed
Fonts can now be created "from scratch"
Full support for glyph creation from SVG paths
1 parent 1de3778 commit fedc4a5

11 files changed

+190
-113
lines changed

classes/font_binary_stream.cls.php

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ class Font_Binary_Stream {
5151
const modeRead = "rb";
5252
const modeWrite = "wb";
5353
const modeReadWrite = "rb+";
54+
55+
static function backtrace(){
56+
var_dump(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS));
57+
}
5458

5559
/**
5660
* Open a font file in read mode
@@ -177,13 +181,20 @@ public function writeInt8($data) {
177181

178182
public function readUInt16() {
179183
$a = unpack("nn", $this->read(2));
180-
//if ($a == null) var_dump(debug_backtrace(false));
181184
return $a["n"];
182185
}
186+
187+
public function readUFWord(){
188+
return $this->readUInt16();
189+
}
183190

184191
public function writeUInt16($data) {
185192
return $this->write(pack("n", $data), 2);
186193
}
194+
195+
public function writeUFWord($data){
196+
return $this->writeUInt16($data);
197+
}
187198

188199
public function readInt16() {
189200
$v = $this->readUInt16();
@@ -194,6 +205,10 @@ public function readInt16() {
194205

195206
return $v;
196207
}
208+
209+
public function readFWord(){
210+
return $this->readInt16();
211+
}
197212

198213
public function writeInt16($data) {
199214
if ($data < 0) {
@@ -202,6 +217,10 @@ public function writeInt16($data) {
202217

203218
return $this->writeUInt16($data);
204219
}
220+
221+
public function writeFWord($data){
222+
return $this->writeInt16($data);
223+
}
205224

206225
public function readUInt32() {
207226
$a = unpack("NN", $this->read(4));

classes/font_glyph_outline.cls.php

Lines changed: 44 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,16 @@
1616
* @package php-font-lib
1717
*/
1818
class Font_Glyph_Outline extends Font_Binary_Stream {
19-
const ARG_1_AND_2_ARE_WORDS = 1;
20-
const ARGS_ARE_XY_VALUES = 2;
21-
const ROUND_XY_TO_GRID = 4;
22-
const WE_HAVE_A_SCALE = 8;
23-
const MORE_COMPONENTS = 32;
24-
const WE_HAVE_AN_X_AND_Y_SCALE = 64;
25-
const WE_HAVE_A_TWO_BY_TWO = 128;
26-
const WE_HAVE_INSTRUCTIONS = 256;
27-
const USE_MY_METRICS = 512;
28-
const OVERLAP_COMPOUND = 1024;
19+
const ARG_1_AND_2_ARE_WORDS = 0x0001;
20+
const ARGS_ARE_XY_VALUES = 0x0002;
21+
const ROUND_XY_TO_GRID = 0x0004;
22+
const WE_HAVE_A_SCALE = 0x0008;
23+
const MORE_COMPONENTS = 0x0020;
24+
const WE_HAVE_AN_X_AND_Y_SCALE = 0x0040;
25+
const WE_HAVE_A_TWO_BY_TWO = 0x0080;
26+
const WE_HAVE_INSTRUCTIONS = 0x0100;
27+
const USE_MY_METRICS = 0x0200;
28+
const OVERLAP_COMPOUND = 0x0400;
2929

3030
/**
3131
* @var Font_Table_glyf
@@ -35,18 +35,36 @@ class Font_Glyph_Outline extends Font_Binary_Stream {
3535
protected $offset;
3636
protected $size;
3737

38-
protected $data;
38+
// Data
39+
public $numberOfContours;
40+
public $xMin;
41+
public $yMin;
42+
public $xMax;
43+
public $yMax;
3944

45+
public $raw;
46+
47+
/**
48+
* @return Font_Glyph_Outline
49+
*/
4050
static function init(Font_Table_glyf $table, $offset, $size) {
4151
$font = $table->getFont();
4252
$font->seek($offset);
4353

54+
/**
55+
* @var Font_Glyph_Outline
56+
*/
57+
$glyph;
58+
4459
if ($font->readInt16() > -1) {
45-
return new Font_Glyph_Outline_Simple($table, $offset, $size);
60+
$glyph = new Font_Glyph_Outline_Simple($table, $offset, $size);
4661
}
4762
else {
48-
return new Font_Glyph_Outline_Composite($table, $offset, $size);
63+
$glyph = new Font_Glyph_Outline_Composite($table, $offset, $size);
4964
}
65+
66+
$glyph->parse();
67+
return $glyph;
5068
}
5169

5270
/**
@@ -56,14 +74,6 @@ function getFont() {
5674
return $this->table->getFont();
5775
}
5876

59-
function getGlyphData(){
60-
if (empty($this->data)) {
61-
$this->parse();
62-
}
63-
64-
return $this->data;
65-
}
66-
6777
function __construct(Font_Table_glyf $table, $offset = null, $size = null) {
6878
$this->table = $table;
6979
$this->offset = $offset;
@@ -78,21 +88,22 @@ function parse() {
7888
return;
7989
}
8090

81-
$data = $font->unpack(array(
82-
"numberOfContours" => self::int16,
83-
"xMin" => self::FWord,
84-
"yMin" => self::FWord,
85-
"xMax" => self::FWord,
86-
"yMax" => self::FWord,
87-
));
88-
89-
//$data["outline"] = $font->read($this->size - 10);
90-
91-
return $this->data = $data;
91+
$this->raw = $font->read($this->size);
9292
}
9393

94-
function encode(){
94+
function parseData(){
9595
$font = $this->getFont();
9696
$font->seek($this->offset);
97+
98+
$this->numberOfContours = $font->readInt16();
99+
$this->xMin = $font->readFWord();
100+
$this->yMin = $font->readFWord();
101+
$this->xMax = $font->readFWord();
102+
$this->yMax = $font->readFWord();
103+
}
104+
105+
function encode(){
106+
$font = $this->getFont();
107+
return $font->write($this->raw, strlen($this->raw));
97108
}
98109
}

classes/font_glyph_outline_composite.cls.php

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,15 @@
1313
* @package php-font-lib
1414
*/
1515
class Font_Glyph_Outline_Composite extends Font_Glyph_Outline {
16-
function parse(){
17-
$data = parent::parse();
18-
$font = $this->getFont();
16+
public $flags;
17+
public $glyphIndex;
18+
19+
function parseData(){
20+
parent::parseData();
1921

20-
$data["flags"] = $font->readUInt16();
21-
$data["glyphIndex"] = $font->readUInt16();
22+
$font = $this->getFont();
2223

23-
$this->table = null;
24-
return $this->data = $data;
24+
$this->flags = $font->readUInt16();
25+
$this->glyphIndex = $font->readUInt16();
2526
}
2627
}

classes/font_glyph_outline_simple.cls.php

Lines changed: 47 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -20,27 +20,30 @@ class Font_Glyph_Outline_Simple extends Font_Glyph_Outline {
2020
const THIS_X_IS_SAME = 0x10;
2121
const THIS_Y_IS_SAME = 0x20;
2222

23-
function parse(){
24-
$data = parent::parse();
23+
public $instructions;
24+
public $points;
25+
26+
function parseData(){
27+
parent::parseData();
2528

2629
if (!$this->size) {
2730
return;
2831
}
2932

3033
$font = $this->getFont();
3134

32-
$noc = $data["numberOfContours"];
33-
$data["endPtsOfContours"] = $font->r(array(self::uint16, $noc));
35+
$noc = $this->numberOfContours;
3436

3537
if ($noc == 0) {
36-
$this->table = null;
3738
return;
3839
}
3940

41+
$endPtsOfContours = $font->r(array(self::uint16, $noc));
42+
4043
$instructionLength = $font->readUInt16();
41-
$data["instructions"] = $font->r(array(self::uint8, $instructionLength));
44+
$this->instructions = $font->r(array(self::uint8, $instructionLength));
4245

43-
$count = $data["endPtsOfContours"][$noc-1] + 1;
46+
$count = $endPtsOfContours[$noc-1] + 1;
4447

4548
// Flags
4649
$flags = array();
@@ -61,7 +64,7 @@ function parse(){
6164
$points = array();
6265
foreach ($flags as $i => $flag) {
6366
$points[$i]["onCurve"] = $flag & self::ON_CURVE;
64-
$points[$i]["endOfContour"] = in_array($i, $data["endPtsOfContours"]);
67+
$points[$i]["endOfContour"] = in_array($i, $endPtsOfContours);
6568
}
6669

6770
// X Coords
@@ -108,10 +111,7 @@ function parse(){
108111
$points[$i]["y"] = $y;
109112
}
110113

111-
$data["points"] = $points;
112-
113-
$this->table = null;
114-
return $this->data = $data;
114+
$this->points = $points;
115115
}
116116

117117
public function splitSVGPath($path) {
@@ -130,14 +130,12 @@ public function makePoints($path) {
130130
switch($path[$i]) {
131131
// moveTo
132132
case "M":
133-
//if ($i == 0) {
134-
$points[] = array(
135-
"onCurve" => true,
136-
"x" => $path[++$i],
137-
"y" => $path[++$i],
138-
"endOfContour" => ($path[$i] === "z"),
139-
);
140-
//}
133+
$points[] = array(
134+
"onCurve" => true,
135+
"x" => $path[++$i],
136+
"y" => $path[++$i],
137+
"endOfContour" => false,
138+
);
141139
break;
142140

143141
// lineTo
@@ -146,7 +144,7 @@ public function makePoints($path) {
146144
"onCurve" => true,
147145
"x" => $path[++$i],
148146
"y" => $path[++$i],
149-
"endOfContour" => ($path[$i] === "z"),
147+
"endOfContour" => false,
150148
);
151149
break;
152150

@@ -162,13 +160,15 @@ public function makePoints($path) {
162160
"onCurve" => true,
163161
"x" => $path[++$i],
164162
"y" => $path[++$i],
165-
"endOfContour" => ($path[$i] === "z"),
163+
"endOfContour" => false,
166164
);
167165
break;
168166

169167
// closePath
170-
default:
171168
case "z":
169+
$points[count($points)-1]["endOfContour"] = true;
170+
171+
default:
172172
$i++;
173173
break;
174174
}
@@ -178,9 +178,11 @@ public function makePoints($path) {
178178
}
179179

180180
function encode(){
181-
parent::encode();
181+
if (empty($this->points)) {
182+
return parent::encode();
183+
}
182184

183-
return $this->encodePoints($this->data["points"]);
185+
return $this->size = $this->encodePoints($this->points);
184186
}
185187

186188
public function encodePoints($points) {
@@ -189,8 +191,10 @@ public function encodePoints($points) {
189191
$coords_x = array();
190192
$coords_y = array();
191193

192-
$last_x = 10e10;
193-
$last_y = 10e10;
194+
$last_x = 0;
195+
$last_y = 0;
196+
$xMin = $yMin = 0xFFFF;
197+
$xMax = $yMax = -0xFFFF;
194198
foreach($points as $i => $point) {
195199
$flag = 0;
196200
if ($point["onCurve"]) {
@@ -206,15 +210,21 @@ public function encodePoints($points) {
206210
$flag |= self::THIS_X_IS_SAME;
207211
}
208212
else {
209-
$coords_x[] = intval($point["x"]); // int16
213+
$x = intval($point["x"]);
214+
$xMin = min($x, $xMin);
215+
$xMax = max($x, $xMax);
216+
$coords_x[] = $x-$last_x; // int16
210217
}
211218

212219
// Simplified, we could do some optimizations
213220
if ($point["y"] == $last_y) {
214221
$flag |= self::THIS_Y_IS_SAME;
215222
}
216223
else {
217-
$coords_y[] = intval($point["y"]); // int16
224+
$y = intval($point["y"]);
225+
$yMin = min($y, $yMin);
226+
$yMax = max($y, $yMax);
227+
$coords_y[] = $y-$last_y; // int16
218228
}
219229

220230
$flags[] = $flag;
@@ -226,24 +236,25 @@ public function encodePoints($points) {
226236

227237
$l = 0;
228238
$l += $font->writeInt16(count($endPtsOfContours)); // endPtsOfContours
229-
$l += $font->writeInt16(min($coords_x)); // xMin
230-
$l += $font->writeInt16(min($coords_y)); // yMin
231-
$l += $font->writeInt16(max($coords_x)); // xMax
232-
$l += $font->writeInt16(max($coords_y)); // yMax
239+
$l += $font->writeFWord(isset($this->xMin) ? $this->xMin : $xMin); // xMin
240+
$l += $font->writeFWord(isset($this->yMin) ? $this->yMin : $yMin); // yMin
241+
$l += $font->writeFWord(isset($this->xMax) ? $this->xMax : $xMax); // xMax
242+
$l += $font->writeFWord(isset($this->yMax) ? $this->yMax : $yMax); // yMax
243+
244+
// Simple glyf
233245
$l += $font->w(array(self::uint16, count($endPtsOfContours)), $endPtsOfContours); // endPtsOfContours
234-
$l += $font->writeInt16(0); // instructionLength
246+
$l += $font->writeUInt16(0); // instructionLength
235247
$l += $font->w(array(self::uint8, count($flags)), $flags); // flags
236248
$l += $font->w(array(self::int16, count($coords_x)), $coords_x); // xCoordinates
237249
$l += $font->w(array(self::int16, count($coords_y)), $coords_y); // yCoordinates
238-
239250
return $l;
240251
}
241252

242253
public function getSVGContours($points = null){
243254
$path = "";
244255

245256
if (!$points) {
246-
$points = $this->data["points"];
257+
$points = $this->points;
247258
}
248259

249260
$length = count($points);

0 commit comments

Comments
 (0)