使用PHP解析PDF文件之 (三) 解析交叉应用表

  • 发布时间 2015-02-12
  • 分类 php

本文主要讲解使用PHP,解析交叉引用表

%PDF-1.3 %Äåòåë§ó ÐÄÆ 4 0 obj << /Length 5 0 R /Filter /FlateDecode >> stream xM Â0…÷=Åwí$mjâB
........
endobj xref 0 75 0000000000 65535 f 0000230284 00000 n 0000000253 00000 n 0000214387 00000 n 0000000022 00000 n 0000000234 00000 n 0000000358 00000 n 0000004450 00000 n 0000003201 00000 n 0000214556 00000 n 0000000465 00000 n 0000003180 00000 n 0000003237 00000 n 0000004429 00000 n 0000004858 00000 n
........
trailer << /Size 75 /Root 50 0 R /Info 1 0 R /ID [ ] >> startxref 230428 %%EOF

2.解析交叉应用表

通过前面得到的xref位置230428,来对xref内的对象位置进行分析

// xref 0 75 0000000000 65535 f 0000230284 00000 n.....

$offset = 230428;

// 跳过开始的xref这4个字符
$offset += 4;
// $offset = 230432

// 跳过null,空格,制表符等
$offset += strspn($content, "\x00\x09\x0a\x0c\x0d\x20", $offset);
// $offset = 230433

// 初始化对象编号
$objectNumber = 0;

// 用于保存获取的object数据
$object = [];

// 正则获取每组数据,PREG_OFFSET_CAPTURE带上数据的所在位置
// no1 => 0 75\n
// no2 => 0000000000 65535 f\n
// no3 => 0000230284 00000 n\n
while (preg_match('/([0-9]+)[\x20]([0-9]+)[\x20]?([nf]?)(\r\n|[\x20]?[\r\n])/', $content, $matches, PREG_OFFSET_CAPTURE, $offset) > 0) {
    // 可能会匹配到后面,所以匹配的位置和当前所在的字符位置不一致则跳出了
    // no1 => $matches[0][1] = 230433
    // no2 => $matches[0][1] = 230438
    // no3 => $matches[0][1] = 230458
    if ($matches[0][1] != $offset) {
       break;
    }

    // no1 => $matches[0][0] = '0 75\n' => strlen => 5 => $offset = 230438 
    // no2 => $matches[0][0] = '0000000000 65535 f\n' => strlen => 20 => $offset = 230458
    // no3 => $matches[0][0] = '0000230284 00000 n\n' => strlen => 20 => $offset = 230478  
    $offset += strlen($matches[0][0]);

    // no1 => $matches[3][0] = ''
    // no1 => $matches[3][0] = 'f'
    // no3 => $matches[3][0] = 'n'
    switch ($matches[3][0]) {
        case 'n':
            $crossReference = [];
            // n表示用到的对象,0000230284表示该对象在$content的位置
            // 文件区中对象的编号格式是"objectNumber 00000 obj"
            $crossReference['id'] = $objectNumber . '_' . intval($matches[2][0]); // 1_0
            $crossReference['offset'] = intval($matches[1][0]); //  230284
            $object[] = $crossReference;
            $objectNumber++;
        break;
        case 'f':
            // f表示无用的对象,只需跳过object编号
            $objectNumber++;
        break;
        default:
            // 0 75 实际代表的是对象编号从0开始,共有75个对象,所以使用$matches[1][0] = 0来重置$objectNumber
            $objectNumber = intval($matches[1][0]);

    }
}

这样所有的对象位置和编号都会保存在$object数组里