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