Skip to content

Commit

Permalink
Implement word cache around fontkit
Browse files Browse the repository at this point in the history
Caches the results of fontkit layout calls, which can be expensive depending on the opentype features in the font. Splits up text by words and shapes each individually to increase cache efficiency.
  • Loading branch information
devongovett committed May 29, 2017
1 parent d2e18da commit 6e9e58f
Showing 1 changed file with 52 additions and 8 deletions.
60 changes: 52 additions & 8 deletions lib/font/embedded.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,57 @@ class EmbeddedFont extends PDFFont
@lineGap = @font.lineGap * @scale
@bbox = @font.bbox

@layoutCache = Object.create(null)

layoutRun: (text, features) ->
run = @font.layout text, features

# Normalize position values
for position, i in run.positions
for key of position
position[key] *= @scale

position.advanceWidth = run.glyphs[i].advanceWidth * @scale

return run

layoutCached: (text) ->
if cached = @layoutCache[text]
return cached

run = @layoutRun text
@layoutCache[text] = run
return run

layout: (text, features, onlyWidth = false) ->
# Skip the cache if any user defined features are applied
if features
return @layoutRun text, features

glyphs = if onlyWidth then null else []
positions = if onlyWidth then null else []
advanceWidth = 0

# Split the string by words to increase cache efficiency.
# For this purpose, spaces and tabs are a good enough delimeter.
last = 0
index = 0
while index <= text.length
if (index is text.length and last < index) or text.charAt(index) in [' ', '\t']
run = @layoutCached text.slice(last, ++index)
if not onlyWidth
glyphs.push run.glyphs...
positions.push run.positions...

advanceWidth += run.advanceWidth
last = index
else
index++

return {glyphs, positions, advanceWidth}

encode: (text, features) ->
{glyphs, positions} = @font.layout text, features
{glyphs, positions} = @layout text, features

res = []
for glyph, i in glyphs
Expand All @@ -25,16 +74,11 @@ class EmbeddedFont extends PDFFont
@widths[gid] ?= glyph.advanceWidth * @scale
@unicode[gid] ?= glyph.codePoints

for key of positions[i]
positions[i][key] *= @scale

positions[i].advanceWidth = glyph.advanceWidth * @scale

return [res, positions]

widthOfString: (string, size, features) ->
width = @font.layout(string, features).advanceWidth
scale = size / @font.unitsPerEm
width = @layout(string, features, true).advanceWidth
scale = size / 1000
return width * scale

embed: ->
Expand Down

0 comments on commit 6e9e58f

Please sign in to comment.