#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());
}
}
}