Permalink
Fetching contributors…
Cannot retrieve contributors at this time
363 lines (327 sloc) 11.7 KB
/* ------------------------------------------------------------------------- */
//
// Copyright (c) 2010 CubeSoft, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
/* ------------------------------------------------------------------------- */
using Cube.FileSystem;
using Cube.Pdf.Mixin;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace Cube.Pdf.Pdfium
{
/* --------------------------------------------------------------------- */
///
/// PdfiumReader
///
/// <summary>
/// PDFium の API をラップした Reader クラスです。
/// </summary>
///
/* --------------------------------------------------------------------- */
internal sealed class PdfiumReader : PdfiumLibrary, IDisposable
{
#region Constructors
/* ----------------------------------------------------------------- */
///
/// PdfiumReader
///
/// <summary>
/// オブジェクトを初期化します。
/// </summary>
///
/// <param name="src">入力ファイルのパス</param>
/// <param name="io">I/O オブジェクト</param>
///
/* ----------------------------------------------------------------- */
private PdfiumReader(string src, IO io)
{
Source = src;
IO = io;
_stream = IO.OpenRead(src);
_delegate = new ReadDelegate(Read);
}
#endregion
#region Properties
/* ----------------------------------------------------------------- */
///
/// Source
///
/// <summary>
/// 対象となる PDF ファイルのパスを取得します。
/// </summary>
///
/* ----------------------------------------------------------------- */
public string Source { get; }
/* ----------------------------------------------------------------- */
///
/// IO
///
/// <summary>
/// I/O オブジェクトを取得します。
/// </summary>
///
/* ----------------------------------------------------------------- */
public IO IO { get; }
/* ----------------------------------------------------------------- */
///
/// File
///
/// <summary>
/// ファイル情報を取得します。
/// </summary>
///
/* ----------------------------------------------------------------- */
public PdfFile File { get; private set; }
/* ----------------------------------------------------------------- */
///
/// Metadata
///
/// <summary>
/// メタ情報を取得します。
/// </summary>
///
/* ----------------------------------------------------------------- */
public Metadata Metadata { get; private set; }
/* ----------------------------------------------------------------- */
///
/// Encryption
///
/// <summary>
/// 暗号化情報を取得します。
/// </summary>
///
/* ----------------------------------------------------------------- */
public Encryption Encryption { get; private set; }
/* ----------------------------------------------------------------- */
///
/// Pages
///
/// <summary>
/// ページ一覧を取得します。
/// </summary>
///
/* ----------------------------------------------------------------- */
public IEnumerable<Page> Pages { get; private set; }
#endregion
#region Methods
/* ----------------------------------------------------------------- */
///
/// Create
///
/// <summary>
/// Creates a new instance of the PdfiumReader class with the
/// specified arguments.
/// </summary>
///
/// <param name="src">Path of the PDF file.</param>
/// <param name="query">Password query.</param>
/// <param name="fullaccess">Requires full access.</param>
/// <param name="io">I/O handler.</param>
///
/// <returns>PdfiumReader object.</returns>
///
/* ----------------------------------------------------------------- */
public static PdfiumReader Create(string src, IQuery<string> query, bool fullaccess, IO io)
{
var dest = new PdfiumReader(src, io);
var pass = (query as QueryValue<string>)?.Value ?? string.Empty;
while (true)
{
try
{
dest.Load(pass);
var denied = fullaccess && dest.File is PdfFile pf && !pf.FullAccess;
if (denied) throw new LoadException(LoadStatus.PasswordError);
return dest;
}
catch (LoadException e)
{
if (e.Status != LoadStatus.PasswordError) throw;
if (query is QueryValue<string>) throw new EncryptionException(e.Message, e);
var args = query.RequestPassword(src);
if (!args.Cancel) pass = args.Result;
else throw new OperationCanceledException();
}
}
}
/* ----------------------------------------------------------------- */
///
/// Invoke
///
/// <summary>
/// Invokes the specified action.
/// </summary>
///
/// <param name="action">Action object.</param>
///
/* ----------------------------------------------------------------- */
public void Invoke(Action<IntPtr> action)
{
if (_disposed) throw new ObjectDisposedException(GetType().Name);
lock (_lock) action(_core);
}
/* ----------------------------------------------------------------- */
///
/// Invoke
///
/// <summary>
/// Invokes the specified function.
/// </summary>
///
/// <param name="func">Function object.</param>
///
/// <returns>Return value for the specified object.</returns>
///
/* ----------------------------------------------------------------- */
public T Invoke<T>(Func<IntPtr, T> func)
{
if (_disposed) throw new ObjectDisposedException(GetType().Name);
lock (_lock) return func(_core);
}
#region IDisposable
/* ----------------------------------------------------------------- */
///
/// Dispose
///
/// <summary>
/// Releases all resources used by the PdfiumReader.
/// </summary>
///
/* ----------------------------------------------------------------- */
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/* ----------------------------------------------------------------- */
///
/// Dispose
///
/// <summary>
/// Releases the unmanaged resources used by the PdfiumReader
/// and optionally releases the managed resources.
/// </summary>
///
/// <param name="disposing">
/// true to release both managed and unmanaged resources;
/// false to release only unmanaged resources.
/// </param>
///
/* ----------------------------------------------------------------- */
private void Dispose(bool disposing)
{
if (_disposed) return;
_disposed = true;
lock (_lock)
{
if (_core != IntPtr.Zero)
{
PdfiumApi.FPDF_CloseDocument(_core);
_core = IntPtr.Zero;
}
}
if (disposing) _stream.Dispose();
}
#endregion
#endregion
#region Implementations
/* ----------------------------------------------------------------- */
///
/// Load
///
/// <summary>
/// PDF を読み込みます。
/// </summary>
///
/// <param name="password">パスワード</param>
///
/* ----------------------------------------------------------------- */
private void Load(string password)
{
var core = PdfiumApi.FPDF_LoadCustomDocument(
new FileAccess
{
Length = (uint)_stream.Length,
GetBlock = Marshal.GetFunctionPointerForDelegate(_delegate),
Parameter = IntPtr.Zero,
},
password
);
if (core == IntPtr.Zero) throw GetLastError();
_core = core;
Encryption = EncryptionFactory.Create(this, password);
File = Create(password, !Encryption.OpenWithPassword);
Pages = new ReadOnlyPageList(this, File);
Metadata = MetadataFactory.Create(this);
}
/* ----------------------------------------------------------------- */
///
/// Create
///
/// <summary>
/// Creates a PdfFile object from the specified arguments.
/// </summary>
///
/* ----------------------------------------------------------------- */
private PdfFile Create(string password, bool fullaccess)
{
var dest = IO.GetPdfFile(Source, password);
dest.Count = Invoke(e => PdfiumApi.FPDF_GetPageCount(e));
dest.FullAccess = fullaccess;
return dest;
}
/* ----------------------------------------------------------------- */
///
/// Read
///
/// <summary>
/// ストリームからデータを読み込みます。
/// </summary>
///
/* ----------------------------------------------------------------- */
private int Read(IntPtr param, uint pos, IntPtr buffer, uint size)
{
try
{
var managed = new byte[size];
_stream.Position = pos;
if (_stream.Read(managed, 0, (int)size) != size) return 0;
Marshal.Copy(managed, 0, buffer, (int)size);
return 1;
}
catch { return 0; }
}
/* ----------------------------------------------------------------- */
///
/// ReadDelegate
///
/// <summary>
/// ストリームからデータを読み込むメソッドを表します。
/// </summary>
///
/* ----------------------------------------------------------------- */
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int ReadDelegate(IntPtr param, uint pos, IntPtr buffer, uint size);
#endregion
#region Fields
private readonly object _lock = new object();
private readonly System.IO.Stream _stream;
private readonly ReadDelegate _delegate;
private IntPtr _core;
private bool _disposed;
#endregion
}
}