using Paroxe.PdfRenderer.WebGL;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;
using Paroxe.PdfRenderer.Internal;
using System.IO;
namespace Paroxe.PdfRenderer
{
///
/// Represents a PDF document. This class is the entry point of all functionalities.
///
public class PDFDocument : IPDFDocumentInternal, IDisposable
{
private bool m_Disposed;
private bool m_Disposing;
private IntPtr m_NativePointer;
private GCHandle m_BufferHandle;
private byte[] m_DocumentBuffer;
private bool m_ValidDocument;
private PDFRenderer m_Renderer;
private HashSet m_LoadedPages;
public static PDFJS_Promise LoadDocumentFromUrlAsync(string url)
{
PDFJS_Promise documentPromise = new PDFJS_Promise();
#if !UNITY_WEBGL || UNITY_EDITOR
PDFJS_Library.Instance.PreparePromiseCoroutine(LoadDocumentFromWWWCoroutine, documentPromise, url).Start();
#else
LoadDocumentParameters parameters = new LoadDocumentParameters();
parameters.url = url;
PDFJS_Library.Instance.PreparePromiseCoroutine(LoadDocumentCoroutine, documentPromise, parameters).Start();
#endif
return documentPromise;
}
#if !UNITY_WEBGL || UNITY_EDITOR
private static IEnumerator LoadDocumentFromWWWCoroutine(PDFJS_PromiseCoroutine promiseCoroutine, IPDFJS_Promise promise, object urlString)
{
PDFJS_Promise documentPromise = promise as PDFJS_Promise;
PDFLibrary.Instance.EnsureInitialized();
while (!PDFLibrary.Instance.IsInitialized)
yield return null;
string url = urlString as string;
PDFWebRequest www = new PDFWebRequest(url);
www.SendWebRequest();
yield return www;
if (string.IsNullOrEmpty(www.error))
{
documentPromise.HasFinished = true;
documentPromise.HasSucceeded = true;
documentPromise.HasReceivedJSResponse = true;
documentPromise.Result = new PDFDocument(www.bytes);
promiseCoroutine.ExecuteThenAction(true, documentPromise.Result);
}
else
{
documentPromise.HasFinished = true;
documentPromise.HasSucceeded = false;
promiseCoroutine.ExecuteThenAction(false, null);
}
www.Dispose();
www = null;
}
#endif
public static PDFJS_Promise LoadDocumentFromBytesAsync(byte[] bytes)
{
PDFJS_Promise documentPromise = new PDFJS_Promise();
#if !UNITY_WEBGL || UNITY_EDITOR
documentPromise.HasFinished = true;
documentPromise.HasSucceeded = true;
documentPromise.HasReceivedJSResponse = true;
documentPromise.Result = new PDFDocument(bytes);
#else
LoadDocumentParameters parameters = new LoadDocumentParameters();
parameters.bytes = bytes;
PDFJS_Library.Instance.PreparePromiseCoroutine(LoadDocumentCoroutine, documentPromise, parameters).Start();
#endif
return documentPromise;
}
public PDFDocument(IntPtr nativePointer)
{
PDFLibrary.AddRef("PDFDocument");
m_NativePointer = nativePointer;
m_ValidDocument = true;
}
#if !UNITY_WEBGL || UNITY_EDITOR
///
/// Open PDF Document with the specified byte array.
///
///
public PDFDocument(byte[] buffer)
: this(buffer, "")
{ }
///
/// Open PDF Document with the specified byte array.
///
///
/// Can be null or empty
public PDFDocument(byte[] buffer, string password)
{
PDFLibrary.AddRef("PDFDocument");
CommonInit(buffer, password);
}
///
/// Open PDF Document located at the specified path
///
///
public PDFDocument(string filePath)
: this(filePath, "")
{ }
///
///
///
///
/// Can be null or empty
public PDFDocument(string filePath, string password)
{
PDFLibrary.AddRef("PDFDocument");
CommonInit(File.ReadAllBytes(filePath), password);
}
#endif
~PDFDocument()
{
Dispose(false);
}
protected virtual void Dispose(bool _)
{
if (!m_Disposed)
{
m_Disposing = true;
lock (PDFLibrary.nativeLock)
{
if (m_NativePointer != IntPtr.Zero)
{
if (m_LoadedPages != null)
{
foreach (PDFPage page in m_LoadedPages)
page.Dispose();
m_LoadedPages = null;
}
#if UNITY_WEBGL && !UNITY_EDITOR
NativeMethods.PDFJS_CloseDocument(m_NativePointer.ToInt32());
#else
NativeMethods.FPDF_CloseDocument(m_NativePointer);
#endif
if (m_DocumentBuffer != null)
m_BufferHandle.Free();
m_NativePointer = IntPtr.Zero;
}
}
PDFLibrary.RemoveRef("PDFDocument");
m_Disposing = false;
m_Disposed = true;
}
}
void IPDFDocumentInternal.OnPageClose(PDFPage page)
{
if (m_LoadedPages != null && !m_Disposing)
m_LoadedPages.Remove(page);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
///
/// Return a convenience PDFRenderer instance.
///
public PDFRenderer Renderer
{
get
{
if (m_Renderer == null)
m_Renderer = new PDFRenderer();
return m_Renderer;
}
}
///
/// The byte array of the document.
///
public byte[] DocumentBuffer
{
get { return m_DocumentBuffer; }
}
///
/// Return if the document is valid. The document can be invalid if the password is invalid or if the
/// document itseft is corrupted. See PDFLibrary.GetLastError.
///
public bool IsValid
{
get { return m_ValidDocument; }
}
public IntPtr NativePointer
{
get { return m_NativePointer; }
}
public int GetPageCount()
{
#if UNITY_WEBGL && !UNITY_EDITOR
return NativeMethods.PDFJS_GetPageCount(m_NativePointer.ToInt32());
#else
return NativeMethods.FPDF_GetPageCount(m_NativePointer);
#endif
}
#if !UNITY_WEBGL || UNITY_EDITOR
public Vector2 GetPageSize(int pageIndex)
{
double width;
double height;
NativeMethods.FPDF_GetPageSizeByIndex(m_NativePointer, pageIndex, out width, out height);
return new Vector2((float)width, (float)height);
}
#endif
#if !UNITY_WEBGL || UNITY_EDITOR
public int GetPageWidth(int pageIndex)
{
double width;
double height;
NativeMethods.FPDF_GetPageSizeByIndex(m_NativePointer, pageIndex, out width, out height);
return (int)width;
}
#endif
#if !UNITY_WEBGL || UNITY_EDITOR
public int GetPageHeight(int pageIndex)
{
double width;
double height;
NativeMethods.FPDF_GetPageSizeByIndex(m_NativePointer, pageIndex, out width, out height);
return (int)height;
}
#endif
#if !UNITY_WEBGL
///
/// Return the root bookmark of the document.
///
///
public PDFBookmark GetRootBookmark()
{
return GetRootBookmark(null);
}
#endif
#if !UNITY_WEBGL
///
/// Return the root bookmark of the document.
///
/// Pass the device that will receive bookmarks action
///
public PDFBookmark GetRootBookmark(IPDFDevice device)
{
return new PDFBookmark(this, null, IntPtr.Zero, device);
}
#endif
#if !UNITY_WEBGL || UNITY_EDITOR
public PDFPage GetPage(int index)
{
PDFPage page = new PDFPage(this, index);
if (m_LoadedPages == null)
m_LoadedPages = new HashSet();
m_LoadedPages.Add(page);
return page;
}
#endif
public PDFJS_Promise GetPageAsync(int index)
{
return PDFPage.LoadPageAsync(this, index);
}
private void CommonInit(byte[] buffer, string password)
{
m_DocumentBuffer = buffer;
if (m_DocumentBuffer != null)
{
#if !UNITY_WEBGL || UNITY_EDITOR
m_BufferHandle = GCHandle.Alloc(m_DocumentBuffer, GCHandleType.Pinned);
m_NativePointer = NativeMethods.FPDF_LoadMemDocument(m_BufferHandle.AddrOfPinnedObject(), m_DocumentBuffer.Length, password);
#endif
m_ValidDocument = (m_NativePointer != IntPtr.Zero);
}
else
{
m_ValidDocument = false;
}
}
#if UNITY_WEBGL && !UNITY_EDITOR
class LoadDocumentParameters
{
public string url;
public byte[] bytes;
}
private static IEnumerator LoadDocumentCoroutine(PDFJS_PromiseCoroutine promiseCoroutine, IPDFJS_Promise promise, object pars)
{
PDFJS_Promise documentPromise = promise as PDFJS_Promise;
PDFLibrary.Instance.EnsureInitialized();
while (!PDFLibrary.Instance.IsInitialized)
yield return null;
LoadDocumentParameters parameters = pars as LoadDocumentParameters;
if (!string.IsNullOrEmpty(parameters.url))
NativeMethods.PDFJS_LoadDocumentFromURL(promise.PromiseHandle, parameters.url);
else
NativeMethods.PDFJS_LoadDocumentFromBytes(promise.PromiseHandle, Convert.ToBase64String(parameters.bytes));
while (!promiseCoroutine.Promise.HasReceivedJSResponse)
yield return null;
if (documentPromise.HasSucceeded)
{
int documentHandle = int.Parse(promiseCoroutine.Promise.JSObjectHandle);
PDFDocument document = new PDFDocument(new IntPtr(documentHandle));
documentPromise.Result = document;
documentPromise.HasFinished = true;
promiseCoroutine.ExecuteThenAction(true, documentHandle);
}
else
{
documentPromise.Result = null;
documentPromise.HasFinished = true;
promiseCoroutine.ExecuteThenAction(false, null);
}
}
#endif
}
public interface IPDFDocumentInternal
{
void OnPageClose(PDFPage page);
}
}