page2pdf_server 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/.babelrc +3 -3
  2. package/.eslintrc +32 -32
  3. package/.github/ISSUE_TEMPLATE/bug_report.md +28 -28
  4. package/.github/ISSUE_TEMPLATE/feature_request.md +15 -15
  5. package/.github/ISSUE_TEMPLATE/refactoring.md +15 -15
  6. package/.github/PULL_REQUEST_TEMPLATE.md +18 -18
  7. package/.github/stale.yml +17 -17
  8. package/.github/workflows/cd.yml +75 -75
  9. package/.github/workflows/ci.yml +36 -36
  10. package/.husky/pre-commit +6 -6
  11. package/.husky/pre-push +4 -4
  12. package/.prettierrc +8 -8
  13. package/.vscode/settings.json +3 -3
  14. package/LICENSE +17 -17
  15. package/README.md +54 -23
  16. package/ecosystem.config.js +41 -41
  17. package/jest.config.js +35 -35
  18. package/nodemon.json +6 -6
  19. package/package.json +8 -10
  20. package/src/CSS/345/205/274/345/256/271/346/200/247.txt +69 -0
  21. package/src/app.ts +7 -2
  22. package/src/components/home/controller.ts +2 -2
  23. package/src/components/home/pdfController.ts +9 -5
  24. package/src/components/home/services.ts +3 -3
  25. package/src/components/home/splitController.ts +9 -5
  26. package/src/components/home/validators.ts +1 -1
  27. package/src/db/home.ts +3 -3
  28. package/src/helpers/apiResponse.ts +1 -1
  29. package/src/helpers/error/TimeOutError.ts +1 -1
  30. package/src/helpers/loggers.ts +1 -1
  31. package/src/index.ts +4 -1
  32. package/src/middlewares/errorHandler.ts +1 -1
  33. package/src/routes/index.ts +2 -2
  34. package/src/types/request/config.ts +10 -5
  35. package/src/types/request/home.ts +1 -1
  36. package/src/utils/pdfgen.ts +66 -16
  37. package/src//346/265/213/350/257/225.txt +26 -2
  38. package/test//346/211/223/345/215/260/346/234/215/345/212/241.http +17 -0
  39. package/tsconfig.json +41 -41
  40. package/tslint.json +22 -22
  41. package/.idea/codeStyles/Project.xml +0 -58
  42. package/.idea/codeStyles/codeStyleConfig.xml +0 -5
  43. package/.idea/encodings.xml +0 -7
  44. package/.idea/inspectionProfiles/Project_Default.xml +0 -7
  45. package/.idea/modules.xml +0 -8
  46. package/.idea/page2pdf-server.iml +0 -12
  47. package/.idea/tenstack-starter-main.iml +0 -12
  48. package/.idea/vcs.xml +0 -6
package/README.md CHANGED
@@ -42,12 +42,17 @@ chrome-remote-interface不会为您启动Chrome。本来想用chrome-remote-inte
42
42
  注意,动态网页在Chrome浏览器直接打印预览的效果应该和本软件转换输出的pdf一致效果,为打印设置的css必须在应用层中自己去添加的。
43
43
  纯粹静态html的打印转换pdf,且是标准纸张大小的横竖纸张方向掺杂的这种情况:考虑一次提取一部分pageRanges[]吗,分解多个的files[url,out]独立生成之后做合并。
44
44
 
45
- ## Prerequisites
45
+ ## Prerequisites 必须配置的
46
46
 
47
- - [Node.js](https://nodejs.org) (`>= 18.0.0`)
47
+ - [Node.js](https://nodejs.org) (`>= 20.0.0`)
48
48
  - [Yarn](https://yarnpkg.com/en/docs/install) or [NPM](https://docs.npmjs.com/getting-started/installing-node)
49
- - 谷歌浏览器,要配置端口号,启动命令设置 C:\Users\herzhang\AppData\Local\Google\Chrome\Application\chrome.exe --remote-debugging-port=9872
50
-
49
+ - 客户电脑必须有Chrome浏览器;
50
+ - 要点击谷歌浏览器的链接,要配置端口号,启动命令设置:目标修改: C:\Users\herzhang\AppData\Local\Google\Chrome\Application\chrome.exe --remote-debugging-port=9872
51
+ 比如: "C:\Program Files\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9872
52
+ - 之后服务期间,需要首先就该点击该链接开启Chrome浏览器。
53
+ - 特别注意:windows用户若非管理员账户的:桌面上的Chrome快捷方式右键点属性在“兼容性”设置中:必须勾选“管理员身份运行”;
54
+ 注意浏览器的管理员和普通身份运行的效果进入显示都不同的。目标URL网站需要登陆处理的,登陆点击后浏览器没有反馈,需直接关闭该TAB页面。
55
+ -报错Error: connect ECONNREFUSED 127.0.0.1:9872 【解决】先在windows任务管理器把chrome.exe的进程全部关闭后重来。
51
56
  ## Install
52
57
 
53
58
  - Fork or Use [this](https://github.com/filoscoder/tenstack-starter/generate) template repository.
@@ -57,8 +62,10 @@ chrome-remote-interface不会为您启动Chrome。本来想用chrome-remote-inte
57
62
  > Make sure you already have [`node.js`](https://github.com/filoscoder/tenstack-starter#prerequisites) and [`npm`](https://github.com/filoscoder/tenstack-starter#prerequisites) or [`yarn`](https://github.com/filoscoder/tenstack-starter#prerequisites) installed in your system.
58
63
 
59
64
  - Set your `git remote add origin` path
60
- - 用户端电脑地安装:
61
- 只需要本工程 /config, /dist, 文书打印转换器.bat, 已经node.js,nodemon;
65
+ - 用户电脑也需要做些安装的:
66
+ 只需要本工程目录的 /config, /dist, 文书打印转换器.bat,新/pdfs目录, 并且页已经node.js, nodemon;
67
+ 还必须要安装工程目录/package.json; /module-alias/的目录全部依赖包的。
68
+ -
62
69
  ```bash
63
70
  git remote add origin ${forked-and-cloned-path}
64
71
  ```
@@ -82,7 +89,9 @@ chrome-remote-interface不会为您启动Chrome。本来想用chrome-remote-inte
82
89
  <br>
83
90
  <br>
84
91
 
85
- ## Alias @
92
+ ## Alias 其它,对比 @
93
+ Puppeteer 是一个基于Chrome调试协议CDP 构建的额外高层 API,除其他功能外,它可以启动并使用捆绑版本的 Chromium,而非系统上安装的版本。
94
+ 我这个工具目前使用CDP协议构建的,而不是利用Puppeteer做的。
86
95
 
87
96
  To make paths clean and ease to access `@` is setup up for `/src` path
88
97
 
@@ -203,25 +212,31 @@ If you have any question or suggestion, don't hesitate to contact me:
203
212
 
204
213
  注意,如果发送post请求的body底下的内容实际为空的话,本转换器默认执行:把工作目录的所有pdf文件按照修改时间顺序进行合并,默认输出文件名=配置好的名字。
205
214
  假若某个URL不正常的,很可能无法触发页面加载完成,导致作业一致无法继续下去,大大超出预期执行时间。URL对应网页若不是一次性加载完成还会陆陆续续修改内容的就是这种情况造成死等长时间没结果。
215
+ # 调试 "start": "NODE_ENV=development run-s prettify lint & nodemon" 用Debug,异步执行,打断块点有点毛病
206
216
 
207
217
  # 页眉页脚配置html脚本转为json后
208
218
  例子,用https://uutool.cn/html2json/ 工具可转换,转换才能通过api/pdf发送命令的。
209
- "lay": {
210
- "head": [
211
- "<div style=\\\"position: relative; width:100%; text-align:center; border-bottom: 1pt solid #eeeeee; margin: 3.5mm 0px 10px; font-size: 10pt\\\"></span>",
212
- "<div style=\\\"position: absolute; width:100%; text-align: center; bottom: 5px;\\\"><span class=title></div>",
213
- "<div style=\\\"position: absolute; text-align: right; bottom: 5px;right: 20px;\\\">version: 1.0</div>",
214
- "</div>"
215
- ] ,
216
- "foot": [
217
- "<div style=\\\"position: relative; width: 100%; text-align: left; border-top: 1pt solid #eeeeee; margin: 10px 0px 1.5mm; font-size: 8pt;\\\">",
218
- "<div style=\\\"position: absolute; text-align: left; top: 5px;left: 60px;\\\">YYYY-MM-DD</div>",
219
- "<div style=\\\"position: absolute; width: 100%; text-align: center; top: 5px;\\\">&copy;2022 ABCD</div>",
220
- "<div style=\\\"position: absolute; text-align: right;top: 5px;right: 20px;\\\"> <span class=pageNumber></span> / <span class=totalPages></span></div>",
221
- "</div>"
222
- ]
223
- }
224
-
219
+ { name: (original ? "记录" : "报告") + rep?.isp?.no,
220
+ singleTab: true,
221
+ lay: {
222
+ head: [
223
+ '<div style=\\"position: relative; width:100%; text-align:center; border-bottom: 1pt solid #eeeeee; margin: 3.5mm 0px 10px; font-size: 10pt\\">',
224
+ `<div style=\\"position: absolute; width:100%; text-align:left; bottom: 5px; left: 50px;\\">报告No: ${rep?.isp?.no}</div></div>`
225
+ ],
226
+ foot: [
227
+ '<div style=\\"position: relative; width: 100%; text-align: left; border-top: 1pt solid #eeeeee; margin: 10px 0px 1.5mm; font-size: 8pt;\\">',
228
+ '<div style=\\"position: absolute; width: 100%; text-align: center; top: 5px;\\">共<span>~pageNumber~</span>页 / 第<span>~totalPages~</span>页</div></div>'
229
+ ],
230
+ },
231
+ files: [
232
+ {
233
+ url,
234
+ out: `tmp-${rep?.isp?.no}` + (original ? "-O" : ""),
235
+ headFrom: 3,
236
+ frNo: 3,
237
+ },
238
+ ] }
239
+ 其中 -NOT_DISPLAY- 指示若没有页码的就不显示相关局部部分。
225
240
  # 注意适用性
226
241
  本文书打印转换器只能适用单用户在客户本机用,每次执行一个打印指令,不要并行发出指令,本机工作目录也是临时文件,针对当前指令适用的,不能存放无关文件。
227
242
  本转换器console命令输出窗口可实时观察到当前打印转换指令的执行过程输出提示,同时目标浏览器也会新开启浏览窗口,这些由本转换器自动开启的浏览窗口实际上
@@ -237,3 +252,19 @@ If you have any question or suggestion, don't hesitate to contact me:
237
252
  {"name":"D4", "css": "10.4in 12.6in","w": 10.4,"h": 12.6}
238
253
  ]
239
254
  }
255
+ # 拒绝连接的
256
+ 对Chrome 右键 点选 以管理员身份运行;
257
+ # 动态布局问题
258
+ 只用css做动态的组件和用了js脚本做动态布局的组件在打印时刻不同,比如"customize-easy-ui-component"库的LineColumnFlex和LineColumn的差别即使这样,
259
+ 前者在打印时刻需要约束浏览器窗口的宽度为纸张输出的宽度差不多的大小,后者可打印页数就可能和内容页数不一致,后者只用css的就不会有这个麻烦。
260
+ 就算同一个组件有可能出现:同一次打印出现两种布局状态。比如这样的:<LineColumnFlex 1> {useMediaQuery({query: 'print'}) && <LineColumnFlex 2>}的,
261
+ 前面一个是js按照浏览器打印触发之前的div宽度来布局的有5列,然而后面哪一个是按照最终纸张输出宽度来做布局的才2列,完全不一样了!但是后面第二个组件可能输出不完整的,
262
+ 因为打印纸张的输出页数实际在useMediaQuery({query: 'print'})逻辑条件成立以前就敲定了可打印纸张数量!!所以有很大问题。js动态代码简单,css动态写代码麻烦。
263
+ 就接着这个例子讲:用{useMediaQuery({query: 'print'})&&逻辑嵌套的哪些内容,就算我约束了浏览器宽度为纸张宽度以后,实际上在打印敲定输出页数时间这些逻辑嵌套的
264
+ 需要打印内容实际上也并没有计算在立刻敲定输出页数之内的,因此也必然导致打印页数丢失或不够的问题的,这个情况必须用css,除非不打印比打印语境内容占用高度还少的话。
265
+ <div css={{"@media not print": {display: 'none'}}}>
266
+ <LineColumnFlex column={7}>
267
+ </div>
268
+ 如上代码会有严重毛病:打印输出实际上只有一次机会render;useMeasure(ref还是空的)div宽度=0导致实际只有输出一列的布局,打印没机会修正了,屏幕情况可以不限次数render,
269
+ 可是打印就不行了,上面这情况太特殊了。
270
+
@@ -1,41 +1,41 @@
1
- module.exports = {
2
- apps: [
3
- {
4
- name: '<app-name>',
5
- script: 'dist/src/index.js',
6
- // Options reference: https://pm2.keymetrics.io/docs/usage/application-declaration/
7
- exec_mode: 'cluster',
8
- instances: 2,
9
- kill_timeout: 5000,
10
- autorestart: true,
11
- max_restarts: 10,
12
- watch: true,
13
- watch_options: {
14
- followSymlinks: false,
15
- },
16
- max_memory_restart: '1G',
17
- env: {
18
- NODE_ENV: 'development',
19
- },
20
- env_production: {
21
- NODE_ENV: 'production',
22
- port: 8080,
23
- },
24
- merge_logs: true,
25
- log_date_format: 'YYYY-MM-DDTHH:mm:ss.sssZ',
26
- },
27
- ],
28
- // https://pm2.keymetrics.io/docs/usage/deployment/
29
- deploy: {
30
- production: {
31
- key: process.env.EC2_PEM_PATH,
32
- host: process.env.EC2_HOST,
33
- user: 'ubuntu',
34
- ref: 'origin/main',
35
- repo: 'git@github.com:<username>/<repo-name>.git',
36
- path: '/home/ubuntu',
37
- 'post-deploy':
38
- 'yarn && yarn clean && yarn build && pm2 startOrRestart ecosystem.config.js --env production && pm2 set pm2:autodump true && pm2 save',
39
- },
40
- },
41
- };
1
+ module.exports = {
2
+ apps: [
3
+ {
4
+ name: '<app-name>',
5
+ script: 'dist/src/index.js',
6
+ // Options reference: https://pm2.keymetrics.io/docs/usage/application-declaration/
7
+ exec_mode: 'cluster',
8
+ instances: 2,
9
+ kill_timeout: 5000,
10
+ autorestart: true,
11
+ max_restarts: 10,
12
+ watch: true,
13
+ watch_options: {
14
+ followSymlinks: false,
15
+ },
16
+ max_memory_restart: '1G',
17
+ env: {
18
+ NODE_ENV: 'development',
19
+ },
20
+ env_production: {
21
+ NODE_ENV: 'production',
22
+ port: 8080,
23
+ },
24
+ merge_logs: true,
25
+ log_date_format: 'YYYY-MM-DDTHH:mm:ss.sssZ',
26
+ },
27
+ ],
28
+ // https://pm2.keymetrics.io/docs/usage/deployment/
29
+ deploy: {
30
+ production: {
31
+ key: process.env.EC2_PEM_PATH,
32
+ host: process.env.EC2_HOST,
33
+ user: 'ubuntu',
34
+ ref: 'origin/main',
35
+ repo: 'git@github.com:<username>/<repo-name>.git',
36
+ path: '/home/ubuntu',
37
+ 'post-deploy':
38
+ 'yarn && yarn clean && yarn build && pm2 startOrRestart ecosystem.config.js --env production && pm2 set pm2:autodump true && pm2 save',
39
+ },
40
+ },
41
+ };
package/jest.config.js CHANGED
@@ -1,35 +1,35 @@
1
- const { defaults: tsjPreset } = require("ts-jest/presets");
2
-
3
- module.exports = {
4
- preset: "ts-jest",
5
- moduleFileExtensions: ["ts", "js", "json"],
6
- transform: {
7
- "^.+\\.(ts|tsx)$": [
8
- "ts-jest",
9
- {
10
- tsconfig: "tsconfig.json",
11
- diagnostics: false,
12
- },
13
- ],
14
- ...tsjPreset.transform,
15
- },
16
- testPathIgnorePatterns: ["dist"],
17
- testMatch: ["**/__tests__/**/*.test.(ts|js)"],
18
- testEnvironment: "node",
19
- moduleNameMapper: {
20
- "@/(.*)": "<rootDir>/src/$1",
21
- },
22
- watchPlugins: [
23
- "jest-watch-typeahead/filename",
24
- "jest-watch-typeahead/testname",
25
- ],
26
- coverageThreshold: {
27
- global: {
28
- branches: 1,
29
- functions: 1,
30
- lines: 1,
31
- statements: 1,
32
- },
33
- },
34
- coverageReporters: ["json", "lcov", "text", "clover"],
35
- };
1
+ const { defaults: tsjPreset } = require("ts-jest/presets");
2
+
3
+ module.exports = {
4
+ preset: "ts-jest",
5
+ moduleFileExtensions: ["ts", "js", "json"],
6
+ transform: {
7
+ "^.+\\.(ts|tsx)$": [
8
+ "ts-jest",
9
+ {
10
+ tsconfig: "tsconfig.json",
11
+ diagnostics: false,
12
+ },
13
+ ],
14
+ ...tsjPreset.transform,
15
+ },
16
+ testPathIgnorePatterns: ["dist"],
17
+ testMatch: ["**/__tests__/**/*.test.(ts|js)"],
18
+ testEnvironment: "node",
19
+ moduleNameMapper: {
20
+ "@/(.*)": "<rootDir>/src/$1",
21
+ },
22
+ watchPlugins: [
23
+ "jest-watch-typeahead/filename",
24
+ "jest-watch-typeahead/testname",
25
+ ],
26
+ coverageThreshold: {
27
+ global: {
28
+ branches: 1,
29
+ functions: 1,
30
+ lines: 1,
31
+ statements: 1,
32
+ },
33
+ },
34
+ coverageReporters: ["json", "lcov", "text", "clover"],
35
+ };
package/nodemon.json CHANGED
@@ -1,6 +1,6 @@
1
- {
2
- "watch": ["src/**/*.ts", ".env"],
3
- "ext": "ts,js,.env",
4
- "verbose": false,
5
- "exec": "ts-node -r tsconfig-paths/register --files src/index.ts"
6
- }
1
+ {
2
+ "watch": ["src/**/*.ts", ".env"],
3
+ "ext": "ts,js,.env",
4
+ "verbose": false,
5
+ "exec": "ts-node -r tsconfig-paths/register --files src/index.ts"
6
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "page2pdf_server",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "description": "文书打印转换器",
5
5
  "private": false,
6
6
  "authors": "herzhang",
@@ -46,6 +46,7 @@
46
46
  "chrome-remote-interface": "^0.32.2",
47
47
  "config": "^3.3.9",
48
48
  "connect-timeout": "^1.9.0",
49
+ "@types/cors": "^2.8.13",
49
50
  "cors": "^2.8.5",
50
51
  "cross-env": "^7.0.3",
51
52
  "dotenv": "^16.3.1",
@@ -61,22 +62,20 @@
61
62
  "pino-http": "^8.3.3",
62
63
  "pino-pretty": "^10.0.0",
63
64
  "rimraf": "^5.0.1",
65
+ "@types/node": "^20.3.1",
66
+ "@types/jsonwebtoken": "^9.0.2",
67
+ "@types/express": "^4.17.17",
68
+ "@types/express-pino-logger": "^4.0.3",
69
+ "@types/bcrypt": "^5.0.0",
64
70
  "save-dev": "^0.0.1-security"
65
71
  },
66
72
  "devDependencies": {
67
73
  "@babel/preset-env": "^7.22.5",
68
74
  "@babel/preset-typescript": "^7.22.5",
69
- "@types/bcrypt": "^5.0.0",
70
75
  "@types/connect-timeout": "^0.0.37",
71
- "@types/cors": "^2.8.13",
72
- "@types/express": "^4.17.17",
73
- "@types/express-pino-logger": "^4.0.3",
74
76
  "@types/jest": "^29.5.2",
75
- "@types/jsonwebtoken": "^9.0.2",
76
77
  "@types/lodash-es": "^4.17.7",
77
- "@types/module-alias": "^2.0.1",
78
78
  "@types/morgan": "^1.9.4",
79
- "@types/node": "^20.3.1",
80
79
  "@types/supertest": "^2.0.12",
81
80
  "@typescript-eslint/eslint-plugin": "^5.59.11",
82
81
  "@typescript-eslint/parser": "^5.59.11",
@@ -87,7 +86,6 @@
87
86
  "husky": "^8.0.3",
88
87
  "jest": "^29.5.0",
89
88
  "jest-watch-typeahead": "^2.2.2",
90
- "module-alias": "^2.2.3",
91
89
  "nodemon": "^3.0.1",
92
90
  "npm-run-all": "^4.1.5",
93
91
  "pm2": "^5.3.0",
@@ -99,6 +97,6 @@
99
97
  "typescript": "^5.1.6"
100
98
  },
101
99
  "engines": {
102
- "node": ">= 18.0.0"
100
+ "node": ">= 20.0.0"
103
101
  }
104
102
  }
@@ -54,3 +54,72 @@ Chrome浏览器已经 可支持的有这些:
54
54
  CDP({})前后两次CDP打开新的Page若前面没有close(),那么旧Page.printToPDF也会被后面新Page.navigate所覆盖的,浏览器不支持合并打印的!
55
55
  Page.printToPDF({})实际也只能类似Chrome打印预览菜单一致,只能打印当前页面,动态内容页面也是随着鼠标用户操作有可能不一样的打印结果。
56
56
 
57
+ 【精准控制打印位置思路】
58
+ 简单直接做法<span position: absolute; bottom: -1200vh;">尾巴</span>但须知道整个文档打印几张纸的,然后在作为参数优化css的计算再来决定做控制,可能会有重叠paint需强制断开页。
59
+ 中间区域<div style="position: relative; page-break-before: always;">¥..<span style="position: absolute;top: calc(200vh - 3rem);">尾巴</span>
60
+ 必须知道该区域有几张纸;可能会有重叠paint需强制断开页。
61
+ 浮动布局<div style="display: flex; flex-direction: column; align-content: flex-end; height: 1300vh;"><div height: 100%;>¥</div><div display: inline-flex;>尾巴</div>必须知道该区域有几张纸,不用担心会有重叠paint。
62
+ 后面一定有强制页断开的情景,把紧邻前面的元素定位到纸张的底部位置?【关键】是强制分页能确保独立性。
63
+ <div css={{"@media print": {pageBreakBefore: 'always'}}}><span style="position: absolute;top: -3rem;">尾巴</span><Text>紧跟的新页</Text></div>不需要算区域有几张,不用担心会有重叠paint。
64
+ 最后一页情况乱特例<div style="height: 0vh; position: relative; page-break-before: always;"><span style="display: block; position:absolute; bottom:0;">尾巴</span></div>最后多个尾随的空白页!多出一个空白页情况的不打印?
65
+ 2023/11/23:验证这下的代码还是无法正常打印页码 位置的。每一个重复div标签计数一次,只显示一次。
66
+ <div css={{"@media print": {pageBreakBefore: 'always'}}}>
67
+ <div
68
+ css={{
69
+ position: 'absolute',
70
+ top: '-2rem',
71
+ "@media print": {
72
+ ":after": {
73
+ content: 'counter(page)',
74
+ },
75
+ counterIncrement: 'page',
76
+ // position: 'absolute',
77
+ // bottom: '8rem',
78
+ right: '-1rem',
79
+ fontSize: '12px',
80
+ color: '#777',
81
+ padding: '5px',
82
+ backgroundColor: '#f7f7f7',
83
+ border: '1px solid #ddd',
84
+ borderRadius: '2px',
85
+ // pageBreakAfter: 'always',
86
+ }
87
+ }}
88
+ >
89
+ 尾巴
90
+ </div>
91
+ <Text>紧跟的新页</Text>
92
+ </div>
93
+ 【动态内容遇到的打印问题】末尾插入方式“只能支持页面启动就明确打印页数的才有效;用副作用中间纯动态变更的就不行了,打印页数早就被限制死了,后面无法再改了。
94
+ const [vtpage, setVtpage] = React.useState(Math.ceil((yBdv0?.top!-yHolw?.bottom!)/yHolw?.width!) || 0);
95
+ React.useEffect(() => {
96
+ if((yBdv0?.top!-yHolw?.bottom!)>0 && (yHolw?.width!)<yBdv0?.width! && (yHolw?.bottom!)>0)
97
+ {
98
+ console.log(`撞见该是却也显示了情形:`, yHolw,"虚拟断开",yBdv0, "系数有=", (yBdv0?.top!-yHolw?.bottom!)/yHolw?.width!);
99
+ //没处理【报错】Too many re-renders. React limits the number of renders to prevent an infinite loop.
100
+ setVtpage(Math.ceil((yBdv0?.top!-yHolw?.bottom!)/yHolw?.width!) || 0);
101
+ }
102
+ }, [yBdv0, yHolw, setVtpage]);
103
+ return <div ref={refBdv0} css={{"@media print": {pageBreakBefore:'always'}}}/>
104
+ { (new Array(vtpage)).fill(null).map((_, i:number) => {
105
+ console.log(`该是却也显示了情形加虚拟纸张数=`, vtpage,"断页i=",i);
106
+ return <div key={i} css={{"@media print": {pageBreakBefore:'always'}}}/>;
107
+ }) }
108
+ foot: [
109
+ '<div style=\\"position: relative; width: 100%; text-align: left; margin: 10px 0px 1.5mm; font-size: 8pt;\\">',
110
+ '<div style=\\"position: absolute; text-align: left; top: 5px;left: 60px;\\"></div>',
111
+ '<div style=\\"position: absolute; width: 100%; text-align: center; top: 5px;\\"></div>',
112
+ '<div style=\\"-NOT_DISPLAY- position: absolute; text-align: right;top: 5px;right: 20px;\\">共 <span>~totalPages~</span> 页 / 第 <span>~pageNumber~</span> 页</div>',
113
+ "</div>",
114
+ ],
115
+ 不同版本的Chrome可能对CSS样式、字体渲染或布局引擎存在差异,导致生成的PDF不一致。若网页使用的字体在目标电脑上未安装,Chrome可能自动替换为默认字体,网络延迟资源加载失败,所以用Puppeteer替代浏览器原生打印。
116
+
117
+ 失败,Error: 开启窗口尝试2次失败: 【因为】浏览器启动参数要配置特定端口号的。
118
+
119
+ [功能按钮]
120
+ 打印预览{非检验员也能用},
121
+ 本地转Pdf{非检验员也能用,未盖章,安装node等},
122
+ 后端转Pdf{未盖章,立刻下载,用户需排队},
123
+ 上传Pdf盖章,
124
+ 代理转Pdf{异步的,不能提前获得结果,独立服务器},
125
+ 下载盖章后Pdf{假如有存储Pdf};
package/src/app.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { EventEmitter } from "events";
1
2
  import cors from "cors";
2
3
  import express from "express";
3
4
  import helmet from "helmet";
@@ -5,8 +6,12 @@ import morgan from "morgan";
5
6
  import timeout from "connect-timeout";
6
7
  import CONFIG from "./configEnv";
7
8
  import { expressPinoLogger } from "./helpers";
8
- import * as errorHandler from "@/middlewares/errorHandler";
9
- import routes from "@/routes";
9
+ import * as errorHandler from "./middlewares/errorHandler";
10
+ import routes from "./routes";
11
+
12
+ /**打印不全,太多页数情形:
13
+ * */
14
+ EventEmitter.defaultMaxListeners = 100;
10
15
 
11
16
  export const createApp = (): express.Application => {
12
17
  const app = express();
@@ -1,7 +1,7 @@
1
1
  import { OK } from "http-status/lib";
2
2
  import { HomeServices } from "./services";
3
- import { getAppInfoQuery } from "@/types/request/home";
4
- import { apiResponse } from "@/helpers/apiResponse";
3
+ import { getAppInfoQuery } from "../../types/request/home";
4
+ import { apiResponse } from "../../helpers/apiResponse";
5
5
 
6
6
  /**
7
7
  * 为何为了保证单步模式调试运行"test:watchAll": "cross-env NODE_ENV=development jest --watchAll --runInBand --detectOpenHandles",
@@ -1,8 +1,12 @@
1
1
  import { OK } from "http-status/lib";
2
- import { ConfigRoot, FileTransform, HeadFooter } from "@/types/request/config";
3
- import { apiResponse } from "@/helpers/apiResponse";
4
- import { RenderPDF } from "@/utils/pdfgen";
5
- import CONFIG from "@/configEnv";
2
+ import {
3
+ ConfigRoot,
4
+ FileTransform,
5
+ HeadFooter,
6
+ } from "../../types/request/config";
7
+ import { apiResponse } from "../../helpers/apiResponse";
8
+ import { RenderPDF } from "../../utils/pdfgen";
9
+ import CONFIG from "../../configEnv";
6
10
 
7
11
  export class PdfController {
8
12
  /** node.js对比前端App 太严格了,一点小毛病页不行啊。
@@ -14,7 +18,7 @@ export class PdfController {
14
18
  */
15
19
  static postMakePdf = async (req: Req, res: Res, next: NextFn) => {
16
20
  try {
17
- // const appInfoKey1 = req.query.key as getAppInfoQuery;
21
+ // const appInfoKey1 = req.query.key as getAppInfoQuery; 所有打印都走ConfigRoot配置包=task:可能有多个URL合并输出的。
18
22
  const task = req.body as ConfigRoot<FileTransform>;
19
23
  task.lay as HeadFooter;
20
24
  //预处理汇总输出的pdf页眉页脚, 转换页眉页脚的html存储
@@ -1,6 +1,6 @@
1
- import AppInformation from "@/types/response/AppInformation";
2
- import { HomeDAO } from "@/db/home";
3
- import { getAppInfoQuery } from "@/types/request/home";
1
+ import AppInformation from "../../types/response/AppInformation";
2
+ import { HomeDAO } from "../../db/home";
3
+ import { getAppInfoQuery } from "../../types/request/home";
4
4
 
5
5
  export class HomeServices {
6
6
  homeDAO!: HomeDAO;
@@ -1,11 +1,15 @@
1
1
  import fs from "fs";
2
2
  import { OK } from "http-status/lib";
3
- import { ConfigRoot, FileTransform, HeadFooter } from "@/types/request/config";
4
- import { apiResponse } from "@/helpers/apiResponse";
3
+ import {
4
+ ConfigRoot,
5
+ FileTransform,
6
+ HeadFooter,
7
+ } from "../../types/request/config";
8
+ import { apiResponse } from "../../helpers/apiResponse";
5
9
 
6
- import { RenderPDF } from "@/utils/pdfgen";
7
- import CONFIG from "@/configEnv";
8
- import { SplitConfig, SplitPdfFile } from "@/types/request/split";
10
+ import { RenderPDF } from "../../utils/pdfgen";
11
+ import CONFIG from "../../configEnv";
12
+ import { SplitConfig, SplitPdfFile } from "../../types/request/split";
9
13
  // import CONFIG from "@/configEnv";
10
14
  // eslint-disable-next-line @typescript-eslint/no-var-requires
11
15
  const { PDFDocument } = require("pdf-lib");
@@ -1,5 +1,5 @@
1
1
  import { query } from "express-validator";
2
- import CONFIG from "@/configEnv";
2
+ import CONFIG from "../../configEnv";
3
3
 
4
4
  const appKeys = Object.keys(CONFIG.APP).map((key) => key.toLocaleLowerCase());
5
5
 
package/src/db/home.ts CHANGED
@@ -1,6 +1,6 @@
1
- import AppInformation from "@/types/response/AppInformation";
2
- import CONFIG from "@/configEnv";
3
- import { getAppInfoQuery } from "@/types/request/home";
1
+ import AppInformation from "../types/response/AppInformation";
2
+ import CONFIG from "../configEnv";
3
+ import { getAppInfoQuery } from "../types/request/home";
4
4
 
5
5
  export class HomeDAO {
6
6
  get = (key?: getAppInfoQuery): Promise<AppInformation | any> => {
@@ -1,5 +1,5 @@
1
1
  import HttpStatus, { OK } from "http-status/lib";
2
- import { ApiSuccessResponse } from "@/types/response";
2
+ import { ApiSuccessResponse } from "../types/response";
3
3
 
4
4
  export const apiResponse = <T>(data?: T): ApiSuccessResponse<T> => {
5
5
  return {
@@ -1,5 +1,5 @@
1
1
  import httpStatus, { REQUEST_TIMEOUT } from "http-status/lib";
2
- import CONFIG from "@/configEnv";
2
+ import CONFIG from "../../configEnv";
3
3
 
4
4
  class TimeOutError {
5
5
  readonly status: number;
@@ -1,6 +1,6 @@
1
1
  import httpStatus from "http-status/lib";
2
2
  import expressPino from "express-pino-logger";
3
- import { hidePassword } from "@/utils/auth";
3
+ import { hidePassword } from "../utils/auth";
4
4
 
5
5
  const { OK, BAD_REQUEST, SERVER_ERROR } = httpStatus;
6
6
 
package/src/index.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  // ! Don't convert require into import
2
2
  // eslint-disable-next-line @typescript-eslint/no-var-requires
3
- require("module-alias").addAlias("@", __dirname);
3
+ //去掉这个了 离开开发环境无法 独立运行服务端,报错module-alias找不到
4
+ //去掉这个了 require("module-alias").addAlias("@", __dirname);
5
+ //去掉这个了
4
6
 
5
7
  import { createApp } from "./app";
6
8
  import { startServer } from "./server";
@@ -11,3 +13,4 @@ if (process.env.NODE_ENV !== "test") {
11
13
  }
12
14
 
13
15
  //关键文档Chrome DevTools Protocol资料, https://chromedevtools.github.io/devtools-protocol/tot/Page
16
+ export * from "./types/request/config";
@@ -5,7 +5,7 @@ import HttpStatus, {
5
5
  INTERNAL_SERVER_ERROR,
6
6
  REQUEST_TIMEOUT,
7
7
  } from "http-status/lib";
8
- import { TimeOutError } from "@/helpers/error";
8
+ import { TimeOutError } from "../helpers/error";
9
9
 
10
10
  /**
11
11
  * @description Error response middleware for 404 not found. This middleware function should be at the very bottom of the stack.
@@ -4,8 +4,8 @@ import {
4
4
  appKeyValidator,
5
5
  PdfController,
6
6
  SplitController,
7
- } from "@/components/home";
8
- import { sanitizer } from "@/helpers";
7
+ } from "../components/home";
8
+ import { sanitizer } from "../helpers";
9
9
 
10
10
  const router = Router();
11
11
 
@@ -86,6 +86,9 @@ export interface FileTransform {
86
86
  footerTemplate?: string;
87
87
  headerTemplateL?: string;
88
88
  footerTemplateL?: string;
89
+ //这针对:可能由于动态布局导致的浏览器打印预览和实际URL纯粹js网页动态布局的真实内容页数不一致的毛病。
90
+ //开启的浏览器窗口宽度。默认最大化的。
91
+ brwidth?: number;
89
92
  //第一阶段Url转pdf的浏览器执行窗口的targetId,不可配置。
90
93
  CDPtab?: string;
91
94
  }
@@ -99,7 +102,8 @@ export interface FileTransform {
99
102
  For example, <span class=title></span> would generate span containing the title.
100
103
  */
101
104
 
102
- /**页眉页脚声明;前提条件是页眉页脚htm描述里面:不应该把-NOT_DISPLAY- ${pageNumber} ${totalPages}这三个保留字字符串当作普通文字输出的。
105
+ /**页眉页脚声明;前提条件是页眉页脚htm描述里面: 上一个版本保留字 ${pageNumber} ${totalPages}
106
+ * 不应该把 -NOT_DISPLAY- ~pageNumber~ ~totalPages~ 这三个 保留字字符串当作普通文字输出的。
103
107
  * 前端发送数据包时刻,页眉页脚的脚本需要在 HTML转JSON 工具网站 进行转换处理后的;网站地址 https://uutool.cn/html2json/ 转换htm模板后是数组[string]
104
108
  * 实际上也就进行拆分多行字符串拼接数组,还有把html语法中的 ""引号 进行转义 style="position: 变成 style=\\\"position: 处置的。
105
109
  * */
@@ -112,10 +116,11 @@ export interface HeadFooter {
112
116
  //双面打印:右手边位置的,或单面打印使用
113
117
  foot?: string | string[] | null; //实际前端发过来却是[string]的也可以啊。
114
118
  /**可注入3个参数"特殊标记字符串"如下: 服务端依据参数来对注入的html进行修改的。
115
- * ${pageNumber}
116
- * ${totalPages}
117
- * -NOT_DISPLAY-
118
- * 这三个分别代表 页码 总页数 以及页码<div style=""></div>中的可替代占位位置。-NOT_DISPLAY-在后端要直接替代成display:none;的CSS标记字符串!其它2个替换成数字号码。
119
+ * ~pageNumber~
120
+ * ~totalPages~
121
+ * -NOT_DISPLAY- 有页码才能显示,无页码就隐藏的标记。
122
+ * 这三个分别代表 页码 总页数 以及页码<div style=""></div>中的可替代占位位置。
123
+ * 其它2个替换成数字号码。
119
124
  * */
120
125
  //双面打印:左手边位置的
121
126
  headL?: string | string[] | null;
@@ -1,3 +1,3 @@
1
- import CONFIG from "@/configEnv";
1
+ import CONFIG from "../../configEnv";
2
2
 
3
3
  export type getAppInfoQuery = `${Lowercase<keyof typeof CONFIG.APP>}`;