diff options
| author | 2018-07-18 15:53:48 -0700 | |
|---|---|---|
| committer | 2018-07-19 10:37:33 -0700 | |
| commit | fc09c9ee91a0418099a92d5565ff6a4d4abe7ec2 (patch) | |
| tree | bec7ecdc6083932a520bad81488985a38723e52a /graphics/java/android | |
| parent | edf3e1fa2effc158d3be00a6e75fc06a6925362b (diff) | |
Introduce FontFileUtil
FontFileUtil provides style analysis of font file.
This CL also introduce a basic test functionality of font families.
Test: atest android.graphics.FontFileUtilTest
Bug: 72665240
Change-Id: I967d56eb37ee0e4c6d59f59c301a4a6f009fad2d
Diffstat (limited to 'graphics/java/android')
| -rw-r--r-- | graphics/java/android/graphics/fonts/FontFileUtil.java | 127 |
1 files changed, 127 insertions, 0 deletions
diff --git a/graphics/java/android/graphics/fonts/FontFileUtil.java b/graphics/java/android/graphics/fonts/FontFileUtil.java new file mode 100644 index 000000000000..d15f581f918c --- /dev/null +++ b/graphics/java/android/graphics/fonts/FontFileUtil.java @@ -0,0 +1,127 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics.fonts; + +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** + * Provides a utility for font file operations. + * @hide + */ +public class FontFileUtil { + + private FontFileUtil() {} // Do not instanciate + + /** + * Unpack the weight value from packed integer. + */ + public static int unpackWeight(int packed) { + return packed & 0xFFFF; + } + + /** + * Unpack the italic value from packed integer. + */ + public static boolean unpackItalic(int packed) { + return (packed & 0x10000) != 0; + } + + private static int pack(@IntRange(from = 0, to = 1000) int weight, boolean italic) { + return weight | (italic ? 0x10000 : 0); + } + + private static final int SFNT_VERSION_1 = 0x00010000; + private static final int SFNT_VERSION_OTTO = 0x4F54544F; + private static final int TTC_TAG = 0x74746366; + private static final int OS2_TABLE_TAG = 0x4F532F32; + + /** + * Analyze the font file returns packed style info + */ + public static final int analyzeStyle(@NonNull ByteBuffer buffer, + @IntRange(from = 0) int ttcIndex, @Nullable FontVariationAxis[] varSettings) + throws IOException { + int weight = -1; + int italic = -1; + if (varSettings != null) { + for (FontVariationAxis axis :varSettings) { + if ("wght".equals(axis.getTag())) { + weight = (int) axis.getStyleValue(); + } else if ("ital".equals(axis.getTag())) { + italic = (axis.getStyleValue() == 1.0f) ? 1 : 0; + } + } + } + + if (weight != -1 && italic != -1) { + // Both weight/italic style are specifeid by variation settings. + // No need to look into OS/2 table. + // TODO: Good to look HVAR table to check if this font supports wght/ital axes. + return pack(weight, italic == 1); + } + + ByteOrder originalOrder = buffer.order(); + buffer.order(ByteOrder.BIG_ENDIAN); + try { + int fontFileOffset = 0; + int magicNumber = buffer.getInt(0); + if (magicNumber == TTC_TAG) { + // TTC file. + if (ttcIndex >= buffer.getInt(8 /* offset to number of fonts in TTC */)) { + throw new IOException("Font index out of bounds"); + } + fontFileOffset = buffer.getInt( + 12 /* offset to array of offsets of font files */ + 4 * ttcIndex); + } + int sfntVersion = buffer.getInt(fontFileOffset); + + if (sfntVersion != SFNT_VERSION_1 && sfntVersion != SFNT_VERSION_OTTO) { + throw new IOException("Unknown font file format"); + } + + int numTables = buffer.getShort(fontFileOffset + 4 /* offset to number of tables */); + int os2TableOffset = -1; + for (int i = 0; i < numTables; ++i) { + int tableOffset = fontFileOffset + 12 /* size of offset table */ + + i * 16 /* size of table record */; + if (buffer.getInt(tableOffset) == OS2_TABLE_TAG) { + os2TableOffset = buffer.getInt(tableOffset + 8 /* offset to the table */); + break; + } + } + + if (os2TableOffset == -1) { + // Couldn't find OS/2 table. use regular style + return pack(400, false); + } + + int weightFromOS2 = buffer.getShort(os2TableOffset + 4 /* offset to weight class */); + boolean italicFromOS2 = + (buffer.getShort(os2TableOffset + 62 /* offset to fsSelection */) & 1) != 0; + return pack(weight == -1 ? weightFromOS2 : weight, + italic == -1 ? italicFromOS2 : italic == 1); + } finally { + buffer.order(originalOrder); + } + } +} |