diff options
author | fmalita <fmalita@chromium.org> | 2015-02-04 17:54:46 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-02-04 17:54:46 -0800 |
commit | a9d9de45c1cd83419b4afc7884359e92bc173009 (patch) | |
tree | a0da05b853f11a27573d26bfd5b8652c42f2b6c3 /experimental/svg | |
parent | 12753ccc9a3a096a8b528fab6dfed431eb9aa8b8 (diff) |
[SkSVGDevice] drawTextOnPath() support
R=reed@google.com,mtklein@google.com
Review URL: https://codereview.chromium.org/874853003
Diffstat (limited to 'experimental/svg')
-rw-r--r-- | experimental/svg/SkSVGDevice.cpp | 153 |
1 files changed, 105 insertions, 48 deletions
diff --git a/experimental/svg/SkSVGDevice.cpp b/experimental/svg/SkSVGDevice.cpp index 91ff31364d..f3ac02d853 100644 --- a/experimental/svg/SkSVGDevice.cpp +++ b/experimental/svg/SkSVGDevice.cpp @@ -57,6 +57,48 @@ static const char* svg_join(SkPaint::Join join) { 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 '&': @@ -136,7 +178,7 @@ struct Resources { // 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++); @@ -146,9 +188,14 @@ public: 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 { @@ -169,7 +216,10 @@ public: fWriter->startElement(name); this->addPaint(paint, res); - this->addTransform(*draw.fMatrix); + + if (!draw.fMatrix->isIdentity()) { + this->addAttribute("transform", svg_transform(*draw.fMatrix)); + } } ~AutoElement() { @@ -197,7 +247,8 @@ public: } 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); @@ -205,7 +256,6 @@ private: 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); @@ -262,37 +312,6 @@ void SkSVGDevice::AutoElement::addPaint(const SkPaint& paint, const Resources& r } } -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); @@ -362,9 +381,7 @@ void SkSVGDevice::AutoElement::addClipResources(const SkDraw& draw, Resources* r 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); } } @@ -386,7 +403,10 @@ SkString SkSVGDevice::AutoElement::addLinearGradientDef(const SkShader::Gradient 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) { @@ -421,7 +441,13 @@ void SkSVGDevice::AutoElement::addRectAttributes(const SkRect& rect) { 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(); @@ -439,6 +465,10 @@ void SkSVGDevice::AutoElement::addFontAttributes(const SkPaint& paint) { 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) { @@ -510,10 +540,7 @@ void SkSVGDevice::drawRRect(const SkDraw&, const SkRRect& rr, const SkPaint& pai 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, @@ -538,7 +565,7 @@ void SkSVGDevice::drawBitmapRect(const SkDraw&, const SkBitmap&, const SkRect* s 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)); @@ -550,7 +577,7 @@ void SkSVGDevice::drawPosText(const SkDraw& draw, const void* text, size_t len, SkASSERT(scalarsPerPos == 1 || scalarsPerPos == 2); AutoElement elem("text", fWriter, fResourceBucket, draw, paint); - elem.addFontAttributes(paint); + elem.addTextAttributes(paint); SkString xStr; SkString yStr; @@ -573,8 +600,38 @@ void SkSVGDevice::drawPosText(const SkDraw& draw, const void* text, size_t len, 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, |