Delphi 版本的获取PDF页数算法--PDF Page Count Algorithm(Delphi source code)

简介:
type
  PPdfObj = ^TPdfObj;
  TPdfObj = record
    number,
    offset: integer;
  end;

function GetPdfPageCount(const filename: string): integer;
var
  ms: TMemoryStream;
  k, cnt, pagesNum, rootNum: integer;
  p, p2: pchar;
  PdfObj: PPdfObj;
  PdfObjList: TList;

  //Summary of steps taken to parse PDF file for page count :-
  //1. Locate 'startxref' at end of file
  //2. get 'xref' offset and go to xref table
  //3. fill my pdfObj List with object numbers and offsets
  //4. handle subsections within xref table.
  //5. read 'trailer' section at end of each xref
  //6. store 'Root' object number if found in 'trailer'
  //7. if 'Prev' xref found in 'trailer' - loop back to step 2
  //8. locate Root in my full pdfObj List
  //9. locate 'Pages' object from Root
  //10. get Count from Pages.

  function GetNumber(out num: integer): boolean;
  var
    tmpStr: string;
  begin
    tmpStr := '';
    while p^ < #33 do inc(p); //skip leading CR,LF & SPC
    while (p^ in ['0'..'9']) do
    begin
      tmpStr := tmpStr + p^;
      inc(p);
    end;
    result := tmpStr <> '';
    if not result then exit;
    num := strtoint(tmpStr);
  end;

  function IsString(const str: string): boolean;
  var
    len: integer;
  begin
    len := length(str);
    result := CompareMem( p, pchar(str), len);
    inc(p, len);
  end;

  function FindStrInDict(const str: string): boolean;
  begin
    //PDF 'dictionaries' (assoc. arrays) terminate with '>>'
    result := false;
    while not result do
    begin
      while (p^ <> '>') and (p^ <> str[1]) do inc(p);
      if (p^ = '>') then
      begin
        inc(p);
        if (p^ = '>') then exit else continue;
      end;
      result := IsString(str);
    end;
  end;

begin
  //on error return -1 as page count
  result := -1;
  try
    ms := TMemoryStream.Create;
    PdfObjList := TList.Create;
    screen.Cursor := crHourGlass;
    application.ProcessMessages;
    try
      ms.LoadFromFile(filename);

      //find 'startxref' ignoring '%%EOF'
      p := pchar(ms.Memory) + ms.Size -5;
      //21-Jun-05: bugfix
      //sometimes rubbish is appended to the pdf so
      //look deeper for 'startxref'
      p2 := pchar(ms.Memory);
      repeat
        while (p > p2) and (p^ <> 'f') do dec(p);
        if (p = p2) then exit;
        if StrLComp( (p-8), 'startxref', 9) = 0 then break;
        dec(p);
      until false;
      inc(p);

      rootNum := -1; //ie flag not yet found

      //xref offset ==> k
      if not GetNumber(k) then exit;
      p :=  pchar(ms.Memory) + k +4;

      while true do //top of loop  //////////////////////////////
      begin
        //get base object number ==> k
        if not GetNumber(k) then exit;
        //get object count ==> cnt
        if not GetNumber(cnt) then exit;
        while not (p^ in ['0'..'9']) do inc(p); //skip CR, LF
        p2 := p;
        //add all objects in section to list ...
        for cnt := 0 to cnt-1 do
        begin
          new(PdfObj);
          PdfObjList.Add(PdfObj);
          PdfObj.number := k + cnt;
          if not GetNumber(PdfObj.offset) then exit;
          inc(p2,20);
          p := p2;
        end;
        //check for and process further subsections ...
        if p^ in ['0'..'9'] then continue;

        // parse 'trailer dictionary' ...
        if not IsString('trailer') then exit;
        p2 := p;
        // get Root (aka /Catalog) ...
        if (rootNum = -1) and FindStrInDict('/Root') then
          if not GetNumber(rootNum) then exit;
        p := p2;
        if not FindStrInDict('/Prev') then break; //no more xrefs

        //next xref offset ==> k
        if not GetNumber(k) then exit;
        p :=  pchar(ms.Memory) + k +4;

      end; //bottom of loop /////////////////////////////////////

      //Make sure we've got Root the object number ...
      if rootNum < 0 then exit;
      //Find Root object in list and go to its offset ...
      k := 0;
      while k < PdfObjList.Count do
        if PPdfObj(PdfObjList[k]).number = rootNum then
          break else
          inc(k);
      if k = PdfObjList.Count then exit;
      p := pchar(ms.Memory) + PPdfObj(PdfObjList[k]).offset;
      //double check that this is the Root object ...
      if not GetNumber(k) or (k <> rootNum) then exit;
      if not FindStrInDict('/Pages') then exit;
      //get Pages object number ==> pagesNum
      if not GetNumber(pagesNum) then exit;
      k := 0;
      while k < PdfObjList.Count do
        if PPdfObj(PdfObjList[k]).number = pagesNum then
          break else
          inc(k);
      if k = PdfObjList.Count then exit;
      //Pages object found in list, now go to offset ...
      p := pchar(ms.Memory) + PPdfObj(PdfObjList[k]).offset;
      //make sure it's the Pages object ...
      if not GetNumber(k) or (k <> pagesNum) then exit;
      if not FindStrInDict('/Count') then exit;
      if not GetNumber(cnt) then exit;
      //21-Jun-05: bugfix
      //occasionally the 'count' value is an indirect object
      if GetNumber(k) and IsString(' R') then
      begin
        //this is an indirect object to the count value,
        //so find the obj ...
        k := 0;
        while k < PdfObjList.Count do
          if PPdfObj(PdfObjList[k]).number = cnt then break else inc(k);
        if k = PdfObjList.Count then exit;
        p := pchar(ms.Memory) + PPdfObj(PdfObjList[k]).offset;
        if not GetNumber(k) or //skip the object num
          not GetNumber(k) or //skip the generation num
          not IsString(' obj') or
          not GetNumber(cnt) then exit;
      end;
      result := cnt;
    finally
      screen.Cursor := crDefault;
      for k := 0 to PdfObjList.Count -1 do
        dispose(PPdfObj(PdfObjList[k]));
      PdfObjList.Free;
      ms.Free;
    end;
  except
    //nb: errors are flagged by returning -1
  end;
end;

from http://www.angusj.com/delphitips/pdfpagecount.php
 
 
本文转自 RubyPdf 的中文博客博客园博客,原文链接: http://www.cnblogs.com/hardrock/archive/2006/06/26/436065.html,如需转载请自行联系原作者
相关文章
|
6月前
|
机器学习/深度学习 算法 程序员
C++ Algorithm 库 算法秘境探索(Algorithm Wonderland Exploration)
C++ Algorithm 库 算法秘境探索(Algorithm Wonderland Exploration)
234 1
|
1月前
|
Python
《Cython 从入门到精通》PDF 版本新鲜出炉啦!!!
《Cython 从入门到精通》PDF 版本新鲜出炉啦!!!
34 1
|
2月前
|
算法 计算机视觉
Mat未初始化引起拼接算法结果,release版本和debug版本不一致
在OpenCV中由于Mat对象未初始化导致的拼接算法在release版本和debug版本中结果不一致的问题,并提供了通过显式初始化Mat对象为零来解决这一问题的修改方法。
|
3月前
|
算法 关系型数据库 MySQL
揭秘MySQL中的版本号排序:这个超级算法将颠覆你的排序世界!
【8月更文挑战第8天】在软件开发与数据管理中,正确排序版本号对软件更新及数据分析至关重要。因MySQL默认按字符串排序版本号,可能出现&#39;1.20.0&#39;在&#39;1.10.0&#39;之前的不合理情况。解决办法是将版本号各部分转换为整数后排序。例如,使用`SUBSTRING_INDEX`和`CAST`函数从`software`表的`version`字段提取并转换版本号,再按这些整数排序。这种方法可确保版本号按逻辑正确排序,适用于&#39;major.minor.patch&#39;格式的版本号。对于更复杂格式,需调整处理逻辑。掌握此技巧可有效应对版本号排序需求。
195 3
|
3月前
|
机器学习/深度学习 算法 网络性能优化
【博士每天一篇文献-算法】A brain-inspired algorithm that mitigates catastrophic forgetting of
本文提出了一种受大脑启发的神经调节辅助信用分配(NACA)算法,该算法通过模拟大脑中的神经调节机制,有效减轻了人工神经网络(ANNs)和脉冲神经网络(SNNs)在学习过程中的灾难性遗忘问题,并具有较低的计算成本。
56 1
|
5月前
|
算法 C语言 Ruby
分形逃逸时间算法中的 Normalized Iteration Count(NIC)技术 让颜色更柔和
Normalized Iteration Count (NIC) 技术是一种提升逃逸时间算法中分形图像质量的方法,它产生更平滑的颜色过渡。数学公式表示为:`mu = n + 1 - log(log(|Z(n)|)) / log(p)`,其中 `Z(n)` 是迭代次数,`|Z(n)|` 是复数模长,`p` 通常取2。示例代码提供了 Ruby, Maxima 和 C 语言的实现。
|
5月前
|
算法 Java 计算机视觉
图像处理之泛洪填充算法(Flood Fill Algorithm)
图像处理之泛洪填充算法(Flood Fill Algorithm)
270 6
|
6月前
|
存储 Java
java使用pdfbox 3.0版本删除pdf文件中指定字符所在行,生成新的pdf文件
【5月更文挑战第25天】java使用pdfbox 3.0版本删除pdf文件中指定字符所在行,生成新的pdf文件
772 1
|
6月前
|
算法 关系型数据库 MySQL
实时计算 Flink版产品使用合集之哪个版本可以做增量快照算法
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
|
5月前
|
算法 计算机视觉
图像处理之线性插值旋转算法(biline-interpolation rotate algorithm)
图像处理之线性插值旋转算法(biline-interpolation rotate algorithm)
59 0
下一篇
无影云桌面