Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(13)

Side by Side Diff: src/svg/SkSVGDevice.cpp

Issue 928583003: [SkSVGDevice] Fix whitespace text handling (Closed) Base URL: https://chromium.googlesource.com/skia.git@master
Patch Set: Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright 2015 Google Inc. 2 * Copyright 2015 Google Inc.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license that can be 4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file. 5 * found in the LICENSE file.
6 */ 6 */
7 7
8 #include "SkSVGDevice.h" 8 #include "SkSVGDevice.h"
9 9
10 #include "SkBitmap.h" 10 #include "SkBitmap.h"
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after
94 tstr.printf("matrix(%g %g %g %g %g %g)", 94 tstr.printf("matrix(%g %g %g %g %g %g)",
95 t.getScaleX(), t.getSkewY(), 95 t.getScaleX(), t.getSkewY(),
96 t.getSkewX(), t.getScaleY(), 96 t.getSkewX(), t.getScaleY(),
97 t.getTranslateX(), t.getTranslateY()); 97 t.getTranslateX(), t.getTranslateY());
98 break; 98 break;
99 } 99 }
100 100
101 return tstr; 101 return tstr;
102 } 102 }
103 103
104 static void append_escaped_unichar(SkUnichar c, SkString* text) {
105 switch(c) {
106 case '&':
107 text->append("&");
108 break;
109 case '"':
110 text->append(""");
111 break;
112 case '\'':
113 text->append("'");
114 break;
115 case '<':
116 text->append("&lt;");
117 break;
118 case '>':
119 text->append("&gt;");
120 break;
121 default:
122 text->appendUnichar(c);
123 break;
124 }
125 }
126
127 static SkString svg_text(const void* text, size_t byteLen, const SkPaint& paint) {
128 SkString svgText;
129 int count = paint.countText(text, byteLen);
130
131 switch(paint.getTextEncoding()) {
132 case SkPaint::kGlyphID_TextEncoding: {
133 SkASSERT(count * sizeof(uint16_t) == byteLen);
134 SkAutoSTArray<64, SkUnichar> unichars(count);
135 paint.glyphsToUnichars((const uint16_t*)text, count, unichars.get());
136 for (int i = 0; i < count; ++i) {
137 append_escaped_unichar(unichars[i], &svgText);
138 }
139 } break;
140 case SkPaint::kUTF8_TextEncoding: {
141 const char* c8 = reinterpret_cast<const char*>(text);
142 for (int i = 0; i < count; ++i) {
143 append_escaped_unichar(SkUTF8_NextUnichar(&c8), &svgText);
144 }
145 SkASSERT(reinterpret_cast<const char*>(text) + byteLen == c8);
146 } break;
147 case SkPaint::kUTF16_TextEncoding: {
148 const uint16_t* c16 = reinterpret_cast<const uint16_t*>(text);
149 for (int i = 0; i < count; ++i) {
150 append_escaped_unichar(SkUTF16_NextUnichar(&c16), &svgText);
151 }
152 SkASSERT(SkIsAlign2(byteLen));
153 SkASSERT(reinterpret_cast<const uint16_t*>(text) + (byteLen / 2) == c16) ;
154 } break;
155 case SkPaint::kUTF32_TextEncoding: {
156 SkASSERT(count * sizeof(uint32_t) == byteLen);
157 const uint32_t* c32 = reinterpret_cast<const uint32_t*>(text);
158 for (int i = 0; i < count; ++i) {
159 append_escaped_unichar(c32[i], &svgText);
160 }
161 } break;
162 default:
163 SkFAIL("unknown text encoding");
164 }
165
166 return svgText;
167 }
168
169 uint32_t hash_family_string(const SkString& family) { 104 uint32_t hash_family_string(const SkString& family) {
170 // This is a lame hash function, but we don't really expect to see more than 1-2 105 // This is a lame hash function, but we don't really expect to see more than 1-2
171 // family names under normal circumstances. 106 // family names under normal circumstances.
172 return SkChecksum::Mix(SkToU32(family.size())); 107 return SkChecksum::Mix(SkToU32(family.size()));
173 } 108 }
174 109
175 struct Resources { 110 struct Resources {
176 Resources(const SkPaint& paint) 111 Resources(const SkPaint& paint)
177 : fPaintServer(svg_color(paint.getColor())) {} 112 : fPaintServer(svg_color(paint.getColor())) {}
178 113
179 SkString fPaintServer; 114 SkString fPaintServer;
180 SkString fClip; 115 SkString fClip;
181 }; 116 };
182 117
118 class SVGTextBuilder : SkNoncopyable {
119 public:
120 SVGTextBuilder(const void* text, size_t byteLen, const SkPaint& paint, const SkPoint& offset,
121 unsigned scalarsPerPos, const SkScalar pos[] = NULL)
122 : fOffset(offset)
123 , fScalarsPerPos(scalarsPerPos)
124 , fPos(pos)
125 , fLastCharWasWhitespace(true) // start off in whitespace mode to strip all leading space
126 {
127 SkASSERT(scalarsPerPos <= 2);
128 SkASSERT(scalarsPerPos == 0 || SkToBool(pos));
129
130 int count = paint.countText(text, byteLen);
131
132 switch(paint.getTextEncoding()) {
133 case SkPaint::kGlyphID_TextEncoding: {
134 SkASSERT(count * sizeof(uint16_t) == byteLen);
135 SkAutoSTArray<64, SkUnichar> unichars(count);
136 paint.glyphsToUnichars((const uint16_t*)text, count, unichars.get()) ;
137 for (int i = 0; i < count; ++i) {
138 this->appendUnichar(unichars[i]);
139 }
140 } break;
141 case SkPaint::kUTF8_TextEncoding: {
142 const char* c8 = reinterpret_cast<const char*>(text);
143 for (int i = 0; i < count; ++i) {
144 this->appendUnichar(SkUTF8_NextUnichar(&c8));
145 }
146 SkASSERT(reinterpret_cast<const char*>(text) + byteLen == c8);
147 } break;
148 case SkPaint::kUTF16_TextEncoding: {
149 const uint16_t* c16 = reinterpret_cast<const uint16_t*>(text);
150 for (int i = 0; i < count; ++i) {
151 this->appendUnichar(SkUTF16_NextUnichar(&c16));
152 }
153 SkASSERT(SkIsAlign2(byteLen));
154 SkASSERT(reinterpret_cast<const uint16_t*>(text) + (byteLen / 2) == c16);
155 } break;
156 case SkPaint::kUTF32_TextEncoding: {
157 SkASSERT(count * sizeof(uint32_t) == byteLen);
158 const uint32_t* c32 = reinterpret_cast<const uint32_t*>(text);
159 for (int i = 0; i < count; ++i) {
160 this->appendUnichar(c32[i]);
161 }
162 } break;
163 default:
164 SkFAIL("unknown text encoding");
165 }
166
167 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.
168 case 0:
169 SkASSERT(fPosX.isEmpty());
170 fPosX.appendScalar(offset.x());
171 // fall through
172 case 1:
173 SkASSERT(fPosY.isEmpty());
174 fPosY.appendScalar(offset.y());
175 break;
176 case 2:
177 SkASSERT(fPosX.isEmpty() == fText.isEmpty());
178 SkASSERT(fPosY.isEmpty() == fText.isEmpty());
179 break;
180 default:
181 SkFAIL("illegal scalarsPer argument");
182 }
183 }
184
185 const SkString& text() const { return fText; }
186 const SkString& posX() const { return fPosX; }
187 const SkString& posY() const { return fPosY; }
188
189 private:
190 void appendUnichar(SkUnichar c) {
191 bool discardPos = false;
192 bool isWhitespace = false;
193
194 switch(c) {
195 case ' ':
196 case '\t':
197 // consolidate whitespace to match SVG's xml:space=default munging
198 // (http://www.w3.org/TR/SVG/text.html#WhiteSpace)
199 if (fLastCharWasWhitespace) {
200 discardPos = true;
201 } else {
202 fText.appendUnichar(c);
203 }
204 isWhitespace = true;
205 break;
206 case '\0':
207 // 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.
208 // are not legal XML characters (http://www.w3.org/TR/REC-xml/#chars ets)
209 discardPos = true;
210 isWhitespace = fLastCharWasWhitespace; // preserve whitespace consol idation
211 break;
212 case '&':
213 fText.append("&amp;");
214 break;
215 case '"':
216 fText.append("&quot;");
217 break;
218 case '\'':
219 fText.append("&apos;");
220 break;
221 case '<':
222 fText.append("&lt;");
223 break;
224 case '>':
225 fText.append("&gt;");
226 break;
227 default:
228 fText.appendUnichar(c);
229 break;
230 }
231
232 this->advancePos(discardPos);
233 fLastCharWasWhitespace = isWhitespace;
234 }
235
236 void advancePos(bool discard) {
237 if (!discard && fScalarsPerPos > 0) {
238 fPosX.appendf("%.8g, ", fOffset.x() + fPos[0]);
239 if (fScalarsPerPos > 1) {
240 SkASSERT(fScalarsPerPos == 2);
241 fPosY.appendf("%.8g, ", fOffset.y() + fPos[1]);
242 }
243 }
244 fPos += fScalarsPerPos;
245 }
246
247 const SkPoint& fOffset;
248 const unsigned fScalarsPerPos;
249 const SkScalar* fPos;
250
251 SkString fText, fPosX, fPosY;
252 bool fLastCharWasWhitespace;
253 };
254
183 } 255 }
184 256
185 // For now all this does is serve unique serial IDs, but it will eventually evol ve to track 257 // For now all this does is serve unique serial IDs, but it will eventually evol ve to track
186 // and deduplicate resources. 258 // and deduplicate resources.
187 class SkSVGDevice::ResourceBucket : ::SkNoncopyable { 259 class SkSVGDevice::ResourceBucket : ::SkNoncopyable {
188 public: 260 public:
189 ResourceBucket() : fGradientCount(0), fClipCount(0), fPathCount(0) {} 261 ResourceBucket() : fGradientCount(0), fClipCount(0), fPathCount(0) {}
190 262
191 SkString addLinearGradient() { 263 SkString addLinearGradient() {
192 return SkStringPrintf("gradient_%d", fGradientCount++); 264 return SkStringPrintf("gradient_%d", fGradientCount++);
(...skipping 385 matching lines...) Expand 10 before | Expand all | Expand 10 after
578 const SkRect& dst, const SkPaint& paint, 650 const SkRect& dst, const SkPaint& paint,
579 SkCanvas::DrawBitmapRectFlags flags) { 651 SkCanvas::DrawBitmapRectFlags flags) {
580 // todo 652 // todo
581 SkDebugf("unsupported operation: drawBitmapRect()\n"); 653 SkDebugf("unsupported operation: drawBitmapRect()\n");
582 } 654 }
583 655
584 void SkSVGDevice::drawText(const SkDraw& draw, const void* text, size_t len, 656 void SkSVGDevice::drawText(const SkDraw& draw, const void* text, size_t len,
585 SkScalar x, SkScalar y, const SkPaint& paint) { 657 SkScalar x, SkScalar y, const SkPaint& paint) {
586 AutoElement elem("text", fWriter, fResourceBucket, draw, paint); 658 AutoElement elem("text", fWriter, fResourceBucket, draw, paint);
587 elem.addTextAttributes(paint); 659 elem.addTextAttributes(paint);
588 elem.addAttribute("x", x); 660
589 elem.addAttribute("y", y); 661 SVGTextBuilder builder(text, len, paint, SkPoint::Make(x, y), 0);
590 elem.addText(svg_text(text, len, paint)); 662 elem.addAttribute("x", builder.posX());
663 elem.addAttribute("y", builder.posY());
664 elem.addText(builder.text());
591 } 665 }
592 666
593 void SkSVGDevice::drawPosText(const SkDraw& draw, const void* text, size_t len, 667 void SkSVGDevice::drawPosText(const SkDraw& draw, const void* text, size_t len,
594 const SkScalar pos[], int scalarsPerPos, const SkP oint& offset, 668 const SkScalar pos[], int scalarsPerPos, const SkP oint& offset,
595 const SkPaint& paint) { 669 const SkPaint& paint) {
596 SkASSERT(scalarsPerPos == 1 || scalarsPerPos == 2); 670 SkASSERT(scalarsPerPos == 1 || scalarsPerPos == 2);
597 671
598 AutoElement elem("text", fWriter, fResourceBucket, draw, paint); 672 AutoElement elem("text", fWriter, fResourceBucket, draw, paint);
599 elem.addTextAttributes(paint); 673 elem.addTextAttributes(paint);
600 674
601 SkString xStr; 675 SVGTextBuilder builder(text, len, paint, offset, scalarsPerPos, pos);
602 SkString yStr; 676 elem.addAttribute("x", builder.posX());
603 for (int i = 0; i < paint.countText(text, len); ++i) { 677 elem.addAttribute("y", builder.posY());
604 xStr.appendf("%.8g, ", offset.x() + pos[i * scalarsPerPos]); 678 elem.addText(builder.text());
605
606 if (scalarsPerPos == 2) {
607 yStr.appendf("%.8g, ", offset.y() + pos[i * scalarsPerPos + 1]);
608 }
609 }
610
611 if (scalarsPerPos != 2) {
612 yStr.appendScalar(offset.y());
613 }
614
615 elem.addAttribute("x", xStr);
616 elem.addAttribute("y", yStr);
617 elem.addText(svg_text(text, len, paint));
618 } 679 }
619 680
620 void SkSVGDevice::drawTextOnPath(const SkDraw&, const void* text, size_t len, co nst SkPath& path, 681 void SkSVGDevice::drawTextOnPath(const SkDraw&, const void* text, size_t len, co nst SkPath& path,
621 const SkMatrix* matrix, const SkPaint& paint) { 682 const SkMatrix* matrix, const SkPaint& paint) {
622 SkString pathID = fResourceBucket->addPath(); 683 SkString pathID = fResourceBucket->addPath();
623 684
624 { 685 {
625 AutoElement defs("defs", fWriter); 686 AutoElement defs("defs", fWriter);
626 AutoElement pathElement("path", fWriter); 687 AutoElement pathElement("path", fWriter);
627 pathElement.addAttribute("id", pathID); 688 pathElement.addAttribute("id", pathID);
(...skipping 13 matching lines...) Expand all
641 AutoElement textPathElement("textPath", fWriter); 702 AutoElement textPathElement("textPath", fWriter);
642 textPathElement.addAttribute("xlink:href", SkStringPrintf("#%s", pat hID.c_str())); 703 textPathElement.addAttribute("xlink:href", SkStringPrintf("#%s", pat hID.c_str()));
643 704
644 if (paint.getTextAlign() != SkPaint::kLeft_Align) { 705 if (paint.getTextAlign() != SkPaint::kLeft_Align) {
645 SkASSERT(paint.getTextAlign() == SkPaint::kCenter_Align || 706 SkASSERT(paint.getTextAlign() == SkPaint::kCenter_Align ||
646 paint.getTextAlign() == SkPaint::kRight_Align); 707 paint.getTextAlign() == SkPaint::kRight_Align);
647 textPathElement.addAttribute("startOffset", 708 textPathElement.addAttribute("startOffset",
648 paint.getTextAlign() == SkPaint::kCenter_Align ? "50%" : "10 0%"); 709 paint.getTextAlign() == SkPaint::kCenter_Align ? "50%" : "10 0%");
649 } 710 }
650 711
651 textPathElement.addText(svg_text(text, len, paint)); 712 SVGTextBuilder builder(text, len, paint, SkPoint::Make(0, 0), 0);
713 textPathElement.addText(builder.text());
652 } 714 }
653 } 715 }
654 } 716 }
655 717
656 void SkSVGDevice::drawVertices(const SkDraw&, SkCanvas::VertexMode, int vertexCo unt, 718 void SkSVGDevice::drawVertices(const SkDraw&, SkCanvas::VertexMode, int vertexCo unt,
657 const SkPoint verts[], const SkPoint texs[], 719 const SkPoint verts[], const SkPoint texs[],
658 const SkColor colors[], SkXfermode* xmode, 720 const SkColor colors[], SkXfermode* xmode,
659 const uint16_t indices[], int indexCount, 721 const uint16_t indices[], int indexCount,
660 const SkPaint& paint) { 722 const SkPaint& paint) {
661 // todo 723 // todo
662 SkDebugf("unsupported operation: drawVertices()\n"); 724 SkDebugf("unsupported operation: drawVertices()\n");
663 } 725 }
664 726
665 void SkSVGDevice::drawDevice(const SkDraw&, SkBaseDevice*, int x, int y, 727 void SkSVGDevice::drawDevice(const SkDraw&, SkBaseDevice*, int x, int y,
666 const SkPaint&) { 728 const SkPaint&) {
667 // todo 729 // todo
668 SkDebugf("unsupported operation: drawDevice()\n"); 730 SkDebugf("unsupported operation: drawDevice()\n");
669 } 731 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698