Ver Fonte

报表打印服务客户端初始化版本

lhy há 1 ano atrás
pai
commit
e99c8fb703
46 ficheiros alterados com 4041 adições e 0 exclusões
  1. 33 0
      PrintServer/.gitignore
  2. BIN
      PrintServer/.mvn/wrapper/maven-wrapper.jar
  3. 2 0
      PrintServer/.mvn/wrapper/maven-wrapper.properties
  4. 42 0
      PrintServer/README.md
  5. BIN
      PrintServer/dll/jacob-1.20-x64.dll
  6. BIN
      PrintServer/dll/jacob-1.20-x86.dll
  7. 326 0
      PrintServer/innoSetup5/ChineseSimplifiedFor5.isl
  8. 94 0
      PrintServer/innoSetup5/template.iss
  9. BIN
      PrintServer/lib/jacob.jar
  10. BIN
      PrintServer/lib/jxbrowser-7.21.jar
  11. BIN
      PrintServer/lib/jxbrowser-javafx-7.21.jar
  12. BIN
      PrintServer/lib/jxbrowser-win64-7.21.jar
  13. 23 0
      PrintServer/logs/printServer.log
  14. BIN
      PrintServer/logs/printServer.log.2023-07-13.0.gz
  15. BIN
      PrintServer/logs/printServer.log.2023-07-14.0.gz
  16. 308 0
      PrintServer/mvnw
  17. 205 0
      PrintServer/mvnw.cmd
  18. 215 0
      PrintServer/pom.xml
  19. 173 0
      PrintServer/src/main/java/com/jd/printserver/PrintServerApplication.java
  20. 20 0
      PrintServer/src/main/java/com/jd/printserver/common/advice/ExceptionAdvice.java
  21. 179 0
      PrintServer/src/main/java/com/jd/printserver/common/constants/Constants.java
  22. 94 0
      PrintServer/src/main/java/com/jd/printserver/common/constants/HttpStatus.java
  23. 97 0
      PrintServer/src/main/java/com/jd/printserver/common/controller/BaseController.java
  24. 188 0
      PrintServer/src/main/java/com/jd/printserver/common/domain/AjaxResult.java
  25. 96 0
      PrintServer/src/main/java/com/jd/printserver/controller/PrintServerController.java
  26. 15 0
      PrintServer/src/main/java/com/jd/printserver/javafx/controller/MainController.java
  27. 237 0
      PrintServer/src/main/java/com/jd/printserver/javafx/controller/SettingController.java
  28. 63 0
      PrintServer/src/main/java/com/jd/printserver/javafx/entity/PrintParam.java
  29. 9 0
      PrintServer/src/main/java/com/jd/printserver/javafx/view/MainView.java
  30. 9 0
      PrintServer/src/main/java/com/jd/printserver/javafx/view/SettingView.java
  31. 20 0
      PrintServer/src/main/java/com/jd/printserver/utils/ArrayUtils.java
  32. 188 0
      PrintServer/src/main/java/com/jd/printserver/utils/DateUtils.java
  33. 139 0
      PrintServer/src/main/java/com/jd/printserver/utils/FileUtils.java
  34. 62 0
      PrintServer/src/main/java/com/jd/printserver/utils/ImageUtils.java
  35. 151 0
      PrintServer/src/main/java/com/jd/printserver/utils/JxbrowserUtils.java
  36. 70 0
      PrintServer/src/main/java/com/jd/printserver/utils/PortAvailableUtils.java
  37. 239 0
      PrintServer/src/main/java/com/jd/printserver/utils/PrintUtils.java
  38. 611 0
      PrintServer/src/main/java/com/jd/printserver/utils/StringUtils.java
  39. 5 0
      PrintServer/src/main/java/com/jd/printserver/utils/Url2DataInterface.java
  40. 0 0
      PrintServer/src/main/resources/application.properties
  41. 21 0
      PrintServer/src/main/resources/application.yml
  42. BIN
      PrintServer/src/main/resources/icon/report_print.ico
  43. BIN
      PrintServer/src/main/resources/images/report_print.png
  44. 14 0
      PrintServer/src/main/resources/view/main.fxml
  45. 80 0
      PrintServer/src/main/resources/view/setting.fxml
  46. 13 0
      PrintServer/src/test/java/com/jd/printserver/PrintServerApplicationTests.java

+ 33 - 0
PrintServer/.gitignore

@@ -0,0 +1,33 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/

BIN
PrintServer/.mvn/wrapper/maven-wrapper.jar


+ 2 - 0
PrintServer/.mvn/wrapper/maven-wrapper.properties

@@ -0,0 +1,2 @@
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.2/apache-maven-3.9.2-bin.zip
+wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar

+ 42 - 0
PrintServer/README.md

@@ -0,0 +1,42 @@
+## 本地仓库添加依赖包
+```bash
+mvn install:install-file -Dfile=G:\WorkSoft\STS\workspace\PrintServer\lib\jacob.jar -DgroupId=com.jacob -DartifactId=jacob  -Dversion=1.20  -Dpackaging=jar
+
+mvn install:install-file -Dfile=G:\WorkSoft\STS\workspace\PrintServer\lib\jxbrowser-7.21.jar -DgroupId=com.teamdev.jxbrowser -DartifactId=jxbrowser  -Dversion=7.21  -Dpackaging=jar
+
+mvn install:install-file -Dfile=G:\WorkSoft\STS\workspace\PrintServer\lib\jxbrowser-win64-7.21.jar -DgroupId=com.teamdev.jxbrowser -DartifactId=jxbrowser-win64  -Dversion=7.21  -Dpackaging=jar
+
+mvn install:install-file -Dfile=G:\WorkSoft\STS\workspace\PrintServer\lib\jxbrowser-javafx-7.21.jar -DgroupId=com.teamdev.jxbrowser -DartifactId=jxbrowser-javafx  -Dversion=7.21  -Dpackaging=jar
+
+```
+## 启动方式
+# a.springpoot方式
+
+# b.javafx命令方式
+```bash
+# 在项目根目录执行 mvn jfx:run
+mvn jfx:run
+```
+
+## 打包方式
+# a.springpoot方式
+
+# b.javafx命令方式
+```bash
+# 在项目根目录执行  mvn jfx:native 或者 mvn package jfx:native 打包项目;
+# 电脑里安装了 InnoSetup 的话它还会打包为一个点击下一步,下一步类似的安装的程序
+mvn jfx:native
+```
+
+## 汉化和添加注册表
+#
+# 1.安装InnoSetup5软件,添加汉化文件,例
+# --模版文件 {项目根目录}\innoSetup5\ChineseSimplifiedFor5.isl
+# --InnoSetup5添加汉化文件: 
+# F:\WorkSoft\InnoSetup5\Languages\ChineseSimplifiedFor5.isl
+#
+# 2.修改汉化模板
+# 用压缩软件打开ant-javafx.jar修改com\oracle\tools\packager\windows\template.iss文件(InnoSetup5安装模板文件),例
+# --模版文件 {项目根目录}\innoSetup5\template.iss
+# --ant-javafx.jar修改InnoSetup5安装模板文件(注意:GBK格式,其他格式会出现乱码问题):
+# F:\WorkSoft\Java\jdk1.8.0_231\lib\ant-javafx.jar!\com\oracle\tools\packager\windows\template.iss

BIN
PrintServer/dll/jacob-1.20-x64.dll


BIN
PrintServer/dll/jacob-1.20-x86.dll


+ 326 - 0
PrintServer/innoSetup5/ChineseSimplifiedFor5.isl

@@ -0,0 +1,326 @@
+; *** Inno Setup 版本 6.0.3+ 简体中文消息 ***
+[LangOptions]
+LanguageName=<7B80><4F53><4E2D><6587>
+LanguageID=$0804
+LanguageCodePage=936
+; 下列条目用来定义安装程序界面的字体和大小。
+DialogFontName=宋体
+DialogFontSize=9
+WelcomeFontName=宋体
+WelcomeFontSize=12
+TitleFontName=宋体
+TitleFontSize=29
+CopyrightFontName=宋体
+CopyrightFontSize=9
+
+[Messages]
+
+; *** 应用程序标题
+SetupAppTitle=安装
+SetupWindowTitle=安装 - %1
+UninstallAppTitle=卸载
+UninstallAppFullTitle=%1 卸载
+
+; *** Misc. common
+InformationTitle=信息
+ConfirmTitle=确认
+ErrorTitle=错误
+
+; *** 安装错误消息
+SetupLdrStartupMessage=现在将安装 %1。您想要继续吗?
+LdrCannotCreateTemp=不能创建临时文件。安装中断。
+LdrCannotExecTemp=不能执行临时目录中的文件。安装中断。
+
+; *** 启动错误消息
+LastErrorMessage=%1.%n%n错误 %2: %3
+SetupFileMissing=安装目录中的文件 %1 丢失。请修正这个问题或获取一个新的程序副本。
+SetupFileCorrupt=安装文件被破坏。请获取一个新的程序副本。
+SetupFileCorruptOrWrongVer=安装文件被破坏,或是与这个安装程序的版本不兼容。请修正这个问题或获取新的程序副本。
+InvalidParameter=无效的命令行参数: %n%n%1
+SetupAlreadyRunning=安装程序正在运行。
+WindowsVersionNotSupported=这个程序不支持该版本的计算机运行。
+WindowsServicePackRequired=这个程序要求%1服务包%1或更高。
+NotOnThisPlatform=这个程序将不能运行于 %1。
+OnlyOnThisPlatform=这个程序必须运行于 %1。
+OnlyOnTheseArchitectures=这个程序只能在为下列处理器结构设计的 Windows 版本中进行安装:%n%n%1
+MissingWOW64APIs=您正在运行的Windows版本不包括安装程序执行64位安装所需的功能。若要纠正此问题,请安装服务包 %1.
+WinVersionTooLowError=这个程序需要 %1 版本 %2 或更高。
+WinVersionTooHighError=这个程序不能安装于 %1 版本 %2 或更高。
+AdminPrivilegesRequired=在安装这个程序时您必须以管理员身份登录。
+PowerUserPrivilegesRequired=在安装这个程序时您必须以管理员身份或有权限的用户组身份登录。
+SetupAppRunningError=安装程序发现 %1 当前正在运行。%n%n请先关闭所有运行的窗口,然后单击“确定”继续,或按“取消”退出。
+UninstallAppRunningError=卸载程序发现 %1 当前正在运行。%n%n请先关闭所有运行的窗口,然后单击“确定”继续,或按“取消”退出。
+
+; *** 其他错误
+ErrorCreatingDir=安装程序不能创建目录“%1”。
+ErrorTooManyFilesInDir=不能在目录“%1”中创建文件,因为里面的文件太多
+
+; *** 安装程序公共消息
+ExitSetupTitle=退出安装程序
+ExitSetupMessage=安装程序未完成安装。如果您现在退出,您的程序将不能安装。%n%n您可以以后再运行安装程序完成安装。%n%n退出安装程序吗?
+AboutSetupMenuItem=关于安装程序(&A)...
+AboutSetupTitle=关于安装程序
+AboutSetupMessage=%1 版本 %2%n%3%n%n%1 主页:%n%4
+AboutSetupNote=
+TranslatorNote=
+
+; *** 按钮
+ButtonBack=< 上一步(&B)
+ButtonNext=下一步(&N) >
+ButtonInstall=安装(&I)
+ButtonOK=确定
+ButtonCancel=取消
+ButtonYes=是(&Y)
+ButtonYesToAll=全是(&A)
+ButtonNo=否(&N)
+ButtonNoToAll=全否(&O)
+ButtonFinish=完成(&F)
+ButtonBrowse=浏览(&B)...
+ButtonWizardBrowse=浏览(&R)...
+ButtonNewFolder=新建文件夹(&M)
+
+; *** “选择语言”对话框消息
+SelectLanguageTitle=选择安装语言
+SelectLanguageLabel=选择安装时要使用的语言。
+
+; *** 公共向导文字
+ClickNext=单击“下一步”继续,或单击“取消”退出安装程序。
+BeveledLabel=
+BrowseDialogTitle=浏览文件夹
+BrowseDialogLabel=在下列列表中选择一个文件夹,然后单击“确定”。
+NewFolderName=新建文件夹
+
+; *** “欢迎”向导页
+WelcomeLabel1=欢迎使用 [name] 安装向导
+WelcomeLabel2=现在将安装 [name/ver] 到您的电脑中。%n%n推荐您在继续安装前关闭所有其他应用程序。
+
+; *** “密码”向导页
+WizardPassword=密码
+PasswordLabel1=这个安装程序有密码保护。
+PasswordLabel3=请输入密码,然后单击“下一步”继续。密码区分大小写。
+PasswordEditLabel=密码(&P):
+IncorrectPassword=您输入的密码不正确,请重试。
+
+; *** “许可协议”向导页
+WizardLicense=许可协议
+LicenseLabel=继续安装前请阅读下列重要信息。
+LicenseLabel3=请仔细阅读下列许可协议。您在继续安装前必须同意这些协议条款。
+LicenseAccepted=我同意此协议(&A)
+LicenseNotAccepted=我不同意此协议(&D)
+
+; *** “信息”向导页
+WizardInfoBefore=信息
+InfoBeforeLabel=请在继续安装前阅读下列重要信息。
+InfoBeforeClickLabel=如果您想继续安装,单击“下一步”。
+WizardInfoAfter=信息
+InfoAfterLabel=请在继续安装前阅读下列重要信息。
+InfoAfterClickLabel=如果您想继续安装,单击“下一步”。
+
+; *** “用户信息”向导页
+WizardUserInfo=用户信息
+UserInfoDesc=请输入您的信息。
+UserInfoName=用户名(&U):
+UserInfoOrg=组织(&O):
+UserInfoSerial=序列号(&S):
+UserInfoNameRequired=您必须输入名字。
+
+; *** “选择目标目录”向导面
+WizardSelectDir=选择目标位置
+SelectDirDesc=您想将 [name] 安装在什么地方?
+SelectDirLabel3=安装程序将安装 [name] 到下列文件夹中。
+SelectDirBrowseLabel=单击“下一步”继续。如果您想选择其他文件夹,单击“浏览”。
+DiskSpaceMBLabel=至少需要有 [mb] MB 的可用磁盘空间。
+CannotInstallToNetworkDrive=安装程序无法安装到一个网络驱动器。
+CannotInstallToUNCPath=安装程序无法安装到一个UNC路径。
+InvalidPath=您必须输入一个带驱动器卷标的完整路径,例如:%n%nC:\APP%n%n或下列形式的 UNC 路径:%n%n\\server\share
+InvalidDrive=您选定的驱动器或 UNC 共享不存在或不能访问。请选选择其他位置。
+DiskSpaceWarningTitle=没有足够的磁盘空间
+DiskSpaceWarning=安装程序至少需要 %1 KB 的可用空间才能安装,但选定驱动器只有 %2 KB 的可用空间。%n%n您一定要继续吗?
+DirNameTooLong=文件夹名或路径太长。
+InvalidDirName=文件夹名是无效的。
+BadDirName32=文件夹名不能包含下列任何字符:%n%n%1
+DirExistsTitle=文件夹存在
+DirExists=文件夹:%n%n%1%n%n已经存在。您一定要安装到这个文件夹中吗?
+DirDoesntExistTitle=文件夹不存在
+DirDoesntExist=文件夹:%n%n%1%n%n不存在。您想要创建此目录吗?
+
+; *** “选择组件”向导页
+WizardSelectComponents=选择组件
+SelectComponentsDesc=您想安装哪些程序的组件?
+SelectComponentsLabel2=选择您想要安装的组件;清除您不想安装的组件。然后单击“下一步”继续。
+FullInstallation=完全安装
+; if possible don't translate 'Compact' as 'Minimal' (I mean 'Minimal' in your language)
+CompactInstallation=简洁安装
+CustomInstallation=自定义安装
+NoUninstallWarningTitle=组件存在
+NoUninstallWarning=安装程序侦测到下列组件已在您的电脑中安装。:%n%n%1%n%n取消选定这些组件将不能卸载它们。%n%n您一定要继续吗?
+ComponentSize1=%1 KB
+ComponentSize2=%1 MB
+ComponentsDiskSpaceMBLabel=当前选择的组件至少需要 [mb] MB 的磁盘空间。
+
+; *** “选择附加任务”向导页
+WizardSelectTasks=选择附加任务
+SelectTasksDesc=您想要安装程序执行哪些附加任务?
+SelectTasksLabel2=选择您想要安装程序在安装 [name] 时执行的附加任务,然后单击“下一步”。
+
+; *** “选择开始菜单文件夹”向导页
+WizardSelectProgramGroup=选择开始菜单文件夹
+SelectStartMenuFolderDesc=您想在哪里放置程序的快捷方式?
+SelectStartMenuFolderLabel3=安装程序现在将在下列开始菜单文件夹中创建程序的快捷方式。
+SelectStartMenuFolderBrowseLabel=单击“下一步”继续。如果您想选择其他文件夹,单击“浏览”。
+MustEnterGroupName=您必须输入一个文件夹名。
+GroupNameTooLong=文件夹名或路径太长。
+InvalidGroupName=文件夹名是无效的。
+BadGroupName=文件夹名不能包含下列任何字符:%n%n%1
+NoProgramGroupCheck2=不创建开始菜单文件夹(&D)
+
+; *** “准备安装”向导页
+WizardReady=准备安装
+ReadyLabel1=安装程序现在准备开始安装 [name] 到您的电脑中。
+ReadyLabel2a=单击“安装”继续此安装程序。如果您想要回顾或改变设置,请单击“上一步”。
+ReadyLabel2b=单击“安装”继续此安装程序?
+ReadyMemoUserInfo=用户信息:
+ReadyMemoDir=目标位置:
+ReadyMemoType=安装类型:
+ReadyMemoComponents=选定组件:
+ReadyMemoGroup=开始菜单文件夹:
+ReadyMemoTasks=附加任务:
+
+; *** “正在准备安装”向导页
+WizardPreparing=正在准备安装
+PreparingDesc=安装程序正在准备安装 [name] 到您的电脑中。
+PreviousInstallNotCompleted=先前程序的安装/卸载未完成。您需要重新启动您的电脑才能完成安装。%n%n在重新启动电脑后,再运行安装完成 [name] 的安装。
+CannotContinue=安装程序不能继续。请单击“取消”退出。
+ApplicationsFound=下列应用程序正在使用的文件需要更新设置。它是建议您允许安装程序自动关闭这些应用程序。
+ApplicationsFound2=下列应用程序正在使用的文件需要更新设置。它是建议您允许安装程序自动关闭这些应用程序。安装完成后,安装程序将尝试重新启动应用程序。
+CloseApplications=自动关闭该应用程序(&A)
+DontCloseApplications=不要关闭该应用程序(D)
+ErrorCloseApplications=安装程序无法自动关闭所有应用程序。在继续之前,我们建议您关闭所有使用需要更新的安装程序文件。
+
+; *** “正在安装”向导页
+WizardInstalling=正在安装
+InstallingLabel=安装程序正在安装 [name] 到您的电脑中,请等待。
+
+; *** “安装完成”向导页
+FinishedHeadingLabel=[name] 安装向导完成
+FinishedLabelNoIcons=安装程序已在您的电脑中安装了 [name]。
+FinishedLabel=安装程序已在您的电脑中安装了 [name]。此应用程序可以通过选择安装的快捷方式运行。
+ClickFinish=单击“完成”退出安装程序。
+FinishedRestartLabel=要完成 [name] 的安装,安装程序必须重新启动您的电脑。您想现在重新启动吗?
+FinishedRestartMessage=要完成 [name] 的安装,安装程序必须重新启动您的电脑。%n%n您想现在重新启动吗?
+ShowReadmeCheck=是,您想查阅自述文件
+YesRadio=是,立即重新启动电脑(&Y)
+NoRadio=否,稍后重新启动电脑(&N)
+; 用于象“运行 MyProg.exe”
+RunEntryExec=运行 %1
+; 用于象“查阅 Readme.txt”
+RunEntryShellExec=查阅 %1
+
+; *** “安装程序需要下一张磁盘”提示
+ChangeDiskTitle=安装程序需要下一张磁盘
+SelectDiskLabel2=请插入磁盘 %1 并单击“确定”。%n%n如果这个磁盘中的文件不能在不同于下列显示的文件夹中找到,输入正确的路径或单击“浏览”。
+PathLabel=路径(&P):
+FileNotInDir2=文件“%1”不能在“%2”定位。请插入正确的磁盘或选择其他文件夹。
+SelectDirectoryLabel=请指定下一张磁盘的位置。
+
+; *** 安装状态消息
+SetupAborted=安装程序未完成安装。%n%n请修正这个问题并重新运行安装程序。
+EntryAbortRetryIgnore=单击“重试”再次尝试,单击“忽略”继续进行,或单击“中止”取消安装。
+
+; *** 安装状态消息
+StatusClosingApplications=正在关闭应用程序...
+StatusCreateDirs=正在创建目录...
+StatusExtractFiles=正在解压缩文件...
+StatusCreateIcons=正在创建快捷方式...
+StatusCreateIniEntries=正在创建 INI 条目...
+StatusCreateRegistryEntries=正在创建注册表条目...
+StatusRegisterFiles=正在注册文件...
+StatusSavingUninstall=正在保存卸载信息...
+StatusRunProgram=正在完成安装...
+StatusRestartingApplications=正在重启应用程序...
+StatusRollback=正在撤销更改...
+
+; *** 其他错误
+ErrorInternal2=内部错误: %1
+ErrorFunctionFailedNoCode=%1 失败
+ErrorFunctionFailed=%1 失败;代码 %2
+ErrorFunctionFailedWithMessage=%1 失败;代码 %2.%n%3
+ErrorExecutingProgram=不能执行文件:%n%1
+
+; *** 注册表错误
+ErrorRegOpenKey=错误打开注册表键:%n%1\%2
+ErrorRegCreateKey=错误创建注册表键:%n%1\%2
+ErrorRegWriteKey=错误写入注册表键:%n%1\%2
+
+; *** INI 错误
+ErrorIniEntry=在文件“%1”创建 INI 项目错误。
+
+; *** 文件复制错误
+FileAbortRetryIgnore=单击“重试”重试,单击“忽略”跳过此文件(不推荐),单击“中止”取消安装。
+FileAbortRetryIgnore2=单击“重试”重试,单击“忽略”继续(不推荐),或单击“中止”取消安装。
+SourceIsCorrupted=源文件被破坏
+SourceDoesntExist=源文件“%1”不存在
+ExistingFileReadOnly=现有的文件不能被替换,因为它是只读的。
+ErrorReadingExistingDest=尝试读了现有的文件时发生一个错误:
+FileExists=文件已经存在。
+ExistingFileNewer=现有文件比尝试安装的安装程序更新。
+ErrorChangingAttr=尝试改变下列现有的文件的属性时发生一个错误:
+ErrorCreatingTemp=尝试在目标目录创建文件时发生一个错误:
+ErrorReadingSource=尝试读取下列源文件时发生一个错误:
+ErrorCopying=尝试复制下列文件时发生一个错误:
+ErrorReplacingExistingFile=尝试替换现有的文件时发生错误:
+ErrorRestartReplace=重启电脑后替换文件失败:
+ErrorRenamingTemp=尝试重新命名以下目标目录中的一个文件时发生错误:
+ErrorRegisterServer=不能注册 DLL/OCX: %1
+ErrorRegSvr32Failed=RegSvr32 失败;退出代码 %1
+ErrorRegisterTypeLib=不能注册类型库: %1
+
+; *** 安装后错误
+ErrorOpeningReadme=当尝试打开自述文件时发生一个错误。
+ErrorRestartingComputer=安装程序不能重新启动电脑,请手动重启。
+
+; *** 卸载消息
+UninstallNotFound=文件“%1”不存在。不能卸载。
+UninstallOpenError=文件“%1”不能打开。不能卸载
+UninstallUnsupportedVer=卸载日志文件“%1”有未被这个版本的卸载器承认的格式。不能卸载
+UninstallUnknownEntry=在卸载日志中遇到一个未知的条目 (%1)
+ConfirmUninstall=您确认想要完全删除 %1 及它的所有组件吗?
+UninstallOnlyOnWin64=这个安装程序只能在 64 位 Windows 中进行卸载。
+OnlyAdminCanUninstall=这个安装的程序只能是有管理员权限的用户才能卸载。
+UninstallStatusLabel=正在从您的电脑中删除 %1,请等待。
+UninstalledAll=%1 已顺利地从您的电脑中删除。
+UninstalledMost=%1 卸载完成。%n%n有一些内容不能被删除。您可以手工删除它们。
+UninstalledAndNeedsRestart=要完成 %1 的卸载,您的电脑必须重新启动。%n%n您现在想重新启动电脑吗?
+UninstallDataCorrupted=“%1”文件被破坏,不能卸载
+
+; *** 卸载状态消息
+ConfirmDeleteSharedFileTitle=删除共享文件吗?
+ConfirmDeleteSharedFile2=系统中包含的下列共享文件已经不被其他程序使用。您想要卸载程序删除这些共享文件吗?%n%n如果这些文件被删除,但还有程序正在使用这些文件,这些程序可能不能正确执行。如果您不能确定,选择“否”。把这些文件保留在系统中以免引起问题。
+SharedFileNameLabel=文件名:
+SharedFileLocationLabel=位置:
+WizardUninstalling=卸载状态
+StatusUninstalling=正在卸载 %1...
+
+; *** Shutdown block reasons
+ShutdownBlockReasonInstallingApp=正在安装 %1.
+ShutdownBlockReasonUninstallingApp=正在卸载 %1.
+
+; The custom messages below aren't used by Setup itself, but if you make
+; use of them in your scripts, you'll want to translate them.
+
+[CustomMessages]
+
+NameAndVersion=%1 版本 %2
+AdditionalIcons=附加快捷方式:
+CreateDesktopIcon=创建桌面快捷方式(&D)
+CreateQuickLaunchIcon=创建快速运行栏快捷方式(&Q)
+ProgramOnTheWeb=%1 网站
+UninstallProgram=卸载 %1
+LaunchProgram=运行 %1
+AssocFileExtension=将 %2 文件扩展名与 %1 建立关联(&A)
+AssocingFileExtension=正在将 %2 文件扩展名与 %1 建立关联...
+AutoStartProgramGroupDescription=启动组:
+AutoStartProgram=自动启动 %1
+AddonHostProgramNotFound=%1无法找到您所选择的文件夹。%n%n你想继续吗?
+
+

+ 94 - 0
PrintServer/innoSetup5/template.iss

@@ -0,0 +1,94 @@
+;This file will be executed next to the application bundle image
+;I.e. current directory will contain folder APPLICATION_NAME with application files
+[Setup]
+AppId={{PRODUCT_APP_IDENTIFIER}}
+AppName=APPLICATION_NAME
+AppVersion=APPLICATION_VERSION
+AppVerName=APPLICATION_NAME APPLICATION_VERSION
+AppPublisher=APPLICATION_VENDOR
+AppComments=APPLICATION_COMMENTS
+AppCopyright=APPLICATION_COPYRIGHT
+;AppPublisherURL=http://java.com/
+;AppSupportURL=http://java.com/
+;AppUpdatesURL=http://java.com/
+DefaultDirName=APPLICATION_INSTALL_ROOT\APPLICATION_NAME
+DisableStartupPrompt=Yes
+DisableDirPage=DISABLE_DIR_PAGE
+DisableProgramGroupPage=Yes
+DisableReadyPage=Yes
+DisableFinishedPage=Yes
+DisableWelcomePage=Yes
+DefaultGroupName=APPLICATION_GROUP
+;Optional License
+LicenseFile=APPLICATION_LICENSE_FILE
+;(Windows 2000/XP/Server 2003 are no longer supported.)
+MinVersion=6.0
+OutputBaseFilename=INSTALLER_FILE_NAME
+Compression=lzma
+SolidCompression=yes
+PrivilegesRequired=APPLICATION_INSTALL_PRIVILEGE
+SetupIconFile=APPLICATION_NAME\APPLICATION_NAME.ico
+UninstallDisplayIcon={app}\APPLICATION_NAME.ico
+UninstallDisplayName=APPLICATION_NAME
+WizardImageStretch=No
+WizardSmallImageFile=APPLICATION_NAME-setup-icon.bmp   
+ArchitecturesInstallIn64BitMode=ARCHITECTURE_BIT_MODE
+FILE_ASSOCIATIONS
+
+[Languages]
+; update 2
+;Name: "english"; MessagesFile: "compiler:Default.isl"
+Name: "chinesesimplifiedfor5"; MessagesFile: "compiler:Languages\ChineseSimplifiedFor5.isl"
+;
+
+[Files]
+Source: "APPLICATION_NAME\APPLICATION_NAME.exe"; DestDir: "{app}"; Flags: ignoreversion
+Source: "APPLICATION_NAME\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
+
+[Icons]
+Name: "{group}\APPLICATION_NAME"; Filename: "{app}\APPLICATION_NAME.exe"; IconFilename: "{app}\APPLICATION_NAME.ico"; Check: APPLICATION_MENU_SHORTCUT()
+Name: "{commondesktop}\APPLICATION_NAME"; Filename: "{app}\APPLICATION_NAME.exe";  IconFilename: "{app}\APPLICATION_NAME.ico"; Check: APPLICATION_DESKTOP_SHORTCUT()
+SECONDARY_LAUNCHERS
+
+[Run]
+Filename: "{app}\RUN_FILENAME.exe"; Parameters: "-Xappcds:generatecache"; Check: APPLICATION_APP_CDS_INSTALL()
+Filename: "{app}\RUN_FILENAME.exe"; Description: "{cm:LaunchProgram,APPLICATION_NAME}"; Flags: nowait postinstall skipifsilent; Check: APPLICATION_NOT_SERVICE()
+Filename: "{app}\RUN_FILENAME.exe"; Parameters: "-install -svcName ""APPLICATION_NAME"" -svcDesc ""APPLICATION_DESCRIPTION"" -mainExe ""APPLICATION_LAUNCHER_FILENAME"" START_ON_INSTALL RUN_AT_STARTUP"; Check: APPLICATION_SERVICE()
+
+[UninstallRun]
+; update 3
+Filename: taskkill;Parameters:"/t /f /im RUN_FILENAME.exe";Flags: runhidden
+;
+Filename: "{app}\RUN_FILENAME.exe "; Parameters: "-uninstall -svcName APPLICATION_NAME STOP_ON_UNINSTALL"; Check: APPLICATION_SERVICE()
+
+; update 4
+[UninstallDelete]
+Type: filesandordirs; Name: "{app}"
+;
+
+; update 5
+[Registry]
+Root: HKCU; Subkey:"SOFTWARE\Microsoft\Windows\CurrentVersion\Run"; ValueType: string; ValueName: "RUN_FILENAME";ValueData: "{app}\RUN_FILENAME.exe /start"; Flags: deletevalue uninsdeletevalue
+Root: HKLM; Subkey:"SOFTWARE\Microsoft\Windows\CurrentVersion\Run"; ValueType: string; ValueName: "RUN_FILENAME";ValueData: "{app}\RUN_FILENAME.exe /start"; Flags: deletevalue uninsdeletevalue
+Root: HKLM; Subkey:"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Run"; ValueType: string; ValueName: "RUN_FILENAME";ValueData: "{app}\RUN_FILENAME.exe /start"; Flags: deletevalue uninsdeletevalue; Check:IsWin64
+;
+
+[Code]
+function returnTrue(): Boolean;
+begin
+  Result := True;
+end;
+
+function returnFalse(): Boolean;
+begin
+  Result := False;
+end;
+
+function InitializeSetup(): Boolean;
+begin
+// Possible future improvements:
+//   if version less or same => just launch app
+//   if upgrade => check if same app is running and wait for it to exit
+//   Add pack200/unpack200 support? 
+  Result := True;
+end;  

BIN
PrintServer/lib/jacob.jar


BIN
PrintServer/lib/jxbrowser-7.21.jar


BIN
PrintServer/lib/jxbrowser-javafx-7.21.jar


BIN
PrintServer/lib/jxbrowser-win64-7.21.jar


Diff do ficheiro suprimidas por serem muito extensas
+ 23 - 0
PrintServer/logs/printServer.log


BIN
PrintServer/logs/printServer.log.2023-07-13.0.gz


BIN
PrintServer/logs/printServer.log.2023-07-14.0.gz


+ 308 - 0
PrintServer/mvnw

@@ -0,0 +1,308 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#    https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Apache Maven Wrapper startup batch script, version 3.2.0
+#
+# Required ENV vars:
+# ------------------
+#   JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+#   MAVEN_OPTS - parameters passed to the Java VM when running Maven
+#     e.g. to debug Maven itself, use
+#       set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+#   MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+  if [ -f /usr/local/etc/mavenrc ] ; then
+    . /usr/local/etc/mavenrc
+  fi
+
+  if [ -f /etc/mavenrc ] ; then
+    . /etc/mavenrc
+  fi
+
+  if [ -f "$HOME/.mavenrc" ] ; then
+    . "$HOME/.mavenrc"
+  fi
+
+fi
+
+# OS specific support.  $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "$(uname)" in
+  CYGWIN*) cygwin=true ;;
+  MINGW*) mingw=true;;
+  Darwin*) darwin=true
+    # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+    # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+    if [ -z "$JAVA_HOME" ]; then
+      if [ -x "/usr/libexec/java_home" ]; then
+        JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME
+      else
+        JAVA_HOME="/Library/Java/Home"; export JAVA_HOME
+      fi
+    fi
+    ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+  if [ -r /etc/gentoo-release ] ; then
+    JAVA_HOME=$(java-config --jre-home)
+  fi
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME=$(cygpath --unix "$JAVA_HOME")
+  [ -n "$CLASSPATH" ] &&
+    CLASSPATH=$(cygpath --path --unix "$CLASSPATH")
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+  [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] &&
+    JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+  javaExecutable="$(which javac)"
+  if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then
+    # readlink(1) is not available as standard on Solaris 10.
+    readLink=$(which readlink)
+    if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then
+      if $darwin ; then
+        javaHome="$(dirname "\"$javaExecutable\"")"
+        javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac"
+      else
+        javaExecutable="$(readlink -f "\"$javaExecutable\"")"
+      fi
+      javaHome="$(dirname "\"$javaExecutable\"")"
+      javaHome=$(expr "$javaHome" : '\(.*\)/bin')
+      JAVA_HOME="$javaHome"
+      export JAVA_HOME
+    fi
+  fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+  if [ -n "$JAVA_HOME"  ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+      # IBM's JDK on AIX uses strange locations for the executables
+      JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+      JAVACMD="$JAVA_HOME/bin/java"
+    fi
+  else
+    JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)"
+  fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+  echo "Error: JAVA_HOME is not defined correctly." >&2
+  echo "  We cannot execute $JAVACMD" >&2
+  exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+  echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+  if [ -z "$1" ]
+  then
+    echo "Path not specified to find_maven_basedir"
+    return 1
+  fi
+
+  basedir="$1"
+  wdir="$1"
+  while [ "$wdir" != '/' ] ; do
+    if [ -d "$wdir"/.mvn ] ; then
+      basedir=$wdir
+      break
+    fi
+    # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+    if [ -d "${wdir}" ]; then
+      wdir=$(cd "$wdir/.." || exit 1; pwd)
+    fi
+    # end of workaround
+  done
+  printf '%s' "$(cd "$basedir" || exit 1; pwd)"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+  if [ -f "$1" ]; then
+    # Remove \r in case we run on Windows within Git Bash
+    # and check out the repository with auto CRLF management
+    # enabled. Otherwise, we may read lines that are delimited with
+    # \r\n and produce $'-Xarg\r' rather than -Xarg due to word
+    # splitting rules.
+    tr -s '\r\n' ' ' < "$1"
+  fi
+}
+
+log() {
+  if [ "$MVNW_VERBOSE" = true ]; then
+    printf '%s\n' "$1"
+  fi
+}
+
+BASE_DIR=$(find_maven_basedir "$(dirname "$0")")
+if [ -z "$BASE_DIR" ]; then
+  exit 1;
+fi
+
+MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR
+log "$MAVEN_PROJECTBASEDIR"
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar"
+if [ -r "$wrapperJarPath" ]; then
+    log "Found $wrapperJarPath"
+else
+    log "Couldn't find $wrapperJarPath, downloading it ..."
+
+    if [ -n "$MVNW_REPOURL" ]; then
+      wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
+    else
+      wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
+    fi
+    while IFS="=" read -r key value; do
+      # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' )
+      safeValue=$(echo "$value" | tr -d '\r')
+      case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;;
+      esac
+    done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
+    log "Downloading from: $wrapperUrl"
+
+    if $cygwin; then
+      wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath")
+    fi
+
+    if command -v wget > /dev/null; then
+        log "Found wget ... using wget"
+        [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet"
+        if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+            wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
+        else
+            wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
+        fi
+    elif command -v curl > /dev/null; then
+        log "Found curl ... using curl"
+        [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent"
+        if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+            curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
+        else
+            curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
+        fi
+    else
+        log "Falling back to using Java to download"
+        javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java"
+        javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class"
+        # For Cygwin, switch paths to Windows format before running javac
+        if $cygwin; then
+          javaSource=$(cygpath --path --windows "$javaSource")
+          javaClass=$(cygpath --path --windows "$javaClass")
+        fi
+        if [ -e "$javaSource" ]; then
+            if [ ! -e "$javaClass" ]; then
+                log " - Compiling MavenWrapperDownloader.java ..."
+                ("$JAVA_HOME/bin/javac" "$javaSource")
+            fi
+            if [ -e "$javaClass" ]; then
+                log " - Running MavenWrapperDownloader.java ..."
+                ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath"
+            fi
+        fi
+    fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+# If specified, validate the SHA-256 sum of the Maven wrapper jar file
+wrapperSha256Sum=""
+while IFS="=" read -r key value; do
+  case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;;
+  esac
+done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
+if [ -n "$wrapperSha256Sum" ]; then
+  wrapperSha256Result=false
+  if command -v sha256sum > /dev/null; then
+    if echo "$wrapperSha256Sum  $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then
+      wrapperSha256Result=true
+    fi
+  elif command -v shasum > /dev/null; then
+    if echo "$wrapperSha256Sum  $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then
+      wrapperSha256Result=true
+    fi
+  else
+    echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available."
+    echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties."
+    exit 1
+  fi
+  if [ $wrapperSha256Result = false ]; then
+    echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2
+    echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2
+    echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2
+    exit 1
+  fi
+fi
+
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME")
+  [ -n "$CLASSPATH" ] &&
+    CLASSPATH=$(cygpath --path --windows "$CLASSPATH")
+  [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+    MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR")
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+# shellcheck disable=SC2086 # safe args
+exec "$JAVACMD" \
+  $MAVEN_OPTS \
+  $MAVEN_DEBUG_OPTS \
+  -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+  "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+  ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"

+ 205 - 0
PrintServer/mvnw.cmd

@@ -0,0 +1,205 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements.  See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership.  The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License.  You may obtain a copy of the License at
+@REM
+@REM    https://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied.  See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Apache Maven Wrapper startup batch script, version 3.2.0
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM     e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on"  echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
+if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
+
+FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+    IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Found %WRAPPER_JAR%
+    )
+) else (
+    if not "%MVNW_REPOURL%" == "" (
+        SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
+    )
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Couldn't find %WRAPPER_JAR%, downloading it ...
+        echo Downloading from: %WRAPPER_URL%
+    )
+
+    powershell -Command "&{"^
+		"$webclient = new-object System.Net.WebClient;"^
+		"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+		"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+		"}"^
+		"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^
+		"}"
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Finished downloading %WRAPPER_JAR%
+    )
+)
+@REM End of extension
+
+@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file
+SET WRAPPER_SHA_256_SUM=""
+FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+    IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B
+)
+IF NOT %WRAPPER_SHA_256_SUM%=="" (
+    powershell -Command "&{"^
+       "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^
+       "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^
+       "  Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^
+       "  Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^
+       "  Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^
+       "  exit 1;"^
+       "}"^
+       "}"
+    if ERRORLEVEL 1 goto error
+)
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% ^
+  %JVM_CONFIG_MAVEN_PROPS% ^
+  %MAVEN_OPTS% ^
+  %MAVEN_DEBUG_OPTS% ^
+  -classpath %WRAPPER_JAR% ^
+  "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
+  %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
+if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%"=="on" pause
+
+if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
+
+cmd /C exit /B %ERROR_CODE%

+ 215 - 0
PrintServer/pom.xml

@@ -0,0 +1,215 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>2.7.1</version>
+        <relativePath/> <!-- lookup parent from repository -->
+    </parent>
+    <groupId>com.jd</groupId>
+    <artifactId>printServer</artifactId>
+    <version>1.0.0</version>
+    <name>PrintServerApp</name>
+    <description>打印服务App</description>
+    <properties>
+        <java.version>8</java.version>
+    </properties>
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <optional>true</optional>
+            <!--<scope>provided</scope>-->
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <!--常用工具类 -->
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <!--<version>3.12.0</version>-->
+        </dependency>
+
+
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>5.8.16</version>
+        </dependency>
+
+        <!-- javaFX依赖 -->
+        <dependency>
+            <groupId>org.kordamp.bootstrapfx</groupId>
+            <artifactId>bootstrapfx-core</artifactId>
+            <version>0.4.0</version>
+        </dependency>
+        <!-- springboot项目的javaFX依赖 -->
+        <dependency>
+            <groupId>de.roskenet</groupId>
+            <artifactId>springboot-javafx-support</artifactId>
+            <version>2.1.6</version>
+        </dependency>
+        <dependency>
+            <groupId>com.teamdev.jxbrowser</groupId>
+            <artifactId>jxbrowser</artifactId>
+            <version>7.21</version>
+        </dependency>
+        <dependency>
+            <groupId>com.teamdev.jxbrowser</groupId>
+            <artifactId>jxbrowser-win64</artifactId>
+            <version>7.21</version>
+        </dependency>
+        <dependency>
+            <groupId>com.teamdev.jxbrowser</groupId>
+            <artifactId>jxbrowser-javafx</artifactId>
+            <version>7.21</version>
+        </dependency>
+        <!-- PDF文件格式打印实现 -->
+        <dependency>
+            <groupId>org.apache.pdfbox</groupId>
+            <artifactId>pdfbox</artifactId>
+            <version>2.0.29</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <!--<plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>-->
+                <!--虽然spring boot 官方说的是,要想排除lombok的话,可以这么写,但是,javafx-maven-plugin不兼容这种写法,要使用下面的classpathExcludes代替-->
+                <!--<configuration>
+                    <excludes>
+                        <exclude>
+                            <groupId>org.projectlombok</groupId>
+                            <artifactId>lombok</artifactId>
+                        </exclude>
+                    </excludes>
+                </configuration>-->
+            <!--</plugin>-->
+            <plugin>
+                <groupId>com.zenjava</groupId>
+                <artifactId>javafx-maven-plugin</artifactId>
+                <version>8.8.3</version>
+                <configuration>
+                    <!-- 作者/公司名称 -->
+                    <vendor>重庆巨东科技集团有限公司</vendor>
+                    <!-- main方法的类 -->
+                    <mainClass>com.jd.printserver.PrintServerApplication</mainClass>
+                    <!-- 运行文件名/应用名称 ${project.build.finalName} = ${project.artifactId}-${project.version} -->
+                    <!--<appName>${project.build.finalName}</appName>-->
+                    <appName>${project.name}</appName>
+                    <!-- 发行版本 -->
+                    <nativeReleaseVersion>${project.version}</nativeReleaseVersion>
+                    <!--
+                        图标设置
+
+                        > 参考:https://stackoverflow.com/questions/15880102/how-to-set-custom-icon-for-javafx-native-package-icon-on-windows
+
+                        # 方式1(按deployDir、appName配置读取ico文件)
+                        # 使用jfx:native打包时,默认会去src/main/deploy/package/windows/${appName}.ico
+                        <deployDir>${project.basedir}/src/main/deploy</deployDir>
+
+                        # 方式2(固定使用一个图标,与版本号无关)
+                        # 优先级高于第一种方式
+                        <bundleArguments>
+                            <icon>${project.basedir}/src/main/resources/icon/report_print.ico</icon>
+                        </bundleArguments>
+                     -->
+                    <bundleArguments>
+                        <icon>${project.basedir}/src/main/resources/icon/report_print.ico</icon>
+                        <!--下面这2个参数搭配,可实现一个特别重要的功能,就是,提示用户手动选择程序安装目录,默认目录是在:C:\Program Files (x86)\appName-->
+                        <!--设置为true将在Program Files中安装应用程序。设置为false将应用程序安装到用户的主目录中。默认值为false。-->
+                        <systemWide>true</systemWide>
+                        <!-- 让用户选择安装目标文件夹 -->
+                        <installdirChooser>true</installdirChooser>
+                    </bundleArguments>
+                    <!-- 桌面图标 -->
+                    <needShortcut>true</needShortcut>
+                    <!-- 菜单设置 -->
+                    <needMenu>true</needMenu>
+
+                    <!--添加原生的dll等,因为在打包后,会自带2个java.library.path目录,一个是app目录下,一个是exe所在的目录,只要把dll放到这2个目录的其中一个下,就可以加载dll,可以使用additionalAppResources配置dll目录-->
+                    <!--成功实现将项目目录下的dll目录下的所有文件复制到了app目录下,app目录也是java.library.path目录,可以直接加载dll-->
+                    <!--<additionalAppResources>${project.basedir}/dll</additionalAppResources>-->
+
+                    <!--使用这个additionalBundlerResources,不能实现添加dll到app目录-->
+                    <!--<additionalBundlerResources>-->
+                    <!--</additionalBundlerResources>-->
+
+                    <!--排除掉不想要打包进lib依赖库的依赖-->
+                    <classpathExcludes>
+                        <classpathExclude>
+                            <groupId>org.projectlombok</groupId>
+                            <artifactId>lombok</artifactId>
+                        </classpathExclude>
+                    </classpathExcludes>
+
+                    <!--jvmProperties可以添加额外的jvm参数-->
+                    <!--<jvmProperties>
+                    </jvmProperties>-->
+
+                    <!--默认情况下,这将被设置为'ALL',根据你安装的操作系统,以下值对于安装者来说是可能的:-->
+                    <!--windows.app (只创建Windows可执行文件,不生成安装向导的exe)-->
+                    <!--exe (通过InnoIDE生成exe安装程序)-->
+                    <!--msi (Microsoft Windows MSI Installer, via WiX)-->
+                    <!--可以同时添加多个bundler选项,下面是同时生成exe文件夹及exe安装包的-->
+                    <!--<bundler>windows.app</bundler>-->
+                    <bundler>exe</bundler>
+                    <!--如果构建过程中出现问题,可以打开这个,会显示详细的打包过程信息-->
+                    <verbose>true</verbose>
+
+                    <!--生成完后,如果打开verbose的话,最后会提示一行,配置文件已保存到....,可以复制路径,打开查看生成的xxx.iss inno 安装脚本-->
+                </configuration>
+            </plugin>
+        </plugins>
+
+        <resources>
+            <resource>
+                <!--把src/main/java目录下的properties、xm文件打包打进程序中-->
+                <directory>src/main/java</directory>
+                <includes>
+                    <include>**/*.properties</include>
+                    <include>**/*.xml</include>
+                    <include>**/*.fxml</include>
+                </includes>
+                <filtering>false</filtering>
+            </resource>
+
+            <resource>
+                <!--把src/main/resources目录下的properties、xm文件打包打进程序中-->
+                <directory>src/main/resources</directory>
+                <includes>
+                    <include>**/*.yml</include>
+                    <include>**/*.properties</include>
+                    <include>**/*.xml</include>
+                    <include>**/*.fxml</include>
+                    <include>**/*.setting</include>
+                    <include>**/*.png</include>
+                </includes>
+                <filtering>false</filtering>
+            </resource>
+
+            <!--<resource>-->
+                <!--把lib/目录下第三方jar包打进程序中,如systemPath目录下的jar-->
+                <!--<directory>lib/</directory>
+                <targetPath></targetPath>
+                <includes>
+                    <include>**/*.jar</include>
+                </includes>
+                <filtering>false</filtering>-->
+            <!--</resource>-->
+        </resources>
+    </build>
+
+</project>

+ 173 - 0
PrintServer/src/main/java/com/jd/printserver/PrintServerApplication.java

@@ -0,0 +1,173 @@
+package com.jd.printserver;
+
+import cn.hutool.core.convert.Convert;
+import com.jd.printserver.common.constants.Constants;
+import com.jd.printserver.javafx.entity.PrintParam;
+import com.jd.printserver.javafx.view.MainView;
+import com.jd.printserver.utils.FileUtils;
+import com.jd.printserver.utils.PortAvailableUtils;
+import de.felixroske.jfxsupport.AbstractJavaFxApplicationSupport;
+import javafx.application.Platform;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Scene;
+import javafx.scene.layout.AnchorPane;
+import javafx.stage.Stage;
+import javafx.stage.StageStyle;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.io.File;
+import java.net.URL;
+import java.util.Properties;
+
+@SpringBootApplication
+public class PrintServerApplication extends AbstractJavaFxApplicationSupport {
+
+    public static final Logger log = LoggerFactory.getLogger(PrintServerApplication.class);
+
+    private static Properties properties = null;
+
+    public static void main(String[] args) {
+        initProperties();
+        int port = Convert.toInt(properties.get("server.port"), 0);
+        log.info("启动端口为:" + port);
+        if(port != 0){
+            Boolean flag = PortAvailableUtils.isPortAvailable(port);
+            if(flag){
+                launch(PrintServerApplication.class, MainView.class, args);
+            }else{
+                log.error("端口被占用:" + port);
+            }
+        }
+    }
+
+    private static void initProperties(){
+        Resource resource = new ClassPathResource("application.yml");
+        YamlPropertiesFactoryBean yamlFactory = new YamlPropertiesFactoryBean();
+        yamlFactory.setResources(resource);
+        properties =  yamlFactory.getObject();
+    }
+
+    @Override
+    public void start(Stage primaryStage) throws Exception{
+
+        log.info("初始化界面和系统托盘");
+        File settingFile = FileUtils.Jar2OutFile(Constants.FXML_SETTING_PATH);
+        if(settingFile == null){
+            log.error("未找到配置文件:{}", Constants.FXML_SETTING_PATH);
+            return;
+        }
+        //加载系统托盘图标
+        File trayIconFile = FileUtils.Jar2OutFile(Constants.IMAGE_TRAY_ICON_PATH);
+        if(trayIconFile == null){
+            log.error("未找到系统托盘图标:{}", Constants.IMAGE_TRAY_ICON_PATH);
+            return;
+        }
+
+        //读取打印机配置文件
+        //如果没有默认打印参数,先保存默认打印参数
+        PrintParam printParam = FileUtils.readFile2PrintParam();
+        if(printParam == null){
+            printParam = new PrintParam();
+            FileUtils.wirtePrintParam2File(printParam);
+        }
+
+        String titleStr = Convert.toStr(properties.get("data.app.lable.title"), "");
+        String exitStr = Convert.toStr(properties.get("data.app.lable.exit"), "");
+        String settingStr = Convert.toStr(properties.get("data.app.lable.setting"), "");
+        System.setProperty("file.encoding", Constants.UTF8);
+        primaryStage.setTitle(titleStr);
+        primaryStage.initStyle(StageStyle.UTILITY);
+
+        URL settingUrl = settingFile.toURI().toURL();
+        FXMLLoader fxmlLoader = new FXMLLoader(settingUrl);
+        AnchorPane parent = fxmlLoader.<AnchorPane>load();
+        Scene scene = new Scene(parent);
+        primaryStage.setScene(scene);
+
+        //设置为false时点击关闭按钮程序不会退出
+        Platform.setImplicitExit(false);
+
+        //使用JDialog 作为JPopupMenu载体
+        JDialog popWindow = new JDialog();
+        popWindow.setUndecorated(true);
+        //popWindow作为JPopupMenu载体不需要多大的size
+        popWindow.setSize(1, 1);
+
+        //创建JPopupMenu
+        //重写firePopupMenuWillBecomeInvisible
+        //消失后将绑定的组件一起消失
+        JPopupMenu pop = new JPopupMenu() {
+            @Override
+            public void firePopupMenuWillBecomeInvisible() {
+                popWindow.setVisible(false);
+            }
+        };
+        pop.setSize(200, 100);
+        //添加菜单选项
+        JMenuItem setting = new JMenuItem(settingStr);
+        pop.add(setting);
+        setting.addActionListener(e -> {
+            Platform.runLater(new Runnable(){
+                @Override
+                public void run() {
+                    primaryStage.show();
+                    // 显示到前台
+                    primaryStage.toFront();
+                }
+            });
+            log.info("点击了{}选项", settingStr);
+        });
+        pop.addSeparator();
+        //添加菜单选项
+        JMenuItem exit = new JMenuItem(exitStr);
+        pop.add(exit);
+        exit.addActionListener(e -> {
+            log.info("点击了{}选项", exitStr);
+            System.exit(0);
+        });
+
+
+        //创建托盘图标
+        URL url = trayIconFile.toURI().toURL();
+        //使用awt的组件制作系统托盘按钮
+        Image image = Toolkit.getDefaultToolkit().getImage(url);
+
+        TrayIcon trayIcon = new TrayIcon(image, titleStr);
+        //系统托盘图片自适应
+        trayIcon.setImageAutoSize(true);
+        //给托盘图标添加鼠标监听
+        trayIcon.addMouseListener(new MouseAdapter() {
+            @Override
+            public void mouseReleased(MouseEvent e) {
+                if (e.getButton() == 1) {
+                    //左键点击
+                } else if (e.getButton() == 3 && e.isPopupTrigger()) {
+                    //右键点击
+                    popWindow.setLocation(e.getX() + 5, e.getY() - 5 - 30);
+                    popWindow.setVisible(true);
+                    pop.show(popWindow, 0, 0);
+                }
+            }
+        });
+
+        SystemTray tray = SystemTray.getSystemTray();
+        tray.add(trayIcon);
+
+        //primaryStage.show();
+        //不能置顶,不然保存提示弹窗显示不出来
+        //primaryStage.setAlwaysOnTop(true);
+        log.info("已启动界面和系统托盘");
+        log.info("配置文件地址:{}", settingFile.getCanonicalPath());
+        log.info("系统托盘图标地址 :{}", trayIconFile.getCanonicalPath());
+    }
+
+}

+ 20 - 0
PrintServer/src/main/java/com/jd/printserver/common/advice/ExceptionAdvice.java

@@ -0,0 +1,20 @@
+package com.jd.printserver.common.advice;
+
+import com.jd.printserver.common.controller.BaseController;
+import com.jd.printserver.common.domain.AjaxResult;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+@ControllerAdvice
+public class ExceptionAdvice extends BaseController {
+
+    @ResponseBody
+    @ExceptionHandler(value = Exception.class)
+    public AjaxResult fileUploadExceptionHandler(Exception ex){
+        ex.printStackTrace();
+        return error(ex.getClass() + ": " +ex.getStackTrace()[0].getClassName()
+                + "." + ex.getStackTrace()[0].getMethodName() + "()[" + ex.getStackTrace()[0].getLineNumber() + "]: "
+                + ex.getMessage());
+    }
+}

+ 179 - 0
PrintServer/src/main/java/com/jd/printserver/common/constants/Constants.java

@@ -0,0 +1,179 @@
+package com.jd.printserver.common.constants;
+
+
+/**
+ * 通用常量信息
+ *
+ * @author ruoyi
+ */
+public class Constants
+{
+    /**
+     * UTF-8 字符集
+     */
+    public static final String UTF8 = "UTF-8";
+
+    /**
+     * http请求
+     */
+    public static final String HTTP = "http://";
+
+    /**
+     * https请求
+     */
+    public static final String HTTPS = "https://";
+
+    public static final String RESOURCE_SEPARATOR = "/";
+    public static final String RESOURCE_BOTTOM_SEPARATOR = "_";
+
+    public static final String RESOURCE_CUT_IMAGES_FILENAME = "cut";
+    public static final String RESOURCE_PRINT_IMAGES_FILENAME = "print";
+
+    /**
+     * 资源映射路径 前缀
+     */
+    public static final String RESOURCE_PREFIX = "/profile";
+
+    /**
+     * 资源映射路径 前缀
+     */
+    public static final String RESOURCE_JAR_OUT_FILE_PREFIX = "/jarfile";
+
+
+    /**
+     * 配置界面fxml文件
+     */
+    public static final String FXML_SETTING_PATH = "/view/setting.fxml";
+
+    /**
+     * 初始加载界面fxml文件
+     */
+    public static final String FXML_MAIN_PATH = "/view/main.fxml";
+
+    /**
+     * 系统托盘图标
+     */
+    public static final String IMAGE_TRAY_ICON_PATH = "/images/report_print.png";
+
+    /**
+     * 打印参数配置的文件生成目录
+     */
+    public static final String PRINT_PARAM_DATA_PATH = "/printParamData.data";
+
+    /**
+     * 打印网页的文件生成目录
+     */
+    public static final String PRINT_URL_DIRECTORY = "/printUrl";
+    /**
+     * 打印网页的文件生成默认目录
+     */
+    public static final String PRINT_URL_DEFAULT_DIRECTORY = "/printDefaultUrl";
+
+    /**
+     * 图片后缀
+     */
+    public static final String IMAGE_JPG_SUFFIX = ".jpg";
+    public static final String IMAGE_PNG_SUFFIX = ".png";
+    public static final String IMAGE_GIF_SUFFIX = ".gif";
+
+    /**
+     * 获取网页地址的base64图片数据,并打印图片
+     * base64图片前缀
+     */
+    public static final String BASE64_JPG_PREFIX = "data:image/jpeg;base64";
+    public static final String BASE64_PNG_PREFIX = "data:image/png;base64";
+    public static final String BASE64_GIF_PREFIX = "data:image/gif;base64";
+    public static final String[] BASE64_IMAGE_PREFIX = {BASE64_JPG_PREFIX, BASE64_PNG_PREFIX, BASE64_GIF_PREFIX};
+    public static final String JXBROWSER_CONSOLE_BASE64_PREFIX = "printImg ";
+    // 30秒
+    public static final Long JXBROWSER_CONSOLE_BASE64_WAIT_TIME = 30000l;
+    public static final String JXBROWSER_LICENSE_KEY = "6P835FT5HAPTB03TPIEFPGU5ECGJN8GMGDD79MD7Y52NVP0K0IV6FHYZVQI25H0MLGI2";
+
+    /**
+     * 文件转换为pdf,并打印pdf
+     * 文件content_type
+     */
+    public static final String CONTENT_TYPE_PDF = "application/pdf";
+    public static final String CONTENT_TYPE_XLS = "application/vnd.ms-excel";
+    public static final String CONTENT_TYPE_XLSX = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
+    public static final String[] CONTENT_TYPE_PRINT = {CONTENT_TYPE_PDF, CONTENT_TYPE_XLS, CONTENT_TYPE_XLSX};
+
+    // 文件后缀
+    public static final String FILE_PDF_SUFFIX = ".pdf";
+    public static final String FILE_XLS_SUFFIX = ".xls";
+    public static final String FILE_XLSX_SUFFIX = ".xlsx";
+
+    //打印纸张尺寸字符串
+    public static final String PRINT_A4 = "A4:210mm X 297mm";
+    public static final String PRINT_A5 = "A5:148mm X 210mm";
+    public static final String PRINT_B5 = "B5:182mm X 257mm";
+    public static final String PRINT_DEVELOP_C5 = "Devolop C5:162mm X 229mm";
+    public static final String PRINT_DEVELOP_DL = "Devolop DL:110mm X 220mm";
+    public static final String PRINT_DEVELOP_B5 = "Devolop B5:176mm X 250mm";
+    public static final String PRINT_DEVELOP_MONARCH = "Devolop Monarch:3.875inch X 7.5inch";
+    public static final String PRINT_DEVELOP_9 = "Devolop 9:3.875inch X 8.875inch";
+    public static final String PRINT_DEVELOP_10 = "Devolop 10:4.125inch X 9.5inch";
+    public static final String PRINT_LETTER = "Letter:8.5inch X 11inch";
+    public static final String PRINT_LEGAL = "Legal:8.5inch X 14inch";
+    public static final String PRINT_SELF_DEFINE = "self define ...";
+
+    //打印纸张尺寸字符串集合
+    public static final String[] PRINT_PAPER_STR_ARRAY = {PRINT_A4,PRINT_A5,PRINT_B5,PRINT_DEVELOP_C5,PRINT_DEVELOP_DL,
+            PRINT_DEVELOP_B5,PRINT_DEVELOP_MONARCH,PRINT_DEVELOP_9,PRINT_DEVELOP_10,PRINT_LETTER,PRINT_LEGAL};
+
+    // 毫米转像素(mm*0.0394*72=px)
+    // 1=0.0394英寸
+    // 1英寸=72像素
+    public static final double INCHESPER_MILLIMETER = 0.0394;
+    // 1英寸=25.4毫米
+    public static final double MM2INCH = 25.4;
+    public static final double PRINT_PX = 72;
+
+    //打印纸张尺寸
+    public static final int PRINT_A4_WIDTH = 210;
+    public static final int PRINT_A4_HEIGHT = 297;
+
+    public static final int PRINT_A5_WIDTH = 148;
+    public static final int PRINT_A5_HEIGHT = 210;
+
+    public static final int PRINT_B5_WIDTH = 182;
+    public static final int PRINT_B5_HEIGHT = 257;
+
+    public static final int PRINT_DEVELOP_C5_WIDTH = 162;
+    public static final int PRINT_DEVELOP_C5_HEIGHT = 229;
+
+    public static final int PRINT_DEVELOP_DL_WIDTH = 110;
+    public static final int PRINT_DEVELOP_DL_HEIGHT = 220;
+
+    public static final int PRINT_DEVELOP_B5_WIDTH = 176;
+    public static final int PRINT_DEVELOP_B5_HEIGHT = 250;
+
+    public static final int PRINT_DEVELOP_MONARCH_WIDTH = (int)(3.875  / INCHESPER_MILLIMETER);
+    public static final int PRINT_DEVELOP_MONARCH_HEIGHT = (int)(7.5  / INCHESPER_MILLIMETER);
+
+    public static final int PRINT_DEVELOP_9_WIDTH = (int)(3.875  / INCHESPER_MILLIMETER);
+    public static final int PRINT_DEVELOP_9_HEIGHT = (int)(8.875  / INCHESPER_MILLIMETER);
+
+    public static final int PRINT_DEVELOP_10_WIDTH = (int)(4.125  / INCHESPER_MILLIMETER);
+    public static final int PRINT_DEVELOP_10_HEIGHT = (int)(9.5  / INCHESPER_MILLIMETER);
+
+    public static final int PRINT_LETTER_WIDTH = (int)(8.5  / INCHESPER_MILLIMETER);
+    public static final int PRINT_LETTER_HEIGHT = (int)(11  / INCHESPER_MILLIMETER);
+
+    public static final int PRINT_LEGAL_WIDTH = (int)(8.5  / INCHESPER_MILLIMETER);
+    public static final int PRINT_LEGAL_HEIGHT = (int)(14  / INCHESPER_MILLIMETER);
+
+    //打印纸张尺寸宽度集合
+    public static final int[] PRINT_PAPER_MM_WIDTH_ARRAY = {PRINT_A4_WIDTH,PRINT_A5_WIDTH,PRINT_B5_WIDTH,PRINT_DEVELOP_C5_WIDTH,PRINT_DEVELOP_DL_WIDTH,
+            PRINT_DEVELOP_B5_WIDTH,PRINT_DEVELOP_MONARCH_WIDTH,PRINT_DEVELOP_9_WIDTH,PRINT_DEVELOP_10_WIDTH,PRINT_LETTER_WIDTH,PRINT_LEGAL_WIDTH};
+    //打印纸张尺寸高度集合
+    public static final int[] PRINT_PAPER_MM_HEIGHT_ARRAY = {PRINT_A4_HEIGHT,PRINT_A5_HEIGHT,PRINT_B5_HEIGHT,PRINT_DEVELOP_C5_HEIGHT,PRINT_DEVELOP_DL_HEIGHT,
+            PRINT_DEVELOP_B5_HEIGHT,PRINT_DEVELOP_MONARCH_HEIGHT,PRINT_DEVELOP_9_HEIGHT,PRINT_DEVELOP_10_HEIGHT,PRINT_LETTER_HEIGHT,PRINT_LEGAL_HEIGHT};
+
+
+    public static final String[] PRINT_PAPER_SCALE_STR_ARRAY = {"自适应","10%","20%","30%","40%","50%","60%","70%","80%","90%","100%","150%","200%","250%","300%"};
+    public static final float[] PRINT_PAPER_SCALE_ARRAY = {0f,0.1f,0.2f,0.3f,0.4f,0.5f,0.6f,0.7f,0.8f,0.9f,1.0f,1.5f,2.0f,2.5f,3.0f};
+
+    public static final String[] PRINT_PAPER_ORIENTATION_STR_ARRAY = {"自动","横向","纵向"};
+    public static final Integer[] PRINT_PAPER_ORIENTATION_ARRAY = {0,1,2};
+}

+ 94 - 0
PrintServer/src/main/java/com/jd/printserver/common/constants/HttpStatus.java

@@ -0,0 +1,94 @@
+package com.jd.printserver.common.constants;
+
+/**
+ * 返回状态码
+ *
+ * @author ruoyi
+ */
+public class HttpStatus
+{
+    /**
+     * 操作成功
+     */
+    public static final int SUCCESS = 200;
+
+    /**
+     * 对象创建成功
+     */
+    public static final int CREATED = 201;
+
+    /**
+     * 请求已经被接受
+     */
+    public static final int ACCEPTED = 202;
+
+    /**
+     * 操作已经执行成功,但是没有返回数据
+     */
+    public static final int NO_CONTENT = 204;
+
+    /**
+     * 资源已被移除
+     */
+    public static final int MOVED_PERM = 301;
+
+    /**
+     * 重定向
+     */
+    public static final int SEE_OTHER = 303;
+
+    /**
+     * 资源没有被修改
+     */
+    public static final int NOT_MODIFIED = 304;
+
+    /**
+     * 参数列表错误(缺少,格式不匹配)
+     */
+    public static final int BAD_REQUEST = 400;
+
+    /**
+     * 未授权
+     */
+    public static final int UNAUTHORIZED = 401;
+
+    /**
+     * 访问受限,授权过期
+     */
+    public static final int FORBIDDEN = 403;
+
+    /**
+     * 资源,服务未找到
+     */
+    public static final int NOT_FOUND = 404;
+
+    /**
+     * 不允许的http方法
+     */
+    public static final int BAD_METHOD = 405;
+
+    /**
+     * 资源冲突,或者资源被锁
+     */
+    public static final int CONFLICT = 409;
+
+    /**
+     * 不支持的数据,媒体类型
+     */
+    public static final int UNSUPPORTED_TYPE = 415;
+
+    /**
+     * 系统内部错误
+     */
+    public static final int ERROR = 500;
+
+    /**
+     * 接口未实现
+     */
+    public static final int NOT_IMPLEMENTED = 501;
+
+    /**
+     * 系统警告消息
+     */
+    public static final int WARN = 601;
+}

+ 97 - 0
PrintServer/src/main/java/com/jd/printserver/common/controller/BaseController.java

@@ -0,0 +1,97 @@
+package com.jd.printserver.common.controller;
+
+import com.jd.printserver.common.domain.AjaxResult;
+import com.jd.printserver.utils.StringUtils;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * web层通用数据处理
+ *
+ * @author ruoyi
+ */
+public class BaseController
+{
+
+    protected final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    /**
+     * 返回成功
+     */
+    public AjaxResult success()
+    {
+        return AjaxResult.success();
+    }
+
+    /**
+     * 返回失败消息
+     */
+    public AjaxResult error()
+    {
+        return AjaxResult.error();
+    }
+
+    /**
+     * 返回成功消息
+     */
+    public AjaxResult success(String message)
+    {
+        return AjaxResult.success(message);
+    }
+
+    /**
+     * 返回成功消息
+     */
+    public AjaxResult success(Object data)
+    {
+        return AjaxResult.success(data);
+    }
+
+    /**
+     * 返回失败消息
+     */
+    public AjaxResult error(String message)
+    {
+        return AjaxResult.error(message);
+    }
+
+    /**
+     * 返回警告消息
+     */
+    public AjaxResult warn(String message)
+    {
+        return AjaxResult.warn(message);
+    }
+
+    /**
+     * 响应返回结果
+     *
+     * @param rows 影响行数
+     * @return 操作结果
+     */
+    protected AjaxResult toAjax(int rows)
+    {
+        return rows > 0 ? AjaxResult.success() : AjaxResult.error();
+    }
+
+    /**
+     * 响应返回结果
+     *
+     * @param result 结果
+     * @return 操作结果
+     */
+    protected AjaxResult toAjax(boolean result)
+    {
+        return result ? success() : error();
+    }
+
+    /**
+     * 页面跳转
+     */
+    public String redirect(String url)
+    {
+        return StringUtils.format("redirect:{}", url);
+    }
+
+}

+ 188 - 0
PrintServer/src/main/java/com/jd/printserver/common/domain/AjaxResult.java

@@ -0,0 +1,188 @@
+package com.jd.printserver.common.domain;
+
+
+
+import com.jd.printserver.common.constants.HttpStatus;
+import com.jd.printserver.utils.StringUtils;
+
+import java.util.HashMap;
+
+/**
+ * 操作消息提醒
+ *
+ * @author ruoyi
+ */
+public class AjaxResult extends HashMap<String, Object>
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 状态码 */
+    public static final String CODE_TAG = "code";
+
+    /** 返回内容 */
+    public static final String MSG_TAG = "msg";
+
+    /** 数据对象 */
+    public static final String DATA_TAG = "data";
+
+    /**
+     * 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。
+     */
+    public AjaxResult()
+    {
+    }
+
+    /**
+     * 初始化一个新创建的 AjaxResult 对象
+     *
+     * @param code 状态码
+     * @param msg 返回内容
+     */
+    public AjaxResult(int code, String msg)
+    {
+        super.put(CODE_TAG, code);
+        super.put(MSG_TAG, msg);
+    }
+
+    /**
+     * 初始化一个新创建的 AjaxResult 对象
+     *
+     * @param code 状态码
+     * @param msg 返回内容
+     * @param data 数据对象
+     */
+    public AjaxResult(int code, String msg, Object data)
+    {
+        super.put(CODE_TAG, code);
+        super.put(MSG_TAG, msg);
+        if (StringUtils.isNotNull(data))
+        {
+            super.put(DATA_TAG, data);
+        }
+    }
+
+    /**
+     * 返回成功消息
+     *
+     * @return 成功消息
+     */
+    public static AjaxResult success()
+    {
+        return AjaxResult.success("操作成功");
+    }
+
+    /**
+     * 返回成功数据
+     *
+     * @return 成功消息
+     */
+    public static AjaxResult success(Object data)
+    {
+        return AjaxResult.success("操作成功", data);
+    }
+
+    /**
+     * 返回成功消息
+     *
+     * @param msg 返回内容
+     * @return 成功消息
+     */
+    public static AjaxResult success(String msg)
+    {
+        return AjaxResult.success(msg, null);
+    }
+
+    /**
+     * 返回成功消息
+     *
+     * @param msg 返回内容
+     * @param data 数据对象
+     * @return 成功消息
+     */
+    public static AjaxResult success(String msg, Object data)
+    {
+        return new AjaxResult(HttpStatus.SUCCESS, msg, data);
+    }
+
+    /**
+     * 返回警告消息
+     *
+     * @param msg 返回内容
+     * @return 警告消息
+     */
+    public static AjaxResult warn(String msg)
+    {
+        return AjaxResult.warn(msg, null);
+    }
+
+    /**
+     * 返回警告消息
+     *
+     * @param msg 返回内容
+     * @param data 数据对象
+     * @return 警告消息
+     */
+    public static AjaxResult warn(String msg, Object data)
+    {
+        return new AjaxResult(HttpStatus.WARN, msg, data);
+    }
+
+    /**
+     * 返回错误消息
+     *
+     * @return 错误消息
+     */
+    public static AjaxResult error()
+    {
+        return AjaxResult.error("操作失败");
+    }
+
+    /**
+     * 返回错误消息
+     *
+     * @param msg 返回内容
+     * @return 错误消息
+     */
+    public static AjaxResult error(String msg)
+    {
+        return AjaxResult.error(msg, null);
+    }
+
+    /**
+     * 返回错误消息
+     *
+     * @param msg 返回内容
+     * @param data 数据对象
+     * @return 错误消息
+     */
+    public static AjaxResult error(String msg, Object data)
+    {
+        return new AjaxResult(HttpStatus.ERROR, msg, data);
+    }
+
+    /**
+     * 返回错误消息
+     *
+     * @param code 状态码
+     * @param msg 返回内容
+     * @return 错误消息
+     */
+    public static AjaxResult error(int code, String msg)
+    {
+        return new AjaxResult(code, msg, null);
+    }
+
+    /**
+     * 方便链式调用
+     *
+     * @param key 键
+     * @param value 值
+     * @return 数据对象
+     */
+    @Override
+    public AjaxResult put(String key, Object value)
+    {
+        super.put(key, value);
+        return this;
+    }
+}

+ 96 - 0
PrintServer/src/main/java/com/jd/printserver/controller/PrintServerController.java

@@ -0,0 +1,96 @@
+package com.jd.printserver.controller;
+
+import cn.hutool.core.codec.Base64Decoder;
+import cn.hutool.core.io.FileUtil;
+import com.jd.printserver.common.constants.Constants;
+import com.jd.printserver.common.controller.BaseController;
+import com.jd.printserver.common.domain.AjaxResult;
+import com.jd.printserver.javafx.entity.PrintParam;
+import com.jd.printserver.utils.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.io.File;
+import java.io.IOException;
+
+
+@RestController
+@RequestMapping(value = "/api")
+public class PrintServerController extends BaseController {
+
+
+    public static final Logger log = LoggerFactory.getLogger(PrintServerController.class);
+
+    @RequestMapping(value = "/printUrl")
+    public AjaxResult printUrl(String url){
+        log.info("--------------------------本地打印开始");
+        log.info("数据访问地址:{}", url);
+        if (StringUtils.isEmpty(url)) {
+            log.info("接口参数缺失:{}", url);
+            return error("参数缺失");
+        }
+        String data = JxbrowserUtils.getResultData(url);
+        if(data == null){
+            log.info("获取报表图片文件失败");
+            return error("获取报表失败");
+        }
+        String[] imgData = data.split(",");
+        if(!ArrayUtils.contains(Constants.BASE64_IMAGE_PREFIX, imgData[0])){
+            log.info("不是Base64格式图片文件");
+            return error("获取报表格式错误");
+        }
+
+        byte[] imgBytes = Base64Decoder.decode(imgData[1]);
+        for (int i = 0; i < imgBytes.length; ++i) {
+            if (imgBytes[i] < 0) {
+                imgBytes[i] += 256;
+            }
+        }
+
+        String path = null;
+        if(imgData[0].equals(Constants.BASE64_JPG_PREFIX)){
+            path = FileUtils.newFilePath(Constants.PRINT_URL_DIRECTORY, Constants.IMAGE_JPG_SUFFIX);
+        }else if(imgData[0].equals(Constants.BASE64_PNG_PREFIX)){
+            path = FileUtils.newFilePath(Constants.PRINT_URL_DIRECTORY, Constants.IMAGE_PNG_SUFFIX);
+        }else if(imgData[0].equals(Constants.BASE64_GIF_PREFIX)){
+            path = FileUtils.newFilePath(Constants.PRINT_URL_DIRECTORY, Constants.IMAGE_GIF_SUFFIX);
+        }
+        if(path == null){
+            log.info("不是可处理(jpg/png/gif)的图片文件");
+            return error("访问文件失败");
+        }
+        FileUtil.writeBytes(imgBytes, path);
+        log.info("初始化打印文件:{}", path);
+        File inFile = new File(path);
+        if(inFile == null || !inFile.exists()){
+            return error("报表文件持久化失败");
+        }
+//        File outFile = ImageUtils.authCut(inFile);
+//        if(outFile == null || !outFile.exists()){
+//            return error("打印文件去除边界、背景失败");
+//        }
+        PrintParam printParam = FileUtils.readFile2PrintParam();
+        log.info("初始化打印参数:{}", printParam);
+        if(printParam == null){
+            return error("初始化打印参数失败");
+        }
+//        String outPath = outFile.getPath();
+        String outPath = null;
+        try {
+            outPath = inFile.getCanonicalPath();
+        } catch (IOException e) {
+            return error("报表文件持久化失败");
+        }
+        log.info("处理打印文件,适应打印机:{}", outPath);
+        Boolean printFlag = PrintUtils.PDFprint(outPath, printParam);
+        log.info("打印返回结果:{}", printFlag);
+        log.info("--------------------------结束本地打印");
+        if(!printFlag){
+            return error("打印失败");
+        }
+        return success();
+    }
+
+}

+ 15 - 0
PrintServer/src/main/java/com/jd/printserver/javafx/controller/MainController.java

@@ -0,0 +1,15 @@
+package com.jd.printserver.javafx.controller;
+
+import de.felixroske.jfxsupport.FXMLController;
+import javafx.fxml.Initializable;
+
+import java.net.URL;
+import java.util.ResourceBundle;
+
+@FXMLController
+public class MainController implements Initializable {
+    @Override
+    public void initialize(URL location, ResourceBundle resources) {
+
+    }
+}

+ 237 - 0
PrintServer/src/main/java/com/jd/printserver/javafx/controller/SettingController.java

@@ -0,0 +1,237 @@
+package com.jd.printserver.javafx.controller;
+
+import com.jd.printserver.PrintServerApplication;
+import com.jd.printserver.common.constants.Constants;
+import com.jd.printserver.javafx.entity.PrintParam;
+import com.jd.printserver.utils.FileUtils;
+import de.felixroske.jfxsupport.FXMLController;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.fxml.FXML;
+import javafx.fxml.Initializable;
+import javafx.scene.control.*;
+import javafx.stage.Stage;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.net.URL;
+import java.util.Optional;
+import java.util.ResourceBundle;
+
+@FXMLController
+public class SettingController implements Initializable {
+
+    public static final Logger log = LoggerFactory.getLogger(PrintServerApplication.class);
+
+
+    @FXML
+    private TextField paperHeightTextField;
+
+    @FXML
+    private TextField paperWidthTextField;
+
+    @FXML
+    private ChoiceBox<String> pageSizeChoiceBox;
+
+    @FXML
+    private ChoiceBox<String> orientationChoiceBox;
+
+    @FXML
+    private Button closeWindowButton;
+
+    @FXML
+    private TextField paperBottomTextField;
+
+    @FXML
+    private TextField paperLeftTextField;
+
+    @FXML
+    private TextField paperRightTextField;
+
+    @FXML
+    private ChoiceBox<String> scaleChoiceBox;
+
+    @FXML
+    private TextField copiesTextField;
+
+    @FXML
+    private TextField paperTopTextField;
+
+    @FXML
+    private TextField jobNameTextField;
+
+
+
+    private PrintParam printParam = null;
+
+    @Override
+    public void initialize(URL location, ResourceBundle resources) {
+        printParam = FileUtils.readFile2PrintParam();
+        if(printParam == null){
+            printParam = new PrintParam();
+        }
+        setAllPrintParam2Paper();
+    }
+
+
+
+    public void setAllPrintParam2Paper(){
+        pageSizeChoiceBox.getItems().addAll(Constants.PRINT_PAPER_STR_ARRAY);
+        int index = -1;
+        for (int i = 0; i < Constants.PRINT_PAPER_MM_WIDTH_ARRAY.length; i++) {
+            if(Constants.PRINT_PAPER_MM_WIDTH_ARRAY[i] == printParam.getWidth().intValue()
+                    && Constants.PRINT_PAPER_MM_HEIGHT_ARRAY[i] == printParam.getHeight().intValue()){
+                index = i;
+            }
+        }
+        if(index > -1){
+            pageSizeChoiceBox.getSelectionModel().select(index);
+        }
+
+        orientationChoiceBox.getItems().addAll(Constants.PRINT_PAPER_ORIENTATION_STR_ARRAY);
+        int index3 = -1;
+        for (int i = 0; i < Constants.PRINT_PAPER_ORIENTATION_ARRAY.length; i++) {
+            if(Constants.PRINT_PAPER_ORIENTATION_ARRAY[i] == printParam.getOrientation().intValue()){
+                index3 = i;
+            }
+        }
+        if(index3 > -1){
+            orientationChoiceBox.getSelectionModel().select(index3);
+        }
+
+
+        paperWidthTextField.setText(printParam.getWidth().toString());
+        paperHeightTextField.setText(printParam.getHeight().toString());
+        paperLeftTextField.setText(printParam.getMarginLeft().toString());
+        paperRightTextField.setText(printParam.getMarginRight().toString());
+        paperTopTextField.setText(printParam.getMarginTop().toString());
+        paperBottomTextField.setText(printParam.getMarginBottom().toString());
+
+        copiesTextField.setText(printParam.getCopies().toString());
+        copiesTextField.textProperty().addListener(new ChangeListener<String>() {
+            @Override
+            public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
+                if (!newValue.matches("\\d*")) {
+                    copiesTextField.setText(newValue.replaceAll("[^\\d]", ""));
+                }
+                if(copiesTextField.getText().indexOf("0") == 0){
+                    copiesTextField.setText(Integer.valueOf(newValue).toString());
+                }
+            }
+        });
+
+        scaleChoiceBox.getItems().addAll(Constants.PRINT_PAPER_SCALE_STR_ARRAY);
+        int index2 = -1;
+        for (int i = 0; i < Constants.PRINT_PAPER_SCALE_ARRAY.length; i++) {
+            if(Constants.PRINT_PAPER_SCALE_ARRAY[i] == printParam.getScale().floatValue()){
+                index2 = i;
+            }
+        }
+        if(index2 > -1){
+            scaleChoiceBox.getSelectionModel().select(index2);
+        }
+
+        jobNameTextField.setText(printParam.getJobName());
+
+    }
+
+    @FXML
+    public void pageSizeChoiceOnAction() {
+        int index = pageSizeChoiceBox.getSelectionModel().getSelectedIndex();
+        if(index > -1){
+            int width = Constants.PRINT_PAPER_MM_WIDTH_ARRAY[index];
+            int height = Constants.PRINT_PAPER_MM_HEIGHT_ARRAY[index];
+            paperWidthTextField.setText(Integer.toString(width));
+            paperHeightTextField.setText(Integer.toString(height));
+        }
+    }
+
+    @FXML
+    public void saveSettingOnMouseClicked() {
+        Boolean flag = confirmAlert("请确认是否要保存设置!");
+        if(flag){
+            printParam.setWidth(Integer.parseInt(paperWidthTextField.getText()));
+            printParam.setHeight(Integer.parseInt(paperHeightTextField.getText()));
+            printParam.setMarginLeft(Integer.parseInt(paperLeftTextField.getText()));
+            printParam.setMarginRight(Integer.parseInt(paperRightTextField.getText()));
+            printParam.setMarginTop(Integer.parseInt(paperTopTextField.getText()));
+            printParam.setMarginBottom(Integer.parseInt(paperBottomTextField.getText()));
+
+            printParam.setOrientation(Constants.PRINT_PAPER_ORIENTATION_ARRAY[orientationChoiceBox.getSelectionModel().getSelectedIndex()]);
+
+            //printParam.setOrientation 在group已设置
+
+            printParam.setCopies(Integer.parseInt(copiesTextField.getText()));
+
+            printParam.setWidth(Integer.parseInt(paperWidthTextField.getText()));
+
+            printParam.setScale(Constants.PRINT_PAPER_SCALE_ARRAY[scaleChoiceBox.getSelectionModel().getSelectedIndex()]);
+
+            printParam.setJobName(jobNameTextField.getText());
+
+            flag = FileUtils.wirtePrintParam2File(printParam);
+            if(flag){
+                successAlert("保存成功!");
+            }else{
+                errorAlert("保存失败!");
+            }
+        }
+    }
+
+    @FXML
+    public void revertDefaultSettingOnMouseClicked() {
+        Boolean flag = confirmAlert("请确认是否要恢复为默认设置!");
+        if(flag){
+            printParam = new PrintParam();
+            setAllPrintParam2Paper();
+            flag = FileUtils.wirtePrintParam2File(printParam);
+            if(flag){
+                successAlert("恢复成功!");
+            }else{
+                errorAlert("恢复失败!");
+            }
+        }
+
+    }
+
+
+
+    @FXML
+    public void closeWindowOnMouseClicked() {
+        Stage stage = (Stage) closeWindowButton.getScene().getWindow();
+        stage.close();
+    }
+
+    private void successAlert(String info){
+        Alert alert = new Alert(Alert.AlertType.INFORMATION);
+        Stage stage = (Stage) closeWindowButton.getScene().getWindow();
+        // 将主舞台设置为Alert对话框的所有者, alert将始终位于主阶段之上
+        alert.initOwner(stage);
+        alert.setHeaderText(info);
+        alert.show();
+    }
+    private void errorAlert(String info){
+        Alert alert = new Alert(Alert.AlertType.ERROR);
+        Stage stage = (Stage) closeWindowButton.getScene().getWindow();
+        // 将主舞台设置为Alert对话框的所有者, alert将始终位于主阶段之上
+        alert.initOwner(stage);
+        alert.setHeaderText(info);
+        alert.show();
+    }
+
+    private Boolean confirmAlert(String info){
+        Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
+        Stage stage = (Stage) closeWindowButton.getScene().getWindow();
+        // 将主舞台设置为Alert对话框的所有者, alert将始终位于主阶段之上
+        alert.initOwner(stage);
+        alert.setHeaderText(info);
+        Optional<ButtonType> option = alert.showAndWait();
+        if (option.get() == ButtonType.OK) {
+            return true;
+        } else if (option.get() == ButtonType.CANCEL) {
+            return false;
+        }
+        return false;
+    }
+
+}

+ 63 - 0
PrintServer/src/main/java/com/jd/printserver/javafx/entity/PrintParam.java

@@ -0,0 +1,63 @@
+package com.jd.printserver.javafx.entity;
+
+import com.jd.printserver.common.constants.Constants;
+import lombok.Data;
+
+@Data
+public class PrintParam {
+
+    /**
+     * 作业名称
+     */
+    private String jobName = "川仪打印作业";
+
+    /**
+     * 打印方向  0 自动 1 横向 2 纵向
+     */
+    private Integer orientation = 0;
+
+    /**
+     * 打印份数
+     */
+    private Integer copies = 1;
+
+    /**
+     * 打印区域
+     */
+    private Integer width = Constants.PRINT_A4_WIDTH;
+    private Integer height = Constants.PRINT_A4_HEIGHT;
+    private Integer marginLeft = 10;
+    private Integer marginTop = 10;
+    private Integer marginRight = 10;
+    private Integer marginBottom = 10;
+
+    /**
+     * 纸张单位类型 1 毫米 2 英寸
+     */
+    private Integer unit = 1;
+
+    /**
+     * 单双面 1 单面打印 2 双面打印,长边翻转 3 双面打印,短边翻转
+     */
+    private Integer sides = 1;
+
+    /**
+     * 颜色 1 黑白打印 2 彩打
+     */
+    private Integer chromaticity = 1;
+
+    /**
+     * 缩放比例 0 自适应  x x%
+     */
+    private Float scale = 0f;
+
+    /**
+     * 毫米转像素
+     *  px = mm * 72 / 25.4
+     * @return int
+     */
+    public int mm2px(int value){
+        return (int)(value * Constants.PRINT_PX / Constants.MM2INCH);
+    }
+
+}

+ 9 - 0
PrintServer/src/main/java/com/jd/printserver/javafx/view/MainView.java

@@ -0,0 +1,9 @@
+package com.jd.printserver.javafx.view;
+
+import com.jd.printserver.common.constants.Constants;
+import de.felixroske.jfxsupport.AbstractFxmlView;
+import de.felixroske.jfxsupport.FXMLView;
+
+@FXMLView(Constants.FXML_MAIN_PATH)
+public class MainView extends AbstractFxmlView {
+}

+ 9 - 0
PrintServer/src/main/java/com/jd/printserver/javafx/view/SettingView.java

@@ -0,0 +1,9 @@
+package com.jd.printserver.javafx.view;
+
+import com.jd.printserver.common.constants.Constants;
+import de.felixroske.jfxsupport.AbstractFxmlView;
+import de.felixroske.jfxsupport.FXMLView;
+
+@FXMLView(Constants.FXML_SETTING_PATH)
+public class SettingView extends AbstractFxmlView {
+}

+ 20 - 0
PrintServer/src/main/java/com/jd/printserver/utils/ArrayUtils.java

@@ -0,0 +1,20 @@
+package com.jd.printserver.utils;
+
+import com.jd.printserver.common.constants.Constants;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class ArrayUtils {
+
+    /**
+     *
+     * @param arrays 备选数组
+     * @param key 包含字符串
+     * @return
+     */
+    public static Boolean contains(String[] arrays, String key){
+        List list = Arrays.asList(arrays);
+        return list.contains(key);
+    }
+}

+ 188 - 0
PrintServer/src/main/java/com/jd/printserver/utils/DateUtils.java

@@ -0,0 +1,188 @@
+package com.jd.printserver.utils;
+
+import org.apache.commons.lang3.time.DateFormatUtils;
+
+import java.lang.management.ManagementFactory;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.time.*;
+import java.util.Date;
+
+/**
+ * 时间工具类
+ *
+ * @author ruoyi
+ */
+public class DateUtils extends org.apache.commons.lang3.time.DateUtils
+{
+    public static String YYYY = "yyyy";
+
+    public static String YYYY_MM = "yyyy-MM";
+
+    public static String YYYY_MM_DD = "yyyy-MM-dd";
+
+    public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";
+
+    public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
+
+    private static String[] parsePatterns = {
+            "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
+            "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
+            "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};
+
+    /**
+     * 获取当前Date型日期
+     *
+     * @return Date() 当前日期
+     */
+    public static Date getNowDate()
+    {
+        return new Date();
+    }
+
+    /**
+     * 获取当前日期, 默认格式为yyyy-MM-dd
+     *
+     * @return String
+     */
+    public static String getDate()
+    {
+        return dateTimeNow(YYYY_MM_DD);
+    }
+
+    public static final String getTime()
+    {
+        return dateTimeNow(YYYY_MM_DD_HH_MM_SS);
+    }
+
+    public static final String dateTimeNow()
+    {
+        return dateTimeNow(YYYYMMDDHHMMSS);
+    }
+
+    public static final String dateTimeNow(final String format)
+    {
+        return parseDateToStr(format, new Date());
+    }
+
+    public static final String dateTime(final Date date)
+    {
+        return parseDateToStr(YYYY_MM_DD, date);
+    }
+
+    public static final String parseDateToStr(final String format, final Date date)
+    {
+        return new SimpleDateFormat(format).format(date);
+    }
+
+    public static final Date dateTime(final String format, final String ts)
+    {
+        try
+        {
+            return new SimpleDateFormat(format).parse(ts);
+        }
+        catch (ParseException e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 日期路径 即年/月/日 如2018/08/08
+     */
+    public static final String datePath()
+    {
+        Date now = new Date();
+        return DateFormatUtils.format(now, "yyyy/MM/dd");
+    }
+
+    /**
+     * 日期路径 即年/月/日 如20180808
+     */
+    public static final String dateTime()
+    {
+        Date now = new Date();
+        return DateFormatUtils.format(now, "yyyyMMdd");
+    }
+
+    /**
+     * 日期型字符串转化为日期 格式
+     */
+    public static Date parseDate(Object str)
+    {
+        if (str == null)
+        {
+            return null;
+        }
+        try
+        {
+            return parseDate(str.toString(), parsePatterns);
+        }
+        catch (ParseException e)
+        {
+            return null;
+        }
+    }
+
+    /**
+     * 获取服务器启动时间
+     */
+    public static Date getServerStartDate()
+    {
+        long time = ManagementFactory.getRuntimeMXBean().getStartTime();
+        return new Date(time);
+    }
+
+    /**
+     * 计算相差天数
+     */
+    public static int differentDaysByMillisecond(Date date1, Date date2)
+    {
+        return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24)));
+    }
+
+    /**
+     * 计算时间差
+     *
+     * @param endTime 最后时间
+     * @param startTime 开始时间
+     * @return 时间差(天/小时/分钟)
+     */
+    public static String timeDistance(Date endDate, Date startTime)
+    {
+        long nd = 1000 * 24 * 60 * 60;
+        long nh = 1000 * 60 * 60;
+        long nm = 1000 * 60;
+        // long ns = 1000;
+        // 获得两个时间的毫秒时间差异
+        long diff = endDate.getTime() - startTime.getTime();
+        // 计算差多少天
+        long day = diff / nd;
+        // 计算差多少小时
+        long hour = diff % nd / nh;
+        // 计算差多少分钟
+        long min = diff % nd % nh / nm;
+        // 计算差多少秒//输出结果
+        // long sec = diff % nd % nh % nm / ns;
+        return day + "天" + hour + "小时" + min + "分钟";
+    }
+
+    /**
+     * 增加 LocalDateTime ==> Date
+     */
+    public static Date toDate(LocalDateTime temporalAccessor)
+    {
+        ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault());
+        return Date.from(zdt.toInstant());
+    }
+
+    /**
+     * 增加 LocalDate ==> Date
+     */
+    public static Date toDate(LocalDate temporalAccessor)
+    {
+        LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0));
+        ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault());
+        return Date.from(zdt.toInstant());
+    }
+}

+ 139 - 0
PrintServer/src/main/java/com/jd/printserver/utils/FileUtils.java

@@ -0,0 +1,139 @@
+package com.jd.printserver.utils;
+
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.lang.Snowflake;
+import cn.hutool.core.util.IdUtil;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import com.jd.printserver.common.constants.Constants;
+import com.jd.printserver.javafx.entity.PrintParam;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class FileUtils {
+
+
+    public static final Logger log = LoggerFactory.getLogger(FileUtils.class);
+
+    private static final Snowflake snowflake = IdUtil.getSnowflake(1, 1);
+
+    public static PrintParam printParamNew = null;
+
+
+    private static File newFile(String path){
+        if(path.indexOf(Constants.RESOURCE_SEPARATOR) != 0){
+            path = Constants.RESOURCE_SEPARATOR + path;
+        }
+        String root = null;
+        try {
+            root = FileUtils.class.getProtectionDomain().getCodeSource().getLocation().getPath();
+            if (System.getProperty("os.name").contains("dows")) {
+                root = root.substring(1, root.length());
+            }
+            if (root.contains("jar")) {
+                root = root.substring(0, root.lastIndexOf("."));
+                root = root.substring(0, root.lastIndexOf("/"));
+            }
+            root = new File(root).getCanonicalPath();
+        } catch (IOException e) {
+            log.error("获取项目根目录失败", e);
+            return null;
+        }
+        path = root + Constants.RESOURCE_PREFIX + path;
+        System.err.println(path);
+        return new File(path);
+    }
+
+    /**
+     *
+     * @param fileDirectory 文件夹名称
+     * @param fileSuffix 文件后缀
+     * @return
+     */
+    public static String newFilePath(String fileDirectory, String fileSuffix){
+        if(StringUtils.isEmpty(fileDirectory)){
+            fileDirectory = Constants.PRINT_URL_DEFAULT_DIRECTORY;
+        }
+        if(fileDirectory.indexOf(Constants.RESOURCE_SEPARATOR) != 0){
+            fileDirectory = Constants.RESOURCE_SEPARATOR + fileDirectory;
+        }
+        if(fileSuffix == null){
+            fileSuffix = "";
+        }
+        String path = fileDirectory
+                + Constants.RESOURCE_SEPARATOR + DateUtils.dateTimeNow(DateUtils.YYYYMMDDHHMMSS.substring(0, 8))
+                + Constants.RESOURCE_SEPARATOR + DateUtils.dateTimeNow(DateUtils.YYYYMMDDHHMMSS.substring(8))
+                + Constants.RESOURCE_BOTTOM_SEPARATOR + snowflake.nextIdStr() + fileSuffix;
+        try {
+            File file = newFile(path);
+            if(file == null){
+                return null;
+            }
+            return file.getCanonicalPath();
+        } catch (IOException e) {
+            log.error("创建文件" + path + "失败", e);
+            return null;
+        }
+    }
+
+    public synchronized static Boolean wirtePrintParam2File(PrintParam printParam){
+        String path = Constants.PRINT_PARAM_DATA_PATH;
+        File file = newFile(path);
+        if(file == null){
+            return false;
+        }
+        try {
+            JSONObject jsonDataObj = JSONUtil.parseObj(printParam);
+            FileUtil.writeUtf8String(jsonDataObj.toString(), file);
+            printParamNew = printParam;
+            return true;
+        }catch (Exception e){
+            log.error("写入文件中打印参数失败");
+            return false;
+        }
+    }
+
+    public static PrintParam readFile2PrintParam(){
+        if(printParamNew != null){
+            return printParamNew;
+        }
+        String path = Constants.PRINT_PARAM_DATA_PATH;
+        File file = newFile(path);
+        if(file == null){
+            return null;
+        }
+        try {
+            JSONObject jsonObj = JSONUtil.parseObj(FileUtil.readUtf8String(file));
+            return printParamNew = JSONUtil.toBean(jsonObj, PrintParam.class);
+        }catch (Exception e){
+            log.error("读取文件中打印参数失败");
+            return null;
+        }
+    }
+
+    public static File Jar2OutFile(String filePath){
+        try {
+            if(filePath.indexOf(Constants.RESOURCE_SEPARATOR) == 0){
+                filePath = filePath.substring(1);
+            }
+            String outFilePath = Constants.RESOURCE_JAR_OUT_FILE_PREFIX + Constants.RESOURCE_SEPARATOR + filePath;
+            File file = newFile(outFilePath);
+            if(file == null){
+                return null;
+            }
+            InputStream is = FileUtils.class.getClassLoader().getResourceAsStream(filePath);
+
+            FileUtil.writeFromStream(is,file);
+            return file;
+        } catch (Exception e) {
+            log.error("获取"+filePath+"失败", e);
+            return null;
+        }
+    }
+
+
+}

+ 62 - 0
PrintServer/src/main/java/com/jd/printserver/utils/ImageUtils.java

@@ -0,0 +1,62 @@
+package com.jd.printserver.utils;
+
+import cn.hutool.core.img.ImgUtil;
+import cn.hutool.core.io.FileUtil;
+import com.jd.printserver.common.constants.Constants;
+import com.jd.printserver.javafx.entity.PrintParam;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.imageio.ImageIO;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+
+public class ImageUtils {
+    public static final Logger log = LoggerFactory.getLogger(PrintUtils.class);
+    public static File authCut(File infile){
+        String inPath = infile.getPath();
+        int index = inPath.lastIndexOf(".");
+        String outPathPrefix = inPath.substring(0, index);
+        // 图片对象
+        BufferedImage bufferedImage = null;
+        try {
+            bufferedImage = ImageIO.read(new FileInputStream(infile));
+        } catch (IOException e) {
+            return null;
+        }
+
+        String cutPath = outPathPrefix + Constants.RESOURCE_BOTTOM_SEPARATOR + Constants.RESOURCE_CUT_IMAGES_FILENAME + Constants.IMAGE_PNG_SUFFIX;
+        File cutFile = FileUtil.file(cutPath);
+        ImgUtil.cut(
+                bufferedImage,
+                cutFile,
+                //裁剪的矩形区域
+                new Rectangle(1, 1, bufferedImage.getWidth() - 2, bufferedImage.getHeight() - 2)
+        );
+
+        String delBgPath = outPathPrefix + Constants.RESOURCE_BOTTOM_SEPARATOR + Constants.RESOURCE_PRINT_IMAGES_FILENAME + Constants.IMAGE_PNG_SUFFIX;
+        File delBgFile = FileUtil.file(delBgPath);
+        ImgUtil.backgroundRemoval(cutFile, delBgFile, Color.WHITE,0);
+
+
+
+        if(cutFile != null && cutFile.exists()){
+            try {
+                cutFile.delete();
+            }catch (Exception e){
+                log.error("清除截取文件失败", e);
+            }
+        }
+        return delBgFile;
+
+    }
+
+    public static void main(String[] args) {
+        PrintParam printParam =new PrintParam();
+        printParam.setOrientation(2);
+        authCut(new File("G:\\WorkSoft\\PrintServerApp\\app\\profile\\printUrl\\20230712\\104516_1678958674699227136_22.png"));
+    }
+}

+ 151 - 0
PrintServer/src/main/java/com/jd/printserver/utils/JxbrowserUtils.java

@@ -0,0 +1,151 @@
+package com.jd.printserver.utils;
+
+import com.jd.printserver.common.constants.Constants;
+import com.teamdev.jxbrowser.browser.Browser;
+import com.teamdev.jxbrowser.browser.event.ConsoleMessageReceived;
+import com.teamdev.jxbrowser.engine.Engine;
+import com.teamdev.jxbrowser.engine.EngineOptions;
+import com.teamdev.jxbrowser.engine.ProprietaryFeature;
+import com.teamdev.jxbrowser.engine.RenderingMode;
+import com.teamdev.jxbrowser.js.ConsoleMessage;
+import com.teamdev.jxbrowser.view.javafx.BrowserView;
+import javafx.application.Platform;
+import javafx.scene.Scene;
+import javafx.scene.layout.BorderPane;
+import javafx.stage.Stage;
+import org.apache.commons.lang3.time.StopWatch;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class JxbrowserUtils {
+
+
+    public static final Logger log = LoggerFactory.getLogger(JxbrowserUtils.class);
+
+    public static String resultData = null;
+    public static Engine engine = null;
+    public static Browser browser = null;
+    public static Stage stage = null;
+
+    /**
+     * 获取网页返回数据最大等待时间。 30秒
+     */
+    private static Long maxWaitTime = Constants.JXBROWSER_CONSOLE_BASE64_WAIT_TIME;
+
+    static {
+        System.setProperty("jxbrowser.license.key", Constants.JXBROWSER_LICENSE_KEY);
+
+    }
+
+    public static String getResultData(final String url){
+        return getResultData(url,null, maxWaitTime);
+    }
+
+    public static String getResultData(final String url, final Long maxWaitTime){
+        return getResultData(url,null, maxWaitTime);
+    }
+
+
+    public static void getResultData(final String url,final Url2DataInterface url2DataInterface){
+        getResultData(url, url2DataInterface, null);
+    }
+
+    private synchronized static String getResultData(final String url, final Url2DataInterface url2DataInterface, final Long maxWaitTime){
+        resultData = null;
+        Platform.runLater(new Runnable() {
+            @Override
+            public void run() {
+                log.info("初始化隐藏浏览器");
+                if(stage == null || browser == null || browser.isClosed()){
+                    engine = Engine.newInstance(
+                            EngineOptions.newBuilder(RenderingMode.OFF_SCREEN)
+                                    .enableProprietaryFeature(ProprietaryFeature.AAC)
+                                    .enableProprietaryFeature(ProprietaryFeature.H_264)
+                                    .build());
+                    browser = engine.newBrowser();
+                    stage = new Stage();
+                    BrowserView view = BrowserView.newInstance(browser);
+                    Scene scene = new Scene(new BorderPane(view), 0, 0);
+                    stage.setScene(scene);
+                    //stage.show();
+                    //js打印监听
+                    browser.on(ConsoleMessageReceived.class, event -> {
+                        ConsoleMessage consoleMessage = event.consoleMessage();
+                        String message = consoleMessage.message();
+                        if(StringUtils.isNotEmpty(message)){
+                            String key = Constants.JXBROWSER_CONSOLE_BASE64_PREFIX;
+                            if(message.indexOf(key) == 0){
+                                if(message.length() > key.length()){
+                                    log.info("浏览器console.info(message)输出结果:{}", message);
+                                    message = message.substring(key.length());
+                                    if(url2DataInterface != null){
+                                        url2DataInterface.getData(message);
+                                    }else{
+                                        resultData = message;
+                                    }
+                                }
+                            }
+                        }
+                    });
+                }
+                browser.navigation().loadUrl(url);
+            }
+        });
+        if(maxWaitTime != null){
+            StopWatch stopWatch = new StopWatch();
+            stopWatch.start();
+            while (resultData == null){
+                try {
+                    Thread.sleep(100);
+                } catch (InterruptedException e) {
+                }
+                stopWatch.split();
+                if(stopWatch.getTime() >= maxWaitTime){
+                    break;
+                }
+            }
+            stopWatch.stop();
+            if(resultData != null){
+                log.info("浏览器{}毫秒返回结果", stopWatch.getTime());
+            }else{
+                log.info("浏览器{}毫秒后未返回结果,强制结束", stopWatch.getTime());
+            }
+        }
+        return resultData;
+    }
+
+    private static  void closeAll(){
+        if(engine != null && !engine.isClosed()){
+            try {
+                engine.close();
+                engine = null;
+            }catch (Exception e){
+                log.warn("关闭浏览器引擎失败", e);
+            }
+        }
+        if(browser != null && !browser.isClosed()){
+            try {
+                browser.close();
+                browser = null;
+            }catch (Exception e){
+                log.warn("关闭浏览器失败", e);
+            }
+        }
+        if(stage != null){
+            try {
+                Platform.runLater(new Runnable() {
+                    @Override
+                    public void run() {
+
+                        stage.close();
+                    }
+                });
+                stage = null;
+            }catch (Exception e){
+                log.warn("关闭窗口失败", e);
+            }
+        }
+
+    }
+}

+ 70 - 0
PrintServer/src/main/java/com/jd/printserver/utils/PortAvailableUtils.java

@@ -0,0 +1,70 @@
+package com.jd.printserver.utils;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.NetworkInterface;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+
+public class PortAvailableUtils {
+    /**
+     * 查看端口是否被占用
+     *
+     * @param port
+     * @return
+     */
+    public static boolean isPortAvailable(int port) {
+        boolean flag = false;
+        List<String> ipList = new ArrayList<>();
+        try {
+            Enumeration<NetworkInterface> netInterfaces = NetworkInterface.getNetworkInterfaces();
+            while (netInterfaces.hasMoreElements()) {
+                NetworkInterface nif = netInterfaces.nextElement();
+                Enumeration<InetAddress> iparray = nif.getInetAddresses();
+                while (iparray.hasMoreElements()) {
+                    ipList.add(iparray.nextElement().getHostAddress());
+                }
+            }
+            for (int i = 0; i < ipList.size(); i++) {
+                flag = bindPort(ipList.get(i), port);
+                if (!flag) {
+                    break;
+                }
+            }
+            if (flag) {
+                //未被占用
+                return true;
+            } else {
+                //已被占用
+                return false;
+            }
+        } catch (Exception e) {
+            return false;
+        }
+    }
+
+    /**
+     * 查看是否能绑定ip端口
+     *
+     * @param host
+     * @param port
+     * @return
+     * @throws Exception
+     */
+    private static Boolean bindPort(String host, int port) throws Exception {
+        boolean flag = false;
+        try {
+            Socket socket = new Socket();
+            socket.bind(new InetSocketAddress(host, port));
+            socket.close();
+            flag = true;
+        } catch (Exception e) {
+            flag = false;
+            e.printStackTrace();
+        } finally {
+            return flag;
+        }
+    }
+}

+ 239 - 0
PrintServer/src/main/java/com/jd/printserver/utils/PrintUtils.java

@@ -0,0 +1,239 @@
+package com.jd.printserver.utils;
+
+import com.jd.printserver.javafx.entity.PrintParam;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDPageContentStream;
+import org.apache.pdfbox.pdmodel.common.PDRectangle;
+import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
+import org.apache.pdfbox.printing.PDFPrintable;
+import org.apache.pdfbox.printing.Scaling;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.print.*;
+import javax.print.attribute.*;
+import javax.print.attribute.standard.*;
+import java.awt.print.*;
+import java.io.*;
+import java.math.BigDecimal;
+
+public class PrintUtils {
+
+
+    public static final Logger log = LoggerFactory.getLogger(PrintUtils.class);
+
+    public static void main(String[] args) throws PrinterException {
+        PrintParam printParam = new PrintParam();
+        printParam.setMarginTop(5);
+        printParam.setMarginLeft(5);
+        printParam.setMarginRight(5);
+        printParam.setMarginBottom(5);
+        printParam.setOrientation(0);
+        printParam.setScale(0f);
+        PDFprint("G:\\WorkSoft\\STS\\workspace\\PrintServer\\target\\classes\\profile\\printUrl\\20230714\\175210_1679790882943012864_h.png", printParam);
+    }
+
+
+    public static Boolean PDFprint(String inputImgPath, PrintParam printParam){
+
+        try (PDDocument document = new PDDocument()) {
+            //单位:像素
+            // 10mm边距, 对应 28px
+            // 28px = 10mm * 72 / 25.4
+            int marginLeft = printParam.mm2px(printParam.getMarginLeft());
+            int marginTop = printParam.mm2px(printParam.getMarginTop());
+            int marginRight = printParam.mm2px(printParam.getMarginRight());
+            int marginBottom = printParam.mm2px(printParam.getMarginBottom());
+            int paperWidth = printParam.mm2px(printParam.getWidth());
+            int paperHeight = printParam.mm2px(printParam.getHeight());
+            int printWidth = paperWidth - marginLeft -marginRight;
+            int printHeight = paperHeight - marginTop - marginBottom;
+            int pdfPaperWidth = 0;
+            int pdfPaperHeight = 0;
+            int pdfWidth = 0;
+            int pdfHeight = 0;
+
+            PDImageXObject pdImage = PDImageXObject.createFromFile(inputImgPath, document);
+            log.info("pdImage.getWidth():" + pdImage.getWidth());
+            log.info("pdImage.getHeight():" + pdImage.getHeight());
+
+            if(printParam.getOrientation() == 0){
+                // 纵向缩放比例
+                float zScale = getMinScale(printWidth, printHeight, pdImage.getWidth(), pdImage.getHeight());
+                // 横向缩放比例
+                float hScale = getMinScale(printHeight, printWidth, pdImage.getWidth(), pdImage.getHeight());
+
+                if(zScale < hScale){
+                    // 横向需要缩小比例小
+                    printParam.setOrientation(1);
+                }else{
+                    // 纵向需要缩小比例小
+                    printParam.setOrientation(2);
+                }
+
+            }
+            if (printParam.getOrientation() == 1) {
+                // 横向(宽高互反)
+                pdfPaperWidth = paperHeight;
+                pdfPaperHeight = paperWidth;
+                pdfWidth = printHeight;
+                pdfHeight = printWidth;
+            } else {
+                //纵向
+                pdfPaperWidth = paperWidth;
+                pdfPaperHeight = paperHeight;
+                pdfWidth = printWidth;
+                pdfHeight = printHeight;
+            }
+            float scale = printParam.getScale();
+            if(scale == 0f){
+                //缩小
+                scale = getMinScale(pdfWidth, pdfHeight, pdImage.getWidth(), pdImage.getHeight());
+            }
+
+
+//             PDPage page = new PDPage(PDRectangle.A4);
+            PDPage page = new PDPage(new PDRectangle(pdfPaperWidth, pdfPaperHeight));
+            document.addPage(page);
+
+            int imageWidth = BigDecimal.valueOf(pdImage.getWidth())
+                    //乘以
+                    .multiply(BigDecimal.valueOf(scale))
+                    // 丢弃小数部分,加一
+                    .setScale(0,BigDecimal.ROUND_DOWN)
+                    .intValue();
+            int imageHeight = BigDecimal.valueOf(pdImage.getHeight())
+                    //乘以
+                    .multiply(BigDecimal.valueOf(scale))
+                    // 丢弃小数部分,加一
+                    .setScale(0,BigDecimal.ROUND_DOWN)
+                    .intValue();
+
+
+            try (PDPageContentStream contentStream = new PDPageContentStream(document, page)) {
+                contentStream.drawImage(
+                        pdImage,
+                        marginLeft + 1,
+                        pdfPaperHeight - imageHeight - marginBottom + 1,
+                        imageWidth - 2,
+                        imageHeight - 2);
+            } catch (IOException e) {
+                log.error("写入PDF失败", e);
+                return false;
+            }
+
+
+            document.save(inputImgPath.substring(0,inputImgPath.lastIndexOf(".")) + ".pdf");
+
+            PrinterJob printJob = PrinterJob.getPrinterJob();
+            printJob.setJobName(printParam.getJobName());
+            //默认打印机
+            PrintService printService = PrintServiceLookup.lookupDefaultPrintService();
+
+            if (printService != null) {
+                try {
+                    printJob.setPrintService(printService);
+                } catch (PrinterException e) {
+                    log.error("打印失败,打印机不可用,请检查");
+                    return false;
+                }
+            } else {
+                log.error("打印失败,未找到打印机,请检查");
+                return false;
+            }
+            // 设置纸张及缩放
+            // ACTUAL_SIZE 以100%比例打印图像
+            // SHRINK_TO_FIT 如果需要,缩小图像以适应页面
+            // STRETCH_TO_FIT 如果需要,拉伸图像以填充页面
+            // SCALE_TO_FIT 根据需要拉伸或收缩图像以填充页面
+            PDFPrintable pdfPrintable = new PDFPrintable(document, Scaling.ACTUAL_SIZE);
+
+
+            Paper paper = new Paper();
+            paper.setSize(paperWidth, paperHeight);
+            // 设置边距
+            paper.setImageableArea(marginLeft, marginTop, printWidth, printHeight);
+            // 自定义页面设置
+            PageFormat pageFormat = new PageFormat();
+            //设置打印方向
+            // LANDSCAPE 横向。原点在纸张的左下角,x从下到上,y从左到右。请注意,这不是Macintosh的横向视图,而是Window和PostScript的横向视图
+            // PORTRAIT 纵向。原点位于纸张的左上角,x向右,y沿纸张向下
+            // REVERSE_LANDSCAPE 横向。原点在纸张的右上角,x从上到下,y从右到左。请注意,这是Macintosh环境
+            //横向 LANDSCAPE 纵向 PORTRAIT
+            switch (printParam.getOrientation()) {
+                case 1:
+                    pageFormat.setOrientation(PageFormat.LANDSCAPE);
+                    break;
+                case 2:
+                    pageFormat.setOrientation(PageFormat.PORTRAIT);
+                    break;
+                default:
+                    pageFormat.setOrientation(PageFormat.LANDSCAPE);
+            }
+
+            pageFormat.setPaper(paper);
+            //设置多页打印
+            Book book = new Book();
+
+
+            book.append(pdfPrintable, pageFormat, document.getNumberOfPages());
+            printJob.setPageable(book);
+            //设置打印份数
+            printJob.setCopies(printParam.getCopies());
+            //添加打印属性
+            HashPrintRequestAttributeSet pars = new HashPrintRequestAttributeSet();
+            // pars.add(MediaSizeName.ISO_A4);//A4纸张
+
+            // 单双面
+            // ONE_SIDED(0) 单面打印
+            // DUPLEX(1) 双面打印,长边翻转
+            // TUMBLE(2) 双面打印,短边翻转
+            switch (printParam.getSides()) {
+                case 1:
+                    pars.add(Sides.ONE_SIDED);
+                    break;
+                case 2:
+                    pars.add(Sides.DUPLEX);
+                    break;
+                case 3:
+                    pars.add(Sides.TUMBLE);
+                    break;
+                default:
+                    pars.add(Sides.ONE_SIDED);
+            }
+            try {
+                printJob.print(pars);
+            } catch (PrinterException e) {
+                log.error("打印失败,打印机不可用或打印参数错误,请检查");
+                return false;
+            }
+
+            return true;
+        } catch (IOException e) {
+            log.error("打印PDF失败", e);
+            return false;
+        }
+    }
+
+    public static float getMinScale(int printWidth, int printHeight, int imageWidth, int imageHeight){
+        float scale;
+        float wScale = 1f;
+        if(printWidth < imageWidth){
+
+            wScale = BigDecimal.valueOf(printWidth)
+                    .divide(BigDecimal.valueOf(imageWidth),4, BigDecimal.ROUND_DOWN).floatValue();
+        }
+        float hScale = 1f;
+        if(printHeight < imageHeight){
+            hScale = BigDecimal.valueOf(printHeight)
+                    .divide(BigDecimal.valueOf(imageHeight), 4, BigDecimal.ROUND_DOWN).floatValue();
+        }
+        if(wScale < hScale){
+            scale = wScale;
+        }else{
+            scale = hScale;
+        }
+        return scale;
+    }
+}

+ 611 - 0
PrintServer/src/main/java/com/jd/printserver/utils/StringUtils.java

@@ -0,0 +1,611 @@
+package com.jd.printserver.utils;
+
+
+import cn.hutool.core.text.StrFormatter;
+import com.jd.printserver.common.constants.Constants;
+import org.springframework.util.AntPathMatcher;
+
+import java.util.*;
+
+/**
+ * 字符串工具类
+ *
+ * @author ruoyi
+ */
+public class StringUtils extends org.apache.commons.lang3.StringUtils
+{
+    /** 空字符串 */
+    private static final String NULLSTR = "";
+
+    /** 下划线 */
+    private static final char SEPARATOR = '_';
+
+    /**
+     * 获取参数不为空值
+     *
+     * @param value defaultValue 要判断的value
+     * @return value 返回值
+     */
+    public static <T> T nvl(T value, T defaultValue)
+    {
+        return value != null ? value : defaultValue;
+    }
+
+    /**
+     * * 判断一个Collection是否为空, 包含List,Set,Queue
+     *
+     * @param coll 要判断的Collection
+     * @return true:为空 false:非空
+     */
+    public static boolean isEmpty(Collection<?> coll)
+    {
+        return isNull(coll) || coll.isEmpty();
+    }
+
+    /**
+     * * 判断一个Collection是否非空,包含List,Set,Queue
+     *
+     * @param coll 要判断的Collection
+     * @return true:非空 false:空
+     */
+    public static boolean isNotEmpty(Collection<?> coll)
+    {
+        return !isEmpty(coll);
+    }
+
+    /**
+     * * 判断一个对象数组是否为空
+     *
+     * @param objects 要判断的对象数组
+     ** @return true:为空 false:非空
+     */
+    public static boolean isEmpty(Object[] objects)
+    {
+        return isNull(objects) || (objects.length == 0);
+    }
+
+    /**
+     * * 判断一个对象数组是否非空
+     *
+     * @param objects 要判断的对象数组
+     * @return true:非空 false:空
+     */
+    public static boolean isNotEmpty(Object[] objects)
+    {
+        return !isEmpty(objects);
+    }
+
+    /**
+     * * 判断一个Map是否为空
+     *
+     * @param map 要判断的Map
+     * @return true:为空 false:非空
+     */
+    public static boolean isEmpty(Map<?, ?> map)
+    {
+        return isNull(map) || map.isEmpty();
+    }
+
+    /**
+     * * 判断一个Map是否为空
+     *
+     * @param map 要判断的Map
+     * @return true:非空 false:空
+     */
+    public static boolean isNotEmpty(Map<?, ?> map)
+    {
+        return !isEmpty(map);
+    }
+
+    /**
+     * * 判断一个字符串是否为空串
+     *
+     * @param str String
+     * @return true:为空 false:非空
+     */
+    public static boolean isEmpty(String str)
+    {
+        return isNull(str) || NULLSTR.equals(str.trim());
+    }
+
+    /**
+     * * 判断一个字符串是否为非空串
+     *
+     * @param str String
+     * @return true:非空串 false:空串
+     */
+    public static boolean isNotEmpty(String str)
+    {
+        return !isEmpty(str);
+    }
+
+    /**
+     * * 判断一个对象是否为空
+     *
+     * @param object Object
+     * @return true:为空 false:非空
+     */
+    public static boolean isNull(Object object)
+    {
+        return object == null;
+    }
+
+    /**
+     * * 判断一个对象是否非空
+     *
+     * @param object Object
+     * @return true:非空 false:空
+     */
+    public static boolean isNotNull(Object object)
+    {
+        return !isNull(object);
+    }
+
+    /**
+     * * 判断一个对象是否是数组类型(Java基本型别的数组)
+     *
+     * @param object 对象
+     * @return true:是数组 false:不是数组
+     */
+    public static boolean isArray(Object object)
+    {
+        return isNotNull(object) && object.getClass().isArray();
+    }
+
+    /**
+     * 去空格
+     */
+    public static String trim(String str)
+    {
+        return (str == null ? "" : str.trim());
+    }
+
+    /**
+     * 截取字符串
+     *
+     * @param str 字符串
+     * @param start 开始
+     * @return 结果
+     */
+    public static String substring(final String str, int start)
+    {
+        if (str == null)
+        {
+            return NULLSTR;
+        }
+
+        if (start < 0)
+        {
+            start = str.length() + start;
+        }
+
+        if (start < 0)
+        {
+            start = 0;
+        }
+        if (start > str.length())
+        {
+            return NULLSTR;
+        }
+
+        return str.substring(start);
+    }
+
+    /**
+     * 截取字符串
+     *
+     * @param str 字符串
+     * @param start 开始
+     * @param end 结束
+     * @return 结果
+     */
+    public static String substring(final String str, int start, int end)
+    {
+        if (str == null)
+        {
+            return NULLSTR;
+        }
+
+        if (end < 0)
+        {
+            end = str.length() + end;
+        }
+        if (start < 0)
+        {
+            start = str.length() + start;
+        }
+
+        if (end > str.length())
+        {
+            end = str.length();
+        }
+
+        if (start > end)
+        {
+            return NULLSTR;
+        }
+
+        if (start < 0)
+        {
+            start = 0;
+        }
+        if (end < 0)
+        {
+            end = 0;
+        }
+
+        return str.substring(start, end);
+    }
+
+    /**
+     * 格式化文本, {} 表示占位符<br>
+     * 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
+     * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
+     * 例:<br>
+     * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br>
+     * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br>
+     * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>
+     *
+     * @param template 文本模板,被替换的部分用 {} 表示
+     * @param params 参数值
+     * @return 格式化后的文本
+     */
+    public static String format(String template, Object... params)
+    {
+        if (isEmpty(params) || isEmpty(template))
+        {
+            return template;
+        }
+        return StrFormatter.format(template, params);
+    }
+
+    /**
+     * 是否为http(s)://开头
+     *
+     * @param link 链接
+     * @return 结果
+     */
+    public static boolean ishttp(String link)
+    {
+        return StringUtils.startsWithAny(link, Constants.HTTP, Constants.HTTPS);
+    }
+
+    /**
+     * 字符串转set
+     *
+     * @param str 字符串
+     * @param sep 分隔符
+     * @return set集合
+     */
+    public static final Set<String> str2Set(String str, String sep)
+    {
+        return new HashSet<String>(str2List(str, sep, true, false));
+    }
+
+    /**
+     * 字符串转list
+     *
+     * @param str 字符串
+     * @param sep 分隔符
+     * @param filterBlank 过滤纯空白
+     * @param trim 去掉首尾空白
+     * @return list集合
+     */
+    public static final List<String> str2List(String str, String sep, boolean filterBlank, boolean trim)
+    {
+        List<String> list = new ArrayList<String>();
+        if (StringUtils.isEmpty(str))
+        {
+            return list;
+        }
+
+        // 过滤空白字符串
+        if (filterBlank && StringUtils.isBlank(str))
+        {
+            return list;
+        }
+        String[] split = str.split(sep);
+        for (String string : split)
+        {
+            if (filterBlank && StringUtils.isBlank(string))
+            {
+                continue;
+            }
+            if (trim)
+            {
+                string = string.trim();
+            }
+            list.add(string);
+        }
+
+        return list;
+    }
+
+    /**
+     * 判断给定的collection列表中是否包含数组array 判断给定的数组array中是否包含给定的元素value
+     *
+     * @param collection 给定的集合
+     * @param array 给定的数组
+     * @return boolean 结果
+     */
+    public static boolean containsAny(Collection<String> collection, String... array)
+    {
+        if (isEmpty(collection) || isEmpty(array))
+        {
+            return false;
+        }
+        else
+        {
+            for (String str : array)
+            {
+                if (collection.contains(str))
+                {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    /**
+     * 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写
+     *
+     * @param cs 指定字符串
+     * @param searchCharSequences 需要检查的字符串数组
+     * @return 是否包含任意一个字符串
+     */
+    public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences)
+    {
+        if (isEmpty(cs) || isEmpty(searchCharSequences))
+        {
+            return false;
+        }
+        for (CharSequence testStr : searchCharSequences)
+        {
+            if (containsIgnoreCase(cs, testStr))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 驼峰转下划线命名
+     */
+    public static String toUnderScoreCase(String str)
+    {
+        if (str == null)
+        {
+            return null;
+        }
+        StringBuilder sb = new StringBuilder();
+        // 前置字符是否大写
+        boolean preCharIsUpperCase = true;
+        // 当前字符是否大写
+        boolean curreCharIsUpperCase = true;
+        // 下一字符是否大写
+        boolean nexteCharIsUpperCase = true;
+        for (int i = 0; i < str.length(); i++)
+        {
+            char c = str.charAt(i);
+            if (i > 0)
+            {
+                preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1));
+            }
+            else
+            {
+                preCharIsUpperCase = false;
+            }
+
+            curreCharIsUpperCase = Character.isUpperCase(c);
+
+            if (i < (str.length() - 1))
+            {
+                nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1));
+            }
+
+            if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase)
+            {
+                sb.append(SEPARATOR);
+            }
+            else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase)
+            {
+                sb.append(SEPARATOR);
+            }
+            sb.append(Character.toLowerCase(c));
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * 是否包含字符串
+     * 
+     * @param str 验证字符串
+     * @param strs 字符串组
+     * @return 包含返回true
+     */
+    public static boolean inStringIgnoreCase(String str, String... strs)
+    {
+        if (str != null && strs != null)
+        {
+            for (String s : strs)
+            {
+                if (str.equalsIgnoreCase(trim(s)))
+                {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld
+     * 
+     * @param name 转换前的下划线大写方式命名的字符串
+     * @return 转换后的驼峰式命名的字符串
+     */
+    public static String convertToCamelCase(String name)
+    {
+        StringBuilder result = new StringBuilder();
+        // 快速检查
+        if (name == null || name.isEmpty())
+        {
+            // 没必要转换
+            return "";
+        }
+        else if (!name.contains("_"))
+        {
+            // 不含下划线,仅将首字母大写
+            return name.substring(0, 1).toUpperCase() + name.substring(1);
+        }
+        // 用下划线将原始字符串分割
+        String[] camels = name.split("_");
+        for (String camel : camels)
+        {
+            // 跳过原始字符串中开头、结尾的下换线或双重下划线
+            if (camel.isEmpty())
+            {
+                continue;
+            }
+            // 首字母大写
+            result.append(camel.substring(0, 1).toUpperCase());
+            result.append(camel.substring(1).toLowerCase());
+        }
+        return result.toString();
+    }
+
+    /**
+     * 驼峰式命名法
+     * 例如:user_name->userName
+     */
+    public static String toCamelCase(String s)
+    {
+        if (s == null)
+        {
+            return null;
+        }
+        if (s.indexOf(SEPARATOR) == -1)
+        {
+            return s;
+        }
+        s = s.toLowerCase();
+        StringBuilder sb = new StringBuilder(s.length());
+        boolean upperCase = false;
+        for (int i = 0; i < s.length(); i++)
+        {
+            char c = s.charAt(i);
+
+            if (c == SEPARATOR)
+            {
+                upperCase = true;
+            }
+            else if (upperCase)
+            {
+                sb.append(Character.toUpperCase(c));
+                upperCase = false;
+            }
+            else
+            {
+                sb.append(c);
+            }
+        }
+        return sb.toString();
+    }
+
+    /**
+     * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串
+     * 
+     * @param str 指定字符串
+     * @param strs 需要检查的字符串数组
+     * @return 是否匹配
+     */
+    public static boolean matches(String str, List<String> strs)
+    {
+        if (isEmpty(str) || isEmpty(strs))
+        {
+            return false;
+        }
+        for (String pattern : strs)
+        {
+            if (isMatch(pattern, str))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 判断url是否与规则配置: 
+     *  表示单个字符; 
+     * * 表示一层路径内的任意字符串,不可跨层级; 
+     * ** 表示任意层路径;
+     * 
+     * @param pattern 匹配规则
+     * @param url 需要匹配的url
+     * @return
+     */
+    public static boolean isMatch(String pattern, String url)
+    {
+        AntPathMatcher matcher = new AntPathMatcher();
+        return matcher.match(pattern, url);
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <T> T cast(Object obj)
+    {
+        return (T) obj;
+    }
+
+    /**
+     * 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。
+     * 
+     * @param num 数字对象
+     * @param size 字符串指定长度
+     * @return 返回数字的字符串格式,该字符串为指定长度。
+     */
+    public static final String padl(final Number num, final int size)
+    {
+        return padl(num.toString(), size, '0');
+    }
+
+    /**
+     * 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。
+     * 
+     * @param s 原始字符串
+     * @param size 字符串指定长度
+     * @param c 用于补齐的字符
+     * @return 返回指定长度的字符串,由原字符串左补齐或截取得到。
+     */
+    public static final String padl(final String s, final int size, final char c)
+    {
+        final StringBuilder sb = new StringBuilder(size);
+        if (s != null)
+        {
+            final int len = s.length();
+            if (s.length() <= size)
+            {
+                for (int i = size - len; i > 0; i--)
+                {
+                    sb.append(c);
+                }
+                sb.append(s);
+            }
+            else
+            {
+                return s.substring(len - size, len);
+            }
+        }
+        else
+        {
+            for (int i = size; i > 0; i--)
+            {
+                sb.append(c);
+            }
+        }
+        return sb.toString();
+    }
+}

+ 5 - 0
PrintServer/src/main/java/com/jd/printserver/utils/Url2DataInterface.java

@@ -0,0 +1,5 @@
+package com.jd.printserver.utils;
+
+public interface Url2DataInterface {
+    public void getData(String data);
+}

+ 0 - 0
PrintServer/src/main/resources/application.properties


+ 21 - 0
PrintServer/src/main/resources/application.yml

@@ -0,0 +1,21 @@
+#spring:
+#  profiles:
+#    active: dev
+#  servlet:
+#    multipart:
+#      enabled: true
+#      max-file-size: 20MB
+#      max-request-size: 200MB
+server:
+  port: 8084
+logging:
+  level:
+    root: info
+  file:
+    name: logs/printServer.log
+data:
+  app:
+    lable:
+      title: "川仪打印服务"
+      exit: "退出"
+      setting: "配置"

BIN
PrintServer/src/main/resources/icon/report_print.ico


BIN
PrintServer/src/main/resources/images/report_print.png


+ 14 - 0
PrintServer/src/main/resources/view/main.fxml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import java.lang.*?>
+<?import java.util.*?>
+<?import javafx.scene.*?>
+<?import javafx.scene.control.*?>
+<?import javafx.scene.layout.*?>
+
+<AnchorPane xmlns="http://javafx.com/javafx"
+            xmlns:fx="http://javafx.com/fxml"
+            fx:controller="com.jd.printserver.javafx.controller.MainController"
+            prefHeight="400.0" prefWidth="600.0">
+
+</AnchorPane>

+ 80 - 0
PrintServer/src/main/resources/view/setting.fxml

@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import java.lang.*?>
+<?import javafx.scene.effect.*?>
+<?import javafx.scene.text.*?>
+<?import javafx.scene.control.*?>
+<?import javafx.scene.layout.*?>
+
+<AnchorPane prefHeight="257.0" prefWidth="602.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.jd.printserver.javafx.controller.SettingController">
+   <children>
+      <ChoiceBox fx:id="pageSizeChoiceBox" layoutX="126.0" layoutY="29.0" onAction="#pageSizeChoiceOnAction" prefHeight="23.0" prefWidth="213.0">
+         <effect>
+            <Blend />
+         </effect>
+      </ChoiceBox>
+      <ChoiceBox fx:id="orientationChoiceBox" layoutX="439.0" layoutY="29.0" prefHeight="23.0" prefWidth="111.0">
+         <effect>
+            <Blend />
+         </effect>
+      </ChoiceBox>
+      <Label layoutX="383.0" layoutY="68.0" text="份数">
+         <font>
+            <Font size="14.0" />
+         </font>
+      </Label>
+      <Label layoutX="383.0" layoutY="29.0" text="方向">
+         <font>
+            <Font size="14.0" />
+         </font>
+      </Label>
+      <Label layoutX="49.0" layoutY="29.0" text="页面尺寸">
+         <font>
+            <Font size="14.0" />
+         </font>
+      </Label>
+      <Label layoutX="133.0" layoutY="68.0" text="宽度" />
+      <Label layoutX="244.0" layoutY="68.0" text="高度" />
+      <Label layoutX="203.0" layoutY="68.0" text="mm" />
+      <Label layoutX="314.0" layoutY="68.0" text="mm" />
+      <Label layoutX="203.0" layoutY="107.0" text="mm" />
+      <Label layoutX="139.0" layoutY="107.0" text="左" />
+      <Label layoutX="314.0" layoutY="107.0" text="mm" />
+      <Label layoutX="250.0" layoutY="107.0" text="上" />
+      <Label layoutX="203.0" layoutY="146.0" text="mm" />
+      <Label layoutX="139.0" layoutY="146.0" text="右" />
+      <Label layoutX="314.0" layoutY="146.0" text="mm" />
+      <Label layoutX="250.0" layoutY="146.0" text="下" />
+      <Label layoutX="49.0" layoutY="107.0" text="页面边距">
+         <font>
+            <Font size="14.0" />
+         </font>
+      </Label>
+      <Button layoutX="236.0" layoutY="206.0" mnemonicParsing="false" onMouseClicked="#saveSettingOnMouseClicked" text="保存设置" />
+      <Button layoutX="318.0" layoutY="206.0" mnemonicParsing="false" onMouseClicked="#revertDefaultSettingOnMouseClicked" text="恢复默认" />
+      <TextField fx:id="paperWidthTextField" editable="false" layoutX="158.0" layoutY="68.0" prefHeight="23.0" prefWidth="45.0" />
+      <TextField fx:id="paperHeightTextField" editable="false" layoutX="268.0" layoutY="68.0" prefHeight="23.0" prefWidth="45.0" />
+      <TextField fx:id="paperLeftTextField" layoutX="158.0" layoutY="107.0" prefHeight="23.0" prefWidth="45.0" />
+      <TextField fx:id="paperTopTextField" layoutX="268.0" layoutY="107.0" prefHeight="23.0" prefWidth="45.0" />
+      <TextField fx:id="paperBottomTextField" layoutX="268.0" layoutY="146.0" prefHeight="23.0" prefWidth="45.0" />
+      <TextField fx:id="paperRightTextField" layoutX="158.0" layoutY="146.0" prefHeight="23.0" prefWidth="45.0" />
+      <Button fx:id="closeWindowButton" layoutX="405.0" layoutY="206.0" mnemonicParsing="false" onMouseClicked="#closeWindowOnMouseClicked" text="关闭" />
+      <Label layoutX="355.0" layoutY="146.0" text="作业名称">
+         <font>
+            <Font size="14.0" />
+         </font>
+      </Label>
+      <TextField fx:id="jobNameTextField" layoutX="439.0" layoutY="146.0" prefHeight="23.0" prefWidth="111.0" />
+      <TextField fx:id="copiesTextField" layoutX="439.0" layoutY="68.0" prefHeight="23.0" prefWidth="111.0" />
+      <Label layoutX="355.0" layoutY="107.0" text="缩放比例">
+         <font>
+            <Font size="14.0" />
+         </font>
+      </Label>
+      <ChoiceBox fx:id="scaleChoiceBox" layoutX="439.0" layoutY="107.0" prefHeight="23.0" prefWidth="111.0">
+         <effect>
+            <Blend />
+         </effect>
+      </ChoiceBox>
+   </children>
+</AnchorPane>

+ 13 - 0
PrintServer/src/test/java/com/jd/printserver/PrintServerApplicationTests.java

@@ -0,0 +1,13 @@
+package com.jd.printserver;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+//@SpringBootTest
+class PrintServerApplicationTests {
+
+    //@Test
+    void contextLoads() {
+    }
+
+}

Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff