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; /// /// 分析内容后得到的 PDF 命令操作符及操作数列表。 /// public IList Commands { get; } readonly Stack _commandStack; EnclosingCommand _currentCommand; float _textWidth; public PdfPageCommandProcessor() { PopulateOperators(); RegisterContentOperator("TJ", new AccumulatedShowTextArray()); Commands = new List(); _commandStack = new Stack(); } public PdfPageCommandProcessor(PRStream form) : this() { var resources = form.Locate(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 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)) { // 兼容结构异常的 PDF 文档(github: #121) if (_commandStack.Count > 0) { _commandStack.Pop(); _currentCommand = _commandStack.Count > 0 ? _commandStack.Peek() : null; } else { _currentCommand = null; } return; } if (_currentCommand != null) { _currentCommand.Commands.Add(cmd); } else { Commands.Add(cmd); } if (cmd is EnclosingCommand ec) { _commandStack.Push(ec); _currentCommand = ec; } } /// /// 将 的内容写入到目标 。 /// /// 目标流对象。 internal void WritePdfCommands(System.IO.Stream target) { foreach (var item in Commands) { item.WriteToPdf(target); } } /// /// 将 的内容写入到目标 的第 页。 /// /// 目标 。 /// 要写入的页码。 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 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 }; } } }