5

我正在使用 fpdf 生成 pdf 发票。

一些包含许多项目的发票,详细信息需要进入第二页。但是,我需要在第一页上显示总数和其他详细信息。

现在,如果我可以像这样添加新页面: $pdf->AddPage();

但是,这会将所有内容放入第二页,无论此声明之后。

似乎没有办法为 write 或 cell 方法指定页面。

渲染和计算有点复杂,因此不想存储到临时数组中并在完全渲染第一页后显示。

谢谢你。

4

10 回答 10

6

我遇到了一个非常相似的问题,我继续往课堂上添加了这个函数:

function SetPage($num) {
    $this->page = $num;
}

这确实符合您的预期。但是,当您尝试返回下一页时,您需要明确设置它或添加到 Cell() 功能。这是它添加页面的地方:

    if($this->y+$h>$this->PageBreakTrigger && !$this->InHeader && !$this->InFooter && $this->AcceptPageBreak())

这将检查 AcceptPageBreak 并且显然页面的高度是否小于我们停留在页面上时的最终高度。我所做的是在这里添加一个条件来检查下面是否有另一个页面。如果有,而不是添加新页面,我继续并将 Y 设置为我的边距并使用上述功能访问该页面。像这样:

if ($this->page < count($this->pages)) {
        $this->SetPage(($this->page)+1);
        $this->y = .5;
    }

如果您对弄乱 FPDF 感到不自在,这可能不适合您,但我在解决此问题后意识到实际上该脚本非常容易导航,并且为简单的改进留下了很大的空间。请注意,除了在上面检查另一个页面之外,您还可以添加一个可以调用的变量,该变量会临时告诉脚本您无需添加页面,而是跳转到下一个页面。

于 2013-01-06T22:57:11.847 回答
4

FPDF 不允许您返回上一页。一旦你完成了一个页面 - 通过调用 AddPage() 或在打开 SetAutoPageBreak() 时空间不足 - 你就完成了它。

解决方法是生成没有总数的文档,将其写入临时文件,然后将其加载回来(使用 FPDI:http ://www.setasign.de/products/pdf-php-solutions/fpdi/ )并添加总数在正确的地方。

于 2009-10-22T19:17:26.850 回答
4
if(!empty($this->replace) && !empty($this->replacement)){
    $this->pages[$n]=str_replace($this->replace,$this->replacement,$this->pages[$n]);
}

我将此代码添加到 /Page 内容行 2851 下 _putpages() 中的 fpdf 类中

将上面添加到代码后的示例:

$pdf->replace = array("{paid_msg}","{balance}");
$pdf->replacement = array("Paid","0.00");

它可以是字符串或数组。

于 2010-12-17T19:04:08.890 回答
1

我在使用两列布局时遇到了这个问题,其中一列可能太大以至于会跨越到下一页。我想将 Y 位置设置回与第一列相同的 Y 位置,以便它们彼此顶部对齐,但还要在两列中最大的列下继续文档。

我的解决方案是记录文档的“垂直位置”,其中包括页码 ( $pdf->PageNo()) 和 Y 位置 ( $pdf->GetY())。

您需要存储两个不同的垂直位置。首先,存储“起点”,这是您开始第二列的地方。其次,存储文档最下方的“最大点”。最大的一点很棘手,因为您不能单独查看页码或 Y 值,您必须同时查看两者。

我创建了这三种方法来帮助我。

此解决方案不包括示例中的 X 位置。

public function GetVerticalPosition() {
  // Include page and Y position of the document
  return array(
    'page' => $this->PageNo(),
    'y' => $this->GetY(),
  );
}

public function SetVerticalPosition( $pos ) {
  // Set the page and Y position of the document
  $this->page = $pos['page'];
  $this->SetY( $pos['y'] );
}

public function FurthestVerticalPosition( $aPos, $bPos = null ) {
  if ( $bPos === null ) $bPos = $this->GetVerticalPosition();

  // Returns the "furthest" vertical position between two points, based on page and Y position
  if ( 
    ($aPos['page'] > $bPos['page']) // Furthest position is located on another page
    ||
    ($aPos['page'] == $bPos['page'] && $aPos['y'] > $bPos['y'] ) // Furthest position is within the same page, but further down
  ) {
    return $aPos;
  }else{
    return $bPos;
  }
}

用法非常简单。在绘制可变高度列之前,您需要获取起始位置,并开始收集最大位置。

$startPos = $this->GetVerticalPosition();
$furthestPos = $this->GetVerticalPosition();

在渲染每个单元格之后,在渲染同一级别的另一个单元格之前,您希望更新最远位置(如果需要),然后设置回起始位置。

// Returns the furthest of the two possibilites
$furthestPos = $this->FurthestVerticalPosition( $this->GetVerticalPosition(), $furthestPos );
$this->SetVerticalPosition( $startPos );

完成列渲染后,将文档设置为迄今为止记录的最大距离。

$this->SetVerticalPosition( $furthestPos );

现在您的列已正确对齐,并且文档指针立即位于最远绘制的列之后。

于 2014-03-19T18:43:00.737 回答
1

我遇到了同样的问题,我覆盖了 FPDF 类的一些方法,并且能够以相当简单的方式修复它。为了来回页面,您只需更改 FPDF 对象的页面属性。但是,一旦您返回一个页面,当您前进到下一页时,您最终会添加一个新页面,而不是前进到已经存在的页面。为了解决这个问题,我创建了一个名为 add_new_page 的布尔值,并在我重写的 accept_page_break 方法中使用它来查看是否创建一个新页面或进入已经存在的页面。

def prev_page(self):
    self.page -= 1
    self.add_new_page = False

def next_page(self):
    self.page += 1

def accept_page_break(self):
    "Accept automatic page break or not"
    if self.add_page:
        return self.auto_page_break
    else:
        self.page += 1
        self.add_new_page = True
于 2017-08-08T20:39:02.153 回答
1

我使用了 Radley Sustaire 的答案(谢谢),效果很好。我想为那些无法正常工作的人添加这个:

  1. 将公共函数添加到 FPDF 源 (fpdf.php)
  2. 当您想返回不同页面中的不同 Y-Pos 时,您可以使用数组来跟踪着陆点:
    $startPos[] = $pdf->GetVerticalPosition();
  1. 要返回某个页面,您可以使用:
    $pdf->SetVerticalPosition( $startPos[0] );

[0] -> 数组中的第一页/Ypos
[1] -> 数组中的第二页/Ypos
[n] -> 数组中的 n+1 页/Ypos

于 2021-07-30T09:57:23.217 回答
0

这里有一些聪明的解决方案,但我不推荐这种技巧,因为它违背了基本的 FPDF 模型,迟早会导致其他问题。

编程已经够复杂了!

因此,将发票的模型或任何它与演示文稿分开。构建整个模型。然后输出演示文稿(本例中为 PDF)。

于 2013-01-07T14:37:35.953 回答
0

我发现 *str_replace* 方法非常合适,因为需要编写“n of m pages”。首先在我构建每一页时正常写入 n 数字,最后逐页替换 $pdf->pages[$i] 中的 m 密码。

于 2013-02-01T18:55:59.837 回答
0

只是一个简单的“hack”,但是数据需要按页码排序(不是很困难^ ^)

放入您的“for循环”放置数据,例如:

// ensure we are on the good page, but fields HAVE TO be ordered by page number
while($this->fpdf->PageNo() != $myData->getPage()){
    $this->fpdf->AddPage();
}

你完成了!无需更改 FPDF :D

于 2017-11-18T23:58:35.133 回答
0

感谢 Jared Alessandroni 的出色回答。在使用具有 2 列的 MultiCell() 的情况下,只需稍作调整,这对我有用。

更新单元格功能

function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='')
{
    // Output a cell
    $txt = (string)$txt;
    $k = $this->k;

    // This is where triggered the page break 
    if($this->y+$h>$this->PageBreakTrigger && !$this->InHeader && !$this->InFooter && $this->AcceptPageBreak())
    {
        // Automatic page break
        $x = $this->x;
        $ws = $this->ws;
        if($ws>0)
        {
            $this->ws = 0;
            $this->_out('0 Tw');
        }

        // if page num is not last, go to next page 
        if ($this->page < count($this->pages)) {
            $this->SetPage(($this->page)+1);
            $this->y = $this->tMargin;
        } else { // just regular way
            $this->AddPage($this->CurOrientation,$this->CurPageSize,$this->CurRotation);
        }

        $this->x = $x;
        if($ws>0)
        {
            $this->ws = $ws;
            $this->_out(sprintf('%.3F Tw',$ws*$k));
        }
    }
    
    if($w==0)
        $w = $this->w-$this->rMargin-$this->x;
    $s = '';
    if($fill || $border==1)
    {
        if($fill)
            $op = ($border==1) ? 'B' : 'f';
        else
            $op = 'S';
        $s = sprintf('%.2F %.2F %.2F %.2F re %s ',$this->x*$k,($this->h-$this->y)*$k,$w*$k,-$h*$k,$op);
    }
    if(is_string($border))
    {
        $x = $this->x;
        $y = $this->y;
        if(strpos($border,'L')!==false)
            $s .= sprintf('%.2F %.2F m %.2F %.2F l S ',$x*$k,($this->h-$y)*$k,$x*$k,($this->h-($y+$h))*$k);
        if(strpos($border,'T')!==false)
            $s .= sprintf('%.2F %.2F m %.2F %.2F l S ',$x*$k,($this->h-$y)*$k,($x+$w)*$k,($this->h-$y)*$k);
        if(strpos($border,'R')!==false)
            $s .= sprintf('%.2F %.2F m %.2F %.2F l S ',($x+$w)*$k,($this->h-$y)*$k,($x+$w)*$k,($this->h-($y+$h))*$k);
        if(strpos($border,'B')!==false)
            $s .= sprintf('%.2F %.2F m %.2F %.2F l S ',$x*$k,($this->h-($y+$h))*$k,($x+$w)*$k,($this->h-($y+$h))*$k);
    }
    if($txt!=='')
    {
        if(!isset($this->CurrentFont))
            $this->Error('No font has been set');
        if($align=='R')
            $dx = $w-$this->cMargin-$this->GetStringWidth($txt);
        elseif($align=='C')
            $dx = ($w-$this->GetStringWidth($txt))/2;
        else
            $dx = $this->cMargin;
        if($this->ColorFlag)
            $s .= 'q '.$this->TextColor.' ';
        // If multibyte, Tw has no effect - do word spacing using an adjustment before each space
        if ($this->ws && $this->unifontSubset) {
            foreach($this->UTF8StringToArray($txt) as $uni)
                $this->CurrentFont['subset'][$uni] = $uni;
            $space = $this->_escape($this->UTF8ToUTF16BE(' ', false));
            $s .= sprintf('BT 0 Tw %.2F %.2F Td [',($this->x+$dx)*$k,($this->h-($this->y+.5*$h+.3*$this->FontSize))*$k);
            $t = explode(' ',$txt);
            $numt = count($t);
            for($i=0;$i<$numt;$i++) {
                $tx = $t[$i];
                $tx = '('.$this->_escape($this->UTF8ToUTF16BE($tx, false)).')';
                $s .= sprintf('%s ',$tx);
                if (($i+1)<$numt) {
                    $adj = -($this->ws*$this->k)*1000/$this->FontSizePt;
                    $s .= sprintf('%d(%s) ',$adj,$space);
                }
            }
            $s .= '] TJ';
            $s .= ' ET';
        }
        else {
            if ($this->unifontSubset)
            {
                $txt2 = '('.$this->_escape($this->UTF8ToUTF16BE($txt, false)).')';
                foreach($this->UTF8StringToArray($txt) as $uni)
                    $this->CurrentFont['subset'][$uni] = $uni;
            }
            else
                $txt2='('.$this->_escape($txt).')';
            $s .= sprintf('BT %.2F %.2F Td %s Tj ET',($this->x+$dx)*$k,($this->h-($this->y+.5*$h+.3*$this->FontSize))*$k,$txt2);
        }
        if($this->underline)
            $s .= ' '.$this->_dounderline($this->x+$dx,$this->y+.5*$h+.3*$this->FontSize,$txt);
        if($this->ColorFlag)
            $s .= ' Q';
        if($link)
            $this->Link($this->x+$dx,$this->y+.5*$h-.5*$this->FontSize,$this->GetStringWidth($txt),$this->FontSize,$link);
    }
    if($s)
        $this->_out($s);
    $this->lasth = $h;
    if($ln>0)
    {
        // Go to next line
        $this->y += $h;
        if($ln==1)
            $this->x = $this->lMargin;
    }
    else
        $this->x += $w;
}
于 2021-11-11T10:25:41.637 回答