mirror of
https://github.com/wmjordan/PDFPatcher.git
synced 2026-10-19 23:01:48 +08:00
220 lines
6.4 KiB
C#
220 lines
6.4 KiB
C#
using System;
|
|
using System.Drawing;
|
|
using System.Drawing.Imaging;
|
|
using PDFPatcher.Common;
|
|
|
|
namespace MuPDF.Extensions
|
|
{
|
|
public static class MuPDFExtensions
|
|
{
|
|
#region 文档基础结构
|
|
public static TObj Get<TObj>(this PdfArray array, int index) where TObj : PdfObject {
|
|
return array[index].UnderlyingObject as TObj;
|
|
}
|
|
public static TObj Get<TObj>(this PdfDictionary dict, int index) where TObj : PdfObject {
|
|
return dict[index].Value.UnderlyingObject as TObj;
|
|
}
|
|
public static TObj Get<TObj>(this PdfDictionary dict, PdfNames key) where TObj : PdfObject {
|
|
return dict[key].UnderlyingObject as TObj;
|
|
}
|
|
public static TObj Get<TObj>(this PdfDictionary dict, PdfNames key, PdfNames alias) where TObj : PdfObject {
|
|
return dict.GetValue(key, alias).UnderlyingObject as TObj;
|
|
}
|
|
#endregion
|
|
|
|
#region 几何尺寸
|
|
public static RectangleF ToRectangleF(this Box box) {
|
|
return RectangleF.FromLTRB(box.X0, box.Y0, box.X1, box.Y1);
|
|
}
|
|
public static Rectangle ToRectangle(this Box box) {
|
|
return Rectangle.FromLTRB((int)box.X0, (int)box.Y0, (int)box.X1, (int)box.Y1);
|
|
}
|
|
public static void Deconstruct(this Point point, out float x, out float y) {
|
|
x = point.X; y = point.Y;
|
|
}
|
|
|
|
public static bool IsHorizontalNeighbor(this Box me, Box other) {
|
|
if (me.IsEmpty || other.IsInfinite || other.IsEmpty || me.IsInfinite || other.Y0 > me.Y1 || other.Y1 < me.Y0) {
|
|
return false;
|
|
}
|
|
var h = me.Height / other.Height;
|
|
return h > 0.4 && h < 2.5;
|
|
}
|
|
#endregion
|
|
|
|
public static string GetText(this TextLine textLine) {
|
|
var sb = StringBuilderCache.Acquire(10);
|
|
foreach (var ch in textLine) {
|
|
sb.Append(Char.IsSurrogate((char)ch.Character) ? "?" : char.ConvertFromUtf32(ch.Character));
|
|
}
|
|
return StringBuilderCache.GetStringAndRelease(sb);
|
|
}
|
|
|
|
#region 渲染页面
|
|
public static Bitmap RenderBitmapPage(this Page page, int width, int height, ImageRendererOptions options, Cookie cookie) {
|
|
using (var pix = InternalRenderPage(page, width, height, options, cookie)) {
|
|
if (pix != null) {
|
|
return pix.ToBitmap(options);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
static Pixmap InternalRenderPage(Page page, int width, int height, ImageRendererOptions options, Cookie cookie) {
|
|
var b = page.Bound;
|
|
if (b.Width == 0 || b.Height == 0) {
|
|
return null;
|
|
}
|
|
var ctm = CalculateMatrix(page, width, height, options);
|
|
var bbox = width > 0 && height > 0 ? new BBox(0, 0, width, height) : b.Transform(ctm).Round();
|
|
|
|
var pix = Pixmap.Create(options.ColorSpace == ColorspaceKind.None ? ColorspaceKind.Rgb : options.ColorSpace, bbox);
|
|
if (pix == null) {
|
|
throw new MuException($"无法渲染页面:{(page.PageNumber + 1).ToText()}");
|
|
}
|
|
pix.Clear(0xFF);
|
|
try {
|
|
using (var dev = Device.NewDraw(pix, Matrix.Identity)) {
|
|
if (options.LowQuality) {
|
|
dev.EnableDeviceHints(DeviceHints.DontInterpolateImages | DeviceHints.NoCache);
|
|
}
|
|
if (cookie.IsCancellationPending) {
|
|
return null;
|
|
}
|
|
page.RunContents(dev, ctm, cookie);
|
|
if (options.HideAnnotations == false) {
|
|
page.RunAnnotations(dev, ctm, cookie);
|
|
page.RunWidgets(dev, ctm, cookie);
|
|
}
|
|
dev.Close();
|
|
|
|
if (cookie.IsCancellationPending) {
|
|
return null;
|
|
}
|
|
if (options.TintColor != Color.Transparent) {
|
|
pix.Tint(options.TintColor.ToArgb());
|
|
}
|
|
if (options.Gamma != 1.0f) {
|
|
pix.Gamma(options.Gamma);
|
|
}
|
|
return pix;
|
|
}
|
|
}
|
|
catch {
|
|
pix.Dispose();
|
|
throw;
|
|
}
|
|
}
|
|
|
|
static Matrix CalculateMatrix(Page page, int width, int height, ImageRendererOptions options) {
|
|
float w = width, h = height;
|
|
var b = page.Bound;
|
|
if (options.UseSpecificWidth) {
|
|
if (w < 0) {
|
|
w = -w;
|
|
}
|
|
if (h < 0) {
|
|
h = -h;
|
|
}
|
|
if (options.FitArea && w != 0 && h != 0) {
|
|
var rw = w / b.Width;
|
|
var rh = h / b.Height;
|
|
if (rw < rh) {
|
|
h = 0;
|
|
}
|
|
else {
|
|
w = 0;
|
|
}
|
|
}
|
|
if (w == 0 && h == 0) { // No resize
|
|
w = b.Width;
|
|
h = b.Height;
|
|
}
|
|
else if (h == 0) {
|
|
h = width * b.Height / b.Width;
|
|
}
|
|
else if (w == 0) {
|
|
w = height * b.Width / b.Height;
|
|
}
|
|
}
|
|
else if (w == 0 || h == 0) {
|
|
w = b.Width * options.ScaleRatio * options.Dpi / 72;
|
|
h = b.Height * options.ScaleRatio * options.Dpi / 72;
|
|
}
|
|
|
|
var ctm = Matrix.Scale(w / b.Width, h / b.Height).RotateTo(options.Rotation);
|
|
if (options.VerticalFlipImages) {
|
|
ctm = ctm.Concat(Matrix.VerticalFlip);
|
|
}
|
|
if (options.HorizontalFlipImages) {
|
|
ctm = ctm.Concat(Matrix.HorizontalFlip);
|
|
}
|
|
return ctm;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 将 Pixmap 的数据转换为 <see cref="Bitmap"/>。
|
|
/// </summary>
|
|
public static unsafe Bitmap ToBitmap(this Pixmap pix, ImageRendererOptions options) {
|
|
int width = pix.Width;
|
|
int height = pix.Height;
|
|
bool grayscale = options.ColorSpace == ColorspaceKind.Gray;
|
|
bool invert = options.InvertColor;
|
|
var bmp = new Bitmap(width, height, grayscale ? PixelFormat.Format8bppIndexed : PixelFormat.Format24bppRgb);
|
|
var imageData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, bmp.PixelFormat);
|
|
var ptrSrc = (byte*)pix.Samples;
|
|
var ptrDest = (byte*)imageData.Scan0;
|
|
if (grayscale) {
|
|
var palette = bmp.Palette;
|
|
for (int i = 0; i < 256; ++i)
|
|
palette.Entries[i] = Color.FromArgb(i, i, i);
|
|
bmp.Palette = palette;
|
|
for (int y = 0; y < height; y++) {
|
|
var pl = ptrDest;
|
|
var sl = ptrSrc;
|
|
for (int x = 0; x < width; x++) {
|
|
*pl = invert ? (byte)(*sl ^ 0xFF) : *sl;
|
|
pl++;
|
|
sl++;
|
|
}
|
|
ptrDest += imageData.Stride;
|
|
ptrSrc = sl;
|
|
}
|
|
}
|
|
else { // DeviceBGR
|
|
for (int y = 0; y < height; y++) {
|
|
var pl = ptrDest;
|
|
var sl = ptrSrc;
|
|
if (invert) {
|
|
for (int x = 0; x < width; x++) {
|
|
// 在这里进行 RGB 到 DIB BGR 的转换(省去 Mupdf 内部的转换工作)
|
|
pl[2] = (byte)(*sl ^ 0xFF); sl++; // R
|
|
pl[1] = (byte)(*sl ^ 0xFF); sl++; // G
|
|
pl[0] = (byte)(*sl ^ 0xFF); sl++; // B
|
|
pl += 3;
|
|
}
|
|
}
|
|
else {
|
|
for (int x = 0; x < width; x++) {
|
|
// 在这里进行 RGB 到 DIB BGR 的转换(省去 Mupdf 内部的转换工作)
|
|
pl[2] = *sl; sl++; // R
|
|
pl[1] = *sl; sl++; // G
|
|
pl[0] = *sl; sl++; // B
|
|
pl += 3;
|
|
}
|
|
}
|
|
ptrDest += imageData.Stride;
|
|
ptrSrc = sl;
|
|
}
|
|
}
|
|
bmp.UnlockBits(imageData);
|
|
if (options.Dpi > 0) {
|
|
bmp.SetResolution(options.Dpi, options.Dpi);
|
|
}
|
|
return bmp;
|
|
}
|
|
#endregion
|
|
|
|
}
|
|
}
|