技術要點:在PDF中找到一個固定錨點,在需要放置圖片的地方找到測試出錨點對應的XY位
// 使用了poppler方法,其他PDF庫在獲取坐標方面有各種問題,他的安裝是在Linux底層,比在PHP項目中用Composer安裝的庫看上去更穩定,功能更強
// pdftohtml方法需要先將PDF緩存成一個XML文件來進行坐標及其他具體數據的獲取,所以要新建一個$TempPath做為緩存位置可以方便后期統一清理
// $filePath = 'file/SL/06/SL1.01.06.V01_測試'; // PDF文件實際位置
$filePath = 'file/' . input('post.fcate') . '/' . input('post.fcode') . '/' . input('post.fname'); // 自動獲取文件+路徑
$fname = str_replace('.pdf', '', input('post.fname')); // 文件名去掉PDF后綴
$TempPath = 'cache/' . $fname; // PDF文件緩存位置
$fNo = explode('_', $fname);
$fNo = $fNo[0]; // 只取文件名前面的編號// 設置PDF文件路徑
$pdfFilePath = $filePath;
// 執行pdftohtml命令,轉換為HTML并輸出坐標
// -c:保持原始布局
// -hidden:隱藏元素
// -xml:生成 XML 格式的輸出,適合后續解析
// -noimages:不包含圖片
// -stdout:將輸出發送到標準輸出而不是文件
// -xml方法會生成PDF中的圖片,其他不生成圖片的方法會與-xml方法沖突導致無法生成xml文件,所以使用緩存方法統一對xml文件進行處理
$command = "pdftohtml -xml -c -hidden $pdfFilePath $TempPath";
// 在緩存位置自動生成XML文件
exec($command);// 解析生成的XML文件獲取坐標信息
$xml = simplexml_load_file($TempPath . '.xml');
// 輸出坐標信息
//var_dump($xml->page->text);
// 自動計算用值
$left = '4.2';
$top = '4.06';
$signTime = '11';// print_r($xml->page->text);
// 獲取所有簽名附近的唯一標識的坐標,通過計算得出簽名和簽名日期真正的坐標
foreach ($xml->page->text as $v) {// 文件編號if (strstr($v, 'Procedure No.')) {$No_L = (float)($v['left'] / $left) + 19.6;$No_T = (float)($v['top'] / $top) - 0.46;// echo 'Procedure No. $No_L = ' . $No_L . ' | $No_T = ' . $No_T . '<br />';} elseif (strstr($v, 'File No.')) {$No_L = (float)($v['left'] / $left) + 11;$No_T = (float)($v['top'] / $top) - 0.46;// echo 'File No. $No_L = ' . $No_L . ' | $No_T = ' . $No_T . '<br />';}// 編輯者簽名if ($v == 'EDITED BY') {$Edit_L = (float)($v['left'] / $left);$Edit_T = (float)($v['top'] / $top);// echo '$Edit_L = ' . $Edit_L . ' | $Edit_T = ' . $Edit_T . '<br />';}// 審批簽名if ($v == 'REVIEWED BY') {$Revi_L = (float)($v['left'] / $left);$Revi_T = (float)($v['top'] / $top);// echo '$Revi_L = ' . $Revi_L . ' | $Revi_T = ' . $Revi_T . '<br />';}// 批準簽名if ($v == 'APPROVED BY') {$Appr_L = (float)($v['left'] / $left);$Appr_T = (float)($v['top'] / $top);// echo '$Appr_L = ' . $Appr_L . ' | $Appr_T = ' . $Appr_T . '<br />';}
}// 創建 TCPDF 和 FPDI 的實例
// 在剛才獲取的坐標中插入對應的簽名及日期
$pdf = new TcpdfFpdi();// 加載現有的 PDF 文件
$file = $pdfFilePath;
$pageCount = $pdf->setSourceFile($file); // 獲取 PDF 文件的頁數// 遍歷每一頁
for ($pageNo = 1; $pageNo <= $pageCount; $pageNo++) {// 導入頁面$templateId = $pdf->importPage($pageNo);// 添加新頁面$pdf->addPage();// 使用導入的頁面$pdf->useTemplate($templateId);// 只修改第一頁if ($pageNo == 1) {// 設置插入圖片的位置和大小$pic1 = 'signature/【pic1】.png';$pic2 = 'signature/【pic2】.png';$pic3 = 'signature/【pic3】.png';$pdf->Image($pic1, $Edit_L, $Edit_T, 16, 9); // Image(簽名地址, X坐標, Y坐標, 寬, 高) 位置插入圖片,寬高為 50$pdf->SetFont('', '', 9); // SetFont(字體, 粗/斜 , 字號)$pdf->Text($Edit_L, $Edit_T + $signTime, date('Y-m-s', input('post.addtime_int'))); // Text(X坐標, Y坐標, 內容)$pdf->Image($pic2, $Revi_L, $Revi_T, 16, 9);$pdf->SetFont('', '', 9);$pdf->Text($Revi_L, $Revi_T + $signTime, date('Y-m-s', input('post.revtime_int')));$pdf->Image($pic3, $Appr_L, $Appr_T, 16, 9);$pdf->SetFont('', '', 9);$pdf->Text($Appr_L, $Appr_T + $signTime, date('Y-m-s'));}$pdf->SetFont('', '', 9);$pdf->Text($No_L, $No_T, $fNo);// $pdf->SetMargins(10, 10, 10); // 設置頁面邊距$pdf->SetDrawColor(255, 255, 255); // 設置邊框顏色為白色// $pdf->SetAutoPageBreak(true, 10); // 自動分頁的底部邊距
}
// 輸出修改后的 PDF 文件 Output實際使用的是fopen()方法,所以要用絕對路徑,不要用相對路徑會報錯
$pdf->Output($_SERVER['DOCUMENT_ROOT'] . '/' . $pdfFilePath, 'F'); // 'I' 為直接輸出到瀏覽器,'F' 為保存文件