private static int LoadSubTable(FontParser parser, int start, FontFace font, Glyph[] glyphs) { // Total characters in subtable: int characterCount = 0; // Seek to the cmap now: parser.Position = start; // Check it's format 4: int format = parser.ReadUInt16(); #if INFINITEXT_DEBUG Fonts.OnLogMessage("Cmap subtable format: " + format); #endif if (format > 13) { // We now have e.g. 14.0 - ulong here ("Length"): parser.Position += 4; } else if (format > 6) { // We now have e.g. 12.0 - another short here (reserved): parser.Position += 2; // Length and language are both 4 byte ints now: parser.Position += 8; } else { // Size of the sub-table (map length, u16): parser.Position += 2; // Structure of the sub-table (map language, u16): parser.Position += 2; } switch (format) { case 0: // Byte encoding table: for (int i = 0; i < 256; i++) { int rByte = parser.ReadByte(); Glyph glyph = glyphs[rByte]; if (glyph != null) { characterCount++; glyph.AddCharcode(i); } } break; case 2: // The offset to the headers: int subOffset = parser.Position + (256 * 2); // For each high byte: for (int i = 0; i < 256; i++) { // Read the index to the header and zero out the bottom 3 bits: int headerPosition = subOffset + (parser.ReadUInt16() & (~7)); // Read the header: int firstCode = parser.ReadUInt16(ref headerPosition); int entryCount = parser.ReadUInt16(ref headerPosition); short idDelta = parser.ReadInt16(ref headerPosition); // Grab the current position: int pos = headerPosition; // Read the idRangeOffset - the last part of the header: pos += parser.ReadUInt16(ref headerPosition); int maxCode = firstCode + entryCount; // Get the high byte value: int highByte = (i << 8); // For each low byte: for (int j = firstCode; j < maxCode; j++) { // Get the full charcode (which might not actually exist yet): int charCode = highByte + j; // Read the base of the glyphIndex: int p = parser.ReadUInt16(ref pos); if (p == 0) { continue; } p = (p + idDelta) & 0xFFFF; if (p == 0) { continue; } Glyph glyph = glyphs[p]; if (glyph != null) { characterCount++; glyph.AddCharcode(charCode); } } } break; case 4: // Segment count. It's doubled. int segCount = (parser.ReadUInt16() >> 1); // Search range, entry selector and range shift (don't need any): parser.Position += 6; int baseIndex = parser.Position; int endCountIndex = baseIndex; baseIndex += 2; int startCountIndex = baseIndex + segCount * 2; int idDeltaIndex = baseIndex + segCount * 4; int idRangeOffsetIndex = baseIndex + segCount * 6; for (int i = 0; i < segCount - 1; i++) { int endCount = parser.ReadUInt16(ref endCountIndex); int startCount = parser.ReadUInt16(ref startCountIndex); int idDelta = parser.ReadInt16(ref idDeltaIndex); int idRangeOffset = parser.ReadUInt16(ref idRangeOffsetIndex); for (int c = startCount; c <= endCount; c++) { int glyphIndex; if (idRangeOffset != 0) { // The idRangeOffset is relative to the current position in the idRangeOffset array. // Take the current offset in the idRangeOffset array. int glyphIndexOffset = (idRangeOffsetIndex - 2); // Add the value of the idRangeOffset, which will move us into the glyphIndex array. glyphIndexOffset += idRangeOffset; // Then add the character index of the current segment, multiplied by 2 for USHORTs. glyphIndexOffset += (c - startCount) * 2; glyphIndex = parser.ReadUInt16(ref glyphIndexOffset); if (glyphIndex != 0) { glyphIndex = (glyphIndex + idDelta) & 0xFFFF; } } else { glyphIndex = (c + idDelta) & 0xFFFF; } // Add a charcode to the glyph now: Glyph glyph = glyphs[glyphIndex]; if (glyph != null) { characterCount++; glyph.AddCharcode(c); } } } break; case 6: int firstCCode = parser.ReadUInt16(); int entryCCount = parser.ReadUInt16(); for (int i = 0; i < entryCCount; i++) { Glyph glyphC = glyphs[parser.ReadUInt16()]; if (glyphC != null) { characterCount++; glyphC.AddCharcode(firstCCode + i); } } break; case 10: // Trimmed array. Similar to format 6. int startCharCode = parser.ReadUInt16(); int numChars = parser.ReadUInt16(); for (int i = 0; i < numChars; i++) { Glyph glyphC = glyphs[parser.ReadUInt16()]; if (glyphC != null) { characterCount++; glyphC.AddCharcode(startCharCode + i); } } break; case 12: // Segmented coverage. // Mapping of 1 charcode to 1 glyph. "Segmented" because it can come in blocks called groups. int groups = (int)parser.ReadUInt32(); // For each group of glyphs.. for (int i = 0; i < groups; i++) { // Start/end charcode: int startCode = (int)parser.ReadUInt32(); int endCode = (int)parser.ReadUInt32(); // Start glyph ID: int startGlyph = (int)parser.ReadUInt32(); int count = (endCode - startCode); // For each glyph/charcode pair.. for (int j = 0; j <= count; j++) { // Get the glyph: int glyphIndex = (startGlyph + j); Glyph glyph = glyphs[glyphIndex]; if (glyph != null) { characterCount++; // Charcode is.. glyph.AddCharcode(startCode + j); } } } break; case 13: // Many to one. Same format as #12 but the meaning is a bit different. // How many groups? int glyphCount = (int)parser.ReadUInt32(); for (int i = 0; i < glyphCount; i++) { int startCode = (int)parser.ReadUInt32(); int endCode = (int)parser.ReadUInt32(); int glyphID = (int)parser.ReadUInt32(); // Get the glyph: Glyph glyph = glyphs[glyphID]; if (glyph != null) { int count = (endCode - startCode); // For each charcode.. for (int j = 0; j <= count; j++) { characterCount++; // Hook up glyph to this charcode: glyph.AddCharcode(startCode + j); } } } break; case 14: Fonts.OnLogMessage("InfiniText partially supports part of the font '" + font.Family.Name + "' - this is harmless. Search for this message for more."); // This font contains a format 14 CMAP Table. // Format 14 is "Unicode variation selectors" - essentially different versions of the same character. // E.g. a text Emoji character and a graphical one. // In a text system like InfiniText, that just means we must map a bunch of different charcodes // to the same glyph. // .. I Think! As usual, the OpenType spec doesn't make too much sense. // However, it appears to be entirely optional. // So, approx implementation is below, however getting the utf32 code point from the variation + base character // is completely undocumented - my best guess unfortunately threw errors. // See the commented out block below! break; /* * * case 14: * * // How many var selector records? * int records=(int)parser.ReadUInt32(); * * for(int i=0;i<records;i++){ * * // variation selector: * int varSelector=(int)parser.ReadUInt24(); * * // Offsets: * int defaultUVSOffset=(int)parser.ReadUInt32(); * int nonDefaultUVSOffset=(int)parser.ReadUInt32(); * * // Grab parser position: * int position=parser.Position; * * // Got a ref to a default style table? * if(defaultUVSOffset!=0){ * * // Yep! The UVS is simply a list of "base" characters, each with ranges of available charcodes. * // [BaseCharCode][The extended part. Each of these comes from the range.] * // The actual glyph is the one that we get by directly looking up each of the base characters. * * // Seek to the table: * parser.Position=start+defaultUVSOffset; * * // Read the unicode value ranges count: * int numUniRangesCount=(int)parser.ReadUInt32(); * * // For each one.. * for(int m=0;m<numUniRangesCount;m++){ * * // Read the base charcode: * int baseCharcode=(int)parser.ReadUInt24(); * * // Read the size of the range: * byte rangeSize=parser.ReadByte(); * * for(int c=0;c<=rangeSize;c++){ * * // Fetch the base glyph: * Glyph glyph=font.GetGlyphDirect(baseCharcode); * * if(glyph!=null){ * * // Combine baseCharcode with varSelector next to form the variation (of "glyph"). * * // Get the full charcode (this is incorrect!): * // int charcode=char.ConvertToUtf32((char)baseCharcode,(char)varSelector); * * // Add: * //glyph.AddCharcode(charcode); * * } * * // Move baseCharcode along: * baseCharcode++; * * } * * } * * // Restore parser: * parser.Position=position; * * } * * // Got a ref to a non-default style table? * if(nonDefaultUVSOffset!=0){ * * // Yep! The UVS is simply a list of "base" characters, each with ranges of available charcodes. * // [BaseCharCode][The extended part. Each of these comes from the range.] * // This time though, the glyph to use is directly specified * // (that's what gives it the "non-default" property). * * // Seek to the table: * parser.Position=start+nonDefaultUVSOffset; * * // Read the number of mappings: * int numMappings=(int)parser.ReadUInt32(); * * // For each one.. * for(int m=0;m<numMappings;m++){ * * // Read the base charcode: * int baseCharcode=(int)parser.ReadUInt24(); * * // Read glyph ID: * int glyphID=(int)parser.ReadUInt16(); * * // Get the glyph: * Glyph glyph=glyphs[glyphID]; * * if(glyph!=null){ * * // Combine baseCharcode with varSelector next to form the variation (of "glyph"). * * // Get the full charcode (this is incorrect!): * // int charcode=char.ConvertToUtf32((char)baseCharcode,(char)varSelector); * * // Add: * //glyph.AddCharcode(charcode); * * } * * } * * // Restore parser: * parser.Position=position; * * } * * } * * break; * */ default: Fonts.OnLogMessage("InfiniText does not currently support part of this font. If you need it, please contact us with this: Format: " + format); break; } return(characterCount); }
public static bool Load(FontParser parser, int start, FontFace font, Glyph[] glyphs) { // Seek there: parser.Position = start; // Read the version (and check if it's zero): if (parser.ReadUInt16() != 0) { return(false); } // Strangely the cmap table can have lots of tables inside it. For now we're looking for the common type 3 table. // Number of tables: int tableCount = parser.ReadUInt16(); // Total characters in font: int characterCount = 0; int offset = -1; int favour = 0; for (int i = 0; i < tableCount; i += 1) { // Grab the platform ID: int platformId = parser.ReadUInt16(); // And the encoding ID: int encodingId = parser.ReadUInt16(); if (platformId == 3 || platformId == 0) { if (encodingId == 10) { // Top favourite - most broad Unicode encoding. // Read offset: offset = (int)parser.ReadUInt32(); break; } else if (encodingId == 1 || encodingId == 0) { // Read offset: offset = (int)parser.ReadUInt32(); // Mid-range favourite: favour = 1; continue; } else if (favour == 0) { // Anything else we'll give a try: // Read offset (but don't break): offset = (int)parser.ReadUInt32(); continue; } } // Skip offset: parser.Position += 4; } if (offset == -1) { // We don't support this font :( return(false); } // Seek to the cmap now: parser.Position = start + offset; // Check it's format 4: int format = parser.ReadUInt16(); if (format > 6) { // We now have e.g. 12.0 - another short here: parser.Position += 2; // Size/ structure are both 4 byte ints now: parser.Position += 8; } else { // Size of the sub-table (map length, u16): parser.Position += 2; // Structure of the sub-table (map language, u16): parser.Position += 2; } switch (format) { case 0: // Byte encoding table: for (int i = 0; i < 256; i++) { int rByte = parser.ReadByte(); Glyph glyph = glyphs[rByte]; if (glyph != null) { characterCount++; glyph.AddCharcode(i); } } break; case 2: // The offset to the headers: int subOffset = parser.Position + (256 * 2); // For each high byte: for (int i = 0; i < 256; i++) { // Read the index to the header and zero out the bottom 3 bits: int headerPosition = subOffset + (parser.ReadUInt16() & (~7)); // Read the header: int firstCode = parser.ReadUInt16(ref headerPosition); int entryCount = parser.ReadUInt16(ref headerPosition); short idDelta = parser.ReadInt16(ref headerPosition); // Grab the current position: int pos = headerPosition; // Read the idRangeOffset - the last part of the header: pos += parser.ReadUInt16(ref headerPosition); int maxCode = firstCode + entryCount; // Get the high byte value: int highByte = (i << 8); // For each low byte: for (int j = firstCode; j < maxCode; j++) { // Get the full charcode (which might not actually exist yet): int charCode = highByte + j; // Read the base of the glyphIndex: int p = parser.ReadUInt16(ref pos); if (p == 0) { continue; } p = (p + idDelta) & 0xFFFF; if (p == 0) { continue; } Glyph glyph = glyphs[p]; if (glyph != null) { characterCount++; glyph.AddCharcode(charCode); } } } break; case 4: // Segment count. It's doubled. int segCount = (parser.ReadUInt16() >> 1); // Search range, entry selector and range shift (don't need any): parser.Position += 6; int baseIndex = parser.Position; int endCountIndex = baseIndex; baseIndex += 2; int startCountIndex = baseIndex + segCount * 2; int idDeltaIndex = baseIndex + segCount * 4; int idRangeOffsetIndex = baseIndex + segCount * 6; for (int i = 0; i < segCount - 1; i++) { int endCount = parser.ReadUInt16(ref endCountIndex); int startCount = parser.ReadUInt16(ref startCountIndex); int idDelta = parser.ReadInt16(ref idDeltaIndex); int idRangeOffset = parser.ReadUInt16(ref idRangeOffsetIndex); for (int c = startCount; c <= endCount; c++) { int glyphIndex; if (idRangeOffset != 0) { // The idRangeOffset is relative to the current position in the idRangeOffset array. // Take the current offset in the idRangeOffset array. int glyphIndexOffset = (idRangeOffsetIndex - 2); // Add the value of the idRangeOffset, which will move us into the glyphIndex array. glyphIndexOffset += idRangeOffset; // Then add the character index of the current segment, multiplied by 2 for USHORTs. glyphIndexOffset += (c - startCount) * 2; glyphIndex = parser.ReadUInt16(ref glyphIndexOffset); if (glyphIndex != 0) { glyphIndex = (glyphIndex + idDelta) & 0xFFFF; } } else { glyphIndex = (c + idDelta) & 0xFFFF; } // Add a charcode to the glyph now: Glyph glyph = glyphs[glyphIndex]; if (glyph != null) { characterCount++; glyph.AddCharcode(c); } } } break; case 6: int firstCCode = parser.ReadUInt16(); int entryCCount = parser.ReadUInt16(); for (int i = 0; i < entryCCount; i++) { Glyph glyphC = glyphs[parser.ReadUInt16()]; if (glyphC != null) { characterCount++; glyphC.AddCharcode(firstCCode + i); } } break; case 12: int groups = (int)parser.ReadUInt32(); for (int i = 0; i < groups; i++) { int startCode = (int)parser.ReadUInt32(); int endCode = (int)parser.ReadUInt32(); int startGlyph = (int)parser.ReadUInt32(); int count = (endCode - startCode); for (int j = 0; j <= count; j++) { int glyphIndex = (startGlyph + j); Glyph glyph = glyphs[glyphIndex]; if (glyph != null) { characterCount++; glyph.AddCharcode(startCode + j); } } } break; default: Fonts.OnLogMessage("InfiniText does not currently support this font. If you need it, please contact us with this: Format: " + format); break; } font.CharacterCount = characterCount; return(true); }