protected List<TrueTypeGlyph> getCharactersToOutput(int glyph) throws IOException { ArrayList<TrueTypeGlyph> characters = new ArrayList<TrueTypeGlyph>(); int[] glyphDataPosition = getGlyphDataPosition(glyph); int glyphDataOffset = glyphDataPosition[0]; int glyphDataLength = glyphDataPosition[1]; if (glyphDataLength == 0) // no contour return characters; int tableGlyphOffset = ((int[]) positionTables.get("glyf"))[0]; rf.seek(tableGlyphOffset + glyphDataOffset); int numContours = rf.readShort(); if (numContours >= 0) return characters; rf.skipBytes(8); for (; ; ) { int flags = rf.readUnsignedShort(); Integer cGlyph = rf.readUnsignedShort(); TrueTypeGlyph trueTypeGlyph = new TrueTypeGlyph(cGlyph.intValue()); if (!glyphDefined.contains(trueTypeGlyph)) { characters.add(trueTypeGlyph); } if ((flags & MORE_COMPONENTS) == 0) return characters; int skip; if ((flags & ARG_1_AND_2_ARE_WORDS) != 0) skip = 4; else skip = 2; if ((flags & WE_HAVE_A_SCALE) != 0) skip += 2; else if ((flags & WE_HAVE_AN_X_AND_Y_SCALE) != 0) skip += 4; if ((flags & WE_HAVE_A_TWO_BY_TWO) != 0) skip += 8; rf.skipBytes(skip); } }
/** * Reads the kerning information from the 'kern' table. * * @throws IOException the font file could not be read */ void readKerning() throws IOException { int table_location[]; table_location = (int[]) positionTables.get("kern"); if (table_location == null) return; rf.seek(table_location[0] + 2); int nTables = rf.readUnsignedShort(); int checkpoint = table_location[0] + 4; int length = 0; for (int k = 0; k < nTables; ++k) { checkpoint += length; rf.seek(checkpoint); rf.skipBytes(2); length = rf.readUnsignedShort(); int coverage = rf.readUnsignedShort(); if ((coverage & 0xfff7) == 0x0001) { int nPairs = rf.readUnsignedShort(); rf.skipBytes(6); for (int j = 0; j < nPairs; ++j) { int pair = rf.readInt(); int value = ((int) rf.readShort() * 1000) / head.unitsPerEm; kerning.put(pair, value); } } } }
/** * Gets the Postscript font name. * * @throws DocumentException the font is invalid * @throws IOException the font file could not be read * @return the Postscript font name */ String getBaseFont() throws DocumentException, IOException { int table_location[]; table_location = (int[]) positionTables.get("name"); if (table_location == null) throw new DocumentException("Table 'name' does not exist in " + fileName + style); rf.seek(table_location[0] + 2); int numRecords = rf.readUnsignedShort(); int startOfStorage = rf.readUnsignedShort(); for (int k = 0; k < numRecords; ++k) { int platformID = rf.readUnsignedShort(); int nameID = rf.readUnsignedShort(); int length = rf.readUnsignedShort(); int offset = rf.readUnsignedShort(); if (nameID == 6) { rf.seek(table_location[0] + startOfStorage + offset); if (platformID != 0 && platformID != 3) { String name = readStandardString(length); name = name.replace(' ', '_'); return name.replace((char) 0, '_'); } } } File file = new File(fileName); return file.getName().replace(' ', '_'); }
/** * Reads the glyphs widths. The widths are extracted from the table 'hmtx'. The glyphs are * normalized to 1000 units. * * @throws DocumentException the font is invalid * @throws IOException the font file could not be read */ protected void readGlyphWidths() throws DocumentException, IOException { int table_location[]; table_location = (int[]) positionTables.get("hmtx"); if (table_location == null) throw new DocumentException("Table 'hmtx' does not exist in " + fileName + style); rf.seek(table_location[0]); GlyphWidths = new int[hhea.numberOfHMetrics]; for (int k = 0; k < hhea.numberOfHMetrics; ++k) { GlyphWidths[k] = (rf.readUnsignedShort() * 1000) / head.unitsPerEm; rf.readUnsignedShort(); } }
/** * The information in the maps of the table 'cmap' is coded in several formats. Format 6 is a * trimmed table mapping. It is similar to format 0 but can have less than 256 entries. * * @return a <CODE>HashMap</CODE> representing this map * @throws IOException the font file could not be read */ HashMap<Integer, int[]> readFormat6() throws IOException { HashMap<Integer, int[]> h = new HashMap<Integer, int[]>(); rf.skipBytes(4); int start_code = rf.readUnsignedShort(); int code_count = rf.readUnsignedShort(); for (int k = 0; k < code_count; ++k) { int r[] = new int[2]; r[0] = rf.readUnsignedShort(); r[1] = getGlyphWidth(r[0]); h.put(k + start_code, r); } return h; }
private void fillHead() throws DocumentException, IOException { int tableLocation[] = getTableLocation("head"); rf.seek(tableLocation[0] + 16); head.flags = rf.readUnsignedShort(); head.unitsPerEm = rf.readUnsignedShort(); rf.skipBytes(16); head.xMin = rf.readShort(); head.yMin = rf.readShort(); head.xMax = rf.readShort(); head.yMax = rf.readShort(); head.macStyle = rf.readUnsignedShort(); rf.skip(4); int indexToLocFormat = rf.readUnsignedShort(); head.locaBytesPerEntry = indexToLocFormat == 0 ? 2 : 4; }
private void fillHHea() throws DocumentException, IOException { int[] tableLocation = getTableLocation("hhea"); rf.seek(tableLocation[0] + 4); hhea.Ascender = rf.readShort(); hhea.Descender = rf.readShort(); hhea.LineGap = rf.readShort(); hhea.advanceWidthMax = rf.readUnsignedShort(); hhea.minLeftSideBearing = rf.readShort(); hhea.minRightSideBearing = rf.readShort(); hhea.xMaxExtent = rf.readShort(); hhea.caretSlopeRise = rf.readShort(); hhea.caretSlopeRun = rf.readShort(); rf.skipBytes(12); hhea.numberOfHMetrics = rf.readUnsignedShort(); }
public TIFFDirectory(RandomAccessFileOrArray stream, int directory) throws IOException { long global_save_offset = stream.getFilePointer(); long ifd_offset; stream.seek(0L); int endian = stream.readUnsignedShort(); if (!isValidEndianTag(endian)) { throw new IllegalArgumentException("Bad endianness tag (not 0x4949 or 0x4d4d)."); } isBigEndian = (endian == 0x4d4d); int magic = readUnsignedShort(stream); if (magic != 42) { throw new IllegalArgumentException("Bad magic number, should be 42."); } ifd_offset = readUnsignedInt(stream); for (int i = 0; i < directory; i++) { if (ifd_offset == 0L) { throw new IllegalArgumentException("Directory number too large."); } stream.seek(ifd_offset); int entries = readUnsignedShort(stream); stream.skip(12 * entries); ifd_offset = readUnsignedInt(stream); } stream.seek(ifd_offset); initialize(stream); stream.seek(global_save_offset); }
private int readUnsignedShort(RandomAccessFileOrArray stream) throws IOException { if (isBigEndian) { return stream.readUnsignedShort(); } else { return stream.readUnsignedShortLE(); } }
public static int getNumDirectories(RandomAccessFileOrArray stream) throws IOException { long pointer = stream.getFilePointer(); stream.seek(0L); int endian = stream.readUnsignedShort(); if (!isValidEndianTag(endian)) { throw new IllegalArgumentException("Bad endianness tag (not 0x4949 or 0x4d4d)."); } boolean isBigEndian = (endian == 0x4d4d); int magic = readUnsignedShort(stream, isBigEndian); if (magic != 42) { throw new IllegalArgumentException("Bad magic number, should be 42."); } stream.seek(4L); long offset = readUnsignedInt(stream, isBigEndian); int numDirectories = 0; while (offset != 0L) { ++numDirectories; try { stream.seek(offset); int entries = readUnsignedShort(stream, isBigEndian); stream.skip(12 * entries); offset = readUnsignedInt(stream, isBigEndian); } catch (EOFException eof) { break; } } stream.seek(pointer); return numDirectories; }
public TIFFDirectory(RandomAccessFileOrArray stream, long ifd_offset, int directory) throws IOException { long global_save_offset = stream.getFilePointer(); stream.seek(0L); int endian = stream.readUnsignedShort(); if (!isValidEndianTag(endian)) { throw new IllegalArgumentException("Bad endianness tag (not 0x4949 or 0x4d4d)."); } isBigEndian = (endian == 0x4d4d); stream.seek(ifd_offset); int dirNum = 0; while (dirNum < directory) { int numEntries = readUnsignedShort(stream); stream.seek(ifd_offset + 12 * numEntries); ifd_offset = readUnsignedInt(stream); stream.seek(ifd_offset); dirNum++; } initialize(stream); stream.seek(global_save_offset); }
private void readBbox() throws DocumentException, IOException { int tableLocation[]; tableLocation = (int[]) positionTables.get("head"); if (tableLocation == null) throw new DocumentException("Table 'head' does not exist in " + fileName + style); rf.seek(tableLocation[0] + HEAD_LOCA_FORMAT_OFFSET); boolean locaShortTable = (rf.readUnsignedShort() == 0); tableLocation = (int[]) positionTables.get("loca"); if (tableLocation == null) return; rf.seek(tableLocation[0]); int locaTable[]; if (locaShortTable) { int entries = tableLocation[1] / 2; locaTable = new int[entries]; for (int k = 0; k < entries; ++k) locaTable[k] = rf.readUnsignedShort() * 2; } else { int entries = tableLocation[1] / 4; locaTable = new int[entries]; for (int k = 0; k < entries; ++k) locaTable[k] = rf.readInt(); } tableLocation = (int[]) positionTables.get("glyf"); if (tableLocation == null) throw new DocumentException("Table 'glyf' does not exist in " + fileName + style); int tableGlyphOffset = tableLocation[0]; bboxes = new int[locaTable.length - 1][]; for (int glyph = 0; glyph < locaTable.length - 1; ++glyph) { int start = locaTable[glyph]; if (start != locaTable[glyph + 1]) { rf.seek(tableGlyphOffset + start + 2); bboxes[glyph] = new int[] { (rf.readShort() * 1000) / head.unitsPerEm, (rf.readShort() * 1000) / head.unitsPerEm, (rf.readShort() * 1000) / head.unitsPerEm, (rf.readShort() * 1000) / head.unitsPerEm }; } } }
private void processPost() throws DocumentException, IOException { int[] tableLocation = getTableLocation("post"); if (tableLocation == null) { italicAngle = -Math.atan2(hhea.caretSlopeRun, hhea.caretSlopeRise) * 180 / Math.PI; return; } rf.seek(tableLocation[0] + 4); short mantissa = rf.readShort(); int fraction = rf.readUnsignedShort(); italicAngle = (double) mantissa + (double) fraction / 16384.0; underlinePosition = (double) rf.readShort() / head.unitsPerEm; underlineThickness = (double) rf.readShort() / head.unitsPerEm; isFixedPitch = rf.readInt() != 0; }
private int[] getGlyphDataPosition(int glyphIndex) throws IOException { int[] glyphDataPosition = new int[2]; int[] tableLocation = (int[]) positionTables.get("loca"); int offset = tableLocation[0] + head.locaBytesPerEntry * glyphIndex; rf.seek(offset); if (head.locaBytesPerEntry == 4) { glyphDataPosition[0] = rf.readInt(); glyphDataPosition[1] = rf.readInt() - glyphDataPosition[0]; } else { glyphDataPosition[0] = rf.readUnsignedShort() * 2; glyphDataPosition[1] = rf.readUnsignedShort() * 2 - glyphDataPosition[0]; } return glyphDataPosition; }
/** * Reads the several maps from the table 'cmap'. The maps of interest are 1.0 for symbolic fonts * and 3.1 for all others. A symbolic font is defined as having the map 3.0. * * @throws DocumentException the font is invalid * @throws IOException the font file could not be read */ public void readCMaps() throws DocumentException, IOException { int table_location[]; table_location = (int[]) positionTables.get("cmap"); if (table_location == null) throw new DocumentException("Table 'cmap' does not exist in " + fileName + style); rf.seek(table_location[0]); rf.skipBytes(2); int num_tables = rf.readUnsignedShort(); fontSpecific = false; int map10 = 0; int map31 = 0; int map30 = 0; for (int k = 0; k < num_tables; ++k) { int platId = rf.readUnsignedShort(); int platSpecId = rf.readUnsignedShort(); int offset = rf.readInt(); if (platId == 3 && platSpecId == 0) { fontSpecific = true; map30 = offset; } else if (platId == 3 && platSpecId == 1) { map31 = offset; } if (platId == 1 && platSpecId == 0) { map10 = offset; } } if (map10 > 0) { rf.seek(table_location[0] + map10); int format = rf.readUnsignedShort(); switch (format) { case 0: cmap10 = readFormat0(); break; case 4: cmap10 = readFormat4(); break; case 6: cmap10 = readFormat6(); break; } } if (map31 > 0) { rf.seek(table_location[0] + map31); int format = rf.readUnsignedShort(); if (format == 4) { cmap31 = readFormat4(); } } if (map30 > 0) { rf.seek(table_location[0] + map30); int format = rf.readUnsignedShort(); if (format == 4) { cmap10 = readFormat4(); } } }
private static RandomAccessFileOrArray populateDescription0( String path, BaseFont font, FontDescription descr, RandomAccessFileOrArray rf) throws NoSuchFieldException, IllegalAccessException, DocumentException, IOException { Map tables = extractTables(font); descr.setStyle(guessStyle(font)); int[] location = (int[]) tables.get("OS/2"); if (location == null) { throw new DocumentException("Table 'OS/2' does not exist in " + path); } rf.seek(location[0]); int want = 4; long got = rf.skip(want); if (got < want) { throw new DocumentException( "Skip TT font weight, expect read " + want + " bytes, but only got " + got); } descr.setWeight(rf.readUnsignedShort()); want = 20; got = rf.skip(want); if (got < want) { throw new DocumentException( "Skip TT font strikeout, expect read " + want + " bytes, but only got " + got); } descr.setYStrikeoutSize(rf.readShort()); descr.setYStrikeoutPosition(rf.readShort()); location = (int[]) tables.get("post"); if (location != null) { rf.seek(location[0]); want = 8; got = rf.skip(want); if (got < want) { throw new DocumentException( "Skip TT font underline, expect read " + want + " bytes, but only got " + got); } descr.setUnderlinePosition(rf.readShort()); descr.setUnderlineThickness(rf.readShort()); } rf.close(); rf = null; return rf; }
private void fillOS() throws DocumentException, IOException { int[] tableLocation = getTableLocation("OS/2"); rf.seek(tableLocation[0]); int version = rf.readUnsignedShort(); os_2.xAvgCharWidth = rf.readShort(); os_2.usWeightClass = rf.readUnsignedShort(); os_2.usWidthClass = rf.readUnsignedShort(); os_2.fsType = rf.readShort(); os_2.ySubscriptXSize = rf.readShort(); os_2.ySubscriptYSize = rf.readShort(); os_2.ySubscriptXOffset = rf.readShort(); os_2.ySubscriptYOffset = rf.readShort(); os_2.ySuperscriptXSize = rf.readShort(); os_2.ySuperscriptYSize = rf.readShort(); os_2.ySuperscriptXOffset = rf.readShort(); os_2.ySuperscriptYOffset = rf.readShort(); os_2.yStrikeoutSize = rf.readShort(); os_2.yStrikeoutPosition = rf.readShort(); os_2.sFamilyClass = rf.readShort(); rf.readFully(os_2.panose); rf.skipBytes(16); rf.readFully(os_2.achVendID); os_2.fsSelection = rf.readUnsignedShort(); os_2.usFirstCharIndex = rf.readUnsignedShort(); os_2.usLastCharIndex = rf.readUnsignedShort(); os_2.sTypoAscender = rf.readShort(); os_2.sTypoDescender = rf.readShort(); if (os_2.sTypoDescender > 0) os_2.sTypoDescender = (short) (-os_2.sTypoDescender); os_2.sTypoLineGap = rf.readShort(); os_2.usWinAscent = rf.readUnsignedShort(); os_2.usWinDescent = rf.readUnsignedShort(); os_2.ulCodePageRange1 = 0; os_2.ulCodePageRange2 = 0; if (version > 0) { os_2.ulCodePageRange1 = rf.readInt(); os_2.ulCodePageRange2 = rf.readInt(); } if (version > 1) { rf.skipBytes(2); os_2.sCapHeight = rf.readShort(); } else os_2.sCapHeight = (int) (0.7 * head.unitsPerEm); }
/** * The information in the maps of the table 'cmap' is coded in several formats. Format 4 is the * Microsoft standard character to glyph index mapping table. * * @return a <CODE>HashMap</CODE> representing this map * @throws IOException the font file could not be read */ HashMap<Integer, int[]> readFormat4() throws IOException { HashMap<Integer, int[]> h = new HashMap<Integer, int[]>(); int table_lenght = rf.readUnsignedShort(); rf.skipBytes(2); int segCount = rf.readUnsignedShort() / 2; rf.skipBytes(6); int endCount[] = new int[segCount]; for (int k = 0; k < segCount; ++k) { endCount[k] = rf.readUnsignedShort(); } rf.skipBytes(2); int startCount[] = new int[segCount]; for (int k = 0; k < segCount; ++k) { startCount[k] = rf.readUnsignedShort(); } int idDelta[] = new int[segCount]; for (int k = 0; k < segCount; ++k) { idDelta[k] = rf.readUnsignedShort(); } int idRO[] = new int[segCount]; for (int k = 0; k < segCount; ++k) { idRO[k] = rf.readUnsignedShort(); } int glyphId[] = new int[table_lenght / 2 - 8 - segCount * 4]; for (int k = 0; k < glyphId.length; ++k) { glyphId[k] = rf.readUnsignedShort(); } for (int k = 0; k < segCount; ++k) { int glyph; for (int j = startCount[k]; j <= endCount[k] && j != 0xFFFF; ++j) { if (idRO[k] == 0) { glyph = (j + idDelta[k]) & 0xFFFF; } else { int idx = k + idRO[k] / 2 - segCount + j - startCount[k]; if (idx >= glyphId.length) continue; glyph = (glyphId[idx] + idDelta[k]) & 0xFFFF; } int r[] = new int[2]; r[0] = glyph; r[1] = getGlyphWidth(r[0]); h.put((fontSpecific ? ((j & 0xff00) == 0xf000 ? j & 0xff : j) : j), r); } } return h; }
/** * Extracts the names of the font in all the languages available. * * @param id the name id to retrieve * @throws DocumentException on error * @throws IOException on error */ String[][] getNames(int id) throws DocumentException, IOException { int table_location[]; table_location = (int[]) positionTables.get("name"); if (table_location == null) throw new DocumentException("Table 'name' does not exist in " + fileName + style); rf.seek(table_location[0] + 2); int numRecords = rf.readUnsignedShort(); int startOfStorage = rf.readUnsignedShort(); ArrayList<String[]> names = new ArrayList<String[]>(); for (int k = 0; k < numRecords; ++k) { int platformID = rf.readUnsignedShort(); int platformEncodingID = rf.readUnsignedShort(); int languageID = rf.readUnsignedShort(); int nameID = rf.readUnsignedShort(); int length = rf.readUnsignedShort(); int offset = rf.readUnsignedShort(); if (nameID == id) { int pos = rf.getFilePointer(); rf.seek(table_location[0] + startOfStorage + offset); String name; if (platformID == 0 || platformID == 3 || (platformID == 2 && platformEncodingID == 1)) { name = readUnicodeString(length); } else { name = readStandardString(length); } names.add( new String[] { String.valueOf(platformID), String.valueOf(platformEncodingID), String.valueOf(languageID), name }); rf.seek(pos); } } String thisName[][] = new String[names.size()][]; for (int k = 0; k < names.size(); ++k) thisName[k] = (String[]) names.get(k); return thisName; }