// ===========================================================================
// ©2013-2026 WebSupergoo. All rights reserved.
//
// This source code is for use exclusively with the ABCpdf product with
// which it is distributed, under the terms of the license for that
// product. Details can be found at
//
// http://www.websupergoo.com/
//
// This copyright notice must not be deleted and must be reproduced alongside
// any sections of code extracted from this module.
// ===========================================================================
using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using SD2 = System.Drawing.Drawing2D;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using WebSupergoo.ABCpdf14;
using WebSupergoo.ABCpdf14.Objects;
using WebSupergoo.ABCpdf14.Atoms;
using WebSupergoo.ABCpdf14.Operations;
namespace WebSupergoo.PDFSurgeon {
public sealed class ExtractorContext {
private static readonly string[] _aActionEntryList = {
"O", "C", "K", "F", "V", "C",
"E", "X", "D", "U", "Fo", "Bl", "PO", "PC", "PV", "PI",
"WC", "WS", "DS", "WP", "DP"
};
private static readonly string[] _aActionSearchList;
static ExtractorContext() {
_aActionSearchList = new string[_aActionEntryList.Length];
for (int i = 0; i < _aActionEntryList.Length; ++i)
_aActionSearchList[i] = ',' + _aActionEntryList[i] + ',';
}
private HashSet<int> _pageContentList;
private Dictionary<int, int> _pageContentIdToPageId;
private Dictionary<int, int> _pageContentIdToPageNumber;
private List<int> _cmapList;
private SortedDictionary<int, string> _xfaDict;
private SortedDictionary<int, ScriptType> _scriptDict;
private SortedDictionary<int, string> _type3GlyphDict;
public ExtractorContext() {
_pageContentList = new HashSet<int>();
_pageContentIdToPageId = new Dictionary<int, int>();
_pageContentIdToPageNumber = new Dictionary<int, int>();
_cmapList = new List<int>();
_xfaDict = new SortedDictionary<int, string>();
_scriptDict = new SortedDictionary<int, ScriptType>();
_type3GlyphDict = new SortedDictionary<int, string>();
}
public HashSet<int> PageContents { get { return _pageContentList; } }
public Dictionary<int, int> PageContentIdToPageId { get { return _pageContentIdToPageId; } }
public Dictionary<int, int> PageContentIdToPageNumber { get { return _pageContentIdToPageNumber; } }
public List<int> CMaps { get { return _cmapList; } }
public SortedDictionary<int, string> Xfa { get { return _xfaDict; } }
public SortedDictionary<int, ScriptType> Scripts { get { return _scriptDict; } }
public SortedDictionary<int, string> Type3Glyphs { get { return _type3GlyphDict; } }
public void AddXfa(int id, string name) {
string oldName;
if (_xfaDict.TryGetValue(id, out oldName)) {
if (oldName == name)
return;
if (name != null) {
if (oldName == "")
return;
name = "";
}
}
_xfaDict[id] = name;
}
public void AddScript(int id, ScriptType type) {
ScriptType oldType;
if (_scriptDict.TryGetValue(id, out oldType))
oldType.Add(type);
else
_scriptDict[id] = type;
}
public void AddAction(Doc doc, int id, ScriptType type) {
int jsId = doc.GetInfoInt(id, "/A*/JS:Ref");
if (jsId != 0 && doc.GetInfo(id, "/A*/S*:Name") == "JavaScript")
AddScript(jsId, type);
}
public void AddAdditionalAction(Doc doc, int id, ScriptType type) {
string keys = doc.GetInfo(id, "/AA*:Keys");
if (keys == "")
return;
keys = ',' + keys + ',';
for (int i = 0; i < _aActionSearchList.Length; ++i) {
if (keys.IndexOf(_aActionSearchList[i]) >= 0) {
string path = "/AA*/" + _aActionEntryList[i];
int jsId = doc.GetInfoInt(id, path + "*/JS:Ref");
if (jsId != 0 && doc.GetInfo(id, path + "*/S*:Name") == "JavaScript")
AddScript(jsId, type);
}
}
}
public void AddType3Glyph(Doc doc, int id) {
string s = doc.GetInfo(id, "/CharProcs*:Keys");
if (s != "") {
string[] keys = s.Split(new char[] { ',' },
StringSplitOptions.RemoveEmptyEntries);
foreach (string name in keys) {
int glyphId = doc.GetInfoInt(id, "/CharProcs*/" + name + ":Ref");
if (glyphId != 0) {
string oldName;
if (_type3GlyphDict.TryGetValue(glyphId, out oldName)) {
if (oldName == name || oldName == "")
continue;
_type3GlyphDict[glyphId] = "";
}
else
_type3GlyphDict[glyphId] = name;
}
}
}
}
}
public class Preview {
public static Image InitialImage { get; set; }
public static Image ErrorImage { get; set; }
public Page Page;
public IndirectObject Target;
public double Scale;
public Image Image;
public GraphicsScanner.Graphics Graphics;
private Preview() { }
public Preview(Page page, IndirectObject target) {
Page = page;
Image = InitialImage;
Target = target;
var mediaBox = page.MediaBox;
double dimension = mediaBox != null ? Math.Max(mediaBox.Width, mediaBox.Height) : 0;
Scale = dimension < 850 ? 1.0 : 850 / dimension;
}
public Preview(Bitmap bm) {
Image = bm != null ? bm : ErrorImage;
Scale = 1.0;
}
public void Reload() {
if (Page == null)
return;
Image = InitialImage;
Graphics = null;
Doc doc = Page.Doc;
doc.Page = Page.ID;
doc.Rect.SetRect(doc.MediaBox);
double top = doc.Rect.Top;
double dpi = 72.0 * Scale;
doc.Rendering.DotsPerInch = dpi;
// We could run parts of this in a separate thread. However we need
// to be careful here as the process of changing the value of the content stream
// might interfere with an ongoing render operation with catastrophic results.
try {
using (var operation = new RenderOperation(doc)) {
Image = operation.GetBitmap();
var scanner = new GraphicsScanner(Target);
var results = scanner.GetResults();
results.Transform((float)top, (float)dpi);
Graphics = results;
}
}
catch {
Image = ErrorImage;
}
}
}
public sealed class ScriptType {
public string DocumentName;
public string PageName;
public string AnnotationName;
public static ScriptType NewDocument(string name) {
ScriptType v = new ScriptType();
v.DocumentName = name;
return v;
}
public static ScriptType NewPage(string name) {
ScriptType v = new ScriptType();
v.PageName = name;
return v;
}
public static ScriptType NewAnnotation(string name) {
ScriptType v = new ScriptType();
v.AnnotationName = name;
return v;
}
public void Add(ScriptType v) {
if (DocumentName != v.DocumentName)
DocumentName = DocumentName == null ? v.DocumentName : "";
if (PageName != v.PageName)
PageName = PageName == null ? v.PageName : "";
if (AnnotationName != v.AnnotationName)
AnnotationName = AnnotationName == null ? v.AnnotationName : "";
}
}
public class Property {
public Property(string key, string value) { K = key; V = value; }
public string K { get; set; }
public string V { get; set; }
public string Summary { get { int n = 0; return K != "ID" && int.TryParse(V, out n) ? $"{K} {V}" : $"{V}"; } }
public string Detail { get { return V.Contains(" ") ? $"{K}: \"{V}\"" : $"{K}: {V}"; } }
}
public class ObjectExtractor {
public static ObjectExtractor FromIndirectObject(IndirectObject obj, ExtractorContext context) {
if (obj == null)
throw new ArgumentNullException("obj", "IndirectObject obj cannot be null.");
if (obj is StreamObject)
return StreamObjectExtractor.FromStreamObject((StreamObject)obj, context);
if (obj is Annotation)
return new AnnotationExtractor((Annotation)obj, context);
if (obj is Page)
return new PageExtractor((Page)obj, context);
if (obj is Pages)
return new PagesExtractor((Pages)obj, context);
if (obj is FontObject)
return new FontObjectExtractor((FontObject)obj, context);
if (obj is Field)
return FieldExtractor.FromField((Field)obj, context);
if (obj is GraphicsState)
return new GraphicsStateExtractor((GraphicsState)obj, context);
if (obj is Bookmark)
return BookmarkExtractor.FromBookmark((Bookmark)obj, context);
if (obj is ColorSpace)
return new ColorSpaceExtractor((ColorSpace)obj, context);
if (obj is Catalog)
return new CatalogExtractor((Catalog)obj, context);
return new ObjectExtractor(obj, context);
}
private IndirectObject _obj;
private ExtractorContext _context;
public ObjectExtractor(IndirectObject obj, ExtractorContext context) {
_obj = obj;
_context = context;
}
public Doc Doc { get { return _obj.Doc; } }
public IndirectObject Object { get { return _obj; } }
public ExtractorContext Context { get { return _context; } }
public virtual bool IsAscii { get { return true; } }
public virtual void Invalidate() { }
public virtual string GetValue(Doc doc) {
if ((doc == null) || (_obj == null))
return "";
return doc.GetInfo(_obj.ID, "value").Replace("\r\n", "\n").Replace("\r", "\n").Replace("\n", "\r\n");
}
public virtual void SetValue(Doc doc, string value) {
if ((doc == null) || (_obj == null))
return;
int id = _obj.ID;
doc.SetInfo(id, "value", value);
_obj = doc.ObjectSoup[id];
Invalidate();
}
public List<Property> GetProperties() {
var props = new List<Property>();
try {
GetProperties(props);
}
catch (Exception ex) {
props.Add(new Property("Error", $"{ex.Message}"));
}
return props;
}
public virtual void GetProperties(List<Property> props) {
props.Add(new Property("ID", $"{_obj.ID}"));
props.Add(new Property("Name", $"{_obj.GetType().Name}"));
}
public virtual Preview GetPreview(bool reload) { return null; }
public virtual string GetAtom() { return Object.Atom.ToString(); }
public virtual string GetContent() { return Object.Atom.ToString(); }
protected static string FormatList(string[] list) {
if (list == null || list.Length <= 0)
return "";
StringBuilder builder = new StringBuilder();
builder.Append(list[0]);
for (int i = 1; i < list.Length; ++i)
builder.Append(", ").Append(list[i]);
return builder.ToString();
}
}
public class CatalogExtractor : ObjectExtractor {
public CatalogExtractor(Catalog obj, ExtractorContext context) : base(obj, context) {
Doc doc = obj.Doc;
int id = obj.ID;
int treeId = doc.GetInfoInt(id, "/Names*/JavaScript:Ref");
if (treeId != 0)
AddScriptInTree(doc, context, treeId);
int count = doc.GetInfoInt(id, "/AcroForm*/XFA*:Count");
if (count > 0) {
for (int i = 0; i < count; i += 2) {
context.AddXfa(doc.GetInfoInt(id,
"/AcroForm*/XFA*[" + (i + 1).ToString() + "]:Ref"),
doc.GetInfo(id, "/AcroForm*/XFA*[" + i.ToString() + "]*:Text"));
}
}
else {
int xfaId = doc.GetInfoInt(id, "/AcroForm*/XFA:Ref");
if (xfaId != 0)
context.AddXfa(xfaId, null);
}
}
private static void AddScriptInTree(Doc doc, ExtractorContext context, int nodeId) {
int count = doc.GetInfoInt(nodeId, "/Kids*:Count");
if (count > 0) {
for (int i = 0; i < count; ++i) {
int childNodeId = doc.GetInfoInt(nodeId, "/Kids*[" + i.ToString() + "]:Ref");
if (childNodeId != 0)
AddScriptInTree(doc, context, childNodeId); // recur on childNodeID
}
return;
}
count = doc.GetInfoInt(nodeId, "/Names*:Count");
for (int i = 0; i < count; i += 2) {
string path = "/Names*[" + (i + 1).ToString();
int jsId = doc.GetInfoInt(nodeId, path + "]*/JS:Ref");
if (jsId != 0 && doc.GetInfo(nodeId, path + "]*/S*:Name") == "JavaScript") {
context.AddScript(jsId, ScriptType.NewDocument(
doc.GetInfo(nodeId, "/Names*[" + i.ToString() + "]*:Text")));
}
}
}
public new Catalog Object { get { return (Catalog)base.Object; } }
public override void GetProperties(List<Property> props) {
base.GetProperties(props);
Catalog obj = Object;
props.Add(new Property("Revisions", $"{obj.Soup.Revisions}"));
}
}
public class ColorSpaceExtractor : ObjectExtractor {
public ColorSpaceExtractor(ColorSpace obj, ExtractorContext context) : base(obj, context) { }
public new ColorSpace Object { get { return (ColorSpace)base.Object; } }
public override void GetProperties(List<Property> props) {
base.GetProperties(props);
ColorSpace obj = Object;
props.Add(new Property("Name", $"{obj.Name}"));
props.Add(new Property("ColorSpace", $"{ obj.ColorSpaceType}"));
props.Add(new Property("Components", $"{ obj.Components}"));
}
}
public class BookmarkExtractor : ObjectExtractor {
public static BookmarkExtractor FromBookmark(Bookmark obj, ExtractorContext context) {
if (obj is Outline)
return new OutlineExtractor((Outline)obj, context);
return new BookmarkExtractor(obj, context);
}
public BookmarkExtractor(Bookmark obj, ExtractorContext context) : base(obj, context) { }
public new Bookmark Object { get { return (Bookmark)base.Object; } }
public override void GetProperties(List<Property> props) {
base.GetProperties(props);
Bookmark obj = Object;
props.Add(new Property("Title", $"{obj.Title}"));
}
}
public class OutlineExtractor : BookmarkExtractor {
public OutlineExtractor(Outline obj, ExtractorContext context) : base(obj, context) { }
public new Outline Object { get { return (Outline)base.Object; } }
}
public class GraphicsStateExtractor : ObjectExtractor {
public GraphicsStateExtractor(GraphicsState obj, ExtractorContext context) : base(obj, context) { }
public new GraphicsState Object { get { return (GraphicsState)base.Object; } }
}
public class FieldExtractor : ObjectExtractor {
public static FieldExtractor FromField(Field obj, ExtractorContext context) {
if (obj is Signature)
return new SignatureExtractor((Signature)obj, context);
return new FieldExtractor(obj, context);
}
public bool IsCombinedWithWidget = false;
public FieldExtractor(Field obj, ExtractorContext context) : base(obj, context) { }
public new Field Object { get { return (Field)base.Object; } }
public override void GetProperties(List<Property> props) {
if (!IsCombinedWithWidget)
base.GetProperties(props);
Field obj = Object;
props.Add(new Property("Name", $"{obj.Name}"));
props.Add(new Property("Value", $"{obj.Value}"));
props.Add(new Property("Type", $"{obj.FieldType}"));
if (!string.IsNullOrWhiteSpace(obj.Format))
props.Add(new Property("Format", $"{obj.Format}"));
}
}
public class SignatureExtractor : FieldExtractor {
public SignatureExtractor(Signature obj, ExtractorContext context) : base(obj, context) { Object.Validate(); }
public new Signature Object { get { return (Signature)base.Object; } }
public override void GetProperties(List<Property> props) {
base.GetProperties(props);
Signature obj = Object;
props.Add(new Property("Revision", $"{obj.SigningRevision} of {obj.Soup.Revisions}"));
props.Add(new Property("IsModified", $"{obj.IsModified}"));
props.Add(new Property("IsTimeValid", $"{obj.IsTimeValid}"));
props.Add(new Property("IsSecure", $"{obj.IsSecure}"));
props.Add(new Property("IsTrusted", $"{obj.IsTrusted}"));
if (obj.Signer != null)
props.Add(new Property("Signer", $"{obj.Signer}"));
if (obj.Reason != null)
props.Add(new Property("Reason", $"{obj.Reason}"));
if (obj.Location != null)
props.Add(new Property("Location", $"{obj.Location}"));
}
}
public class FontObjectExtractor : ObjectExtractor {
public FontObjectExtractor(FontObject obj, ExtractorContext context)
: base(obj, context) {
Doc doc = obj.Doc;
int id = obj.ID;
int cmapId = doc.GetInfoInt(id, "/ToUnicode:Ref");
if (cmapId != 0)
context.CMaps.Add(cmapId);
context.AddType3Glyph(doc, id);
}
public new FontObject Object { get { return (FontObject)base.Object; } }
}
public class AnnotationExtractor : ObjectExtractor {
public FieldExtractor _fieldExtractor = null;
public FormXObjectExtractor _formxExtractor = null;
public AnnotationExtractor(Annotation obj, ExtractorContext context) : base(obj, context) {
Doc doc = obj.Doc;
int id = obj.ID;
ScriptType scryptType = ScriptType.NewAnnotation(id.ToString());
context.AddAction(doc, id, scryptType);
context.AddAdditionalAction(doc, id, scryptType);
var field = obj.Doc.Form[obj.FullName];
if (field != null) {
_fieldExtractor = FieldExtractor.FromField(field, context);
_fieldExtractor.IsCombinedWithWidget = true;
}
}
public new Annotation Object { get { return (Annotation)base.Object; } }
public override void GetProperties(List<Property> props) {
base.GetProperties(props);
Annotation obj = Object;
props.Add(new Property("SubType", $"{obj.SubType}"));
if (_fieldExtractor != null)
_fieldExtractor.GetProperties(props);
}
public override Preview GetPreview(bool reload) {
Annotation obj = Object;
if (_formxExtractor == null) {
var ap = obj.NormalAppearance;
if (ap == null) {
obj.UpdateAppearance();
ap = obj.NormalAppearance;
}
if (ap != null)
_formxExtractor = new FormXObjectExtractor(ap, Context);
}
if (_formxExtractor != null)
return _formxExtractor.GetPreview(reload);
return null;
}
}
public class PageExtractor : ObjectExtractor {
private string _atom;
private int _pageNum;
private Preview _image;
public PageExtractor(Page obj, ExtractorContext context) : base(obj, context) {
_atom = obj.Atom.ToString();
Doc doc = obj.Doc;
int id = obj.ID;
doc.Page = id;
if (doc.Page == id)
_pageNum = doc.PageNumber;
context.AddAdditionalAction(doc, id, ScriptType.NewPage(id.ToString()));
foreach (StreamObject so in obj.GetLayers()) {
context.PageContents.Add(so.ID);
context.PageContentIdToPageId[so.ID] = id;
context.PageContentIdToPageNumber[so.ID] = _pageNum;
}
}
public new Page Object { get { return (Page)base.Object; } }
public override void GetProperties(List<Property> props) {
base.GetProperties(props);
Page obj = Object;
if (_pageNum != 0)
props.Add(new Property("Page", $"{_pageNum}"));
}
public override Preview GetPreview(bool reload) {
if (_image == null || reload) {
_image = new Preview(Object, Object);
_image.Reload();
}
return _image;
}
public override string GetAtom() { return _atom; }
}
public class PagesExtractor : ObjectExtractor {
public PagesExtractor(Pages obj, ExtractorContext context) : base(obj, context) { }
public new Pages Object { get { return (Pages)base.Object; } }
public override void GetProperties(List<Property> props) {
base.GetProperties(props);
Pages obj = Object;
props.Add(new Property("Count", $"{obj.Count}"));
}
}
public class StreamObjectExtractor : ObjectExtractor {
public static StreamObjectExtractor FromStreamObject(StreamObject obj, ExtractorContext context) {
if (obj is PixMap)
return new PixMapExtractor((PixMap)obj, context);
if (obj is Layer)
return LayerExtractor.FromLayer((Layer)obj, context);
if (obj is IccProfile)
return new IccProfileExtractor((IccProfile)obj, context);
if (obj is FormXObject)
return new FormXObjectExtractor((FormXObject)obj, context);
return new StreamObjectExtractor(obj, context);
}
private string _atom;
public StreamObjectExtractor(StreamObject obj, ExtractorContext context) : base(obj, context) { }
public new StreamObject Object { get { return (StreamObject)base.Object; } }
public override bool IsAscii {
get {
StreamObject so = Object;
if (so == null) return true;
byte[] data = so.GetData();
for (int i = 0; i < data.Length; i++) {
if ((data[i] >= 32) && (data[i] < 128))
continue;
if ((data[i] != '\r') && (data[i] != '\n') && (data[i] != '\t'))
return false;
}
return base.IsAscii;
}
}
public override string GetValue(Doc doc) {
if (!ContentPageNumber.HasValue)
return base.GetValue(doc);
StreamObject so = Object;
if ((doc == null) || (so == null))
return "";
return doc.GetInfo(so.ID, "value");
}
public string StreamType { get; set; }
public int? ContentPageNumber { get; set; }
public override void Invalidate() {
_atom = null;
StreamType = null;
base.Invalidate();
}
public override void GetProperties(List<Property> props) {
base.GetProperties(props);
UpdateStreamType();
StreamObject obj = Object;
props.Add(new Property("StreamType", $"{StreamType}"));
props.Add(new Property("Compression", $"{obj.Compression}"));
props.Add(new Property("Length", $"{obj.Length}"));
}
private void UpdateStreamType() {
if (StreamType != null)
return;
if (ContentPageNumber != null) {
StreamType = $"Page {ContentPageNumber} Content";
return;
}
StreamObject obj = Object;
Doc doc = obj.Doc;
int id = obj.ID;
string type = doc.GetInfo(id, "/Type*:Name");
if (type == "Metadata")
StreamType = "Metadata";
else if (doc.GetInfo(id, "/PatternType") != "")
StreamType = "Pattern";
else if (doc.GetInfoInt(id, "/FunctionType*:Num") == 4)
StreamType = "Function/Type-4";
else
StreamType = "";
}
private bool ContainsText() {
StreamObject obj = Object;
CompressionType[] comps = obj.Compressions;
CompressionType comp = comps.Length > 0 ? comps[0] : CompressionType.None;
return (comp == CompressionType.AsciiHex) || (comp == CompressionType.Ascii85);
}
public override string GetAtom() {
if (_atom == null)
_atom = base.GetAtom();
return _atom;
}
public override string GetContent() {
UpdateStreamType();
if (StreamType != "") {
if (_atom == null)
_atom = base.GetAtom();
StreamObject obj = Object;
int len = obj.CopyDecompressedData();
byte[] data = new byte[len];
obj.CopyDecompressedData(data);
return Encoding.ASCII.GetString(data);
}
if (ContainsText()) {
return Object.GetText();
}
return base.GetContent();
}
public string GetSnippet() {
const int max = 250;
string txt = Object.GetText();
if (txt.Length > max)
txt = txt.Substring(0, max);
txt = Regex.Escape(txt);
txt = txt.Replace("\u0000", @"\0");
if (txt.Length > max)
txt = txt.Substring(0, max - 3) + "...";
return txt;
}
}
public class IccProfileExtractor : StreamObjectExtractor {
public IccProfileExtractor(IccProfile obj, ExtractorContext context) : base(obj, context) { }
public new IccProfile Object { get { return (IccProfile)base.Object; } }
}
public class PixMapExtractor : StreamObjectExtractor {
private Preview _image;
public PixMapExtractor(PixMap obj, ExtractorContext context) : base(obj, context) { }
public new PixMap Object { get { return (PixMap)base.Object; } }
public override void GetProperties(List<Property> props) {
base.GetProperties(props);
PixMap obj = Object;
int n = obj.Components;
int bpc = obj.BitsPerComponent;
props.Add(new Property("PixMap", $"{obj.Width}\u00d7{obj.Height} {obj.ColorSpaceType} {n} col {bpc} bit"));
}
public override Preview GetPreview(bool reload) {
if (_image == null || reload)
if (Object.Width > 0 && Object.Height > 0)
_image = new Preview(Object.GetBitmap());
return _image;
}
}
public class LayerExtractor : StreamObjectExtractor {
public static LayerExtractor FromLayer(Layer obj, ExtractorContext context) {
return new LayerExtractor(obj, context);
}
public LayerExtractor(Layer obj, ExtractorContext context) : base(obj, context) { }
public new Layer Object { get { return (Layer)base.Object; } }
public override void GetProperties(List<Property> props) {
base.GetProperties(props);
Layer obj = Object;
props.Add(new Property("Rect", $"{obj.Rect}"));
}
}
public class FormXObjectExtractor : StreamObjectExtractor {
private Preview _image;
public FormXObjectExtractor(FormXObject obj, ExtractorContext context) : base(obj, context) { }
public new FormXObject Object { get { return (FormXObject)base.Object; } }
public override Preview GetPreview(bool reload) {
if (_image == null || reload) {
var pair = MakePage();
_image = new Preview(pair.Item1, pair.Item2);
_image.Reload();
}
return _image;
}
private Tuple<Page, FormXObject> MakePage() {
FormXObject me = Object;
ObjectSoupSubset subset = new ObjectSoupSubset(me.Soup);
subset.AddFamily(me);
Doc doc = new Doc();
doc.MediaBox.String = me.BBox.String;
doc.Rect.String = doc.MediaBox.String;
doc.Page = doc.AddPage();
subset.CopyTo(doc.ObjectSoup);
foreach (var io in doc.ObjectSoup)
if (io != null)
io.Transcode();
int id = subset.RemapIDs[me.ID];
FormXObject form = doc.ObjectSoup[id] as FormXObject;
form.Matrix = null;
doc.AddXObject(form);
Page page = (Page)doc.ObjectSoup[doc.Page];
return new Tuple<Page, FormXObject>(page, form);
}
}
internal sealed class TemporaryFile : IDisposable {
private string mPath;
private TemporaryFile() { }
public TemporaryFile(string ext) {
mPath = GetTempFilePath(ext);
}
~TemporaryFile() {
Dispose(false);
}
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
// sealed class cannot introduce virtual method
// warned if sealed class introduces protected member
private /*virtual*/ void Dispose(bool disposing) {
try {
DeleteFile();
}
catch {
}
}
public string Path { get { return mPath; } }
public void DeleteFile() {
if (mPath != null) {
if (File.Exists(mPath))
File.Delete(mPath);
mPath = null;
}
}
private static string GetTempFilePath(string ext) {
if (string.IsNullOrWhiteSpace(ext))
ext = ".dat";
else if (!ext.StartsWith("."))
ext = "." + ext;
return System.IO.Path.Combine(System.IO.Path.GetTempPath(), Guid.NewGuid().ToString() + ext);
}
}
internal class Navigator {
private List<int> _ids = new List<int>();
private int _pos = -1;
public void Add(int id) {
if (_pos >= 0 && _pos < _ids.Count && _ids[_pos] == id)
return;
while (_pos >= 0 && _pos < _ids.Count - 1)
_ids.RemoveAt(_ids.Count - 1);
_ids.Add(id);
_pos = _ids.Count - 1;
}
public bool CanNavigate() {
return _ids.Count > 0;
}
public int Forward() {
_pos = Math.Min(_pos + 1, _ids.Count - 1);
return _ids[_pos];
}
public int Back() {
_pos = Math.Max(_pos - 1, 0);
return _ids[_pos];
}
}
}