| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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 org.apache.fontbox.ttf; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.logging.log4j.Logger; |
| import org.apache.logging.log4j.LogManager; |
| /** |
| * A "cmap" subtable. |
| * |
| * @author Ben Litchfield |
| */ |
| public class CmapSubtable implements CmapLookup |
| { |
| private static final Logger LOG = LogManager.getLogger(CmapSubtable.class); |
| |
| private static final long LEAD_OFFSET = 0xD800l - (0x10000 >> 10); |
| private static final long SURROGATE_OFFSET = 0x10000l - (0xD800 << 10) - 0xDC00; |
| |
| private int platformId; |
| private int platformEncodingId; |
| private long subTableOffset; |
| private int[] glyphIdToCharacterCode; |
| private final Map<Integer, List<Integer>> glyphIdToCharacterCodeMultiple = new HashMap<>(); |
| private Map<Integer, Integer> characterCodeToGlyphId = Collections.emptyMap(); |
| |
| /** |
| * This will read the required data from the stream. |
| * |
| * @param data The stream to read the data from. |
| * @throws IOException If there is an error reading the data. |
| */ |
| void initData(TTFDataStream data) throws IOException |
| { |
| platformId = data.readUnsignedShort(); |
| platformEncodingId = data.readUnsignedShort(); |
| subTableOffset = data.readUnsignedInt(); |
| } |
| |
| /** |
| * This will read the required data from the stream. |
| * |
| * @param cmap the CMAP this encoding belongs to. |
| * @param numGlyphs number of glyphs. |
| * @param data The stream to read the data from. |
| * @throws IOException If there is an error reading the data. |
| */ |
| void initSubtable(CmapTable cmap, int numGlyphs, TTFDataStream data) throws IOException |
| { |
| data.seek(cmap.getOffset() + subTableOffset); |
| int subtableFormat = data.readUnsignedShort(); |
| long length; |
| long version; |
| if (subtableFormat < 8) |
| { |
| length = data.readUnsignedShort(); |
| version = data.readUnsignedShort(); |
| } |
| else |
| { |
| // read an other UnsignedShort to read a Fixed32 |
| data.readUnsignedShort(); |
| length = data.readUnsignedInt(); |
| version = data.readUnsignedInt(); |
| } |
| |
| switch (subtableFormat) |
| { |
| case 0: |
| processSubtype0(data); |
| break; |
| case 2: |
| processSubtype2(data, numGlyphs); |
| break; |
| case 4: |
| processSubtype4(data, numGlyphs); |
| break; |
| case 6: |
| processSubtype6(data, numGlyphs); |
| break; |
| case 8: |
| processSubtype8(data, numGlyphs); |
| break; |
| case 10: |
| processSubtype10(data, numGlyphs); |
| break; |
| case 12: |
| processSubtype12(data, numGlyphs); |
| break; |
| case 13: |
| processSubtype13(data, numGlyphs); |
| break; |
| case 14: |
| processSubtype14(data, numGlyphs); |
| break; |
| default: |
| throw new IOException("Unknown cmap format:" + subtableFormat); |
| } |
| } |
| |
| /** |
| * Reads a format 8 subtable. |
| * |
| * @param data the data stream of the to be parsed ttf font |
| * @param numGlyphs number of glyphs to be read |
| * @throws IOException If there is an error parsing the true type font. |
| */ |
| void processSubtype8(TTFDataStream data, int numGlyphs) throws IOException |
| { |
| // --- is32 is a 65536 BITS array ( = 8192 BYTES) |
| int[] is32 = data.readUnsignedByteArray(8192); |
| long nbGroups = data.readUnsignedInt(); |
| |
| // --- nbGroups shouldn't be greater than 65536 |
| if (nbGroups > 65536) |
| { |
| throw new IOException("CMap ( Subtype8 ) is invalid"); |
| } |
| |
| glyphIdToCharacterCode = newGlyphIdToCharacterCode(numGlyphs); |
| characterCodeToGlyphId = new HashMap<>(numGlyphs); |
| if (numGlyphs == 0) |
| { |
| LOG.warn("subtable has no glyphs"); |
| return; |
| } |
| // -- Read all sub header |
| for (long i = 0; i < nbGroups; ++i) |
| { |
| long firstCode = data.readUnsignedInt(); |
| long endCode = data.readUnsignedInt(); |
| long startGlyph = data.readUnsignedInt(); |
| |
| // -- process simple validation |
| if (firstCode > endCode || 0 > firstCode) |
| { |
| throw new IOException("Range invalid"); |
| } |
| |
| for (long j = firstCode; j <= endCode; ++j) |
| { |
| // -- Convert the Character code in decimal |
| if (j > Integer.MAX_VALUE) |
| { |
| throw new IOException("[Sub Format 8] Invalid character code " + j); |
| } |
| if ((int) j / 8 >= is32.length) |
| { |
| throw new IOException("[Sub Format 8] Invalid character code " + j); |
| } |
| |
| int currentCharCode; |
| if ((is32[(int) j / 8] & (1 << ((int) j % 8))) == 0) |
| { |
| currentCharCode = (int) j; |
| } |
| else |
| { |
| // the character code uses a 32bits format |
| // convert it in decimal : see http://www.unicode.org/faq//utf_bom.html#utf16-4 |
| long lead = LEAD_OFFSET + (j >> 10); |
| long trail = 0xDC00 + (j & 0x3FF); |
| |
| long codepoint = (lead << 10) + trail + SURROGATE_OFFSET; |
| if (codepoint > Integer.MAX_VALUE) |
| { |
| throw new IOException("[Sub Format 8] Invalid character code " + codepoint); |
| } |
| currentCharCode = (int) codepoint; |
| } |
| |
| long glyphIndex = startGlyph + (j - firstCode); |
| if (glyphIndex > numGlyphs || glyphIndex > Integer.MAX_VALUE) |
| { |
| throw new IOException("CMap contains an invalid glyph index"); |
| } |
| |
| glyphIdToCharacterCode[(int) glyphIndex] = currentCharCode; |
| characterCodeToGlyphId.put(currentCharCode, (int) glyphIndex); |
| } |
| } |
| } |
| |
| /** |
| * Reads a format 10 subtable. |
| * |
| * @param data the data stream of the to be parsed ttf font |
| * @param numGlyphs number of glyphs to be read |
| * @throws IOException If there is an error parsing the true type font. |
| */ |
| void processSubtype10(TTFDataStream data, int numGlyphs) throws IOException |
| { |
| long startCode = data.readUnsignedInt(); |
| long numChars = data.readUnsignedInt(); |
| if (numChars > Integer.MAX_VALUE) |
| { |
| throw new IOException("Invalid number of Characters"); |
| } |
| |
| if (startCode < 0 || startCode > 0x0010FFFF || (startCode + numChars) > 0x0010FFFF |
| || ((startCode + numChars) >= 0x0000D800 && (startCode + numChars) <= 0x0000DFFF)) |
| { |
| throw new IOException("Invalid character codes, " + |
| String.format("startCode: 0x%X, numChars: %d", startCode, numChars)); |
| |
| } |
| } |
| |
| /** |
| * Reads a format 12 subtable. |
| * |
| * @param data the data stream of the to be parsed ttf font |
| * @param numGlyphs number of glyphs to be read |
| * @throws IOException If there is an error parsing the true type font. |
| */ |
| void processSubtype12(TTFDataStream data, int numGlyphs) throws IOException |
| { |
| int maxGlyphId = 0; |
| long nbGroups = data.readUnsignedInt(); |
| glyphIdToCharacterCode = newGlyphIdToCharacterCode(numGlyphs); |
| characterCodeToGlyphId = new HashMap<>(numGlyphs); |
| if (numGlyphs == 0) |
| { |
| LOG.warn("subtable has no glyphs"); |
| return; |
| } |
| for (long i = 0; i < nbGroups; ++i) |
| { |
| long firstCode = data.readUnsignedInt(); |
| long endCode = data.readUnsignedInt(); |
| long startGlyph = data.readUnsignedInt(); |
| |
| if (firstCode < 0 || firstCode > 0x0010FFFF || |
| firstCode >= 0x0000D800 && firstCode <= 0x0000DFFF) |
| { |
| throw new IOException("Invalid character code " + String.format("0x%X", firstCode)); |
| } |
| |
| if (endCode > 0 && endCode < firstCode || |
| endCode > 0x0010FFFF || |
| endCode >= 0x0000D800 && endCode <= 0x0000DFFF) |
| { |
| throw new IOException("Invalid character code " + String.format("0x%X", endCode)); |
| } |
| |
| for (long j = 0; j <= endCode - firstCode; ++j) |
| { |
| long glyphIndex = startGlyph + j; |
| if (glyphIndex >= numGlyphs) |
| { |
| LOG.warn("Format 12 cmap contains an invalid glyph index"); |
| break; |
| } |
| |
| if (firstCode + j > 0x10FFFF) |
| { |
| LOG.warn("Format 12 cmap contains character beyond UCS-4"); |
| } |
| |
| maxGlyphId = Math.max(maxGlyphId, (int) glyphIndex); |
| characterCodeToGlyphId.put((int) (firstCode + j), (int) glyphIndex); |
| } |
| } |
| buildGlyphIdToCharacterCodeLookup(maxGlyphId); |
| } |
| |
| /** |
| * Reads a format 13 subtable. |
| * |
| * @param data the data stream of the to be parsed ttf font |
| * @param numGlyphs number of glyphs to be read |
| * @throws IOException If there is an error parsing the true type font. |
| */ |
| void processSubtype13(TTFDataStream data, int numGlyphs) throws IOException |
| { |
| long nbGroups = data.readUnsignedInt(); |
| glyphIdToCharacterCode = newGlyphIdToCharacterCode(numGlyphs); |
| characterCodeToGlyphId = new HashMap<>(numGlyphs); |
| if (numGlyphs == 0) |
| { |
| LOG.warn("subtable has no glyphs"); |
| return; |
| } |
| for (long i = 0; i < nbGroups; ++i) |
| { |
| long firstCode = data.readUnsignedInt(); |
| long endCode = data.readUnsignedInt(); |
| long glyphId = data.readUnsignedInt(); |
| |
| if (glyphId > numGlyphs) |
| { |
| LOG.warn("Format 13 cmap contains an invalid glyph index"); |
| break; |
| } |
| |
| if (firstCode < 0 || firstCode > 0x0010FFFF || (firstCode >= 0x0000D800 && firstCode <= 0x0000DFFF)) |
| { |
| throw new IOException("Invalid character code " + String.format("0x%X", firstCode)); |
| } |
| |
| if ((endCode > 0 && endCode < firstCode) || endCode > 0x0010FFFF |
| || (endCode >= 0x0000D800 && endCode <= 0x0000DFFF)) |
| { |
| throw new IOException("Invalid character code " + String.format("0x%X", endCode)); |
| } |
| |
| for (long j = 0; j <= endCode - firstCode; ++j) |
| { |
| if (firstCode + j > Integer.MAX_VALUE) |
| { |
| throw new IOException("Character Code greater than Integer.MAX_VALUE"); |
| } |
| |
| if (firstCode + j > 0x10FFFF) |
| { |
| LOG.warn("Format 13 cmap contains character beyond UCS-4"); |
| } |
| |
| glyphIdToCharacterCode[(int) glyphId] = (int) (firstCode + j); |
| characterCodeToGlyphId.put((int) (firstCode + j), (int) glyphId); |
| } |
| } |
| } |
| |
| /** |
| * Reads a format 14 subtable. |
| * |
| * @param data the data stream of the to be parsed ttf font |
| * @param numGlyphs number of glyphs to be read |
| * @throws IOException If there is an error parsing the true type font. |
| */ |
| void processSubtype14(TTFDataStream data, int numGlyphs) throws IOException |
| { |
| // Unicode Variation Sequences (UVS) |
| // see http://blogs.adobe.com/CCJKType/2013/05/opentype-cmap-table-ramblings.html |
| LOG.warn("Format 14 cmap table is not supported and will be ignored"); |
| } |
| |
| /** |
| * Reads a format 6 subtable. |
| * |
| * @param data the data stream of the to be parsed ttf font |
| * @param numGlyphs number of glyphs to be read |
| * @throws IOException If there is an error parsing the true type font. |
| */ |
| void processSubtype6(TTFDataStream data, int numGlyphs) throws IOException |
| { |
| int firstCode = data.readUnsignedShort(); |
| int entryCount = data.readUnsignedShort(); |
| // skip empty tables |
| if (entryCount == 0) |
| { |
| return; |
| } |
| characterCodeToGlyphId = new HashMap<>(numGlyphs); |
| int[] glyphIdArray = data.readUnsignedShortArray(entryCount); |
| int maxGlyphId = 0; |
| for (int i = 0; i < entryCount; i++) |
| { |
| maxGlyphId = Math.max(maxGlyphId, glyphIdArray[i]); |
| characterCodeToGlyphId.put(firstCode + i, glyphIdArray[i]); |
| } |
| buildGlyphIdToCharacterCodeLookup(maxGlyphId); |
| } |
| |
| /** |
| * Reads a format 4 subtable. |
| * |
| * @param data the data stream of the to be parsed ttf font |
| * @param numGlyphs number of glyphs to be read |
| * @throws IOException If there is an error parsing the true type font. |
| */ |
| void processSubtype4(TTFDataStream data, int numGlyphs) throws IOException |
| { |
| int segCountX2 = data.readUnsignedShort(); |
| int segCount = segCountX2 / 2; |
| int searchRange = data.readUnsignedShort(); |
| int entrySelector = data.readUnsignedShort(); |
| int rangeShift = data.readUnsignedShort(); |
| int[] endCount = data.readUnsignedShortArray(segCount); |
| int reservedPad = data.readUnsignedShort(); |
| int[] startCount = data.readUnsignedShortArray(segCount); |
| int[] idDelta = data.readUnsignedShortArray(segCount); |
| long idRangeOffsetPosition = data.getCurrentPosition(); |
| int[] idRangeOffset = data.readUnsignedShortArray(segCount); |
| |
| characterCodeToGlyphId = new HashMap<>(numGlyphs); |
| int maxGlyphId = 0; |
| |
| for (int i = 0; i < segCount; i++) |
| { |
| int start = startCount[i]; |
| int end = endCount[i]; |
| int delta = idDelta[i]; |
| int rangeOffset = idRangeOffset[i]; |
| long segmentRangeOffset = idRangeOffsetPosition + (i * 2L) + rangeOffset; |
| if (start != 65535 && end != 65535) |
| { |
| for (int j = start; j <= end; j++) |
| { |
| if (rangeOffset == 0) |
| { |
| int glyphid = (j + delta) & 0xFFFF; |
| maxGlyphId = Math.max(glyphid, maxGlyphId); |
| characterCodeToGlyphId.put(j, glyphid); |
| } |
| else |
| { |
| long glyphOffset = segmentRangeOffset + ((j - start) * 2L); |
| data.seek(glyphOffset); |
| int glyphIndex = data.readUnsignedShort(); |
| if (glyphIndex != 0) |
| { |
| glyphIndex = (glyphIndex + delta) & 0xFFFF; |
| maxGlyphId = Math.max(glyphIndex, maxGlyphId); |
| characterCodeToGlyphId.put(j, glyphIndex); |
| } |
| } |
| } |
| } |
| } |
| |
| /* |
| * this is the final result key=glyphId, value is character codes Create an array that contains MAX(GlyphIds) |
| * element, or -1 |
| */ |
| if (characterCodeToGlyphId.isEmpty()) |
| { |
| LOG.warn("cmap format 4 subtable is empty"); |
| return; |
| } |
| buildGlyphIdToCharacterCodeLookup(maxGlyphId); |
| } |
| |
| private void buildGlyphIdToCharacterCodeLookup(int maxGlyphId) |
| { |
| glyphIdToCharacterCode = newGlyphIdToCharacterCode(maxGlyphId + 1); |
| characterCodeToGlyphId.forEach((key, value) -> |
| { |
| if (glyphIdToCharacterCode[value] == -1) |
| { |
| // add new value to the array |
| glyphIdToCharacterCode[value] = key; |
| } |
| else |
| { |
| // there is already a mapping for the given glyphId |
| List<Integer> mappedValues = glyphIdToCharacterCodeMultiple.get(value); |
| if (mappedValues == null) |
| { |
| mappedValues = new ArrayList<>(2); |
| glyphIdToCharacterCodeMultiple.put(value, mappedValues); |
| mappedValues.add(glyphIdToCharacterCode[value]); |
| // mark value as multiple mapping |
| glyphIdToCharacterCode[value] = Integer.MIN_VALUE; |
| } |
| mappedValues.add(key); |
| } |
| }); |
| } |
| |
| /** |
| * Read a format 2 subtable. |
| * |
| * @param data the data stream of the to be parsed ttf font |
| * @param numGlyphs number of glyphs to be read |
| * @throws IOException If there is an error parsing the true type font. |
| */ |
| void processSubtype2(TTFDataStream data, int numGlyphs) throws IOException |
| { |
| int[] subHeaderKeys = new int[256]; |
| // ---- keep the Max Index of the SubHeader array to know its length |
| int maxSubHeaderIndex = 0; |
| for (int i = 0; i < 256; i++) |
| { |
| subHeaderKeys[i] = data.readUnsignedShort(); |
| maxSubHeaderIndex = Math.max(maxSubHeaderIndex, subHeaderKeys[i] / 8); |
| } |
| |
| // ---- Read all SubHeaders to avoid useless seek on DataSource |
| SubHeader[] subHeaders = new SubHeader[maxSubHeaderIndex + 1]; |
| for (int i = 0; i <= maxSubHeaderIndex; ++i) |
| { |
| int firstCode = data.readUnsignedShort(); |
| int entryCount = data.readUnsignedShort(); |
| short idDelta = data.readSignedShort(); |
| int idRangeOffset = data.readUnsignedShort() - (maxSubHeaderIndex + 1 - i - 1) * 8 - 2; |
| subHeaders[i] = new SubHeader(firstCode, entryCount, idDelta, idRangeOffset); |
| } |
| long startGlyphIndexOffset = data.getCurrentPosition(); |
| glyphIdToCharacterCode = newGlyphIdToCharacterCode(numGlyphs); |
| characterCodeToGlyphId = new HashMap<>(numGlyphs); |
| if (numGlyphs == 0) |
| { |
| LOG.warn("subtable has no glyphs"); |
| return; |
| } |
| for (int i = 0; i <= maxSubHeaderIndex; ++i) |
| { |
| SubHeader sh = subHeaders[i]; |
| int firstCode = sh.getFirstCode(); |
| int idRangeOffset = sh.getIdRangeOffset(); |
| int idDelta = sh.getIdDelta(); |
| int entryCount = sh.getEntryCount(); |
| data.seek(startGlyphIndexOffset + idRangeOffset); |
| for (int j = 0; j < entryCount; ++j) |
| { |
| // ---- compute the Character Code |
| int charCode = i; |
| charCode = (charCode << 8) + (firstCode + j); |
| |
| // ---- Go to the CharacterCOde position in the Sub Array |
| // of the glyphIndexArray |
| // glyphIndexArray contains Unsigned Short so add (j * 2) bytes |
| // at the index position |
| int p = data.readUnsignedShort(); |
| // ---- compute the glyphIndex |
| if (p > 0) |
| { |
| p = (p + idDelta) % 65536; |
| if (p < 0) |
| { |
| p += 65536; |
| } |
| } |
| |
| if (p >= numGlyphs) |
| { |
| LOG.warn("glyphId {} for charcode {} ignored, numGlyphs is {}", p, charCode, |
| numGlyphs); |
| continue; |
| } |
| |
| glyphIdToCharacterCode[p] = charCode; |
| characterCodeToGlyphId.put(charCode, p); |
| } |
| } |
| } |
| |
| /** |
| * Initialize the CMapEntry when it is a subtype 0. |
| * |
| * @param data the data stream of the to be parsed ttf font |
| * @throws IOException If there is an error parsing the true type font. |
| */ |
| void processSubtype0(TTFDataStream data) throws IOException |
| { |
| byte[] glyphMapping = data.read(256); |
| glyphIdToCharacterCode = newGlyphIdToCharacterCode(256); |
| characterCodeToGlyphId = new HashMap<>(glyphMapping.length); |
| for (int i = 0; i < glyphMapping.length; i++) |
| { |
| int glyphIndex = glyphMapping[i] & 0xFF; |
| glyphIdToCharacterCode[glyphIndex] = i; |
| characterCodeToGlyphId.put(i, glyphIndex); |
| } |
| } |
| |
| /** |
| * Workaround for the fact that glyphIdToCharacterCode doesn't distinguish between |
| * missing character codes and code 0. |
| */ |
| private int[] newGlyphIdToCharacterCode(int size) |
| { |
| int[] gidToCode = new int[size]; |
| Arrays.fill(gidToCode, -1); |
| return gidToCode; |
| } |
| |
| /** |
| * @return Returns the platformEncodingId. |
| */ |
| public int getPlatformEncodingId() |
| { |
| return platformEncodingId; |
| } |
| |
| /** |
| * @param platformEncodingIdValue The platformEncodingId to set. |
| */ |
| public void setPlatformEncodingId(int platformEncodingIdValue) |
| { |
| platformEncodingId = platformEncodingIdValue; |
| } |
| |
| /** |
| * @return Returns the platformId. |
| */ |
| public int getPlatformId() |
| { |
| return platformId; |
| } |
| |
| /** |
| * @param platformIdValue The platformId to set. |
| */ |
| public void setPlatformId(int platformIdValue) |
| { |
| platformId = platformIdValue; |
| } |
| |
| /** |
| * Returns the GlyphId linked with the given character code. |
| * |
| * @param characterCode the given character code to be mapped |
| * @return glyphId the corresponding glyph id for the given character code |
| */ |
| @Override |
| public int getGlyphId(int characterCode) |
| { |
| Integer glyphId = characterCodeToGlyphId.get(characterCode); |
| return glyphId == null ? 0 : glyphId; |
| } |
| |
| private int getCharCode(int gid) |
| { |
| if (gid < 0 || glyphIdToCharacterCode == null || gid >= glyphIdToCharacterCode.length) |
| { |
| return -1; |
| } |
| return glyphIdToCharacterCode[gid]; |
| } |
| |
| /** |
| * Returns all possible character codes for the given gid, or null if there is none. |
| * |
| * @param gid glyph id |
| * @return a list with all character codes the given gid maps to |
| * |
| */ |
| @Override |
| public List<Integer> getCharCodes(int gid) |
| { |
| int code = getCharCode(gid); |
| if (code == -1) |
| { |
| return null; |
| } |
| List<Integer> codes = null; |
| if (code == Integer.MIN_VALUE) |
| { |
| List<Integer> mappedValues = glyphIdToCharacterCodeMultiple.get(gid); |
| if (mappedValues != null) |
| { |
| codes = new ArrayList<>(mappedValues); |
| // sort the list to provide a reliable order |
| Collections.sort(codes); |
| } |
| } |
| else |
| { |
| codes = Collections.singletonList(code); |
| } |
| return codes; |
| } |
| |
| @Override |
| public String toString() |
| { |
| return "{" + getPlatformId() + " " + getPlatformEncodingId() + "}"; |
| } |
| |
| /** |
| * |
| * Class used to manage CMap - Format 2. |
| * |
| */ |
| private static class SubHeader |
| { |
| private final int firstCode; |
| private final int entryCount; |
| /** |
| * used to compute the GlyphIndex : P = glyphIndexArray.SubArray[pos] GlyphIndex = P + idDelta % 65536. |
| */ |
| private final short idDelta; |
| /** |
| * Number of bytes to skip to reach the firstCode in the glyphIndexArray. |
| */ |
| private final int idRangeOffset; |
| |
| private SubHeader(int firstCodeValue, int entryCountValue, short idDeltaValue, int idRangeOffsetValue) |
| { |
| firstCode = firstCodeValue; |
| entryCount = entryCountValue; |
| idDelta = idDeltaValue; |
| idRangeOffset = idRangeOffsetValue; |
| } |
| |
| /** |
| * @return the firstCode |
| */ |
| private int getFirstCode() |
| { |
| return firstCode; |
| } |
| |
| /** |
| * @return the entryCount |
| */ |
| private int getEntryCount() |
| { |
| return entryCount; |
| } |
| |
| /** |
| * @return the idDelta |
| */ |
| private short getIdDelta() |
| { |
| return idDelta; |
| } |
| |
| /** |
| * @return the idRangeOffset |
| */ |
| private int getIdRangeOffset() |
| { |
| return idRangeOffset; |
| } |
| } |
| } |