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

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: review comments 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 if (scalarsPerPos < 2) {
168 SkASSERT(fPosY.isEmpty());
169 fPosY.appendScalar(offset.y()); // DrawText or DrawPosTextH (fixed Y ).
170 }
171
172 if (scalarsPerPos < 1) {
173 SkASSERT(fPosX.isEmpty());
174 fPosX.appendScalar(offset.x()); // DrawText (X also fixed).
175 }
176 }
177
178 const SkString& text() const { return fText; }
179 const SkString& posX() const { return fPosX; }
180 const SkString& posY() const { return fPosY; }
181
182 private:
183 void appendUnichar(SkUnichar c) {
184 bool discardPos = false;
185 bool isWhitespace = false;
186
187 switch(c) {
188 case ' ':
189 case '\t':
190 // consolidate whitespace to match SVG's xml:space=default munging
191 // (http://www.w3.org/TR/SVG/text.html#WhiteSpace)
192 if (fLastCharWasWhitespace) {
193 discardPos = true;
194 } else {
195 fText.appendUnichar(c);
196 }
197 isWhitespace = true;
198 break;
199 case '\0':
200 // SkPaint::glyphsToUnichars() returns \0 for inconvertible glyphs, but these
201 // are not legal XML characters (http://www.w3.org/TR/REC-xml/#chars ets)
202 discardPos = true;
203 isWhitespace = fLastCharWasWhitespace; // preserve whitespace consol idation
204 break;
205 case '&':
206 fText.append("&amp;");
207 break;
208 case '"':
209 fText.append("&quot;");
210 break;
211 case '\'':
212 fText.append("&apos;");
213 break;
214 case '<':
215 fText.append("&lt;");
216 break;
217 case '>':
218 fText.append("&gt;");
219 break;
220 default:
221 fText.appendUnichar(c);
222 break;
223 }
224
225 this->advancePos(discardPos);
226 fLastCharWasWhitespace = isWhitespace;
227 }
228
229 void advancePos(bool discard) {
230 if (!discard && fScalarsPerPos > 0) {
231 fPosX.appendf("%.8g, ", fOffset.x() + fPos[0]);
232 if (fScalarsPerPos > 1) {
233 SkASSERT(fScalarsPerPos == 2);
234 fPosY.appendf("%.8g, ", fOffset.y() + fPos[1]);
235 }
236 }
237 fPos += fScalarsPerPos;
238 }
239
240 const SkPoint& fOffset;
241 const unsigned fScalarsPerPos;
242 const SkScalar* fPos;
243
244 SkString fText, fPosX, fPosY;
245 bool fLastCharWasWhitespace;
246 };
247
183 } 248 }
184 249
185 // For now all this does is serve unique serial IDs, but it will eventually evol ve to track 250 // For now all this does is serve unique serial IDs, but it will eventually evol ve to track
186 // and deduplicate resources. 251 // and deduplicate resources.
187 class SkSVGDevice::ResourceBucket : ::SkNoncopyable { 252 class SkSVGDevice::ResourceBucket : ::SkNoncopyable {
188 public: 253 public:
189 ResourceBucket() : fGradientCount(0), fClipCount(0), fPathCount(0) {} 254 ResourceBucket() : fGradientCount(0), fClipCount(0), fPathCount(0) {}
190 255
191 SkString addLinearGradient() { 256 SkString addLinearGradient() {
192 return SkStringPrintf("gradient_%d", fGradientCount++); 257 return SkStringPrintf("gradient_%d", fGradientCount++);
(...skipping 385 matching lines...) Expand 10 before | Expand all | Expand 10 after
578 const SkRect& dst, const SkPaint& paint, 643 const SkRect& dst, const SkPaint& paint,
579 SkCanvas::DrawBitmapRectFlags flags) { 644 SkCanvas::DrawBitmapRectFlags flags) {
580 // todo 645 // todo
581 SkDebugf("unsupported operation: drawBitmapRect()\n"); 646 SkDebugf("unsupported operation: drawBitmapRect()\n");
582 } 647 }
583 648
584 void SkSVGDevice::drawText(const SkDraw& draw, const void* text, size_t len, 649 void SkSVGDevice::drawText(const SkDraw& draw, const void* text, size_t len,
585 SkScalar x, SkScalar y, const SkPaint& paint) { 650 SkScalar x, SkScalar y, const SkPaint& paint) {
586 AutoElement elem("text", fWriter, fResourceBucket, draw, paint); 651 AutoElement elem("text", fWriter, fResourceBucket, draw, paint);
587 elem.addTextAttributes(paint); 652 elem.addTextAttributes(paint);
588 elem.addAttribute("x", x); 653
589 elem.addAttribute("y", y); 654 SVGTextBuilder builder(text, len, paint, SkPoint::Make(x, y), 0);
590 elem.addText(svg_text(text, len, paint)); 655 elem.addAttribute("x", builder.posX());
656 elem.addAttribute("y", builder.posY());
657 elem.addText(builder.text());
591 } 658 }
592 659
593 void SkSVGDevice::drawPosText(const SkDraw& draw, const void* text, size_t len, 660 void SkSVGDevice::drawPosText(const SkDraw& draw, const void* text, size_t len,
594 const SkScalar pos[], int scalarsPerPos, const SkP oint& offset, 661 const SkScalar pos[], int scalarsPerPos, const SkP oint& offset,
595 const SkPaint& paint) { 662 const SkPaint& paint) {
596 SkASSERT(scalarsPerPos == 1 || scalarsPerPos == 2); 663 SkASSERT(scalarsPerPos == 1 || scalarsPerPos == 2);
597 664
598 AutoElement elem("text", fWriter, fResourceBucket, draw, paint); 665 AutoElement elem("text", fWriter, fResourceBucket, draw, paint);
599 elem.addTextAttributes(paint); 666 elem.addTextAttributes(paint);
600 667
601 SkString xStr; 668 SVGTextBuilder builder(text, len, paint, offset, scalarsPerPos, pos);
602 SkString yStr; 669 elem.addAttribute("x", builder.posX());
603 for (int i = 0; i < paint.countText(text, len); ++i) { 670 elem.addAttribute("y", builder.posY());
604 xStr.appendf("%.8g, ", offset.x() + pos[i * scalarsPerPos]); 671 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 } 672 }
619 673
620 void SkSVGDevice::drawTextOnPath(const SkDraw&, const void* text, size_t len, co nst SkPath& path, 674 void SkSVGDevice::drawTextOnPath(const SkDraw&, const void* text, size_t len, co nst SkPath& path,
621 const SkMatrix* matrix, const SkPaint& paint) { 675 const SkMatrix* matrix, const SkPaint& paint) {
622 SkString pathID = fResourceBucket->addPath(); 676 SkString pathID = fResourceBucket->addPath();
623 677
624 { 678 {
625 AutoElement defs("defs", fWriter); 679 AutoElement defs("defs", fWriter);
626 AutoElement pathElement("path", fWriter); 680 AutoElement pathElement("path", fWriter);
627 pathElement.addAttribute("id", pathID); 681 pathElement.addAttribute("id", pathID);
(...skipping 13 matching lines...) Expand all
641 AutoElement textPathElement("textPath", fWriter); 695 AutoElement textPathElement("textPath", fWriter);
642 textPathElement.addAttribute("xlink:href", SkStringPrintf("#%s", pat hID.c_str())); 696 textPathElement.addAttribute("xlink:href", SkStringPrintf("#%s", pat hID.c_str()));
643 697
644 if (paint.getTextAlign() != SkPaint::kLeft_Align) { 698 if (paint.getTextAlign() != SkPaint::kLeft_Align) {
645 SkASSERT(paint.getTextAlign() == SkPaint::kCenter_Align || 699 SkASSERT(paint.getTextAlign() == SkPaint::kCenter_Align ||
646 paint.getTextAlign() == SkPaint::kRight_Align); 700 paint.getTextAlign() == SkPaint::kRight_Align);
647 textPathElement.addAttribute("startOffset", 701 textPathElement.addAttribute("startOffset",
648 paint.getTextAlign() == SkPaint::kCenter_Align ? "50%" : "10 0%"); 702 paint.getTextAlign() == SkPaint::kCenter_Align ? "50%" : "10 0%");
649 } 703 }
650 704
651 textPathElement.addText(svg_text(text, len, paint)); 705 SVGTextBuilder builder(text, len, paint, SkPoint::Make(0, 0), 0);
706 textPathElement.addText(builder.text());
652 } 707 }
653 } 708 }
654 } 709 }
655 710
656 void SkSVGDevice::drawVertices(const SkDraw&, SkCanvas::VertexMode, int vertexCo unt, 711 void SkSVGDevice::drawVertices(const SkDraw&, SkCanvas::VertexMode, int vertexCo unt,
657 const SkPoint verts[], const SkPoint texs[], 712 const SkPoint verts[], const SkPoint texs[],
658 const SkColor colors[], SkXfermode* xmode, 713 const SkColor colors[], SkXfermode* xmode,
659 const uint16_t indices[], int indexCount, 714 const uint16_t indices[], int indexCount,
660 const SkPaint& paint) { 715 const SkPaint& paint) {
661 // todo 716 // todo
662 SkDebugf("unsupported operation: drawVertices()\n"); 717 SkDebugf("unsupported operation: drawVertices()\n");
663 } 718 }
664 719
665 void SkSVGDevice::drawDevice(const SkDraw&, SkBaseDevice*, int x, int y, 720 void SkSVGDevice::drawDevice(const SkDraw&, SkBaseDevice*, int x, int y,
666 const SkPaint&) { 721 const SkPaint&) {
667 // todo 722 // todo
668 SkDebugf("unsupported operation: drawDevice()\n"); 723 SkDebugf("unsupported operation: drawDevice()\n");
669 } 724 }
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