<?php
require('fpdf.php');
class PDF_FlowingBlock extends FPDF
{
protected $flowingBlockAttr;
function saveFont()
{
$saved = array();
$saved[ 'family' ] = $this->FontFamily;
$saved[ 'style' ] = $this->FontStyle;
$saved[ 'sizePt' ] = $this->FontSizePt;
$saved[ 'size' ] = $this->FontSize;
$saved[ 'curr' ] = $this->CurrentFont;
return $saved;
}
function restoreFont( $saved )
{
$this->FontFamily = $saved[ 'family' ];
$this->FontStyle = $saved[ 'style' ];
$this->FontSizePt = $saved[ 'sizePt' ];
$this->FontSize = $saved[ 'size' ];
$this->CurrentFont = $saved[ 'curr' ];
if( $this->page > 0)
$this->_out( sprintf( 'BT /F%d %.2F Tf ET', $this->CurrentFont[ 'i' ], $this->FontSizePt ) );
}
function newFlowingBlock( $w, $h, $b = 0, $a = 'J', $f = 0 )
{
// cell width in points
$this->flowingBlockAttr[ 'width' ] = $w * $this->k;
// line height in user units
$this->flowingBlockAttr[ 'height' ] = $h;
$this->flowingBlockAttr[ 'lineCount' ] = 0;
$this->flowingBlockAttr[ 'border' ] = $b;
$this->flowingBlockAttr[ 'align' ] = $a;
$this->flowingBlockAttr[ 'fill' ] = $f;
$this->flowingBlockAttr[ 'font' ] = array();
$this->flowingBlockAttr[ 'content' ] = array();
$this->flowingBlockAttr[ 'contentWidth' ] = 0;
}
function finishFlowingBlock()
{
$maxWidth = $this->flowingBlockAttr[ 'width' ];
$lineHeight = $this->flowingBlockAttr[ 'height' ];
$border = $this->flowingBlockAttr[ 'border' ];
$align = $this->flowingBlockAttr[ 'align' ];
$fill = $this->flowingBlockAttr[ 'fill' ];
$content = $this->flowingBlockAttr[ 'content' ];
$font = $this->flowingBlockAttr[ 'font' ];
// set normal spacing
$this->_out( sprintf( '%.3F Tw', 0 ) );
// print out each chunk
// the amount of space taken up so far in user units
$usedWidth = 0;
foreach ( $content as $k => $chunk )
{
$b = '';
if ( is_int( strpos( $border, 'B' ) ) )
$b .= 'B';
if ( $k == 0 && is_int( strpos( $border, 'L' ) ) )
$b .= 'L';
if ( $k == count( $content ) - 1 && is_int( strpos( $border, 'R' ) ) )
$b .= 'R';
$this->restoreFont( $font[ $k ] );
// if it's the last chunk of this line, move to the next line after
if ( $k == count( $content ) - 1 )
$this->Cell( ( $maxWidth / $this->k ) - $usedWidth + 2 * $this->cMargin, $lineHeight, $chunk, $b, 1, $align, $fill );
else
$this->Cell( $this->GetStringWidth( $chunk ), $lineHeight, $chunk, $b, 0, $align, $fill );
$usedWidth += $this->GetStringWidth( $chunk );
}
}
function WriteFlowingBlock( $s )
{
// width of all the content so far in points
$contentWidth =& $this->flowingBlockAttr[ 'contentWidth' ];
// cell width in points
$maxWidth = $this->flowingBlockAttr[ 'width' ];
$lineCount =& $this->flowingBlockAttr[ 'lineCount' ];
// line height in user units
$lineHeight = $this->flowingBlockAttr[ 'height' ];
$border = $this->flowingBlockAttr[ 'border' ];
$align = $this->flowingBlockAttr[ 'align' ];
$fill = $this->flowingBlockAttr[ 'fill' ];
$content =& $this->flowingBlockAttr[ 'content' ];
$font =& $this->flowingBlockAttr[ 'font' ];
$font[] = $this->saveFont();
$content[] = '';
$currContent =& $content[ count( $content ) - 1 ];
// where the line should be cutoff if it is to be justified
$cutoffWidth = $contentWidth;
// for every character in the string
for ( $i = 0; $i < strlen( $s ); $i++ )
{
// extract the current character
$c = $s[ $i ];
// get the width of the character in points
$cw = $this->CurrentFont[ 'cw' ][ $c ] * ( $this->FontSizePt / 1000 );
if ( $c == ' ' )
{
$currContent .= ' ';
$cutoffWidth = $contentWidth;
$contentWidth += $cw;
continue;
}
// try adding another char
if ( $contentWidth + $cw > $maxWidth )
{
// won't fit, output what we have
$lineCount++;
// contains any content that didn't make it into this print
$savedContent = '';
$savedFont = array();
// first, cut off and save any partial words at the end of the string
$words = explode( ' ', $currContent );
// if it looks like we didn't finish any words for this chunk
if ( count( $words ) == 1 )
{
// save and crop off the content currently on the stack
$savedContent = array_pop( $content );
$savedFont = array_pop( $font );
// trim any trailing spaces off the last bit of content
$currContent =& $content[ count( $content ) - 1 ];
$currContent = rtrim( $currContent );
}
// otherwise, we need to find which bit to cut off
else
{
$lastContent = '';
for ( $w = 0; $w < count( $words ) - 1; $w++)
$lastContent .= "{$words[ $w ]} ";
$savedContent = $words[ count( $words ) - 1 ];
$savedFont = $this->saveFont();
// replace the current content with the cropped version
$currContent = rtrim( $lastContent );
}
// update $contentWidth and $cutoffWidth since they changed with cropping
$contentWidth = 0;
foreach ( $content as $k => $chunk )
{
$this->restoreFont( $font[ $k ] );
$contentWidth += $this->GetStringWidth( $chunk ) * $this->k;
}
$cutoffWidth = $contentWidth;
// if it's justified, we need to find the char spacing
if( $align == 'J' )
{
// count how many spaces there are in the entire content string
$numSpaces = 0;
foreach ( $content as $chunk )
$numSpaces += substr_count( $chunk, ' ' );
// if there's more than one space, find word spacing in points
if ( $numSpaces > 0 )
$this->ws = ( $maxWidth - $cutoffWidth ) / $numSpaces;
else
$this->ws = 0;
$this->_out( sprintf( '%.3F Tw', $this->ws ) );
}
// otherwise, we want normal spacing
else
$this->_out( sprintf( '%.3F Tw', 0 ) );
// print out each chunk
$usedWidth = 0;
foreach ( $content as $k => $chunk )
{
$this->restoreFont( $font[ $k ] );
$stringWidth = $this->GetStringWidth( $chunk ) + ( $this->ws * substr_count( $chunk, ' ' ) / $this->k );
// determine which borders should be used
$b = '';
if ( $lineCount == 1 && is_int( strpos( $border, 'T' ) ) )
$b .= 'T';
if ( $k == 0 && is_int( strpos( $border, 'L' ) ) )
$b .= 'L';
if ( $k == count( $content ) - 1 && is_int( strpos( $border, 'R' ) ) )
$b .= 'R';
// if it's the last chunk of this line, move to the next line after
if ( $k == count( $content ) - 1 )
$this->Cell( ( $maxWidth / $this->k ) - $usedWidth + 2 * $this->cMargin, $lineHeight, $chunk, $b, 1, $align, $fill );
else
{
$this->Cell( $stringWidth + 2 * $this->cMargin, $lineHeight, $chunk, $b, 0, $align, $fill );
$this->x -= 2 * $this->cMargin;
}
$usedWidth += $stringWidth;
}
// move on to the next line, reset variables, tack on saved content and current char
$this->restoreFont( $savedFont );
$font = array( $savedFont );
$content = array( $savedContent . $s[ $i ] );
$currContent =& $content[ 0 ];
$contentWidth = $this->GetStringWidth( $currContent ) * $this->k;
$cutoffWidth = $contentWidth;
}
// another character will fit, so add it on
else
{
$contentWidth += $cw;
$currContent .= $s[ $i ];
}
}
}
}
?>
<?php
require('flowing_block.php');
$pdf = new PDF_FlowingBlock();
$pdf->AddPage();
$pdf->newFlowingBlock( 40, 6, 'TBLR', 'J' );
$pdf->SetFont( 'Arial', 'B', 16 );
$pdf->WriteFlowingBlock( 'Hello ' );
$pdf->SetFont( 'Arial', 'I', 8 );
$pdf->WriteFlowingBlock( 'World! ' );
$pdf->SetFont( 'Times', '', 10 );
$pdf->WriteFlowingBlock( 'This is a test of the flowing block script.' );
$pdf->SetFont( 'Arial', 'B', 12 );
$pdf->WriteFlowingBlock( ' All' );
$pdf->SetFont( 'Times', '', 10 );
$pdf->WriteFlowingBlock( ' of this should be justified correctly.' . str_repeat( ' This is a test of the flowing block script.', 3 ) );
$pdf->finishFlowingBlock();
$pdf->AddPage();
$pdf->newFlowingBlock( 40, 6, 'TBLR', 'L' );
$pdf->SetFont( 'Arial', 'B', 16 );
$pdf->WriteFlowingBlock( 'Hello ' );
$pdf->SetFont( 'Arial', 'I', 8 );
$pdf->WriteFlowingBlock( 'World! ' );
$pdf->SetFont( 'Times', '', 10 );
$pdf->WriteFlowingBlock( 'This is a test of the flowing block script.' );
$pdf->SetFont( 'Arial', 'B', 12 );
$pdf->WriteFlowingBlock( ' All' );
$pdf->SetFont( 'Times', '', 10 );
$pdf->WriteFlowingBlock( ' of this should be justified correctly.' . str_repeat( ' This is a test of the flowing block script.', 3 ) );
$pdf->finishFlowingBlock();
$pdf->AddPage();
$pdf->newFlowingBlock( 40, 6, 'TBLR', 'R' );
$pdf->SetFont( 'Arial', 'B', 16 );
$pdf->WriteFlowingBlock( 'Hello ' );
$pdf->SetFont( 'Arial', 'I', 8 );
$pdf->WriteFlowingBlock( 'World! ' );
$pdf->SetFont( 'Times', '', 10 );
$pdf->WriteFlowingBlock( 'This is a test of the flowing block script.' );
$pdf->SetFont( 'Arial', 'B', 12 );
$pdf->WriteFlowingBlock( ' All' );
$pdf->SetFont( 'Times', '', 10 );
$pdf->WriteFlowingBlock( ' of this should be justified correctly.' . str_repeat( ' This is a test of the flowing block script.', 3 ) );
$pdf->finishFlowingBlock();
$pdf->AddPage();
$pdf->newFlowingBlock( 40, 6, 'TBLR', 'C' );
$pdf->SetFont( 'Arial', 'B', 16 );
$pdf->WriteFlowingBlock( 'Hello ' );
$pdf->SetFont( 'Arial', 'I', 8 );
$pdf->WriteFlowingBlock( 'World! ' );
$pdf->SetFont( 'Times', '', 10 );
$pdf->WriteFlowingBlock( 'This is a test of the flowing block script.' );
$pdf->SetFont( 'Arial', 'B', 12 );
$pdf->WriteFlowingBlock( ' All' );
$pdf->SetFont( 'Times', '', 10 );
$pdf->WriteFlowingBlock( ' of this should be justified correctly.' . str_repeat( ' This is a test of the flowing block script.', 3 ) );
$pdf->finishFlowingBlock();
$pdf->Output();
?>
View the result