Files
PDFPatcher/App/Processor/PdfPageCommandProcessor.cs
T
2021-12-24 20:35:03 +08:00

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
};
}
}
}