pdf.js
Version:
A PDF generation library for Node.js
202 lines (165 loc) • 6.37 kB
text/coffeescript
###
PDFFont - embeds fonts in PDF documents
By Devon Govett
###
TTFFont = require './font/ttf'
AFMFont = require './font/afm'
Subset = require './font/subset'
class PDFFont
constructor: ( , , , ) ->
if in
()
else if /\.(ttf|ttc)$/i.test
= TTFFont.open ,
= new Subset
()
else if /\.dfont$/i.test
= TTFFont.fromDFont ,
= new Subset
()
else
throw new Error 'Not a supported font format or standard PDF font.'
use: (characters) ->
?.use characters
embed: ->
() unless
encode: (text) ->
?.encodeText(text) or text
registerTTF: ->
= 1000.0 / .head.unitsPerEm
= (Math.round e * for e in .bbox)
= 0 # not sure how to compute this for true-type fonts...
if .post.exists
raw = .post.italic_angle
hi = raw >> 16
low = raw & 0xFF
hi = -((hi ^ 0xFFFF) + 1) if hi & 0x8000 isnt 0
= +"#{hi}.#{low}"
else
= 0
= Math.round .ascender *
= Math.round .decender *
= Math.round .lineGap *
= ( .os2.exists and .os2.capHeight) or
= ( .os2.exists and .os2.xHeight) or 0
= ( .os2.exists and .os2.familyClass or 0) >> 8
= in [1,2,3,4,5,7]
= is 10
= 0
|= 1 << 0 if .post.isFixedPitch
|= 1 << 1 if
|= 1 << 3 if
|= 1 << 6 if isnt 0
|= 1 << 5 # assume the font is nonsymbolic...
= .cmap.unicode
throw new Error 'No unicode cmap for font' if not
= .hmtx
= (Math.round .widths[gid] * for i, gid of .codeMap when i >= 32)
# Create a placeholder reference to be filled in embedTTF.
= .ref
Type: 'Font'
Subtype: 'TrueType'
embedTTF: ->
data = .encode()
= .ref
Length: data.length
Length1: data.length
.add data
= .ref
Type: 'FontDescriptor'
FontName: .postscriptName
FontFile2:
FontBBox:
Flags:
StemV:
ItalicAngle:
Ascent:
Descent:
CapHeight:
XHeight:
firstChar = +Object.keys( .cmap)[0]
charWidths = for code, glyph of .cmap
Math.round .hmtx.forGlyph(glyph).advance *
cmap = .ref()
cmap.add toUnicodeCmap( .subset)
cmap.finalize(true) # compress it
ref =
Type: 'Font'
BaseFont: .postscriptName
Subtype: 'TrueType'
FontDescriptor:
FirstChar: firstChar
LastChar: firstChar + charWidths.length - 1
Widths: .ref charWidths
Encoding: 'MacRomanEncoding'
ToUnicode: cmap
for key, val of ref
.data[key] = val
toUnicodeCmap = (map) ->
unicodeMap = '''
/CIDInit /ProcSet findresource begin
12 dict begin
begincmap
/CIDSystemInfo <<
/Registry (Adobe)
/Ordering (UCS)
/Supplement 0
>> def
/CMapName /Adobe-Identity-UCS def
/CMapType 2 def
1 begincodespacerange
<00><ff>
endcodespacerange
'''
codes = Object.keys(map).sort (a, b) -> a - b
range = []
for code in codes
if range.length >= 100
unicodeMap += "\n#{range.length} beginbfchar\n#{range.join('\n')}\nendbfchar"
range = []
unicode = ('0000' + map[code].toString(16)).slice(-4)
code = (+code).toString(16)
range.push "<#{code}><#{unicode}>"
unicodeMap += "\n#{range.length} beginbfchar\n#{range.join('\n')}\nendbfchar\n" if range.length
unicodeMap += '''
endcmap
CMapName currentdict /CMap defineresource pop
end
end
'''
embedStandard: ->
= true
font = AFMFont.open __dirname + "/font/data/#{@filename}.afm"
{ , , , , } = font
= .ref
Type: 'Font'
BaseFont:
Subtype: 'Type1'
_standardFonts: [
"Courier"
"Courier-Bold"
"Courier-Oblique"
"Courier-BoldOblique"
"Helvetica"
"Helvetica-Bold"
"Helvetica-Oblique"
"Helvetica-BoldOblique"
"Times-Roman"
"Times-Bold"
"Times-Italic"
"Times-BoldItalic"
"Symbol"
"ZapfDingbats"
]
widthOfString: (string, size) ->
string = '' + string
width = 0
for i in [0...string.length]
charCode = string.charCodeAt(i) - if then 0 else 32
width += [charCode] or 0
scale = size / 1000
return width * scale
lineHeight: (size, includeGap = false) ->
gap = if includeGap then else 0
( + gap - ) / 1000 * size
module.exports = PDFFont