using System; using System.Collections.Generic; using iTextSharp.text.pdf; using PDFPatcher.Model; using Matrix = iTextSharp.text.pdf.parser.Matrix; namespace PDFPatcher.Processor { sealed class PdfPageCommandProcessor : PdfContentStreamProcessor, IPdfPageCommandContainer { readonly Stack _commandStack = new(); EnclosingCommand _currentCommand; float _textWidth; public bool HasCommand => Commands.Count > 0; /// /// 分析内容后得到的 PDF 命令操作符及操作数列表。 /// public IList Commands { get; } = []; public string LastError { get; private set; } public PdfPageCommandProcessor() { PopulateOperators(); RegisterContentOperator("TJ", new AccumulatedShowTextArray()); } public PdfPageCommandProcessor(PRStream form) : this() { ProcessContent(PdfReader.GetStreamBytes(form), form.Locate(PdfName.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) { PdfPageCommand cmd; try { base.InvokeOperator(oper, operands); } catch (Exception ex) { cmd = new InvalidCommand(oper, operands) { Error = LastError = oper + " " + (ex is IndexOutOfRangeException ? "指令参数不足" : ex is ArgumentOutOfRangeException ? "指令参数不足" : ex is InvalidCastException ? "指令参数类型不匹配" : ex.Message) }; goto ADD_COMMAND; } switch (oper.ToString()) { case "TJ": if (CurrentGraphicState.Font == null) { cmd = new InvalidCommand(oper, operands) { Error = LastError = "TJ 前缺少 Tf 指令" }; goto ADD_COMMAND; } 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 "Do": case "S": case "s": case "F": case "f": case "f*": case "B": case "B*": case "b": case "b*": case "n": cmd = new OutputCommand(oper, operands); break; case "q": case "BT": case "BMC": case "BDC": case "BX": cmd = new EnclosingCommand(oper, operands); break; case "Q": case "ET": case "EMC": case "EX": // 兼容结构异常的 PDF 文档(github: #121) if (_commandStack.Count > 0) { _commandStack.Pop(); _currentCommand = _commandStack.Count > 0 ? _commandStack.Peek() : null; } else { _currentCommand = null; cmd = new InvalidCommand(oper, operands) { Error = "嵌套指令不配对" }; goto ADD_COMMAND; } return; case "BI": cmd = new InlineImageCommand(oper, operands); break; default: cmd = new AdjustCommand(oper, operands); break; } ADD_COMMAND: 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); } 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 }; } } }