PDF回填译文后数字签名就失效了?Python检测签名与增量保存实测

【信息科学与工程学】计算机科学与自动化-——第十五篇云计算 12 公有云里的“多Region + 多AZ“ 02 运营算法01 算法逐步思考推理思考(含数学方程式、对象、资源、任务、进程/协程/线程及对应的代数、约束、目标函数/传递函数/依赖函数、参数及参数的数值范围及边界条件、逐步推理的数学方程式列表、上下文切换、并发/串行/随机/乱序/顺序)算法逐步思考推理思考(含数学方程式、对象、资源、任务、进程/协程/线程及对应的代数、约束、目标函数/传递函数/依赖函数、参数及参数的数值范围及边界条件、逐步推理的数学方程式列表、上下文切换、并发/串行/随机/乱序/顺序)云厂商提高闲置资源利用率。算法复杂度为O(N),N为实例数量。 阅读详情

前言

在批量 PDF 翻译的流水线里,有一类文件特别麻烦:带数字签名的 PDF。典型场景是电子合同、电子发票、加盖签章的报价单。这类文件如果被程序"打开—改内容—另存",签名往往会失效,而失效的合同在业务上是废纸。

问题的关键不在于"能不能改",而在于用什么方式保存。本文用一组可复现的实测,把 PDF 签名的检测方法、不同保存方式对签名的影响、以及实际开发中踩到的两个坑讲清楚。

顺便说清一个前提:翻译回填本身是"必须改文件"的操作——要么替换原文字,要么在旁侧插入译文图层。也就是说,只要你的流水线要处理带签名的 PDF,就一定会碰到"改了之后签名怎么办"这个问题。所以正确的思路不是"怎么改才能让签名看起来还有效",而是先判断这份文件能不能改,能改的用哪种保存方式才不破坏已签名的字节。这也是本文实验的设计出发点。

环境准备

  • Python 3.13
  • 依赖:pip install pymupdf pypdf reportlab numpy
  • 实测版本:PyMuPDF 1.28.2 / pypdf 6.18.0 / reportlab 5.0.1

一、先理解签名为什么会被破坏

PDF 签名的原理是:签名时把文件的一段字节区间算出一个摘要(digest),并把摘要和证书信息写在签名对象里,然后用 /ByteRange 记录"我覆盖了哪些字节范围"。验签时重新算这段范围的摘要,与签名里存的比对,一致就说明签完之后这些字节没被动过。

签名对象长这样:

<< /Type /Sig
   /Filter /Adobe.PPKLite
   /SubFilter /adbe.pkcs7.detached
   /Contents <....数字签名值....>
   /ByteRange [0 45391 45647 46234] >>

/ByteRange [0 a b c] 的含义是:覆盖 [0, a) 和 [b, c) 两段,中间 [a, b) 正是 /Contents 那个签名值本身(它当然不能覆盖自己)。

由此可以直接推出本文的核心结论:任何会导致"已签名区间字节发生变化"的保存方式,都会让签名失效。 完整重写就是典型——它会重新排版整个文件,字节区间全变了。

二、怎么检测 PDF 里有没有签名

先解决"有没有"的问题,这部分不依赖任何签名验证库:

import re
import pymupdf

def scan_signature(path: str) -> dict:
    """扫描 PDF 里的签名信息

    返回:SigFlags、签名域数量、/ByteRange 出现次数
    """
    doc = pymupdf.open(path)
    # 1) 签名域:AcroForm 里 FT=/Sig 的表单域
    fields = [w for w in doc[0].widgets()
              if w.field_type == pymupdf.PDF_WIDGET_TYPE_SIGNATURE]
    # 2) 文档级标志位,bit0=有签名,bit1=仅允许追加
    sigflags = doc.get_sigflags()
    doc.close()
    # 3) 直接数原始字节里的 /ByteRange(最直接的存在性证据)
    raw = open(path, "rb").read()
    return {
        "SigFlags": sigflags,
        "签名域数量": len(fields),
        "签名域名": [f.field_name for f in fields],
        "ByteRange出现次数": len(re.findall(rb"/ByteRange", raw)),
    }

实测输出(样本是我们后面手工构造的带签名文件):

{'file': '无签名基础件', 'get_sigflags': -1, 'AcroForm_SigFlags': None,
 '签名域数量': 0, '签名域名': [], '/ByteRange出现次数': 0, '文件大小': 1981}
{'file': '自签样本',     'get_sigflags': 3,  'AcroForm_SigFlags': ('int', '3'),
 '签名域数量': 1, '签名域名': ['Signature1'], '/ByteRange出现次数': 1, '文件大小': 46234}

get_sigflags() 返回 3 的含义是 1 | 2:bit0 表示文档里存在签名,bit1(AppendOnly)表示该签名要求后续只能做增量追加。这个值非常有用——看到 AppendOnly 就应该强制走增量保存,别再用默认的 save()。

三、构造一个可校验的签名样本

真实 PKI 签名需要证书,为了把实验做得可复现,我构造了一个只保留签名失效判据的最小样本:用一个定宽的 /ByteRange 占位,落盘后按真实字节偏移回填,再对覆盖范围算 SHA-256 写进 /Contents。这样"摘要一致 = 签名有效"的判据与真实签名完全等价。

import hashlib, re

HEXLEN = 256                                   # /Contents 占位长度(hex 字符数)
SEED_BR = "[0 1000000000 2000000000 3000000000]"   # 定宽种子:10 位、无前导零

def patch_and_sign(path: str) -> dict:
    """长度零变化地回填真实 /ByteRange,并把摘要写进 /Contents

    关键:占位与回填的长度必须完全一致。
    否则 xref 表里的字节偏移会失效,文件会被 MuPDF 判定为 repaired,
    后续连增量保存都会被拒绝。
    """
    data = open(path, "rb").read()
    m = re.search(rb"/ByteRange\s*\[\s*(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s*\]", data)
    idx = data.find(b"0" * HEXLEN)             # /Contents 占位起点
    c0, c1, size = idx, idx + HEXLEN, len(data)
    vals = [f"{c0:010d}", f"{c1:010d}", f"{size:010d}"]
    spans = [m.span(2), m.span(3), m.span(4)]
    # 从后往前替换,避免前面的替换把后面的偏移带偏
    for (s, e), v in reversed(list(zip(spans, vals))):
        assert e - s == 10
        data = data[:s] + v.encode() + data[e:]
    assert len(data) == size, "长度变化会导致 xref 失效"
    digest = hashlib.sha256(data[:c0] + data[c1:]).hexdigest()
    data = data.replace(b"0" * HEXLEN, digest.encode() + b"0" * (HEXLEN - 64), 1)
    open(path, "wb").write(data)
    return {"c0": c0, "c1": c1, "filesize": size, "digest": digest}

样本构造出来是 /ByteRange [0 45391 45647 46234],即覆盖前 45391 字节 + 后 587 字节。

验签函数同理,按 /ByteRange 重算摘要即可:

def verify(path: str, label: str = "") -> dict:
    """按 /ByteRange 重算摘要,与 /Contents 比对"""
    data = open(path, "rb").read()
    m = re.search(rb"/ByteRange\s*\[\s*(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s*\]", data)
    a, b, c, d = (int(x) for x in m.groups())
    stored = re.search(rb"/Contents\s*<([0-9A-Fa-f]+)>", data).group(1)[:64].decode().lower()
    if b > len(data) or d > len(data):
        return {"file": label, "status": "INVALID(偏移越界:文件已被重写/缩短)"}
    computed = hashlib.sha256(data[a:b] + data[c:d]).hexdigest()
    return {"file": label, "status": "VALID(摘要一致)" if computed == stored
            else "INVALID(摘要不一致)"}

四、五组对照实验

准备五种最常见的保存方式,全部作用在同一个已签名样本上:

import shutil, pymupdf, pypdf

# A. 完整重写(最常见的默认写法,且没改任何内容)
doc = pymupdf.open("signed.pdf"); doc.save("full_rewrite.pdf"); doc.close()

# B. 增量保存,不改内容
shutil.copy("signed.pdf", "incr_only.pdf")
doc = pymupdf.open("incr_only.pdf")
doc.save("incr_only.pdf", incremental=True, encryption=pymupdf.PDF_ENCRYPT_KEEP)
doc.close()

# C. 增量保存 + 在第 2 页插入译文
shutil.copy("signed.pdf", "incr_translated.pdf")
doc = pymupdf.open("incr_translated.pdf")
doc[1].insert_text((72, 700), "Appendix A (translated)", fontsize=11)
doc.save("incr_translated.pdf", incremental=True, encryption=pymupdf.PDF_ENCRYPT_KEEP)
doc.close()

# D. pypdf 的 append:框架不同,但本质也是完整重写
w = pypdf.PdfWriter(); w.append(pypdf.PdfReader("signed.pdf"))
with open("pypdf_append.pdf", "wb") as f:
    w.write(f)

# E. 增量保存 + 修改第 1 页旋转标记(正文完全没动)
shutil.copy("signed.pdf", "incr_rot.pdf")
doc = pymupdf.open("incr_rot.pdf"); doc[0].set_rotation(90)
doc.save("incr_rot.pdf", incremental=True, encryption=pymupdf.PDF_ENCRYPT_KEEP)
doc.close()

# F. 完整重写 + 插入译文
doc = pymupdf.open("signed.pdf")
doc[1].insert_text((72, 700), "Appendix A (translated)", fontsize=11)
doc.save("full_translated.pdf"); doc.close()

实测结果:

方案保存方式文件体积原文件前缀是否逐字节保持签名校验
基线—46234 B—VALID
A完整重写(未改内容)46219 B否INVALID(偏移越界)
B增量保存(未改内容)46234 B是VALID
C增量保存 + 插译文47149 B(+915)是VALID
Dpypdf append 重写46496 B否INVALID(摘要不一致)
E增量保存 + 改 /Rotate46632 B(+398)是VALID
F完整重写 + 插译文46693 B否INVALID

几个值得注意的细节:

第一,A 组最反直觉。 它没有修改任何内容,只是"打开再另存",签名照样失效了。原因是完整重写后文件从 46234 字节变成 46219 字节,缩短了 15 字节,/ByteRange 里的末端偏移 46234 已经超出文件末尾,签名在结构上就直接失效了。所以只要走默认 save(),"没改内容"也救不了签名。

第二,C 组和 E 组证明增量保存确实能改东西。 C 组往文档里插入了新文字、文件增大了 915 字节,签名仍然校验通过——因为增量保存只在文件末尾追加一个更新段,原有字节一个都没动,[0, 45391) 这段的摘要自然还是对的。E 组同理,即使修改了页面对象,也是以"追加一个新版对象"的方式实现。

第三,D 组和 A 组表现不同但结果一样。 pypdf 的 PdfWriter.append() 重写后体积是 46496 字节(比原文件还大),/ByteRange 的偏移也没越界,但摘要不一致——因为它重新生成了文件内容,字节区间对不上了。

五、两个真实的开发坑

坑一:增量保存必须显式保持加密参数。

不加 encryption 参数直接增量保存,会直接抛异常:

pymupdf.mupdf.FzErrorArgument: code=4: Can't do incremental writes when changing encryption

修复方式是显式传 encryption=pymupdf.PDF_ENCRYPT_KEEP。另外注意,增量保存只能写回"打开时读取的那个文件",想保留原件要先 shutil.copy 一份再操作。

坑二:文件一旦被判定为 “repaired”,增量保存会被直接拒绝。

pymupdf.mupdf.FzErrorArgument: code=4: Can't do incremental writes on a repaired file

这个坑我在做本文实验时真实踩到了:第一版脚本用字节替换回填 /ByteRange,替换后的文本比占位符长了 29 字节,导致 xref 表里的偏移全部失效,MuPDF 加载时判定文件需要修复,随后 save(incremental=True) 被拒绝。教训是:任何字节级改动都必须保证长度不变,否则不只是签名失效,整个文件的可写性都会丢。

六、工程结论

给批量翻译流水线的处理策略:

  1. 翻译前先扫描签名。用第二节的 scan_signature(),检出 /ByteRange 或签名域就标记为"受保护文件"。
  2. 受保护文件一律走增量保存,并显式带上 encryption=pymupdf.PDF_ENCRYPT_KEEP,保存前先复制原件。
  3. 不要用完整重写处理带签名的 PDF,无论你有没有改内容——A 组实验已经证明这条路必坏。
  4. 要清楚增量保存的边界。增量保存保证的是"已签名的字节没被动过",签名本身仍成立;但用验签工具核对时,通常仍会提示"文档在签名之后被修改过",因为文件里追加了增量更新段。若文件带的是认证签名(/SigFlags 的 AppendOnly 位),那它只允许追加注释和表单填写,不允许修改正文——这种情况下正确的做法不是想办法绕过去,而是:翻译结果单独出一份文件,或者把改动交给签署方重新签。
  5. 签名文件建议单独出队列,与普通文件分开处理,避免被批处理脚本用默认姿势写坏。

参考资料

  • PyMuPDF 官方文档:Document.save() 的 incremental 与 encryption 参数
  • PDF 规范 ISO 32000-1 第 12.8 节(数字签名)、7.5.6 节(增量更新)
  • pypdf 文档:PdfWriter.append / clone_document_from_reader

标签:PDF、Python、PDF翻译、技术避坑、文档处理

【信息科学与工程学】【数据科学】数据科学领域 第六篇 算法设计 01 算法设计 阅读详情

相关推荐

PDF工具

不收徒

m0_74042991的博客 174

PDF 不止装文字:用 Python 给 PDF 添加和提取内嵌附件

一份完整的项目报告,往往不只有 PDF 正文,还包括用于计算的 Excel 表格、原始图片或补充说明。如果这些文件分开发送,接收者就需要逐一下载、整理,还可能遗漏其中某一份材料。例如,在财务报告中附上原始工作簿,在产品手册中附上示例文件,或者在合同中附上补充材料。本文介绍如何使用完成两项操作:向 PDF 添加附件,以及从 PDF 中提取附件。

asdzx67的博客 95

Arduino蜂鸣器播放《Astronomia》:从乐谱到代码的嵌入式音乐实现

在嵌入式系统开发中,通过编程控制硬件执行特定任务是核心基础。其原理在于利用微控制器生成精确的时序和频率信号,驱动执行器(如蜂鸣器、电机)完成预定动作。这项技术的价值在于实现了软件逻辑与物理世界的直接交互,是物联网设备、智能硬件开发的基石。常见的应用场景包括电子玩具的音效生成、报警器设计以及交互式艺术装置。本文以热门歌曲《Astronomia》的播放为例,具体展示了如何运用Arduino的`tone()`函数和数组数据结构,将抽象的乐谱转化为可执行的频率与延时控制代码,完整呈现了从**核心硬件解析**到**软

weixin_29796279的博客 348

Java PDF 报表开发:一条逾期数据从 Map 到气泡再到评级报告 PDF 的完整链路

摘要(meta description):气泡图是散点图的加料版——x、y 定位置,size 定敞口,颜色定等级,一条数据同时编码四个维度。本文以 jquick-pdf 气泡图为例,跟着"客户C:账龄 9 个月、逾期率 6.8%、敞口 410、高风险"这条数据走完全程:从 Map 组装、轴配置、图例挂载,到 renderChart 出 SVG、绑定占位符、模板落版成 PDF,每一站都有代码和渲染结果对照。附完整 Java 源码与模板(jquick-pdfx 4.7.0+,Apache-2.0)。标签:、、、

PaoHaiJiao的博客 752

安卓设备安全相关技术

当前安卓设备使用的加密方案是基于Soc中一块可写入的rom实现,称为熔丝,其结果不可逆。 1、secureboot 2、AVB(Android Verify Boot) 3、dm-verity(device mapper) 4、keymaster 5、crypto 6、rpmb(replay private memory block) 7、TEE(Trust execute environment) 8、SELinux .........

weixin_43797328的博客 4838

使用Termux在Android设备上编译运行SpecCPU2006

SPEC2006搭配Termux,竟然还能这么玩?!

菜菜的菜园子 2960

PDF解析器代码详解:从文件结构到交叉引用表解析

本文详解了PDF解析器的代码实现,重点分析了PDF文件结构和交叉引用表解析过程。首先介绍了PDF文件的四部分结构:文件头、对象体、交叉引用表和文件尾。然后详细讲解了代码如何定位文件头、解析版本号,以及如何通过回溯搜索定位"startxref"关键字。最后深入分析了传统XRef表链的加载过程,包括处理增量更新、遍历Prev链以及防止循环引用等关键步骤。整个解析过程严格遵循PDF规范,确保了文件解析的准确性和兼容性。

C++ 是一门不原谅粗心的语言,而我恰好喜欢这种不原谅。 我是 x,正在成为更严谨的 C++ 开发工程师的路上。 520

SpringBoot+Vue前后端分离项目实战:个人博客系统

MyBlog主要是基于 SpringBoot + Vue 前后端分离开发的一款动态个人博客系统,后台和前台均通过JSON数据做交互,主要功能有文章管理、分类管理、标签管理、附件管理、评论管理、友链管理、个人资料/博客信息管理等。

Myles'Blog 1万+

BrotliCompressor压缩器封装,以及 PDF编码器介绍

Brotli压缩算法简介 Brotli是Google开发的开源无损压缩算法(2015年发布),专为Web内容优化设计。其核心优势包括: 高压缩率:比GZip小20%-26%,内置13,000+常见Web词汇的静态字典; 灵活压缩:0-11级可调(0-4级快速,5-11级高压缩比); 技术组合:融合LZ77、Huffman编码及创新上下文建模; Web应用:广泛用于HTTP内容压缩、前端构建工具及API数据传输。 兼容所有现代浏览器和主流服务器,虽压缩速度较慢,但解压极快,是现代网站性能优化的首选方案。 PD

C++ 是一门不原谅粗心的语言,而我恰好喜欢这种不原谅。 我是 x,正在成为更严谨的 C++ 开发工程师的路上。 1353

OpenClaw分布式高可用部署实战:Docker+K8s集群化,支撑万级并发生产业务

OpenClaw从单节点到分布式高可用集群,是企业AI从试点到规模化落地的必经之路。基于Docker+K8s的部署方案,不仅解决了高并发、高可用、弹性伸缩的生产痛点,更让运维成本大幅降低,业务稳定性提升到99.99%。对于电商、制造、政务等对稳定性有极致要求的行业,这套方案是OpenClaw生产上线的标准形态——功能拉满的同时,守住安全、稳定、可控的底线。

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 275

Linux ARM平台开发系列讲解(摄像头V4L2子系统) 2.12.5 V4L2 control的原理和实现

既然涉及到视频输入,就会有很多与 相关的效果,比如对比度、饱和度、色温、白平衡等等,这些都是通用的、必须的控制项,并且大多数仅需要设置一个整数值即可。 内核中已经为我们提供了这个控制类型的,我们只需要明确自己需要添加什么控制类型,然后将其添加进内核中即可。该部分摄像头代码一半厂家会提供驱动或者相对应的参数,否则没有图像处理知识很难开发出来。 返回总目录...

DSMGUOGUO的博客 1436

2026医学教材资料合集pdf电子版完整齐全

本文整理了一份涵盖临床、中医、护理、药学等多专业的医学生资料合集,包含本科期末复习资料、第九版临床教材(23本可复制书签)、研究生“红色生死恋”教材68本及配套PPT课件。文章强调资料使用需结合自身专业与学习阶段,以“蓝皮教材打基础、红皮教材备研考、规培教材练实战”为路径,倡导精准定位、主线唯一、避免囤积,真正实现从“收藏”到“阅读”的转化,提醒医学生:真正的成长在于读透一本好书,而非拥有全部资料。

2601_96733043的博客 1943

Android SurfaceFlinger——概述(一)

对于系统渲染流程的理解是指导 SurfaceFlinger 设计的核心思想,从 Android 4.1 一直到 Android 9.0 都没有太大的变化。只要抓住这五个核心思想,我们阅读 SurfaceFlinger 的难度就会下降不少。下面看一下 SurfaceFlinger 的体系和 Skia 以及 View的绘制流程的关系。framework 面向开发者所有的 View 是便于开发的控件,里面仅仅只是提供了当前 View 各种属性以及功能。

小旭的专栏 2584

PDF文档结构分析 一

PDF文档摘要 本文分析两个空白PDF文档的结构差异: 一页空白PDF: 包含9个对象(5在用/4空闲) 单一A4页面(595×842点) 基础结构仅含目录、页面树和空页面 文件大小较小(交叉引用表起始349) 五页空白PDF: 扩展至13个对象(11在用/2空闲) 包含5个A4页面(对象4、7-10) 新增页面均含/Rotate 0属性 文件体积更大(交叉引用表起始935) 主要差异体现在页面数量、对象结构和文件体积,后者通过复制页面对象实现多页扩展,保持相同的A4尺寸和空白内容特性。两个文档均由MuPD

C++ 是一门不原谅粗心的语言,而我恰好喜欢这种不原谅。 我是 x,正在成为更严谨的 C++ 开发工程师的路上。 3101

A2A 协议详解:Agent Card 的设计与实现

Agent Card 是 Agent2Agent (A2A) 协议中实现智能体发现与互操作性的核心组件。本文将深入剖析 Agent Card 的设计理念、关键字段及其在智能体生态系统中的作用。我们将通过详细的 JSON Schema 解析和 Python SDK 示例,帮助您理解如何精确地定义和发布智能体能力,为构建可发现、可协作的 AI 智能体奠定基础。当您使用a2a.Agent类初始化一个智能体时,SDK 会根据您提供的参数(nameversion等)自动构建内部的 Agent Card 表示。

加入“Super Entity”,与全能开发团队共探AI智能体与数字人项目,开启前沿技术之旅。 1476

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

gc.py 功能介绍:PDF 对象流还原工具(用于 pdfium 测试)

摘要: gc.py 是一个 PDF 对象流还原工具,用于将经过对象流压缩的 PDF 文件转换为标准交叉引用表(XRef Table)格式,为测试 pdfium 的对象流压缩功能提供纯净材料。该工具通过 podofogc 实现对象流解压,生成独立对象和交叉引用表,确保文件结构完整。用户可配置输入/输出路径、递归扫描等参数,并通过 qpdf 验证还原效果。测试流程包括压缩率、文件完整性和兼容性验证。podofogc 因其高效还原和垃圾清理功能成为首选工具。注意事项包括测试材料一致性、文件命名冲突处理和超时设置。

C++ 是一门不原谅粗心的语言,而我恰好喜欢这种不原谅。 我是 x,正在成为更严谨的 C++ 开发工程师的路上。 497

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

屏幕、电子书、打印档怎么选?4款免费PDF压缩工具对照

免费PDF压缩工具的档位比的是图片DPI:屏幕72、电子书150、打印300、Mac预览144;扫描件降到72dpi小字难认,300dpi扫描件选打印档几乎不变,附图映 ImgIng等4种办法实测。

2611_96826367的博客 185

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

PDF转JSON/Markdown:如何批量解析复杂文档并保留页面结构?

面对多栏、跨页表格、图文混排等复杂PDF文档,传统OCR难以完整保留结构与逻辑。TextIn xParse通过版面分析与结构化输出,支持将PDF等多格式文件批量解析为Markdown或JSON,精准识别标题、表格、图片、公式等元素,保留阅读顺序、行列关系及页面坐标信息,助力企业实现知识库建设、业务自动化与AI Agent文档处理,提升文档数据转化效率与可用性。

INTSIG的博客 358

三款实用工具推荐:PDF橡皮擦、桌签打印与Excel图表生成

推荐三款实用小工具:1. PDF橡皮擦(@lizhipei78开发),绿色版,专用于图片型PDF去水印,支持亮度调节与“胶带遮挡”功能,操作简便;2. 桌签打印工具,支持批量导入姓名、自定义背景与字体,可直接打印,适合会议桌签制作;3. Excel图表生成工具(@Duni1314开发),一键生成柱状图、饼图、甘特图等多种图表,操作简单,适合新手。三款工具均高效实用,值得收藏。下载链接:https://pan.quark.cn/s/c742b5d9413c

weixin_68551689的博客 196

Markdown 转 PDF API:表格与代码块、公开链接与私有文件

Markdown 转 PDF API 接收非空 Markdown 字符串,返回排版后的 PDF。可以用 JSON 或表单提交标题、表格、代码块等内容。公开模式返回下载 URL,私有模式返回文件引用。

DevOpenClub 专栏 170

【Python】国家中小学智慧教育平台电子教材批量下载与 PDF 合成实战

成果:两本语文电子课本(一年级上册 118 页 / 23MB、三年级上册 130 页 / 30.3MB),合计 248 页、约 54MB,单本下载加合成 40~60 秒,PDF 页数校验全部通过。经验教训SPA 站点的数据接口优先从 bundle 里挖,比抓页面稳得多;资源链接要按 private/public 分组看待,只取平台公开部分;页数、目录名这类“可推断”的信息,永远用实际响应验证一次;受限环境里“能不能跑浏览器”要在方案定稿前确认。后续计划:这版 PDF 是图片版,没有文字层;

木头大左的博客 139

CAD图纸输出为PDF格式

打印机选项输出PDF,窗口选择要打印的图纸区域。

liu3332456755的博客 14

HTML转PDF的页面尺寸策略与无损优化原理:从@page到PDF Form复用

HTML转PDF不应统一套用A4:有@page时沿用页面声明,连续网页按实际渲染宽高生成自定义页面,PDF中转的HTML依据逐页尺寸元数据恢复版式。生成后通过PDF Form复用相同填充路径做无损优化,保持可见字体与图片像素不变,并回读核对页数、尺寸与文字。

2601_96880668的博客 266

PDF 转 Word API:单文件上传、扫描识别与 DOCX 下载

PDF 转 Word API 接收一个未加密、非空白的 PDF 文件,以 multipart 表单上传,生成 DOCX。公开模式返回下载地址,私有模式返回文件引用。文本和扫描件的转换结果都适合用原 PDF 核对。

DevOpenClub 专栏 169

西门子ProSave v21无传输PDF等--选项与操作单元上的软件不兼容

出现错误如下,选项与操作单元上的软件不兼容 解决办法:用低版本的PROSAVE传输即可,其他都不用动。 我用V17的PROSAVE,出现了一个问题:就算触摸屏选择PN/IE方式,PROSAVE上如果用PN方式怎么也连不上,必须选Ethenet方式才可以。

hong7817的专栏 187
上一篇: 批量PDF翻译任务队列设计:断点续传与失败重试的实测方案
下一篇: PDF页面旋转与CropBox陷阱:译文回填位置总是错位的根因实测
SamChan90
博客等级 码龄1年 39粉丝 109原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值