using System; using System.Collections; using Paroxe.PdfRenderer.Internal; using System.Text; using UnityEngine; namespace Paroxe.PdfRenderer { public class PDFLibrary : IDisposable { public static readonly Encoding Encoding = new UnicodeEncoding(false, false, false); #if (UNITY_IOS || UNITY_WEBGL) && !UNITY_EDITOR public const string PLUGIN_ASSEMBLY = "__Internal"; #else public const string PLUGIN_ASSEMBLY = "pdfrenderer"; #endif public static object nativeLock = new object(); public enum ErrorCode { ErrSuccess = 0, // No error. ErrUnknown = 1, // Unknown error. ErrFile = 2, // File not found or could not be opened. ErrFormat = 3, // File not in PDF format or corrupted. ErrPassword = 4, // Password required or incorrect password. ErrSecurity = 5, // Unsupported security scheme. ErrPage = 6 // Page not found or content error. } private static bool m_Disposed; private static PDFLibrary m_Instance; private static int m_RefCount; private #if UNITY_WEBGL && !UNITY_EDITOR static #endif bool m_IsInitialized; private static bool m_AlreadyDestroyed; #if UNITY_WEBGL || !UNITY_EDITOR private static bool m_AlreadyInitialized; #endif private PDFLibrary() { #if UNITY_WEBGL && !UNITY_EDITOR if (!m_AlreadyInitialized) { NativeMethods.PDFJS_InitLibrary(); m_AlreadyInitialized = true; } #else #if !UNITY_EDITOR if (!m_AlreadyInitialized) #endif { m_IsInitialized = true; m_AlreadyDestroyed = false; NativeMethods.FPDF_InitLibrary(); #if !UNITY_EDITOR m_AlreadyInitialized = true; #endif } #endif } ~PDFLibrary() { Dispose(false); } /// /// Return the last error code. /// /// public static ErrorCode GetLastError() { #if UNITY_WEBGL && !UNITY_EDITOR return ErrorCode.ErrSuccess; #else Instance.EnsureInitialized(); return (ErrorCode) NativeMethods.FPDF_GetLastError(); #endif } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { lock (nativeLock) { if (!m_Disposed) { if (m_RefCount == 0) { #if PDFRENDERER_DEBUG PrintInstanceMap(); #endif #if !UNITY_WEBGL || UNITY_EDITOR if (!m_AlreadyDestroyed) { m_AlreadyDestroyed = true; #if UNITY_EDITOR NativeMethods.FPDF_DestroyLibrary(); #endif } #endif m_Disposed = true; m_Instance = null; } m_Disposed = true; } } } #if PDFRENDERER_DEBUG private static Dictionary s_InstanceMap = new Dictionary(); private static void PrintInstanceMap() { foreach (string key in s_InstanceMap.Keys) { Debug.Log(key + " Count: " + s_InstanceMap[key]); } } #endif internal static void AddRef(string token) { lock (nativeLock) { #if PDFRENDERER_DEBUG if (s_InstanceMap.ContainsKey(token)) s_InstanceMap[token] = s_InstanceMap[token] + 1; else s_InstanceMap.Add(token, 1); #endif ++m_RefCount; Instance.EnsureInitialized(); } } internal static void RemoveRef(string token) { lock (nativeLock) { --m_RefCount; #if PDFRENDERER_DEBUG s_InstanceMap[token] = s_InstanceMap[token] - 1; #endif if (m_RefCount == 0) { if (m_Disposed) { #if PDFRENDERER_DEBUG PrintInstanceMap(); #endif #if !UNITY_WEBGL || UNITY_EDITOR if (!m_AlreadyDestroyed) { m_AlreadyDestroyed = true; NativeMethods.FPDF_DestroyLibrary(); } #endif m_Instance = null; } else m_Instance.Dispose(); } } } public static PDFLibrary Instance { get { if (m_Instance == null) m_Instance = new PDFLibrary(); return m_Instance; } } public void EnsureInitialized() { } public bool IsInitialized { get { return m_IsInitialized; } set { m_IsInitialized = value; } } public static int RefCount { get { return m_RefCount; } } public void ForceGabageCollection() { if (PDFLibraryMemoryWatcher.I != null) PDFLibraryMemoryWatcher.I.ForceGarbageCollection = true; } } public class PDFLibraryMemoryWatcher : MonoBehaviour { private static PDFLibraryMemoryWatcher s_I; public static PDFLibraryMemoryWatcher I { get { return s_I; } } [RuntimeInitializeOnLoadMethod] private static void EnsureInitialized() { if (s_I == null) { GameObject newObj = new GameObject("PDFLibraryMemoryWatcher"); newObj.hideFlags = HideFlags.HideInHierarchy; DontDestroyOnLoad(newObj); s_I = newObj.AddComponent(); } } public bool ForceGarbageCollection { get; set; } private const int CollectPressure = 16; private const float CollectPressureInterval = 2.0f; private int m_LastCollectRefCount; private IEnumerator Start() { while (true) { if (ForceGarbageCollection || Mathf.Abs(m_LastCollectRefCount - PDFLibrary.RefCount) >= CollectPressure) { m_LastCollectRefCount = PDFLibrary.RefCount; if (ForceGarbageCollection) { ForceGarbageCollection = false; yield return null; } GC.Collect(); float time = Time.unscaledTime; while (Time.unscaledTime - time < CollectPressureInterval) yield return null; } else { yield return null; } } } } }