7 changed files with 811 additions and 99 deletions
@ -0,0 +1,711 @@
@@ -0,0 +1,711 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ |
||||
/* Copyright 2012 Mozilla Foundation |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
/* globals error, Stream, GlyphsUnicode, CFFParser, Encodings */ |
||||
|
||||
'use strict'; |
||||
|
||||
var FontRendererFactory = (function FontRendererFactoryClosure() { |
||||
function getLong(data, offset) { |
||||
return (data[offset] << 24) | (data[offset + 1] << 16) | |
||||
(data[offset + 2] << 8) | data[offset + 3]; |
||||
} |
||||
|
||||
function getUshort(data, offset) { |
||||
return (data[offset] << 8) | data[offset + 1]; |
||||
} |
||||
|
||||
function parseCmap(data, start, end) { |
||||
var offset = getUshort(data, start + 2) === 1 ? getLong(data, start + 8) : |
||||
getLong(data, start + 16); |
||||
var format = getUshort(data, start + offset); |
||||
if (format === 4) { |
||||
var length = getUshort(data, start + offset + 2); |
||||
var segCount = getUshort(data, start + offset + 6) >> 1; |
||||
var p = start + offset + 14; |
||||
var ranges = []; |
||||
for (var i = 0; i < segCount; i++, p += 2) { |
||||
ranges[i] = {end: getUshort(data, p)}; |
||||
} |
||||
p += 2; |
||||
for (var i = 0; i < segCount; i++, p += 2) { |
||||
ranges[i].start = getUshort(data, p); |
||||
} |
||||
for (var i = 0; i < segCount; i++, p += 2) { |
||||
ranges[i].idDelta = getUshort(data, p); |
||||
} |
||||
for (var i = 0; i < segCount; i++, p += 2) { |
||||
var idOffset = getUshort(data, p); |
||||
if (idOffset === 0) { |
||||
continue; |
||||
} |
||||
ranges[i].ids = []; |
||||
for (var j = 0, jj = ranges[i].end - ranges[i].start + 1; j < jj; j++) { |
||||
ranges[i].ids[j] = getUshort(data, p + idOffset); |
||||
idOffset += 2; |
||||
} |
||||
} |
||||
return ranges; |
||||
} else if (format === 12) { |
||||
var length = getLong(data, start + offset + 4); |
||||
var groups = getLong(data, start + offset + 12); |
||||
var p = start + offset + 16; |
||||
var ranges = []; |
||||
for (var i = 0; i < groups; i++) { |
||||
ranges.push({ |
||||
start: getLong(data, p), |
||||
end: getLong(data, p + 4), |
||||
idDelta: getLong(data, p + 8) - getLong(data, p) |
||||
}); |
||||
p += 12; |
||||
} |
||||
return ranges; |
||||
} |
||||
error('not supported cmap: ' + format); |
||||
} |
||||
|
||||
function parseCff(data, start, end) { |
||||
var properties = {}; |
||||
var parser = new CFFParser( |
||||
new Stream(data, start, end - start), properties); |
||||
var cff = parser.parse(); |
||||
return { |
||||
glyphs: cff.charStrings.objects, |
||||
subrs: cff.topDict.privateDict && cff.topDict.privateDict.subrsIndex && |
||||
cff.topDict.privateDict.subrsIndex.objects, |
||||
gsubrs: cff.globalSubrIndex && cff.globalSubrIndex.objects |
||||
}; |
||||
} |
||||
|
||||
function parseGlyfTable(glyf, loca, isGlyphLocationsLong) { |
||||
var itemSize, itemDecode; |
||||
if (isGlyphLocationsLong) { |
||||
itemSize = 4; |
||||
itemDecode = function fontItemDecodeLong(data, offset) { |
||||
return (data[offset] << 24) | (data[offset + 1] << 16) | |
||||
(data[offset + 2] << 8) | data[offset + 3]; |
||||
}; |
||||
} else { |
||||
itemSize = 2; |
||||
itemDecode = function fontItemDecode(data, offset) { |
||||
return (data[offset] << 9) | (data[offset + 1] << 1); |
||||
}; |
||||
} |
||||
var glyphs = []; |
||||
var startOffset = itemDecode(loca, 0); |
||||
for (var j = itemSize; j < loca.length; j += itemSize) { |
||||
var endOffset = itemDecode(loca, j); |
||||
glyphs.push(glyf.subarray(startOffset, endOffset)); |
||||
startOffset = endOffset; |
||||
} |
||||
return glyphs; |
||||
} |
||||
|
||||
function lookupCmap(ranges, unicode) { |
||||
var code = unicode.charCodeAt(0); |
||||
var l = 0, r = ranges.length - 1; |
||||
while (l < r) { |
||||
var c = (l + r + 1) >> 1; |
||||
if (code < ranges[c].start) { |
||||
r = c - 1; |
||||
} else { |
||||
l = c; |
||||
} |
||||
} |
||||
if (ranges[l].start <= code && code <= ranges[l].end) { |
||||
return (ranges[l].idDelta + (ranges[l].ids ? |
||||
ranges[l].ids[code - ranges[l].start] : code)) & 0xFFFF; |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
function compileGlyf(code, js, font) { |
||||
function moveTo(x, y) { |
||||
js.push('c.moveTo(' + x + ',' + y + ');'); |
||||
} |
||||
function lineTo(x, y) { |
||||
js.push('c.lineTo(' + x + ',' + y + ');'); |
||||
} |
||||
function quadraticCurveTo(xa, ya, x, y) { |
||||
js.push('c.quadraticCurveTo(' + xa + ',' + ya + ',' + |
||||
x + ',' + y + ');'); |
||||
} |
||||
|
||||
var i = 0; |
||||
var numberOfContours = ((code[i] << 24) | (code[i + 1] << 16)) >> 16; |
||||
var xMin = ((code[i + 2] << 24) | (code[i + 3] << 16)) >> 16; |
||||
var yMin = ((code[i + 4] << 24) | (code[i + 5] << 16)) >> 16; |
||||
var xMax = ((code[i + 6] << 24) | (code[i + 7] << 16)) >> 16; |
||||
var yMax = ((code[i + 8] << 24) | (code[i + 9] << 16)) >> 16; |
||||
i += 10; |
||||
if (numberOfContours < 0) { |
||||
// composite glyph
|
||||
var x = 0, y = 0; |
||||
do { |
||||
var flags = (code[i] << 8) | code[i + 1]; |
||||
var glyphIndex = (code[i + 2] << 8) | code[i + 3]; |
||||
i += 4; |
||||
var arg1, arg2; |
||||
if ((flags & 0x01)) { |
||||
arg1 = ((code[i] << 24) | (code[i + 1] << 16)) >> 16; |
||||
arg2 = ((code[i + 2] << 24) | (code[i + 3] << 16)) >> 16; |
||||
i += 4; |
||||
} else { |
||||
arg1 = code[i++]; arg2 = code[i++]; |
||||
} |
||||
if ((flags & 0x02)) { |
||||
x = arg1; |
||||
y = arg2; |
||||
} else { |
||||
x = 0; y = 0; // TODO "they are points" ?
|
||||
} |
||||
var scaleX = 1, scaleY = 1, scale01 = 0, scale10 = 0; |
||||
if ((flags & 0x08)) { |
||||
scaleX = |
||||
scaleY = ((code[i] << 24) | (code[i + 1] << 16)) / 1073741824; |
||||
i += 2; |
||||
} else if ((flags & 0x40)) { |
||||
scaleX = ((code[i] << 24) | (code[i + 1] << 16)) / 1073741824; |
||||
scaleY = ((code[i + 2] << 24) | (code[i + 3] << 16)) / 1073741824; |
||||
i += 4; |
||||
} else if ((flags & 0x80)) { |
||||
scaleX = ((code[i] << 24) | (code[i + 1] << 16)) / 1073741824; |
||||
scale01 = ((code[i + 2] << 24) | (code[i + 3] << 16)) / 1073741824; |
||||
scale10 = ((code[i + 4] << 24) | (code[i + 5] << 16)) / 1073741824; |
||||
scaleY = ((code[i + 6] << 24) | (code[i + 7] << 16)) / 1073741824; |
||||
i += 8; |
||||
} |
||||
var subglyph = font.glyphs[glyphIndex]; |
||||
if (subglyph) { |
||||
js.push('c.save();'); |
||||
js.push('c.transform(' + scaleX + ',' + scale01 + ',' + |
||||
scale10 + ',' + scaleY + ',' + x + ',' + y + ');'); |
||||
compileGlyf(subglyph, js, font); |
||||
js.push('c.restore();'); |
||||
} |
||||
} while ((flags & 0x20)); |
||||
} else { |
||||
// simple glyph
|
||||
var endPtsOfContours = []; |
||||
for (var j = 0; j < numberOfContours; j++) { |
||||
endPtsOfContours.push((code[i] << 8) | code[i + 1]); |
||||
i += 2; |
||||
} |
||||
var instructionLength = (code[i] << 8) | code[i + 1]; |
||||
i += 2 + instructionLength; // skipping the instructions
|
||||
var numberOfPoints = endPtsOfContours[endPtsOfContours.length - 1] + 1; |
||||
var points = []; |
||||
while (points.length < numberOfPoints) { |
||||
var flags = code[i++], repeat = 1; |
||||
if ((flags & 0x08)) { |
||||
repeat += code[i++]; |
||||
} |
||||
while (repeat-- > 0) { |
||||
points.push({flags: flags}); |
||||
} |
||||
} |
||||
var x = 0, y = 0; |
||||
for (var j = 0; j < numberOfPoints; j++) { |
||||
switch (points[j].flags & 0x12) { |
||||
case 0x00: |
||||
x += ((code[i] << 24) | (code[i + 1] << 16)) >> 16; |
||||
i += 2; |
||||
break; |
||||
case 0x02: |
||||
x -= code[i++]; |
||||
break; |
||||
case 0x12: |
||||
x += code[i++]; |
||||
break; |
||||
} |
||||
points[j].x = x; |
||||
} |
||||
for (var j = 0; j < numberOfPoints; j++) { |
||||
switch (points[j].flags & 0x24) { |
||||
case 0x00: |
||||
y += ((code[i] << 24) | (code[i + 1] << 16)) >> 16; |
||||
i += 2; |
||||
break; |
||||
case 0x04: |
||||
y -= code[i++]; |
||||
break; |
||||
case 0x24: |
||||
y += code[i++]; |
||||
break; |
||||
} |
||||
points[j].y = y; |
||||
} |
||||
|
||||
var startPoint = 0; |
||||
for (var i = 0; i < numberOfContours; i++) { |
||||
var endPoint = endPtsOfContours[i]; |
||||
// contours might have implicit points, which is located in the middle
|
||||
// between two neighboring off-curve points
|
||||
var contour = points.slice(startPoint, endPoint + 1); |
||||
if ((contour[0].flags & 1)) { |
||||
contour.push(contour[0]); // using start point at the contour end
|
||||
} else if ((contour[contour.length - 1].flags & 1)) { |
||||
// first is off-curve point, trying to use one from the end
|
||||
contour.unshift(contour[contour.length - 1]); |
||||
} else { |
||||
// start and end are off-curve points, creating implicit one
|
||||
var p = { |
||||
flags: 1, |
||||
x: (contour[0].x + contour[contour.length - 1].x) / 2, |
||||
y: (contour[0].y + contour[contour.length - 1].y) / 2 |
||||
}; |
||||
contour.unshift(p); |
||||
contour.push(p); |
||||
} |
||||
moveTo(contour[0].x, contour[0].y); |
||||
for (var j = 1, jj = contour.length; j < jj; j++) { |
||||
if ((contour[j].flags & 1)) { |
||||
lineTo(contour[j].x, contour[j].y); |
||||
} else if ((contour[j + 1].flags & 1)){ |
||||
quadraticCurveTo(contour[j].x, contour[j].y, |
||||
contour[j + 1].x, contour[j + 1].y); |
||||
j++; |
||||
} else { |
||||
quadraticCurveTo(contour[j].x, contour[j].y, |
||||
(contour[j].x + contour[j + 1].x) / 2, |
||||
(contour[j].y + contour[j + 1].y) / 2); |
||||
} |
||||
} |
||||
startPoint = endPoint + 1; |
||||
} |
||||
} |
||||
} |
||||
|
||||
function compileCharString(code, js, font) { |
||||
var stack = []; |
||||
var x = 0, y = 0; |
||||
var stems = 0; |
||||
|
||||
function moveTo(x, y) { |
||||
js.push('c.moveTo(' + x + ',' + y + ');'); |
||||
} |
||||
function lineTo(x, y) { |
||||
js.push('c.lineTo(' + x + ',' + y + ');'); |
||||
} |
||||
function bezierCurveTo(x1, y1, x2, y2, x, y) { |
||||
js.push('c.bezierCurveTo(' + x1 + ',' + y1 + ',' + x2 + ',' + y2 + ',' + |
||||
x + ',' + y + ');'); |
||||
} |
||||
|
||||
function parse(code) { |
||||
var i = 0; |
||||
while (i < code.length) { |
||||
var stackClean = false; |
||||
var v = code[i++]; |
||||
switch (v) { |
||||
case 1: // hstem
|
||||
stems += stack.length >> 1; |
||||
stackClean = true; |
||||
break; |
||||
case 3: // vstem
|
||||
stems += stack.length >> 1; |
||||
stackClean = true; |
||||
break; |
||||
case 4: // vmoveto
|
||||
y += stack.pop(); |
||||
moveTo(x, y); |
||||
stackClean = true; |
||||
break; |
||||
case 5: // rlineto
|
||||
while (stack.length > 0) { |
||||
x += stack.shift(); |
||||
y += stack.shift(); |
||||
lineTo(x, y); |
||||
} |
||||
break; |
||||
case 6: // hlineto
|
||||
while (stack.length > 0) { |
||||
x += stack.shift(); |
||||
lineTo(x, y); |
||||
if (stack.length === 0) { |
||||
break; |
||||
} |
||||
y += stack.shift(); |
||||
lineTo(x, y); |
||||
} |
||||
break; |
||||
case 7: // vlineto
|
||||
while (stack.length > 0) { |
||||
y += stack.shift(); |
||||
lineTo(x, y); |
||||
if (stack.length === 0) { |
||||
break; |
||||
} |
||||
x += stack.shift(); |
||||
lineTo(x, y); |
||||
} |
||||
break; |
||||
case 8: // rrcurveto
|
||||
while (stack.length > 0) { |
||||
var xa = x + stack.shift(), ya = y + stack.shift(); |
||||
var xb = xa + stack.shift(), yb = ya + stack.shift(); |
||||
x = xb + stack.shift(); y = yb + stack.shift(); |
||||
bezierCurveTo(xa, ya, xb, yb, x, y); |
||||
} |
||||
break; |
||||
case 10: // callsubr
|
||||
var n = stack.pop() + font.subrsBias; |
||||
var subrCode = font.subrs[n]; |
||||
if (subrCode) { |
||||
parse(subrCode); |
||||
} |
||||
break; |
||||
case 11: // return
|
||||
return; |
||||
case 12: |
||||
v = code[i++]; |
||||
switch (v) { |
||||
case 34: // flex
|
||||
var xa = x + stack.shift(); |
||||
var xb = xa + stack.shift(), y1 = y + stack.shift(); |
||||
x = xb + stack.shift(); |
||||
bezierCurveTo(xa, y, xb, y1, x, y1); |
||||
var xa = x + stack.shift(); |
||||
var xb = xa + stack.shift(); |
||||
x = xb + stack.shift(); |
||||
bezierCurveTo(xa, y1, xb, y, x, y); |
||||
break; |
||||
case 35: // flex
|
||||
var xa = x + stack.shift(), ya = y + stack.shift(); |
||||
var xb = xa + stack.shift(), yb = ya + stack.shift(); |
||||
x = xb + stack.shift(); y = yb + stack.shift(); |
||||
bezierCurveTo(xa, ya, xb, yb, x, y); |
||||
var xa = x + stack.shift(), ya = y + stack.shift(); |
||||
var xb = xa + stack.shift(), yb = ya + stack.shift(); |
||||
x = xb + stack.shift(); y = yb + stack.shift(); |
||||
bezierCurveTo(xa, ya, xb, yb, x, y); |
||||
stack.pop(); // fd
|
||||
break; |
||||
case 36: // hflex1
|
||||
var xa = x + stack.shift(), y1 = y + stack.shift(); |
||||
var xb = xa + stack.shift(), y2 = y1 + stack.shift(); |
||||
x = xb + stack.shift(); |
||||
bezierCurveTo(xa, y1, xb, y2, x, y2); |
||||
var xa = x + stack.shift(); |
||||
var xb = xa + stack.shift(), y3 = y2 + stack.shift(); |
||||
x = xb + stack.shift(); |
||||
bezierCurveTo(xa, y2, xb, y3, x, y); |
||||
break; |
||||
case 37: // flex1
|
||||
var x0 = x, y0 = y; |
||||
var xa = x + stack.shift(), ya = y + stack.shift(); |
||||
var xb = xa + stack.shift(), yb = ya + stack.shift(); |
||||
x = xb + stack.shift(); y = yb + stack.shift(); |
||||
bezierCurveTo(xa, ya, xb, yb, x, y); |
||||
var xa = x + stack.shift(), ya = y + stack.shift(); |
||||
var xb = xa + stack.shift(), yb = ya + stack.shift(); |
||||
x = xb; y = yb; |
||||
if (Math.abs(x - x0) > Math.abs(y - y0)) |
||||
x += stack.shift(); |
||||
else |
||||
y += stack.shift(); |
||||
bezierCurveTo(xa, ya, xb, yb, x, y); |
||||
break; |
||||
default: |
||||
error('unknown operator: 12 ' + v); |
||||
} |
||||
break; |
||||
case 14: // endchar
|
||||
if (stack.length >= 4) { |
||||
var achar = stack.pop(); |
||||
var bchar = stack.pop(); |
||||
y = stack.pop(); |
||||
x = stack.pop(); |
||||
js.push('c.save();'); |
||||
js.push('c.translate('+ x + ',' + y + ');'); |
||||
var gid = lookupCmap(font.cmap, String.fromCharCode( |
||||
font.glyphNameMap[Encodings.StandardEncoding[achar]])); |
||||
compileCharString(font.glyphs[gid], js, font); |
||||
js.push('c.restore();'); |
||||
|
||||
gid = lookupCmap(font.cmap, String.fromCharCode( |
||||
font.glyphNameMap[Encodings.StandardEncoding[bchar]])); |
||||
compileCharString(font.glyphs[gid], js, font); |
||||
} |
||||
return; |
||||
case 18: // hstemhm
|
||||
stems += stack.length >> 1; |
||||
stackClean = true; |
||||
break; |
||||
case 19: // hintmask
|
||||
stems += stack.length >> 1; |
||||
i += (stems + 7) >> 3; |
||||
stackClean = true; |
||||
break; |
||||
case 20: // cntrmask
|
||||
stems += stack.length >> 1; |
||||
i += (stems + 7) >> 3; |
||||
stackClean = true; |
||||
break; |
||||
case 21: // rmoveto
|
||||
y += stack.pop(); |
||||
x += stack.pop(); |
||||
moveTo(x, y); |
||||
stackClean = true; |
||||
break; |
||||
case 22: // hmoveto
|
||||
x += stack.pop(); |
||||
moveTo(x, y); |
||||
stackClean = true; |
||||
break; |
||||
case 23: // vstemhm
|
||||
stems += stack.length >> 1; |
||||
stackClean = true; |
||||
break; |
||||
case 24: // rcurveline
|
||||
while (stack.length > 2) { |
||||
var xa = x + stack.shift(), ya = y + stack.shift(); |
||||
var xb = xa + stack.shift(), yb = ya + stack.shift(); |
||||
x = xb + stack.shift(); y = yb + stack.shift(); |
||||
bezierCurveTo(xa, ya, xb, yb, x, y); |
||||
} |
||||
x += stack.shift(); |
||||
y += stack.shift(); |
||||
lineTo(x, y); |
||||
break; |
||||
case 25: // rlinecurve
|
||||
while (stack.length > 6) { |
||||
x += stack.shift(); |
||||
y += stack.shift(); |
||||
lineTo(x, y); |
||||
} |
||||
var xa = x + stack.shift(), ya = y + stack.shift(); |
||||
var xb = xa + stack.shift(), yb = ya + stack.shift(); |
||||
x = xb + stack.shift(); y = yb + stack.shift(); |
||||
bezierCurveTo(xa, ya, xb, yb, x, y); |
||||
break; |
||||
case 26: // vvcurveto
|
||||
if (stack.length % 2) { |
||||
x += stack.shift(); |
||||
} |
||||
while (stack.length > 0) { |
||||
var xa = x, ya = y + stack.shift(); |
||||
var xb = xa + stack.shift(), yb = ya + stack.shift(); |
||||
x = xb; y = yb + stack.shift(); |
||||
bezierCurveTo(xa, ya, xb, yb, x, y); |
||||
} |
||||
break; |
||||
case 27: // hhcurveto
|
||||
if (stack.length % 2) { |
||||
y += stack.shift(); |
||||
} |
||||
while (stack.length > 0) { |
||||
var xa = x + stack.shift(), ya = y; |
||||
var xb = xa + stack.shift(), yb = ya + stack.shift(); |
||||
x = xb + stack.shift(); y = yb; |
||||
bezierCurveTo(xa, ya, xb, yb, x, y); |
||||
} |
||||
break; |
||||
case 28: |
||||
stack.push(((code[i] << 24) | (code[i + 1] << 16)) >> 16); |
||||
i += 2; |
||||
break; |
||||
case 29: // callgsubr
|
||||
var n = stack.pop() + font.gsubrsBias; |
||||
var subrCode = font.gsubrs[n]; |
||||
if (subrCode) { |
||||
parse(subrCode); |
||||
} |
||||
break; |
||||
case 30: // vhcurveto
|
||||
while (stack.length > 0) { |
||||
var xa = x, ya = y + stack.shift(); |
||||
var xb = xa + stack.shift(), yb = ya + stack.shift(); |
||||
x = xb + stack.shift(); |
||||
y = yb + (stack.length === 1 ? stack.shift() : 0); |
||||
bezierCurveTo(xa, ya, xb, yb, x, y); |
||||
if (stack.length === 0) { |
||||
break; |
||||
} |
||||
|
||||
var xa = x + stack.shift(), ya = y; |
||||
var xb = xa + stack.shift(), yb = ya + stack.shift(); |
||||
y = yb + stack.shift(); |
||||
x = xb + (stack.length === 1 ? stack.shift() : 0); |
||||
bezierCurveTo(xa, ya, xb, yb, x, y); |
||||
} |
||||
break; |
||||
case 31: // hvcurveto
|
||||
while (stack.length > 0) { |
||||
var xa = x + stack.shift(), ya = y; |
||||
var xb = xa + stack.shift(), yb = ya + stack.shift(); |
||||
y = yb + stack.shift(); |
||||
x = xb + (stack.length === 1 ? stack.shift() : 0); |
||||
bezierCurveTo(xa, ya, xb, yb, x, y); |
||||
if (stack.length === 0) { |
||||
break; |
||||
} |
||||
|
||||
var xa = x, ya = y + stack.shift(); |
||||
var xb = xa + stack.shift(), yb = ya + stack.shift(); |
||||
x = xb + stack.shift(); |
||||
y = yb + (stack.length === 1 ? stack.shift() : 0); |
||||
bezierCurveTo(xa, ya, xb, yb, x, y); |
||||
} |
||||
break; |
||||
default: |
||||
if (v < 32) |
||||
error('unknown operator: ' + v); |
||||
if (v < 247) |
||||
stack.push(v - 139); |
||||
else if (v < 251) |
||||
stack.push((v - 247) * 256 + code[i++] + 108); |
||||
else if (v < 255) |
||||
stack.push(-(v - 251) * 256 - code[i++] - 108); |
||||
else { |
||||
stack.push(((code[i] << 24) | (code[i + 1] << 16) | |
||||
(code[i + 2] << 8) | code[i + 3]) / 65536); |
||||
i += 4; |
||||
} |
||||
break; |
||||
} |
||||
if (stackClean) { |
||||
stack.length = 0; |
||||
} |
||||
} |
||||
} |
||||
parse(code); |
||||
} |
||||
|
||||
function TrueTypeCompiled(glyphs, cmap, fontMatrix) { |
||||
this.glyphs = glyphs; |
||||
this.cmap = cmap; |
||||
this.fontMatrix = fontMatrix || [0.000488, 0, 0, 0.000488, 0, 0]; |
||||
|
||||
this.compiledGlyphs = []; |
||||
} |
||||
|
||||
var noop = function () {}; |
||||
|
||||
TrueTypeCompiled.prototype = { |
||||
getPathGenerator: function (unicode) { |
||||
var gid = lookupCmap(this.cmap, unicode); |
||||
var fn = this.compiledGlyphs[gid]; |
||||
if (!fn) { |
||||
this.compiledGlyphs[gid] = fn = this.compileGlyph(this.glyphs[gid]); |
||||
} |
||||
return fn; |
||||
}, |
||||
compileGlyph: function (code) { |
||||
if (!code || code.length === 0 || code[0] === 14) { |
||||
return noop; |
||||
} |
||||
|
||||
var js = []; |
||||
js.push('c.save();'); |
||||
js.push('c.transform(' + this.fontMatrix.join(',') + ');'); |
||||
js.push('c.scale(size, -size);'); |
||||
|
||||
var stack = [], x = 0, y = 0; |
||||
compileGlyf(code, js, this); |
||||
|
||||
js.push('c.restore();'); |
||||
|
||||
/*jshint -W054 */ |
||||
return new Function('c', 'size', js.join('\n')); |
||||
} |
||||
}; |
||||
|
||||
function Type2Compiled(cffInfo, cmap, fontMatrix, glyphNameMap) { |
||||
this.glyphs = cffInfo.glyphs; |
||||
this.gsubrs = cffInfo.gsubrs || []; |
||||
this.subrs = cffInfo.subrs || []; |
||||
this.cmap = cmap; |
||||
this.glyphNameMap = glyphNameMap || GlyphsUnicode; |
||||
this.fontMatrix = fontMatrix || [0.001, 0, 0, 0.001, 0, 0]; |
||||
|
||||
this.compiledGlyphs = []; |
||||
this.gsubrsBias = this.gsubrs.length < 1240 ? 107 : |
||||
this.gsubrs.length < 33900 ? 1131 : 32768; |
||||
this.subrsBias = this.subrs.length < 1240 ? 107 : |
||||
this.subrs.length < 33900 ? 1131 : 32768; |
||||
} |
||||
|
||||
Type2Compiled.prototype = { |
||||
getPathGenerator: function (unicode) { |
||||
var gid = lookupCmap(this.cmap, unicode); |
||||
var fn = this.compiledGlyphs[gid]; |
||||
if (!fn) { |
||||
this.compiledGlyphs[gid] = fn = this.compileGlyph(this.glyphs[gid]); |
||||
} |
||||
return fn; |
||||
}, |
||||
compileGlyph: function (code) { |
||||
if (!code || code.length === 0 || code[0] === 14) { |
||||
return noop; |
||||
} |
||||
|
||||
var js = []; |
||||
js.push('c.save();'); |
||||
js.push('c.transform(' + this.fontMatrix.join(',') + ');'); |
||||
js.push('c.scale(size, -size);'); |
||||
|
||||
var stack = [], x = 0, y = 0; |
||||
compileCharString(code, js, this); |
||||
|
||||
js.push('c.restore();'); |
||||
|
||||
/*jshint -W054 */ |
||||
return new Function('c', 'size', js.join('\n')); |
||||
} |
||||
}; |
||||
|
||||
return { |
||||
create: function FontRendererFactory_create(font) { |
||||
var data = new Uint8Array(font.data); |
||||
var cmap, glyf, loca, cff, indexToLocFormat, unitsPerEm; |
||||
var numTables = getUshort(data, 4); |
||||
for (var i = 0, p = 12; i < numTables; i++, p += 16) { |
||||
var tag = String.fromCharCode.apply(null, data.subarray(p, p + 4)); |
||||
var offset = getLong(data, p + 8); |
||||
var length = getLong(data, p + 12); |
||||
switch (tag) { |
||||
case 'cmap': |
||||
cmap = parseCmap(data, offset, offset + length); |
||||
break; |
||||
case 'glyf': |
||||
glyf = data.subarray(offset, offset + length); |
||||
break; |
||||
case 'loca': |
||||
loca = data.subarray(offset, offset + length); |
||||
break; |
||||
case 'head': |
||||
unitsPerEm = getUshort(data, offset + 18); |
||||
indexToLocFormat = getUshort(data, offset + 50); |
||||
break; |
||||
case 'CFF ': |
||||
cff = parseCff(data, offset, offset + length); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
if (glyf) { |
||||
var fontMatrix = !unitsPerEm ? font.fontMatrix : |
||||
[1 / unitsPerEm, 0, 0, 1 / unitsPerEm, 0, 0]; |
||||
return new TrueTypeCompiled( |
||||
parseGlyfTable(glyf, loca, indexToLocFormat), cmap, fontMatrix); |
||||
} else { |
||||
return new Type2Compiled(cff, cmap, font.fontMatrix, font.glyphNameMap); |
||||
} |
||||
} |
||||
}; |
||||
})(); |
||||
|
Loading…
Reference in new issue