1*834a2baaSAndroid Build Coastguard Worker /*
2*834a2baaSAndroid Build Coastguard Worker * Copyright (C) 2018 The Android Open Source Project
3*834a2baaSAndroid Build Coastguard Worker *
4*834a2baaSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*834a2baaSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*834a2baaSAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*834a2baaSAndroid Build Coastguard Worker *
8*834a2baaSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*834a2baaSAndroid Build Coastguard Worker *
10*834a2baaSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*834a2baaSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*834a2baaSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*834a2baaSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*834a2baaSAndroid Build Coastguard Worker * limitations under the License.
15*834a2baaSAndroid Build Coastguard Worker */
16*834a2baaSAndroid Build Coastguard Worker
17*834a2baaSAndroid Build Coastguard Worker #include <gtest/gtest.h>
18*834a2baaSAndroid Build Coastguard Worker
19*834a2baaSAndroid Build Coastguard Worker #include "minikin/Hyphenator.h"
20*834a2baaSAndroid Build Coastguard Worker #include "minikin/LineBreaker.h"
21*834a2baaSAndroid Build Coastguard Worker
22*834a2baaSAndroid Build Coastguard Worker #include "LocaleListCache.h"
23*834a2baaSAndroid Build Coastguard Worker #include "MinikinInternal.h"
24*834a2baaSAndroid Build Coastguard Worker #include "UnicodeUtils.h"
25*834a2baaSAndroid Build Coastguard Worker
26*834a2baaSAndroid Build Coastguard Worker namespace minikin {
27*834a2baaSAndroid Build Coastguard Worker namespace line_breaker_test_helper {
28*834a2baaSAndroid Build Coastguard Worker
29*834a2baaSAndroid Build Coastguard Worker class RectangleLineWidth : public LineWidth {
30*834a2baaSAndroid Build Coastguard Worker public:
RectangleLineWidth(float width)31*834a2baaSAndroid Build Coastguard Worker RectangleLineWidth(float width) : mWidth(width) {}
~RectangleLineWidth()32*834a2baaSAndroid Build Coastguard Worker virtual ~RectangleLineWidth() {}
33*834a2baaSAndroid Build Coastguard Worker
getAt(size_t)34*834a2baaSAndroid Build Coastguard Worker float getAt(size_t) const override { return mWidth; }
getMin()35*834a2baaSAndroid Build Coastguard Worker float getMin() const override { return mWidth; }
36*834a2baaSAndroid Build Coastguard Worker
37*834a2baaSAndroid Build Coastguard Worker private:
38*834a2baaSAndroid Build Coastguard Worker float mWidth;
39*834a2baaSAndroid Build Coastguard Worker };
40*834a2baaSAndroid Build Coastguard Worker
41*834a2baaSAndroid Build Coastguard Worker // The run implemenataion for returning the same width for all characters.
42*834a2baaSAndroid Build Coastguard Worker class ConstantRun : public Run {
43*834a2baaSAndroid Build Coastguard Worker public:
ConstantRun(const Range & range,const std::string & lang,float width,float ascent,float descent)44*834a2baaSAndroid Build Coastguard Worker ConstantRun(const Range& range, const std::string& lang, float width, float ascent,
45*834a2baaSAndroid Build Coastguard Worker float descent)
46*834a2baaSAndroid Build Coastguard Worker : Run(range),
47*834a2baaSAndroid Build Coastguard Worker mPaint(nullptr /* font collection */),
48*834a2baaSAndroid Build Coastguard Worker mWidth(width),
49*834a2baaSAndroid Build Coastguard Worker mAscent(ascent),
50*834a2baaSAndroid Build Coastguard Worker mDescent(descent) {
51*834a2baaSAndroid Build Coastguard Worker mLocaleListId = LocaleListCache::getId(lang);
52*834a2baaSAndroid Build Coastguard Worker }
53*834a2baaSAndroid Build Coastguard Worker
isRtl()54*834a2baaSAndroid Build Coastguard Worker virtual bool isRtl() const override { return false; }
canBreak()55*834a2baaSAndroid Build Coastguard Worker virtual bool canBreak() const override { return true; }
canHyphenate()56*834a2baaSAndroid Build Coastguard Worker virtual bool canHyphenate() const override { return true; }
getLocaleListId()57*834a2baaSAndroid Build Coastguard Worker virtual uint32_t getLocaleListId() const { return mLocaleListId; }
58*834a2baaSAndroid Build Coastguard Worker
getMetrics(const U16StringPiece &,std::vector<float> * advances,std::vector<uint8_t> *,LayoutPieces *,bool,LayoutPieces *)59*834a2baaSAndroid Build Coastguard Worker virtual void getMetrics(const U16StringPiece&, std::vector<float>* advances,
60*834a2baaSAndroid Build Coastguard Worker std::vector<uint8_t>* /*flags*/, LayoutPieces*,
61*834a2baaSAndroid Build Coastguard Worker bool /*boundsCalculation*/, LayoutPieces*) const {
62*834a2baaSAndroid Build Coastguard Worker std::fill(advances->begin() + mRange.getStart(), advances->begin() + mRange.getEnd(),
63*834a2baaSAndroid Build Coastguard Worker mWidth);
64*834a2baaSAndroid Build Coastguard Worker }
65*834a2baaSAndroid Build Coastguard Worker
getBounds(const U16StringPiece &,const Range &,const LayoutPieces &)66*834a2baaSAndroid Build Coastguard Worker virtual std::pair<float, MinikinRect> getBounds(const U16StringPiece& /* text */,
67*834a2baaSAndroid Build Coastguard Worker const Range& /* range */,
68*834a2baaSAndroid Build Coastguard Worker const LayoutPieces& /* pieces */) const {
69*834a2baaSAndroid Build Coastguard Worker return std::make_pair(mWidth, MinikinRect());
70*834a2baaSAndroid Build Coastguard Worker }
71*834a2baaSAndroid Build Coastguard Worker
getExtent(const U16StringPiece &,const Range &,const LayoutPieces &)72*834a2baaSAndroid Build Coastguard Worker virtual MinikinExtent getExtent(const U16StringPiece& /* text */, const Range& /* range */,
73*834a2baaSAndroid Build Coastguard Worker const LayoutPieces& /* pieces */) const override {
74*834a2baaSAndroid Build Coastguard Worker return {mAscent, mDescent};
75*834a2baaSAndroid Build Coastguard Worker }
76*834a2baaSAndroid Build Coastguard Worker
getLineMetrics(const U16StringPiece & text,const Range & range,const LayoutPieces & pieces)77*834a2baaSAndroid Build Coastguard Worker virtual LineMetrics getLineMetrics(const U16StringPiece& text, const Range& range,
78*834a2baaSAndroid Build Coastguard Worker const LayoutPieces& pieces) const {
79*834a2baaSAndroid Build Coastguard Worker auto [adv, rect] = getBounds(text, range, pieces);
80*834a2baaSAndroid Build Coastguard Worker return LineMetrics(getExtent(text, range, pieces), rect, adv);
81*834a2baaSAndroid Build Coastguard Worker }
82*834a2baaSAndroid Build Coastguard Worker
getPaint()83*834a2baaSAndroid Build Coastguard Worker virtual const MinikinPaint* getPaint() const { return &mPaint; }
84*834a2baaSAndroid Build Coastguard Worker
measureHyphenPiece(const U16StringPiece &,const Range & range,StartHyphenEdit start,EndHyphenEdit end,LayoutPieces *)85*834a2baaSAndroid Build Coastguard Worker virtual float measureHyphenPiece(const U16StringPiece&, const Range& range,
86*834a2baaSAndroid Build Coastguard Worker StartHyphenEdit start, EndHyphenEdit end,
87*834a2baaSAndroid Build Coastguard Worker LayoutPieces*) const {
88*834a2baaSAndroid Build Coastguard Worker uint32_t extraCharForHyphen = 0;
89*834a2baaSAndroid Build Coastguard Worker if (isInsertion(start)) {
90*834a2baaSAndroid Build Coastguard Worker extraCharForHyphen++;
91*834a2baaSAndroid Build Coastguard Worker }
92*834a2baaSAndroid Build Coastguard Worker if (isInsertion(end)) {
93*834a2baaSAndroid Build Coastguard Worker extraCharForHyphen++;
94*834a2baaSAndroid Build Coastguard Worker }
95*834a2baaSAndroid Build Coastguard Worker return mWidth * (range.getLength() + extraCharForHyphen);
96*834a2baaSAndroid Build Coastguard Worker }
97*834a2baaSAndroid Build Coastguard Worker
appendLayout(const U16StringPiece &,const Range &,const Range &,const LayoutPieces &,const MinikinPaint &,uint32_t,StartHyphenEdit,EndHyphenEdit,Layout *)98*834a2baaSAndroid Build Coastguard Worker virtual void appendLayout(const U16StringPiece&, const Range&, const Range&,
99*834a2baaSAndroid Build Coastguard Worker const LayoutPieces&, const MinikinPaint&, uint32_t, StartHyphenEdit,
100*834a2baaSAndroid Build Coastguard Worker EndHyphenEdit, Layout*) const {}
101*834a2baaSAndroid Build Coastguard Worker
measureText(const U16StringPiece &)102*834a2baaSAndroid Build Coastguard Worker virtual float measureText(const U16StringPiece&) const { return 0; }
103*834a2baaSAndroid Build Coastguard Worker
lineBreakStyle()104*834a2baaSAndroid Build Coastguard Worker virtual LineBreakStyle lineBreakStyle() const override { return LineBreakStyle::None; }
105*834a2baaSAndroid Build Coastguard Worker
lineBreakWordStyle()106*834a2baaSAndroid Build Coastguard Worker virtual LineBreakWordStyle lineBreakWordStyle() const override {
107*834a2baaSAndroid Build Coastguard Worker return LineBreakWordStyle::None;
108*834a2baaSAndroid Build Coastguard Worker }
109*834a2baaSAndroid Build Coastguard Worker
110*834a2baaSAndroid Build Coastguard Worker private:
111*834a2baaSAndroid Build Coastguard Worker MinikinPaint mPaint;
112*834a2baaSAndroid Build Coastguard Worker uint32_t mLocaleListId;
113*834a2baaSAndroid Build Coastguard Worker float mWidth;
114*834a2baaSAndroid Build Coastguard Worker float mAscent;
115*834a2baaSAndroid Build Coastguard Worker float mDescent;
116*834a2baaSAndroid Build Coastguard Worker };
117*834a2baaSAndroid Build Coastguard Worker
118*834a2baaSAndroid Build Coastguard Worker struct LineBreakExpectation {
119*834a2baaSAndroid Build Coastguard Worker std::string mLineContent;
120*834a2baaSAndroid Build Coastguard Worker float mWidth;
121*834a2baaSAndroid Build Coastguard Worker StartHyphenEdit mStartEdit;
122*834a2baaSAndroid Build Coastguard Worker EndHyphenEdit mEndEdit;
123*834a2baaSAndroid Build Coastguard Worker float mAscent;
124*834a2baaSAndroid Build Coastguard Worker float mDescent;
125*834a2baaSAndroid Build Coastguard Worker };
126*834a2baaSAndroid Build Coastguard Worker
sameLineBreak(const std::vector<LineBreakExpectation> & expected,const LineBreakResult & actual)127*834a2baaSAndroid Build Coastguard Worker static bool sameLineBreak(const std::vector<LineBreakExpectation>& expected,
128*834a2baaSAndroid Build Coastguard Worker const LineBreakResult& actual) {
129*834a2baaSAndroid Build Coastguard Worker if (expected.size() != actual.breakPoints.size()) {
130*834a2baaSAndroid Build Coastguard Worker return false;
131*834a2baaSAndroid Build Coastguard Worker }
132*834a2baaSAndroid Build Coastguard Worker
133*834a2baaSAndroid Build Coastguard Worker uint32_t breakOffset = 0;
134*834a2baaSAndroid Build Coastguard Worker for (uint32_t i = 0; i < expected.size(); ++i) {
135*834a2baaSAndroid Build Coastguard Worker std::vector<uint16_t> u16Str = utf8ToUtf16(expected[i].mLineContent);
136*834a2baaSAndroid Build Coastguard Worker
137*834a2baaSAndroid Build Coastguard Worker // The expected string contains auto inserted hyphen. Remove it for computing offset.
138*834a2baaSAndroid Build Coastguard Worker uint32_t lineLength = u16Str.size();
139*834a2baaSAndroid Build Coastguard Worker if (isInsertion(expected[i].mStartEdit)) {
140*834a2baaSAndroid Build Coastguard Worker if (u16Str[0] != '-') {
141*834a2baaSAndroid Build Coastguard Worker return false;
142*834a2baaSAndroid Build Coastguard Worker }
143*834a2baaSAndroid Build Coastguard Worker --lineLength;
144*834a2baaSAndroid Build Coastguard Worker }
145*834a2baaSAndroid Build Coastguard Worker if (isInsertion(expected[i].mEndEdit)) {
146*834a2baaSAndroid Build Coastguard Worker if (u16Str.back() != '-') {
147*834a2baaSAndroid Build Coastguard Worker return false;
148*834a2baaSAndroid Build Coastguard Worker }
149*834a2baaSAndroid Build Coastguard Worker --lineLength;
150*834a2baaSAndroid Build Coastguard Worker }
151*834a2baaSAndroid Build Coastguard Worker breakOffset += lineLength;
152*834a2baaSAndroid Build Coastguard Worker
153*834a2baaSAndroid Build Coastguard Worker if (breakOffset != static_cast<uint32_t>(actual.breakPoints[i])) {
154*834a2baaSAndroid Build Coastguard Worker return false;
155*834a2baaSAndroid Build Coastguard Worker }
156*834a2baaSAndroid Build Coastguard Worker if (expected[i].mWidth != actual.widths[i]) {
157*834a2baaSAndroid Build Coastguard Worker return false;
158*834a2baaSAndroid Build Coastguard Worker }
159*834a2baaSAndroid Build Coastguard Worker HyphenEdit edit = static_cast<HyphenEdit>(actual.flags[i] & 0xFF);
160*834a2baaSAndroid Build Coastguard Worker if (expected[i].mStartEdit != startHyphenEdit(edit)) {
161*834a2baaSAndroid Build Coastguard Worker return false;
162*834a2baaSAndroid Build Coastguard Worker }
163*834a2baaSAndroid Build Coastguard Worker if (expected[i].mEndEdit != endHyphenEdit(edit)) {
164*834a2baaSAndroid Build Coastguard Worker return false;
165*834a2baaSAndroid Build Coastguard Worker }
166*834a2baaSAndroid Build Coastguard Worker if (expected[i].mAscent != actual.ascents[i]) {
167*834a2baaSAndroid Build Coastguard Worker return false;
168*834a2baaSAndroid Build Coastguard Worker }
169*834a2baaSAndroid Build Coastguard Worker if (expected[i].mDescent != actual.descents[i]) {
170*834a2baaSAndroid Build Coastguard Worker return false;
171*834a2baaSAndroid Build Coastguard Worker }
172*834a2baaSAndroid Build Coastguard Worker }
173*834a2baaSAndroid Build Coastguard Worker return true;
174*834a2baaSAndroid Build Coastguard Worker }
175*834a2baaSAndroid Build Coastguard Worker
176*834a2baaSAndroid Build Coastguard Worker // Make debug string.
toString(const std::vector<LineBreakExpectation> & lines)177*834a2baaSAndroid Build Coastguard Worker static std::string toString(const std::vector<LineBreakExpectation>& lines) {
178*834a2baaSAndroid Build Coastguard Worker std::string out;
179*834a2baaSAndroid Build Coastguard Worker for (uint32_t i = 0; i < lines.size(); ++i) {
180*834a2baaSAndroid Build Coastguard Worker const LineBreakExpectation& line = lines[i];
181*834a2baaSAndroid Build Coastguard Worker
182*834a2baaSAndroid Build Coastguard Worker char lineMsg[128] = {};
183*834a2baaSAndroid Build Coastguard Worker snprintf(lineMsg, sizeof(lineMsg),
184*834a2baaSAndroid Build Coastguard Worker "Line %2d, Width: %5.1f, Hyphen(%hhu, %hhu), Extent(%5.1f, %5.1f), Text: \"%s\"\n",
185*834a2baaSAndroid Build Coastguard Worker i, line.mWidth, line.mStartEdit, line.mEndEdit, line.mAscent, line.mDescent,
186*834a2baaSAndroid Build Coastguard Worker line.mLineContent.c_str());
187*834a2baaSAndroid Build Coastguard Worker out += lineMsg;
188*834a2baaSAndroid Build Coastguard Worker }
189*834a2baaSAndroid Build Coastguard Worker return out;
190*834a2baaSAndroid Build Coastguard Worker }
191*834a2baaSAndroid Build Coastguard Worker
192*834a2baaSAndroid Build Coastguard Worker // Make debug string.
toString(const U16StringPiece & textBuf,const LineBreakResult & lines)193*834a2baaSAndroid Build Coastguard Worker static std::string toString(const U16StringPiece& textBuf, const LineBreakResult& lines) {
194*834a2baaSAndroid Build Coastguard Worker std::string out;
195*834a2baaSAndroid Build Coastguard Worker for (uint32_t i = 0; i < lines.breakPoints.size(); ++i) {
196*834a2baaSAndroid Build Coastguard Worker const Range textRange(i == 0 ? 0 : lines.breakPoints[i - 1], lines.breakPoints[i]);
197*834a2baaSAndroid Build Coastguard Worker const HyphenEdit edit = static_cast<HyphenEdit>(lines.flags[i] & 0xFF);
198*834a2baaSAndroid Build Coastguard Worker
199*834a2baaSAndroid Build Coastguard Worker const StartHyphenEdit startEdit = startHyphenEdit(edit);
200*834a2baaSAndroid Build Coastguard Worker const EndHyphenEdit endEdit = endHyphenEdit(edit);
201*834a2baaSAndroid Build Coastguard Worker std::string hyphenatedStr = utf16ToUtf8(textBuf.substr(textRange));
202*834a2baaSAndroid Build Coastguard Worker
203*834a2baaSAndroid Build Coastguard Worker if (isInsertion(startEdit)) {
204*834a2baaSAndroid Build Coastguard Worker hyphenatedStr.insert(0, "-");
205*834a2baaSAndroid Build Coastguard Worker }
206*834a2baaSAndroid Build Coastguard Worker if (isInsertion(endEdit)) {
207*834a2baaSAndroid Build Coastguard Worker hyphenatedStr.push_back('-');
208*834a2baaSAndroid Build Coastguard Worker }
209*834a2baaSAndroid Build Coastguard Worker char lineMsg[256] = {};
210*834a2baaSAndroid Build Coastguard Worker snprintf(lineMsg, sizeof(lineMsg),
211*834a2baaSAndroid Build Coastguard Worker "Line %2d, Width: %5.1f, Hyphen(%hhu, %hhu), Extent(%5.1f, %5.1f), Bounds(%f, %f, "
212*834a2baaSAndroid Build Coastguard Worker "%f, %f), Text: \"%s\"\n",
213*834a2baaSAndroid Build Coastguard Worker i, lines.widths[i], startEdit, endEdit, lines.ascents[i], lines.descents[i],
214*834a2baaSAndroid Build Coastguard Worker lines.bounds[i].mLeft, lines.bounds[i].mTop, lines.bounds[i].mRight,
215*834a2baaSAndroid Build Coastguard Worker lines.bounds[i].mBottom, hyphenatedStr.c_str());
216*834a2baaSAndroid Build Coastguard Worker out += lineMsg;
217*834a2baaSAndroid Build Coastguard Worker }
218*834a2baaSAndroid Build Coastguard Worker return out;
219*834a2baaSAndroid Build Coastguard Worker }
220*834a2baaSAndroid Build Coastguard Worker
221*834a2baaSAndroid Build Coastguard Worker } // namespace line_breaker_test_helper
222*834a2baaSAndroid Build Coastguard Worker } // namespace minikin
223