• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 package com.android.server.graphics.fonts;
18 
19 import android.graphics.Bitmap;
20 import android.graphics.Canvas;
21 import android.graphics.Typeface;
22 import android.graphics.fonts.Font;
23 import android.graphics.fonts.FontFamily;
24 import android.graphics.fonts.FontFileUtil;
25 import android.text.Layout;
26 import android.text.StaticLayout;
27 import android.text.TextPaint;
28 import android.text.TextUtils;
29 
30 import java.io.File;
31 import java.io.FileInputStream;
32 import java.io.IOException;
33 import java.nio.ByteBuffer;
34 import java.nio.DirectByteBuffer;
35 import java.nio.NioUtils;
36 import java.nio.channels.FileChannel;
37 
38 /* package */  class OtfFontFileParser implements UpdatableFontDir.FontFileParser {
39     @Override
getPostScriptName(File file)40     public String getPostScriptName(File file) throws IOException {
41         ByteBuffer buffer = mmap(file);
42         try {
43             return FontFileUtil.getPostScriptName(buffer, 0);
44         } finally {
45             unmap(buffer);
46         }
47     }
48 
49     @Override
buildFontFileName(File file)50     public String buildFontFileName(File file) throws IOException {
51         ByteBuffer buffer = mmap(file);
52         try {
53             String psName = FontFileUtil.getPostScriptName(buffer, 0);
54             int isType1Font = FontFileUtil.isPostScriptType1Font(buffer, 0);
55             int isCollection = FontFileUtil.isCollectionFont(buffer);
56 
57             if (TextUtils.isEmpty(psName) || isType1Font == -1 || isCollection == -1) {
58                 return null;
59             }
60 
61             String extension;
62             if (isCollection == 1) {
63                 extension = isType1Font == 1 ? ".otc" : ".ttc";
64             } else {
65                 extension = isType1Font == 1 ? ".otf" : ".ttf";
66             }
67             return psName + extension;
68         } finally {
69             unmap(buffer);
70         }
71 
72     }
73 
74     @Override
getRevision(File file)75     public long getRevision(File file) throws IOException {
76         ByteBuffer buffer = mmap(file);
77         try {
78             return FontFileUtil.getRevision(buffer, 0);
79         } finally {
80             unmap(buffer);
81         }
82     }
83 
84     @Override
tryToCreateTypeface(File file)85     public void tryToCreateTypeface(File file) throws Throwable {
86         ByteBuffer buffer = mmap(file);
87         try {
88             Font font = new Font.Builder(buffer).build();
89             FontFamily family = new FontFamily.Builder(font).build();
90             Typeface typeface = new Typeface.CustomFallbackBuilder(family).build();
91 
92             TextPaint p = new TextPaint();
93             p.setTextSize(24f);
94             p.setTypeface(typeface);
95 
96             // Test string to try with the passed font.
97             // TODO: Good to extract from font file.
98             String testTextToDraw = "abcXYZ@- "
99                     + "\uD83E\uDED6" // Emoji E13.0
100                     + "\uD83C\uDDFA\uD83C\uDDF8" // Emoji Flags
101                     + "\uD83D\uDC8F\uD83C\uDFFB" // Emoji Skin tone Sequence
102                     // ZWJ Sequence
103                     + "\uD83D\uDC68\uD83C\uDFFC\u200D\u2764\uFE0F\u200D\uD83D\uDC8B\u200D"
104                     + "\uD83D\uDC68\uD83C\uDFFF";
105 
106             int width = (int) Math.ceil(Layout.getDesiredWidth(testTextToDraw, p));
107             StaticLayout layout = StaticLayout.Builder.obtain(
108                     testTextToDraw, 0, testTextToDraw.length(), p, width).build();
109             Bitmap bmp = Bitmap.createBitmap(
110                     layout.getWidth(), layout.getHeight(), Bitmap.Config.ALPHA_8);
111             Canvas canvas = new Canvas(bmp);
112             layout.draw(canvas);
113         } finally {
114             unmap(buffer);
115         }
116     }
117 
mmap(File file)118     private static ByteBuffer mmap(File file) throws IOException {
119         try (FileInputStream in = new FileInputStream(file)) {
120             FileChannel fileChannel = in.getChannel();
121             return fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size());
122         }
123     }
124 
unmap(ByteBuffer buffer)125     private static void unmap(ByteBuffer buffer) {
126         if (buffer instanceof DirectByteBuffer) {
127             NioUtils.freeDirectBuffer(buffer);
128         }
129     }
130 }
131