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