当前位置: 首页 > 工具软件 > PDFLib-Lite > 使用案例 >

PDF 格式

曾嘉瑞
2023-12-01
pdf(Portable Document Format,便携式文档结构)是一种很有用的文件格式,其最大的特点是平台无关而且功能强大(支持文字/图象/音乐/视频).

一、PDF文件结构

  PDF文件结构可分为以下几块:   
1.header:
  pdf文件的第一行,格式如下:   
  %PDF-1.3   
  表示当前文件使用的PDF版本是1.3(目前最高版本为1.5)   
2.body:
  pdf文件中用到的所有对象,包括文本/图象/音乐/视频/字体/超连接/加密信息等等,格式如下:  
3 0 obj

<< /Type /Pages

   /Kids [ 5 0 R ]
   /Count 1
>>

endobj

第一个数字3称为对象号,来唯一标识一个对象的,第二个是产生号,是用来表明它在被创建后的第几次修改,所有新创建的PDF文件的产生号应该都是0,即第一次被创建以后没有被修改过。上面的例子就说明该对象的对象号是3,而且创建后没有被修改过。对象的内容应该是包含在<< 和>>之间的,最后以关键字endobj结束。

3.cross  reference  table:    
  所有pdf对象的引用表,其格式如下:   
  xref
  0   5
  0000000000   65535   f
  0000000009   00000   n
  0000000074   00000   n
  0000000120   00000   n
  0000000179   00000   n
其中, xref说明一个 交叉引用 表的开始标志,交叉引用表的第一行0 5 说明下面各行所描述的对象号是从0开始,并且有5个对象。 0000000000 65535f,一般每个PDF文件都是以这一行开始交叉应用表的,说明对象0的起始地址为0000000000,产生号(generation number)为65535,也是最大产生号,不可以再进行更改,而且最后对象的表示是f, 表明该对象为free, 这里,大家可以看到,其实这个对象可以看作是 文件头 0000000009 00000 n就是表示对象1,0000000009是其 偏移地址 ,00000为5位产生号(最大为65535),0表明该对象未被修改过,  n 表示该对象在使用,区别与自由对象(f),可以更改。
4.trailer: 
  整个pdf文件的入口点,形式如下:   
  trailer   
  <<   
  /Size   8   
  /Root   1   0   R   
  >>   
  startxref   
  553   
  %%EOF   

  /size   :这个pdf中总共使用了多少个对象   
  /root   :这个pdf文件的catalog对象的对象号,这是pdf中最顶层的对象,“ /Root 1 0 R” 说明根对象的对象号为1。  
  /startxref:   后面的数字表示cross reference table的开始位置, 553说明 交叉引用表的偏移地址,从而可以找到PDF文档中所有的对象的相对地址,进而 访问对象。   
  /%%EOF   :文件结束符.

二、pdf 内部8种类型

  实际一个pdf文件是很复杂的,但是上面几个部分是确定的,只能多不能少.下一讲我说一下pdf里面8种类型.
1.booleam
  用关键字true或false表示,可以是array对象的一个元素,或dictionary对象的一个条目.
2.numeric
  包括整形和实型,不支持非十进制数字,不支持指数形式的数字.
  例:
        1)整数 123 4567 +111 -2     
  范围:正2的31次方-1到负的2的31次方
  2)实数 12.3 0.8 +6.3 -4.01 -3. +.03
  范围:±3.403 × 10的38次方     ±1.175 × 10的-38次方
  注意:如果整数超过表示范围将转化成实数,如果实数超过范围就出错了
3.string
  由一系列0-255之间的字节组成,一个string总长度不能超过65535.string有以下两种方式:
  1)由()包含起来的一个字串,中间可以使用转义符"/".
  例:
  (abc)  表示abc
  (a//)  表示a/
  2)由<>包含起来的一个16进制串,两位表示一个字符,不足两位用0补齐
  例:
  <Aabb>  表示AA和BB两个字符
  <AAB>  表示AA和B0两个字符
4.name
  由一个前导/和后面一系列字符组成,最大长度为127.和string不同的是,name是不可分割的
和唯一的,不可分割就是说一个name对象就是一个原子,比如/name,不能说n就是这个name的
一个元素;唯一就是指两个相同的name一定代表同一个对象.从pdf1.2开始,除了ascii的0,别
的都可以用一个#加两个十六进制的数字表示.例:
  /name  表示name
  /name#20is 表示name is
  /name#200 表示name 0
5.array
  用[]包含的一组对象,可以是任何pdf对象(包括array).虽然pdf只支持一维array,但可以通过
 array的嵌套实现任意维数的array(但是一个array的元素不能超过8191),例:
 [549 3.14 false (Ralph) /SomeName]
6.Dictionary
  用"<<"和">>"包含的若干组条目,每组条目都由key和value组成,其中key必须是name对象,并且
 一个dictionary内的key是唯一的;value可以是任何pdf的合法对象(包括dictionary对象).例:
  << /IntegerItem 12
  /StringItem (a string)
  /Subdictionary << /Item1 0.4
  /Item2 true
  /LastItem (not!)
  /VeryLastItem (OK)
  >>
  >>
7.stream
  由关键字stream和endstream包含一系列字节.内容和string很相似,但有区别:stream可以分几次
 读取,分开使用不同的部分,string必须作为一个整体一次全部读取使用;string有长度限制,但
 stream却没有这个限制.一般较大的数据都用stream表示.例:(略)
8.NULL
  用null表示,代表空.如果一个key的值为null,则这个key可以被忽略;如果引用一个不存在的
 object则等价于引用一个空对象.例:(略)
给大家说点有用的东西:为什么有的pdf不允许打印?
pdf有自己的加密措施,其中就有限制打印.
找到trailer,如果这个pdf是加密的话会有一个/Encrypt的name,他的值一般形式是n 0 R,表示这个pdf
文件的加密信息在n 0这个obj里面记录.找到这个obj,其下有一个/P的name,他的值是一个数字(32位)

其中第三位代表是否有打印权限:)

三、PDF生成方法

由于pdf的特殊文件结构,注定pdf的生成是一件很麻烦的事情。目前最常用的就是adobe公司提供的sdk了,但是由于其依赖与adobe的环境,所以使用起来不是很方便。在这里我给大家介绍另外一种方法——使用pdflib生成pdf。 

  pdflib也提供了一组sdk,但和adobe的sdk相比,pdflib很小,但功能却一点也不弱。所以对于开发者来说,pdflib是一个明智的选择。你可以从下面的网址得到到它:   
  http://www.pdflib.com/products/pdflib/download/index.html。如果你没有得到序列号,那么生成的pdf将会加上水印,其他功能和商业版一样。   
  另外你还可以从下面网址得到pdflib-lite版:   
  http://www.pdflib.com/products/pdflib/download-source.html   
  pdflib-lite使用协议:   
  http://www.pdflib.com/purchase/license-lite.html   
  和pdflib相比,pdflib-lite最大的特点就是你可以拥有完全的源代码,但功能不如pdflib全面,比如支持的字体要比pdflib少很多。但是,如果你对pdf文件本身很了解的话,你完全可以在pdflib-lite的基础上再做发挥,这就要看你的C语言功底如何了。如果要用做商业产品,请仔细阅读授权协议!   
    
  下载pdflib压缩包(大约6m)后,在pdflib文件夹下有pdflib.dll,pdflib.lib,pdflib.h,pdflib.reg。对于我们来说,只要有前3个文件就可以了。 下面是一个生成pdf的完整代码:
#include   <stdio.h>   
#include   <stdlib.h>
#include   "pdflib.h"   

  int  main(void)   
  {   
          PDF   *p;   
          int   font;   
    
          /*   create   a   new   PDFlib   object   */   
          if   ((p   =   PDF_new())   ==   (PDF   *)   0)   
          {   
                  printf("Couldn't   create   PDFlib   object   (out   of   memory)!/n");   
                  return(2);   
          }   
    
          PDF_TRY(p)   {   
  if(PDF_begin_document(p,   "hello.pdf",   0,   "")   ==   -1)   {   
          printf("Error:   %s/n",   PDF_get_errmsg(p));   
          return(2);   
  }   
    
  /*   This   line   is   required   to   avoid   problems   on   Japanese   systems   */   
  PDF_set_parameter(p,   "hypertextencoding",   "host");   
    
  PDF_set_info(p,   "Creator",   "hello.c");   
  PDF_set_info(p,   "Author",   "Thomas   Merz");   
  PDF_set_info(p,   "Title",   "Hello,   world   (C)!");   
    
  PDF_begin_page_ext(p,   a4_width,   a4_height,   "");   
    
  /*   Change   "host"   encoding   to   "winansi"   or   whatever   you   need!   */   
  font   =   PDF_load_font(p,   "Helvetica-Bold",   0,   "host",   "");   
    
  PDF_setfont(p,   font,   24);   
  PDF_set_text_pos(p,   50,   700);   
  PDF_show(p,   "Hello,   world!");   
  PDF_continue_text(p,   "(says   C)");   
  PDF_end_page_ext(p,   "");   
    
  PDF_end_document(p,   "");   
          }   
    
          PDF_CATCH(p)   {   
                  printf("PDFlib   exception   occurred   in   hello   sample:/n");   
                  printf("[%d]   %s:   %s/n",   
          PDF_get_errnum(p),   PDF_get_apiname(p),   PDF_get_errmsg(p));   
                  PDF_delete(p);   
                  return(2);   
          } 
          PDF_delete(p);   
          return   0;   
  }
到现在,一个hello.pdf的pdf文件就生成了,你完全可以自己发挥,做出很复杂的pdf文件,这就看你的想象力了:)不过在发挥的时候要注意,pdf的坐标和我们通常理解的坐标是不一样的,屏幕坐标的原点在左上角,pdf文件的原点在左下脚。 
 类似资料: