1*20bfefbeSAndroid Build Coastguard Worker /*
2*20bfefbeSAndroid Build Coastguard Worker *
3*20bfefbeSAndroid Build Coastguard Worker * Copyright 2019, The Android Open Source Project
4*20bfefbeSAndroid Build Coastguard Worker *
5*20bfefbeSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
6*20bfefbeSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
7*20bfefbeSAndroid Build Coastguard Worker * You may obtain a copy of the License at
8*20bfefbeSAndroid Build Coastguard Worker *
9*20bfefbeSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
10*20bfefbeSAndroid Build Coastguard Worker *
11*20bfefbeSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
12*20bfefbeSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
13*20bfefbeSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*20bfefbeSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
15*20bfefbeSAndroid Build Coastguard Worker * limitations under the License.
16*20bfefbeSAndroid Build Coastguard Worker */
17*20bfefbeSAndroid Build Coastguard Worker
18*20bfefbeSAndroid Build Coastguard Worker #include <teeui/font_rendering.h>
19*20bfefbeSAndroid Build Coastguard Worker
20*20bfefbeSAndroid Build Coastguard Worker namespace teeui {
21*20bfefbeSAndroid Build Coastguard Worker
isBreakable(unsigned long codePoint)22*20bfefbeSAndroid Build Coastguard Worker bool isBreakable(unsigned long codePoint) {
23*20bfefbeSAndroid Build Coastguard Worker switch (codePoint) {
24*20bfefbeSAndroid Build Coastguard Worker case 9:
25*20bfefbeSAndroid Build Coastguard Worker case 0xA:
26*20bfefbeSAndroid Build Coastguard Worker case 0xB:
27*20bfefbeSAndroid Build Coastguard Worker case 0xC:
28*20bfefbeSAndroid Build Coastguard Worker case 0xD:
29*20bfefbeSAndroid Build Coastguard Worker case 0x20:
30*20bfefbeSAndroid Build Coastguard Worker case 0x85:
31*20bfefbeSAndroid Build Coastguard Worker case 0x1680: // Ogham Space Mark
32*20bfefbeSAndroid Build Coastguard Worker case 0x180E: // Mongolian Vowel Separator
33*20bfefbeSAndroid Build Coastguard Worker case 0x2000: // EN Quad
34*20bfefbeSAndroid Build Coastguard Worker case 0x2001: // EM Quad
35*20bfefbeSAndroid Build Coastguard Worker case 0x2002: // EN Space
36*20bfefbeSAndroid Build Coastguard Worker case 0x2003: // EM Space
37*20bfefbeSAndroid Build Coastguard Worker case 0x2004: // three per em space
38*20bfefbeSAndroid Build Coastguard Worker case 0x2005: // four per em space
39*20bfefbeSAndroid Build Coastguard Worker case 0x2006: // six per em space
40*20bfefbeSAndroid Build Coastguard Worker case 0x2008: // Punctuation space
41*20bfefbeSAndroid Build Coastguard Worker case 0x2009: // Thin Space
42*20bfefbeSAndroid Build Coastguard Worker case 0x200A: // Hair Space
43*20bfefbeSAndroid Build Coastguard Worker case 0x200B: // Zero Width Space
44*20bfefbeSAndroid Build Coastguard Worker case 0x200C: // Zero Width Non-Joiner
45*20bfefbeSAndroid Build Coastguard Worker case 0x200D: // Zero Width Joiner
46*20bfefbeSAndroid Build Coastguard Worker case 0x2028: // Line Separator
47*20bfefbeSAndroid Build Coastguard Worker case 0x2029: // Paragraph Separator
48*20bfefbeSAndroid Build Coastguard Worker case 0x205F: // Medium Mathematical Space
49*20bfefbeSAndroid Build Coastguard Worker case 0x3000: // Ideographic Space
50*20bfefbeSAndroid Build Coastguard Worker return true;
51*20bfefbeSAndroid Build Coastguard Worker default:
52*20bfefbeSAndroid Build Coastguard Worker return false;
53*20bfefbeSAndroid Build Coastguard Worker }
54*20bfefbeSAndroid Build Coastguard Worker }
55*20bfefbeSAndroid Build Coastguard Worker
isNewline(unsigned long codePoint)56*20bfefbeSAndroid Build Coastguard Worker bool isNewline(unsigned long codePoint) {
57*20bfefbeSAndroid Build Coastguard Worker return codePoint == '\n';
58*20bfefbeSAndroid Build Coastguard Worker }
59*20bfefbeSAndroid Build Coastguard Worker
setCharSize(signed long char_size,unsigned int dpi)60*20bfefbeSAndroid Build Coastguard Worker Error TextFace::setCharSize(signed long char_size, unsigned int dpi) {
61*20bfefbeSAndroid Build Coastguard Worker if (!face_) return Error::NotInitialized;
62*20bfefbeSAndroid Build Coastguard Worker auto error = FT_Set_Char_Size(*face_, 0, char_size, 0, dpi);
63*20bfefbeSAndroid Build Coastguard Worker if (error) return Error::CharSizeNotSet;
64*20bfefbeSAndroid Build Coastguard Worker return Error::OK;
65*20bfefbeSAndroid Build Coastguard Worker }
66*20bfefbeSAndroid Build Coastguard Worker
setCharSizeInPix(pxs size)67*20bfefbeSAndroid Build Coastguard Worker Error TextFace::setCharSizeInPix(pxs size) {
68*20bfefbeSAndroid Build Coastguard Worker if (!face_) return Error::NotInitialized;
69*20bfefbeSAndroid Build Coastguard Worker if (FT_Set_Pixel_Sizes(*face_, 0, size.count())) {
70*20bfefbeSAndroid Build Coastguard Worker return Error::CharSizeNotSet;
71*20bfefbeSAndroid Build Coastguard Worker }
72*20bfefbeSAndroid Build Coastguard Worker return Error::OK;
73*20bfefbeSAndroid Build Coastguard Worker }
74*20bfefbeSAndroid Build Coastguard Worker
getCharIndex(unsigned long codePoint)75*20bfefbeSAndroid Build Coastguard Worker GlyphIndex TextFace::getCharIndex(unsigned long codePoint) {
76*20bfefbeSAndroid Build Coastguard Worker if (!face_) return 0;
77*20bfefbeSAndroid Build Coastguard Worker return FT_Get_Char_Index(*face_, codePoint);
78*20bfefbeSAndroid Build Coastguard Worker }
79*20bfefbeSAndroid Build Coastguard Worker
loadGlyph(GlyphIndex index)80*20bfefbeSAndroid Build Coastguard Worker Error TextFace::loadGlyph(GlyphIndex index) {
81*20bfefbeSAndroid Build Coastguard Worker if (!face_) return Error::NotInitialized;
82*20bfefbeSAndroid Build Coastguard Worker if (FT_Load_Glyph(*face_, index, FT_LOAD_DEFAULT)) {
83*20bfefbeSAndroid Build Coastguard Worker return Error::GlyphNotLoaded;
84*20bfefbeSAndroid Build Coastguard Worker }
85*20bfefbeSAndroid Build Coastguard Worker return Error::OK;
86*20bfefbeSAndroid Build Coastguard Worker }
87*20bfefbeSAndroid Build Coastguard Worker
renderGlyph()88*20bfefbeSAndroid Build Coastguard Worker Error TextFace::renderGlyph() {
89*20bfefbeSAndroid Build Coastguard Worker if (!face_) return Error::NotInitialized;
90*20bfefbeSAndroid Build Coastguard Worker if (FT_Render_Glyph(face_->glyph, FT_RENDER_MODE_NORMAL)) {
91*20bfefbeSAndroid Build Coastguard Worker return Error::GlyphNotRendered;
92*20bfefbeSAndroid Build Coastguard Worker }
93*20bfefbeSAndroid Build Coastguard Worker return Error::OK;
94*20bfefbeSAndroid Build Coastguard Worker }
95*20bfefbeSAndroid Build Coastguard Worker
advance() const96*20bfefbeSAndroid Build Coastguard Worker Vec2d<pxs> TextFace::advance() const {
97*20bfefbeSAndroid Build Coastguard Worker return Vec2d<pxs>(face_->glyph->advance.x / 64.0, face_->glyph->advance.y / 64.0);
98*20bfefbeSAndroid Build Coastguard Worker }
99*20bfefbeSAndroid Build Coastguard Worker
kern(GlyphIndex previous) const100*20bfefbeSAndroid Build Coastguard Worker Vec2d<pxs> TextFace::kern(GlyphIndex previous) const {
101*20bfefbeSAndroid Build Coastguard Worker FT_Vector offset = {0, 0};
102*20bfefbeSAndroid Build Coastguard Worker if (hasKerning_ && previous) {
103*20bfefbeSAndroid Build Coastguard Worker if (!FT_Get_Kerning(*face_, previous, face_->glyph->glyph_index, FT_KERNING_DEFAULT,
104*20bfefbeSAndroid Build Coastguard Worker &offset)) {
105*20bfefbeSAndroid Build Coastguard Worker offset = {0, 0};
106*20bfefbeSAndroid Build Coastguard Worker }
107*20bfefbeSAndroid Build Coastguard Worker }
108*20bfefbeSAndroid Build Coastguard Worker return {offset.x / 64.0, offset.y / 64.0};
109*20bfefbeSAndroid Build Coastguard Worker }
110*20bfefbeSAndroid Build Coastguard Worker
getGlyphBBox() const111*20bfefbeSAndroid Build Coastguard Worker optional<Box<pxs>> TextFace::getGlyphBBox() const {
112*20bfefbeSAndroid Build Coastguard Worker FT_Glyph glyph;
113*20bfefbeSAndroid Build Coastguard Worker if (!FT_Get_Glyph(face_->glyph, &glyph)) {
114*20bfefbeSAndroid Build Coastguard Worker FT_BBox cbox;
115*20bfefbeSAndroid Build Coastguard Worker FT_Glyph_Get_CBox(glyph, ft_glyph_bbox_pixels, &cbox);
116*20bfefbeSAndroid Build Coastguard Worker FT_Done_Glyph(glyph);
117*20bfefbeSAndroid Build Coastguard Worker return {{cbox.xMin, -cbox.yMax, cbox.xMax - cbox.xMin, cbox.yMax - cbox.yMin}};
118*20bfefbeSAndroid Build Coastguard Worker }
119*20bfefbeSAndroid Build Coastguard Worker return {};
120*20bfefbeSAndroid Build Coastguard Worker }
121*20bfefbeSAndroid Build Coastguard Worker
create()122*20bfefbeSAndroid Build Coastguard Worker std::tuple<Error, TextContext> TextContext::create() {
123*20bfefbeSAndroid Build Coastguard Worker std::tuple<Error, TextContext> result;
124*20bfefbeSAndroid Build Coastguard Worker auto& [rc, lib] = result;
125*20bfefbeSAndroid Build Coastguard Worker rc = Error::NotInitialized;
126*20bfefbeSAndroid Build Coastguard Worker FT_Library library = nullptr;
127*20bfefbeSAndroid Build Coastguard Worker auto error = FT_Init_FreeType(&library);
128*20bfefbeSAndroid Build Coastguard Worker if (error) return result;
129*20bfefbeSAndroid Build Coastguard Worker rc = Error::OK;
130*20bfefbeSAndroid Build Coastguard Worker lib.library_ = Handle(library);
131*20bfefbeSAndroid Build Coastguard Worker return result;
132*20bfefbeSAndroid Build Coastguard Worker }
133*20bfefbeSAndroid Build Coastguard Worker
134*20bfefbeSAndroid Build Coastguard Worker std::tuple<Error, Box<pxs>, UTF8Range<const char*>>
findLongestWordSequence(TextFace * face,const UTF8Range<const char * > & text,const Box<pxs> & boundingBox)135*20bfefbeSAndroid Build Coastguard Worker findLongestWordSequence(TextFace* face, const UTF8Range<const char*>& text,
136*20bfefbeSAndroid Build Coastguard Worker const Box<pxs>& boundingBox) {
137*20bfefbeSAndroid Build Coastguard Worker std::tuple<Error, Box<pxs>, UTF8Range<const char*>> result;
138*20bfefbeSAndroid Build Coastguard Worker auto& [error, bBox, resultRange] = result;
139*20bfefbeSAndroid Build Coastguard Worker
140*20bfefbeSAndroid Build Coastguard Worker Vec2d<pxs> pen = {0, 0};
141*20bfefbeSAndroid Build Coastguard Worker bBox = {pen, {0, 0}};
142*20bfefbeSAndroid Build Coastguard Worker
143*20bfefbeSAndroid Build Coastguard Worker GlyphIndex previous = 0;
144*20bfefbeSAndroid Build Coastguard Worker UTF8WordRange<const char*> wordRange(text);
145*20bfefbeSAndroid Build Coastguard Worker auto rangeBegin = wordRange.begin();
146*20bfefbeSAndroid Build Coastguard Worker if (isBreakable((*rangeBegin).codePoint())) {
147*20bfefbeSAndroid Build Coastguard Worker ++rangeBegin;
148*20bfefbeSAndroid Build Coastguard Worker }
149*20bfefbeSAndroid Build Coastguard Worker auto wordEnd = rangeBegin;
150*20bfefbeSAndroid Build Coastguard Worker auto wordStart = wordEnd;
151*20bfefbeSAndroid Build Coastguard Worker ++wordEnd;
152*20bfefbeSAndroid Build Coastguard Worker auto sequenceEnd = *wordStart;
153*20bfefbeSAndroid Build Coastguard Worker auto lastBreakableBegin = *wordStart;
154*20bfefbeSAndroid Build Coastguard Worker Box<pxs> currentBox = bBox;
155*20bfefbeSAndroid Build Coastguard Worker Box<pxs> currentFullWordBox = bBox;
156*20bfefbeSAndroid Build Coastguard Worker while (wordStart != wordRange.end()) {
157*20bfefbeSAndroid Build Coastguard Worker auto codePoint = UTF8Range<const char*>::codePoint(**wordStart);
158*20bfefbeSAndroid Build Coastguard Worker if (isBreakable(codePoint)) {
159*20bfefbeSAndroid Build Coastguard Worker lastBreakableBegin = *wordStart;
160*20bfefbeSAndroid Build Coastguard Worker currentFullWordBox = currentBox;
161*20bfefbeSAndroid Build Coastguard Worker }
162*20bfefbeSAndroid Build Coastguard Worker
163*20bfefbeSAndroid Build Coastguard Worker Box<pxs> workingBox = currentBox;
164*20bfefbeSAndroid Build Coastguard Worker auto c = *wordStart;
165*20bfefbeSAndroid Build Coastguard Worker bool exceedsBoundingBox = false;
166*20bfefbeSAndroid Build Coastguard Worker while (c != *wordEnd) {
167*20bfefbeSAndroid Build Coastguard Worker codePoint = c.codePoint();
168*20bfefbeSAndroid Build Coastguard Worker auto gindex = face->getCharIndex(codePoint);
169*20bfefbeSAndroid Build Coastguard Worker if (gindex == 0) {
170*20bfefbeSAndroid Build Coastguard Worker error = Error::GlyphNotLoaded;
171*20bfefbeSAndroid Build Coastguard Worker return result;
172*20bfefbeSAndroid Build Coastguard Worker }
173*20bfefbeSAndroid Build Coastguard Worker error = face->loadGlyph(gindex);
174*20bfefbeSAndroid Build Coastguard Worker if (error != Error::OK) return result;
175*20bfefbeSAndroid Build Coastguard Worker pen += face->kern(previous);
176*20bfefbeSAndroid Build Coastguard Worker if (auto gBox = face->getGlyphBBox()) {
177*20bfefbeSAndroid Build Coastguard Worker TEEUI_LOG << "Glyph Box: " << *gBox << ENDL;
178*20bfefbeSAndroid Build Coastguard Worker workingBox = workingBox.merge(gBox->translateSelf(pen));
179*20bfefbeSAndroid Build Coastguard Worker TEEUI_LOG << "WorkingBox: " << workingBox << ENDL;
180*20bfefbeSAndroid Build Coastguard Worker } else {
181*20bfefbeSAndroid Build Coastguard Worker error = Error::BBoxComputation;
182*20bfefbeSAndroid Build Coastguard Worker return result;
183*20bfefbeSAndroid Build Coastguard Worker }
184*20bfefbeSAndroid Build Coastguard Worker pen += face->advance();
185*20bfefbeSAndroid Build Coastguard Worker previous = gindex;
186*20bfefbeSAndroid Build Coastguard Worker ++c;
187*20bfefbeSAndroid Build Coastguard Worker if (workingBox.fitsInside(boundingBox)) {
188*20bfefbeSAndroid Build Coastguard Worker currentBox = workingBox;
189*20bfefbeSAndroid Build Coastguard Worker sequenceEnd = c;
190*20bfefbeSAndroid Build Coastguard Worker } else {
191*20bfefbeSAndroid Build Coastguard Worker exceedsBoundingBox = true;
192*20bfefbeSAndroid Build Coastguard Worker TEEUI_LOG << "exceeding bbox" << ENDL;
193*20bfefbeSAndroid Build Coastguard Worker break;
194*20bfefbeSAndroid Build Coastguard Worker }
195*20bfefbeSAndroid Build Coastguard Worker }
196*20bfefbeSAndroid Build Coastguard Worker if (exceedsBoundingBox) break;
197*20bfefbeSAndroid Build Coastguard Worker wordStart = wordEnd;
198*20bfefbeSAndroid Build Coastguard Worker ++wordEnd;
199*20bfefbeSAndroid Build Coastguard Worker }
200*20bfefbeSAndroid Build Coastguard Worker if (wordStart == wordRange.end()) {
201*20bfefbeSAndroid Build Coastguard Worker bBox = currentBox;
202*20bfefbeSAndroid Build Coastguard Worker resultRange = {**rangeBegin, *text.end()};
203*20bfefbeSAndroid Build Coastguard Worker TEEUI_LOG << "full range" << ENDL;
204*20bfefbeSAndroid Build Coastguard Worker } else if (*rangeBegin != lastBreakableBegin) {
205*20bfefbeSAndroid Build Coastguard Worker bBox = currentFullWordBox;
206*20bfefbeSAndroid Build Coastguard Worker resultRange = {**rangeBegin, *lastBreakableBegin};
207*20bfefbeSAndroid Build Coastguard Worker TEEUI_LOG << "partial range:" << ENDL;
208*20bfefbeSAndroid Build Coastguard Worker } else {
209*20bfefbeSAndroid Build Coastguard Worker bBox = currentBox;
210*20bfefbeSAndroid Build Coastguard Worker resultRange = {**rangeBegin, *sequenceEnd};
211*20bfefbeSAndroid Build Coastguard Worker TEEUI_LOG << "unbreakable" << ENDL;
212*20bfefbeSAndroid Build Coastguard Worker }
213*20bfefbeSAndroid Build Coastguard Worker error = Error::OK;
214*20bfefbeSAndroid Build Coastguard Worker return result;
215*20bfefbeSAndroid Build Coastguard Worker }
216*20bfefbeSAndroid Build Coastguard Worker
drawText(TextFace * face,const UTF8Range<const char * > & text,const PixelDrawer & drawPixel,PxPoint pen)217*20bfefbeSAndroid Build Coastguard Worker Error drawText(TextFace* face, const UTF8Range<const char*>& text, const PixelDrawer& drawPixel,
218*20bfefbeSAndroid Build Coastguard Worker PxPoint pen) {
219*20bfefbeSAndroid Build Coastguard Worker Error error;
220*20bfefbeSAndroid Build Coastguard Worker
221*20bfefbeSAndroid Build Coastguard Worker for (auto c : text) {
222*20bfefbeSAndroid Build Coastguard Worker auto codePoint = UTF8Range<const char*>::codePoint(c);
223*20bfefbeSAndroid Build Coastguard Worker auto gindex = face->getCharIndex(codePoint);
224*20bfefbeSAndroid Build Coastguard Worker error = face->loadGlyph(gindex);
225*20bfefbeSAndroid Build Coastguard Worker if (error == Error::OK) error = face->renderGlyph();
226*20bfefbeSAndroid Build Coastguard Worker if (error == Error::OK) error = face->drawGlyph(pen, drawPixel);
227*20bfefbeSAndroid Build Coastguard Worker if (error != Error::OK) return error;
228*20bfefbeSAndroid Build Coastguard Worker
229*20bfefbeSAndroid Build Coastguard Worker pen += face->advance();
230*20bfefbeSAndroid Build Coastguard Worker }
231*20bfefbeSAndroid Build Coastguard Worker return Error::OK;
232*20bfefbeSAndroid Build Coastguard Worker }
233*20bfefbeSAndroid Build Coastguard Worker
234*20bfefbeSAndroid Build Coastguard Worker } // namespace teeui
235