Advertisement
-Burgonya-Rabir

COS Parser

Mar 22nd, 2018
90
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 20.66 KB | None | 0 0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6.  
  7. using System.IO;
  8. using System.Collections;
  9.  
  10. namespace ConsoleTest
  11. {
  12.     class COSParse
  13.     {
  14.         const string whiteSpaces = " \r\n\t";
  15.         const string blockTerminators = whiteSpaces + "/[]<>()";
  16.         const string numbers = "0123456789";
  17.         public const string cosNumberFilter = "0123456789.";
  18.  
  19.         MemoryStream stream;
  20.         BinaryReader reader;
  21.  
  22.         List<COSObj> objects = new List<COSObj>();
  23.  
  24.         public COSParse(string filePath)
  25.         {
  26.             this.stream = new MemoryStream();
  27.  
  28.             FileStream file = new FileStream(filePath, FileMode.Open);
  29.             file.CopyTo(this.stream);
  30.             file.Close();
  31.  
  32.             this.reader = new BinaryReader(this.stream, Encoding.GetEncoding(1252));
  33.             this.reader.BaseStream.Seek(0, SeekOrigin.Begin);
  34.        
  35.             this.ParseStream();
  36.         }
  37.  
  38.         #region Document Level
  39.         public void ParseStream()
  40.         {
  41.             while (this.reader.BaseStream.Position < this.reader.BaseStream.Length)
  42.             {
  43.                 if ((char)this.reader.PeekChar() == '%') SkipComment(this.reader);
  44.                 else if (numbers.Contains((char)reader.PeekChar())) this.objects.Add(this.ParseNextObject());
  45.                 else if (this.ContinueWith(this.reader, "startxref")) this.ParseStartXref();
  46.                 else
  47.                 {
  48.                     throw new Exception($"Unknown Char: {reader.PeekChar()}: {(char)reader.PeekChar()}");
  49.                 }
  50.  
  51.                 this.SkipWhiteSpaces(this.reader);
  52.             }
  53.         }
  54.  
  55.         public COSObj ParseNextObject()
  56.         {
  57.             string objectID = ReadBlock(reader);
  58.             string objectRevision = ReadBlock(reader);
  59.             string objSuffix = ReadBlock(reader);
  60.  
  61.             this.SkipToNextBlock(reader);
  62.  
  63.             COSType data = ParseNextData(reader);
  64.             this.SkipToNextBlock(reader);
  65.  
  66.             if (this.ContinueWith(reader, "endobj"))
  67.             {
  68.                 this.reader.ReadChar(); //e
  69.                 this.reader.ReadChar(); //n
  70.                 this.reader.ReadChar(); //d
  71.                 this.reader.ReadChar(); //o
  72.                 this.reader.ReadChar(); //b
  73.                 this.reader.ReadChar(); //j
  74.  
  75.                 return new COSObject(int.Parse(objectID), int.Parse(objectRevision), data);
  76.             }
  77.             else if (this.ContinueWith(reader, "stream"))
  78.             {
  79.                 List<byte> streamData = new List<byte>();
  80.                 while (!this.ContinueWith(reader, "endstream"))
  81.                 {
  82.                     streamData.Add(reader.ReadByte());
  83.                 }
  84.                 this.reader.ReadChar(); //e
  85.                 this.reader.ReadChar(); //n
  86.                 this.reader.ReadChar(); //d
  87.                 this.reader.ReadChar(); //s
  88.                 this.reader.ReadChar(); //t
  89.                 this.reader.ReadChar(); //r
  90.                 this.reader.ReadChar(); //e
  91.                 this.reader.ReadChar(); //a
  92.                 this.reader.ReadChar(); //m
  93.  
  94.                 this.SkipWhiteSpaces(reader);
  95.  
  96.                 this.reader.ReadChar(); //e
  97.                 this.reader.ReadChar(); //n
  98.                 this.reader.ReadChar(); //d
  99.                 this.reader.ReadChar(); //o
  100.                 this.reader.ReadChar(); //b
  101.                 this.reader.ReadChar(); //j
  102.  
  103.                 return new COSStreamObject(int.Parse(objectID), int.Parse(objectRevision), data as COSDictionary, streamData.ToArray());
  104.             }
  105.             else throw new Exception("Invalid object");
  106.         }
  107.  
  108.         public void ParseStartXref()
  109.         {
  110.             while (!this.ContinueWith(this.reader, "%%EOF")) this.reader.ReadChar();
  111.             this.reader.ReadChar(); //%
  112.             this.reader.ReadChar(); //%
  113.             this.reader.ReadChar(); //E
  114.             this.reader.ReadChar(); //O
  115.             this.reader.ReadChar(); //F
  116.         }
  117.         #endregion
  118.  
  119.         #region Object Level
  120.         public COSType ParseNextData(BinaryReader reader)
  121.         {
  122.             if (this.ContinueWith(reader, "<<")) return ParseDictionary(reader);
  123.             else if (reader.PeekChar() == '<' || reader.PeekChar() == '(') return ParseString(reader);
  124.             else if (reader.PeekChar() == '/') return ParseName(reader);
  125.             else if (this.reader.PeekChar() == '[') return ParseArray(reader);
  126.             else if (this.ContinueWith(reader, "true") || this.ContinueWith(reader, "false")) return ParseBool(reader);
  127.             else if (cosNumberFilter.Contains((char)reader.PeekChar())) //Number [num] / Indirect reference [int int, 'R']
  128.             {
  129.                 int num1v = 0;
  130.                 string num1 = ReadBlock(reader);
  131.                 long curPos = reader.BaseStream.Position;
  132.                 this.SkipToNextBlock(reader);
  133.  
  134.                 if (int.TryParse(num1, out num1v)) //Integer / Indirect Ref
  135.                 {
  136.                     int num2v = 0;
  137.                     string num2 = ReadBlock(reader);
  138.                     this.SkipToNextBlock(reader);
  139.  
  140.                     if (int.TryParse(num2, out num2v))  //2x Multiple integer / Indirect Ref
  141.                     {
  142.                         string letter = ReadBlock(reader);
  143.  
  144.                         if (letter == "R") return new COSReference(num1v, num2v); //Indirect Ref
  145.                     }
  146.                 }
  147.  
  148.                 reader.BaseStream.Seek(curPos, SeekOrigin.Begin);
  149.                 return new COSNumber(num1);
  150.  
  151.             }
  152.             else return null;
  153.         }
  154.  
  155.         public COSDictionary ParseDictionary(BinaryReader reader)
  156.         {
  157.             COSDictionary ret = new COSDictionary();
  158.  
  159.             reader.ReadChar(); //<
  160.             reader.ReadChar(); //<
  161.             SkipToNextBlock(reader);
  162.  
  163.             while (!this.ContinueWith(reader, ">>"))
  164.             {
  165.                 //Parse Dictionary Name
  166.                 COSName key = ParseName(reader);
  167.  
  168.                 this.SkipToNextBlock(reader);
  169.                 COSType value = ParseNextData(reader);
  170.  
  171.                 ret.Add(key, value);
  172.             }
  173.  
  174.             this.SkipToNextBlock(reader);
  175.             reader.ReadChar(); //>
  176.             reader.ReadChar(); //>
  177.             return ret;
  178.         }
  179.  
  180.         private COSName ParseName(BinaryReader reader)
  181.         {
  182.             return new COSName(ReadBlock(reader));
  183.         }
  184.  
  185.         private COSArray ParseArray(BinaryReader reader)
  186.         {
  187.             reader.ReadChar(); // '['
  188.             SkipToNextBlock(reader);
  189.  
  190.             if (reader.PeekChar() == ']') return new COSArray(new COSType[0]); //Empty array
  191.             else
  192.             {
  193.                 List<COSType> entries = new List<COSType>();
  194.  
  195.                 while (reader.PeekChar() != ']')
  196.                 {
  197.                     entries.Add(this.ParseNextData(reader));
  198.                     this.SkipToNextBlock(reader);
  199.                 }
  200.                 reader.ReadChar(); // ']'
  201.                 return new COSArray(entries.ToArray());
  202.             }
  203.         }
  204.  
  205.         private COSString ParseString(BinaryReader reader)
  206.         {
  207.             string block = this.ReadBlock(reader);
  208.             block += (char)reader.ReadByte();
  209.             return new COSString(block);
  210.         }
  211.  
  212.         private COSBool ParseBool(BinaryReader reader)
  213.         {
  214.             if (this.ContinueWith(reader, "true"))
  215.             {
  216.                 reader.ReadChar(); //t
  217.                 reader.ReadChar(); //r
  218.                 reader.ReadChar(); //u
  219.                 reader.ReadChar(); //e
  220.                 return new COSBool(true);
  221.             }
  222.             else if (this.ContinueWith(reader, "false"))
  223.             {
  224.                 reader.ReadChar(); //f
  225.                 reader.ReadChar(); //a
  226.                 reader.ReadChar(); //l
  227.                 reader.ReadChar(); //s
  228.                 reader.ReadChar(); //e
  229.                 return new COSBool(false);
  230.             }
  231.             else throw new Exception("Invalid COS bool value");
  232.         }
  233.         #endregion
  234.  
  235.         #region Helpers
  236.         private void SkipComment(BinaryReader reader)
  237.         {
  238.             if (reader.PeekChar() == '%')
  239.             {
  240.                 reader.ReadChar();
  241.                 SkipToNextLine(reader);
  242.             }
  243.         }
  244.  
  245.         private void SkipToNextLine(BinaryReader reader)
  246.         {
  247.             while (reader.BaseStream.Position < reader.BaseStream.Length && reader.PeekChar() != '\r' && reader.PeekChar() != '\n') reader.ReadChar();
  248.  
  249.             if (reader.PeekChar() == '\r' || reader.PeekChar() == '\n')
  250.             {
  251.                 while (reader.BaseStream.Position < reader.BaseStream.Length && (reader.PeekChar() == '\r' || reader.PeekChar() == '\n')) reader.ReadChar();
  252.             }
  253.         }
  254.  
  255.         private void SkipWhiteSpaces(BinaryReader reader)
  256.         {
  257.             while (reader.BaseStream.Position < reader.BaseStream.Length && whiteSpaces.Contains((char)reader.PeekChar())) reader.ReadChar();
  258.  
  259.             if (reader.PeekChar() == '\r' || reader.PeekChar() == '\n')
  260.             {
  261.                 while (reader.BaseStream.Position < reader.BaseStream.Length && (reader.PeekChar() == '\r' || reader.PeekChar() == '\n')) reader.ReadChar();
  262.             }
  263.         }
  264.  
  265.         private void SkipToNextBlock(BinaryReader reader)
  266.         {
  267.             SkipWhiteSpaces(reader);
  268.             while (reader.PeekChar() == '%')
  269.             {
  270.                 SkipToNextLine(reader);
  271.                 SkipWhiteSpaces(reader);
  272.             }
  273.         }
  274.  
  275.         private string ReadBlock(BinaryReader reader)
  276.         {
  277.             this.SkipWhiteSpaces(reader);
  278.  
  279.             string ret = ((char)reader.ReadByte()).ToString();
  280.             while (!blockTerminators.Contains((char)reader.PeekChar())) ret += (char)reader.ReadByte();
  281.  
  282.             return ret;
  283.         }
  284.  
  285.         private bool ContinueWith(BinaryReader reader, string value)
  286.         {
  287.             long startPos = reader.BaseStream.Position;
  288.             if (value.Length > 0 && (char)reader.PeekChar() == value[0] && reader.BaseStream.Length - reader.BaseStream.Position >= value.Length)
  289.             {
  290.                 bool ret = true;
  291.                 for (int i = 0; i < value.Length && ret; i++)
  292.                 {
  293.                     ret &= value[i] == (char)reader.ReadChar();
  294.                 }
  295.  
  296.                 reader.BaseStream.Seek(startPos, SeekOrigin.Begin);
  297.                 return ret;
  298.             }
  299.             else return false;
  300.         }
  301.         #endregion
  302.  
  303.         public string ToString(int ident)
  304.         {
  305.             string prefix = new string('\t', ident);
  306.  
  307.             string ret = $"{prefix}DOC{Environment.NewLine}";
  308.             ret += $"{prefix}{{{Environment.NewLine}";
  309.  
  310.             foreach (COSObj obj in this.objects) ret += obj.ToString(ident + 1) + Environment.NewLine;
  311.  
  312.             ret += $"{prefix}}}{Environment.NewLine}";
  313.             return ret;
  314.         }
  315.     }
  316.  
  317.     interface COSObj { string ToString(int ident); }
  318.  
  319.     class COSObject : COSObj
  320.     {
  321.         int ID;
  322.         int revision;
  323.  
  324.         COSType data;
  325.  
  326.         public COSObject(int ID, int revision, COSType data)
  327.         {
  328.             this.ID = ID;
  329.             this.revision = revision;
  330.             this.data = data;
  331.         }
  332.  
  333.         public string ToString(int ident)
  334.         {
  335.             string prefix = new string('\t', ident);
  336.  
  337.             string ret = $"{prefix}OBJ ID[{ID}] REVISION[{revision}]{Environment.NewLine}";
  338.             ret += $"{prefix}{{{Environment.NewLine}";
  339.  
  340.             ret += data.ToString(ident + 1) + Environment.NewLine;
  341.  
  342.             ret += $"{prefix}}}{Environment.NewLine}";
  343.  
  344.             return ret;
  345.         }
  346.     }
  347.  
  348.     class COSStreamObject : MemoryStream, COSObj
  349.     {
  350.         int ID;
  351.         int revision;
  352.  
  353.         COSDictionary attributes;
  354.  
  355.         public COSStreamObject(int ID, int revision, COSDictionary attributes, byte[] data) : base(data)
  356.         {
  357.             this.ID = ID;
  358.             this.revision = revision;
  359.             this.attributes = attributes;
  360.         }
  361.  
  362.         public string ToString(int ident)
  363.         {
  364.             string prefix = new string('\t', ident);
  365.             string prefix2 = new string('\t', ident + 1);
  366.  
  367.             string ret = $"{prefix}OBJ ID[{ID}] REVISION[{revision}]{Environment.NewLine}";
  368.             ret += $"{prefix}{{{Environment.NewLine}";
  369.  
  370.             ret += $"{prefix2}ATTRIBUTES{Environment.NewLine}";
  371.             ret += $"{prefix2}{{{Environment.NewLine}";
  372.             ret += prefix2 + attributes.ToString(ident + 1, true) + Environment.NewLine;
  373.             ret += $"{prefix2}}}{Environment.NewLine}";
  374.  
  375.             this.Seek(0, SeekOrigin.Begin);
  376.  
  377.             ret += $"{prefix2}BODY [";
  378.             while (this.Position < this.Length) ret += " 0x" + this.ReadByte().ToString("X2");
  379.             ret += $" ]{Environment.NewLine}";
  380.  
  381.             ret += $"{prefix}}}{Environment.NewLine}";
  382.  
  383.             return ret;
  384.         }
  385.     }
  386.  
  387.     interface COSType { string ToString(int ident); }
  388.  
  389.     class COSName : COSType
  390.     {
  391.         public string name;
  392.  
  393.         public COSName(string name)
  394.         {
  395.             if (name[0] != '/' || name.Length < 2) throw new Exception("Invalid COS name, COS names must start with '/' and must be at least 2 char long");
  396.             this.name = name.Substring(1);
  397.         }
  398.  
  399.         public override string ToString()
  400.         {
  401.             return $"/{name}";
  402.         }
  403.  
  404.         public override bool Equals(object obj)
  405.         {
  406.             return obj is COSName && (obj as COSName).name.Equals(this.name);
  407.         }
  408.  
  409.         public override int GetHashCode()
  410.         {
  411.             return this.name.GetHashCode();
  412.         }
  413.  
  414.         public string ToString(int ident)
  415.         {
  416.             string prefix = new string('\t', ident);
  417.  
  418.             return $"{prefix}{this.ToString()}";
  419.         }
  420.     }
  421.  
  422.     class COSDictionary : COSType, IDictionary<COSName, COSType>
  423.     {
  424.         Dictionary<COSName, COSType> dict;
  425.  
  426.         public COSDictionary()
  427.         {
  428.             this.dict = new Dictionary<COSName, COSType>();
  429.         }
  430.  
  431.         public COSType this[COSName key]
  432.         {
  433.             get { return dict[key]; }
  434.             set { dict[key] = value; }
  435.         }
  436.  
  437.         public int Count
  438.         {
  439.             get {  return dict.Count; }
  440.         }
  441.  
  442.         public bool IsReadOnly
  443.         {
  444.             get { return false; }
  445.         }
  446.  
  447.         public ICollection<COSName> Keys
  448.         {
  449.             get { return dict.Keys; }
  450.         }
  451.  
  452.         public ICollection<COSType> Values
  453.         {
  454.             get { return dict.Values; }
  455.         }
  456.  
  457.         public void Add(KeyValuePair<COSName, COSType> item)
  458.         {
  459.             dict.Add(item.Key, item.Value);
  460.         }
  461.  
  462.         public void Add(COSName key, COSType value)
  463.         {
  464.             dict.Add(key, value);
  465.         }
  466.  
  467.         public void Clear()
  468.         {
  469.             dict.Clear();
  470.         }
  471.  
  472.         public bool Contains(KeyValuePair<COSName, COSType> item)
  473.         {
  474.             return dict.Contains(item);
  475.         }
  476.  
  477.         public bool ContainsKey(COSName key)
  478.         {
  479.             return dict.ContainsKey(key);
  480.         }
  481.  
  482.         public void CopyTo(KeyValuePair<COSName, COSType>[] array, int arrayIndex)
  483.         {
  484.             throw new NotImplementedException();
  485.         }
  486.  
  487.         public IEnumerator<KeyValuePair<COSName, COSType>> GetEnumerator()
  488.         {
  489.             return dict.GetEnumerator();
  490.         }
  491.  
  492.         public bool Remove(KeyValuePair<COSName, COSType> item)
  493.         {
  494.             return dict.Remove(item.Key);
  495.         }
  496.  
  497.         public bool Remove(COSName key)
  498.         {
  499.             return dict.Remove(key);
  500.         }
  501.  
  502.         public string ToString(int ident)
  503.         {
  504.             return this.ToString(ident, false);
  505.         }
  506.  
  507.         public string ToString(int ident, bool multiLine)
  508.         {
  509.             string prefix = new string('\t', ident);
  510.  
  511.             string ret = prefix + "{";
  512.             foreach (KeyValuePair<COSName, COSType> item in this) ret += $" {item.Key}: {item.Value}";
  513.             return ret + " }";
  514.         }
  515.  
  516.         public bool TryGetValue(COSName key, out COSType value)
  517.         {
  518.             return dict.TryGetValue(key, out value);
  519.         }
  520.  
  521.         IEnumerator IEnumerable.GetEnumerator()
  522.         {
  523.             return dict.GetEnumerator();
  524.         }
  525.     }
  526.  
  527.     class COSNumber : COSType
  528.     {
  529.         string value;
  530.  
  531.         public COSNumber(string number)
  532.         {
  533.             for (int i = 0; i < number.Length; i++) if (!COSParse.cosNumberFilter.Contains(number[i])) throw new Exception($"Invalid COS number: {number}");
  534.  
  535.             this.value = number;
  536.         }
  537.  
  538.         public override string ToString()
  539.         {
  540.             return value;
  541.         }
  542.  
  543.         public override bool Equals(object obj)
  544.         {
  545.             return obj is COSNumber && (obj as COSNumber).value == this.value;
  546.         }
  547.  
  548.         public override int GetHashCode()
  549.         {
  550.             return this.value.GetHashCode();
  551.         }
  552.  
  553.         public string ToString(int ident)
  554.         {
  555.             string prefix = new string('\t', ident);
  556.             return $"{prefix}{this.value}";
  557.         }
  558.     }
  559.  
  560.     class COSArray : COSType
  561.     {
  562.         COSType[] values;
  563.         public COSArray(COSType[] values)
  564.         {
  565.             if (values.Length > 0)
  566.             {
  567.                 Type t = values[0].GetType();
  568.                 for (int i = 1; i < values.Length; i++)
  569.                 {
  570.                     if (values[i].GetType() != t) throw new Exception("COS array must consist of elements from the same type");
  571.                 }
  572.             }
  573.            
  574.  
  575.             this.values = values;
  576.         }
  577.  
  578.         public override bool Equals(object obj)
  579.         {
  580.             return obj is COSArray && (obj as COSArray).values.Equals(this.values);
  581.         }
  582.  
  583.         public override int GetHashCode()
  584.         {
  585.             return this.values.GetHashCode();
  586.         }
  587.  
  588.         public override string ToString()
  589.         {
  590.             string ret = "[";
  591.             foreach (COSType item in this.values) ret += " " + item.ToString(0);
  592.             ret += " ]";
  593.             return ret;
  594.         }
  595.  
  596.         public string ToString(int ident)
  597.         {
  598.             string prefix = new string('\t', ident);
  599.             return $"{prefix}{this.ToString()}";
  600.         }
  601.     }
  602.  
  603.     class COSString : COSType
  604.     {
  605.         public string value;
  606.         public bool isHex;
  607.  
  608.         public COSString(string value)
  609.         {
  610.             if (value[0] == '(' && value[value.Length-1] == ')')
  611.             {
  612.                 this.value = value.Substring(1, value.Length - 2);
  613.                 this.isHex = false;
  614.             }
  615.             else if (value[0] == '<' && value[value.Length - 1] == '>')
  616.             {
  617.                 value = value.Substring(1, value.Length - 2);
  618.                 this.value = "";
  619.                 for (int i = 0; i < value.Length; i += 2)
  620.                 {
  621.                     this.value += (char)Convert.ToInt32(value.Substring(i, 2), 16);
  622.                     this.isHex = true;
  623.                 }
  624.             }
  625.         }
  626.  
  627.         public string ToString(int ident)
  628.         {
  629.             string prefix = new string('\t', ident);
  630.  
  631.             if (this.isHex)
  632.             {
  633.                 string ret = $"{prefix}<";
  634.                 for (int i = 0; i < this.value.Length; i++) ret += " 0x" + ((int)this.value[i]).ToString("X2");
  635.                 ret += " >";
  636.                 return ret;
  637.             }
  638.             else return $"{prefix}({value})";
  639.         }
  640.     }
  641.  
  642.     class COSReference : COSType
  643.     {
  644.         public int objectID;
  645.         public int objectRevision;
  646.  
  647.         public COSReference(int objectID, int objectRevision)
  648.         {
  649.             this.objectID = objectID;
  650.             this.objectRevision = objectRevision;
  651.         }
  652.  
  653.         public override string ToString()
  654.         {
  655.             return $"{objectID} {objectRevision} R";
  656.         }
  657.  
  658.         public string ToString(int ident)
  659.         {
  660.             string prefix = new string('\t', ident);
  661.             return prefix + this.ToString();
  662.         }
  663.     }
  664.  
  665.     class COSBool : COSType
  666.     {
  667.         bool value;
  668.  
  669.         public COSBool(bool value)
  670.         {
  671.             this.value = value;
  672.         }
  673.  
  674.         public override string ToString()
  675.         {
  676.             return this.value.ToString();
  677.         }
  678.  
  679.         public string ToString(int ident)
  680.         {
  681.             string prefix = new string('\t', ident);
  682.  
  683.             return prefix + this.value.ToString();
  684.         }
  685.     }
  686. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement