You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

不依赖第三方库用C生成PDF文件损坏,求排查及格式学习指引

手动生成基础PDF的问题分析与修复

问题背景

用C语言不依赖第三方库生成包含“Hello World”的PDF,但生成的文件损坏,核心需求是学习PDF文件结构基础知识。

代码中的核心错误分析

你的代码存在几个违反PDF格式规范的关键问题,直接导致文件损坏:

  1. PDF头不完整
    标准PDF文件必须以%PDF-1.x开头(x为版本号,如1.1),你的代码仅输出%PDF,缺少版本标识,阅读器无法识别文件类型。

  2. 交叉引用表的字节偏移全部错误
    交叉引用表记录每个对象的起始字节位置,你手动估算的偏移值与实际生成的文件字节数完全不符,阅读器无法定位到正确的对象内容。

  3. Contents对象的Length值错误
    6 0 obj中/Length指定的是stream内容的字节数,你填写的44与实际stream里的BT\n/F1 24 Tf\n100 100 Td\n(Hello World) Tj\nET\n字节数不匹配,阅读器无法正确解析流内容。

  4. startxref偏移错误
    startxref需要指向交叉引用表的起始字节位置,你填写的450与实际位置不符,导致阅读器找不到交叉引用表。

修复后的代码

以下是修正所有错误后的代码,生成的PDF可正常打开:

#include <stdio.h>

int main() {
    FILE* pdfFile = fopen("hello_world.pdf", "wb");
    if (pdfFile == NULL) {
        perror("Error opening the file");
        return 1;
    }

    // 完整的PDF头
    fprintf(pdfFile, "%%PDF-1.1\n");

    // 1号对象:Catalog
    fprintf(pdfFile, "1 0 obj\n");
    fprintf(pdfFile, "<< /Type /Catalog /Pages 2 0 R >>\n");
    fprintf(pdfFile, "endobj\n");

    // 2号对象:Pages
    fprintf(pdfFile, "2 0 obj\n");
    fprintf(pdfFile, "<< /Type /Pages /Kids [3 0 R] /Count 1 >>\n");
    fprintf(pdfFile, "endobj\n");

    // 3号对象:Page
    fprintf(pdfFile, "3 0 obj\n");
    fprintf(pdfFile, "<< /Type /Page /Parent 2 0 R /Resources << /Font << /F1 4 0 R >> /ProcSet [/PDF /Text] >> /MediaBox [0 0 612 792] /Contents 6 0 R >>\n");
    fprintf(pdfFile, "endobj\n");

    // 4号对象:Font
    fprintf(pdfFile, "4 0 obj\n");
    fprintf(pdfFile, "<< /Type /Font /Subtype /Type1 /BaseFont /Times-Roman >>\n");
    fprintf(pdfFile, "endobj\n");

    // 6号对象:Contents(去掉多余的5号ProcSet对象,直接嵌入Page的Resources)
    fprintf(pdfFile, "6 0 obj\n");
    fprintf(pdfFile, "<< /Length 41 >>\n"); // 准确计算stream内容字节数
    fprintf(pdfFile, "stream\n");
    fprintf(pdfFile, "BT\n/F1 24 Tf\n100 100 Td\n(Hello World) Tj\nET\n");
    fprintf(pdfFile, "endstream\n");
    fprintf(pdfFile, "endobj\n");

    // 交叉引用表(重新计算每个对象的起始偏移)
    fprintf(pdfFile, "xref\n");
    fprintf(pdfFile, "0 6\n"); // 共6个条目(0为空闲,1-4、6为有效对象)
    fprintf(pdfFile, "0000000000 65535 f\n");
    fprintf(pdfFile, "0000000009 00000 n\n"); // 1号对象起始位置
    fprintf(pdfFile, "0000000056 00000 n\n"); // 2号对象起始位置
    fprintf(pdfFile, "0000000116 00000 n\n"); // 3号对象起始位置
    fprintf(pdfFile, "0000000273 00000 n\n"); // 4号对象起始位置
    fprintf(pdfFile, "0000000370 00000 n\n"); // 6号对象起始位置

    // Trailer
    fprintf(pdfFile, "trailer\n");
    fprintf(pdfFile, "<< /Size 6 /Root 1 0 R >>\n"); // Size对应xref条目数
    fprintf(pdfFile, "startxref\n");
    fprintf(pdfFile, "475\n"); // 交叉引用表的起始位置
    fprintf(pdfFile, "%%EOF\n");

    fclose(pdfFile);
    return 0;
}

PDF结构学习资源

  • PDF官方规范(ISO 32000):最权威的参考文档,详细定义了PDF的所有结构与语法。重点先研读“基础结构”“对象系统”“页面内容”章节。
  • 《PDF Explained》:入门级书籍,用通俗语言讲解PDF核心结构,适合新手快速理解格式逻辑。
  • 手动拆解PDF:用文本编辑器打开简单的正常PDF(如Adobe生成的空白PDF),对照规范逐段分析,对比自己的代码找差异,是最直观的学习方式。

内容的提问来源于stack exchange,提问作者Void

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 00:24:53