/** * <summary>Loads format-0 cmap subtable (Byte encoding table, i.e. Apple standard * character-to-glyph index mapping table).</summary> */ private void LoadCMapFormat0( ) { /* * NOTE: This is a simple 1-to-1 mapping of character codes to glyph indices. * The glyph collection is limited to 256 entries. */ Symbolic = true; GlyphIndexes = new Dictionary <int, int>(256); // Skip to the mapping array! FontData.Skip(4); // Glyph index array. // Iterating through the glyph indexes... for ( int code = 0; code < 256; code++ ) { GlyphIndexes[ code // Character code. ] = FontData.ReadByte() // Glyph index. ; } }
/** * <summary>Parse the next token [PDF:1.6:3.1].</summary> * <remarks> * Contract: * <list type="bullet"> * <li>Preconditions: * <list type="number"> * <item>To properly parse the current token, the pointer MUST be just before its starting (leading whitespaces are ignored).</item> * </list> * </item> * <item>Postconditions: * <list type="number"> * <item id="moveNext_contract_post[0]">When this method terminates, the pointer IS at the last byte of the current token.</item> * </list> * </item> * <item>Invariants: * <list type="number"> * <item>The byte-level position of the pointer IS anytime (during token parsing) at the end of the current token (whereas the 'current token' represents the token-level position of the pointer).</item> * </list> * </item> * <item>Side-effects: * <list type="number"> * <item>See <see href="#moveNext_contract_post[0]">Postconditions</see>.</item> * </list> * </item> * </list> * </remarks> * <returns>Whether a new token was found.</returns> */ public bool MoveNext( ) { if (stream == null) { return(false); } /* * NOTE: It'd be interesting to evaluate an alternative regular-expression-based * implementation... */ int c = 0; // Skip white-space characters [PDF:1.6:3.1.1]. while (true) { c = stream.ReadByte(); if (c == -1) { /* NOTE: Current stream has finished. */ // Move to the next stream! MoveNextStream(); // No more streams? if (stream == null) { return(false); } } else if (!IsWhitespace(c)) // Keep goin' till there's a white-space character... { break; } } StringBuilder buffer = null; token = null; // Which character is it? switch (c) { case '/': // Name. tokenType = TokenTypeEnum.Name; buffer = new StringBuilder(); while ((c = stream.ReadByte()) != -1) { if (IsDelimiter(c) || IsWhitespace(c)) { break; } // Is it an hexadecimal code [PDF:1.6:3.2.4]? if (c == '#') { try { c = (GetHex(stream.ReadByte()) << 4) + GetHex(stream.ReadByte()); } catch { throw new FileFormatException("Unexpected EOF (malformed hexadecimal code in name object).", stream.Position); } } buffer.Append((char)c); } stream.Skip(-1); // Recover the first byte after the current token. break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '.': case '-': case '+': // Number [PDF:1.6:3.2.2] | Indirect reference. switch (c) { case '.': // Decimal point. tokenType = TokenTypeEnum.Real; break; default: // Digit or signum. tokenType = TokenTypeEnum.Integer; // By default (it may be real). break; } // Building the number... buffer = new StringBuilder(); do { buffer.Append((char)c); c = stream.ReadByte(); if (c == -1) { break; } if (c == '.') { tokenType = TokenTypeEnum.Real; } else if (c < '0' || c > '9') { break; } } while(true); stream.Skip(-1); // Recover the first byte after the current token. break; case '[': // Array (begin). tokenType = TokenTypeEnum.ArrayBegin; break; case ']': // Array (end). tokenType = TokenTypeEnum.ArrayEnd; break; case '<': // Dictionary (begin) | Hexadecimal string. c = stream.ReadByte(); if (c == -1) { throw new FileFormatException("Unexpected EOF (isolated opening angle-bracket character).", stream.Position); } // Is it a dictionary (2nd angle bracket [PDF:1.6:3.2.6])? if (c == '<') { tokenType = TokenTypeEnum.DictionaryBegin; break; } // Hexadecimal string (single angle bracket [PDF:1.6:3.2.3]). tokenType = TokenTypeEnum.Hex; // [FIX:0.0.4:4] It skipped after the first hexadecimal character, missing it. buffer = new StringBuilder(); while (c != '>') // NOT string end. { buffer.Append((char)c); c = stream.ReadByte(); if (c == -1) { throw new FileFormatException("Unexpected EOF (malformed hex string).", stream.Position); } } break; case '>': // Dictionary (end). c = stream.ReadByte(); if (c != '>') { throw new FileFormatException("Malformed dictionary.", stream.Position); } tokenType = TokenTypeEnum.DictionaryEnd; break; case '(': // Literal string. tokenType = TokenTypeEnum.Literal; buffer = new StringBuilder(); int level = 0; while (true) { c = stream.ReadByte(); if (c == -1) { break; } if (c == '(') { level++; } else if (c == ')') { level--; } else if (c == '\\') { bool lineBreak = false; c = stream.ReadByte(); switch (c) { case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; case 'b': c = '\b'; break; case 'f': c = '\f'; break; case '(': case ')': case '\\': break; case '\r': lineBreak = true; c = stream.ReadByte(); if (c != '\n') { stream.Skip(-1); } break; case '\n': lineBreak = true; break; default: { // Is it outside the octal encoding? if (c < '0' || c > '7') { break; } // Octal [PDF:1.6:3.2.3]. int octal = c - '0'; c = stream.ReadByte(); // Octal end? if (c < '0' || c > '7') { c = octal; stream.Skip(-1); break; } octal = (octal << 3) + c - '0'; c = stream.ReadByte(); // Octal end? if (c < '0' || c > '7') { c = octal; stream.Skip(-1); break; } octal = (octal << 3) + c - '0'; c = octal & 0xff; break; } } if (lineBreak) { continue; } if (c == -1) { break; } } else if (c == '\r') { c = stream.ReadByte(); if (c == -1) { break; } if (c != '\n') { c = '\n'; stream.Skip(-1); } } if (level == -1) { break; } buffer.Append((char)c); } if (c == -1) { throw new FileFormatException("Malformed literal string.", stream.Position); } break; case '%': // Comment. tokenType = TokenTypeEnum.Comment; buffer = new StringBuilder(); while (true) { c = stream.ReadByte(); if (c == -1 || IsEOL(c)) { break; } buffer.Append((char)c); } break; default: // Keyword. tokenType = TokenTypeEnum.Keyword; buffer = new StringBuilder(); do { buffer.Append((char)c); c = stream.ReadByte(); if (c == -1) { break; } } while(!IsDelimiter(c) && !IsWhitespace(c)); stream.Skip(-1); // Recover the first byte after the current token. break; } if (buffer != null) { /* * Here we prepare the current token state. */ // Wich token type? switch (tokenType) { case TokenTypeEnum.Keyword: token = buffer.ToString(); // Late recognition. switch ((string)token) { case Keyword.False: case Keyword.True: // Boolean. tokenType = TokenTypeEnum.Boolean; token = bool.Parse((string)token); break; case Keyword.Null: // Null. tokenType = TokenTypeEnum.Null; token = null; break; } break; case TokenTypeEnum.Comment: case TokenTypeEnum.Hex: case TokenTypeEnum.Name: token = buffer.ToString(); break; case TokenTypeEnum.Literal: token = buffer.ToString(); // Late recognition. if (((string)token).StartsWith("D:")) // Date. { tokenType = TokenTypeEnum.Date; token = PdfDate.ToDate((string)token); } break; case TokenTypeEnum.Integer: token = Int32.Parse( buffer.ToString(), NumberStyles.Integer, StandardNumberFormatInfo ); break; case TokenTypeEnum.Real: // [FIX:1668410] Parsing of float numbers was buggy (localized default number format). token = Single.Parse( buffer.ToString(), NumberStyles.Float, StandardNumberFormatInfo ); break; } } return(true); }
/** * <summary>Loads the font data.</summary> */ private void Load( ) { Metrics = new FontMetrics(); // 1. Offset Table. FontData.Seek(0); // Get the outline format! this.OutlineFormat = GetOutlineFormat(FontData.ReadInt()); // Get the number of tables! int tableCount = FontData.ReadUnsignedShort(); tableOffsets = new Dictionary <string, int>(tableCount); // 2. Table Directory. // Skip to the beginning of the table directory! FontData.Skip(6); // Collecting the table offsets... for ( int index = 0; index < tableCount; index++ ) { // Get the table tag! String tag = ReadAsciiString(4); // Skip to the table offset! FontData.Skip(4); // Get the table offset! int offset = FontData.ReadInt(); // Collect the table offset! tableOffsets[tag] = offset; // Skip to the next entry! FontData.Skip(4); } FontName = Load_GetName(NameID_FontPostscriptName); Load_Tables(); Load_CMap(); Load_GlyphWidths(); Load_GlyphKerning(); }