1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "Minikin"
18 
19 #include "FreeTypeMinikinFontForTest.h"
20 
21 #include <fcntl.h>
22 #include <sys/mman.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include <unistd.h>
26 #include <string>
27 
28 #include <ft2build.h>
29 #include <log/log.h>
30 #include FT_OUTLINE_H
31 
32 #include "minikin/MinikinFont.h"
33 
34 namespace minikin {
35 namespace {
36 
37 static int uniqueId = 0;
38 
39 constexpr FT_Int32 LOAD_FLAG =
40         FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
41 
FTPosToFloat(FT_Pos x)42 constexpr float FTPosToFloat(FT_Pos x) {
43     return x / 64.0;
44 }
45 
FTFloatToF26Dot6(float x)46 constexpr FT_F26Dot6 FTFloatToF26Dot6(float x) {
47     return static_cast<FT_F26Dot6>(x * 64);
48 }
49 
loadGlyphOrDie(uint32_t glyphId,float size,FT_Face face)50 void loadGlyphOrDie(uint32_t glyphId, float size, FT_Face face) {
51     const FT_F26Dot6 scale = FTFloatToF26Dot6(size);
52     LOG_ALWAYS_FATAL_IF(FT_Set_Char_Size(face, scale, scale, 72 /* dpi */, 72 /* dpi */),
53                         "Failed to set character size.");
54     LOG_ALWAYS_FATAL_IF(FT_Load_Glyph(face, glyphId, LOAD_FLAG), "Failed to load glyph");
55     LOG_ALWAYS_FATAL_IF(face->glyph->format != FT_GLYPH_FORMAT_OUTLINE,
56                         "Only outline font is supported.");
57 }
58 
59 }  // namespace
60 
FreeTypeMinikinFontForTest(const std::string & font_path,int index)61 FreeTypeMinikinFontForTest::FreeTypeMinikinFontForTest(const std::string& font_path, int index)
62         : MinikinFont(uniqueId++), mFontPath(font_path), mFontIndex(index) {
63     int fd = open(font_path.c_str(), O_RDONLY);
64     LOG_ALWAYS_FATAL_IF(fd == -1, "Open failed: %s", font_path.c_str());
65     struct stat st = {};
66     LOG_ALWAYS_FATAL_IF(fstat(fd, &st) != 0);
67     mFontSize = st.st_size;
68     mFontData = mmap(NULL, mFontSize, PROT_READ, MAP_SHARED, fd, 0);
69     LOG_ALWAYS_FATAL_IF(mFontData == nullptr);
70     close(fd);
71 
72     LOG_ALWAYS_FATAL_IF(FT_Init_FreeType(&mFtLibrary), "Failed to initialize FreeType");
73 
74     FT_Open_Args args;
75     args.flags = FT_OPEN_MEMORY;
76     args.memory_base = static_cast<const FT_Byte*>(mFontData);
77     args.memory_size = mFontSize;
78     LOG_ALWAYS_FATAL_IF(FT_Open_Face(mFtLibrary, &args, index, &mFtFace), "Failed to open FT_Face");
79 }
80 
~FreeTypeMinikinFontForTest()81 FreeTypeMinikinFontForTest::~FreeTypeMinikinFontForTest() {
82     FT_Done_Face(mFtFace);
83     FT_Done_FreeType(mFtLibrary);
84     munmap(mFontData, mFontSize);
85 }
86 
GetHorizontalAdvance(uint32_t glyphId,const MinikinPaint & paint,const FontFakery &) const87 float FreeTypeMinikinFontForTest::GetHorizontalAdvance(uint32_t glyphId, const MinikinPaint& paint,
88                                                        const FontFakery& /* fakery */) const {
89     loadGlyphOrDie(glyphId, paint.size, mFtFace);
90     return FTPosToFloat(mFtFace->glyph->advance.x);
91 }
92 
GetBounds(MinikinRect * bounds,uint32_t glyphId,const MinikinPaint & paint,const FontFakery &) const93 void FreeTypeMinikinFontForTest::GetBounds(MinikinRect* bounds, uint32_t glyphId,
94                                            const MinikinPaint& paint,
95                                            const FontFakery& /* fakery */) const {
96     loadGlyphOrDie(glyphId, paint.size, mFtFace);
97 
98     FT_BBox bbox;
99     FT_Outline_Get_CBox(&mFtFace->glyph->outline, &bbox);
100 
101     bounds->mLeft = FTPosToFloat(bbox.xMin);
102     bounds->mTop = FTPosToFloat(bbox.yMax);
103     bounds->mRight = FTPosToFloat(bbox.xMax);
104     bounds->mBottom = FTPosToFloat(bbox.yMin);
105 }
106 
GetFontExtent(MinikinExtent * extent,const MinikinPaint &,const FontFakery &) const107 void FreeTypeMinikinFontForTest::GetFontExtent(MinikinExtent* extent,
108                                                const MinikinPaint& /* paint */,
109                                                const FontFakery& /* fakery */) const {
110     // TODO: Retrieve font metrics from FreeType.
111     extent->ascent = -10.0f;
112     extent->descent = 20.0f;
113     extent->line_gap = 0.0f;
114 }
115 
116 }  // namespace minikin
117