More text enhancements and fixes. Demo page now has italic/underline/overline/line-through and shadow buttons for text manipulations.

This commit is contained in:
kangax 2011-03-21 17:24:36 -04:00
parent b60edcc379
commit 91205b4bb6
6 changed files with 194 additions and 84 deletions

54
dist/all.js vendored
View file

@ -1192,14 +1192,7 @@ Cufon.registerEngine('canvas', (function() {
g.fillStyle = Cufon.textOptions.color || style.get('color');
if (textDecoration.underline) line(-font.face['underline-position'], g.fillStyle);
if (textDecoration.overline) line(font.ascent, g.fillStyle);
function renderText() {
if (isItalic) {
g.save();
g.transform(1, 0, -0.25, 1, 0, 0);
}
var left = 0;
for (var i = 0, l = chars.length; i < l; ++i) {
if (chars[i] === '\n') {
@ -1208,20 +1201,50 @@ Cufon.registerEngine('canvas', (function() {
continue;
}
var glyph = font.glyphs[chars[i]] || font.missingGlyph;
if (!glyph) continue;
var charWidth = Number(glyph.w || font.w) + letterSpacing;
if (textDecoration) {
g.save();
g.strokeStyle = g.fillStyle;
g.beginPath();
if (textDecoration.underline) {
g.moveTo(0, -font.face['underline-position']);
g.lineTo(charWidth, -font.face['underline-position']);
}
if (textDecoration.overline) {
g.moveTo(0, font.ascent);
g.lineTo(charWidth, font.ascent);
}
if (textDecoration['line-through']) {
g.moveTo(0, -font.descent);
g.lineTo(charWidth, -font.descent);
}
g.stroke();
g.restore();
}
if (isItalic) {
g.save();
g.transform(1, 0, -0.25, 1, 0, 0);
}
g.beginPath();
if (glyph.d) {
if (glyph.code) interpret(glyph.code, g);
else glyph.code = generateFromVML('m' + glyph.d, g);
}
g.fill();
var charWidth = Number(glyph.w || font.w) + letterSpacing;
if (isItalic) {
g.restore();
}
g.translate(charWidth, 0);
left += charWidth;
}
if (isItalic) {
g.restore();
}
}
if (shadows) {
@ -1238,11 +1261,6 @@ Cufon.registerEngine('canvas', (function() {
g.save();
renderText();
g.restore();
if (textDecoration['line-through']) {
line(-font.descent, g.fillStyle);
}
g.restore();
g.restore();
@ -9492,7 +9510,8 @@ fabric.util.animate = animate;
fontFamily: this.fontfamily,
enableTextDecoration: true,
textDecoration: this.textDecoration,
textShadow: this.textShadow
textShadow: this.textShadow,
fontStyle: this.fontStyle
});
this.width = o.width;
@ -9514,7 +9533,6 @@ fabric.util.animate = animate;
el.style.fontSize = '40px';
el.style.fontWeight = '400';
el.style.fontStyle = 'normal';
el.style.letterSpacing = 'normal';
el.style.color = '#000000';
el.style.fontWeight = '600';

View file

@ -644,16 +644,16 @@ Cufon.registerEngine('canvas', (function() {
}
return function(font, text, style, options, node, el) {
var redraw = (text === null);
var viewBox = font.viewBox;
var size = style.getSize('fontSize', font.baseSize);
var letterSpacing = style.get('letterSpacing');
letterSpacing = (letterSpacing == 'normal') ? 0 : size.convertFrom(parseInt(letterSpacing, 10));
var expandTop = 0, expandRight = 0, expandBottom = 0, expandLeft = 0;
var shadows = options.textShadow, shadowOffsets = [];
if (shadows) {
@ -668,11 +668,11 @@ Cufon.registerEngine('canvas', (function() {
if (x < expandLeft) expandLeft = x;
}
}
var chars = Cufon.CSS.textTransform(redraw ? node.alt : text, style).split('');
var width = 0, lastWidth = null;
var maxWidth = 0, lines = 1;
for (var i = 0, l = chars.length; i < l; ++i) {
if (chars[i] === '\n') {
@ -688,14 +688,14 @@ Cufon.registerEngine('canvas', (function() {
width += lastWidth = Number(glyph.w || font.w) + letterSpacing;
}
width = Math.max(maxWidth, width)
if (lastWidth === null) return null; // there's nothing to render
expandRight += (viewBox.width - lastWidth);
expandLeft += viewBox.minX;
var wrapper, canvas;
if (redraw) {
wrapper = node;
canvas = node.firstChild;
@ -704,10 +704,10 @@ Cufon.registerEngine('canvas', (function() {
wrapper = document.createElement('span');
wrapper.className = 'cufon cufon-canvas';
wrapper.alt = text;
canvas = document.createElement('canvas');
wrapper.appendChild(canvas);
if (options.printable) {
var print = document.createElement('span');
print.className = 'cufon-alt';
@ -715,33 +715,32 @@ Cufon.registerEngine('canvas', (function() {
wrapper.appendChild(print);
}
}
var wStyle = wrapper.style;
var cStyle = canvas.style;
var height = size.convert(viewBox.height - expandTop + expandBottom);
var roundedHeight = Math.ceil(height);
var roundingFactor = roundedHeight / height;
canvas.width = Math.ceil(size.convert(width + expandRight - expandLeft) * roundingFactor);
canvas.height = roundedHeight;
// 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 _width = Math.ceil(size.convert(width * roundingFactor));
var wrapperWidth = _width + 'px';
var _height = size.convert(font.height);
Cufon.textOptions.width = _width;
Cufon.textOptions.height = _height * lines;
Cufon.textOptions.lines = lines;
if (HAS_INLINE_BLOCK) {
wStyle.width = wrapperWidth;
wStyle.height = _height + 'px';
@ -750,71 +749,93 @@ Cufon.registerEngine('canvas', (function() {
wStyle.paddingLeft = wrapperWidth;
wStyle.paddingBottom = (_height - 1) + 'px';
}
var g = Cufon.textOptions.context || canvas.getContext('2d'),
var g = Cufon.textOptions.context || canvas.getContext('2d'),
scale = roundedHeight / viewBox.height;
g.save();
g.scale(scale, scale);
g.translate(
-expandLeft - ((1/scale * canvas.width) / 2) + (Cufon.fonts[font.family].offsetLeft || 0),
-expandLeft - ((1/scale * canvas.width) / 2) + (Cufon.fonts[font.family].offsetLeft || 0),
-expandTop - (canvas.height / scale * Cufon.textOptions.lines) / 2
);
g.lineWidth = font.face['underline-thickness'];
g.save();
function line(y, color) {
g.strokeStyle = color;
g.beginPath();
g.moveTo(0, y);
g.lineTo(width, y);
g.stroke();
}
var textDecoration = options.enableTextDecoration ? Cufon.CSS.textDecoration(el, style) : {},
isItalic = options.fontStyle === 'italic';
g.fillStyle = Cufon.textOptions.color || style.get('color');
if (textDecoration.underline) line(-font.face['underline-position'], g.fillStyle);
if (textDecoration.overline) line(font.ascent, g.fillStyle);
function renderText() {
if (isItalic) {
g.save();
g.transform(1, 0, -0.25, 1, 0, 0);
}
var left = 0;
for (var i = 0, l = chars.length; i < l; ++i) {
if (chars[i] === '\n') {
// move "caret" to the current left
g.translate(-left, -font.ascent - font.ascent / 5 /* space between lines */);
left = 0;
continue;
}
var glyph = font.glyphs[chars[i]] || font.missingGlyph;
if (!glyph) continue;
var charWidth = Number(glyph.w || font.w) + letterSpacing;
if (textDecoration) {
g.save();
g.strokeStyle = g.fillStyle;
g.beginPath();
if (textDecoration.underline) {
g.moveTo(0, -font.face['underline-position']);
g.lineTo(charWidth, -font.face['underline-position']);
}
if (textDecoration.overline) {
g.moveTo(0, font.ascent);
g.lineTo(charWidth, font.ascent);
}
if (textDecoration['line-through']) {
g.moveTo(0, -font.descent);
g.lineTo(charWidth, -font.descent);
}
g.stroke();
g.restore();
}
if (isItalic) {
g.save();
g.transform(1, 0, -0.25, 1, 0, 0);
}
g.beginPath();
if (glyph.d) {
if (glyph.code) interpret(glyph.code, g);
else glyph.code = generateFromVML('m' + glyph.d, g);
}
g.fill();
var charWidth = Number(glyph.w || font.w) + letterSpacing;
if (isItalic) {
g.restore();
}
g.translate(charWidth, 0);
left += charWidth;
}
if (isItalic) {
g.restore();
}
}
if (shadows) {
for (var i = 0, l = shadows.length; i < l; ++i) {
var shadow = shadows[i];
@ -825,20 +846,15 @@ Cufon.registerEngine('canvas', (function() {
g.restore();
}
}
g.save();
renderText();
g.restore();
if (textDecoration['line-through']) {
line(-font.descent, g.fillStyle);
}
g.restore();
g.restore();
return wrapper;
};
})());

View file

@ -108,7 +108,8 @@
fontFamily: this.fontfamily,
enableTextDecoration: true,
textDecoration: this.textDecoration,
textShadow: this.textShadow
textShadow: this.textShadow,
fontStyle: this.fontStyle
});
// update width, height
@ -134,7 +135,6 @@
// need to specify these manually, since Jaxer doesn't support retrieving computed style
el.style.fontSize = '40px';
el.style.fontWeight = '400';
el.style.fontStyle = 'normal';
el.style.letterSpacing = 'normal';
el.style.color = '#000000';
el.style.fontWeight = '600';

View file

@ -7,10 +7,17 @@ h2 { background: #ffc; margin-top: 0; padding: 5px; color: #333; font-size: 1em;
#commands ul { list-style: none; padding-left: 0; }
#canvas-console { display: block; font-size: 11px; }
#rasterize { margin-top: 10px; color: green; }
#complexity { position: absolute; bottom: -20px; left: 0; }
#complexity { clear: both; padding-top: 10px; }
#controls { margin-bottom: 5px; }
#text-controls { clear: both; margin-top: 20px }
#text-controls button { vertical-align: top; }
#drawing-mode-options { margin-top: 5px; background: #ffc; padding: 5px; border: 1px solid #aaa; }
#text { position: relative; top: 30px; width: 250px; height: 100px; }
#text { position: relative; width: 250px; height: 100px; }
#text-cmd-linethrough { text-decoration: line-through; }
#text-cmd-underline { text-decoration: underline; }
#text-cmd-overline { text-decoration: overline; }
#text-cmd-italic { font-style: italic; }
#text-cmd-shadow { text-shadow: rgb(100,100,100) 1px 1px 3px; }
.canvas-container { margin: 0 auto; float: left; border: 1px solid #aaa; }
.clear { color: red; font-weight: bold; margin-top: 1em; }
@ -19,4 +26,6 @@ h2 { background: #ffc; margin-top: 0; padding: 5px; color: #333; font-size: 1em;
#drawing-mode.is-drawing { color: red; }
.svg-shapes { overflow: hidden; width: 320px; }
.svg-shapes li { display: inline; }
.svg-shapes li { display: inline; }
button.selected { font-weight: bold; vertical-align: top }

View file

@ -443,4 +443,64 @@
};
}
var cmdUnderlineBtn = document.getElementById('text-cmd-underline');
if (cmdUnderlineBtn) {
cmdUnderlineBtn.onclick = function() {
var activeObject = canvas.getActiveObject();
if (activeObject && activeObject.type === 'text') {
activeObject.textDecoration = (activeObject.textDecoration == 'underline' ? '' : 'underline');
this.className = activeObject.textDecoration ? 'selected' : '';
canvas.renderAll();
}
};
}
var cmdLinethroughBtn = document.getElementById('text-cmd-linethrough');
if (cmdLinethroughBtn) {
cmdLinethroughBtn.onclick = function() {
var activeObject = canvas.getActiveObject();
if (activeObject && activeObject.type === 'text') {
activeObject.textDecoration = (activeObject.textDecoration == 'line-through' ? '' : 'line-through');
this.className = activeObject.textDecoration ? 'selected' : '';
canvas.renderAll();
}
};
}
var cmdOverlineBtn = document.getElementById('text-cmd-overline');
if (cmdOverlineBtn) {
cmdOverlineBtn.onclick = function() {
var activeObject = canvas.getActiveObject();
if (activeObject && activeObject.type === 'text') {
activeObject.textDecoration = (activeObject.textDecoration == 'overline' ? '' : 'overline');
this.className = activeObject.textDecoration ? 'selected' : '';
canvas.renderAll();
}
};
}
var cmdItalicBtn = document.getElementById('text-cmd-italic');
if (cmdItalicBtn) {
cmdItalicBtn.onclick = function() {
var activeObject = canvas.getActiveObject();
if (activeObject && activeObject.type === 'text') {
activeObject.fontStyle = (activeObject.fontStyle == 'italic' ? '' : 'italic');
this.className = activeObject.fontStyle ? 'selected' : '';
canvas.renderAll();
}
};
}
var cmdShadowBtn = document.getElementById('text-cmd-shadow');
if (cmdShadowBtn) {
cmdShadowBtn.onclick = function() {
var activeObject = canvas.getActiveObject();
if (activeObject && activeObject.type === 'text') {
activeObject.textShadow = !activeObject.textShadow ? 'rgba(0,0,0,0.2) 2px 2px 10px' : '';
this.className = activeObject.fontStyle ? 'selected' : '';
canvas.renderAll();
}
};
}
})(this);

View file

@ -24,10 +24,17 @@
</div>
<div style="position:relative;width:804px;height:704px;float:left;">
<canvas id="canvas" width="800" height="700"></canvas>
<div id="fps">&nbsp;</div>
<canvas id="canvas" width="800" height="700"></canvas>
<div id="complexity">Canvas complexity (number of paths):<strong></strong></div>
<textarea id="text"></textarea>
<div id="text-controls">
<textarea id="text"></textarea>
<button type="button" id="text-cmd-italic">Italic</button>
<button type="button" id="text-cmd-underline">Underline</button>
<button type="button" id="text-cmd-linethrough">Linethrough</button>
<button type="button" id="text-cmd-overline">Overline</button>
<button type="button" id="text-cmd-shadow">Shadow</button>
</div>
</div>
<div id="commands">