我正在使用 fpdf 生成 pdf 发票。
一些包含许多项目的发票,详细信息需要进入第二页。但是,我需要在第一页上显示总数和其他详细信息。
现在,如果我可以像这样添加新页面: $pdf->AddPage();
但是,这会将所有内容放入第二页,无论此声明之后。
似乎没有办法为 write 或 cell 方法指定页面。
渲染和计算有点复杂,因此不想存储到临时数组中并在完全渲染第一页后显示。
谢谢你。
我遇到了一个非常相似的问题,我继续往课堂上添加了这个函数:
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 感到不自在,这可能不适合您,但我在解决此问题后意识到实际上该脚本非常容易导航,并且为简单的改进留下了很大的空间。请注意,除了在上面检查另一个页面之外,您还可以添加一个可以调用的变量,该变量会临时告诉脚本您无需添加页面,而是跳转到下一个页面。
FPDF 不允许您返回上一页。一旦你完成了一个页面 - 通过调用 AddPage() 或在打开 SetAutoPageBreak() 时空间不足 - 你就完成了它。
解决方法是生成没有总数的文档,将其写入临时文件,然后将其加载回来(使用 FPDI:http ://www.setasign.de/products/pdf-php-solutions/fpdi/ )并添加总数在正确的地方。
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");
它可以是字符串或数组。
我在使用两列布局时遇到了这个问题,其中一列可能太大以至于会跨越到下一页。我想将 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 );
现在您的列已正确对齐,并且文档指针立即位于最远绘制的列之后。
我遇到了同样的问题,我覆盖了 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
我使用了 Radley Sustaire 的答案(谢谢),效果很好。我想为那些无法正常工作的人添加这个:
$startPos[] = $pdf->GetVerticalPosition();
$pdf->SetVerticalPosition( $startPos[0] );
[0] -> 数组中的第一页/Ypos
[1] -> 数组中的第二页/Ypos
[n] -> 数组中的 n+1 页/Ypos
这里有一些聪明的解决方案,但我不推荐这种技巧,因为它违背了基本的 FPDF 模型,迟早会导致其他问题。
编程已经够复杂了!
因此,将发票的模型或任何它与演示文稿分开。构建整个模型。然后输出演示文稿(本例中为 PDF)。
我发现 *str_replace* 方法非常合适,因为需要编写“n of m pages”。首先在我构建每一页时正常写入 n 数字,最后逐页替换 $pdf->pages[$i] 中的 m 密码。
只是一个简单的“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
感谢 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;
}