mirror of
https://github.com/wmjordan/PDFPatcher.git
synced 2026-10-22 00:16:23 +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
|
|
};
|
|
}
|
|
}
|
|
}
|