Merge "Implement family fallback."

This commit is contained in:
Seigo Nonaka
2017-07-13 20:02:29 +00:00
committed by Android (Google) Code Review
15 changed files with 1688 additions and 209 deletions

View File

@@ -64,17 +64,19 @@ public final class FontConfig {
private final int mWeight;
private final boolean mIsItalic;
private Uri mUri;
private final String mFallbackFor;
/**
* @hide
*/
public Font(@NonNull String fontName, int ttcIndex, @NonNull FontVariationAxis[] axes,
int weight, boolean isItalic) {
int weight, boolean isItalic, String fallbackFor) {
mFontName = fontName;
mTtcIndex = ttcIndex;
mAxes = axes;
mWeight = weight;
mIsItalic = isItalic;
mFallbackFor = fallbackFor;
}
/**
@@ -125,6 +127,10 @@ public final class FontConfig {
public void setUri(@NonNull Uri uri) {
mUri = uri;
}
public String getFallbackFor() {
return mFallbackFor;
}
}
/**

Binary file not shown.

View File

@@ -0,0 +1,187 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright (C) 2017 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.
-->
<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
<GlyphOrder>
<GlyphID id="0" name=".notdef"/>
<GlyphID id="1" name="1em"/>
<GlyphID id="2" name="3em"/>
</GlyphOrder>
<head>
<tableVersion value="1.0"/>
<fontRevision value="1.0"/>
<checkSumAdjustment value="0x640cdb2f"/>
<magicNumber value="0x5f0f3cf5"/>
<flags value="00000000 00000011"/>
<unitsPerEm value="1000"/>
<created value="Fri Mar 17 07:26:00 2017"/>
<macStyle value="00000000 00000000"/>
<lowestRecPPEM value="7"/>
<fontDirectionHint value="2"/>
<glyphDataFormat value="0"/>
</head>
<hhea>
<tableVersion value="1.0"/>
<ascent value="1000"/>
<descent value="-200"/>
<lineGap value="0"/>
<caretSlopeRise value="1"/>
<caretSlopeRun value="0"/>
<caretOffset value="0"/>
<reserved0 value="0"/>
<reserved1 value="0"/>
<reserved2 value="0"/>
<reserved3 value="0"/>
<metricDataFormat value="0"/>
</hhea>
<maxp>
<tableVersion value="0x10000"/>
<maxZones value="0"/>
<maxTwilightPoints value="0"/>
<maxStorage value="0"/>
<maxFunctionDefs value="0"/>
<maxInstructionDefs value="0"/>
<maxStackElements value="0"/>
<maxSizeOfInstructions value="0"/>
<maxComponentElements value="0"/>
</maxp>
<OS_2>
<!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
will be recalculated by the compiler -->
<version value="3"/>
<xAvgCharWidth value="594"/>
<usWeightClass value="400"/>
<usWidthClass value="5"/>
<fsType value="00000000 00001000"/>
<ySubscriptXSize value="650"/>
<ySubscriptYSize value="600"/>
<ySubscriptXOffset value="0"/>
<ySubscriptYOffset value="75"/>
<ySuperscriptXSize value="650"/>
<ySuperscriptYSize value="600"/>
<ySuperscriptXOffset value="0"/>
<ySuperscriptYOffset value="350"/>
<yStrikeoutSize value="50"/>
<yStrikeoutPosition value="300"/>
<sFamilyClass value="0"/>
<panose>
<bFamilyType value="0"/>
<bSerifStyle value="0"/>
<bWeight value="5"/>
<bProportion value="0"/>
<bContrast value="0"/>
<bStrokeVariation value="0"/>
<bArmStyle value="0"/>
<bLetterForm value="0"/>
<bMidline value="0"/>
<bXHeight value="0"/>
</panose>
<ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
<ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
<ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
<ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
<achVendID value="UKWN"/>
<fsSelection value="00000000 01000000"/>
<usFirstCharIndex value="32"/>
<usLastCharIndex value="122"/>
<sTypoAscender value="800"/>
<sTypoDescender value="-200"/>
<sTypoLineGap value="200"/>
<usWinAscent value="1000"/>
<usWinDescent value="200"/>
<ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
<ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
<sxHeight value="500"/>
<sCapHeight value="700"/>
<usDefaultChar value="0"/>
<usBreakChar value="32"/>
<usMaxContext value="0"/>
</OS_2>
<hmtx>
<mtx name=".notdef" width="500" lsb="93"/>
<mtx name="1em" width="1000" lsb="93"/>
<mtx name="3em" width="3000" lsb="93"/>
</hmtx>
<cmap>
<tableVersion version="0"/>
<cmap_format_4 platformID="3" platEncID="10" language="0">
<map code="0x0061" name="3em" />
<map code="0x0062" name="1em" />
<map code="0x0063" name="1em" />
<map code="0x0064" name="1em" />
<map code="0x0065" name="1em" />
</cmap_format_4>
</cmap>
<loca>
<!-- The 'loca' table will be calculated by the compiler -->
</loca>
<glyf>
<TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
<TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
<TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
</glyf>
<name>
<namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
Copyright (C) 2017 The Android Open Source Project
</namerecord>
<namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
Sample Font
</namerecord>
<namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
Regular
</namerecord>
<namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
Sample Font
</namerecord>
<namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
SampleFont-Regular
</namerecord>
<namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
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.
</namerecord>
<namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
http://www.apache.org/licenses/LICENSE-2.0
</namerecord>
</name>
<post>
<formatType value="3.0"/>
<italicAngle value="0.0"/>
<underlinePosition value="-75"/>
<underlineThickness value="50"/>
<isFixedPitch value="0"/>
<minMemType42 value="0"/>
<maxMemType42 value="0"/>
<minMemType1 value="0"/>
<maxMemType1 value="0"/>
</post>
</ttFont>

Binary file not shown.

View File

@@ -0,0 +1,184 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright (C) 2017 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.
-->
<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
<GlyphOrder>
<GlyphID id="0" name=".notdef"/>
<GlyphID id="1" name="2em"/>
</GlyphOrder>
<head>
<tableVersion value="1.0"/>
<fontRevision value="1.0"/>
<checkSumAdjustment value="0x640cdb2f"/>
<magicNumber value="0x5f0f3cf5"/>
<flags value="00000000 00000011"/>
<unitsPerEm value="1000"/>
<created value="Fri Mar 17 07:26:00 2017"/>
<macStyle value="00000000 00000000"/>
<lowestRecPPEM value="7"/>
<fontDirectionHint value="2"/>
<glyphDataFormat value="0"/>
</head>
<hhea>
<tableVersion value="1.0"/>
<ascent value="1000"/>
<descent value="-200"/>
<lineGap value="0"/>
<caretSlopeRise value="1"/>
<caretSlopeRun value="0"/>
<caretOffset value="0"/>
<reserved0 value="0"/>
<reserved1 value="0"/>
<reserved2 value="0"/>
<reserved3 value="0"/>
<metricDataFormat value="0"/>
</hhea>
<maxp>
<tableVersion value="0x10000"/>
<maxZones value="0"/>
<maxTwilightPoints value="0"/>
<maxStorage value="0"/>
<maxFunctionDefs value="0"/>
<maxInstructionDefs value="0"/>
<maxStackElements value="0"/>
<maxSizeOfInstructions value="0"/>
<maxComponentElements value="0"/>
</maxp>
<OS_2>
<!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
will be recalculated by the compiler -->
<version value="3"/>
<xAvgCharWidth value="594"/>
<usWeightClass value="400"/>
<usWidthClass value="5"/>
<fsType value="00000000 00001000"/>
<ySubscriptXSize value="650"/>
<ySubscriptYSize value="600"/>
<ySubscriptXOffset value="0"/>
<ySubscriptYOffset value="75"/>
<ySuperscriptXSize value="650"/>
<ySuperscriptYSize value="600"/>
<ySuperscriptXOffset value="0"/>
<ySuperscriptYOffset value="350"/>
<yStrikeoutSize value="50"/>
<yStrikeoutPosition value="300"/>
<sFamilyClass value="0"/>
<panose>
<bFamilyType value="0"/>
<bSerifStyle value="0"/>
<bWeight value="5"/>
<bProportion value="0"/>
<bContrast value="0"/>
<bStrokeVariation value="0"/>
<bArmStyle value="0"/>
<bLetterForm value="0"/>
<bMidline value="0"/>
<bXHeight value="0"/>
</panose>
<ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
<ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
<ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
<ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
<achVendID value="UKWN"/>
<fsSelection value="00000000 01000000"/>
<usFirstCharIndex value="32"/>
<usLastCharIndex value="122"/>
<sTypoAscender value="800"/>
<sTypoDescender value="-200"/>
<sTypoLineGap value="200"/>
<usWinAscent value="1000"/>
<usWinDescent value="200"/>
<ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
<ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
<sxHeight value="500"/>
<sCapHeight value="700"/>
<usDefaultChar value="0"/>
<usBreakChar value="32"/>
<usMaxContext value="0"/>
</OS_2>
<hmtx>
<mtx name=".notdef" width="500" lsb="93"/>
<mtx name="2em" width="1000" lsb="93"/>
</hmtx>
<cmap>
<tableVersion version="0"/>
<cmap_format_4 platformID="3" platEncID="10" language="0">
<map code="0x0061" name="2em" />
<map code="0x0062" name="2em" />
<map code="0x0063" name="2em" />
<map code="0x0064" name="2em" />
<map code="0x0065" name="2em" />
</cmap_format_4>
</cmap>
<loca>
<!-- The 'loca' table will be calculated by the compiler -->
</loca>
<glyf>
<TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
<TTGlyph name="2em" xMin="0" yMin="0" xMax="0" yMax="0" />
</glyf>
<name>
<namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
Copyright (C) 2017 The Android Open Source Project
</namerecord>
<namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
Sample Font
</namerecord>
<namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
Regular
</namerecord>
<namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
Sample Font
</namerecord>
<namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
SampleFont-Regular
</namerecord>
<namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
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.
</namerecord>
<namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
http://www.apache.org/licenses/LICENSE-2.0
</namerecord>
</name>
<post>
<formatType value="3.0"/>
<italicAngle value="0.0"/>
<underlinePosition value="-75"/>
<underlineThickness value="50"/>
<isFixedPitch value="0"/>
<minMemType42 value="0"/>
<maxMemType42 value="0"/>
<minMemType1 value="0"/>
<maxMemType1 value="0"/>
</post>
</ttFont>

Binary file not shown.

View File

@@ -0,0 +1,187 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright (C) 2017 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.
-->
<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
<GlyphOrder>
<GlyphID id="0" name=".notdef"/>
<GlyphID id="1" name="1em"/>
<GlyphID id="2" name="3em"/>
</GlyphOrder>
<head>
<tableVersion value="1.0"/>
<fontRevision value="1.0"/>
<checkSumAdjustment value="0x640cdb2f"/>
<magicNumber value="0x5f0f3cf5"/>
<flags value="00000000 00000011"/>
<unitsPerEm value="1000"/>
<created value="Fri Mar 17 07:26:00 2017"/>
<macStyle value="00000000 00000000"/>
<lowestRecPPEM value="7"/>
<fontDirectionHint value="2"/>
<glyphDataFormat value="0"/>
</head>
<hhea>
<tableVersion value="1.0"/>
<ascent value="1000"/>
<descent value="-200"/>
<lineGap value="0"/>
<caretSlopeRise value="1"/>
<caretSlopeRun value="0"/>
<caretOffset value="0"/>
<reserved0 value="0"/>
<reserved1 value="0"/>
<reserved2 value="0"/>
<reserved3 value="0"/>
<metricDataFormat value="0"/>
</hhea>
<maxp>
<tableVersion value="0x10000"/>
<maxZones value="0"/>
<maxTwilightPoints value="0"/>
<maxStorage value="0"/>
<maxFunctionDefs value="0"/>
<maxInstructionDefs value="0"/>
<maxStackElements value="0"/>
<maxSizeOfInstructions value="0"/>
<maxComponentElements value="0"/>
</maxp>
<OS_2>
<!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
will be recalculated by the compiler -->
<version value="3"/>
<xAvgCharWidth value="594"/>
<usWeightClass value="400"/>
<usWidthClass value="5"/>
<fsType value="00000000 00001000"/>
<ySubscriptXSize value="650"/>
<ySubscriptYSize value="600"/>
<ySubscriptXOffset value="0"/>
<ySubscriptYOffset value="75"/>
<ySuperscriptXSize value="650"/>
<ySuperscriptYSize value="600"/>
<ySuperscriptXOffset value="0"/>
<ySuperscriptYOffset value="350"/>
<yStrikeoutSize value="50"/>
<yStrikeoutPosition value="300"/>
<sFamilyClass value="0"/>
<panose>
<bFamilyType value="0"/>
<bSerifStyle value="0"/>
<bWeight value="5"/>
<bProportion value="0"/>
<bContrast value="0"/>
<bStrokeVariation value="0"/>
<bArmStyle value="0"/>
<bLetterForm value="0"/>
<bMidline value="0"/>
<bXHeight value="0"/>
</panose>
<ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
<ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
<ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
<ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
<achVendID value="UKWN"/>
<fsSelection value="00000000 01000000"/>
<usFirstCharIndex value="32"/>
<usLastCharIndex value="122"/>
<sTypoAscender value="800"/>
<sTypoDescender value="-200"/>
<sTypoLineGap value="200"/>
<usWinAscent value="1000"/>
<usWinDescent value="200"/>
<ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
<ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
<sxHeight value="500"/>
<sCapHeight value="700"/>
<usDefaultChar value="0"/>
<usBreakChar value="32"/>
<usMaxContext value="0"/>
</OS_2>
<hmtx>
<mtx name=".notdef" width="500" lsb="93"/>
<mtx name="1em" width="1000" lsb="93"/>
<mtx name="3em" width="3000" lsb="93"/>
</hmtx>
<cmap>
<tableVersion version="0"/>
<cmap_format_4 platformID="3" platEncID="10" language="0">
<map code="0x0061" name="1em" />
<map code="0x0062" name="3em" />
<map code="0x0063" name="1em" />
<map code="0x0064" name="1em" />
<map code="0x0065" name="1em" />
</cmap_format_4>
</cmap>
<loca>
<!-- The 'loca' table will be calculated by the compiler -->
</loca>
<glyf>
<TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
<TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
<TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
</glyf>
<name>
<namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
Copyright (C) 2017 The Android Open Source Project
</namerecord>
<namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
Sample Font
</namerecord>
<namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
Regular
</namerecord>
<namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
Sample Font
</namerecord>
<namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
SampleFont-Regular
</namerecord>
<namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
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.
</namerecord>
<namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
http://www.apache.org/licenses/LICENSE-2.0
</namerecord>
</name>
<post>
<formatType value="3.0"/>
<italicAngle value="0.0"/>
<underlinePosition value="-75"/>
<underlineThickness value="50"/>
<isFixedPitch value="0"/>
<minMemType42 value="0"/>
<maxMemType42 value="0"/>
<minMemType1 value="0"/>
<maxMemType1 value="0"/>
</post>
</ttFont>

Binary file not shown.

View File

@@ -0,0 +1,187 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright (C) 2017 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.
-->
<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
<GlyphOrder>
<GlyphID id="0" name=".notdef"/>
<GlyphID id="1" name="1em"/>
<GlyphID id="2" name="3em"/>
</GlyphOrder>
<head>
<tableVersion value="1.0"/>
<fontRevision value="1.0"/>
<checkSumAdjustment value="0x640cdb2f"/>
<magicNumber value="0x5f0f3cf5"/>
<flags value="00000000 00000011"/>
<unitsPerEm value="1000"/>
<created value="Fri Mar 17 07:26:00 2017"/>
<macStyle value="00000000 00000000"/>
<lowestRecPPEM value="7"/>
<fontDirectionHint value="2"/>
<glyphDataFormat value="0"/>
</head>
<hhea>
<tableVersion value="1.0"/>
<ascent value="1000"/>
<descent value="-200"/>
<lineGap value="0"/>
<caretSlopeRise value="1"/>
<caretSlopeRun value="0"/>
<caretOffset value="0"/>
<reserved0 value="0"/>
<reserved1 value="0"/>
<reserved2 value="0"/>
<reserved3 value="0"/>
<metricDataFormat value="0"/>
</hhea>
<maxp>
<tableVersion value="0x10000"/>
<maxZones value="0"/>
<maxTwilightPoints value="0"/>
<maxStorage value="0"/>
<maxFunctionDefs value="0"/>
<maxInstructionDefs value="0"/>
<maxStackElements value="0"/>
<maxSizeOfInstructions value="0"/>
<maxComponentElements value="0"/>
</maxp>
<OS_2>
<!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
will be recalculated by the compiler -->
<version value="3"/>
<xAvgCharWidth value="594"/>
<usWeightClass value="400"/>
<usWidthClass value="5"/>
<fsType value="00000000 00001000"/>
<ySubscriptXSize value="650"/>
<ySubscriptYSize value="600"/>
<ySubscriptXOffset value="0"/>
<ySubscriptYOffset value="75"/>
<ySuperscriptXSize value="650"/>
<ySuperscriptYSize value="600"/>
<ySuperscriptXOffset value="0"/>
<ySuperscriptYOffset value="350"/>
<yStrikeoutSize value="50"/>
<yStrikeoutPosition value="300"/>
<sFamilyClass value="0"/>
<panose>
<bFamilyType value="0"/>
<bSerifStyle value="0"/>
<bWeight value="5"/>
<bProportion value="0"/>
<bContrast value="0"/>
<bStrokeVariation value="0"/>
<bArmStyle value="0"/>
<bLetterForm value="0"/>
<bMidline value="0"/>
<bXHeight value="0"/>
</panose>
<ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
<ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
<ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
<ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
<achVendID value="UKWN"/>
<fsSelection value="00000000 01000000"/>
<usFirstCharIndex value="32"/>
<usLastCharIndex value="122"/>
<sTypoAscender value="800"/>
<sTypoDescender value="-200"/>
<sTypoLineGap value="200"/>
<usWinAscent value="1000"/>
<usWinDescent value="200"/>
<ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
<ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
<sxHeight value="500"/>
<sCapHeight value="700"/>
<usDefaultChar value="0"/>
<usBreakChar value="32"/>
<usMaxContext value="0"/>
</OS_2>
<hmtx>
<mtx name=".notdef" width="500" lsb="93"/>
<mtx name="1em" width="1000" lsb="93"/>
<mtx name="3em" width="3000" lsb="93"/>
</hmtx>
<cmap>
<tableVersion version="0"/>
<cmap_format_4 platformID="3" platEncID="10" language="0">
<map code="0x0061" name="1em" />
<map code="0x0062" name="1em" />
<map code="0x0063" name="3em" />
<map code="0x0064" name="1em" />
<map code="0x0065" name="1em" />
</cmap_format_4>
</cmap>
<loca>
<!-- The 'loca' table will be calculated by the compiler -->
</loca>
<glyf>
<TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
<TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
<TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
</glyf>
<name>
<namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
Copyright (C) 2017 The Android Open Source Project
</namerecord>
<namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
Sample Font
</namerecord>
<namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
Regular
</namerecord>
<namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
Sample Font
</namerecord>
<namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
SampleFont-Regular
</namerecord>
<namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
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.
</namerecord>
<namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
http://www.apache.org/licenses/LICENSE-2.0
</namerecord>
</name>
<post>
<formatType value="3.0"/>
<italicAngle value="0.0"/>
<underlinePosition value="-75"/>
<underlineThickness value="50"/>
<isFixedPitch value="0"/>
<minMemType42 value="0"/>
<maxMemType42 value="0"/>
<minMemType1 value="0"/>
<maxMemType1 value="0"/>
</post>
</ttFont>

Binary file not shown.

View File

@@ -0,0 +1,180 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright (C) 2017 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.
-->
<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
<GlyphOrder>
<GlyphID id="0" name=".notdef"/>
<GlyphID id="1" name="dummy"/>
</GlyphOrder>
<head>
<tableVersion value="1.0"/>
<fontRevision value="1.0"/>
<checkSumAdjustment value="0x640cdb2f"/>
<magicNumber value="0x5f0f3cf5"/>
<flags value="00000000 00000011"/>
<unitsPerEm value="1000"/>
<created value="Fri Mar 17 07:26:00 2017"/>
<macStyle value="00000000 00000000"/>
<lowestRecPPEM value="7"/>
<fontDirectionHint value="2"/>
<glyphDataFormat value="0"/>
</head>
<hhea>
<tableVersion value="1.0"/>
<ascent value="1000"/>
<descent value="-200"/>
<lineGap value="0"/>
<caretSlopeRise value="1"/>
<caretSlopeRun value="0"/>
<caretOffset value="0"/>
<reserved0 value="0"/>
<reserved1 value="0"/>
<reserved2 value="0"/>
<reserved3 value="0"/>
<metricDataFormat value="0"/>
</hhea>
<maxp>
<tableVersion value="0x10000"/>
<maxZones value="0"/>
<maxTwilightPoints value="0"/>
<maxStorage value="0"/>
<maxFunctionDefs value="0"/>
<maxInstructionDefs value="0"/>
<maxStackElements value="0"/>
<maxSizeOfInstructions value="0"/>
<maxComponentElements value="0"/>
</maxp>
<OS_2>
<!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
will be recalculated by the compiler -->
<version value="3"/>
<xAvgCharWidth value="594"/>
<usWeightClass value="400"/>
<usWidthClass value="5"/>
<fsType value="00000000 00001000"/>
<ySubscriptXSize value="650"/>
<ySubscriptYSize value="600"/>
<ySubscriptXOffset value="0"/>
<ySubscriptYOffset value="75"/>
<ySuperscriptXSize value="650"/>
<ySuperscriptYSize value="600"/>
<ySuperscriptXOffset value="0"/>
<ySuperscriptYOffset value="350"/>
<yStrikeoutSize value="50"/>
<yStrikeoutPosition value="300"/>
<sFamilyClass value="0"/>
<panose>
<bFamilyType value="0"/>
<bSerifStyle value="0"/>
<bWeight value="5"/>
<bProportion value="0"/>
<bContrast value="0"/>
<bStrokeVariation value="0"/>
<bArmStyle value="0"/>
<bLetterForm value="0"/>
<bMidline value="0"/>
<bXHeight value="0"/>
</panose>
<ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
<ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
<ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
<ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
<achVendID value="UKWN"/>
<fsSelection value="00000000 01000000"/>
<usFirstCharIndex value="32"/>
<usLastCharIndex value="122"/>
<sTypoAscender value="800"/>
<sTypoDescender value="-200"/>
<sTypoLineGap value="200"/>
<usWinAscent value="1000"/>
<usWinDescent value="200"/>
<ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
<ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
<sxHeight value="500"/>
<sCapHeight value="700"/>
<usDefaultChar value="0"/>
<usBreakChar value="32"/>
<usMaxContext value="0"/>
</OS_2>
<hmtx>
<mtx name=".notdef" width="500" lsb="93"/>
<mtx name="dummy" width="500" lsb="93"/>
</hmtx>
<cmap>
<tableVersion version="0"/>
<cmap_format_4 platformID="3" platEncID="10" language="0">
<map code="0xFFFD" name="dummy" /> <!-- dummy entry -->
</cmap_format_4>
</cmap>
<loca>
<!-- The 'loca' table will be calculated by the compiler -->
</loca>
<glyf>
<TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
<TTGlyph name="dummy" xMin="0" yMin="0" xMax="0" yMax="0" />
</glyf>
<name>
<namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
Copyright (C) 2017 The Android Open Source Project
</namerecord>
<namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
Sample Font
</namerecord>
<namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
Regular
</namerecord>
<namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
Sample Font
</namerecord>
<namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
SampleFont-Regular
</namerecord>
<namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
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.
</namerecord>
<namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
http://www.apache.org/licenses/LICENSE-2.0
</namerecord>
</name>
<post>
<formatType value="3.0"/>
<italicAngle value="0.0"/>
<underlinePosition value="-75"/>
<underlineThickness value="50"/>
<isFixedPitch value="0"/>
<minMemType42 value="0"/>
<maxMemType42 value="0"/>
<minMemType1 value="0"/>
<maxMemType1 value="0"/>
</post>
</ttFont>

View File

@@ -0,0 +1,469 @@
/*
* Copyright (C) 2017 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;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import android.content.Context;
import android.content.res.AssetManager;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.util.ArrayMap;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class TypefaceSystemFallbackTest {
private static final String SYSTEM_FONT_DIR = "/system/fonts/";
private static final String SYSTEM_FONTS_XML = "/system/etc/fonts.xml";
private static final String[] TEST_FONT_FILES = {
"a3em.ttf", // Supports "a","b","c". The width of "a" is 3em, others are 1em.
"b3em.ttf", // Supports "a","b","c". The width of "b" is 3em, others are 1em.
"c3em.ttf", // Supports "a","b","c". The width of "c" is 3em, others are 1em.
"all2em.ttf", // Supports "a,","b","c". All of them have the same width of 2em.
"no_coverage.ttf", // This font doesn't support any characters.
};
private static final String TEST_FONTS_XML;
private static final String TEST_FONT_DIR;
private static final float GLYPH_1EM_WIDTH;
private static final float GLYPH_2EM_WIDTH;
private static final float GLYPH_3EM_WIDTH;
static {
final Context targetCtx = InstrumentationRegistry.getInstrumentation().getTargetContext();
final File cacheDir = new File(targetCtx.getCacheDir(), "TypefaceSystemFallbackTest");
if (!cacheDir.isDirectory()) {
cacheDir.mkdirs();
}
TEST_FONT_DIR = cacheDir.getAbsolutePath() + "/";
TEST_FONTS_XML = new File(cacheDir, "fonts.xml").getAbsolutePath();
final AssetManager am =
InstrumentationRegistry.getInstrumentation().getContext().getAssets();
final Paint paint = new Paint();
paint.setTypeface(new Typeface.Builder(am, "fonts/a3em.ttf").build());
GLYPH_3EM_WIDTH = paint.measureText("a");
GLYPH_1EM_WIDTH = paint.measureText("b");
paint.setTypeface(new Typeface.Builder(am, "fonts/all2em.ttf").build());
GLYPH_2EM_WIDTH = paint.measureText("a");
}
@Before
public void setUp() {
final AssetManager am =
InstrumentationRegistry.getInstrumentation().getContext().getAssets();
for (final String fontFile : TEST_FONT_FILES) {
final String sourceInAsset = "fonts/" + fontFile;
final File outInCache = new File(TEST_FONT_DIR, fontFile);
try (InputStream is = am.open(sourceInAsset)) {
Files.copy(is, outInCache.toPath(), StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@After
public void tearDown() {
for (final String fontFile : TEST_FONT_FILES) {
final File outInCache = new File(TEST_FONT_DIR, fontFile);
outInCache.delete();
}
}
private static void buildSystemFallback(String xml,
ArrayMap<String, Typeface> fontMap, ArrayMap<String, FontFamily[]> fallbackMap) {
try (FileOutputStream fos = new FileOutputStream(TEST_FONTS_XML)) {
fos.write(xml.getBytes(Charset.forName("UTF-8")));
} catch (IOException e) {
throw new RuntimeException(e);
}
Typeface.buildSystemFallback(TEST_FONTS_XML, TEST_FONT_DIR, fontMap, fallbackMap);
}
@Test
public void testBuildSystemFallback() {
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
Typeface.buildSystemFallback(SYSTEM_FONTS_XML, SYSTEM_FONT_DIR, fontMap, fallbackMap);
assertFalse(fontMap.isEmpty());
assertFalse(fallbackMap.isEmpty());
}
@Test
public void testBuildSystemFallback_NonExistentFontShouldBeIgnored() {
final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ "<familyset version='22'>"
+ " <family name='sans-serif'>"
+ " <font weight='400' style='normal'>a3em.ttf</font>"
+ " <font weight='400' style='normal'>NoSuchFont.ttf</font>"
+ " </family>"
+ " <family name='NoSuchFont'>"
+ " <font weight='400' style='normal'>NoSuchFont.ttf</font>"
+ " </family>"
+ " <family>"
+ " <font weight='400' style='normal'>NoSuchFont.ttf</font>"
+ " </family>"
+ "</familyset>";
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
buildSystemFallback(xml, fontMap, fallbackMap);
assertEquals(1, fontMap.size());
assertTrue(fontMap.containsKey("sans-serif"));
assertEquals(1, fallbackMap.size());
assertTrue(fallbackMap.containsKey("sans-serif"));
}
@Test
public void testBuildSystemFallback_NamedFamily() {
final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ "<familyset version='22'>"
+ " <family name='sans-serif'>"
+ " <font weight='400' style='normal'>a3em.ttf</font>"
+ " </family>"
+ " <family name='test'>"
+ " <font weight='400' style='normal'>b3em.ttf</font>"
+ " </family>"
+ " <family name='test2'>"
+ " <font weight='400' style='normal'>c3em.ttf</font>"
+ " </family>"
+ " <family>"
+ " <font weight='400' style='normal'>all2em.ttf</font>"
+ " </family>"
+ "</familyset>";
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
buildSystemFallback(xml, fontMap, fallbackMap);
final Paint paint = new Paint();
final Typeface sansSerifTypeface = fontMap.get("sans-serif");
assertNotNull(sansSerifTypeface);
paint.setTypeface(sansSerifTypeface);
assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
final Typeface testTypeface = fontMap.get("test");
assertNotNull(testTypeface);
paint.setTypeface(testTypeface);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f);
assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
final Typeface test2Typeface = fontMap.get("test2");
assertNotNull(test2Typeface);
paint.setTypeface(test2Typeface);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f);
assertEquals(GLYPH_3EM_WIDTH, paint.measureText("c"), 0.0f);
}
@Test
public void testBuildSystemFallback_defaultFallback() {
final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ "<familyset version='22'>"
+ " <family name='sans-serif'>"
+ " <font weight='400' style='normal'>no_coverage.ttf</font>"
+ " </family>"
+ " <family name='test'>"
+ " <font weight='400' style='normal'>no_coverage.ttf</font>"
+ " </family>"
+ " <family>"
+ " <font weight='400' style='normal'>a3em.ttf</font>"
+ " </family>"
+ " <family>"
+ " <font weight='400' style='normal'>all2em.ttf</font>"
+ " </family>"
+ "</familyset>";
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
buildSystemFallback(xml, fontMap, fallbackMap);
final Paint paint = new Paint();
final Typeface sansSerifTypeface = fontMap.get("sans-serif");
assertNotNull(sansSerifTypeface);
paint.setTypeface(sansSerifTypeface);
assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
final Typeface testTypeface = fontMap.get("test");
assertNotNull(testTypeface);
paint.setTypeface(testTypeface);
assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
}
@Test
public void testBuildSystemFallback_namedFallbackFamily() {
final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ "<familyset version='22'>"
+ " <family name='sans-serif'>"
+ " <font weight='400' style='normal'>no_coverage.ttf</font>"
+ " </family>"
+ " <family name='test'>"
+ " <font weight='400' style='normal'>no_coverage.ttf</font>"
+ " </family>"
+ " <family name='test2'>"
+ " <font weight='400' style='normal'>no_coverage.ttf</font>"
+ " </family>"
+ " <family>"
+ " <font weight='400' style='normal' fallbackFor='test'>a3em.ttf</font>"
+ " </family>"
+ " <family>"
+ " <font weight='400' style='normal' fallbackFor='test2'>b3em.ttf</font>"
+ " </family>"
+ " <family>"
+ " <font weight='400' style='normal'>all2em.ttf</font>"
+ " </family>"
+ "</familyset>";
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
buildSystemFallback(xml, fontMap, fallbackMap);
final Paint paint = new Paint();
final Typeface sansSerifTypeface = fontMap.get("sans-serif");
assertNotNull(sansSerifTypeface);
paint.setTypeface(sansSerifTypeface);
assertEquals(GLYPH_2EM_WIDTH, paint.measureText("a"), 0.0f);
assertEquals(GLYPH_2EM_WIDTH, paint.measureText("b"), 0.0f);
assertEquals(GLYPH_2EM_WIDTH, paint.measureText("c"), 0.0f);
final Typeface testTypeface = fontMap.get("test");
assertNotNull(testTypeface);
paint.setTypeface(testTypeface);
assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
final Typeface test2Typeface = fontMap.get("test2");
assertNotNull(test2Typeface);
paint.setTypeface(test2Typeface);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f);
assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
}
@Test
public void testBuildSystemFallback_namedFallbackFamily2() {
final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ "<familyset version='22'>"
+ " <family name='sans-serif'>"
+ " <font weight='400' style='normal'>no_coverage.ttf</font>"
+ " </family>"
+ " <family name='test'>"
+ " <font weight='400' style='normal'>no_coverage.ttf</font>"
+ " </family>"
+ " <family name='test2'>"
+ " <font weight='400' style='normal'>no_coverage.ttf</font>"
+ " </family>"
+ " <family>"
+ " <font weight='400' style='normal' fallbackFor='test'>a3em.ttf</font>"
+ " <font weight='400' style='normal'>b3em.ttf</font>"
+ " </family>"
+ " <family>"
+ " <font weight='400' style='normal'>all2em.ttf</font>"
+ " </family>"
+ "</familyset>";
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
buildSystemFallback(xml, fontMap, fallbackMap);
final Paint paint = new Paint();
final Typeface sansSerifTypeface = fontMap.get("sans-serif");
assertNotNull(sansSerifTypeface);
paint.setTypeface(sansSerifTypeface);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f);
assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
final Typeface testTypeface = fontMap.get("test");
assertNotNull(testTypeface);
paint.setTypeface(testTypeface);
assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
final Typeface test2Typeface = fontMap.get("test2");
assertNotNull(test2Typeface);
paint.setTypeface(test2Typeface);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f);
assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
}
@Test
public void testBuildSystemFallback_ImplicitSansSerifFallback() {
final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ "<familyset version='22'>"
+ " <family name='sans-serif'>"
+ " <font weight='400' style='normal'>a3em.ttf</font>"
+ " </family>"
+ " <family name='test'>"
+ " <font weight='400' style='normal'>no_coverage.ttf</font>"
+ " </family>"
+ " <family name='test2'>"
+ " <font weight='400' style='normal'>no_coverage.ttf</font>"
+ " </family>"
+ " <family>"
+ " <font weight='400' style='normal'>all2em.ttf</font>"
+ " </family>"
+ "</familyset>";
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
buildSystemFallback(xml, fontMap, fallbackMap);
final Paint paint = new Paint();
final Typeface testTypeface = fontMap.get("test");
assertNotNull(testTypeface);
paint.setTypeface(testTypeface);
assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
final Typeface test2Typeface = fontMap.get("test2");
assertNotNull(test2Typeface);
paint.setTypeface(test2Typeface);
assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
}
@Test
public void testBuildSystemFallback_ElegantFallback() {
final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ "<familyset version='22'>"
+ " <family name='sans-serif'>"
+ " <font weight='400' style='normal'>no_coverage.ttf</font>"
+ " </family>"
+ " <family name='serif'>"
+ " <font weight='400' style='normal'>no_coverage.ttf</font>"
+ " </family>"
+ " <family variant='elegant'>"
+ " <font weight='400' style='normal'>a3em.ttf</font>"
+ " </family>"
+ " <family variant='compact'>"
+ " <font weight='400' style='normal'>b3em.ttf</font>"
+ " </family>"
+ "</familyset>";
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
buildSystemFallback(xml, fontMap, fallbackMap);
final Paint paint = new Paint();
final Typeface testTypeface = fontMap.get("serif");
assertNotNull(testTypeface);
paint.setTypeface(testTypeface);
paint.setElegantTextHeight(true);
assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
paint.setElegantTextHeight(false);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f);
assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
}
@Test
public void testBuildSystemFallback_ElegantFallback_customFallback() {
final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ "<familyset version='22'>"
+ " <family name='sans-serif'>"
+ " <font weight='400' style='normal'>no_coverage.ttf</font>"
+ " </family>"
+ " <family name='serif'>"
+ " <font weight='400' style='normal'>no_coverage.ttf</font>"
+ " </family>"
+ " <family variant='elegant'>"
+ " <font weight='400' style='normal'>a3em.ttf</font>"
+ " <font weight='400' style='normal' fallbackFor='serif'>b3em.ttf</font>"
+ " </family>"
+ " <family variant='compact'>"
+ " <font weight='400' style='normal'>c3em.ttf</font>"
+ " </family>"
+ "</familyset>";
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
buildSystemFallback(xml, fontMap, fallbackMap);
final Paint paint = new Paint();
Typeface testTypeface = fontMap.get("serif");
assertNotNull(testTypeface);
paint.setTypeface(testTypeface);
paint.setElegantTextHeight(true);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f);
assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
paint.setElegantTextHeight(false);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f);
assertEquals(GLYPH_3EM_WIDTH, paint.measureText("c"), 0.0f);
testTypeface = fontMap.get("sans-serif");
assertNotNull(testTypeface);
paint.setTypeface(testTypeface);
paint.setElegantTextHeight(true);
assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
paint.setElegantTextHeight(false);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f);
assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f);
assertEquals(GLYPH_3EM_WIDTH, paint.measureText("c"), 0.0f);
}
}

View File

@@ -111,6 +111,7 @@ public class FontListParser {
String weightStr = parser.getAttributeValue(null, "weight");
int weight = weightStr == null ? 400 : Integer.parseInt(weightStr);
boolean isItalic = "italic".equals(parser.getAttributeValue(null, "style"));
String fallbackFor = parser.getAttributeValue(null, "fallbackFor");
StringBuilder filename = new StringBuilder();
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() == XmlPullParser.TEXT) {
@@ -126,7 +127,7 @@ public class FontListParser {
}
String sanitizedName = FILENAME_WHITESPACE_PATTERN.matcher(filename).replaceAll("");
return new FontConfig.Font(sanitizedName, index,
axes.toArray(new FontVariationAxis[axes.size()]), weight, isItalic);
axes.toArray(new FontVariationAxis[axes.size()]), weight, isItalic, fallbackFor);
}
private static FontVariationAxis readAxis(XmlPullParser parser)

View File

@@ -38,6 +38,7 @@ import android.os.ResultReceiver;
import android.provider.FontRequest;
import android.provider.FontsContract;
import android.text.FontConfig;
import android.util.ArrayMap;
import android.util.Base64;
import android.util.Log;
import android.util.LongSparseArray;
@@ -45,6 +46,7 @@ import android.util.LruCache;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import libcore.io.IoUtils;
@@ -105,12 +107,10 @@ public class Typeface {
private static final LruCache<String, Typeface> sDynamicTypefaceCache = new LruCache<>(16);
static Typeface sDefaultTypeface;
static Map<String, Typeface> sSystemFontMap;
static FontFamily[] sFallbackFonts;
static final Map<String, Typeface> sSystemFontMap;
static final Map<String, FontFamily[]> sSystemFallbackMap;
private static final Object sLock = new Object();
static final String FONTS_CONFIG = "fonts.xml";
/**
* @hide
*/
@@ -129,6 +129,7 @@ public class Typeface {
// Must be the same as the C++ constant in core/jni/android/graphics/FontFamily.cpp
/** @hide */
public static final int RESOLVE_BY_FONT_TABLE = -1;
private static final String DEFAULT_FAMILY = "sans-serif";
// Style value for building typeface.
private static final int STYLE_NORMAL = 0;
@@ -163,28 +164,27 @@ public class Typeface {
*/
@Nullable
public static Typeface createFromResources(AssetManager mgr, String path, int cookie) {
if (sFallbackFonts != null) {
synchronized (sDynamicTypefaceCache) {
final String key = Builder.createAssetUid(
mgr, path, 0 /* ttcIndex */, null /* axes */,
RESOLVE_BY_FONT_TABLE /* weight */, RESOLVE_BY_FONT_TABLE /* italic */);
Typeface typeface = sDynamicTypefaceCache.get(key);
if (typeface != null) return typeface;
synchronized (sDynamicTypefaceCache) {
final String key = Builder.createAssetUid(
mgr, path, 0 /* ttcIndex */, null /* axes */,
RESOLVE_BY_FONT_TABLE /* weight */, RESOLVE_BY_FONT_TABLE /* italic */,
DEFAULT_FAMILY);
Typeface typeface = sDynamicTypefaceCache.get(key);
if (typeface != null) return typeface;
FontFamily fontFamily = new FontFamily();
// TODO: introduce ttc index and variation settings to resource type font.
if (fontFamily.addFontFromAssetManager(mgr, path, cookie, false /* isAsset */,
0 /* ttcIndex */, RESOLVE_BY_FONT_TABLE /* weight */,
RESOLVE_BY_FONT_TABLE /* italic */, null /* axes */)) {
if (!fontFamily.freeze()) {
return null;
}
FontFamily[] families = {fontFamily};
typeface = createFromFamiliesWithDefault(families,
RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
sDynamicTypefaceCache.put(key, typeface);
return typeface;
FontFamily fontFamily = new FontFamily();
// TODO: introduce ttc index and variation settings to resource type font.
if (fontFamily.addFontFromAssetManager(mgr, path, cookie, false /* isAsset */,
0 /* ttcIndex */, RESOLVE_BY_FONT_TABLE /* weight */,
RESOLVE_BY_FONT_TABLE /* italic */, null /* axes */)) {
if (!fontFamily.freeze()) {
return null;
}
FontFamily[] families = {fontFamily};
typeface = createFromFamiliesWithDefault(families, DEFAULT_FAMILY,
RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
sDynamicTypefaceCache.put(key, typeface);
return typeface;
}
}
return null;
@@ -197,61 +197,57 @@ public class Typeface {
@Nullable
public static Typeface createFromResources(
FamilyResourceEntry entry, AssetManager mgr, String path) {
if (sFallbackFonts != null) {
if (entry instanceof ProviderResourceEntry) {
final ProviderResourceEntry providerEntry = (ProviderResourceEntry) entry;
// Downloadable font
List<List<String>> givenCerts = providerEntry.getCerts();
List<List<byte[]>> certs = new ArrayList<>();
if (givenCerts != null) {
for (int i = 0; i < givenCerts.size(); i++) {
List<String> certSet = givenCerts.get(i);
List<byte[]> byteArraySet = new ArrayList<>();
for (int j = 0; j < certSet.size(); j++) {
byteArraySet.add(Base64.decode(certSet.get(j), Base64.DEFAULT));
}
certs.add(byteArraySet);
if (entry instanceof ProviderResourceEntry) {
final ProviderResourceEntry providerEntry = (ProviderResourceEntry) entry;
// Downloadable font
List<List<String>> givenCerts = providerEntry.getCerts();
List<List<byte[]>> certs = new ArrayList<>();
if (givenCerts != null) {
for (int i = 0; i < givenCerts.size(); i++) {
List<String> certSet = givenCerts.get(i);
List<byte[]> byteArraySet = new ArrayList<>();
for (int j = 0; j < certSet.size(); j++) {
byteArraySet.add(Base64.decode(certSet.get(j), Base64.DEFAULT));
}
}
// Downloaded font and it wasn't cached, request it again and return a
// default font instead (nothing we can do now).
FontRequest request = new FontRequest(providerEntry.getAuthority(),
providerEntry.getPackage(), providerEntry.getQuery(), certs);
Typeface typeface = FontsContract.getFontSync(request);
return typeface == null ? DEFAULT : typeface;
}
Typeface typeface = findFromCache(mgr, path);
if (typeface != null) return typeface;
// family is FontFamilyFilesResourceEntry
final FontFamilyFilesResourceEntry filesEntry =
(FontFamilyFilesResourceEntry) entry;
FontFamily fontFamily = new FontFamily();
for (final FontFileResourceEntry fontFile : filesEntry.getEntries()) {
// TODO: Add ttc and variation font support. (b/37853920)
if (!fontFamily.addFontFromAssetManager(mgr, fontFile.getFileName(),
0 /* resourceCookie */, false /* isAsset */, 0 /* ttcIndex */,
fontFile.getWeight(), fontFile.getItalic(), null /* axes */)) {
return null;
certs.add(byteArraySet);
}
}
if (!fontFamily.freeze()) {
// Downloaded font and it wasn't cached, request it again and return a
// default font instead (nothing we can do now).
FontRequest request = new FontRequest(providerEntry.getAuthority(),
providerEntry.getPackage(), providerEntry.getQuery(), certs);
Typeface typeface = FontsContract.getFontSync(request);
return typeface == null ? DEFAULT : typeface;
}
Typeface typeface = findFromCache(mgr, path);
if (typeface != null) return typeface;
// family is FontFamilyFilesResourceEntry
final FontFamilyFilesResourceEntry filesEntry = (FontFamilyFilesResourceEntry) entry;
FontFamily fontFamily = new FontFamily();
for (final FontFileResourceEntry fontFile : filesEntry.getEntries()) {
// TODO: Add ttc and variation font support. (b/37853920)
if (!fontFamily.addFontFromAssetManager(mgr, fontFile.getFileName(),
0 /* resourceCookie */, false /* isAsset */, 0 /* ttcIndex */,
fontFile.getWeight(), fontFile.getItalic(), null /* axes */)) {
return null;
}
FontFamily[] familyChain = { fontFamily };
typeface = createFromFamiliesWithDefault(familyChain,
RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
synchronized (sDynamicTypefaceCache) {
final String key = Builder.createAssetUid(mgr, path, 0 /* ttcIndex */,
null /* axes */, RESOLVE_BY_FONT_TABLE /* weight */,
RESOLVE_BY_FONT_TABLE /* italic */);
sDynamicTypefaceCache.put(key, typeface);
}
return typeface;
}
return null;
if (!fontFamily.freeze()) {
return null;
}
FontFamily[] familyChain = { fontFamily };
typeface = createFromFamiliesWithDefault(familyChain, DEFAULT_FAMILY,
RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
synchronized (sDynamicTypefaceCache) {
final String key = Builder.createAssetUid(mgr, path, 0 /* ttcIndex */,
null /* axes */, RESOLVE_BY_FONT_TABLE /* weight */,
RESOLVE_BY_FONT_TABLE /* italic */, DEFAULT_FAMILY);
sDynamicTypefaceCache.put(key, typeface);
}
return typeface;
}
/**
@@ -261,7 +257,8 @@ public class Typeface {
public static Typeface findFromCache(AssetManager mgr, String path) {
synchronized (sDynamicTypefaceCache) {
final String key = Builder.createAssetUid(mgr, path, 0 /* ttcIndex */, null /* axes */,
RESOLVE_BY_FONT_TABLE /* weight */, RESOLVE_BY_FONT_TABLE /* italic */);
RESOLVE_BY_FONT_TABLE /* weight */, RESOLVE_BY_FONT_TABLE /* italic */,
DEFAULT_FAMILY);
Typeface typeface = sDynamicTypefaceCache.get(key);
if (typeface != null) {
return typeface;
@@ -498,7 +495,7 @@ public class Typeface {
* @return Unique id for a given AssetManager and asset path.
*/
private static String createAssetUid(final AssetManager mgr, String path, int ttcIndex,
@Nullable FontVariationAxis[] axes, int weight, int italic) {
@Nullable FontVariationAxis[] axes, int weight, int italic, String fallback) {
final SparseArray<String> pkgs = mgr.getAssignedPackageIdentifiers();
final StringBuilder builder = new StringBuilder();
final int size = pkgs.size();
@@ -513,7 +510,11 @@ public class Typeface {
builder.append(Integer.toString(weight));
builder.append("-");
builder.append(Integer.toString(italic));
builder.append("-");
// Family name may contain hyphen. Use double hyphen for avoiding key conflicts before
// and after appending falblack name.
builder.append("--");
builder.append(fallback);
builder.append("--");
if (axes != null) {
for (FontVariationAxis axis : axes) {
builder.append(axis.getTag());
@@ -593,13 +594,15 @@ public class Typeface {
return resolveFallbackTypeface();
}
FontFamily[] families = { fontFamily };
return createFromFamiliesWithDefault(families, mWeight, mItalic);
return createFromFamiliesWithDefault(families, mFallbackFamilyName, mWeight,
mItalic);
} catch (IOException e) {
return resolveFallbackTypeface();
}
} else if (mAssetManager != null) { // Builder is created with asset manager.
final String key = createAssetUid(
mAssetManager, mPath, mTtcIndex, mAxes, mWeight, mItalic);
mAssetManager, mPath, mTtcIndex, mAxes, mWeight, mItalic,
mFallbackFamilyName);
synchronized (sLock) {
Typeface typeface = sDynamicTypefaceCache.get(key);
if (typeface != null) return typeface;
@@ -613,7 +616,8 @@ public class Typeface {
return resolveFallbackTypeface();
}
FontFamily[] families = { fontFamily };
typeface = createFromFamiliesWithDefault(families, mWeight, mItalic);
typeface = createFromFamiliesWithDefault(families, mFallbackFamilyName,
mWeight, mItalic);
sDynamicTypefaceCache.put(key, typeface);
return typeface;
}
@@ -627,7 +631,8 @@ public class Typeface {
return resolveFallbackTypeface();
}
FontFamily[] families = { fontFamily };
return createFromFamiliesWithDefault(families, mWeight, mItalic);
return createFromFamiliesWithDefault(families, mFallbackFamilyName, mWeight,
mItalic);
} else if (mFonts != null) {
final FontFamily fontFamily = new FontFamily();
boolean atLeastOneFont = false;
@@ -653,7 +658,8 @@ public class Typeface {
}
fontFamily.freeze();
FontFamily[] families = { fontFamily };
return createFromFamiliesWithDefault(families, mWeight, mItalic);
return createFromFamiliesWithDefault(families, mFallbackFamilyName, mWeight,
mItalic);
}
// Must not reach here.
@@ -673,10 +679,7 @@ public class Typeface {
* @return The best matching typeface.
*/
public static Typeface create(String familyName, int style) {
if (sSystemFontMap != null) {
return create(sSystemFontMap.get(familyName), style);
}
return null;
return create(sSystemFontMap.get(familyName), style);
}
/**
@@ -751,34 +754,33 @@ public class Typeface {
if (path == null) {
throw new NullPointerException(); // for backward compatibility
}
if (sFallbackFonts != null) {
synchronized (sLock) {
Typeface typeface = new Builder(mgr, path).build();
if (typeface != null) return typeface;
synchronized (sLock) {
Typeface typeface = new Builder(mgr, path).build();
if (typeface != null) return typeface;
final String key = Builder.createAssetUid(mgr, path, 0 /* ttcIndex */,
null /* axes */, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
typeface = sDynamicTypefaceCache.get(key);
if (typeface != null) return typeface;
final String key = Builder.createAssetUid(mgr, path, 0 /* ttcIndex */,
null /* axes */, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE,
DEFAULT_FAMILY);
typeface = sDynamicTypefaceCache.get(key);
if (typeface != null) return typeface;
final FontFamily fontFamily = new FontFamily();
if (fontFamily.addFontFromAssetManager(mgr, path, 0, true /* isAsset */,
0 /* ttc index */, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE,
null /* axes */)) {
// Due to backward compatibility, even if the font is not supported by our font
// stack, we need to place the empty font at the first place. The typeface with
// empty font behaves different from default typeface especially in fallback
// font selection.
fontFamily.allowUnsupportedFont();
fontFamily.freeze();
final FontFamily[] families = { fontFamily };
typeface = createFromFamiliesWithDefault(families, RESOLVE_BY_FONT_TABLE,
RESOLVE_BY_FONT_TABLE);
sDynamicTypefaceCache.put(key, typeface);
return typeface;
} else {
fontFamily.abortCreation();
}
final FontFamily fontFamily = new FontFamily();
if (fontFamily.addFontFromAssetManager(mgr, path, 0, true /* isAsset */,
0 /* ttc index */, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE,
null /* axes */)) {
// Due to backward compatibility, even if the font is not supported by our font
// stack, we need to place the empty font at the first place. The typeface with
// empty font behaves different from default typeface especially in fallback
// font selection.
fontFamily.allowUnsupportedFont();
fontFamily.freeze();
final FontFamily[] families = { fontFamily };
typeface = createFromFamiliesWithDefault(families, DEFAULT_FAMILY,
RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
sDynamicTypefaceCache.put(key, typeface);
return typeface;
} else {
fontFamily.abortCreation();
}
}
throw new RuntimeException("Font asset not found " + path);
@@ -815,22 +817,20 @@ public class Typeface {
* @return The new typeface.
*/
public static Typeface createFromFile(@Nullable String path) {
if (sFallbackFonts != null) {
final FontFamily fontFamily = new FontFamily();
if (fontFamily.addFont(path, 0 /* ttcIndex */, null /* axes */,
RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)) {
// Due to backward compatibility, even if the font is not supported by our font
// stack, we need to place the empty font at the first place. The typeface with
// empty font behaves different from default typeface especially in fallback font
// selection.
fontFamily.allowUnsupportedFont();
fontFamily.freeze();
FontFamily[] families = { fontFamily };
return createFromFamiliesWithDefault(families, RESOLVE_BY_FONT_TABLE,
RESOLVE_BY_FONT_TABLE);
} else {
fontFamily.abortCreation();
}
final FontFamily fontFamily = new FontFamily();
if (fontFamily.addFont(path, 0 /* ttcIndex */, null /* axes */,
RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)) {
// Due to backward compatibility, even if the font is not supported by our font
// stack, we need to place the empty font at the first place. The typeface with
// empty font behaves different from default typeface especially in fallback font
// selection.
fontFamily.allowUnsupportedFont();
fontFamily.freeze();
FontFamily[] families = { fontFamily };
return createFromFamiliesWithDefault(families, DEFAULT_FAMILY,
RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
} else {
fontFamily.abortCreation();
}
throw new RuntimeException("Font not found " + path);
}
@@ -852,6 +852,8 @@ public class Typeface {
/**
* Create a new typeface from an array of font families, including
* also the font families in the fallback list.
* @param fallbackName the family name. If given families don't support characters, the
* characters will be rendered with this family.
* @param weight the weight for this family. {@link RESOLVE_BY_FONT_TABLE} can be used. In that
* case, the table information in the first family's font is used. If the first
* family has multiple fonts, the closest to the regular weight and upright font
@@ -863,13 +865,17 @@ public class Typeface {
* @param families array of font families
*/
private static Typeface createFromFamiliesWithDefault(FontFamily[] families,
int weight, int italic) {
long[] ptrArray = new long[families.length + sFallbackFonts.length];
String fallbackName, int weight, int italic) {
FontFamily[] fallback = sSystemFallbackMap.get(fallbackName);
if (fallback == null) {
fallback = sSystemFallbackMap.get(DEFAULT_FAMILY);
}
long[] ptrArray = new long[families.length + fallback.length];
for (int i = 0; i < families.length; i++) {
ptrArray[i] = families[i].mNativePtr;
}
for (int i = 0; i < sFallbackFonts.length; i++) {
ptrArray[i + families.length] = sFallbackFonts[i].mNativePtr;
for (int i = 0; i < fallback.length; i++) {
ptrArray[i + families.length] = fallback[i].mNativePtr;
}
return new Typeface(nativeCreateFromArray(ptrArray, weight, italic));
}
@@ -885,113 +891,189 @@ public class Typeface {
mWeight = nativeGetWeight(ni);
}
private static FontFamily makeFamilyFromParsed(FontConfig.Family family,
Map<String, ByteBuffer> bufferForPath) {
FontFamily fontFamily = new FontFamily(family.getLanguage(), family.getVariant());
for (FontConfig.Font font : family.getFonts()) {
String fullPathName = "/system/fonts/" + font.getFontName();
ByteBuffer fontBuffer = bufferForPath.get(fullPathName);
if (fontBuffer == null) {
try (FileInputStream file = new FileInputStream(fullPathName)) {
FileChannel fileChannel = file.getChannel();
long fontSize = fileChannel.size();
fontBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fontSize);
bufferForPath.put(fullPathName, fontBuffer);
} catch (IOException e) {
Log.e(TAG, "Error mapping font file " + fullPathName);
private static @Nullable ByteBuffer mmap(String fullPath) {
try (FileInputStream file = new FileInputStream(fullPath)) {
final FileChannel fileChannel = file.getChannel();
final long fontSize = fileChannel.size();
return fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fontSize);
} catch (IOException e) {
Log.e(TAG, "Error mapping font file " + fullPath);
return null;
}
}
private static @Nullable FontFamily createFontFamily(
String familyName, List<FontConfig.Font> fonts, String languageTag, int variant,
Map<String, ByteBuffer> cache, String fontDir) {
final FontFamily family = new FontFamily(languageTag, variant);
for (int i = 0; i < fonts.size(); i++) {
final FontConfig.Font font = fonts.get(i);
final String fullPath = fontDir + font.getFontName();
ByteBuffer buffer = cache.get(fullPath);
if (buffer == null) {
if (cache.containsKey(fullPath)) {
continue; // Already failed to mmap. Skip it.
}
buffer = mmap(fullPath);
cache.put(fullPath, buffer);
if (buffer == null) {
continue;
}
}
if (!fontFamily.addFontFromBuffer(fontBuffer, font.getTtcIndex(), font.getAxes(),
if (!family.addFontFromBuffer(buffer, font.getTtcIndex(), font.getAxes(),
font.getWeight(), font.isItalic() ? STYLE_ITALIC : STYLE_NORMAL)) {
Log.e(TAG, "Error creating font " + fullPathName + "#" + font.getTtcIndex());
Log.e(TAG, "Error creating font " + fullPath + "#" + font.getTtcIndex());
}
}
if (!fontFamily.freeze()) {
// Treat as system error since reaching here means that a system pre-installed font
// can't be used by our font stack.
Log.e(TAG, "Unable to load Family: " + family.getName() + ":" + family.getLanguage());
if (!family.freeze()) {
Log.e(TAG, "Unable to load Family: " + familyName + " : " + languageTag);
return null;
}
return fontFamily;
return family;
}
/*
* (non-Javadoc)
private static void pushFamilyToFallback(FontConfig.Family xmlFamily,
ArrayMap<String, ArrayList<FontFamily>> fallbackMap,
Map<String, ByteBuffer> cache,
String fontDir) {
final String languageTag = xmlFamily.getLanguage();
final int variant = xmlFamily.getVariant();
final ArrayList<FontConfig.Font> defaultFonts = new ArrayList<>();
final ArrayMap<String, ArrayList<FontConfig.Font>> specificFallbackFonts = new ArrayMap<>();
// Collect default fallback and specific fallback fonts.
for (final FontConfig.Font font : xmlFamily.getFonts()) {
final String fallbackName = font.getFallbackFor();
if (fallbackName == null) {
defaultFonts.add(font);
} else {
ArrayList<FontConfig.Font> fallback = specificFallbackFonts.get(fallbackName);
if (fallback == null) {
fallback = new ArrayList<>();
specificFallbackFonts.put(fallbackName, fallback);
}
fallback.add(font);
}
}
final FontFamily defaultFamily = defaultFonts.isEmpty() ? null : createFontFamily(
xmlFamily.getName(), defaultFonts, languageTag, variant, cache, fontDir);
// Insert family into fallback map.
for (int i = 0; i < fallbackMap.size(); i++) {
final ArrayList<FontConfig.Font> fallback =
specificFallbackFonts.get(fallbackMap.keyAt(i));
if (fallback == null) {
if (defaultFamily != null) {
fallbackMap.valueAt(i).add(defaultFamily);
}
} else {
final FontFamily family = createFontFamily(
xmlFamily.getName(), fallback, languageTag, variant, cache, fontDir);
if (family != null) {
fallbackMap.valueAt(i).add(family);
}
}
}
}
/**
* Build the system fallback from xml file.
*
* This should only be called once, from the static class initializer block.
* @param xmlPath A full path string to the fonts.xml file.
* @param fontDir A full path string to the system font directory. This must end with
* slash('/').
* @param fontMap An output system font map. Caller must pass empty map.
* @param fallbackMap An output system fallback map. Caller must pass empty map.
* @hide
*/
private static void init() {
// Load font config and initialize Minikin state
File systemFontConfigLocation = getSystemFontConfigLocation();
File configFilename = new File(systemFontConfigLocation, FONTS_CONFIG);
@VisibleForTesting
public static void buildSystemFallback(String xmlPath, String fontDir,
ArrayMap<String, Typeface> fontMap, ArrayMap<String, FontFamily[]> fallbackMap) {
try {
FileInputStream fontsIn = new FileInputStream(configFilename);
FontConfig fontConfig = FontListParser.parse(fontsIn);
final FileInputStream fontsIn = new FileInputStream(xmlPath);
final FontConfig fontConfig = FontListParser.parse(fontsIn);
Map<String, ByteBuffer> bufferForPath = new HashMap<String, ByteBuffer>();
final HashMap<String, ByteBuffer> bufferCache = new HashMap<String, ByteBuffer>();
final FontConfig.Family[] xmlFamilies = fontConfig.getFamilies();
List<FontFamily> familyList = new ArrayList<FontFamily>();
// Note that the default typeface is always present in the fallback list;
// this is an enhancement from pre-Minikin behavior.
for (int i = 0; i < fontConfig.getFamilies().length; i++) {
FontConfig.Family f = fontConfig.getFamilies()[i];
if (i == 0 || f.getName() == null) {
FontFamily family = makeFamilyFromParsed(f, bufferForPath);
if (family != null) {
familyList.add(family);
}
final ArrayMap<String, ArrayList<FontFamily>> fallbackListMap = new ArrayMap<>();
// First traverse families which have a 'name' attribute to create fallback map.
for (final FontConfig.Family xmlFamily : xmlFamilies) {
final String familyName = xmlFamily.getName();
if (familyName == null) {
continue;
}
final FontFamily family = createFontFamily(
xmlFamily.getName(), Arrays.asList(xmlFamily.getFonts()),
xmlFamily.getLanguage(), xmlFamily.getVariant(), bufferCache, fontDir);
if (family == null) {
continue;
}
final ArrayList<FontFamily> fallback = new ArrayList<>();
fallback.add(family);
fallbackListMap.put(familyName, fallback);
}
// Then, add fallback fonts to the each fallback map.
for (int i = 0; i < xmlFamilies.length; i++) {
final FontConfig.Family xmlFamily = xmlFamilies[i];
// The first family (usually the sans-serif family) is always placed immediately
// after the primary family in the fallback.
if (i == 0 || xmlFamily.getName() == null) {
pushFamilyToFallback(xmlFamily, fallbackListMap, bufferCache, fontDir);
}
}
sFallbackFonts = familyList.toArray(new FontFamily[familyList.size()]);
setDefault(Typeface.createFromFamilies(sFallbackFonts));
Map<String, Typeface> systemFonts = new HashMap<String, Typeface>();
for (int i = 0; i < fontConfig.getFamilies().length; i++) {
Typeface typeface;
FontConfig.Family f = fontConfig.getFamilies()[i];
if (f.getName() != null) {
if (i == 0) {
// The first entry is the default typeface; no sense in
// duplicating the corresponding FontFamily.
typeface = sDefaultTypeface;
} else {
FontFamily fontFamily = makeFamilyFromParsed(f, bufferForPath);
if (fontFamily == null) {
continue;
}
FontFamily[] families = { fontFamily };
typeface = Typeface.createFromFamiliesWithDefault(families,
RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
}
systemFonts.put(f.getName(), typeface);
// Build the font map and fallback map.
for (int i = 0; i < fallbackListMap.size(); i++) {
final String fallbackName = fallbackListMap.keyAt(i);
final List<FontFamily> familyList = fallbackListMap.valueAt(i);
final FontFamily[] families = familyList.toArray(new FontFamily[familyList.size()]);
fallbackMap.put(fallbackName, families);
final long[] ptrArray = new long[families.length];
for (int j = 0; j < families.length; j++) {
ptrArray[j] = families[j].mNativePtr;
}
fontMap.put(fallbackName, new Typeface(nativeCreateFromArray(
ptrArray, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)));
}
for (FontConfig.Alias alias : fontConfig.getAliases()) {
Typeface base = systemFonts.get(alias.getToName());
// Insert alias to font maps.
for (final FontConfig.Alias alias : fontConfig.getAliases()) {
Typeface base = fontMap.get(alias.getToName());
Typeface newFace = base;
int weight = alias.getWeight();
if (weight != 400) {
newFace = new Typeface(nativeCreateWeightAlias(base.native_instance, weight));
}
systemFonts.put(alias.getName(), newFace);
fontMap.put(alias.getName(), newFace);
}
sSystemFontMap = systemFonts;
} catch (RuntimeException e) {
Log.w(TAG, "Didn't create default family (most likely, non-Minikin build)", e);
// TODO: normal in non-Minikin case, remove or make error when Minikin-only
} catch (FileNotFoundException e) {
Log.e(TAG, "Error opening " + configFilename, e);
Log.e(TAG, "Error opening " + xmlPath, e);
} catch (IOException e) {
Log.e(TAG, "Error reading " + configFilename, e);
Log.e(TAG, "Error reading " + xmlPath, e);
} catch (XmlPullParserException e) {
Log.e(TAG, "XML parse exception for " + configFilename, e);
Log.e(TAG, "XML parse exception for " + xmlPath, e);
}
}
static {
init();
final ArrayMap<String, Typeface> systemFontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> systemFallbackMap = new ArrayMap<>();
buildSystemFallback("/system/etc/fonts.xml", "/system/fonts/", systemFontMap,
systemFallbackMap);
sSystemFontMap = Collections.unmodifiableMap(systemFontMap);
sSystemFallbackMap = Collections.unmodifiableMap(systemFallbackMap);
setDefault(sSystemFontMap.get(DEFAULT_FAMILY));
// Set up defaults and typefaces exposed in public API
DEFAULT = create((String) null, 0);
DEFAULT_BOLD = create((String) null, Typeface.BOLD);
@@ -1008,10 +1090,6 @@ public class Typeface {
}
private static File getSystemFontConfigLocation() {
return new File("/system/etc/");
}
@Override
protected void finalize() throws Throwable {
try {

View File

@@ -68,7 +68,7 @@ static minikin::FontStyle computeRelativeStyle(int baseWeight, SkTypeface::Style
Typeface* gDefaultTypeface = NULL;
Typeface* Typeface::resolveDefault(Typeface* src) {
LOG_ALWAYS_FATAL_IF(gDefaultTypeface == nullptr);
LOG_ALWAYS_FATAL_IF(src == nullptr && gDefaultTypeface == nullptr);
return src == nullptr ? gDefaultTypeface : src;
}