1 /*
2  * Copyright 2019 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "src/utils/SkShaperJSONWriter.h"
9 
10 #include <algorithm>
11 #include <limits>
12 #include <string>
13 
14 #include "src/utils/SkJSONWriter.h"
15 #include "src/utils/SkUTF.h"
16 
SkShaperJSONWriter(SkJSONWriter * JSONWriter,const char * utf8,size_t size)17 SkShaperJSONWriter::SkShaperJSONWriter(SkJSONWriter* JSONWriter, const char* utf8, size_t size)
18         : fJSONWriter{JSONWriter}
19         , fUTF8{utf8, size} {}
20 
beginLine()21 void SkShaperJSONWriter::beginLine() { }
22 
runInfo(const SkShaper::RunHandler::RunInfo & info)23 void SkShaperJSONWriter::runInfo(const SkShaper::RunHandler::RunInfo& info) { }
24 
commitRunInfo()25 void SkShaperJSONWriter::commitRunInfo() { }
26 
27 SkShaper::RunHandler::Buffer
runBuffer(const SkShaper::RunHandler::RunInfo & info)28 SkShaperJSONWriter::runBuffer(const SkShaper::RunHandler::RunInfo& info) {
29     fGlyphs.resize(info.glyphCount);
30     fPositions.resize(info.glyphCount);
31     fClusters.resize(info.glyphCount);
32     return {fGlyphs.data(), fPositions.data(), nullptr, fClusters.data(), {0, 0}};
33 }
34 
is_one_to_one(const char utf8[],size_t utf8Begin,size_t utf8End,std::vector<uint32_t> & clusters)35 static bool is_one_to_one(const char utf8[], size_t utf8Begin, size_t utf8End,
36         std::vector<uint32_t>& clusters) {
37     size_t lastUtf8Index = utf8End;
38 
39     auto checkCluster = [&](size_t clusterIndex) {
40         if (clusters[clusterIndex] >= lastUtf8Index) {
41             return false;
42         }
43         size_t utf8ClusterSize = lastUtf8Index - clusters[clusterIndex];
44         if (SkUTF::CountUTF8(&utf8[clusters[clusterIndex]], utf8ClusterSize) != 1) {
45             return false;
46         }
47         lastUtf8Index = clusters[clusterIndex];
48         return true;
49     };
50 
51     if (clusters.front() <= clusters.back()) {
52         // left-to-right clusters
53         size_t clusterCursor = clusters.size();
54         while (clusterCursor > 0) {
55             if (!checkCluster(--clusterCursor)) { return false; }
56         }
57     } else {
58         // right-to-left clusters
59         size_t clusterCursor = 0;
60         while (clusterCursor < clusters.size()) {
61             if (!checkCluster(clusterCursor++)) { return false; }
62         }
63     }
64 
65     return true;
66 }
67 
commitRunBuffer(const SkShaper::RunHandler::RunInfo & info)68 void SkShaperJSONWriter::commitRunBuffer(const SkShaper::RunHandler::RunInfo& info) {
69     fJSONWriter->beginObject("run", true);
70 
71     // Font name
72     SkString fontName;
73     info.fFont.getTypeface()->getFamilyName(&fontName);
74     fJSONWriter->appendString("font name", fontName.c_str());
75 
76     // Font size
77     fJSONWriter->appendFloat("font size", info.fFont.getSize());
78 
79     if (info.fBidiLevel > 0) {
80         std::string bidiType = info.fBidiLevel % 2 == 0 ? "left-to-right" : "right-to-left";
81         std::string bidiOutput = bidiType + " lvl " + std::to_string(info.fBidiLevel);
82         fJSONWriter->appendString("BiDi", bidiOutput.c_str());
83     }
84 
85     if (is_one_to_one(fUTF8.c_str(), info.utf8Range.begin(), info.utf8Range.end(), fClusters)) {
86         std::string utf8{&fUTF8[info.utf8Range.begin()], info.utf8Range.size()};
87         fJSONWriter->appendString("UTF8", utf8.c_str());
88 
89         fJSONWriter->beginArray("glyphs", false);
90         for (auto glyphID : fGlyphs) {
91             fJSONWriter->appendU32(glyphID);
92         }
93         fJSONWriter->endArray();
94 
95         fJSONWriter->beginArray("clusters", false);
96         for (auto cluster : fClusters) {
97             fJSONWriter->appendU32(cluster);
98         }
99         fJSONWriter->endArray();
100     } else {
101         VisualizeClusters(fUTF8.c_str(),
102                           info.utf8Range.begin(), info.utf8Range.end(),
103                           SkMakeSpan(fGlyphs),
104                           SkMakeSpan(fClusters),
105                           [this](size_t codePointCount, SkSpan<const char> utf1to1,
106                                  SkSpan<const SkGlyphID> glyph1to1) {
107                               this->displayMToN(codePointCount, utf1to1, glyph1to1);
108                           });
109     }
110 
111     if (info.glyphCount > 1) {
112         fJSONWriter->beginArray("horizontal positions", false);
113         for (auto position : fPositions) {
114             fJSONWriter->appendFloat(position.x());
115         }
116         fJSONWriter->endArray();
117     }
118 
119     fJSONWriter->beginArray("advances", false);
120     for (size_t i = 1; i < info.glyphCount; i++) {
121         fJSONWriter->appendFloat(fPositions[i].fX - fPositions[i-1].fX);
122     }
123     SkPoint lastAdvance = info.fAdvance - (fPositions.back() - fPositions.front());
124     fJSONWriter->appendFloat(lastAdvance.fX);
125     fJSONWriter->endArray();
126 
127     fJSONWriter->endObject();
128 }
129 
BreakupClusters(size_t utf8Begin,size_t utf8End,SkSpan<const uint32_t> clusters,const BreakupCluastersCallback & processMToN)130 void SkShaperJSONWriter::BreakupClusters(size_t utf8Begin, size_t utf8End,
131                                          SkSpan<const uint32_t> clusters,
132                                          const BreakupCluastersCallback& processMToN) {
133 
134     if (clusters.front() <= clusters.back()) {
135         // Handle left-to-right text direction
136         size_t glyphStartIndex = 0;
137         for (size_t glyphEndIndex = 0; glyphEndIndex < clusters.size(); glyphEndIndex++) {
138 
139             if (clusters[glyphStartIndex] == clusters[glyphEndIndex]) { continue; }
140 
141             processMToN(glyphStartIndex, glyphEndIndex,
142                         clusters[glyphStartIndex], clusters[glyphEndIndex]);
143 
144             glyphStartIndex = glyphEndIndex;
145         }
146 
147         processMToN(glyphStartIndex, clusters.size(), clusters[glyphStartIndex], utf8End);
148 
149     } else {
150         // Handle right-to-left text direction.
151         SkASSERT(clusters.size() >= 2);
152         size_t glyphStartIndex = 0;
153         uint32_t utf8EndIndex = utf8End;
154         for (size_t glyphEndIndex = 0; glyphEndIndex < clusters.size(); glyphEndIndex++) {
155 
156             if (clusters[glyphStartIndex] == clusters[glyphEndIndex]) { continue; }
157 
158             processMToN(glyphStartIndex, glyphEndIndex,
159                         clusters[glyphStartIndex], utf8EndIndex);
160 
161             utf8EndIndex = clusters[glyphStartIndex];
162             glyphStartIndex = glyphEndIndex;
163         }
164         processMToN(glyphStartIndex, clusters.size(), utf8Begin, clusters[glyphStartIndex-1]);
165     }
166 }
167 
VisualizeClusters(const char * utf8,size_t utf8Begin,size_t utf8End,SkSpan<const SkGlyphID> glyphIDs,SkSpan<const uint32_t> clusters,const VisualizeClustersCallback & processMToN)168 void SkShaperJSONWriter::VisualizeClusters(const char* utf8, size_t utf8Begin, size_t utf8End,
169                                            SkSpan<const SkGlyphID> glyphIDs,
170                                            SkSpan<const uint32_t> clusters,
171                                            const VisualizeClustersCallback& processMToN) {
172 
173     size_t glyphRangeStart, glyphRangeEnd;
174     uint32_t utf8RangeStart, utf8RangeEnd;
175 
176     auto resetRanges = [&]() {
177         glyphRangeStart = std::numeric_limits<size_t>::max();
178         glyphRangeEnd   = 0;
179         utf8RangeStart  = std::numeric_limits<uint32_t>::max();
180         utf8RangeEnd    = 0;
181     };
182 
183     auto checkRangesAndProcess = [&]() {
184         if (glyphRangeStart < glyphRangeEnd) {
185             size_t glyphRangeCount = glyphRangeEnd - glyphRangeStart;
186             SkSpan<const char> utf8Span{&utf8[utf8RangeStart], utf8RangeEnd - utf8RangeStart};
187             SkSpan<const SkGlyphID> glyphSpan{&glyphIDs[glyphRangeStart], glyphRangeCount};
188 
189             // Glyph count is the same as codepoint count for 1:1.
190             processMToN(glyphRangeCount, utf8Span, glyphSpan);
191         }
192         resetRanges();
193     };
194 
195     auto gatherRuns = [&](size_t glyphStartIndex, size_t glyphEndIndex,
196                           uint32_t utf8StartIndex, uint32_t utf8EndIndex) {
197         int possibleCount = SkUTF::CountUTF8(&utf8[utf8StartIndex], utf8EndIndex - utf8StartIndex);
198         if (possibleCount == -1) { return; }
199         size_t codePointCount = SkTo<size_t>(possibleCount);
200         if (codePointCount == 1 && glyphEndIndex - glyphStartIndex == 1) {
201             glyphRangeStart = std::min(glyphRangeStart, glyphStartIndex);
202             glyphRangeEnd   = std::max(glyphRangeEnd,   glyphEndIndex  );
203             utf8RangeStart  = std::min(utf8RangeStart,  utf8StartIndex );
204             utf8RangeEnd    = std::max(utf8RangeEnd,    utf8EndIndex   );
205         } else {
206             checkRangesAndProcess();
207 
208             SkSpan<const char> utf8Span{&utf8[utf8StartIndex], utf8EndIndex - utf8StartIndex};
209             SkSpan<const SkGlyphID> glyphSpan{&glyphIDs[glyphStartIndex],
210                                               glyphEndIndex - glyphStartIndex};
211 
212             processMToN(codePointCount, utf8Span, glyphSpan);
213         }
214     };
215 
216     resetRanges();
217     BreakupClusters(utf8Begin, utf8End, clusters, gatherRuns);
218     checkRangesAndProcess();
219 }
220 
displayMToN(size_t codePointCount,SkSpan<const char> utf8,SkSpan<const SkGlyphID> glyphIDs)221 void SkShaperJSONWriter::displayMToN(size_t codePointCount,
222                                      SkSpan<const char> utf8,
223                                      SkSpan<const SkGlyphID> glyphIDs) {
224     std::string nString = std::to_string(codePointCount);
225     std::string mString = std::to_string(glyphIDs.size());
226     std::string clusterName = "cluster " + nString + " to " + mString;
227     fJSONWriter->beginObject(clusterName.c_str(), true);
228     std::string utf8String{utf8.data(), utf8.size()};
229     fJSONWriter->appendString("UTF", utf8String.c_str());
230     fJSONWriter->beginArray("glyphsIDs", false);
231     for (auto glyphID : glyphIDs) {
232         fJSONWriter->appendU32(glyphID);
233     }
234     fJSONWriter->endArray();
235     fJSONWriter->endObject();
236 }
237