/* * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions.
*/
/* * A tt font has a CMAP table which is in turn made up of sub-tables which * describe the char to glyph mapping in (possibly) multiple ways. * CMAP subtables are described by 3 values. * 1. Platform ID (eg 3=Microsoft, which is the id we look for in JDK) * 2. Encoding (eg 0=symbol, 1=unicode) * 3. TrueType subtable format (how the char->glyph mapping for the encoding * is stored in the subtable). See the TrueType spec. Format 4 is required * by MS in fonts for windows. Its uses segmented mapping to delta values. * Most typically we see are (3,1,4) : * CMAP Platform ID=3 is what we use. * Encodings that are used in practice by JDK on Solaris are * symbol (3,0) * unicode (3,1) * GBK (3,5) (note that solaris zh fonts report 3,4 but are really 3,5) * The format for almost all subtables is 4. However the solaris (3,5) * encodings are typically in format 2.
*/ abstractclass CMap {
/* * Unicode->other encoding translation array. A pre-computed look up * which can be shared across all fonts using that encoding. * Using this saves running character coverters repeatedly.
*/ char[] xlat;
UVS uvs = null;
static CMap initialize(TrueTypeFont font) {
CMap cmap = null;
int offset, platformID, encodingID=-1;
int three0=0, three1=0, three2=0, three3=0, three4=0, three5=0,
three6=0, three10=0; int zero5 = 0; // for Unicode Variation Sequences boolean threeStar = false;
ByteBuffer cmapBuffer = font.getTableBuffer(TrueTypeFont.cmapTag); int cmapTableOffset = font.getTableSize(TrueTypeFont.cmapTag); short numberSubTables = cmapBuffer.getShort(2);
/* locate the offsets of all 3,* (ie Microsoft platform) encodings */ for (int i=0; i<numberSubTables; i++) {
cmapBuffer.position(i * 8 + 4);
platformID = cmapBuffer.getShort(); if (platformID == 3) {
threeStar = true;
encodingID = cmapBuffer.getShort();
offset = cmapBuffer.getInt(); switch (encodingID) { case 0: three0 = offset; break; // MS Symbol encoding case 1: three1 = offset; break; // MS Unicode cmap case 2: three2 = offset; break; // ShiftJIS cmap. case 3: three3 = offset; break; // GBK cmap case 4: three4 = offset; break; // Big 5 cmap case 5: three5 = offset; break; // Wansung case 6: three6 = offset; break; // Johab case 10: three10 = offset; break; // MS Unicode surrogates
}
} elseif (platformID == 0) {
encodingID = cmapBuffer.getShort();
offset = cmapBuffer.getInt(); if (encodingID == 5) {
zero5 = offset;
}
}
}
/* This defines the preference order for cmap subtables */ if (threeStar) { if (three10 != 0) {
cmap = createCMap(cmapBuffer, three10, null);
} elseif (three0 != 0) { /* The special case treatment of these fonts leads to * anomalies where a user can view "wingdings" and "wingdings2" * and the latter shows all its code points in the unicode * private use area at 0xF000->0XF0FF and the former shows * a scattered subset of its glyphs that are known mappings to * unicode code points. * The primary purpose of these mappings was to facilitate * display of symbol chars etc in composite fonts, however * this is not needed as all these code points are covered * by some other platform symbol font. * Commenting this out reduces the role of these two files * (assuming that they continue to be used in font.properties) * to just one of contributing to the overall composite * font metrics, and also AWT can still access the fonts. * Clients which explicitly accessed these fonts as names * "Symbol" and "Wingdings" (ie as physical fonts) and * expected to see a scattering of these characters will * see them now as missing. How much of a problem is this? * Perhaps we could still support this mapping just for * "Symbol.ttf" but I suspect some users would prefer it * to be mapped in to the Latin range as that is how * the "symbol" font is used in native apps.
*/ // String name = font.platName.toLowerCase(Locale.ENGLISH); // if (name.endsWith("symbol.ttf")) { // cmap = createSymbolCMap(cmapBuffer, three0, Symbols_b2c); // } else if (name.endsWith("wingding.ttf")) { // cmap = createSymbolCMap(cmapBuffer, three0, WingDings_b2c); // } else {
cmap = createCMap(cmapBuffer, three0, null); // }
} elseif (three1 != 0) {
cmap = createCMap(cmapBuffer, three1, null);
} elseif (three2 != 0) {
cmap = createCMap(cmapBuffer, three2,
getConverterMap(ShiftJISEncoding));
} elseif (three3 != 0) {
cmap = createCMap(cmapBuffer, three3,
getConverterMap(GBKEncoding));
} elseif (three4 != 0) {
cmap = createCMap(cmapBuffer, three4,
getConverterMap(Big5Encoding));
} elseif (three5 != 0) {
cmap = createCMap(cmapBuffer, three5,
getConverterMap(WansungEncoding));
} elseif (three6 != 0) {
cmap = createCMap(cmapBuffer, three6,
getConverterMap(JohabEncoding));
}
} else { /* No 3,* subtable was found. Just use whatever is the first * table listed. Not very useful but maybe better than * rejecting the font entirely?
*/
cmap = createCMap(cmapBuffer, cmapBuffer.getInt(8), null);
} // For Unicode Variation Sequences if (cmap != null && zero5 != 0) {
cmap.createUVS(cmapBuffer, zero5);
} return cmap;
}
/* speed up the converting by setting the range for double * byte characters;
*/ staticchar[] getConverter(short encodingID) { int dBegin = 0x8000; int dEnd = 0xffff;
String encoding;
// ensure single byte ascii for (int i = 0x20; i <= 0x7e; i++) {
convertedChars[i] = (char)i;
}
//sjis halfwidth katakana if (encodingID == ShiftJISEncoding) { for (int i = 0xa1; i <= 0xdf; i++) {
convertedChars[i] = (char)(i - 0xa1 + 0xff61);
}
}
/* It would save heap space (approx 60Kbytes for each of these * converters) if stored only valid ranges (ie returned * outputChars directly. But this is tricky since want to * include the ASCII range too.
*/ // System.err.println("oc.len="+outputChars.length); // System.err.println("cc.len="+convertedChars.length); // System.err.println("dbegin="+dBegin);
System.arraycopy(outputChars, 0, convertedChars, dBegin,
outputChars.length);
//return convertedChars; /* invert this map as now want it to map from Unicode * to other encoding.
*/ char [] invertedChars = newchar[65536]; for (int i=0;i<65536;i++) { if (convertedChars[i] != noSuchChar) {
invertedChars[convertedChars[i]] = (char)i;
}
} return invertedChars;
/* * The returned array maps to unicode from some other 2 byte encoding * eg for a 2byte index which represents a SJIS char, the indexed * value is the corresponding unicode char.
*/ staticchar[] getConverterMap(short encodingID) { if (converterMaps[encodingID] == null) {
converterMaps[encodingID] = getConverter(encodingID);
} return converterMaps[encodingID];
}
static CMap createCMap(ByteBuffer buffer, int offset, char[] xlat) { /* First do a sanity check that this cmap subtable is contained * within the cmap table.
*/ int subtableFormat = buffer.getChar(offset); long subtableLength; if (subtableFormat < 8) {
subtableLength = buffer.getChar(offset+2);
} else {
subtableLength = buffer.getInt(offset+4) & INTMASK;
} if (FontUtilities.isLogging() && offset + subtableLength > buffer.capacity()) {
FontUtilities.logWarning("Cmap subtable overflows buffer.");
} switch (subtableFormat) { case 0: returnnew CMapFormat0(buffer, offset); case 2: returnnew CMapFormat2(buffer, offset, xlat); case 4: returnnew CMapFormat4(buffer, offset, xlat); case 6: returnnew CMapFormat6(buffer, offset, xlat); case 8: returnnew CMapFormat8(buffer, offset, xlat); case 10: returnnew CMapFormat10(buffer, offset, xlat); case 12: returnnew CMapFormat12(buffer, offset, xlat); default: thrownew RuntimeException("Cmap format unimplemented: " +
(int)buffer.getChar(offset));
}
}
privatevoid createUVS(ByteBuffer buffer, int offset) { int subtableFormat = buffer.getChar(offset); if (subtableFormat == 14) { long subtableLength = buffer.getInt(offset + 2) & INTMASK; if (FontUtilities.isLogging() && offset + subtableLength > buffer.capacity()) {
FontUtilities.logWarning("Cmap UVS subtable overflows buffer.");
} try { this.uvs = new UVS(buffer, offset);
} catch (Throwable t) {
}
} return;
}
/* final char charVal(byte[] cmap, int index) { return (char)(((0xff & cmap[index]) << 8)+(0xff & cmap[index+1])); }
final short shortVal(byte[] cmap, int index) { return (short)(((0xff & cmap[index]) << 8)+(0xff & cmap[index+1])); }
*/ abstractchar getGlyph(int charCode);
/* Format 4 Header is * ushort format (off=0) * ushort length (off=2) * ushort language (off=4) * ushort segCountX2 (off=6) * ushort searchRange (off=8) * ushort entrySelector (off=10) * ushort rangeShift (off=12) * ushort endCount[segCount] (off=14) * ushort reservedPad * ushort startCount[segCount] * short idDelta[segCount] * idRangeOFfset[segCount] * ushort glyphIdArray[]
*/ staticclass CMapFormat4 extends CMap { int segCount; int entrySelector; int rangeShift; char[] endCount; char[] startCount; short[] idDelta; char[] idRangeOffset; char[] glyphIds;
CMapFormat4(ByteBuffer bbuffer, int offset, char[] xlat) {
this.xlat = xlat;
bbuffer.position(offset);
CharBuffer buffer = bbuffer.asCharBuffer();
buffer.get(); // skip, we already know format=4 int subtableLength = buffer.get(); /* Try to recover from some bad fonts which specify a subtable * length that would overflow the byte buffer holding the whole * cmap table. If this isn't a recoverable situation an exception * may be thrown which is caught higher up the call stack. * Whilst this may seem lenient, in practice, unless the "bad" * subtable we are using is the last one in the cmap table we * would have no way of knowing about this problem anyway.
*/ if (offset+subtableLength > bbuffer.capacity()) {
subtableLength = bbuffer.capacity() - offset;
}
buffer.get(); // skip language
segCount = buffer.get()/2; int searchRange = buffer.get();
entrySelector = buffer.get();
rangeShift = buffer.get()/2;
startCount = newchar[segCount];
endCount = newchar[segCount];
idDelta = newshort[segCount];
idRangeOffset = newchar[segCount];
for (int i=0; i<segCount; i++) {
endCount[i] = buffer.get();
}
buffer.get(); // 2 bytes for reserved pad for (int i=0; i<segCount; i++) {
startCount[i] = buffer.get();
}
for (int i=0; i<segCount; i++) {
idDelta[i] = (short)buffer.get();
}
for (int i=0; i<segCount; i++) { char ctmp = buffer.get();
idRangeOffset[i] = (char)((ctmp>>1)&0xffff);
} /* Can calculate the number of glyph IDs by subtracting * "pos" from the length of the cmap
*/ int pos = (segCount*8+16)/2;
buffer.position(pos); int numGlyphIds = (subtableLength/2 - pos);
glyphIds = newchar[numGlyphIds]; for (int i=0;i<numGlyphIds;i++) {
glyphIds[i] = buffer.get();
} /* System.err.println("segcount="+segCount); System.err.println("entrySelector="+entrySelector); System.err.println("rangeShift="+rangeShift); for (int j=0;j<segCount;j++) { System.err.println("j="+j+ " sc="+(int)(startCount[j]&0xffff)+ " ec="+(int)(endCount[j]&0xffff)+ " delta="+idDelta[j] + " ro="+(int)idRangeOffset[j]); }
//System.err.println("numglyphs="+glyphIds.length); for (int i=0;i<numGlyphIds;i++) { System.err.println("gid["+i+"]="+(int)glyphIds[i]); }
*/
}
char getGlyph(int charCode) {
finalint origCharCode = charCode; int index = 0; char glyphCode = 0;
int controlGlyph = getControlCodeGlyph(charCode, true); if (controlGlyph >= 0) { return (char)controlGlyph;
}
/* presence of translation array indicates that this * cmap is in some other (non-unicode encoding). * In order to look-up a char->glyph mapping we need to * translate the unicode code point to the encoding of * the cmap. * REMIND: VALID CHARCODES??
*/ if (xlat != null) {
charCode = xlat[charCode];
}
/* * Citation from the TrueType (and OpenType) spec: * The segments are sorted in order of increasing endCode * values, and the segment values are specified in four parallel * arrays. You search for the first endCode that is greater than * or equal to the character code you want to map. If the * corresponding startCode is less than or equal to the * character code, then you use the corresponding idDelta and * idRangeOffset to map the character code to a glyph index * (otherwise, the missingGlyph is returned).
*/
/* * CMAP format4 defines several fields for optimized search of * the segment list (entrySelector, searchRange, rangeShift). * However, benefits are negligible and some fonts have incorrect * data - so we use straightforward binary search (see bug 6247425)
*/ int left = 0, right = startCount.length;
index = startCount.length >> 1; while (left < right) { if (endCount[index] < charCode) {
left = index + 1;
} else {
right = index;
}
index = (left + right) >> 1;
}
if (charCode >= startCount[index] && charCode <= endCount[index]) { int rangeOffset = idRangeOffset[index];
if (rangeOffset == 0) {
glyphCode = (char)(charCode + idDelta[index]);
} else { /* Calculate an index into the glyphIds array */
/* skip 6 bytes of format, length, and version */ int len = buffer.getChar(offset+2);
cmap = newbyte[len-6];
buffer.position(offset+6);
buffer.get(cmap);
}
char getGlyph(int charCode) { if (charCode < 256) { if (charCode < 0x0010) { switch (charCode) { case 0x0009: case 0x000a: case 0x000d: return CharToGlyphMapper.INVISIBLE_GLYPH_ID;
}
} return (char)(0xff & cmap[charCode]);
} else { return 0;
}
}
}
// static CMap createSymbolCMap(ByteBuffer buffer, int offset, char[] syms) {
if (mapMe < entryCountArray[key]) { /* "address" arithmetic is needed to calculate the offset * into glyphIndexArray. "idRangeOffSetArray[key]" specifies * the number of bytes from that location in the table where * the subarray of glyphIndexes starting at "firstCode" begins. * Each entry in the subHeader table is 8 bytes, and the * idRangeOffSetArray field is at offset 6 in the entry. * The glyphIndexArray immediately follows the subHeaders. * So if there are "N" entries then the number of bytes to the * start of glyphIndexArray is (N-key)*8-6. * Subtract this from the idRangeOffSetArray value to get * the number of bytes into glyphIndexArray and divide by 2 to * get the (char) array index.
*/ int glyphArrayOffset = ((idRangeOffSetArray.length-key)*8)-6; int glyphSubArrayStart =
(idRangeOffSetArray[key] - glyphArrayOffset)/2; char glyphCode = glyphIndexArray[glyphSubArrayStart+mapMe]; if (glyphCode != 0) {
glyphCode += idDeltaArray[key]; //idDelta return glyphCode;
}
} return getFormatCharGlyph(origCharCode);
}
}
// Format 8: mixed 16-bit and 32-bit coverage // Seems unlikely this code will ever get tested as we look for // MS platform Cmaps and MS states (in the Opentype spec on their website) // that MS doesn't support this format staticclass CMapFormat8 extends CMap { byte[] is32 = newbyte[8192]; int nGroups; int[] startCharCode; int[] endCharCode; int[] startGlyphID;
CMapFormat8(ByteBuffer bbuffer, int offset, char[] xlat) {
bbuffer.position(12);
bbuffer.get(is32);
nGroups = bbuffer.getInt() & INTMASK; // A map group record is three uint32's making for 12 bytes total if (bbuffer.remaining() < (12 * (long)nGroups)) { thrownew RuntimeException("Format 8 table exceeded");
}
startCharCode = newint[nGroups];
endCharCode = newint[nGroups];
startGlyphID = newint[nGroups];
}
char getGlyph(int charCode) { if (xlat != null) { thrownew RuntimeException("xlat array for cmap fmt=8");
} return 0;
}
}
// Format 4-byte 10: Trimmed table mapping // Seems unlikely this code will ever get tested as we look for // MS platform Cmaps and MS states (in the Opentype spec on their website) // that MS doesn't support this format staticclass CMapFormat10 extends CMap {
long firstCode; int entryCount; char[] glyphIdArray;
CMapFormat10(ByteBuffer bbuffer, int offset, char[] xlat) {
bbuffer.position(offset+12);
firstCode = bbuffer.getInt() & INTMASK;
entryCount = bbuffer.getInt() & INTMASK; // each glyph is a uint16, so 2 bytes per value. if (bbuffer.remaining() < (2 * (long)entryCount)) { thrownew RuntimeException("Format 10 table exceeded");
}
CharBuffer buffer = bbuffer.asCharBuffer();
glyphIdArray = newchar[entryCount]; for (int i=0; i< entryCount; i++) {
glyphIdArray[i] = buffer.get();
}
}
char getGlyph(int charCode) {
if (xlat != null) { thrownew RuntimeException("xlat array for cmap fmt=10");
}
UVS(ByteBuffer buffer, int offset) {
buffer.position(offset+6);
numSelectors = buffer.getInt() & INTMASK; // A variation selector record is one 3 byte int + two int32's // making for 11 bytes per record. if (buffer.remaining() < (11 * (long)numSelectors)) { thrownew RuntimeException("Variations exceed buffer");
}
selector = newint[numSelectors];
numUVSMapping = newint[numSelectors];
unicodeValue = newint[numSelectors][];
glyphID = newchar[numSelectors][];
for (int i = 0; i < numSelectors; i++) {
buffer.position(offset + 10 + i * 11);
selector[i] = (buffer.get() & 0xff) << 16; //UINT24
selector[i] += (buffer.get() & 0xff) << 8;
selector[i] += buffer.get() & 0xff;
//skip Default UVS Table
//for Non-Default UVS Table int tableOffset = buffer.getInt(offset + 10 + i * 11 + 7); if (tableOffset == 0) {
numUVSMapping[i] = 0;
} elseif (tableOffset > 0) {
buffer.position(offset+tableOffset);
numUVSMapping[i] = buffer.getInt() & INTMASK; // a UVS mapping record is one 3 byte int + uint16 // making for 5 bytes per record. if (buffer.remaining() < (5 * (long)numUVSMapping[i])) { thrownew RuntimeException("Variations exceed buffer");
}
unicodeValue[i] = newint[numUVSMapping[i]];
glyphID[i] = newchar[numUVSMapping[i]];
staticfinalint VS_NOGLYPH = 0; privateint getGlyph(int charCode, int variationSelector) { int targetSelector = -1; for (int i = 0; i < numSelectors; i++) { if (selector[i] == variationSelector) {
targetSelector = i; break;
}
} if (targetSelector == -1) { return VS_NOGLYPH;
} if (numUVSMapping[targetSelector] > 0) { int index = java.util.Arrays.binarySearch(
unicodeValue[targetSelector], charCode); if (index >= 0) { return glyphID[targetSelector][index];
}
} return VS_NOGLYPH;
}
}
char getVariationGlyph(int charCode, int variationSelector) { char glyph = 0; if (uvs == null) {
glyph = getGlyph(charCode);
} else { int result = uvs.getGlyph(charCode, variationSelector); if (result > 0) {
glyph = (char)(result & 0xFFFF);
} else {
glyph = getGlyph(charCode);
}
} return glyph;
}
}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.31Bemerkung:
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.