| Index: src/svg/SkSVGDevice.cpp
|
| diff --git a/src/svg/SkSVGDevice.cpp b/src/svg/SkSVGDevice.cpp
|
| index 4b9f48983296837c24b6ea9e2838d84215e187d0..da52facad0b1ee9212dac0e003c145701a24e7c7 100644
|
| --- a/src/svg/SkSVGDevice.cpp
|
| +++ b/src/svg/SkSVGDevice.cpp
|
| @@ -101,71 +101,6 @@ static SkString svg_transform(const SkMatrix& t) {
|
| return tstr;
|
| }
|
|
|
| -static void append_escaped_unichar(SkUnichar c, SkString* text) {
|
| - switch(c) {
|
| - case '&':
|
| - text->append("&");
|
| - break;
|
| - case '"':
|
| - text->append(""");
|
| - break;
|
| - case '\'':
|
| - text->append("'");
|
| - break;
|
| - case '<':
|
| - text->append("<");
|
| - break;
|
| - case '>':
|
| - text->append(">");
|
| - break;
|
| - default:
|
| - text->appendUnichar(c);
|
| - break;
|
| - }
|
| -}
|
| -
|
| -static SkString svg_text(const void* text, size_t byteLen, const SkPaint& paint) {
|
| - SkString svgText;
|
| - int count = paint.countText(text, byteLen);
|
| -
|
| - switch(paint.getTextEncoding()) {
|
| - case SkPaint::kGlyphID_TextEncoding: {
|
| - SkASSERT(count * sizeof(uint16_t) == byteLen);
|
| - SkAutoSTArray<64, SkUnichar> unichars(count);
|
| - paint.glyphsToUnichars((const uint16_t*)text, count, unichars.get());
|
| - for (int i = 0; i < count; ++i) {
|
| - append_escaped_unichar(unichars[i], &svgText);
|
| - }
|
| - } break;
|
| - case SkPaint::kUTF8_TextEncoding: {
|
| - const char* c8 = reinterpret_cast<const char*>(text);
|
| - for (int i = 0; i < count; ++i) {
|
| - append_escaped_unichar(SkUTF8_NextUnichar(&c8), &svgText);
|
| - }
|
| - SkASSERT(reinterpret_cast<const char*>(text) + byteLen == c8);
|
| - } break;
|
| - case SkPaint::kUTF16_TextEncoding: {
|
| - const uint16_t* c16 = reinterpret_cast<const uint16_t*>(text);
|
| - for (int i = 0; i < count; ++i) {
|
| - append_escaped_unichar(SkUTF16_NextUnichar(&c16), &svgText);
|
| - }
|
| - SkASSERT(SkIsAlign2(byteLen));
|
| - SkASSERT(reinterpret_cast<const uint16_t*>(text) + (byteLen / 2) == c16);
|
| - } break;
|
| - case SkPaint::kUTF32_TextEncoding: {
|
| - SkASSERT(count * sizeof(uint32_t) == byteLen);
|
| - const uint32_t* c32 = reinterpret_cast<const uint32_t*>(text);
|
| - for (int i = 0; i < count; ++i) {
|
| - append_escaped_unichar(c32[i], &svgText);
|
| - }
|
| - } break;
|
| - default:
|
| - SkFAIL("unknown text encoding");
|
| - }
|
| -
|
| - return svgText;
|
| -}
|
| -
|
| uint32_t hash_family_string(const SkString& family) {
|
| // This is a lame hash function, but we don't really expect to see more than 1-2
|
| // family names under normal circumstances.
|
| @@ -180,6 +115,136 @@ struct Resources {
|
| SkString fClip;
|
| };
|
|
|
| +class SVGTextBuilder : SkNoncopyable {
|
| +public:
|
| + SVGTextBuilder(const void* text, size_t byteLen, const SkPaint& paint, const SkPoint& offset,
|
| + unsigned scalarsPerPos, const SkScalar pos[] = NULL)
|
| + : fOffset(offset)
|
| + , fScalarsPerPos(scalarsPerPos)
|
| + , fPos(pos)
|
| + , fLastCharWasWhitespace(true) // start off in whitespace mode to strip all leading space
|
| + {
|
| + SkASSERT(scalarsPerPos <= 2);
|
| + SkASSERT(scalarsPerPos == 0 || SkToBool(pos));
|
| +
|
| + int count = paint.countText(text, byteLen);
|
| +
|
| + switch(paint.getTextEncoding()) {
|
| + case SkPaint::kGlyphID_TextEncoding: {
|
| + SkASSERT(count * sizeof(uint16_t) == byteLen);
|
| + SkAutoSTArray<64, SkUnichar> unichars(count);
|
| + paint.glyphsToUnichars((const uint16_t*)text, count, unichars.get());
|
| + for (int i = 0; i < count; ++i) {
|
| + this->appendUnichar(unichars[i]);
|
| + }
|
| + } break;
|
| + case SkPaint::kUTF8_TextEncoding: {
|
| + const char* c8 = reinterpret_cast<const char*>(text);
|
| + for (int i = 0; i < count; ++i) {
|
| + this->appendUnichar(SkUTF8_NextUnichar(&c8));
|
| + }
|
| + SkASSERT(reinterpret_cast<const char*>(text) + byteLen == c8);
|
| + } break;
|
| + case SkPaint::kUTF16_TextEncoding: {
|
| + const uint16_t* c16 = reinterpret_cast<const uint16_t*>(text);
|
| + for (int i = 0; i < count; ++i) {
|
| + this->appendUnichar(SkUTF16_NextUnichar(&c16));
|
| + }
|
| + SkASSERT(SkIsAlign2(byteLen));
|
| + SkASSERT(reinterpret_cast<const uint16_t*>(text) + (byteLen / 2) == c16);
|
| + } break;
|
| + case SkPaint::kUTF32_TextEncoding: {
|
| + SkASSERT(count * sizeof(uint32_t) == byteLen);
|
| + const uint32_t* c32 = reinterpret_cast<const uint32_t*>(text);
|
| + for (int i = 0; i < count; ++i) {
|
| + this->appendUnichar(c32[i]);
|
| + }
|
| + } break;
|
| + default:
|
| + SkFAIL("unknown text encoding");
|
| + }
|
| +
|
| + if (scalarsPerPos < 2) {
|
| + SkASSERT(fPosY.isEmpty());
|
| + fPosY.appendScalar(offset.y()); // DrawText or DrawPosTextH (fixed Y).
|
| + }
|
| +
|
| + if (scalarsPerPos < 1) {
|
| + SkASSERT(fPosX.isEmpty());
|
| + fPosX.appendScalar(offset.x()); // DrawText (X also fixed).
|
| + }
|
| + }
|
| +
|
| + const SkString& text() const { return fText; }
|
| + const SkString& posX() const { return fPosX; }
|
| + const SkString& posY() const { return fPosY; }
|
| +
|
| +private:
|
| + void appendUnichar(SkUnichar c) {
|
| + bool discardPos = false;
|
| + bool isWhitespace = false;
|
| +
|
| + switch(c) {
|
| + case ' ':
|
| + case '\t':
|
| + // consolidate whitespace to match SVG's xml:space=default munging
|
| + // (http://www.w3.org/TR/SVG/text.html#WhiteSpace)
|
| + if (fLastCharWasWhitespace) {
|
| + discardPos = true;
|
| + } else {
|
| + fText.appendUnichar(c);
|
| + }
|
| + isWhitespace = true;
|
| + break;
|
| + case '\0':
|
| + // SkPaint::glyphsToUnichars() returns \0 for inconvertible glyphs, but these
|
| + // are not legal XML characters (http://www.w3.org/TR/REC-xml/#charsets)
|
| + discardPos = true;
|
| + isWhitespace = fLastCharWasWhitespace; // preserve whitespace consolidation
|
| + break;
|
| + case '&':
|
| + fText.append("&");
|
| + break;
|
| + case '"':
|
| + fText.append(""");
|
| + break;
|
| + case '\'':
|
| + fText.append("'");
|
| + break;
|
| + case '<':
|
| + fText.append("<");
|
| + break;
|
| + case '>':
|
| + fText.append(">");
|
| + break;
|
| + default:
|
| + fText.appendUnichar(c);
|
| + break;
|
| + }
|
| +
|
| + this->advancePos(discardPos);
|
| + fLastCharWasWhitespace = isWhitespace;
|
| + }
|
| +
|
| + void advancePos(bool discard) {
|
| + if (!discard && fScalarsPerPos > 0) {
|
| + fPosX.appendf("%.8g, ", fOffset.x() + fPos[0]);
|
| + if (fScalarsPerPos > 1) {
|
| + SkASSERT(fScalarsPerPos == 2);
|
| + fPosY.appendf("%.8g, ", fOffset.y() + fPos[1]);
|
| + }
|
| + }
|
| + fPos += fScalarsPerPos;
|
| + }
|
| +
|
| + const SkPoint& fOffset;
|
| + const unsigned fScalarsPerPos;
|
| + const SkScalar* fPos;
|
| +
|
| + SkString fText, fPosX, fPosY;
|
| + bool fLastCharWasWhitespace;
|
| +};
|
| +
|
| }
|
|
|
| // For now all this does is serve unique serial IDs, but it will eventually evolve to track
|
| @@ -585,9 +650,11 @@ 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.addTextAttributes(paint);
|
| - elem.addAttribute("x", x);
|
| - elem.addAttribute("y", y);
|
| - elem.addText(svg_text(text, len, paint));
|
| +
|
| + SVGTextBuilder builder(text, len, paint, SkPoint::Make(x, y), 0);
|
| + elem.addAttribute("x", builder.posX());
|
| + elem.addAttribute("y", builder.posY());
|
| + elem.addText(builder.text());
|
| }
|
|
|
| void SkSVGDevice::drawPosText(const SkDraw& draw, const void* text, size_t len,
|
| @@ -598,23 +665,10 @@ void SkSVGDevice::drawPosText(const SkDraw& draw, const void* text, size_t len,
|
| AutoElement elem("text", fWriter, fResourceBucket, draw, paint);
|
| elem.addTextAttributes(paint);
|
|
|
| - SkString xStr;
|
| - SkString yStr;
|
| - for (int i = 0; i < paint.countText(text, len); ++i) {
|
| - xStr.appendf("%.8g, ", offset.x() + pos[i * scalarsPerPos]);
|
| -
|
| - if (scalarsPerPos == 2) {
|
| - yStr.appendf("%.8g, ", offset.y() + pos[i * scalarsPerPos + 1]);
|
| - }
|
| - }
|
| -
|
| - if (scalarsPerPos != 2) {
|
| - yStr.appendScalar(offset.y());
|
| - }
|
| -
|
| - elem.addAttribute("x", xStr);
|
| - elem.addAttribute("y", yStr);
|
| - elem.addText(svg_text(text, len, paint));
|
| + SVGTextBuilder builder(text, len, paint, offset, scalarsPerPos, pos);
|
| + elem.addAttribute("x", builder.posX());
|
| + elem.addAttribute("y", builder.posY());
|
| + elem.addText(builder.text());
|
| }
|
|
|
| void SkSVGDevice::drawTextOnPath(const SkDraw&, const void* text, size_t len, const SkPath& path,
|
| @@ -648,7 +702,8 @@ void SkSVGDevice::drawTextOnPath(const SkDraw&, const void* text, size_t len, co
|
| paint.getTextAlign() == SkPaint::kCenter_Align ? "50%" : "100%");
|
| }
|
|
|
| - textPathElement.addText(svg_text(text, len, paint));
|
| + SVGTextBuilder builder(text, len, paint, SkPoint::Make(0, 0), 0);
|
| + textPathElement.addText(builder.text());
|
| }
|
| }
|
| }
|
|
|