mirror of
https://github.com/wmjordan/PDFPatcher.git
synced 2026-10-22 08:26:03 +08:00
142 lines
4.3 KiB
C#
142 lines
4.3 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using iTextSharp.text.pdf;
|
|
using PDFPatcher.Common;
|
|
using PDFPatcher.Model;
|
|
using Matrix = iTextSharp.text.pdf.parser.Matrix;
|
|
|
|
namespace PDFPatcher.Processor
|
|
{
|
|
sealed class PdfPageCommandProcessor : PdfContentStreamProcessor, IPdfPageCommandContainer
|
|
{
|
|
|
|
public bool HasCommand => Commands.Count > 0;
|
|
/// <summary>
|
|
/// 分析内容后得到的 PDF 命令操作符及操作数列表。
|
|
/// </summary>
|
|
public IList<PdfPageCommand> Commands { get; }
|
|
readonly Stack<EnclosingCommand> _commandStack;
|
|
EnclosingCommand _currentCommand;
|
|
float _textWidth;
|
|
|
|
public PdfPageCommandProcessor () {
|
|
PopulateOperators ();
|
|
RegisterContentOperator ("TJ", new AccumulatedShowTextArray ());
|
|
Commands = new List<PdfPageCommand> ();
|
|
_commandStack = new Stack<EnclosingCommand> ();
|
|
}
|
|
|
|
public PdfPageCommandProcessor (PRStream form)
|
|
: this () {
|
|
var resources = form.Locate<PdfDictionary> (PdfName.RESOURCES);
|
|
ProcessContent (PdfReader.GetStreamBytes (form), resources);
|
|
}
|
|
|
|
protected override void DisplayPdfString (PdfString str) {
|
|
var gs = CurrentGraphicState;
|
|
var font = gs.Font;
|
|
if (font == null) {
|
|
return;
|
|
}
|
|
float totalWidth = 0;
|
|
foreach (var c in font.DecodeText(str)) {
|
|
var w = font.GetWidth (c) / 1000.0f;
|
|
var wordSpacing = (c == ' ' ? gs.WordSpacing : 0f);
|
|
totalWidth += (w * gs.FontSize + gs.CharacterSpacing + wordSpacing) * gs.HorizontalScaling;
|
|
}
|
|
|
|
_textWidth = totalWidth;
|
|
}
|
|
|
|
protected override void InvokeOperator (PdfLiteral oper, List<PdfObject> operands) {
|
|
base.InvokeOperator (oper, operands);
|
|
PdfPageCommand cmd;
|
|
var o = oper.ToString ();
|
|
switch (o) {
|
|
case "TJ":
|
|
cmd = new PaceAndTextCommand (oper, operands, GetTextInfo (new PdfString ()), CurrentGraphicState.Font);
|
|
break;
|
|
case "Tj":
|
|
case "'":
|
|
case "\"":
|
|
cmd = new TextCommand (oper, operands, GetTextInfo (operands[0] as PdfString));
|
|
break;
|
|
case "Tf":
|
|
cmd = new FontCommand (oper, operands, CurrentGraphicState.Font.FontName);
|
|
break;
|
|
case "cm":
|
|
case "Tm":
|
|
cmd = new MatrixCommand (oper, operands);
|
|
break;
|
|
case "BI":
|
|
cmd = new InlineImageCommand (oper, operands);
|
|
break;
|
|
default:
|
|
cmd = EnclosingCommand.IsStartingCommand (o) ? new EnclosingCommand (oper, operands) : new PdfPageCommand (oper, operands);
|
|
break;
|
|
}
|
|
|
|
if (EnclosingCommand.IsEndingCommand (o)) {
|
|
_commandStack.Pop ();
|
|
_currentCommand = _commandStack.Count > 0 ? _commandStack.Peek () : null;
|
|
return;
|
|
}
|
|
if (_currentCommand != null) {
|
|
_currentCommand.Commands.Add (cmd);
|
|
}
|
|
else {
|
|
Commands.Add (cmd);
|
|
}
|
|
if (cmd is EnclosingCommand ec) {
|
|
_commandStack.Push(ec);
|
|
_currentCommand = ec;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 将 <see cref="Operands"/> 的内容写入到目标 <see cref="System.IO.Stream"/>。
|
|
/// </summary>
|
|
/// <param name="target">目标流对象。</param>
|
|
internal void WritePdfCommands (System.IO.Stream target) {
|
|
foreach (var item in Commands) {
|
|
item.WriteToPdf (target);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 将 <see cref="Operands"/> 的内容写入到目标 <paramref name="pdf"/> 的第 <paramref name="pageNumber"/> 页。
|
|
/// </summary>
|
|
/// <param name="pdf">目标 <see cref="PdfReader"/>。</param>
|
|
/// <param name="pageNumber">要写入的页码。</param>
|
|
internal void WritePdfCommands (PdfReader pdf, int pageNumber) {
|
|
using (var ms = new System.IO.MemoryStream ()) {
|
|
WritePdfCommands (ms);
|
|
ms.Flush ();
|
|
pdf.SafeSetPageContent (pageNumber, ms.ToArray ());
|
|
}
|
|
}
|
|
|
|
internal void WritePdfCommands (PageProcessorContext context) {
|
|
WritePdfCommands (context.Pdf, context.PageNumber);
|
|
}
|
|
|
|
private static string GetOperandsTextValue (List<PdfObject> operands) {
|
|
var n = operands.ConvertAll ((po) => po.Type == PdfObject.NUMBER ? ValueHelper.ToText(((PdfNumber)po).DoubleValue) : null);
|
|
n.RemoveAt (n.Count - 1);
|
|
return String.Join (" ", n.ToArray ());
|
|
}
|
|
|
|
private TextInfo GetTextInfo (PdfString text) {
|
|
var gs = CurrentGraphicState;
|
|
var m = TextMatrix;
|
|
return new TextInfo {
|
|
PdfString = text,
|
|
Text = gs.Font != null ? gs.Font.DecodeText (text) : String.Empty,
|
|
Size = gs.FontSize * m[Matrix.I11],
|
|
Region = new Bound (m[Matrix.I31], m[Matrix.I32], m[Matrix.I31] + _textWidth, 0),
|
|
Font = gs.Font
|
|
};
|
|
}
|
|
}
|
|
}
|