Skip to content

Instantly share code, notes, and snippets.

@danschumann
Last active June 17, 2016 17:31
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save danschumann/cb832f6341aa697e58cb to your computer and use it in GitHub Desktop.
Save danschumann/cb832f6341aa697e58cb to your computer and use it in GitHub Desktop.
html canvas text addons
Canvas = require('canvas')
_ = require('underscore')
Canvas.Context2d::fillTextCircle = (text, x, y, radius, letterSpacing, angle, reverse, letterSpacingFlat, maxAngle) ->
if _.isObject text
{
text
x
y
radius
letterSpacing
angle
reverse
letterSpacingFlat
maxAngle
getAngle
} = text
prevWidth = null
#@textAlign = 'center'
fontSize = parseFloat (''+@font).replace(/[^\d\.]*/, '')
circumference = radius * 2 * Math.PI
angles = []
letterSpan = 0
# Loop once for all angles and total
totalAngle = 0
n = -1
while ++n < text.length
char = text[n]
thisWidth = @measureText(char).width
angles[n] = unless prevWidth? then 0 else (
(
Math.pow((thisWidth + prevWidth - 3)/ circumference * .5, .45) * letterSpacing - letterSpacingFlat
)
) * (reverse && -1 || 1)
prevWidth = @measureText(char).width
totalAngle += angles[n]
totalAngle += switch _.last text
when 'o','N', 'Y'
.010
when 'u', 'h','n','M',"U"
.015
when 'i', 'l', 'q'
.02
when 'g','I'
.025
when 'm'
.035
else 0
# Sometimes we only want the total for computational purposes
return totalAngle if getAngle
@save()
@translate x, y
# Move their specified angle to center text around it
angle -= totalAngle / 2 #center
@textAlign = 'center'
n = -1
angleOffset = 0
while ++n < text.length
@save()
char = text[n]
# Last char gets .0000001
@rotate angle + angleOffset + angles[n] + (if n<text.length-1
switch char
when 'b'
.004
when 'f'
.007
when 't'
.007
when 'q'
-.004
when 'r'
.008
when 'w'
.015
when 'j'
.005
when 'v'
.015
when 'y'
.01
when 'V'
.011
when 'P'
.004
when 'O'
.007
when 'T'
.006
when 'W'
.005
when 'Y'
.005
when 'R'
.006
when 'A'
.004
when 'h'
.006
else 0
else 0
) * (if reverse then -1 else 1)
if maxAngle and angles[n] > maxAngle
@restore()
break
else
@fillText char, 0, (if reverse then fontSize/2 + radius else -radius)
angleOffset += angles[n]
@restore()
@restore()
{totalAngle}
# Since we might require this file multiple times we have to copy the original only once
global._fillText ?= Canvas.Context2d::fillText
Canvas.Context2d::fillText = (text, x, y, args...) ->
# no kerning? default behavior
return global._fillText.apply this, arguments unless @kerning?
# we need to remember some stuff as we loop
offset = 0
_.each text, (letter) =>
global._fillText.apply this, [
letter
x + offset + @kerning
y
].concat args # in case any additional args get sent to fillText at any time
offset += @measureText(letter).width + @kerning
return (app) =>
canvas = new Canvas(500, 500)
ctx = canvas.getContext('2d')
ctx.font = '30px Impact'
ctx.rotate(.2)
ctx.fillText("lorem ipsum", 50, 100)
_.defer => app.get '/logo.png', (req, res) ->
res.writeHead(200,'image/jpg')
imgCanv = canvas.createJPGStream()
imgCanv.on "error", (exception)-> res.send 'error'
imgCanv.on "data", (data)-> res.write data
imgCanv.on "end", -> res.end('binary')
serveCanvas = (res, canvas) ->
res.type "image/png"
nodefn.call( _.bind(canvas.toBuffer, canvas) )
.then (buffer) ->
res.end buffer
.otherwise (er) ->
console.log "ERROR!".red, er, er.stack if er
filterText: ->
# Filter out all characters that break canvas
if ttext = @props.params.ttext
ttext unless _.isString ttext
len = ttext.length
for i in [0...len]
if 760 < ttext.charCodeAt len - i
ttext = @props.params.ttext = ttext.slice(0, len - i) + ttext.slice len - i + 1
@danschumann
Copy link
Author

eventually i could clean this up and put in a module. for now its just a method that writes text rotating around a circle. it's only been tested in arial.

@danschumann
Copy link
Author

there are some other methods mixed on, like a kerning addon for fillText, an example of how to write canvas to the response, and a filter so that canvas doesn't crash the server if someone passes sigart to it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment