diff --git a/js/cufon.js b/js/cufon.js index 2efdde2..9db30e6 100644 --- a/js/cufon.js +++ b/js/cufon.js @@ -902,224 +902,6 @@ var Cufon = (function() { })(); -Cufon.registerEngine('canvas', (function() { - - // Safari 2 doesn't support .apply() on native methods - - var check = document.createElement('canvas'); - if (!check || !check.getContext || !check.getContext.apply) return; - check = null; - - var HAS_INLINE_BLOCK = Cufon.CSS.supports('display', 'inline-block'); - - // Firefox 2 w/ non-strict doctype (almost standards mode) - var HAS_BROKEN_LINEHEIGHT = !HAS_INLINE_BLOCK && (document.compatMode == 'BackCompat' || /frameset|transitional/i.test(document.doctype.publicId)); - - var styleSheet = document.createElement('style'); - styleSheet.type = 'text/css'; - styleSheet.appendChild(document.createTextNode(( - 'cufon{text-indent:0;}' + - '@media screen,projection{' + - 'cufon{display:inline;display:inline-block;position:relative;vertical-align:middle;' + - (HAS_BROKEN_LINEHEIGHT - ? '' - : 'font-size:1px;line-height:1px;') + - '}cufon cufontext{display:-moz-inline-box;display:inline-block;width:0;height:0;text-indent:-10000in;}' + - (HAS_INLINE_BLOCK - ? 'cufon canvas{position:relative;}' - : 'cufon canvas{position:absolute;}') + - 'cufonshy.cufon-shy-disabled,.cufon-viewport-resizing cufonshy{display:none;}' + - 'cufonglue{white-space:nowrap;display:inline-block;}' + - '.cufon-viewport-resizing cufonglue{white-space:normal;}' + - '}' + - '@media print{' + - 'cufon{padding:0;}' + // Firefox 2 - 'cufon canvas{display:none;}' + - '}' - ).replace(/;/g, '!important;'))); - document.getElementsByTagName('head')[0].appendChild(styleSheet); - - function generateFromVML(path, context) { - var atX = 0, atY = 0; - var code = [], re = /([mrvxe])([^a-z]*)/g, match; - generate: for (var i = 0; match = re.exec(path); ++i) { - var c = match[2].split(','); - switch (match[1]) { - case 'v': - code[i] = { m: 'bezierCurveTo', a: [ atX + ~~c[0], atY + ~~c[1], atX + ~~c[2], atY + ~~c[3], atX += ~~c[4], atY += ~~c[5] ] }; - break; - case 'r': - code[i] = { m: 'lineTo', a: [ atX += ~~c[0], atY += ~~c[1] ] }; - break; - case 'm': - code[i] = { m: 'moveTo', a: [ atX = ~~c[0], atY = ~~c[1] ] }; - break; - case 'x': - code[i] = { m: 'closePath' }; - break; - case 'e': - break generate; - } - context[code[i].m].apply(context, code[i].a); - } - return code; - } - - function interpret(code, context) { - for (var i = 0, l = code.length; i < l; ++i) { - var line = code[i]; - context[line.m].apply(context, line.a); - } - } - - return function(font, text, style, options, node, el) { - - var redraw = (text === null); - - if (redraw) text = node.getAttribute('alt'); - - var viewBox = font.viewBox; - - var size = style.getSize('fontSize', font.baseSize); - - var expandTop = 0, expandRight = 0, expandBottom = 0, expandLeft = 0; - var shadows = options.textShadow, shadowOffsets = []; - if (shadows) { - for (var i = shadows.length; i--;) { - var shadow = shadows[i]; - var x = size.convertFrom(parseFloat(shadow.offX)); - var y = size.convertFrom(parseFloat(shadow.offY)); - shadowOffsets[i] = [ x, y ]; - if (y < expandTop) expandTop = y; - if (x > expandRight) expandRight = x; - if (y > expandBottom) expandBottom = y; - if (x < expandLeft) expandLeft = x; - } - } - - var chars = Cufon.CSS.textTransform(text, style).split(''); - - var jumps = font.spacing(chars, - ~~size.convertFrom(parseFloat(style.get('letterSpacing')) || 0), - ~~size.convertFrom(parseFloat(style.get('wordSpacing')) || 0) - ); - - if (!jumps.length) return null; // there's nothing to render - - var width = jumps.total; - - expandRight += viewBox.width - jumps[jumps.length - 1]; - expandLeft += viewBox.minX; - - var wrapper, canvas; - - if (redraw) { - wrapper = node; - canvas = node.firstChild; - } - else { - wrapper = document.createElement('cufon'); - wrapper.className = 'cufon cufon-canvas'; - wrapper.setAttribute('alt', text); - - canvas = document.createElement('canvas'); - wrapper.appendChild(canvas); - - if (options.printable) { - var print = document.createElement('cufontext'); - print.appendChild(document.createTextNode(text)); - wrapper.appendChild(print); - } - } - - var wStyle = wrapper.style; - var cStyle = canvas.style; - - var height = size.convert(viewBox.height); - var roundedHeight = Math.ceil(height); - var roundingFactor = roundedHeight / height; - var stretchFactor = roundingFactor * Cufon.CSS.fontStretch(style.get('fontStretch')); - var stretchedWidth = width * stretchFactor; - - var canvasWidth = Math.ceil(size.convert(stretchedWidth + expandRight - expandLeft)); - var canvasHeight = Math.ceil(size.convert(viewBox.height - expandTop + expandBottom)); - - canvas.width = canvasWidth; - canvas.height = canvasHeight; - - // needed for WebKit and full page zoom - cStyle.width = canvasWidth + 'px'; - cStyle.height = canvasHeight + 'px'; - - // minY has no part in canvas.height - expandTop += viewBox.minY; - - cStyle.top = Math.round(size.convert(expandTop - font.ascent)) + 'px'; - cStyle.left = Math.round(size.convert(expandLeft)) + 'px'; - - var wrapperWidth = Math.max(Math.ceil(size.convert(stretchedWidth)), 0) + 'px'; - - if (HAS_INLINE_BLOCK) { - wStyle.width = wrapperWidth; - wStyle.height = size.convert(font.height) + 'px'; - } - else { - wStyle.paddingLeft = wrapperWidth; - wStyle.paddingBottom = (size.convert(font.height) - 1) + 'px'; - } - - var g = canvas.getContext('2d'), scale = height / viewBox.height; - - // proper horizontal scaling is performed later - g.scale(scale, scale * roundingFactor); - g.translate(-expandLeft, -expandTop); - g.save(); - - function renderText() { - var glyphs = font.glyphs, glyph, i = -1, j = -1, chr; - g.scale(stretchFactor, 1); - while (chr = chars[++i]) { - var glyph = glyphs[chars[i]] || font.missingGlyph; - if (!glyph) continue; - if (glyph.d) { - g.beginPath(); - if (glyph.code) interpret(glyph.code, g); - else glyph.code = generateFromVML('m' + glyph.d, g); - g.fill(); - } - g.translate(jumps[++j], 0); - } - g.restore(); - } - - if (shadows) { - for (var i = shadows.length; i--;) { - var shadow = shadows[i]; - g.save(); - g.fillStyle = shadow.color; - g.translate.apply(g, shadowOffsets[i]); - renderText(); - } - } - - var gradient = options.textGradient; - if (gradient) { - var stops = gradient.stops, fill = g.createLinearGradient(0, viewBox.minY, 0, viewBox.maxY); - for (var i = 0, l = stops.length; i < l; ++i) { - fill.addColorStop.apply(fill, stops[i]); - } - g.fillStyle = fill; - } - else g.fillStyle = style.get('color'); - - renderText(); - - return wrapper; - - }; - -})()); - Cufon.registerEngine('vml', (function() { var ns = document.namespaces; @@ -1376,3 +1158,221 @@ Cufon.registerEngine('vml', (function() { }; })()); + +Cufon.registerEngine('canvas', (function() { + + // Safari 2 doesn't support .apply() on native methods + + var check = document.createElement('canvas'); + if (!check || !check.getContext || !check.getContext.apply) return; + check = null; + + var HAS_INLINE_BLOCK = Cufon.CSS.supports('display', 'inline-block'); + + // Firefox 2 w/ non-strict doctype (almost standards mode) + var HAS_BROKEN_LINEHEIGHT = !HAS_INLINE_BLOCK && (document.compatMode == 'BackCompat' || /frameset|transitional/i.test(document.doctype.publicId)); + + var styleSheet = document.createElement('style'); + styleSheet.type = 'text/css'; + styleSheet.appendChild(document.createTextNode(( + 'cufon{text-indent:0;}' + + '@media screen,projection{' + + 'cufon{display:inline;display:inline-block;position:relative;vertical-align:middle;' + + (HAS_BROKEN_LINEHEIGHT + ? '' + : 'font-size:1px;line-height:1px;') + + '}cufon cufontext{display:-moz-inline-box;display:inline-block;width:0;height:0;text-indent:-10000in;}' + + (HAS_INLINE_BLOCK + ? 'cufon canvas{position:relative;}' + : 'cufon canvas{position:absolute;}') + + 'cufonshy.cufon-shy-disabled,.cufon-viewport-resizing cufonshy{display:none;}' + + 'cufonglue{white-space:nowrap;display:inline-block;}' + + '.cufon-viewport-resizing cufonglue{white-space:normal;}' + + '}' + + '@media print{' + + 'cufon{padding:0;}' + // Firefox 2 + 'cufon canvas{display:none;}' + + '}' + ).replace(/;/g, '!important;'))); + document.getElementsByTagName('head')[0].appendChild(styleSheet); + + function generateFromVML(path, context) { + var atX = 0, atY = 0; + var code = [], re = /([mrvxe])([^a-z]*)/g, match; + generate: for (var i = 0; match = re.exec(path); ++i) { + var c = match[2].split(','); + switch (match[1]) { + case 'v': + code[i] = { m: 'bezierCurveTo', a: [ atX + ~~c[0], atY + ~~c[1], atX + ~~c[2], atY + ~~c[3], atX += ~~c[4], atY += ~~c[5] ] }; + break; + case 'r': + code[i] = { m: 'lineTo', a: [ atX += ~~c[0], atY += ~~c[1] ] }; + break; + case 'm': + code[i] = { m: 'moveTo', a: [ atX = ~~c[0], atY = ~~c[1] ] }; + break; + case 'x': + code[i] = { m: 'closePath' }; + break; + case 'e': + break generate; + } + context[code[i].m].apply(context, code[i].a); + } + return code; + } + + function interpret(code, context) { + for (var i = 0, l = code.length; i < l; ++i) { + var line = code[i]; + context[line.m].apply(context, line.a); + } + } + + return function(font, text, style, options, node, el) { + + var redraw = (text === null); + + if (redraw) text = node.getAttribute('alt'); + + var viewBox = font.viewBox; + + var size = style.getSize('fontSize', font.baseSize); + + var expandTop = 0, expandRight = 0, expandBottom = 0, expandLeft = 0; + var shadows = options.textShadow, shadowOffsets = []; + if (shadows) { + for (var i = shadows.length; i--;) { + var shadow = shadows[i]; + var x = size.convertFrom(parseFloat(shadow.offX)); + var y = size.convertFrom(parseFloat(shadow.offY)); + shadowOffsets[i] = [ x, y ]; + if (y < expandTop) expandTop = y; + if (x > expandRight) expandRight = x; + if (y > expandBottom) expandBottom = y; + if (x < expandLeft) expandLeft = x; + } + } + + var chars = Cufon.CSS.textTransform(text, style).split(''); + + var jumps = font.spacing(chars, + ~~size.convertFrom(parseFloat(style.get('letterSpacing')) || 0), + ~~size.convertFrom(parseFloat(style.get('wordSpacing')) || 0) + ); + + if (!jumps.length) return null; // there's nothing to render + + var width = jumps.total; + + expandRight += viewBox.width - jumps[jumps.length - 1]; + expandLeft += viewBox.minX; + + var wrapper, canvas; + + if (redraw) { + wrapper = node; + canvas = node.firstChild; + } + else { + wrapper = document.createElement('cufon'); + wrapper.className = 'cufon cufon-canvas'; + wrapper.setAttribute('alt', text); + + canvas = document.createElement('canvas'); + wrapper.appendChild(canvas); + + if (options.printable) { + var print = document.createElement('cufontext'); + print.appendChild(document.createTextNode(text)); + wrapper.appendChild(print); + } + } + + var wStyle = wrapper.style; + var cStyle = canvas.style; + + var height = size.convert(viewBox.height); + var roundedHeight = Math.ceil(height); + var roundingFactor = roundedHeight / height; + var stretchFactor = roundingFactor * Cufon.CSS.fontStretch(style.get('fontStretch')); + var stretchedWidth = width * stretchFactor; + + var canvasWidth = Math.ceil(size.convert(stretchedWidth + expandRight - expandLeft)); + var canvasHeight = Math.ceil(size.convert(viewBox.height - expandTop + expandBottom)); + + canvas.width = canvasWidth; + canvas.height = canvasHeight; + + // needed for WebKit and full page zoom + cStyle.width = canvasWidth + 'px'; + cStyle.height = canvasHeight + 'px'; + + // minY has no part in canvas.height + expandTop += viewBox.minY; + + cStyle.top = Math.round(size.convert(expandTop - font.ascent)) + 'px'; + cStyle.left = Math.round(size.convert(expandLeft)) + 'px'; + + var wrapperWidth = Math.max(Math.ceil(size.convert(stretchedWidth)), 0) + 'px'; + + if (HAS_INLINE_BLOCK) { + wStyle.width = wrapperWidth; + wStyle.height = size.convert(font.height) + 'px'; + } + else { + wStyle.paddingLeft = wrapperWidth; + wStyle.paddingBottom = (size.convert(font.height) - 1) + 'px'; + } + + var g = canvas.getContext('2d'), scale = height / viewBox.height; + + // proper horizontal scaling is performed later + g.scale(scale, scale * roundingFactor); + g.translate(-expandLeft, -expandTop); + g.save(); + + function renderText() { + var glyphs = font.glyphs, glyph, i = -1, j = -1, chr; + g.scale(stretchFactor, 1); + while (chr = chars[++i]) { + var glyph = glyphs[chars[i]] || font.missingGlyph; + if (!glyph) continue; + if (glyph.d) { + g.beginPath(); + if (glyph.code) interpret(glyph.code, g); + else glyph.code = generateFromVML('m' + glyph.d, g); + g.fill(); + } + g.translate(jumps[++j], 0); + } + g.restore(); + } + + if (shadows) { + for (var i = shadows.length; i--;) { + var shadow = shadows[i]; + g.save(); + g.fillStyle = shadow.color; + g.translate.apply(g, shadowOffsets[i]); + renderText(); + } + } + + var gradient = options.textGradient; + if (gradient) { + var stops = gradient.stops, fill = g.createLinearGradient(0, viewBox.minY, 0, viewBox.maxY); + for (var i = 0, l = stops.length; i < l; ++i) { + fill.addColorStop.apply(fill, stops[i]); + } + g.fillStyle = fill; + } + else g.fillStyle = style.get('color'); + + renderText(); + + return wrapper; + + }; + +})());