mirror of
https://github.com/wmjordan/PDFPatcher.git
synced 2026-10-18 21:52:34 +08:00
377 lines
10 KiB
C#
377 lines
10 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
|
|
#pragma warning disable 649, 169
|
|
namespace MuPdfSharp
|
|
{
|
|
public interface IMuTextLines
|
|
{
|
|
IEnumerable<MuTextLine> Lines { get; }
|
|
}
|
|
|
|
public interface IMuTextSpans
|
|
{
|
|
IEnumerable<MuTextSpan> Spans { get; }
|
|
}
|
|
|
|
[DebuggerDisplay("Name={Name}")]
|
|
public sealed class MuFont
|
|
{
|
|
readonly ContextHandle _context;
|
|
readonly IntPtr _Font;
|
|
|
|
public unsafe string Name => new string(NativeMethods.GetFontName(_context, _Font));
|
|
public MuFontFlags Attributes => NativeMethods.GetFontFlags(_Font);
|
|
public BBox BBox => NativeMethods.GetFontBBox(_context, _Font);
|
|
|
|
internal MuFont(ContextHandle handle, IntPtr font) {
|
|
_context = handle;
|
|
_Font = font;
|
|
}
|
|
}
|
|
|
|
[Flags]
|
|
public enum MuFontFlags : uint
|
|
{
|
|
None = 0,
|
|
IsMono = 1,
|
|
IsSerif = 1 << 1,
|
|
IsBold = 1 << 2,
|
|
IsItalic = 1 << 3,
|
|
IsSubstitute = 1 << 4, /* use substitute metrics */
|
|
IsStretch = 1 << 5, /* stretch to match PDF metrics */
|
|
IsFakeBold = 1 << 6, /* synthesize bold */
|
|
IsFakeItalic = 1 << 7, /* synthesize italic */
|
|
IsForcedHinting = 1 << 8, /* force hinting for DynaLab fonts */
|
|
HasOpenType = 1 << 9, /* has opentype shaping tables */
|
|
InvalidBBox = 1 << 10
|
|
}
|
|
|
|
[DebuggerDisplay("BBox={BBox}")]
|
|
public sealed class MuTextPage : IMuBoundedElement, IDisposable
|
|
{
|
|
readonly TextPageHandle _handle;
|
|
NativeTextPage _TextPage;
|
|
IEnumerable<MuTextBlock> _Blocks;
|
|
|
|
public Rectangle BBox => _TextPage.MediaBox;
|
|
public IEnumerable<MuTextBlock> Blocks => _Blocks ?? (_Blocks = MuContentBlock.GetTextBlocks(_TextPage._FirstBlock));
|
|
|
|
internal MuTextPage(TextPageHandle nativePage) {
|
|
_handle = nativePage;
|
|
_TextPage = _handle.MarshalAs<NativeTextPage>();
|
|
}
|
|
|
|
///// <summary>
|
|
///// 获取指针指向的所有文本集合。
|
|
///// </summary>
|
|
///// <param name="firstPage">第一个 fz_text_page 指针。</param>
|
|
///// <returns>包含所有文本页的集合。</returns>
|
|
//internal static List<MuTextPage> GetTextPages (TextPageHandle firstPage) {
|
|
// var l = new List<MuTextPage> ();
|
|
// foreach (var p in firstPage.EnumerateLinkedList<NativeTextPage> ()) {
|
|
// l.Add (new MuTextPage (p.Data));
|
|
// }
|
|
// return l;
|
|
//}
|
|
|
|
struct NativeTextPage
|
|
{
|
|
readonly IntPtr /*fz_pool*/ _Pool;
|
|
readonly Rectangle _MediaBox;
|
|
/* fz_text_block */
|
|
internal IntPtr _FirstBlock, _LastBlock;
|
|
|
|
internal Rectangle MediaBox => _MediaBox;
|
|
}
|
|
|
|
#region IDisposable Support
|
|
private bool disposedValue = false; // 要检测冗余调用
|
|
|
|
void Dispose(bool disposing) {
|
|
if (disposedValue) {
|
|
return;
|
|
}
|
|
|
|
if (disposing) {
|
|
_Blocks = null;
|
|
}
|
|
|
|
_handle.DisposeHandle();
|
|
disposedValue = true;
|
|
}
|
|
|
|
~MuTextPage() {
|
|
Dispose(false);
|
|
}
|
|
|
|
// 添加此代码以正确实现可处置模式。
|
|
public void Dispose() {
|
|
Dispose(true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
#endregion
|
|
}
|
|
|
|
public enum ContentBlockType { Text = 0, Image = 1 }
|
|
|
|
public abstract class MuContentBlock : IMuBoundedElement
|
|
{
|
|
public abstract Rectangle BBox { get; }
|
|
public abstract ContentBlockType Type { get; }
|
|
internal static IEnumerable<MuContentBlock> GetBlocks(IntPtr firstBlock) {
|
|
foreach (var item in firstBlock.EnumerateLinkedList<NativeContentBlock>()) {
|
|
switch (item.Data.Type) {
|
|
case ContentBlockType.Text:
|
|
yield return item.Data.ToMuBlock();
|
|
break;
|
|
case ContentBlockType.Image:
|
|
yield return item.Ptr.MarshalAs<NativeImageBlock>().ToMuBlock();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
internal static IEnumerable<MuTextBlock> GetTextBlocks(IntPtr firstBlock) {
|
|
foreach (var item in firstBlock.EnumerateLinkedList<NativeContentBlock>()) {
|
|
if (item.Data.Type == ContentBlockType.Text) {
|
|
yield return item.Data.ToMuBlock() as MuTextBlock;
|
|
}
|
|
}
|
|
}
|
|
|
|
struct NativeImageBlock
|
|
{
|
|
readonly ContentBlockType _Type;
|
|
readonly Rectangle _BBox;
|
|
|
|
/* union {
|
|
struct { fz_stext_line *first_line, *last_line; } t;
|
|
struct { fz_matrix transform; fz_image *image; } i;
|
|
} u; */
|
|
readonly Matrix _Transform;
|
|
readonly IntPtr _Image;
|
|
readonly IntPtr _PreviousBlock, _NextBlock;
|
|
internal MuContentBlock ToMuBlock() {
|
|
return new MuImageBlock(_BBox, _Transform, _Image);
|
|
}
|
|
}
|
|
struct NativeContentBlock : Interop.ILinkedList
|
|
{
|
|
readonly ContentBlockType _Type;
|
|
readonly Rectangle _BBox;
|
|
|
|
/* union {
|
|
struct { fz_stext_line *first_line, *last_line; } t;
|
|
struct { fz_matrix transform; fz_image *image; } i;
|
|
} u; */
|
|
readonly IntPtr _Ptr1, _Ptr2, a, b, c, d, e;
|
|
readonly IntPtr _PreviousBlock, _NextBlock;
|
|
|
|
IntPtr Interop.ILinkedList.Next => _NextBlock;
|
|
internal ContentBlockType Type => _Type;
|
|
internal MuContentBlock ToMuBlock() {
|
|
return new MuTextBlock(_BBox, _Ptr1, _Ptr2);
|
|
}
|
|
}
|
|
}
|
|
|
|
public sealed class MuImageBlock : MuContentBlock, IMuBoundedElement
|
|
{
|
|
readonly Rectangle _BBox;
|
|
readonly Matrix _Matrix;
|
|
readonly IntPtr _Image;
|
|
|
|
internal MuImageBlock(Rectangle bbox, Matrix matrix, IntPtr image) {
|
|
_BBox = bbox;
|
|
_Matrix = matrix;
|
|
_Image = image;
|
|
}
|
|
|
|
public override Rectangle BBox => _BBox;
|
|
public override ContentBlockType Type => ContentBlockType.Image;
|
|
}
|
|
|
|
public sealed class MuTextBlock : MuContentBlock, IMuBoundedElement, IMuTextLines
|
|
{
|
|
readonly Rectangle _BBox;
|
|
readonly IntPtr _FirstLine, _LastLine;
|
|
|
|
IEnumerable<MuTextLine> _Lines;
|
|
public override Rectangle BBox => _BBox;
|
|
public override ContentBlockType Type => ContentBlockType.Text;
|
|
public IEnumerable<MuTextLine> Lines => _Lines ?? (_Lines = MuTextLine.GetLines(_FirstLine));
|
|
|
|
internal MuTextBlock(Rectangle BBox, IntPtr FirstLine, IntPtr LastLine) {
|
|
_BBox = BBox;
|
|
_FirstLine = FirstLine;
|
|
_LastLine = LastLine;
|
|
}
|
|
}
|
|
|
|
[DebuggerDisplay("Text={Text},BBox={BBox}")]
|
|
public sealed class MuTextLine : IMuBoundedElement, IMuTextSpans
|
|
{
|
|
NativeTextLine _textLine;
|
|
string _Text;
|
|
IEnumerable<MuTextChar> _Characters;
|
|
|
|
public Rectangle BBox => _textLine.BBox;
|
|
public IEnumerable<MuTextChar> Characters => _Characters ?? (_Characters = MuTextChar.GetCharacters(_textLine._FirstChar));
|
|
public IList<MuTextSpan> Spans => MuTextChar.GetSpans(this, _textLine._FirstChar, _textLine._LastChar);
|
|
public string Text => _Text ?? (_Text = GetText());
|
|
public MuTextChar FirstCharacter => MuTextChar.GetChar(_textLine._FirstChar);
|
|
|
|
MuTextLine(IntPtr textLine) {
|
|
_textLine = textLine.MarshalAs<NativeTextLine>();
|
|
}
|
|
|
|
IEnumerable<MuTextSpan> IMuTextSpans.Spans => Spans;
|
|
|
|
string GetText() {
|
|
var sb = new System.Text.StringBuilder(50);
|
|
foreach (var ch in Characters) {
|
|
sb.Append(char.ConvertFromUtf32(ch.Unicode));
|
|
}
|
|
return sb.ToString();
|
|
}
|
|
|
|
internal static IEnumerable<MuTextLine> GetLines(IntPtr firstLine) {
|
|
foreach (var item in firstLine.EnumerateLinkedList<NativeTextLine>()) {
|
|
yield return new MuTextLine(item.Ptr);
|
|
}
|
|
}
|
|
|
|
internal struct NativeTextLine : IMuBoundedElement, Interop.ILinkedList
|
|
{
|
|
//int wmode; /* 0 for horizontal, 1 for vertical */
|
|
readonly int _WMode;
|
|
|
|
//fz_point dir; /* normalized direction of baseline */
|
|
readonly Point _Point;
|
|
|
|
//fz_rect bbox;
|
|
readonly Rectangle _BBox;
|
|
//fz_stext_char *first_char, *last_char;
|
|
internal IntPtr _FirstChar, _LastChar;
|
|
|
|
//fz_stext_line *prev, *next;
|
|
readonly IntPtr _PrevLine, _NextLine;
|
|
|
|
IntPtr Interop.ILinkedList.Next => _NextLine;
|
|
|
|
public Rectangle BBox => _BBox;
|
|
}
|
|
}
|
|
|
|
[DebuggerDisplay("Point={Point}; Size={Size}, Char={System.Char.ConvertFromUtf32(Unicode)}({Unicode}); Font={FontID}")]
|
|
public sealed class MuTextChar : IMuBoundedElement
|
|
{
|
|
NativeTextChar _textChar;
|
|
readonly Rectangle _Box;
|
|
|
|
public Point Point => _textChar._Point;
|
|
public Rectangle BBox => _Box.IsEmpty ? Quad.ToRectangle() : _Box;
|
|
public Quad Quad => _textChar._Quad;
|
|
public int Unicode => _textChar._Unicode;
|
|
public float Size => _textChar._Size;
|
|
public IntPtr FontID => _textChar._Font;
|
|
|
|
MuTextChar(NativeTextChar textChar) {
|
|
_textChar = textChar;
|
|
}
|
|
internal static MuTextChar GetChar(IntPtr charPtr) {
|
|
return new MuTextChar(charPtr.MarshalAs<NativeTextChar>());
|
|
}
|
|
internal static IEnumerable<MuTextChar> GetCharacters(IntPtr firstChar) {
|
|
foreach (var item in firstChar.EnumerateLinkedList<NativeTextChar>()) {
|
|
yield return new MuTextChar(item.Data);
|
|
}
|
|
}
|
|
internal unsafe static IList<MuTextSpan> GetSpans(MuTextLine textLine, IntPtr firstChar, IntPtr lastChar) {
|
|
if (firstChar == IntPtr.Zero) {
|
|
return new MuTextSpan[0];
|
|
}
|
|
var r = new List<MuTextSpan>(2);
|
|
var ch = (NativeTextChar*)firstChar;
|
|
var start = ch;
|
|
var end = (NativeTextChar*)lastChar;
|
|
var size = ch->_Size;
|
|
var font = ch->_Font;
|
|
var color = ch->_Color;
|
|
var t = new System.Text.StringBuilder(100);
|
|
t.Append((char)ch->_Unicode);
|
|
do {
|
|
ch = ch->_Next;
|
|
if ((IntPtr)ch == IntPtr.Zero) {
|
|
break;
|
|
}
|
|
if (ch->_Size == size && ch->_Font == font && ch->_Color == color) {
|
|
t.Append((char)ch->_Unicode);
|
|
continue;
|
|
}
|
|
r.Add(new MuTextSpan(textLine, start->_Point, t.ToString(), size, font, start->_Quad.Union(ch->_Quad).ToRectangle(), color));
|
|
t.Length = 0;
|
|
size = ch->_Size;
|
|
font = ch->_Font;
|
|
color = ch->_Color;
|
|
start = ch;
|
|
t.Append((char)ch->_Unicode);
|
|
} while (ch != end);
|
|
if (t.Length > 0) {
|
|
var s = t.ToString().TrimEnd();
|
|
if (s.Length > 0) {
|
|
r.Add(new MuTextSpan(textLine, start->_Point, s, size, font, start->_Quad.Union(end->_Quad).ToRectangle(), color));
|
|
}
|
|
}
|
|
return r;
|
|
}
|
|
unsafe struct NativeTextChar : Interop.ILinkedList
|
|
{
|
|
//int c;
|
|
internal int _Unicode;
|
|
// color
|
|
internal int _Color; // sRGB hex value
|
|
//fz_point origin;
|
|
internal Point _Point;
|
|
//fz_quad quad;
|
|
internal Quad _Quad;
|
|
//float size;
|
|
internal float _Size;
|
|
//fz_font* font;
|
|
internal IntPtr _Font;
|
|
//fz_stext_char* next;
|
|
internal NativeTextChar* _Next;
|
|
|
|
IntPtr Interop.ILinkedList.Next => (IntPtr)_Next;
|
|
}
|
|
}
|
|
|
|
[DebuggerDisplay("Point={Point}; FontID={FontID}; Size={Size}; Color={Color}; Text={Text}")]
|
|
public sealed class MuTextSpan
|
|
{
|
|
public MuTextSpan(MuTextLine line, Point point, string text, float size, IntPtr fontID, Rectangle box, int color) {
|
|
Line = line;
|
|
Point = point;
|
|
Text = text;
|
|
Size = size;
|
|
FontID = fontID;
|
|
Box = box;
|
|
Color = color;
|
|
}
|
|
|
|
public MuTextLine Line { get; }
|
|
public Point Point { get; }
|
|
public string Text { get; }
|
|
public float Size { get; }
|
|
public IntPtr FontID { get; }
|
|
public Rectangle Box { get; }
|
|
public int Color { get; }
|
|
}
|
|
|
|
sealed class TextOptions
|
|
{
|
|
internal int _Flags;
|
|
}
|
|
}
|