如何使用 dompdf 生成目录?

发布于 2024-07-15 17:53:18 字数 141 浏览 7 评论 0原文

我正在使用 php 的 dompdf 库从 HTML 模板生成 PDF 报告。 在该 html 模板中,有一个目录部分。 生成 PDF 时,我需要更新目录的页码。 有谁知道我如何在 php 的 dompdf 库中实现这一点?

提前致谢。

I am using the dompdf library of php to generate PDF report from an HTML template. In that html template there is a section table of contents. When genrating PDF i need to update the page number of table of contents. Does anyone know how I can achieve this in dompdf library of php?

Thanks in advance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

给妤﹃绝世温柔 2024-07-22 17:53:19

我已经在 Drupal 中实现了这一点,并且猜测它也适用于其他 php 开源和框架。
我已将此代码保留在

$GLOBALS['entity_page'][] =  $pdf->get_page_number();        

存储页码的模板中的脚本标记内。 模板扩展名为 tpl.php
现在在模块中
在我添加了其他导出代码之后......

$canvas = $dompdf->get_canvas();
    $font = Font_Metrics::get_font("helvetica", "normal");
    $canvas->page_text(520, 805, "Page {PAGE_NUM}", $font, 9, array(0.4, 0.4, 0.4));

    foreach ($GLOBALS['entity_page'] as $key => $val) {
        $GLOBALS["entity_val"] = 0;
        $GLOBALS["entity_y"] = 110;
        $canvas->page_script('if($PAGE_NUM == 3 && $PAGE_NUM < 4){
                 $font = Font_Metrics::get_font("helvetica", "normal");
                 $x = 380;
                 $y = $GLOBALS["entity_y"];

                 $pdf->text($x, $y, "-------------------------".$GLOBALS["entity_page"][$GLOBALS["entity_val"]]."", $font, 12, array(0, 0, 0, 0.8));
                 $GLOBALS["entity_y"] = $GLOBALS["entity_y"] + 33;
                 $GLOBALS["entity_val"] = $GLOBALS["entity_val"] + 1;

                }');
    }

$pdf->text 这部分添加了 y 轴位置上恒定增量的页码。 其他全局变量entity_y和entity_val用于存储值。

I have achieved this in Drupal and guess it works on other php opensource and frameworks as well.
I have kept this code inside script tag

$GLOBALS['entity_page'][] =  $pdf->get_page_number();        

in template that stores the page number. Template is with extension tpl.php
Now in the module
after other codes for export I have added......

$canvas = $dompdf->get_canvas();
    $font = Font_Metrics::get_font("helvetica", "normal");
    $canvas->page_text(520, 805, "Page {PAGE_NUM}", $font, 9, array(0.4, 0.4, 0.4));

    foreach ($GLOBALS['entity_page'] as $key => $val) {
        $GLOBALS["entity_val"] = 0;
        $GLOBALS["entity_y"] = 110;
        $canvas->page_script('if($PAGE_NUM == 3 && $PAGE_NUM < 4){
                 $font = Font_Metrics::get_font("helvetica", "normal");
                 $x = 380;
                 $y = $GLOBALS["entity_y"];

                 $pdf->text($x, $y, "-------------------------".$GLOBALS["entity_page"][$GLOBALS["entity_val"]]."", $font, 12, array(0, 0, 0, 0.8));
                 $GLOBALS["entity_y"] = $GLOBALS["entity_y"] + 33;
                 $GLOBALS["entity_val"] = $GLOBALS["entity_val"] + 1;

                }');
    }

$pdf->text this part adds the page numbers with constant increment in y axis position. Other global variables entity_y and entity_val are used to store the values.

话少心凉 2024-07-22 17:53:19

从 HTML 生成目录(使用 h1、h2、h3),我执行了以下操作:

  • 首先为每个标头指定一个唯一的 ID(因为我们使用的是 PrinceXML),这是一个很好的做法。
  • 然后创建它的 OL/LI 结构,尽管该代码段可能包含错误。
    $matches = null;
    $smatches = null;
    $had_headers = array();
    preg_match_all('/<h[0-9].*?>.*?<\/h[0-9]>/i', $content, $matches);
    if (!empty($matches[0]) && count($matches[0]) > 0)
    foreach ($matches[0] as $headertag) {
        preg_match('/>(.*?)<\/(h[0-9])>/i', $headertag, $smatches);
    
        if (!empty($smatches[1]) && count($smatches[1]) > 0) {
            $headerid = strip_tags($headertag);
            $headerid = trim(strtolower(preg_replace('/[^a-z0-9]/i', '', $headerid)));
            $smatches[2] = strtolower($smatches[2]);
            $header_depth = intval(trim(str_ireplace('h', '', $smatches[2])));
        
            while (in_array($headerid, $had_headers)) {
                $headerid .= '1';
            }
            $had_headers[] = $headerid;
        
            $content = str_replace($headertag, '<'. $smatches[2] . ' id="' . htmlentities($headerid) . '">' . $smatches[1] . '</' . $smatches[2] . '>', $content);
        }
    }

    $matches = null;
    $smatches = null;
    $toc_html = '<ol id="toc">' . "\n";
    $old_depth = 0;
    $hadfirst = false;
    preg_match_all('/<h[0-9].*?>.*?<\/h[0-9]>/i', $content, $matches);
    if (!empty($matches[0]) && count($matches[0]) > 0)
    for ($i=0; $i < count($matches[0]); $i++) {
        $headertag = $matches[0][$i];
    
        preg_match('/<h[0-9][^>]*?id="(.*?)".*?>(.*?)<\/(h[0-9])>/i', $headertag, $smatches);
    
        if (!empty($smatches[1]) && count($smatches[1]) > 0) {
            $headerid = trim($smatches[1]);
            $header_depth = intval(trim(str_ireplace('h', '', $smatches[3]))) - 1;
        
            // don't take heigher than h3 in TOC
            if ($header_depth > 2)
                continue;
        
            if ($header_depth < $old_depth) {
                $diff = $old_depth - $header_depth; //if going multiple levels up
                $toc_html .= '</li>'.str_repeat('</ol></li>', $diff);
            } elseif ($header_depth > $old_depth) {
                $toc_html .= '<ol>';
            } else {
                $toc_html .= ($hadfirst) ? '</li>' : null;
            }
        
            $toc_html .= '<li><a href="#' . $headerid . '">' . htmlentities(trim(strip_tags($smatches[2]))) . '</a>';
        
            $old_depth = $header_depth;
            $hadfirst = true;
        }
    }
    $toc_html .=  str_repeat('</li></ol>', ($old_depth + 1));

Generating a Table of Contents from HTML (with h1,h2,h3), I did the following:

  • First give every header an unique ID (because we are using PrinceXML) and is a good pratice.
  • Then create an OL/LI structure of it, although that piece of code can contain bugs.
    $matches = null;
    $smatches = null;
    $had_headers = array();
    preg_match_all('/<h[0-9].*?>.*?<\/h[0-9]>/i', $content, $matches);
    if (!empty($matches[0]) && count($matches[0]) > 0)
    foreach ($matches[0] as $headertag) {
        preg_match('/>(.*?)<\/(h[0-9])>/i', $headertag, $smatches);
    
        if (!empty($smatches[1]) && count($smatches[1]) > 0) {
            $headerid = strip_tags($headertag);
            $headerid = trim(strtolower(preg_replace('/[^a-z0-9]/i', '', $headerid)));
            $smatches[2] = strtolower($smatches[2]);
            $header_depth = intval(trim(str_ireplace('h', '', $smatches[2])));
        
            while (in_array($headerid, $had_headers)) {
                $headerid .= '1';
            }
            $had_headers[] = $headerid;
        
            $content = str_replace($headertag, '<'. $smatches[2] . ' id="' . htmlentities($headerid) . '">' . $smatches[1] . '</' . $smatches[2] . '>', $content);
        }
    }

    $matches = null;
    $smatches = null;
    $toc_html = '<ol id="toc">' . "\n";
    $old_depth = 0;
    $hadfirst = false;
    preg_match_all('/<h[0-9].*?>.*?<\/h[0-9]>/i', $content, $matches);
    if (!empty($matches[0]) && count($matches[0]) > 0)
    for ($i=0; $i < count($matches[0]); $i++) {
        $headertag = $matches[0][$i];
    
        preg_match('/<h[0-9][^>]*?id="(.*?)".*?>(.*?)<\/(h[0-9])>/i', $headertag, $smatches);
    
        if (!empty($smatches[1]) && count($smatches[1]) > 0) {
            $headerid = trim($smatches[1]);
            $header_depth = intval(trim(str_ireplace('h', '', $smatches[3]))) - 1;
        
            // don't take heigher than h3 in TOC
            if ($header_depth > 2)
                continue;
        
            if ($header_depth < $old_depth) {
                $diff = $old_depth - $header_depth; //if going multiple levels up
                $toc_html .= '</li>'.str_repeat('</ol></li>', $diff);
            } elseif ($header_depth > $old_depth) {
                $toc_html .= '<ol>';
            } else {
                $toc_html .= ($hadfirst) ? '</li>' : null;
            }
        
            $toc_html .= '<li><a href="#' . $headerid . '">' . htmlentities(trim(strip_tags($smatches[2]))) . '</a>';
        
            $old_depth = $header_depth;
            $hadfirst = true;
        }
    }
    $toc_html .=  str_repeat('</li></ol>', ($old_depth + 1));
挽袖吟 2024-07-22 17:53:19

您可能已经解决了这个问题? 我没有使用过 dompdf,但我在 Zend_Pdf 中做了类似的事情:我为目录创建了一个空白页面,然后继续创建所有其他后续页面,保留一个 page_number => 的数组。 标题。 最后,我返回并使用之前保存的参考更新了内容页面......

You may have solved this already? I've not used dompdf, but i did a similar thing in Zend_Pdf: I made a blank page for the table of contents and then went on creating all the other later pages, keeping an array of page_number => title. At the end I went back and updated the contents page using the reference saved earlier...

金橙橙 2024-07-22 17:53:19

作为 vicky shrestha 答案的扩展,我这里的目录可以扩展超过一页。

36 只是适合设计的任意数量的项目。

foreach ($GLOBALS['entity_page'] as $key => $val) {
    $GLOBALS["entity_y"] = 88;
    $GLOBALS["entity_val"]  = 0;
    $GLOBALS["entity_per_page"] = 36;
    if($val) {
        $canvas->page_script('
        if(isset($GLOBALS["entity_page"][$GLOBALS["entity_val"]])) {
            if($PAGE_NUM == $GLOBALS["entity_page_number"]){
                $x = 505;
                $y = $GLOBALS["entity_y"];
                $font = $fontMetrics->get_font("Open Sans", "Helvetica Neue", "Helvetica, Arial, sans-serif");

                $pdf->text($x, $y, $GLOBALS["entity_page"][$GLOBALS["entity_val"]]."", $font, 7, array(0, 0, 0, 1));
                $GLOBALS["entity_y"] = $GLOBALS["entity_y"] + 19;
                $GLOBALS["entity_val"] = $GLOBALS["entity_val"] + 1;
                if (($GLOBALS["entity_val"] + 1) % $GLOBALS["entity_per_page"] == 0 ) {
                    $GLOBALS["entity_page_number"] = $GLOBALS["entity_page_number"] + 1;
                    $GLOBALS["entity_y"] = 31;
                }
            }
        }');
    }
}

isset 很重要,因为由于某种原因 foreach 会额外循环一次,并且最后会抛出一个越界异常。

As an extension to vicky shrestha's answer here is what I have for a table of contents that expands more than a single page.

36 is just an arbitrary number of items that fit in the design.

foreach ($GLOBALS['entity_page'] as $key => $val) {
    $GLOBALS["entity_y"] = 88;
    $GLOBALS["entity_val"]  = 0;
    $GLOBALS["entity_per_page"] = 36;
    if($val) {
        $canvas->page_script('
        if(isset($GLOBALS["entity_page"][$GLOBALS["entity_val"]])) {
            if($PAGE_NUM == $GLOBALS["entity_page_number"]){
                $x = 505;
                $y = $GLOBALS["entity_y"];
                $font = $fontMetrics->get_font("Open Sans", "Helvetica Neue", "Helvetica, Arial, sans-serif");

                $pdf->text($x, $y, $GLOBALS["entity_page"][$GLOBALS["entity_val"]]."", $font, 7, array(0, 0, 0, 1));
                $GLOBALS["entity_y"] = $GLOBALS["entity_y"] + 19;
                $GLOBALS["entity_val"] = $GLOBALS["entity_val"] + 1;
                if (($GLOBALS["entity_val"] + 1) % $GLOBALS["entity_per_page"] == 0 ) {
                    $GLOBALS["entity_page_number"] = $GLOBALS["entity_page_number"] + 1;
                    $GLOBALS["entity_y"] = 31;
                }
            }
        }');
    }
}

the isset is important as for some reason the foreach will loop an additional time and you will get an out of bounds exception thrown at the end.

怀念你的温柔 2024-07-22 17:53:19

我在 pdf 中创建 TOC 的想法如下:

  1. 生成 pdf,其中页面标题将包含随机令牌而不是名称
  2. 解析生成的 pdf(使用 Smalot\PdfParser)并查找包含令牌的页码
  3. 生成 pdf,其中用真实姓名替换令牌并找到页码。

例子:

$html = "<html>
<div class='toc'>
<a>Article1 ..... {article_1_num}</a>
</div>
...
<div class='article'>
    <div>{article_1_title}</div>
    <div>Article content</div>
</div>
</html>";
//prepare variables to replace in template with random token
$vars = ["{article_1_title}"=>'676TGZGHVGFTRR655R66TTFTF', "{article_1_num}"=>0];
//genetate pdf
$options = new Options();
$options->set('defaultFont', 'Courier');
$options->set('isRemoteEnabled', TRUE);
$options->set('debugKeepTemp', TRUE);
$options->set('isPhpEnabled', TRUE);
$options->set('isHtml5ParserEnabled', true);
$dompdf = new Dompdf($options);
//load html with variables replaced
$dompdf->loadHtml(strtr($html, $vars));
$dompdf->setPaper('A4');
@$dompdf->render();
//create tamporary file
$temp = tempnam(sys_get_temp_dir(), 'prefix');
$output = $dompdf->output();
//save to temporary file
file_put_contents($temp, $output);

// parse pdf 
$parser = new \Smalot\PdfParser\Parser();
$pdf = $parser->parseFile($temp);
$pages = $pdf->getPages();
$pageNum = 0;
//loop the pages and find the one with the token
foreach ($pages as $k=>$page) {
    if(strpos($page,'676TGZGHVGFTRR655R66TTFTF') !== false){
       $pageNum = $k+1;
       break;
    }
}
// remove temp file
unlink($temp);
//prepare variables with real values to replace in template
$vars = ["{article_1_title}"=>'Article no. 1', "{article_1_num}"=>$pageNum];
// generate pdf and stream it to user
$options = new Options();
$options->set('defaultFont', 'Courier');
$options->set('isRemoteEnabled', TRUE);
$options->set('debugKeepTemp', TRUE);
$options->set('isPhpEnabled', TRUE);
$options->set('isHtml5ParserEnabled', true);
$dompdf = new Dompdf($options);
//load html with variables replaced
$dompdf->loadHtml(strtr($html, $vars));
$dompdf->setPaper('A4');
@$dompdf->render();
$dompdf->stream("pdf.pdf", array('Attachment' => 0));

My idea to create TOC in pdf is following:

  1. generate pdf where title of page would contain random token instead of name
  2. parse generated pdf (using Smalot\PdfParser) and find page number which contains the token
  3. generate the pdf where replace tokens with real name and found page number.

Example:

$html = "<html>
<div class='toc'>
<a>Article1 ..... {article_1_num}</a>
</div>
...
<div class='article'>
    <div>{article_1_title}</div>
    <div>Article content</div>
</div>
</html>";
//prepare variables to replace in template with random token
$vars = ["{article_1_title}"=>'676TGZGHVGFTRR655R66TTFTF', "{article_1_num}"=>0];
//genetate pdf
$options = new Options();
$options->set('defaultFont', 'Courier');
$options->set('isRemoteEnabled', TRUE);
$options->set('debugKeepTemp', TRUE);
$options->set('isPhpEnabled', TRUE);
$options->set('isHtml5ParserEnabled', true);
$dompdf = new Dompdf($options);
//load html with variables replaced
$dompdf->loadHtml(strtr($html, $vars));
$dompdf->setPaper('A4');
@$dompdf->render();
//create tamporary file
$temp = tempnam(sys_get_temp_dir(), 'prefix');
$output = $dompdf->output();
//save to temporary file
file_put_contents($temp, $output);

// parse pdf 
$parser = new \Smalot\PdfParser\Parser();
$pdf = $parser->parseFile($temp);
$pages = $pdf->getPages();
$pageNum = 0;
//loop the pages and find the one with the token
foreach ($pages as $k=>$page) {
    if(strpos($page,'676TGZGHVGFTRR655R66TTFTF') !== false){
       $pageNum = $k+1;
       break;
    }
}
// remove temp file
unlink($temp);
//prepare variables with real values to replace in template
$vars = ["{article_1_title}"=>'Article no. 1', "{article_1_num}"=>$pageNum];
// generate pdf and stream it to user
$options = new Options();
$options->set('defaultFont', 'Courier');
$options->set('isRemoteEnabled', TRUE);
$options->set('debugKeepTemp', TRUE);
$options->set('isPhpEnabled', TRUE);
$options->set('isHtml5ParserEnabled', true);
$dompdf = new Dompdf($options);
//load html with variables replaced
$dompdf->loadHtml(strtr($html, $vars));
$dompdf->setPaper('A4');
@$dompdf->render();
$dompdf->stream("pdf.pdf", array('Attachment' => 0));
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文