Chromium Code Reviews| Index: src/svg/SkSVGDevice.cpp |
| diff --git a/src/svg/SkSVGDevice.cpp b/src/svg/SkSVGDevice.cpp |
| index 4b9f48983296837c24b6ea9e2838d84215e187d0..b41ac6433c9b841295e8f5850872c855334404ff 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,143 @@ 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"); |
| + } |
| + |
| + switch (scalarsPerPos) { |
|
mtklein
2015/02/18 19:55:44
This might be clearer as two if-statements:
if (s
f(malita)
2015/02/19 20:58:38
Done.
|
| + case 0: |
| + SkASSERT(fPosX.isEmpty()); |
| + fPosX.appendScalar(offset.x()); |
| + // fall through |
| + case 1: |
| + SkASSERT(fPosY.isEmpty()); |
| + fPosY.appendScalar(offset.y()); |
| + break; |
| + case 2: |
| + SkASSERT(fPosX.isEmpty() == fText.isEmpty()); |
| + SkASSERT(fPosY.isEmpty() == fText.isEmpty()); |
| + break; |
| + default: |
| + SkFAIL("illegal scalarsPer argument"); |
| + } |
| + } |
| + |
| + 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 inconcertible glyphs, but these |
|
mtklein
2015/02/18 19:55:44
inconvertible?
f(malita)
2015/02/19 20:58:38
Done.
|
| + // 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 +657,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 +672,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 +709,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()); |
| } |
| } |
| } |