/// <summary> /// Append text string /// </summary> /// <param name="Text"></param> public void AppendText ( string Text ) { // remove double delimeters if (ByteArray.Count > 0 && !PdfBase.IsDelimiter(ByteArray[ByteArray.Count - 1]) && !PdfBase.IsDelimiter(Text[0])) { ByteArray.Add((byte)' '); } // move charaters to bytes foreach (char Chr in Text) { ByteArray.Add((byte)Chr); } return; }
//////////////////////////////////////////////////////////////////// // Parse Dictionary //////////////////////////////////////////////////////////////////// internal PdfDictionary ParseDictionary ( bool InlineImage ) { // create empty dictionary PdfDictionary Dictionary = new PdfDictionary(); // read first character after << NextChar = ReadChar(); // loop until closing >> or EOF for (;;) { // skip white space SkipWhiteSpace(); // end of file if (NextChar == EOF) { throw new ApplicationException("Invalid dictionary (end of contents)"); } // next character must be / for name if (NextChar != '/') { // end of dictionary if (!InlineImage) { if (NextChar == '>' && ReadChar() == '>') { break; } } // inline image else { if (NextChar == 'I' && ReadChar() == 'D') { break; } } throw new ApplicationException("Invalid dictionary (name entry must have /)"); } // read name StringBuilder Name = new StringBuilder(); Name.Append((char)NextChar); // add more characters until next delimiter while ((NextChar = ReadChar()) != EOF && !PdfBase.IsDelimiter(NextChar)) { Name.Append((char)NextChar); } // read next item PdfBase Value = ParseNextItem(); // end of file if (Value.IsEmpty) { throw new ApplicationException("Invalid dictionary (end of contents)"); } // add to result dictionary Dictionary.AddKeyValue(Name.ToString(), Value); } // read next character after >> or ID NextChar = ReadChar(); // exit return(Dictionary); }
//////////////////////////////////////////////////////////////////// // Test for reference // We have positive integer already. Test for zero and R //////////////////////////////////////////////////////////////////// internal bool TestReference() { // save current file position int Pos = GetPos(); // save next character int TempChar = NextChar; for (;;) { // next character must be space if (!PdfBase.IsWhiteSpace(TempChar)) { break; } // skip additional white space while ((TempChar = ReadChar()) != EOF && PdfBase.IsWhiteSpace(TempChar)) { ; } // generation is not supported // next character must be zero if (TempChar != '0') { break; } // next character must be white space TempChar = ReadChar(); if (!PdfBase.IsWhiteSpace(TempChar)) { break; } // skip additional white space while ((TempChar = ReadChar()) != EOF && PdfBase.IsWhiteSpace(TempChar)) { ; } // next character must be R if (TempChar != 'R') { break; } // next character must be a delimiter TempChar = ReadChar(); if (!PdfBase.IsDelimiter(TempChar)) { break; } // found NextChar = TempChar; return(true); } // restore position SetPos(Pos); return(false); }
/// <summary> /// Parse object reference number n 0 R obj /// </summary> /// <returns>Object number</returns> public int ParseObjectRefNo() { // loop in case of one or more comments SkipComments(); // must be a digit if (NextChar < '0' || NextChar > '9') { return(0); } // next content element StringBuilder NextItem = new StringBuilder(); NextItem.Append((char)NextChar); // add more characters until next delimiter while ((NextChar = ReadChar()) != EOF && !PdfBase.IsDelimiter(NextChar)) { NextItem.Append((char)NextChar); } // integer if (!int.TryParse(NextItem.ToString(), out int ObjNo) || ObjNo <= 0) { return(0); } // next character must be space if (!PdfBase.IsWhiteSpace(NextChar)) { return(0); } // skip additional white space while ((NextChar = ReadChar()) != EOF && PdfBase.IsWhiteSpace(NextChar)) { ; } // next character must be zero if (NextChar != '0') { return(0); } // next character must be white space NextChar = ReadChar(); if (!PdfBase.IsWhiteSpace(NextChar)) { return(0); } // skip additional white space while ((NextChar = ReadChar()) != EOF && PdfBase.IsWhiteSpace(NextChar)) { ; } // next 3 characters must be obj if (NextChar != 'o' || ReadChar() != 'b' || ReadChar() != 'j') { return(0); } // next character must be a delimiter NextChar = ReadChar(); if (!PdfBase.IsDelimiter(NextChar)) { return(0); } // return object number return(ObjNo); }
/// <summary> /// Parse next item /// </summary> /// <returns>Derived class from PdfBase</returns> public PdfBase ParseNextItem() { // loop in case of one or more comments SkipComments(); // end of file if (NextChar == EOF) { return(PdfBase.Empty); } // string if (NextChar == '(') { return(ParseString()); } // array if (NextChar == '[') { return(ParseArray()); } // hex string or dictionary if (NextChar == '<') { // test for dictionary if (ReadChar() == '<') { return(ParseDictionary(false)); } // move pointer back StepBack(); // hex string return(ParseHexString()); } // next content element StringBuilder NextItem = new StringBuilder(); NextItem.Append((char)NextChar); // add more characters until next delimiter while ((NextChar = ReadChar()) != EOF && !PdfBase.IsDelimiter(NextChar)) { NextItem.Append((char)NextChar); } // convert next item to string token string Token = NextItem.ToString(); // name if (Token[0] == '/') { // empty name if (Token.Length == 1) { throw new ApplicationException("Empty name token"); } // exit return(new PdfName(Token)); } // integer if (int.TryParse(Token, out int IntVal)) { // if parsing non contents streams, an integer can be the start of indirect reference number if (!ContentsStream && IntVal > 0 && TestReference()) { return(new PdfReference(IntVal)); } // integer return(new PdfInteger(IntVal)); } // real number with period as decimal separator regardless of region if (float.TryParse(Token, NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign, NumFormatInfo.PeriodDecSep, out float RealVal)) { // if real number is an integer return PdfInt object int TestInt = (int)Math.Truncate(RealVal); if (RealVal == (double)TestInt) { return(new PdfInteger(TestInt)); } return(new PdfReal(RealVal)); } // false if (Token == "false") { return(new PdfBoolean(false)); } // true if (Token == "true") { return(new PdfBoolean(true)); } // null if (Token == "null") { return(new PdfNull()); } // parse all but contents stream if (!ContentsStream) { // stream special case if (Token == "stream") { // stream must be foloowed by NL or CR and NL // if(NextChar == '\n' || NextChar == '\r' && ReadChar() == '\n') return new PdfKeyword(KeyWord.Stream); if (NextChar == '\n') { return(new PdfKeyword(KeyWord.Stream)); } if (NextChar == '\r') { // the PDF spec is very clear that stream must be foloowed by NL or CR and NL // CR by itself is not acceptable if (ReadChar() != '\n') { // HP Scanners Scanned PDF does not conform to PDF standards // https://www.google.com/search?client=firefox-b-d&q=hp+officejet+PDF+scan+files+not+standard // step back to allow re-parsing of the last character StepBack(); Reader.InvalidPdfFile = true; } return(new PdfKeyword(KeyWord.Stream)); } // error throw new ApplicationException("Stream word must be followed by EOL"); } // endstream if (Token == "endstream") { return(new PdfKeyword(KeyWord.EndStream)); } // endobj if (Token == "endobj") { return(new PdfKeyword(KeyWord.EndObj)); } // xref if (Token == "xref") { return(new PdfKeyword(KeyWord.XRef)); } // xref n if (Token == "n") { return(new PdfKeyword(KeyWord.N)); } // xref f if (Token == "f") { return(new PdfKeyword(KeyWord.F)); } // trailer if (Token == "trailer") { return(new PdfKeyword(KeyWord.Trailer)); } } // parse contents stream else { // search for contents operator int OpIndex = Array.BinarySearch(OpCtrl.OpCtrlArray, new OpCtrl(Token)); // not found if (OpIndex < 0) { throw new ApplicationException("Parsing failed: Unknown contents operator"); } // operator enumeration Operator OpCode = OpCtrl.OpCtrlArray[OpIndex].OpCode; // inline image if (OpCode == Operator.BeginInlineImage) { return(ParseInlineImage()); } // PDF operator object if (OpCode != Operator.BeginInlineImageData && OpCode != Operator.EndInlineImage) { return(new PdfOp(OpCode)); } } // error throw new ApplicationException("Parsing failed: Unknown token: " + Token); }