#region PDFsharp - A .NET library for processing PDF // // Authors: // Stefan Lange // // Copyright (c) 2005-2017 empira Software GmbH, Cologne Area (Germany) // // http://www.pdfsharp.com // http://sourceforge.net/projects/pdfsharp // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. #endregion using System; using System.Collections.Generic; using System.Diagnostics; using System.Collections; using System.Globalization; using System.IO; using System.Text; namespace PdfSharp.Pdf.Content.Objects // TODO: split into single files { /// /// Base class for all PDF content stream objects. /// public abstract class CObject : ICloneable { /// /// Initializes a new instance of the class. /// protected CObject() { } /// /// Creates a new object that is a copy of the current instance. /// object ICloneable.Clone() { return Copy(); } /// /// Creates a new object that is a copy of the current instance. /// public CObject Clone() { return Copy(); } /// /// Implements the copy mechanism. Must be overridden in derived classes. /// protected virtual CObject Copy() { return (CObject)MemberwiseClone(); } /// /// /// internal abstract void WriteObject(ContentWriter writer); } /// /// Represents a comment in a PDF content stream. /// [DebuggerDisplay("({Text})")] public class CComment : CObject { /// /// Creates a new object that is a copy of the current instance. /// public new CComment Clone() { return (CComment)Copy(); } /// /// Implements the copy mechanism of this class. /// protected override CObject Copy() { CObject obj = base.Copy(); return obj; } /// /// Gets or sets the comment text. /// public string Text { get { return _text; } set { _text = value; } } string _text; /// /// Returns a string that represents the current comment. /// public override string ToString() { return "% " + _text; } internal override void WriteObject(ContentWriter writer) { writer.WriteLineRaw(ToString()); } } /// /// Represents a sequence of objects in a PDF content stream. /// [DebuggerDisplay("(count={Count})")] public class CSequence : CObject, IList // , ICollection, IEnumerable { /// /// Creates a new object that is a copy of the current instance. /// public new CSequence Clone() { return (CSequence)Copy(); } /// /// Implements the copy mechanism of this class. /// protected override CObject Copy() { CObject obj = base.Copy(); _items = new List(_items); for (int idx = 0; idx < _items.Count; idx++) _items[idx] = _items[idx].Clone(); return obj; } /// /// Adds the specified sequence. /// /// The sequence. public void Add(CSequence sequence) { int count = sequence.Count; for (int idx = 0; idx < count; idx++) _items.Add(sequence[idx]); } #region IList Members /// /// Adds the specified value add the end of the sequence. /// public void Add(CObject value) { _items.Add(value); } /// /// Removes all elements from the sequence. /// public void Clear() { _items.Clear(); } //bool IList.Contains(object value) //{ // return items.Contains(value); //} /// /// Determines whether the specified value is in the sequence. /// public bool Contains(CObject value) { return _items.Contains(value); } /// /// Returns the index of the specified value in the sequence or -1, if no such value is in the sequence. /// public int IndexOf(CObject value) { return _items.IndexOf(value); } /// /// Inserts the specified value in the sequence. /// public void Insert(int index, CObject value) { _items.Insert(index, value); } /////// /////// Gets a value indicating whether the sequence has a fixed size. /////// ////public bool IsFixedSize ////{ //// get { return items.IsFixedSize; } ////} /////// /////// Gets a value indicating whether the sequence is read-only. /////// ////public bool IsReadOnly ////{ //// get { return items.IsReadOnly; } ////} /// /// Removes the specified value from the sequence. /// public bool Remove(CObject value) { return _items.Remove(value); } /// /// Removes the value at the specified index from the sequence. /// public void RemoveAt(int index) { _items.RemoveAt(index); } /// /// Gets or sets a CObject at the specified index. /// /// public CObject this[int index] { get { return (CObject)_items[index]; } set { _items[index] = value; } } #endregion #region ICollection Members /// /// Copies the elements of the sequence to the specified array. /// public void CopyTo(CObject[] array, int index) { _items.CopyTo(array, index); } /// /// Gets the number of elements contained in the sequence. /// public int Count { get { return _items.Count; } } ///// ///// Gets a value indicating whether access to the sequence is synchronized (thread safe). ///// //public bool IsSynchronized //{ // get { return items.IsSynchronized; } //} ///// ///// Gets an object that can be used to synchronize access to the sequence. ///// //public object SyncRoot //{ // get { return items.SyncRoot; } //} #endregion #region IEnumerable Members /// /// Returns an enumerator that iterates through the sequence. /// public IEnumerator GetEnumerator() { return _items.GetEnumerator(); } #endregion /// /// Converts the sequence to a PDF content stream. /// public byte[] ToContent() { Stream stream = new MemoryStream(); ContentWriter writer = new ContentWriter(stream); WriteObject(writer); writer.Close(false); stream.Position = 0; int count = (int)stream.Length; byte[] bytes = new byte[count]; stream.Read(bytes, 0, count); #if !UWP stream.Close(); #else stream.Dispose(); #endif return bytes; } /// /// Returns a string containing all elements of the sequence. /// public override string ToString() { StringBuilder s = new StringBuilder(); for (int idx = 0; idx < _items.Count; idx++) s.Append(_items[idx]); return s.ToString(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } internal override void WriteObject(ContentWriter writer) { for (int idx = 0; idx < _items.Count; idx++) _items[idx].WriteObject(writer); } #region IList Members int IList.IndexOf(CObject item) { throw new NotImplementedException(); } void IList.Insert(int index, CObject item) { throw new NotImplementedException(); } void IList.RemoveAt(int index) { throw new NotImplementedException(); } CObject IList.this[int index] { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } #endregion #region ICollection Members void ICollection.Add(CObject item) { throw new NotImplementedException(); } void ICollection.Clear() { throw new NotImplementedException(); } bool ICollection.Contains(CObject item) { throw new NotImplementedException(); } void ICollection.CopyTo(CObject[] array, int arrayIndex) { throw new NotImplementedException(); } int ICollection.Count { get { throw new NotImplementedException(); } } bool ICollection.IsReadOnly { get { throw new NotImplementedException(); } } bool ICollection.Remove(CObject item) { throw new NotImplementedException(); } #endregion #region IEnumerable Members IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); } #endregion List _items = new List(); } /// /// Represents the base class for numerical objects in a PDF content stream. /// public abstract class CNumber : CObject { /// /// Creates a new object that is a copy of the current instance. /// public new CNumber Clone() { return (CNumber)Copy(); } /// /// Implements the copy mechanism of this class. /// protected override CObject Copy() { CObject obj = base.Copy(); return obj; } //internal override void WriteObject(ContentWriter writer) //{ // throw new Exception("Must not come here."); //} } /// /// Represents an integer value in a PDF content stream. /// [DebuggerDisplay("({Value})")] public class CInteger : CNumber { /// /// Creates a new object that is a copy of the current instance. /// public new CInteger Clone() { return (CInteger)Copy(); } /// /// Implements the copy mechanism of this class. /// protected override CObject Copy() { CObject obj = base.Copy(); return obj; } /// /// Gets or sets the value. /// public int Value { get { return _value; } set { _value = value; } } int _value; /// /// Returns a string that represents the current value. /// public override string ToString() { return _value.ToString(CultureInfo.InvariantCulture); } internal override void WriteObject(ContentWriter writer) { writer.WriteRaw(ToString() + " "); } } /// /// Represents a real value in a PDF content stream. /// [DebuggerDisplay("({Value})")] public class CReal : CNumber { /// /// Creates a new object that is a copy of the current instance. /// public new CReal Clone() { return (CReal)Copy(); } /// /// Implements the copy mechanism of this class. /// protected override CObject Copy() { CObject obj = base.Copy(); return obj; } /// /// Gets or sets the value. /// public double Value { get { return _value; } set { _value = value; } } double _value; /// /// Returns a string that represents the current value. /// public override string ToString() { const string format = Config.SignificantFigures1Plus9; return _value.ToString(format, CultureInfo.InvariantCulture); } internal override void WriteObject(ContentWriter writer) { writer.WriteRaw(ToString() + " "); } } /// /// Type of the parsed string. /// public enum CStringType { /// /// The string has the format "(...)". /// String, /// /// The string has the format "<...>". /// HexString, /// /// The string... TODO. /// UnicodeString, /// /// The string... TODO. /// UnicodeHexString, /// /// HACK: The string is the content of a dictionary. /// Currently there is no parser for dictionaries in Content Streams. /// Dictionary, } /// /// Represents a string value in a PDF content stream. /// [DebuggerDisplay("({Value})")] public class CString : CObject { /// /// Creates a new object that is a copy of the current instance. /// public new CString Clone() { return (CString)Copy(); } /// /// Implements the copy mechanism of this class. /// protected override CObject Copy() { CObject obj = base.Copy(); return obj; } /// /// Gets or sets the value. /// public string Value { get { return _value; } set { _value = value; } } string _value; /// /// Gets or sets the type of the content string. /// public CStringType CStringType { get { return _cStringType; } set { _cStringType = value; } } CStringType _cStringType; /// /// Returns a string that represents the current value. /// public override string ToString() { StringBuilder s = new StringBuilder(); switch (CStringType) { case CStringType.String: s.Append("("); int length = _value.Length; for (int ich = 0; ich < length; ich++) { char ch = _value[ich]; switch (ch) { case Chars.LF: s.Append("\\n"); break; case Chars.CR: s.Append("\\r"); break; case Chars.HT: s.Append("\\t"); break; case Chars.BS: s.Append("\\b"); break; case Chars.FF: s.Append("\\f"); break; case Chars.ParenLeft: s.Append("\\("); break; case Chars.ParenRight: s.Append("\\)"); break; case Chars.BackSlash: s.Append("\\\\"); break; default: #if true_ // not absolut necessary to use octal encoding for characters less than blank if (ch < ' ') { s.Append("\\"); s.Append((char)(((ch >> 6) & 7) + '0')); s.Append((char)(((ch >> 3) & 7) + '0')); s.Append((char)((ch & 7) + '0')); } else #endif s.Append(ch); break; } } s.Append(')'); break; case CStringType.HexString: throw new NotImplementedException(); //break; case CStringType.UnicodeString: throw new NotImplementedException(); //break; case CStringType.UnicodeHexString: throw new NotImplementedException(); //break; case CStringType.Dictionary: s.Append(_value); break; default: throw new ArgumentOutOfRangeException(); } return s.ToString(); } internal override void WriteObject(ContentWriter writer) { writer.WriteRaw(ToString()); } } /// /// Represents a name in a PDF content stream. /// [DebuggerDisplay("({Name})")] public class CName : CObject { /// /// Initializes a new instance of the class. /// public CName() { _name = "/"; } /// /// Initializes a new instance of the class. /// /// The name. public CName(string name) { Name = name; } /// /// Creates a new object that is a copy of the current instance. /// public new CName Clone() { return (CName)Copy(); } /// /// Implements the copy mechanism of this class. /// protected override CObject Copy() { CObject obj = base.Copy(); return obj; } /// /// Gets or sets the name. Names must start with a slash. /// public string Name { get { return _name; } set { if (String.IsNullOrEmpty(_name)) throw new ArgumentNullException(nameof(value)); if (_name[0] != '/') throw new ArgumentException(PSSR.NameMustStartWithSlash); _name = value; } } string _name; /// /// Returns a string that represents the current value. /// public override string ToString() { return _name; } internal override void WriteObject(ContentWriter writer) { writer.WriteRaw(ToString() + " "); } } /// /// Represents an array of objects in a PDF content stream. /// [DebuggerDisplay("(count={Count})")] public class CArray : CSequence { /// /// Creates a new object that is a copy of the current instance. /// public new CArray Clone() { return (CArray)Copy(); } /// /// Implements the copy mechanism of this class. /// protected override CObject Copy() { CObject obj = base.Copy(); return obj; } /// /// Returns a string that represents the current value. /// public override string ToString() { return "[" + base.ToString() + "]"; } internal override void WriteObject(ContentWriter writer) { writer.WriteRaw(ToString()); } } /// /// Represents an operator a PDF content stream. /// [DebuggerDisplay("({Name}, operands={Operands.Count})")] public class COperator : CObject { /// /// Initializes a new instance of the class. /// protected COperator() { } internal COperator(OpCode opcode) { _opcode = opcode; } /// /// Creates a new object that is a copy of the current instance. /// public new COperator Clone() { return (COperator)Copy(); } /// /// Implements the copy mechanism of this class. /// protected override CObject Copy() { CObject obj = base.Copy(); return obj; } /// /// Gets or sets the name of the operator /// /// The name. public virtual string Name { get { return _opcode.Name; } } /// /// Gets or sets the operands. /// /// The operands. public CSequence Operands { get { return _seqence ?? (_seqence = new CSequence()); } } CSequence _seqence; /// /// Gets the operator description for this instance. /// public OpCode OpCode { get { return _opcode; } } readonly OpCode _opcode; /// /// Returns a string that represents the current operator. /// public override string ToString() { if (_opcode.OpCodeName == OpCodeName.Dictionary) return " "; return Name; } internal override void WriteObject(ContentWriter writer) { int count = _seqence != null ? _seqence.Count : 0; for (int idx = 0; idx < count; idx++) { // ReSharper disable once PossibleNullReferenceException because the loop is not entered if _sequence is null _seqence[idx].WriteObject(writer); } writer.WriteLineRaw(ToString()); } } }