Veronique před 1 rokem
revize
90d6e5dc03

+ 4 - 0
.gitignore

@@ -0,0 +1,4 @@
+/.idea/
+/.vs/
+/PDFMonitor_SVG/bin/
+/PDFMonitor_SVG/obj/

+ 25 - 0
PDFMonitor_SVG.sln

@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.10.35201.131
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PDFMonitor_SVG", "PDFMonitor_SVG\PDFMonitor_SVG.csproj", "{35F56CA1-72FA-447F-87CD-823573C4E270}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{35F56CA1-72FA-447F-87CD-823573C4E270}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{35F56CA1-72FA-447F-87CD-823573C4E270}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{35F56CA1-72FA-447F-87CD-823573C4E270}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{35F56CA1-72FA-447F-87CD-823573C4E270}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {E0DBE60F-F102-498A-B27D-F5F05CD4FE1B}
+	EndGlobalSection
+EndGlobal

+ 67 - 0
PDFMonitor_SVG/Entities.cs

@@ -0,0 +1,67 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using static PDFMonitor_SVG.Entities;
+
+namespace PDFMonitor_SVG {
+    internal class Entities {
+        public const int DEFAULT_DPI = 96;
+        public const double DEFAULT_RATIO = 25.4;
+
+        public static int px2mm(double px) {
+            return (int)Math.Round(px / DEFAULT_DPI * DEFAULT_RATIO);
+        }
+
+        //仿FixedThreadPool
+        public class FixedThreadPool {
+            private readonly BlockingCollection<Action> _taskQueue;
+            private readonly Thread[] _threads;
+
+            public FixedThreadPool(int size) {
+                _taskQueue = new BlockingCollection<Action>();
+                _threads = new Thread[size];
+
+                for (int i = 0; i < size; i++) {
+                    _threads[i] = new Thread(Worker);
+                    _threads[i].Start();
+                }
+            }
+
+            public void Execute(Action action) {
+                _taskQueue.Add(action);
+            }
+
+            private void Worker() {
+                foreach (var task in _taskQueue.GetConsumingEnumerable()) {
+                    task();
+                }
+            }
+
+            public void Shutdown() {
+                _taskQueue.CompleteAdding();
+                foreach (var thread in _threads) {
+                    thread.Join();
+                }
+            }
+        }
+
+
+        public class SVGTaskInfo {
+            private string width;
+            private string height;
+            private string svgPath;
+            private string svgFileName;
+
+            public SVGTaskInfo() {
+            }
+
+            public string SvgPath { get => svgPath; set => svgPath = value; }
+            public string Height { get => height; set => height = value; }
+            public string Width { get => width; set => width = value; }
+            public string SvgFileName { get => svgFileName; set => svgFileName = value; }
+        }
+    }
+}

+ 87 - 0
PDFMonitor_SVG/FormMain.Designer.cs

@@ -0,0 +1,87 @@
+namespace PDFMonitor_SVG {
+    partial class FormMain {
+        /// <summary>
+        ///  Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        ///  Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing) {
+            if (disposing && (components != null)) {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows Form Designer generated code
+
+        /// <summary>
+        ///  Required method for Designer support - do not modify
+        ///  the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent() {
+            components = new System.ComponentModel.Container();
+            btnStartService = new Button();
+            btnStopService = new Button();
+            timerGetTask = new System.Windows.Forms.Timer(components);
+            memoLog = new ListBox();
+            SuspendLayout();
+            // 
+            // btnStartService
+            // 
+            btnStartService.Location = new Point(12, 12);
+            btnStartService.Name = "btnStartService";
+            btnStartService.Size = new Size(75, 23);
+            btnStartService.TabIndex = 0;
+            btnStartService.Text = "启动服务";
+            btnStartService.UseVisualStyleBackColor = true;
+            btnStartService.Click += btnStartService_Click;
+            // 
+            // btnStopService
+            // 
+            btnStopService.Location = new Point(93, 12);
+            btnStopService.Name = "btnStopService";
+            btnStopService.Size = new Size(75, 23);
+            btnStopService.TabIndex = 1;
+            btnStopService.Text = "停止服务";
+            btnStopService.UseVisualStyleBackColor = true;
+            btnStopService.Click += btnStopService_Click;
+            // 
+            // timerGetTask
+            // 
+            timerGetTask.Interval = 2000;
+            timerGetTask.Tick += TimerGetTask_Tick;
+            // 
+            // memoLog
+            // 
+            memoLog.FormattingEnabled = true;
+            memoLog.ItemHeight = 17;
+            memoLog.Location = new Point(12, 50);
+            memoLog.Name = "memoLog";
+            memoLog.Size = new Size(762, 395);
+            memoLog.TabIndex = 5;
+            // 
+            // FormMain
+            // 
+            AutoScaleDimensions = new SizeF(7F, 17F);
+            AutoScaleMode = AutoScaleMode.Font;
+            ClientSize = new Size(786, 456);
+            Controls.Add(memoLog);
+            Controls.Add(btnStopService);
+            Controls.Add(btnStartService);
+            Name = "FormMain";
+            Text = "PDF生成服务";
+            ResumeLayout(false);
+        }
+
+        #endregion
+
+        private Button btnStartService;
+        private Button btnStopService;
+        private System.Windows.Forms.Timer timerGetTask;
+        private ListBox memoLog;
+    }
+}

+ 123 - 0
PDFMonitor_SVG/FormMain.cs

@@ -0,0 +1,123 @@
+using Newtonsoft.Json;
+using StackExchange.Redis;
+using System.Reflection;
+using WebSupergoo.ABCpdf11;
+using static PDFMonitor_SVG.Entities;
+
+namespace PDFMonitor_SVG {
+
+    public partial class FormMain : Form {
+        private ConnectionMultiplexer? redisClient;
+        private string? redisServerUrl;
+        private string? redisServerPwd;
+        private string? redisTaskKey;
+        //生成的PDF文件目录
+        private string? completedPDFPath;
+
+        //private FixedThreadPool threadPool;
+        private SynchronizationContext context;
+
+
+        public FormMain() {
+            InitializeComponent();
+            ReadServerConfig();
+            try {
+                redisClient = ConnectionMultiplexer.Connect(redisServerUrl + ",password=" + redisServerPwd);
+                memoLog.Items.Add("redis连接成功");
+            }
+            catch (Exception ex) {
+                memoLog.Items.Add("redis连接失败,请检查程序设置");
+                memoLog.Items.Add(ex.Message);
+            }
+            //threadPool = new FixedThreadPool(8); //默认8个线程
+            ThreadPool.SetMinThreads(4, 4);
+            ThreadPool.SetMaxThreads(8, 8);
+            // 获取当前的SynchronizationContext
+            context = SynchronizationContext.Current;
+
+            WebSupergoo.ABCpdf11.XSettings.InstallLicense("X/VKS0cMn8tAun4hGNvFONyWaelyItt2pqrH5srEKm4brmiSKC69N5FsGmRijGFheK9mhXoi/HVdi/VrNv0Vv1RyfQWQCg==");
+        }
+
+        ~FormMain() {
+            redisClient?.Dispose();
+            //threadPool.Shutdown();
+        }
+
+        private void ReadServerConfig() {
+            string iniPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\PDFMonitor_SVG.ini";
+            IniFile ini = new(iniPath);
+
+            redisServerUrl = ini.Read("redis", "redisServerUrl", "127.0.0.1:6379");
+            redisServerPwd = ini.Read("redis", "redisServerPwd", "Admin@dounengyin@123");
+            redisTaskKey = ini.Read("redis", "redisTaskKey", "sdtool:mall:designer:svg:task:list:1000");
+
+            completedPDFPath = ini.Read("path", "completedPDFPath", "D:\\testPdfOut\\");
+            if (!Directory.Exists(completedPDFPath)) {
+                Directory.CreateDirectory(completedPDFPath);
+            }
+        }
+
+        private void TimerGetTask_Tick(object sender, EventArgs e) {
+            RedisValue taskInfo = redisClient.GetDatabase().ListLeftPop(redisTaskKey);
+            if (!taskInfo.IsNullOrEmpty) {
+                // 有任务了,干活  
+                SVGTaskInfo svgTaskInfo = JsonConvert.DeserializeObject<SVGTaskInfo>(taskInfo);
+                if (svgTaskInfo == null) return;
+
+                memoLog.Items.Add("获取任务[" + svgTaskInfo.SvgFileName + "]成功");
+                //todo 
+                //threadPool.Execute(() => {
+                //    doConvertTask(svgTaskInfo);
+                //});
+                ThreadPool.QueueUserWorkItem(new WaitCallback(doConvertTask), svgTaskInfo);
+            }
+        }
+
+        private void doConvertTask(object state) {
+            SVGTaskInfo svgTaskInfo = (SVGTaskInfo)state;
+            context.Post(_ => { memoLog.Items.Add("任务[" + svgTaskInfo.SvgFileName + "]开始"); }, null);
+            //传过来的是px,转成mm
+            int fileWidth = px2mm(double.Parse(svgTaskInfo.Width));
+            int fileHeight = px2mm(double.Parse(svgTaskInfo.Height));
+            int fileWidthPx = (int)Math.Ceiling(double.Parse(svgTaskInfo.Width));
+
+            Doc doc = new Doc();
+            doc.Units = UnitType.Mm;
+            //doc.MediaBox.Top = 0;
+            //doc.MediaBox.Left = 0;
+            //doc.MediaBox.Right = fileWidth;
+            //doc.MediaBox.Bottom = fileHeight;
+            string mediaBoxStr = "0 0 " + fileWidth + " " + fileHeight;
+            doc.MediaBox.String = mediaBoxStr;
+            doc.MediaBox.Pin = XRect.Corner.TopLeft;
+            doc.Rect.String = doc.MediaBox.String;
+            doc.Rect.Pin = XRect.Corner.TopLeft;
+            doc.HtmlOptions.Media = MediaType.Print;
+
+            string convertedFileName = Path.ChangeExtension(svgTaskInfo.SvgFileName, ".pdf");
+            string convertedFilePath = completedPDFPath + convertedFileName;
+
+            doc.AddImageUrl(svgTaskInfo.SvgPath, true, fileWidthPx, true);
+            doc.Save(convertedFilePath);
+            doc.Clear();
+            doc.Dispose();
+
+            context.Post(_ => { memoLog.Items.Add("任务[" + svgTaskInfo.SvgFileName + "]完成"); }, null);
+        }
+
+        private void btnStartService_Click(object sender, EventArgs e) {
+            if (redisClient == null) {
+                memoLog.Items.Add("redis客户端未连接,请修正程序设置后重启");
+                return;
+            }
+            memoLog.Items.Add("服务已启动");
+
+            timerGetTask.Start();
+        }
+
+        private void btnStopService_Click(object sender, EventArgs e) {
+            timerGetTask.Stop();
+            memoLog.Items.Add("服务已停止");
+        }
+    }
+}

+ 123 - 0
PDFMonitor_SVG/FormMain.resx

@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!--
+    Microsoft ResX Schema
+
+    Version 2.0
+
+    The primary goals of this format is to allow a simple XML format
+    that is mostly human readable. The generation and parsing of the
+    various data types are done through the TypeConverter classes
+    associated with the data types.
+
+    Example:
+
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+
+    There are any number of "resheader" rows that contain simple
+    name/value pairs.
+
+    Each data row contains a name, and value. The row also contains a
+    type or mimetype. Type corresponds to a .NET class that support
+    text/value conversion through the TypeConverter architecture.
+    Classes that don't support this are serialized and stored with the
+    mimetype set.
+
+    The mimetype is used for serialized objects, and tells the
+    ResXResourceReader how to depersist the object. This is currently not
+    extensible. For a given mimetype the value must be set accordingly:
+
+    Note - application/x-microsoft.net.object.binary.base64 is the format
+    that the ResXResourceWriter will generate, however the reader can
+    read any of the formats listed below.
+
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <metadata name="timerGetTask.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>17, 17</value>
+  </metadata>
+</root>

+ 32 - 0
PDFMonitor_SVG/IniFile.cs

@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PDFMonitor_SVG {
+    internal class IniFile {
+        private string filePath;
+
+        [DllImport("kernel32", CharSet = CharSet.Unicode)]
+        private static extern long WritePrivateProfileString(string section, string key, string value, string filePath);
+
+        [DllImport("kernel32", CharSet = CharSet.Unicode)]
+        private static extern int GetPrivateProfileString(string section, string key, string defaultValue, StringBuilder retVal, int size, string filePath);
+
+        public IniFile(string filePath) {
+            this.filePath = filePath;
+        }
+
+        public string Read(string section, string key, string defaultValue = "") {
+            var retVal = new StringBuilder(255);
+            GetPrivateProfileString(section, key, defaultValue, retVal, 255, filePath);
+            return retVal.ToString();
+        }
+
+        public void Write(string section, string key, string value) {
+            WritePrivateProfileString(section, key, value, filePath);
+        }
+    }
+}

+ 38 - 0
PDFMonitor_SVG/PDFMonitor_SVG.csproj

@@ -0,0 +1,38 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>WinExe</OutputType>
+    <TargetFramework>net6.0-windows</TargetFramework>
+    <Nullable>enable</Nullable>
+    <UseWindowsForms>true</UseWindowsForms>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <UseWPF>False</UseWPF>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
+    <PackageReference Include="StackExchange.Redis" Version="2.8.0" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Reference Include="ABCpdf">
+      <HintPath>..\..\DelphiXE10Components\ABCpdf11.3\ABCpdf.dll</HintPath>
+    </Reference>
+  </ItemGroup>
+
+  <ItemGroup>
+    <Compile Update="Properties\Settings.Designer.cs">
+      <DesignTimeSharedInput>True</DesignTimeSharedInput>
+      <AutoGen>True</AutoGen>
+      <DependentUpon>Settings.settings</DependentUpon>
+    </Compile>
+  </ItemGroup>
+
+  <ItemGroup>
+    <None Update="Properties\Settings.settings">
+      <Generator>SettingsSingleFileGenerator</Generator>
+      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
+    </None>
+  </ItemGroup>
+
+</Project>

+ 8 - 0
PDFMonitor_SVG/PDFMonitor_SVG.csproj.user

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Compile Update="FormMain.cs">
+      <SubType>Form</SubType>
+    </Compile>
+  </ItemGroup>
+</Project>

+ 14 - 0
PDFMonitor_SVG/Program.cs

@@ -0,0 +1,14 @@
+namespace PDFMonitor_SVG {
+    internal static class Program {
+        /// <summary>
+        ///  The main entry point for the application.
+        /// </summary>
+        [STAThread]
+        static void Main() {
+            // To customize application configuration such as set high DPI settings or default font,
+            // see https://aka.ms/applicationconfiguration.
+            ApplicationConfiguration.Initialize();
+            Application.Run(new FormMain());
+        }
+    }
+}

+ 26 - 0
PDFMonitor_SVG/Properties/Settings.Designer.cs

@@ -0,0 +1,26 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     此代码由工具生成。
+//     运行时版本:4.0.30319.42000
+//
+//     对此文件的更改可能会导致不正确的行为,并且如果
+//     重新生成代码,这些更改将会丢失。
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace PDFMonitor_SVG.Properties {
+    
+    
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.11.0.0")]
+    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+        
+        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+        
+        public static Settings Default {
+            get {
+                return defaultInstance;
+            }
+        }
+    }
+}

+ 6 - 0
PDFMonitor_SVG/Properties/Settings.settings

@@ -0,0 +1,6 @@
+<?xml version='1.0' encoding='utf-8'?>
+<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
+  <Profiles>
+    <Profile Name="(Default)" />
+  </Profiles>
+</SettingsFile>