diff --git a/其他程序/ExecPythonPDF/ExecPythonPDF.sln b/其他程序/ExecPythonPDF/ExecPythonPDF.sln new file mode 100644 index 0000000..f0b4d9a --- /dev/null +++ b/其他程序/ExecPythonPDF/ExecPythonPDF.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.36602.28 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExecPythonPDF", "ExecPythonPDF\ExecPythonPDF.csproj", "{CFB86248-5DA9-4F84-B2BB-6044B62C1DDC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PDFApi", "PDFApi\PDFApi.csproj", "{DD0C73CE-E2C0-4FFF-9A0F-3E40FCE1D46A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CFB86248-5DA9-4F84-B2BB-6044B62C1DDC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CFB86248-5DA9-4F84-B2BB-6044B62C1DDC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CFB86248-5DA9-4F84-B2BB-6044B62C1DDC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CFB86248-5DA9-4F84-B2BB-6044B62C1DDC}.Release|Any CPU.Build.0 = Release|Any CPU + {DD0C73CE-E2C0-4FFF-9A0F-3E40FCE1D46A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DD0C73CE-E2C0-4FFF-9A0F-3E40FCE1D46A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DD0C73CE-E2C0-4FFF-9A0F-3E40FCE1D46A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DD0C73CE-E2C0-4FFF-9A0F-3E40FCE1D46A}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E5D2507D-ABF1-4FF1-9E13-EBC243F32ACF} + EndGlobalSection +EndGlobal diff --git a/其他程序/ExecPythonPDF/ExecPythonPDF/ExecPythonPDF.csproj b/其他程序/ExecPythonPDF/ExecPythonPDF/ExecPythonPDF.csproj new file mode 100644 index 0000000..a7c4f55 --- /dev/null +++ b/其他程序/ExecPythonPDF/ExecPythonPDF/ExecPythonPDF.csproj @@ -0,0 +1,177 @@ + + + + + Debug + AnyCPU + {CFB86248-5DA9-4F84-B2BB-6044B62C1DDC} + Library + ExecPythonPDF + ExecPythonPDF + v4.8 + 512 + true + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + + + + + ..\packages\itext.9.5.0\lib\net461\itext.barcodes.dll + + + ..\packages\itext.9.5.0\lib\net461\itext.bouncy-castle-connector.dll + + + ..\packages\itext.commons.9.5.0\lib\net461\itext.commons.dll + + + ..\packages\itext.font-asian.9.5.0\lib\net461\itext.font_asian.dll + + + ..\packages\itext.9.5.0\lib\net461\itext.forms.dll + + + ..\packages\itext.9.5.0\lib\net461\itext.io.dll + + + ..\packages\itext.9.5.0\lib\net461\itext.kernel.dll + + + ..\packages\itext.9.5.0\lib\net461\itext.layout.dll + + + ..\packages\itext.9.5.0\lib\net461\itext.pdfa.dll + + + ..\packages\itext.9.5.0\lib\net461\itext.pdfua.dll + + + ..\packages\itext.9.5.0\lib\net461\itext.sign.dll + + + ..\packages\itext.9.5.0\lib\net461\itext.styledxmlparser.dll + + + ..\packages\itext.9.5.0\lib\net461\itext.svg.dll + + + ..\packages\Microsoft.Bcl.AsyncInterfaces.5.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + + ..\packages\Microsoft.Extensions.DependencyInjection.5.0.0\lib\net461\Microsoft.Extensions.DependencyInjection.dll + + + ..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.5.0.0\lib\net461\Microsoft.Extensions.DependencyInjection.Abstractions.dll + + + ..\packages\Microsoft.Extensions.Logging.5.0.0\lib\net461\Microsoft.Extensions.Logging.dll + + + ..\packages\Microsoft.Extensions.Logging.Abstractions.5.0.0\lib\net461\Microsoft.Extensions.Logging.Abstractions.dll + + + ..\packages\Microsoft.Extensions.Options.5.0.0\lib\net461\Microsoft.Extensions.Options.dll + + + ..\packages\Microsoft.Extensions.Primitives.5.0.0\lib\net461\Microsoft.Extensions.Primitives.dll + + + False + D:\A办公\lserp_cs_6.0\引用DLL\Newtonsoft.Json.dll + + + + ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + + ..\packages\System.Diagnostics.DiagnosticSource.5.0.0\lib\net46\System.Diagnostics.DiagnosticSource.dll + + + ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll + + + + ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.5.0.0\lib\net45\System.Runtime.CompilerServices.Unsafe.dll + + + + ..\packages\System.Security.AccessControl.6.0.1\lib\net461\System.Security.AccessControl.dll + + + ..\packages\System.Security.Cryptography.Xml.6.0.2\lib\net461\System.Security.Cryptography.Xml.dll + + + ..\packages\System.Security.Principal.Windows.5.0.0\lib\net461\System.Security.Principal.Windows.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + + ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll + + + + + + + + + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + diff --git a/其他程序/ExecPythonPDF/ExecPythonPDF/Properties/AssemblyInfo.cs b/其他程序/ExecPythonPDF/ExecPythonPDF/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..680ef4a --- /dev/null +++ b/其他程序/ExecPythonPDF/ExecPythonPDF/Properties/AssemblyInfo.cs @@ -0,0 +1,9 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// 有关程序集的一般信息由以下 +// 控制。更改这些特性值可修改 +// 与程序集关联的信息。 +[assembly: AssemblyTitle("ExecPythonPDF")] +[assembly: Assemb \ No newline at end of file diff --git a/其他程序/ExecPythonPDF/ExecPythonPDF/Properties/Resources.Designer.cs b/其他程序/ExecPythonPDF/ExecPythonPDF/Properties/Resources.Designer.cs new file mode 100644 index 0000000..822ad17 --- /dev/null +++ b/其他程序/ExecPythonPDF/ExecPythonPDF/Properties/Resources.Designer.cs @@ -0,0 +1,39 @@ +//------------------------------------------------------------------------------ +// +// 此代码由工具生成。 +// 运行时版本:4.0.30319.42000 +// +// 对此文件的更改可能会导致不正确的行为,并且如果 +// 重新生成代码,这些更改将会丢失。 +// +//------------------------------------------------------------------------------ + +namespace ExecPythonPDF.Properties { + using System; + + + /// + /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// + // 此类是由 StronglyTypedResourceBuilder + // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 + // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen + // (以 /str 作为命令选项),或重新生成 VS 项目。 + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// 返回此类使用的缓存的 ResourceManager 实例。 + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static glo \ No newline at end of file diff --git a/其他程序/ExecPythonPDF/ExecPythonPDF/Properties/Resources.resx b/其他程序/ExecPythonPDF/ExecPythonPDF/Properties/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/其他程序/ExecPythonPDF/ExecPythonPDF/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/其他程序/ExecPythonPDF/ExecPythonPDF/Properties/Settings.Designer.cs b/其他程序/ExecPythonPDF/ExecPythonPDF/Properties/Settings.Designer.cs new file mode 100644 index 0000000..b5ef9d8 --- /dev/null +++ b/其他程序/ExecPythonPDF/ExecPythonPDF/Properties/Settings.Designer.cs @@ -0,0 +1,2 @@ +//------------------------------------------------------------------------------ +// + + + + + + diff --git a/其他程序/ExecPythonPDF/ExecPythonPDF/PythonConsole.cs b/其他程序/ExecPythonPDF/ExecPythonPDF/PythonConsole.cs new file mode 100644 index 0000000..297d8a0 --- /dev/null +++ b/其他程序/ExecPythonPDF/ExecPythonPDF/PythonConsole.cs @@ -0,0 +1,591 @@ +using iText.Kernel.Pdf; +using iText.Kernel.Pdf.Canvas.Parser; +using iText.Kernel.Pdf.Canvas.Parser.Data; +using iText.Kernel.Pdf.Canvas.Parser.Listener; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Text; +using System.Text.RegularExpressions; + +namespace ExecPythonPDF +{ + public class BankRecord + { + public string BankName { get; set; } = ""; + public string RefId { get; set; } = ""; + public string Amount { get; set; } = ""; + public string Date { get; set; } = ""; + public string PayeeAccount { get; set; } = ""; + public string PayeeName { get; set; } = ""; + public string PayeeBank { get; set; } = ""; + public string Remark { get; set; } = ""; + } + + public static class PDFConvert + { + // ==== 静态构造函数:强制加载亚洲字体包,防止 IIS 懒加载导致 UniGB-UTF16-H 报错 ==== + static PDFConvert() + { + try + { + System.Reflection.Assembly.Load("itext.font_asian"); + } + catch + { + // 忽略异常,防止主流程中断 + } + } + + public static JObject ExecutePdfExtraction(string fileUrl, string fileName) + { + JObject rtnJobject = new JObject(); + rtnJobject.Add("success", "false"); + rtnJobject.Add("code", "0"); + rtnJobject.Add("msg", ""); + rtnJobject.Add("data", new JArray()); + try + { + if (!Path.GetExtension(fileName).Equals(".pdf", StringComparison.OrdinalIgnoreCase)) + { + throw new Exception("不是pdf文件"); + } + + byte[] fileData; + if (fileUrl.StartsWith("http", StringComparison.OrdinalIgnoreCase)) + { + using (WebClient webClient = new WebClient()) + { + fileData = webClient.DownloadData(fileUrl); + } + } + else + { + fileData = File.ReadAllBytes(fileUrl); + } + + using (MemoryStream pdfStream = new MemoryStream(fileData)) + { + string content = ConvertPdfToText(pdfStream); + string bankName = BankExtractorFactory.IdentifyBank(fileName); + if (string.IsNullOrEmpty(bankName)) + { + throw new Exception("未知银行"); + } + + List records = BankExtractorFactory.GetExtractor(bankName).Extract(content); + JArray dataArray = rtnJobject["data"] as JArray; + foreach (BankRecord record in records) + { + dataArray.Add(JObject.FromObject(record)); + } + rtnJobject["success"] = "true"; + rtnJobject["code"] = "1"; + rtnJobject["msg"] = "操作成功"; + } + } + catch (Exception ex) + { + rtnJobject["success"] = "false"; + rtnJobject["code"] = "0"; + rtnJobject["msg"] = ex.Message; + } + return rtnJobject; + } + + public static string ConvertPdfToText(Stream pdfStream) + { + StringBuilder textContent = new StringBuilder(); + try + { + using (PdfReader reader = new PdfReader(pdfStream)) + using (PdfDocument pdfDoc = new PdfDocument(reader)) + { + for (int i = 1; i <= pdfDoc.GetNumberOfPages(); i++) + { + PdfPage page = pdfDoc.GetPage(i); + CustomLayoutStrategy strategy = new CustomLayoutStrategy(3.0f); + new PdfCanvasProcessor(strategy).ProcessPageContent(page); + string pageText = strategy.GetResultantText(); + if (!string.IsNullOrWhiteSpace(pageText)) + { + textContent.AppendLine($"--- 第{i}页 ---"); + textContent.AppendLine(pageText); + } + } + } + return textContent.ToString(); + } + catch (Exception ex) + { + throw new Exception($"PDF解析失败: {ex.Message}", ex); + } + } + } + + /// + /// 自定义布局策略:模拟 Python pdfplumber 的 y_tolerance 逻辑 + /// + public class CustomLayoutStrategy : IEventListener + { + private readonly List _chunks = new List(); + private readonly float _yTolerance; + + public CustomLayoutStrategy(float yTolerance = 3.0f) + { + _yTolerance = yTolerance; + } + + public void EventOccurred(IEventData data, EventType type) + { + if (type == EventType.RENDER_TEXT) + { + var renderInfo = (TextRenderInfo)data; + var text = renderInfo.GetText(); + if (string.IsNullOrWhiteSpace(text)) return; + + var segment = renderInfo.GetBaseline(); + var start = segment.GetStartPoint(); + + _chunks.Add(new TextChunk + { + Text = text, + X = start.Get(0), + Y = start.Get(1) + }); + } + } + + public ICollection GetSupportedEvents() + { + return new HashSet { EventType.RENDER_TEXT }; + } + + public string GetResultantText() + { + if (_chunks.Count == 0) return ""; + + var sortedByY = _chunks.OrderByDescending(c => c.Y).ToList(); + var lines = new List>(); + var currentLine = new List(); + + if (sortedByY.Count > 0) + { + currentLine.Add(sortedByY[0]); + float currentLineY = sortedByY[0].Y; + + for (int i = 1; i < sortedByY.Count; i++) + { + var chunk = sortedByY[i]; + if (Math.Abs(chunk.Y - currentLineY) <= _yTolerance) + { + currentLine.Add(chunk); + } + else + { + lines.Add(currentLine); + currentLine = new List { chunk }; + currentLineY = chunk.Y; + } + } + lines.Add(currentLine); + } + + StringBuilder sb = new StringBuilder(); + foreach (var line in lines) + { + var sortedLine = line.OrderBy(c => c.X).ToList(); + sb.AppendLine(SmartJoin(sortedLine)); + } + + return sb.ToString(); + } + + private string SmartJoin(List chunks) + { + if (chunks.Count == 0) return ""; + StringBuilder lineSb = new StringBuilder(); + lineSb.Append(chunks[0].Text); + + for (int i = 1; i < chunks.Count; i++) + { + var prev = chunks[i - 1]; + var curr = chunks[i]; + if (curr.X - (prev.X + GetWidthEstimate(prev.Text)) > 5) + { + lineSb.Append(" "); + } + lineSb.Append(curr.Text); + } + return lineSb.ToString(); + } + + private float GetWidthEstimate(string text) + { + return text.Length * 4.0f; + } + + private class TextChunk + { + public string Text { get; set; } + public float X { get; set; } + public float Y { get; set; } + } + } + + // --- 基类 --- + public abstract class BankExtractor + { + public string BankName { get; } + + protected BankExtractor(string bankName) + { + BankName = bankName; + } + + public abstract List Extract(string content); + + protected string ExtractSingleValue(string content, string pattern) + { + var match = Regex.Match(content, pattern); + return match.Success ? match.Groups[1].Value.Trim() : ""; + } + } + + // --- 1. 中国银行 --- + public class BOCExtractor : BankExtractor + { + public BOCExtractor() : base("中国银行") { } + public override List Extract(string content) + { + var records = new List(); + var pages = content.Split(new[] { "--- 第" }, StringSplitOptions.RemoveEmptyEntries); + + foreach (var rawPage in pages) + { + string pageContent = rawPage; + if (string.IsNullOrWhiteSpace(pageContent)) continue; + + string refId = ExtractSingleValue(pageContent, @"交易流水号[::][^\S\r\n]*([^\s]+)"); + if (string.IsNullOrEmpty(refId)) + { + refId = ExtractSingleValue(pageContent, @"回单编号[::][^\S\r\n]*([^\s]+)"); + } + if (string.IsNullOrEmpty(refId)) continue; + + var record = new BankRecord + { + BankName = BankName, + RefId = refId, + Date = ExtractSingleValue(pageContent, @"日期[::][^\S\r\n]*(\d{4}年\d{1,2}月\d{1,2}日)"), + }; + + string amountStr = ExtractSingleValue(pageContent, @"金额[::][^\S\r\n]*CNY([0-9,.]+)"); + record.Amount = amountStr.Replace(",", ""); + + record.PayeeAccount = ExtractSingleValue(pageContent, @"收款人账号[::][^\S\r\n]*([a-zA-Z0-9]+)"); + record.PayeeName = ExtractSingleValue(pageContent, @"收款人名称[::][^\S\r\n]*([^\r\n]+)"); + record.PayeeBank = ExtractSingleValue(pageContent, @"收款人开户行[::][^\S\r\n]*([^\r\n]+)"); + + string yongtu = ExtractSingleValue(pageContent, @"用途[::][^\S\r\n]*([^\r\n]+)"); + string beizhu = ExtractSingleValue(pageContent, @"备注[::][^\S\r\n]*([^\r\n]+)"); + string fuyan = ExtractSingleValue(pageContent, @"附言[::][^\S\r\n]*([^\r\n]+)"); + + var remarkList = new List(); + if (!string.IsNullOrEmpty(yongtu)) remarkList.Add(yongtu); + if (!string.IsNullOrEmpty(beizhu)) remarkList.Add(beizhu); + if (!string.IsNullOrEmpty(fuyan)) remarkList.Add(fuyan); + + record.Remark = string.Join("-", remarkList.Distinct()); + records.Add(record); + } + return records; + } + } + + // --- 2. 工商银行 --- + public class ICBCExtractor : BankExtractor + { + public ICBCExtractor() : base("工商银行") { } + public override List Extract(string content) + { + var records = new List(); + var pages = content.Split(new[] { "--- 第" }, StringSplitOptions.None); + var extractedIds = new HashSet(); + + foreach (var rawPage in pages) + { + string pageContent = rawPage; + if (string.IsNullOrWhiteSpace(pageContent)) continue; + if (!pageContent.StartsWith("页 ---")) pageContent = "--- 第" + pageContent; + + var record = new BankRecord { BankName = BankName }; + + record.RefId = ExtractSingleValue(pageContent, @"回单编号[::]\s*([^\s]+)"); + string amountStr = ExtractSingleValue(pageContent, @"小写[::]\s*([0-9,.]+)"); + record.Amount = amountStr.Replace(",", ""); + record.Date = ExtractSingleValue(pageContent, @"日期[::]\s*(\d{4}-\d{2}-\d{2})"); + + record.PayeeAccount = ExtractSingleValue(pageContent, @"收款人账号[::]\s*([a-zA-Z0-9]+)"); + record.PayeeBank = ExtractSingleValue(pageContent, @"收款人开户行[::]\s*([^\s]+)"); + record.PayeeName = ExtractSingleValue(pageContent, @"收款人户名[::]\s*([^\s]+)"); + + string summary = ExtractSingleValue(pageContent, @"摘要[::]\s*([^\s]+)"); + string usage = ExtractSingleValue(pageContent, @"用途[::]\s*([^\s]+)"); + + if (summary.Contains("用途:") || summary.Contains("交易机构号:")) summary = ""; + if (usage.Contains("交易机构号:") || usage.Contains("凭证号码:")) usage = ""; + + record.Remark = !string.IsNullOrEmpty(summary) ? summary : usage; + + if (!string.IsNullOrEmpty(record.RefId) && !extractedIds.Contains(record.RefId)) + { + if (!string.IsNullOrEmpty(record.Amount) || !string.IsNullOrEmpty(record.Date)) + { + records.Add(record); + extractedIds.Add(record.RefId); + } + } + } + return records; + } + } + + // --- 3. 成都银行 --- + public class ChengduBankExtractor : BankExtractor + { + public ChengduBankExtractor() : base("成都银行") { } + public override List Extract(string content) + { + var records = new List(); + var pages = content.Split(new[] { "--- 第" }, StringSplitOptions.RemoveEmptyEntries); + + foreach (var rawPage in pages) + { + string pageContent = rawPage; + if (string.IsNullOrWhiteSpace(pageContent)) continue; + + string refPattern = @"参考号[^\S\r\n]*([^\s]+)[^\S\r\n]+交易日期[^\S\r\n]*(\d{4}-\d{2}-\d{2})"; + var refMatches = Regex.Matches(pageContent, refPattern); + + for (int i = 0; i < refMatches.Count; i++) + { + var match = refMatches[i]; + string refId = match.Groups[1].Value; + string date = match.Groups[2].Value; + + int currentStart = match.Index; + int prevEnd = (i > 0) ? (refMatches[i - 1].Index + refMatches[i - 1].Length) : 0; + int lengthBefore = currentStart - prevEnd; + string beforeSearchArea = (lengthBefore > 0) ? pageContent.Substring(prevEnd, lengthBefore) : ""; + + int nextStart = (i < refMatches.Count - 1) ? refMatches[i + 1].Index : pageContent.Length; + int afterStart = match.Index + match.Length; + int lengthAfter = nextStart - afterStart; + string afterSearchArea = (lengthAfter > 0) ? pageContent.Substring(afterStart, lengthAfter) : ""; + + var record = new BankRecord + { + BankName = BankName, + RefId = refId, + Date = date, + Amount = ExtractAmount(beforeSearchArea), + PayeeAccount = ExtractSingleValue(beforeSearchArea, @"收款方账户[^\S\r\n]*([a-zA-Z0-9]*)"), + PayeeName = ExtractSingleValue(beforeSearchArea, @"收款方户名[^\S\r\n]*([^\r\n]*)").Trim(), + Remark = ExtractSingleValue(afterSearchArea, @"摘要[^\S\r\n]*([^\r\n]*)").Trim() + }; + + var bankMatches = Regex.Matches(beforeSearchArea, @"开户行[^\S\r\n]*((?!开户行|方|款|付|收)[^\s]*)"); + if (bankMatches.Count > 0) + { + record.PayeeBank = bankMatches[bankMatches.Count - 1].Groups[1].Value.Trim(); + } + + records.Add(record); + } + } + return records; + } + + private string ExtractAmount(string searchArea) + { + int pos = searchArea.LastIndexOf("交易金额"); + if (pos != -1) + { + string relevant = searchArea.Substring(0, pos); + var lines = relevant.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); + string numPattern = @"([\d,]+\.?\d*)\s*$"; + for (int j = lines.Length - 1; j >= 0; j--) + { + var m = Regex.Match(lines[j].Trim(), numPattern); + if (m.Success) return m.Groups[1].Value.Replace(",", ""); + } + } + return ""; + } + } + + // --- 4. 民生银行 --- + public class CMBCExtractor : BankExtractor + { + public CMBCExtractor() : base("民生银行") { } + public override List Extract(string content) + { + var records = new List(); + var pages = content.Split(new[] { "--- 第" }, StringSplitOptions.RemoveEmptyEntries); + + foreach (var rawPage in pages) + { + string pageContent = rawPage; + if (string.IsNullOrWhiteSpace(pageContent)) continue; + + var refMatch = Regex.Match(pageContent, @"交易流水号\s+([^\s]+)"); + if (!refMatch.Success) continue; + + var record = new BankRecord + { + BankName = BankName, + RefId = refMatch.Groups[1].Value, + Date = ExtractSingleValue(pageContent, @"记账时间\s+(\d{4}-\d{2}-\d{2})") + }; + + string amountStr = ExtractSingleValue(pageContent, @"金额 人民币[::]\s*([0-9,.]+)"); + record.Amount = amountStr.Replace(",", ""); + + record.PayeeName = GetRightColumnValue(pageContent, "全称"); + record.PayeeAccount = GetRightColumnValue(pageContent, "账号"); + record.PayeeBank = GetRightColumnValue(pageContent, "开户行"); + record.Remark = ExtractSingleValue(pageContent, @"(?:客户附言|附言)\s+([^\s]+)"); + + records.Add(record); + } + return records; + } + + private string GetRightColumnValue(string content, string keyword) + { + var matches = Regex.Matches(content, keyword + @"[^\S\r\n]+([^\s]+)"); + if (matches.Count >= 2) + { + return matches[1].Groups[1].Value.Trim(); + } + else if (matches.Count == 1) + { + return matches[0].Groups[1].Value.Trim(); + } + return ""; + } + } + + // --- 5. 浙商银行 --- + public class CZBankExtractor : BankExtractor + { + public CZBankExtractor() : base("浙商银行") { } + public override List Extract(string content) + { + var records = new List(); + var pages = content.Split(new[] { "--- 第" }, StringSplitOptions.RemoveEmptyEntries); + + foreach (var rawPage in pages) + { + string pageContent = rawPage; + if (string.IsNullOrWhiteSpace(pageContent)) continue; + + var refMatch = Regex.Match(pageContent, @"交易流水号[::]\s*([^\s]+)"); + if (!refMatch.Success) continue; + + string refId = refMatch.Groups[1].Value; + + var record = new BankRecord + { + BankName = BankName, + RefId = refId, + Date = ExtractDate(pageContent), + Amount = ExtractAmount(pageContent), + PayeeName = GetRightColumnValue(pageContent, "户名"), + PayeeAccount = GetRightColumnValue(pageContent, "账号"), + }; + + record.PayeeBank = ExtractPayeeBank(pageContent); + + string yongtu = CleanBorderArtifacts(ExtractSingleValue(pageContent, @"用途[::][^\S\r\n]*([^\s]*)")); + string fuyan = CleanBorderArtifacts(ExtractSingleValue(pageContent, @"附言[::][^\S\r\n]*([^\s]*)")); + string zhaiyao = CleanBorderArtifacts(ExtractSingleValue(pageContent, @"摘要[::][^\S\r\n]*([^\s]*)")); + string beizhu = CleanBorderArtifacts(ExtractSingleValue(pageContent, @"备注[::][^\S\r\n]*([^\s]*)")); + + var remarkList = new List(); + if (!string.IsNullOrEmpty(yongtu)) remarkList.Add(yongtu); + if (!string.IsNullOrEmpty(fuyan)) remarkList.Add(fuyan); + if (!string.IsNullOrEmpty(zhaiyao)) remarkList.Add(zhaiyao); + if (!string.IsNullOrEmpty(beizhu)) remarkList.Add(beizhu); + + record.Remark = string.Join("-", remarkList.Distinct()); + + records.Add(record); + } + return records; + } + + private string CleanBorderArtifacts(string text) + { + if (string.IsNullOrWhiteSpace(text)) return ""; + if (text.Length == 1 && "本凭证作客户收付款通知人".Contains(text)) return ""; + return text; + } + + private string ExtractDate(string text) + { + var m = Regex.Match(text, @"(?:银行日期|交易时间)[::]\s*(\d{4}-\d{2}-\d{2})"); + return m.Success ? m.Groups[1].Value : ""; + } + + private string ExtractAmount(string text) + { + var m = Regex.Match(text, @"金额\s+人民币[^\r\n¥]*¥([0-9,.\d]+)"); + if (!m.Success) m = Regex.Match(text, @"金额\s+人民币(?:[^¥]*?)¥([0-9,.\d]+)"); + return m.Success ? m.Groups[1].Value.Replace(",", "") : ""; + } + + private string GetRightColumnValue(string content, string keyword) + { + var matches = Regex.Matches(content, keyword + @"[::]"); + if (matches.Count >= 2) + { + return GetValueAfterMatch(content, matches[1], null); + } + return ""; + } + + private string ExtractPayeeBank(string content) + { + var matches = Regex.Matches(content, @"开户银行[::]"); + if (matches.Count >= 2) + { + string bank1 = GetValueAfterMatch(content, matches[0], matches[1]); + string bank2 = GetValueAfterMatch(content, matches[1], null); + + if (string.IsNullOrEmpty(bank2)) return ""; + + if (content.Contains("(借)") || content.Contains("(借)")) + { + if (bank2.Contains("浙商银行") && !bank1.Contains("浙商银行")) return bank1; + if (bank1.Contains("浙商银行") && !bank2.Contains("浙商银行")) return bank2; + } + + return bank2; + } + return ""; + } + + private string GetValueAfterMatch(string content, Match currentMatch, Match nextMatch) + { + int p = currentMatch.Index + currentMatch.Length; + while (p < content.Length && (content[p] == ' ' || content[p] == '\t')) p++; + + int endLimit = nextMatch != null ? nextMatch.Index : content.Length; + + int endP = p; + \ No newline at end of file diff --git a/其他程序/ExecPythonPDF/ExecPythonPDF/Resources/AdobeFontMetrics/MustRead.html b/其他程序/ExecPythonPDF/ExecPythonPDF/Resources/AdobeFontMetrics/MustRead.html new file mode 100644 index 0000000..6dada97 --- /dev/null +++ b/其他程序/ExecPythonPDF/ExecPythonPDF/Resources/AdobeFontMetrics/MustRead.html @@ -0,0 +1,19 @@ + + + + + + Core 14 AFM Files - ReadMe + + + + or + + + + + +
This file and the 14 PostScript(R) AFM files it accompanies may be used, copied, and distributed for any purpose and without charge, with or without modification, provided that all copyright notices are retained; that the AFM files are not distributed without this file; that all modifications to this file or any of the AFM files are prominently noted in the modified file(s); and that this paragraph is not modified. Adobe Systems has no responsibility or obligation to support the use of the AFM files. Col
+ + + diff --git a/其他程序/ExecPythonPDF/ExecPythonPDF/app.config b/其他程序/ExecPythonPDF/ExecPythonPDF/app.config new file mode 100644 index 0000000..f10b4ac --- /dev/null +++ b/其他程序/ExecPythonPDF/ExecPythonPDF/app.config @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/其他程序/ExecPythonPDF/ExecPythonPDF/packages.config b/其他程序/ExecPythonPDF/ExecPythonPDF/packages.config new file mode 100644 index 0000000..dfd519d --- /dev/null +++ b/其他程序/ExecPythonPDF/ExecPythonPDF/packages.config @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/其他程序/ExecPythonPDF/PDFApi/App_Start/WebApiConfig.cs b/其他程序/ExecPythonPDF/PDFApi/App_Start/WebApiConfig.cs new file mode 100644 index 0000000..023f066 Binary files /dev/null and b/其他程序/ExecPythonPDF/PDFApi/App_Start/WebApiConfig.cs differ diff --git a/其他程序/ExecPythonPDF/PDFApi/Controllers/PdfController.cs b/其他程序/ExecPythonPDF/PDFApi/Controllers/PdfController.cs new file mode 100644 index 0000000..1b19a42 --- /dev/null +++ b/其他程序/ExecPythonPDF/PDFApi/Controllers/PdfController.cs @@ -0,0 +1,14 @@ +using ExecPythonPDF; +using Newtonsoft.Json.Linq; +using System; +using System.Web.Http; + +namespace PDFApi.Controllers +{ + public class PdfController : ApiController + { + [HttpPost] + [Route("Api/ProcessPDF")] // 自定义路由 + public IHttpActionResult ProcessPDF([FromBody] PDFRequest request) + { + \ No newline at end of file diff --git a/其他程序/ExecPythonPDF/PDFApi/Global.asax b/其他程序/ExecPythonPDF/PDFApi/Global.asax new file mode 100644 index 0000000..1bdc64d --- /dev/null +++ b/其他程序/ExecPythonPDF/PDFApi/Global.asax @@ -0,0 +1 @@ +<%@ Application Codebehind="Global.asax.cs" Inherits="PDFApi.WebApiApplication" Language="C#" %> diff --git a/其他程序/ExecPythonPDF/PDFApi/Global.asax.cs b/其他程序/ExecPythonPDF/PDFApi/Global.asax.cs new file mode 100644 index 0000000..69cb54b Binary files /dev/null and b/其他程序/ExecPythonPDF/PDFApi/Global.asax.cs differ diff --git a/其他程序/ExecPythonPDF/PDFApi/PDFApi.csproj b/其他程序/ExecPythonPDF/PDFApi/PDFApi.csproj new file mode 100644 index 0000000..704cfba --- /dev/null +++ b/其他程序/ExecPythonPDF/PDFApi/PDFApi.csproj @@ -0,0 +1,155 @@ + + + + + Debug + AnyCPU + + + 2.0 + {DD0C73CE-E2C0-4FFF-9A0F-3E40FCE1D46A} + {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} + Library + Properties + PDFApi + PDFApi + v4.8 + true + + + + + + + + + + + true + full + false + bin\ + DEBUG;TRACE + prompt + 4 + AnyCPU + + + true + pdbonly + true + bin\ + TRACE + prompt + 4 + + + + ..\packages\itext.font-asian.9.5.0\lib\net461\itext.font_asian.dll + + + + ..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll + + + + + + + + + + + + + + + + + + + + + + ..\packages\Microsoft.AspNet.WebApi.Client.5.2.7\lib\net45\System.Net.Http.Formatting.dll + + + ..\packages\Microsoft.AspNet.WebApi.Core.5.2.7\lib\net45\System.Web.Http.dll + + + ..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.7\lib\net45\System.Web.Http.WebHost.dll + + + ..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.2.0.1\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll + + + + + + + + + + + Global.asax + + + + + + + Web.config + + + Web.config + + + + + + + + + {cfb86248-5da9-4f84-b2bb-6044b62c1ddc} + ExecPythonPDF + + + + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + + + + + True + True + 61392 + / + http://localhost:61392/ + False + False + + + False + + + + + + + 这台计算机上缺少此项目引用的 NuGet 程序包。使用“NuGet 程序包还原”可下载这些程序包。有关更多信息,请参见 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。 + + + + + diff --git a/其他程序/ExecPythonPDF/PDFApi/Properties/AssemblyInfo.cs b/其他程序/ExecPythonPDF/PDFApi/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..fb6ea8c --- /dev/null +++ b/其他程序/ExecPythonPDF/PDFApi/Properties/AssemblyInfo.cs @@ -0,0 +1,8 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// 有关程序集的常规信息通过下列特性集 +// 控制。更改这些特性值可修改 +// 与程序集关联的信息。 +[assembly: AssemblyTitle("PDFApi")] \ No newline at end of file diff --git a/其他程序/ExecPythonPDF/PDFApi/Properties/PublishProfiles/FolderProfile.pubxml b/其他程序/ExecPythonPDF/PDFApi/Properties/PublishProfiles/FolderProfile.pubxml new file mode 100644 index 0000000..019d357 --- /dev/null +++ b/其他程序/ExecPythonPDF/PDFApi/Properties/PublishProfiles/FolderProfile.pubxml @@ -0,0 +1,16 @@ + + + + + False + False + True + Release + Any CPU + FileSystem + bin\app.publish\ + FileSystem + + \ No newline at end of file diff --git a/其他程序/ExecPythonPDF/PDFApi/Web.Debug.config b/其他程序/ExecPythonPDF/PDFApi/Web.Debug.config new file mode 100644 index 0000000..b9a9fde --- /dev/null +++ b/其他程序/ExecPythonPDF/PDFApi/Web.Debug.config @@ -0,0 +1,31 @@ + + + + + + + + + + \ No newline at end of file diff --git a/其他程序/ExecPythonPDF/PDFApi/Web.Release.config b/其他程序/ExecPythonPDF/PDFApi/Web.Release.config new file mode 100644 index 0000000..755e745 --- /dev/null +++ b/其他程序/ExecPythonPDF/PDFApi/Web.Release.config @@ -0,0 +1,32 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/其他程序/ExecPythonPDF/PDFApi/Web.config b/其他程序/ExecPythonPDF/PDFApi/Web.config new file mode 100644 index 0000000..216036c --- /dev/null +++ b/其他程序/ExecPythonPDF/PDFApi/Web.config @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/其他程序/ExecPythonPDF/PDFApi/packages.config b/其他程序/ExecPythonPDF/PDFApi/packages.config new file mode 100644 index 0000000..44e3d48 --- /dev/null +++ b/其他程序/ExecPythonPDF/PDFApi/packages.config @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file