return join_map[join];
}
+// Keep in sync with SkPaint::Align
+static const char* text_align_map[] = {
+ NULL, // kLeft_Align (default)
+ "middle", // kCenter_Align
+ "end" // kRight_Align
+};
+SK_COMPILE_ASSERT(SK_ARRAY_COUNT(text_align_map) == SkPaint::kAlignCount,
+ missing_text_align_map_entry);
+static const char* svg_text_align(SkPaint::Align align) {
+ SkASSERT(align < SK_ARRAY_COUNT(text_align_map));
+ return text_align_map[align];
+}
+
+static SkString svg_transform(const SkMatrix& t) {
+ SkASSERT(!t.isIdentity());
+
+ SkString tstr;
+ switch (t.getType()) {
+ case SkMatrix::kPerspective_Mask:
+ SkDebugf("Can't handle perspective matrices.");
+ break;
+ case SkMatrix::kTranslate_Mask:
+ tstr.printf("translate(%g %g)", t.getTranslateX(), t.getTranslateY());
+ break;
+ case SkMatrix::kScale_Mask:
+ tstr.printf("scale(%g %g)", t.getScaleX(), t.getScaleY());
+ break;
+ default:
+ // http://www.w3.org/TR/SVG/coords.html#TransformMatrixDefined
+ // | a c e |
+ // | b d f |
+ // | 0 0 1 |
+ tstr.printf("matrix(%g %g %g %g %g %g)",
+ t.getScaleX(), t.getSkewY(),
+ t.getSkewX(), t.getScaleY(),
+ t.getTranslateX(), t.getTranslateY());
+ break;
+ }
+
+ return tstr;
+}
+
static void append_escaped_unichar(SkUnichar c, SkString* text) {
switch(c) {
case '&':
// and deduplicate resources.
class SkSVGDevice::ResourceBucket : ::SkNoncopyable {
public:
- ResourceBucket() : fGradientCount(0), fClipCount(0) {}
+ ResourceBucket() : fGradientCount(0), fClipCount(0), fPathCount(0) {}
SkString addLinearGradient() {
return SkStringPrintf("gradient_%d", fGradientCount++);
return SkStringPrintf("clip_%d", fClipCount++);
}
+ SkString addPath() {
+ return SkStringPrintf("path_%d", fPathCount++);
+ }
+
private:
uint32_t fGradientCount;
uint32_t fClipCount;
+ uint32_t fPathCount;
};
class SkSVGDevice::AutoElement : ::SkNoncopyable {
fWriter->startElement(name);
this->addPaint(paint, res);
- this->addTransform(*draw.fMatrix);
+
+ if (!draw.fMatrix->isIdentity()) {
+ this->addAttribute("transform", svg_transform(*draw.fMatrix));
+ }
}
~AutoElement() {
}
void addRectAttributes(const SkRect&);
- void addFontAttributes(const SkPaint&);
+ void addPathAttributes(const SkPath&);
+ void addTextAttributes(const SkPaint&);
private:
Resources addResources(const SkDraw& draw, const SkPaint& paint);
void addShaderResources(const SkPaint& paint, Resources* resources);
void addPaint(const SkPaint& paint, const Resources& resources);
- void addTransform(const SkMatrix& transform, const char name[] = "transform");
SkString addLinearGradientDef(const SkShader::GradientInfo& info, const SkShader* shader);
}
}
-void SkSVGDevice::AutoElement::addTransform(const SkMatrix& t, const char name[]) {
- if (t.isIdentity()) {
- return;
- }
-
- SkString tstr;
- switch (t.getType()) {
- case SkMatrix::kPerspective_Mask:
- SkDebugf("Can't handle perspective matrices.");
- break;
- case SkMatrix::kTranslate_Mask:
- tstr.printf("translate(%g %g)",
- SkScalarToFloat(t.getTranslateX()),
- SkScalarToFloat(t.getTranslateY()));
- break;
- case SkMatrix::kScale_Mask:
- tstr.printf("scale(%g %g)",
- SkScalarToFloat(t.getScaleX()),
- SkScalarToFloat(t.getScaleY()));
- break;
- default:
- tstr.printf("matrix(%g %g %g %g %g %g)",
- SkScalarToFloat(t.getScaleX()), SkScalarToFloat(t.getSkewY()),
- SkScalarToFloat(t.getSkewX()), SkScalarToFloat(t.getScaleY()),
- SkScalarToFloat(t.getTranslateX()), SkScalarToFloat(t.getTranslateY()));
- break;
- }
-
- fWriter->addAttribute(name, tstr.c_str());
-}
-
Resources SkSVGDevice::AutoElement::addResources(const SkDraw& draw, const SkPaint& paint) {
Resources resources(paint);
rectElement.addAttribute("clip-rule", clipRule);
} else {
AutoElement pathElement("path", fWriter);
- SkString pathStr;
- SkParsePath::ToSVGString(clipPath, &pathStr);
- pathElement.addAttribute("d", pathStr.c_str());
+ pathElement.addPathAttributes(clipPath);
pathElement.addAttribute("clip-rule", clipRule);
}
}
gradient.addAttribute("y1", info.fPoint[0].y());
gradient.addAttribute("x2", info.fPoint[1].x());
gradient.addAttribute("y2", info.fPoint[1].y());
- gradient.addTransform(shader->getLocalMatrix(), "gradientTransform");
+
+ if (!shader->getLocalMatrix().isIdentity()) {
+ this->addAttribute("gradientTransform", svg_transform(shader->getLocalMatrix()));
+ }
SkASSERT(info.fColorCount >= 2);
for (int i = 0; i < info.fColorCount; ++i) {
this->addAttribute("height", rect.height());
}
-void SkSVGDevice::AutoElement::addFontAttributes(const SkPaint& paint) {
+void SkSVGDevice::AutoElement::addPathAttributes(const SkPath& path) {
+ SkString pathData;
+ SkParsePath::ToSVGString(path, &pathData);
+ this->addAttribute("d", pathData);
+}
+
+void SkSVGDevice::AutoElement::addTextAttributes(const SkPaint& paint) {
this->addAttribute("font-size", paint.getTextSize());
SkTypeface::Style style = paint.getTypeface()->style();
if (!familyName.isEmpty()) {
this->addAttribute("font-family", familyName);
}
+
+ if (const char* textAlign = svg_text_align(paint.getTextAlign())) {
+ this->addAttribute("text-anchor", textAlign);
+ }
}
SkBaseDevice* SkSVGDevice::Create(const SkISize& size, SkWStream* wstream) {
void SkSVGDevice::drawPath(const SkDraw& draw, const SkPath& path, const SkPaint& paint,
const SkMatrix* prePathMatrix, bool pathIsMutable) {
AutoElement elem("path", fWriter, fResourceBucket, draw, paint);
-
- SkString pathStr;
- SkParsePath::ToSVGString(path, &pathStr);
- elem.addAttribute("d", pathStr.c_str());
+ elem.addPathAttributes(path);
}
void SkSVGDevice::drawBitmap(const SkDraw&, const SkBitmap& bitmap,
void SkSVGDevice::drawText(const SkDraw& draw, const void* text, size_t len,
SkScalar x, SkScalar y, const SkPaint& paint) {
AutoElement elem("text", fWriter, fResourceBucket, draw, paint);
- elem.addFontAttributes(paint);
+ elem.addTextAttributes(paint);
elem.addAttribute("x", x);
elem.addAttribute("y", y);
elem.addText(svg_text(text, len, paint));
SkASSERT(scalarsPerPos == 1 || scalarsPerPos == 2);
AutoElement elem("text", fWriter, fResourceBucket, draw, paint);
- elem.addFontAttributes(paint);
+ elem.addTextAttributes(paint);
SkString xStr;
SkString yStr;
void SkSVGDevice::drawTextOnPath(const SkDraw&, const void* text, size_t len, const SkPath& path,
const SkMatrix* matrix, const SkPaint& paint) {
- // todo
- SkDebugf("unsupported operation: drawTextOnPath()\n");
+ SkString pathID = fResourceBucket->addPath();
+
+ {
+ AutoElement defs("defs", fWriter);
+ AutoElement pathElement("path", fWriter);
+ pathElement.addAttribute("id", pathID);
+ pathElement.addPathAttributes(path);
+
+ }
+
+ {
+ AutoElement textElement("text", fWriter);
+ textElement.addTextAttributes(paint);
+
+ if (matrix && !matrix->isIdentity()) {
+ textElement.addAttribute("transform", svg_transform(*matrix));
+ }
+
+ {
+ AutoElement textPathElement("textPath", fWriter);
+ textPathElement.addAttribute("xlink:href", SkStringPrintf("#%s", pathID.c_str()));
+
+ if (paint.getTextAlign() != SkPaint::kLeft_Align) {
+ SkASSERT(paint.getTextAlign() == SkPaint::kCenter_Align ||
+ paint.getTextAlign() == SkPaint::kRight_Align);
+ textPathElement.addAttribute("startOffset",
+ paint.getTextAlign() == SkPaint::kCenter_Align ? "50%" : "100%");
+ }
+
+ textPathElement.addText(svg_text(text, len, paint));
+ }
+ }
}
void SkSVGDevice::drawVertices(const SkDraw&, SkCanvas::VertexMode, int vertexCount,