跳到正文

PDF工具箱,信创重构版

小编

本工具借助AI重构自信创PDF工具箱(XCPDF)V7.1,所有功能均以可读、无混淆的JavaScript重新实现,js只存放未改动的原版第三方库(PDF.js、JSZip),所有定制与功能脚本放在js/modules并以 myPDF_ 前缀命名。

所有操作均在浏览器本地完成,文件不会上传到任何服务器。

原版作者:相声,感谢其开源精神。原作链接

为符合版规,去除原作中外网链接、二维码等相关表述。

原版(7.1)存在的问题:

1、字体不嵌入的 PDF 打开显示不正常,可对比网盘中“未嵌入字体.pdf”

有些 PDF 为了减小体积,不嵌入字体,只写一个字体名加一个预定义 CMap 编码名。原版没有内置所需的对照字典,结果是文本提取/复制出来全是乱码,页面渲染整页空白或方块乱码。重构版内置了全套 CMap,这类文件可正常显示与提取。(原版加盖图章、骑缝章等导入“未嵌入字体.pdf”实测只显示部分内容)。注:Word 等导出的 PDF 自带字体,不受此问题影响,两版表现一致。

2、无内容安全策略(CSP),注入脚本可直接执行,可对比网盘中“打开百度.pdf“

原版对恶意 PDF 的字体注入没有任何拦截,打开构造好的文件时,其中的代码会被执行(原版加盖图章、骑缝章等24个模块导入“打开百度.pdf”,联网时会自动打开百度网页,断网时会显示网页打不开,但跳转动作本身已证明代码被执行了,另有5个模块点击按钮触发跳转)。

重构版采用双重防护:关闭代码动态编译 + 页面级 CSP 门禁,同样的文件打开后无任何反应。

3、加盖图章、骑缝章不符合实际需求

原版:加盖图章、骑缝章导出后,图章其实是一张贴上去的“贴纸”,用 Acrobat 等专业软件可以移动、删除。

重构版:在加盖图章、骑缝章中新增“导出不可编辑PDF”,把盖章后的页面栅格化为图片,再重新拼成 PDF,每页都是一张图片,分辨率自适应,扫描页保持原页面像素,矢量页按 300dpi 输出。
PDF工具箱,信创重构版

html代码

  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
  6. <!-- ============================================================================
  7.      内容安全策略(CSP):安全加固,请勿删除
  8.      ----------------------------------------------------------------------------
  9.      这里刻意不包含 'unsafe-eval',因此浏览器会拒绝一切 eval / new Function 动态执行。
  10.      pdf.js 2.x 存在 CVE-2024-4367(恶意 PDF 借字体 FontMatrix 注入 JS,注入点就是
  11.      new Function 编译字形指令);本应用已在 js/modules/myPDF_pdf.worker.js 里关闭 isEvalSupported,
  12.      CSP 再兜底一层:即使将来有代码重新打开该开关,注入的代码也无法被编译执行。
  13.      其余规则用于放行离线运行所需的本地脚本、内联样式、data:/blob: 图片与 Worker。
  14.      ========================================================================== -->
  15. <meta http-equiv="Content-Security-Policy" content="default-src 'self' data: blob: file:; script-src 'self' 'unsafe-inline' data: blob:; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data: blob:; worker-src blob: data:; child-src blob:; connect-src 'self' data: blob: file:; object-src 'none'; base-uri 'none'">
  16. <title>离线PDF工具箱</title>
  17. <style>
  18. /* ============================================================================
  19.    基础样式(与原 XCPDF V7.1 界面保持一致的视觉规范)
  20.    ========================================================================== */
  21. :root { --bg: #f1f5f9; --card-bg: #ffffff; --primary: #2563eb; --primary-hover: #1d4ed8; --primary-light: #eff6ff; --text: #0f172a; --text-secondary: #475569; --border: #e2e8f0; --danger: #dc2626; --success: #16a34a; --shadow-sm: 0 1px 3px rgba(0,0,0,0.06); --shadow: 0 10px 25px -5px rgba(0,0,0,0.10), 0 8px 10px -6px rgba(0,0,0,0.05); --radius-sm: 8px; --radius: 12px; --radius-lg: 16px; --transition: 0.2s cubic-bezier(0.4, 0, 0.2, 1); --font: system-ui, -apple-system, 'Segoe UI', Roboto, 'PingFang SC', 'Microsoft YaHei', sans-serif; }
  22. * { margin:0; padding:0; box-sizing:border-box; }
  23. body { font-family:var(--font); background:var(--bg); color:var(--text); min-height:100vh; line-height:1.5; }
  24. [url=home.php?mod=space&uid=945662]@media[/url] (prefers-reduced-motion: reduce) { * { transition: none !important; animation: none !important; scroll-behavior: auto !important; } }
  25. .header { background:var(--card-bg); border-bottom:1px solid var(--border); padding:0 24px; position:sticky; top:0; z-index:100; box-shadow:var(--shadow-sm); }
  26. .header-inner { max-width: 1350px; margin: 0 auto; display: flex; align-items: center; justify-content: space-between; min-height: 60px; width: 100%; }
  27. .logo { font-weight:700; font-size:1.3rem; color:var(--text); white-space:nowrap; flex-shrink:0; cursor:pointer; display:flex; align-items:center; gap:8px; }
  28. .logo img { width:30px; height:30px; }
  29. .tab-nav { display:flex; gap:4px; background:#f1f5f9; border-radius:28px; padding:4px; flex-wrap:nowrap; flex-shrink:0; }
  30. .tab-btn { padding:8px 16px; border-radius:24px; border:none; cursor:pointer; font-size:0.85rem; font-weight:500; background:transparent; color:var(--text-secondary); transition:var(--transition); white-space:nowrap; font-family:var(--font); }
  31. .tab-btn.active { background:#fff; color:var(--primary); box-shadow:0 1px 3px rgba(0,0,0,0.06); font-weight:600; }
  32. .tab-btn:hover:not(.active) { color:var(--text); background:rgba(255,255,255,0.6); }
  33. .main-container { max-width:1400px; margin:0 auto; padding:24px; }
  34. .tab-panel { display:none; }
  35. .tab-panel.active { display:block; }
  36. .card { background:var(--card-bg); border-radius:var(--radius-lg); box-shadow:var(--shadow); padding:28px; margin-bottom:24px; }
  37. .card-title { font-size:1.15rem; font-weight:700; margin-bottom:20px; display:flex; align-items:center; gap:10px; color:var(--text); }
  38. .icon-dot { width:6px; height:22px; background:var(--primary); border-radius:3px; display:inline-block; flex-shrink:0; }
  39. .card-grid { display:grid; grid-template-columns:repeat(10, minmax(0,1fr)); gap:10px; margin-bottom:28px; }
  40. @media (max-width:1100px) { .card-grid { grid-template-columns:repeat(5, minmax(0,1fr)); } }
  41. @media (max-width:640px) { .card-grid { grid-template-columns:repeat(2, minmax(0,1fr)); } }
  42. .tool-card { background:var(--card-bg); border:1px solid var(--border); border-radius:var(--radius); padding:12px 6px; text-align:center; cursor:pointer; transition:var(--transition); user-select:none; }
  43. .tool-card:hover { transform:translateY(-3px); box-shadow:var(--shadow); border-color:var(--primary); }
  44. .tool-icon { font-size:1.45rem; margin-bottom:6px; display:flex; justify-content:center; }
  45. .tool-name { font-weight:600; font-size:0.82rem; margin-bottom:3px; }
  46. .tool-desc { font-size:0.7rem; color:var(--text-secondary); line-height:1.35; }
  47. .btn { padding:10px 20px; border-radius:var(--radius-sm); border:1px solid var(--border); background:var(--card-bg); color:var(--text); font-size:0.9rem; cursor:pointer; transition:var(--transition); font-family:var(--font); }
  48. .btn:hover { border-color:var(--primary); color:var(--primary); }
  49. .btn-primary { background:var(--primary); color:#fff; border-color:var(--primary); font-weight:600; }
  50. .btn-primary:hover { background:var(--primary-hover); color:#fff; }
  51. .btn-danger { background:var(--danger); color:#fff; border-color:var(--danger); font-weight:600; }
  52. .btn-danger:hover { background:#b91c1c; color:#fff; }
  53. .btn-sm { padding:6px 12px; font-size:0.8rem; }
  54. .btn:disabled { opacity:0.5; cursor:not-allowed; }
  55. .upload-area { border:2px dashed var(--border); border-radius:var(--radius); padding:36px 20px; text-align:center; cursor:pointer; transition:var(--transition); margin-bottom:14px; background:var(--primary-light); }
  56. .upload-area:hover, .upload-area.drag-over { border-color:var(--primary); background:var(--primary-light); transform:scale(1.01); }
  57. .primary-text { font-size:1rem; font-weight:600; color:var(--primary); margin-bottom:8px; display:flex; align-items:center; justify-content:center; gap:6px; flex-wrap:wrap; }
  58. .secondary-text { font-size:0.8rem; color:var(--text-secondary); }
  59. .file-list { max-height:300px; overflow-y:auto; margin-bottom:14px; }
  60. .file-item { display:flex; align-items:center; gap:10px; padding:10px 14px; background:var(--card-bg); border:1px solid var(--border); border-radius:var(--radius-sm); margin-bottom:8px; cursor:grab; transition:var(--transition); }
  61. .file-item:hover { border-color:var(--primary); }
  62. .file-item.dragging { opacity:0.4; border-style:dashed; }
  63. .file-item.drag-over-top { box-shadow:0 -2px 0 var(--primary); }
  64. .file-item.drag-over-bottom { box-shadow:0 2px 0 var(--primary); }
  65. .file-name { flex:1; font-size:0.85rem; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  66. .file-size { font-size:0.75rem; color:var(--text-secondary); flex-shrink:0; }
  67. .empty-message { text-align:center; padding:24px; color:var(--text-secondary); font-size:0.85rem; }
  68. .progress { width:100%; height:8px; background:var(--border); border-radius:4px; overflow:hidden; margin:10px 0; }
  69. .progress-bar { height:100%; background:var(--primary); border-radius:4px; transition:width 0.3s; width:0%; }
  70. /* 带"轨道 + 填充 + 百分比文字"的进度条(扇形骑缝章等模块使用) */
  71. .progress-wrap { display:flex; align-items:center; gap:10px; width:100%; }
  72. .progress-wrap .progress-bar { flex:1; height:8px; width:auto; background:var(--border); border-radius:4px; overflow:hidden; transition:none; }
  73. .progress-fill { display:block; height:100%; width:0%; background:var(--primary); border-radius:4px; transition:width 0.25s; }
  74. .progress-text { font-size:0.85rem; color:var(--text-secondary); min-width:40px; text-align:right; flex-shrink:0; font-variant-numeric:tabular-nums; }
  75. .status-message { margin-top:12px; padding:10px 14px; border-radius:var(--radius-sm); background:var(--primary-light); color:var(--text); font-size:0.85rem; line-height:1.6; word-break:break-all; }
  76. .usage-note { background:var(--primary-light); border-radius:var(--radius-sm); padding:14px 16px; font-size:0.85rem; color:var(--text-secondary); }
  77. .split-layout { display:grid; grid-template-columns:1fr 1.2fr; gap:24px; }
  78. @media (max-width:900px) { .split-layout { grid-template-columns:1fr; } }
  79. .form-row { display:flex; align-items:center; gap:10px; margin-bottom:14px; flex-wrap:wrap; }
  80. .form-label { font-size:0.85rem; font-weight:600; color:var(--text); min-width:90px; flex-shrink:0; }
  81. input[type=text], input[type=number], input[type=password], select, textarea { padding:9px 12px; border:1px solid var(--border); border-radius:var(--radius-sm); font-size:0.85rem; font-family:var(--font); background:var(--card-bg); color:var(--text); transition:var(--transition); outline:none; }
  82. input:focus, select:focus, textarea:focus { border-color:var(--primary); }
  83. input[type=color] { width:42px; height:36px; border:1px solid var(--border); border-radius:6px; padding:2px; cursor:pointer; background:var(--card-bg); }
  84. input[type=range] { accent-color:var(--primary); cursor:pointer; }
  85. input[type=checkbox] { width:16px; height:16px; accent-color:var(--primary); cursor:pointer; flex-shrink:0; }
  86. label { cursor:pointer; }
  87. .preview-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(150px,1fr)); gap:12px; }
  88. .preview-item { border:1px solid var(--border); border-radius:var(--radius-sm); padding:8px; text-align:center; background:var(--card-bg); transition:var(--transition); position:relative; }
  89. .preview-item:hover { box-shadow:var(--shadow-sm); border-color:var(--primary); }
  90. .preview-item canvas, .preview-item img { max-width:100%; border-radius:4px; cursor:pointer; }
  91. .page-thumb { width:100%; display:block; background:#e2e8f0; }
  92. .bookmark-tree-item { margin-bottom:2px; }
  93. .bookmark-tree-label { display:flex; align-items:center; gap:8px; padding:6px 8px; border-radius:var(--radius-sm); transition:background 0.15s; }
  94. .bookmark-tree-label:hover { background:var(--primary-light); }
  95. .bookmark-tree-title { flex:1; font-size:0.85rem; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  96. .bookmark-tree-page { font-size:0.75rem; color:var(--text-secondary); flex-shrink:0; }
  97. .bookmark-tree-children { margin-left:18px; }
  98. .loading-spinner { width:26px; height:26px; border:3px solid #e2e8f0; border-top-color:var(--primary); border-radius:50%; animation:spin 0.8s linear infinite; flex-shrink:0; }
  99. @keyframes spin { to { transform:rotate(360deg); } }
  100. .loading-overlay { position:fixed; inset:0; background:rgba(15,23,42,0.45); z-index:9998; display:flex; align-items:center; justify-content:center; }
  101. .shake-once { animation:shake 0.3s; }
  102. @keyframes shake { 0%,100%{transform:translateX(0);} 25%{transform:translateX(-6px);} 75%{transform:translateX(6px);} }
  103. .emoji-icon { width:1.35em; height:1.35em; vertical-align:-0.25em; }
  104. .back-home-btn { display:inline-flex; align-items:center; gap:6px; margin-bottom:16px; }
  105. .global-preview-overlay { position:fixed; inset:0; background:rgba(15,23,42,0.9); z-index:10000; align-items:center; justify-content:center; flex-direction:column; padding:40px; }
  106. @media (max-width:700px) { .header-inner { flex-direction:column; padding:10px 0; gap:8px; } .main-container { padding:14px; } .card { padding:18px; } }
  107. </style>
  108. </head>
  109. <body>
  110. <!-- 顶部导航 -->
  111. <header class="header">
  112.   <div class="header-inner">
  113.     <div class="logo">
  114.       <span>离线PDF工具箱</span>
  115.     </div>
  116.     <div style="font-size:0.8rem; color:var(--text-secondary); margin-top:6px; line-height:1.4;">
  117.       重构自<strong>信创PDF工具箱(XCPDF)V7.1</strong>,所有功能均以<strong>可读、无混淆</strong>的JavaScript重新实现,所有操作均在浏览器本地完成
  118.     </div>
  119.     <nav class="tab-nav">
  120.       <button class="tab-btn active" data-tab="tab-home">&#127968; 所有工具</button>
  121.       <button class="tab-btn" data-tab="tab-about">&#9749; 关于</button>
  122.     </nav>
  123.   </div>
  124. </header>
  125. <div class="main-container">
  126.   <!-- 首页:由 js/modules/myPDF_app.js 依据各模块注册信息自动生成 -->
  127.   <div class="tab-panel active" id="tab-home"></div>
  128.   <!-- 关于 -->
  129.   <div class="tab-panel" id="tab-about">
  130.     <div class="card" style="max-width:900px; margin:0 auto;">
  131.       <div class="card-title"><span class="icon-dot"></span>关于</div>
  132.       <div style="line-height:2; color:var(--text-secondary); font-size:1rem;">
  133.         <p>本工具重构自<strong>信创PDF工具箱(XCPDF)V7.1</strong>,所有功能均以<strong>可读、无混淆</strong>的JavaScript重新实现,<strong>js</strong>只存放未改动的原版第三方库(pdf.js、JSZip),所有定制与功能脚本放在<strong>js/modules</strong>并以 <code>myPDF_</code> 前缀命名。</p>
  134.         <p>所有操作均在浏览器本地完成,文件不会上传到任何服务器。</p>
  135.         <p><strong>原版作者:相声</strong>,感谢其开源精神。</p>
  136.       </div>
  137.     </div>
  138.   </div>
  139.   <!-- 40 个功能模块面板:由 js/modules/myPDF_app.js 创建 -->
  140.   <div id="module-panels"></div>
  141. </div>
  142. <!-- 全局加载遮罩 -->
  143. <div class="loading-overlay" id="loading-overlay" style="display:none;">
  144.   <div style="display:flex;align-items:center;background:#fff;padding:20px 30px;border-radius:24px;box-shadow:0 12px 32px rgba(0,0,0,0.12);">
  145.     <div class="loading-spinner"></div>
  146.     <span id="loading-text" style="margin-left:12px;font-weight:500;">处理中...</span>
  147.   </div>
  148. </div>
  149. <!-- 全局图片预览层 -->
  150. <div id="global-preview-overlay" class="global-preview-overlay" style="display:none;">
  151.   <div style="position:absolute; top:16px; left:50%; transform:translateX(-50%); display:flex; gap:8px; align-items:center; max-width:90%;">
  152.     <span id="global-preview-caption" style="color:#fff; font-size:0.9rem; margin-right:6px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;"></span>
  153.     <div id="global-preview-toolbar" style="display:flex; gap:6px;"></div>
  154.   </div>
  155.   <button id="global-preview-close" class="btn btn-sm" style="position:absolute; top:16px; right:16px;">&#10005; 关闭</button>
  156.   <button id="global-preview-prev" style="position:absolute; left:20px; top:50%; transform:translateY(-50%); font-size:2rem; line-height:1; color:#fff; background:rgba(255,255,255,0.15); border:none; border-radius:50%; width:52px; height:52px; cursor:pointer;">&#8249;</button>
  157.   <img id="global-preview-img" style="max-width:88%; max-height:82%; object-fit:contain; box-shadow:0 0 40px rgba(0,0,0,0.6); border-radius:6px; transition:opacity .15s;">
  158.   <button id="global-preview-next" style="position:absolute; right:20px; top:50%; transform:translateY(-50%); font-size:2rem; line-height:1; color:#fff; background:rgba(255,255,255,0.15); border:none; border-radius:50%; width:52px; height:52px; cursor:pointer;">&#8250;</button>
  159.   <div id="global-preview-index" style="position:absolute; bottom:20px; color:#fff; font-size:0.95rem; background:rgba(0,0,0,0.4); padding:4px 12px; border-radius:12px;"></div>
  160. </div>
  161. <!-- ===== 第三方库:纯官方、未改动的原版依赖(原样保留在 js/ 根目录) ===== -->
  162. <script src="js/pdf.min.js"></script>          <!-- pdf.js 2.16.105 官方构建:PDF 解析与渲染 -->
  163. <script src="js/jszip.min.js"></script>        <!-- JSZip 3.10.2 官方构建:批量导出打包 zip -->
  164. <!-- ===== 定制层:改写/定制的公用脚本(统一放在 js/modules/,文件名带 myPDF_ 前缀) ===== -->
  165. <script src="js/modules/myPDF_pdf.cmaps.js"></script>    <!-- CJK 字符映射表(必须早于 myPDF_pdf.worker.js 加载,以便注入自定义 CMap 读取工厂) -->
  166. <script src="js/modules/myPDF_pdf.worker.js"></script>   <!-- pdf.js Worker(base64 内嵌 + CMap 中文支持配置) -->
  167. <script src="js/modules/myPDF_pdf-lib.min.js"></script>  <!-- pdf-lib 1.17.x 定制加密分支:PDF 写入 / 修改 / 加密解密 -->
  168. <script src="js/modules/myPDF_icons.js"></script>        <!-- SVG 图标雪碧图 -->
  169. <script src="js/modules/myPDF_common.js"></script>       <!-- 公共工具库 + 模块注册表 -->
  170. <!-- ===== 功能模块(每个文件一项功能,注册到 MyPDF) ===== -->
  171. <script src="js/modules/myPDF_bates.js"></script>
  172. <script src="js/modules/myPDF_blank.js"></script>
  173. <script src="js/modules/myPDF_blueprint.js"></script>
  174. <script src="js/modules/myPDF_booklet-split.js"></script>
  175. <script src="js/modules/myPDF_booklet.js"></script>
  176. <script src="js/modules/myPDF_ca-sign.js"></script>
  177. <script src="js/modules/myPDF_compress.js"></script>
  178. <script src="js/modules/myPDF_convert.js"></script>
  179. <script src="js/modules/myPDF_crop-margin.js"></script>
  180. <script src="js/modules/myPDF_delete.js"></script>
  181. <script src="js/modules/myPDF_encrypt.js"></script>
  182. <script src="js/modules/myPDF_extract-images.js"></script>
  183. <script src="js/modules/myPDF_filter.js"></script>
  184. <script src="js/modules/myPDF_flatten.js"></script>
  185. <script src="js/modules/myPDF_img2pdf.js"></script>
  186. <script src="js/modules/myPDF_impose.js"></script>
  187. <script src="js/modules/myPDF_manage.js"></script>
  188. <script src="js/modules/myPDF_mark.js"></script>
  189. <script src="js/modules/myPDF_merge-alt.js"></script>
  190. <script src="js/modules/myPDF_merge.js"></script>
  191. <script src="js/modules/myPDF_meta.js"></script>
  192. <script src="js/modules/myPDF_pad-even.js"></script>
  193. <script src="js/modules/myPDF_pagenum.js"></script>
  194. <script src="js/modules/myPDF_perf-stamp.js"></script>
  195. <script src="js/modules/myPDF_remove-blank.js"></script>
  196. <script src="js/modules/myPDF_remove-bookmarks.js"></script>
  197. <script src="js/modules/myPDF_reverse.js"></script>
  198. <script src="js/modules/myPDF_rotate-single.js"></script>
  199. <script src="js/modules/myPDF_rotate.js"></script>
  200. <script src="js/modules/myPDF_scan-effect.js"></script>
  201. <script src="js/modules/myPDF_skew.js"></script>
  202. <script src="js/modules/myPDF_split-book.js"></script>
  203. <script src="js/modules/myPDF_split-bookmark-explode.js"></script>
  204. <script src="js/modules/myPDF_split-bookmark.js"></script>
  205. <script src="js/modules/myPDF_split.js"></script>
  206. <script src="js/modules/myPDF_stamp.js"></script>
  207. <script src="js/modules/myPDF_unify.js"></script>
  208. <script src="js/modules/myPDF_unlock.js"></script>
  209. <script src="js/modules/myPDF_watermark-image.js"></script>
  210. <script src="js/modules/myPDF_watermark-text.js"></script>
  211. <!-- ===== 应用框架(最后加载) ===== -->
  212. <script src="js/modules/myPDF_app.js"></script>
  213. </body>
  214. </html>
复制代码

下载地址

https://github.com/wallechfox/offline-pdf-tools

https://wwazg.lanzoub.com/b00q1tvw9a

密码:4mse