2009-04-03 00:26:28 -07:00
|
|
|
function addPoint(aSVGElement, aPolylineElement, aX, aY) {
|
|
|
|
var pt = aSVGElement.createSVGPoint();
|
|
|
|
pt.x = aX;
|
|
|
|
pt.y = aY;
|
|
|
|
aPolylineElement.points.appendItem(pt);
|
|
|
|
}
|
|
|
|
|
|
|
|
function round(aFloat)
|
|
|
|
{
|
|
|
|
return aFloat >= 0.0 ? Math.floor(aFloat + 0.5) : Math.ceil(aFloat - 0.5);
|
|
|
|
}
|
|
|
|
|
|
|
|
const kDecorationStyleNone = 0;
|
|
|
|
const kDecorationStyleSolid = 1;
|
|
|
|
const kDecorationStyleDotted = 2;
|
|
|
|
const kDecorationStyleDashed = 3;
|
|
|
|
const kDecorationStyleDouble = 4;
|
|
|
|
const kDecorationStyleWavy = 5;
|
|
|
|
|
|
|
|
const kDotLength = 1.0;
|
|
|
|
const kDashLength = 3.0;
|
|
|
|
|
|
|
|
const kSVGNS = "http://www.w3.org/2000/svg";
|
|
|
|
|
|
|
|
// XXX following functions only support to draw underline now.
|
|
|
|
|
2009-04-05 22:53:00 -07:00
|
|
|
function drawDecorationLine(aDocument, aColor, aPt, aLineSize, aAscent, aOffset,
|
|
|
|
aStyle, aDescentLimit)
|
2009-04-03 00:26:28 -07:00
|
|
|
{
|
2009-04-05 22:53:00 -07:00
|
|
|
var rect = getTextDecorationRect(aPt, aLineSize, aAscent, aOffset, aStyle,
|
|
|
|
aDescentLimit);
|
2009-04-03 00:26:28 -07:00
|
|
|
if (rect.width == 0 || rect.height == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
var root = aDocument.documentElement;
|
|
|
|
var container = aDocument.createElementNS(kSVGNS, "svg");
|
|
|
|
root.appendChild(container);
|
|
|
|
|
|
|
|
var line1 = aDocument.createElementNS(kSVGNS, "polyline");
|
|
|
|
var line2;
|
|
|
|
|
|
|
|
var style = "position: absolute;";
|
|
|
|
style += "left: " + rect.x + "px;";
|
|
|
|
style += "top: " + rect.y + "px;";
|
|
|
|
style += "width: " + rect.width + "px;";
|
|
|
|
style += "height: " + rect.height + "px;";
|
|
|
|
container.setAttribute("style", style);
|
|
|
|
rect.x = rect.y = 0;
|
|
|
|
|
|
|
|
var lineHeight = Math.max(round(aLineSize.height), 1.0);
|
|
|
|
|
|
|
|
switch (aStyle) {
|
|
|
|
case kDecorationStyleDouble:
|
|
|
|
line2 = aDocument.createElementNS(kSVGNS, "polyline");
|
|
|
|
container.appendChild(line2);
|
|
|
|
case kDecorationStyleSolid:
|
|
|
|
container.appendChild(line1);
|
|
|
|
break;
|
|
|
|
case kDecorationStyleDashed:
|
|
|
|
container.appendChild(line1);
|
|
|
|
var dashWidth = lineHeight * kDotLength * kDashLength;
|
|
|
|
var dash = "stroke-dasharray: " + dashWidth + ", " + dashWidth + ";";
|
|
|
|
var lineCap = "stroke-linecap: butt;"
|
|
|
|
line1.setAttribute("style", dash + lineCap);
|
|
|
|
rect.width += dashWidth;
|
|
|
|
break;
|
|
|
|
case kDecorationStyleDotted:
|
|
|
|
container.appendChild(line1);
|
|
|
|
var dashWidth = lineHeight * kDotLength;
|
|
|
|
var dash = "stroke-dasharray: ";
|
|
|
|
var lineCap = "";
|
|
|
|
if (lineHeight > 2.0) {
|
|
|
|
dash += "0.0, " + dashWidth * 2.0 + ";";
|
|
|
|
lineCap = "stroke-linecap: round;";
|
|
|
|
} else {
|
|
|
|
dash += dashWidth + ", " + dashWidth + ";";
|
|
|
|
}
|
|
|
|
rect.width += dashWidth;
|
|
|
|
line1.setAttribute("style", dash + lineCap);
|
|
|
|
break;
|
|
|
|
case kDecorationStyleWavy:
|
|
|
|
container.appendChild(line1);
|
|
|
|
if (lineHeight > 2.0) {
|
|
|
|
//
|
|
|
|
} else {
|
|
|
|
line1.setAttribute("shape-rendering", "optimizeSpeed");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
rect.y += lineHeight / 2;
|
|
|
|
|
|
|
|
line1.setAttribute("fill", "none");
|
|
|
|
line1.setAttribute("stroke", aColor);
|
|
|
|
line1.setAttribute("stroke-width", lineHeight);
|
|
|
|
if (line2) {
|
|
|
|
line2.setAttribute("fill", "none");
|
|
|
|
line2.setAttribute("stroke", aColor);
|
|
|
|
line2.setAttribute("stroke-width", lineHeight);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (aStyle) {
|
|
|
|
case kDecorationStyleSolid:
|
|
|
|
addPoint(container, line1, rect.x, rect.y);
|
|
|
|
addPoint(container, line1, rect.x + rect.width, rect.y);
|
|
|
|
break;
|
|
|
|
case kDecorationStyleDouble:
|
|
|
|
addPoint(container, line1, rect.x, rect.y);
|
|
|
|
addPoint(container, line1, rect.x + rect.width, rect.y);
|
|
|
|
rect.height -= lineHeight;
|
|
|
|
addPoint(container, line2, rect.x, rect.y + rect.height);
|
|
|
|
addPoint(container, line2, rect.x + rect.width, rect.y + rect.height);
|
|
|
|
break;
|
|
|
|
case kDecorationStyleDotted:
|
|
|
|
case kDecorationStyleDashed:
|
|
|
|
addPoint(container, line1, rect.x, rect.y);
|
|
|
|
addPoint(container, line1, rect.x + rect.width, rect.y);
|
|
|
|
break;
|
|
|
|
case kDecorationStyleWavy:
|
|
|
|
rect.x += lineHeight / 2.0;
|
|
|
|
|
|
|
|
var pt = { x: rect.x, y: rect.y };
|
|
|
|
var rightMost = pt.x + rect.width + lineHeight;
|
|
|
|
var adv = rect.height - lineHeight;
|
|
|
|
var flatLengthAtVertex = Math.max((lineHeight - 1.0) * 2.0, 1.0);
|
|
|
|
|
|
|
|
var points = "";
|
|
|
|
|
|
|
|
pt.x -= lineHeight;
|
|
|
|
addPoint(container, line1, pt.x, pt.y);
|
|
|
|
|
|
|
|
pt.x = rect.x;
|
|
|
|
addPoint(container, line1, pt.x, pt.y);
|
|
|
|
|
|
|
|
var goDown = true;
|
|
|
|
while (pt.x < rightMost) {
|
|
|
|
pt.x += adv;
|
|
|
|
pt.y += goDown ? adv : -adv;
|
|
|
|
|
|
|
|
addPoint(container, line1, pt.x, pt.y);
|
|
|
|
|
|
|
|
pt.x += flatLengthAtVertex;
|
|
|
|
addPoint(container, line1, pt.x, pt.y);
|
|
|
|
|
|
|
|
goDown = !goDown;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-05 22:53:00 -07:00
|
|
|
function getTextDecorationRect(aPt, aLineSize, aAscent, aOffset, aStyle,
|
|
|
|
aDescentLimit)
|
2009-04-03 00:26:28 -07:00
|
|
|
{
|
|
|
|
if (aStyle == kDecorationStyleNone)
|
|
|
|
return { x: 0, y: 0, width: 0, height: 0 };
|
|
|
|
|
2009-04-05 22:53:00 -07:00
|
|
|
var liftupUnderline = aDescentLimit >= 0.0;
|
|
|
|
|
2009-04-03 00:26:28 -07:00
|
|
|
var r = {};
|
|
|
|
r.x = Math.floor(aPt.x + 0.5);
|
|
|
|
r.width = round(aLineSize.width);
|
|
|
|
|
|
|
|
var lineHeight = round(aLineSize.height);
|
|
|
|
lineHeight = Math.max(lineHeight, 1.0);
|
2009-04-05 22:53:00 -07:00
|
|
|
|
|
|
|
var ascent = round(aAscent);
|
|
|
|
var descentLimit = round(aDescentLimit);
|
|
|
|
|
|
|
|
var suggestedMaxRectHeight = Math.max(Math.min(ascent, descentLimit), 1.0);
|
2009-04-03 00:26:28 -07:00
|
|
|
var underlineOffsetAdjust = 0.0;
|
|
|
|
r.height = lineHeight;
|
|
|
|
if (aStyle == kDecorationStyleDouble) {
|
|
|
|
var gap = round(lineHeight / 2.0);
|
|
|
|
gap = Math.max(gap, 1.0);
|
|
|
|
r.height = lineHeight * 2.0 + gap;
|
2009-04-05 22:53:00 -07:00
|
|
|
if (liftupUnderline) {
|
|
|
|
if (r.height > suggestedMaxRectHeight) {
|
|
|
|
r.height = Math.max(suggestedMaxRectHeight, lineHeight * 2.0 + 1.0);
|
|
|
|
}
|
|
|
|
}
|
2009-04-03 00:26:28 -07:00
|
|
|
} else if (aStyle == kDecorationStyleWavy) {
|
|
|
|
r.height = lineHeight > 2.0 ? lineHeight * 4.0 : lineHeight * 3.0;
|
2009-04-05 22:53:00 -07:00
|
|
|
if (liftupUnderline) {
|
|
|
|
descentLimit += lineHeight;
|
|
|
|
suggestedMaxRectHeight = Math.max(Math.min(ascent, descentLimit), 1.0);
|
|
|
|
if (r.height > suggestedMaxRectHeight) {
|
|
|
|
r.height = Math.max(suggestedMaxRectHeight, lineHeight * 2.0);
|
|
|
|
}
|
|
|
|
}
|
2009-04-03 00:26:28 -07:00
|
|
|
underlineOffsetAdjust = r.height / 2.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
var baseline = Math.floor(aPt.y + aAscent + 0.5);
|
|
|
|
var offset = 0.0;
|
|
|
|
|
|
|
|
offset = aOffset + underlineOffsetAdjust;
|
2009-04-05 22:53:00 -07:00
|
|
|
if (liftupUnderline) {
|
|
|
|
if (descentLimit < -offset + r.height) {
|
|
|
|
var offsetBottomAligned = -descentLimit + r.height;
|
|
|
|
var offsetTopAligned = underlineOffsetAdjust;
|
|
|
|
offset = Math.min(offsetBottomAligned, offsetTopAligned);
|
|
|
|
}
|
|
|
|
}
|
2009-04-03 00:26:28 -07:00
|
|
|
|
|
|
|
r.y = baseline - Math.floor(offset + 0.5);
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|