gecko/layout/base/tests/decoration_line_rendering.js

210 lines
6.2 KiB
JavaScript

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.
function drawDecorationLine(aDocument, aColor, aPt, aLineSize, aAscent, aOffset,
aStyle, aDescentLimit)
{
var rect = getTextDecorationRect(aPt, aLineSize, aAscent, aOffset, aStyle,
aDescentLimit);
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;
}
}
function getTextDecorationRect(aPt, aLineSize, aAscent, aOffset, aStyle,
aDescentLimit)
{
if (aStyle == kDecorationStyleNone)
return { x: 0, y: 0, width: 0, height: 0 };
var liftupUnderline = aDescentLimit >= 0.0;
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);
var ascent = round(aAscent);
var descentLimit = round(aDescentLimit);
var suggestedMaxRectHeight = Math.max(Math.min(ascent, descentLimit), 1.0);
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;
if (liftupUnderline) {
if (r.height > suggestedMaxRectHeight) {
r.height = Math.max(suggestedMaxRectHeight, lineHeight * 2.0 + 1.0);
}
}
} else if (aStyle == kDecorationStyleWavy) {
r.height = lineHeight > 2.0 ? lineHeight * 4.0 : lineHeight * 3.0;
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);
}
}
underlineOffsetAdjust = r.height / 2.0;
}
var baseline = Math.floor(aPt.y + aAscent + 0.5);
var offset = 0.0;
offset = aOffset + underlineOffsetAdjust;
if (liftupUnderline) {
if (descentLimit < -offset + r.height) {
var offsetBottomAligned = -descentLimit + r.height;
var offsetTopAligned = underlineOffsetAdjust;
offset = Math.min(offsetBottomAligned, offsetTopAligned);
}
}
r.y = baseline - Math.floor(offset + 0.5);
return r;
}