Tuesday, August 16, 2011

PDFlib & PHP (8) Combo #1 Large

Finally, I need to have text flow and while I was going to use a tables-only approach it turns out fit_textflow returns _boxfull and knows where to resume the text of a text flow, on the next page (just like fit_table when fitting added cells).

What I did to make my data-set big enough was to go find the lyrics of the favoriteSong listed in the bands.xml and paste those lyrics into a new tag under band called lyrics.



#########
$file_name = "blog_8.pdf";
# number reflect an A4 sized document.
$doc_width = 595;
$doc_height = 842;
function buildTOC($pdf, $table_of_contents, $font){
global $doc_width, $doc_height, $file_name;
$p = $pdf;
$toc = $table_of_contents;
if ($p->setfont($font, 12) == 0)
throw new Exception("Error@setfont: " . $p->get_errmsg());
###### TABLE TIME ###########
$table=0;
$num_columns = 3;
$num_rows = count($toc);
#llx = lower left x, whereas ury = upper right y
$llx= 30; $lly=50; $urx=550; $ury=760;
# flipping the process to per row, per col.
$row_num = 0;
$widths = array(150, 370, 30);
# keep from ugly via tagging left and right with center, i.e. right center.
$positions = array("left center", "right center", "right center");
do{
$row_num++;
$col_num = 0;
do{
$col_num++;
$position = $positions[$col_num-1];
#addnameddest() may be great for a more complex document, i.e. having sub-sections in the
# middle of a page/section. but, going to the page where a section starts requires laying
# an annotation over each row in the TOC that we have w/o sub-section._this is how:
# create_action w/ type GoTo, create_annotation w/ type: Link
$total_line_length = 100;
list($section, $page_num) = $toc[$row_num-1];
# using groups we can point at the group's page_num.
$link_optlist = "filename=$file_name destination={group=content page=$page_num type=fitwindow}";
$action = $p->create_action("GoTo", $link_optlist);
# the x positions are all good for these table rows to match the fit_table.
# but, the y positions need to change by 20, each, per row...
$anno_ury = $ury-(20*($row_num-1));
$annotation = $p->create_annotation($llx, $anno_ury-20, $urx, $anno_ury, "Link", "linewidth=0 action {activate $action}");
$periods = str_repeat('.', ($total_line_length-(strlen($section)+strlen($page_num))));
$optlist = "fittextline={position={".$position."} font=" . $font .
" fontsize=14} rowheight=20 colwidth=".($widths[$col_num-1]);//.$cell_border_opt;
$value = (($col_num-1) == 0)?$toc[$row_num-1][0]:((($col_num-1)==1)?$periods:$toc[$row_num-1][1]);
$table = $p->add_table_cell($table, $col_num, $row_num, $value, $optlist);
} while($col_num < $num_columns);
} while ($row_num < $num_rows);
###### fit_table optlist. time to place table.
$optlist = "";
//"stroke={{line=other}} header=0 fill={{area=rowodd fillcolor={gray 0.9}}} stroke={{line=other}} ";
$result = $p->fit_table($table, $llx, $lly, $urx, $ury, $optlist);
if ($result == "_error") {
die("Couldn't place table: " . PDF_get_errmsg($p));
}
}
###### END FUNC LIST ###########
try{
# image tag of xml has url to image like so:
# http://img.karaoke-lyrics.net/img/artists/32670/the-pogues-164114.jpg
$xml = new SimpleXMLElement($xmlstr);
# create a new instance of PDFlib.
$p = new PDFlib();
$p->set_parameter("logging", "filename {PDFlib.log}");
$p->set_parameter("errorpolicy", "exception");
# set textformat
$p->set_parameter("textformat", "utf8");
# SPACES are important here, at end of concatenated lines.
# all we really need for example to work are the groups and labels chunk.
$optlist = "groups={title toc content index} ".
"labels={{group=title prefix=title} ".
"{group=toc prefix={toc } start=1 style=r} ".
"{group=content start=1 style=D} ".
"{group=index prefix={index } start=1 style=r} } ".
"destination={ type=fixed zoom=.5 } " .
"viewerpreferences={displaydoctitle=true direction=l2r} ".
"openmode=thumbnails ".
"pagelayout=twocolumnleft ";
if ($p->begin_document("", $optlist) == 0)
throw new Exception("Error@begin_document: " . $p->get_errmsg());
# add some meta data on file
$p->set_info("Creator", "Neil Lindberg");
$p->set_info("Title", "SVM User Guide (2.1)");
# loading font (that most will have)
$font = $p->load_font("Helvetica", "unicode", "");
$p->begin_page_ext($doc_width, $doc_height, "group=title");
if ($p->setfont($font, 24) == 0)
throw new Exception("Error@setfont: " . $p->get_errmsg());
# add line of text, to center of page, approximately :)
$message = "Bands XML Breakdown";
$message_width = $p->stringwidth($message, $font, 24);
$p->show_xy($message, ($doc_width*.5)-($message_width*.5), $doc_height*.5);
#
$p->end_page_ext("");
# make page per band... suspend to later add page footer/num.
$num_bands = count($xml->band);
$count = 0;
$toc = array();
// TODO: Add text to the point of run-on to next page...
foreach($xml->band as $x){
$count++;
# TOC will be a breeze knowing the count reflects the
# page number of the content section, and we only have
# one page per band...
$toc[] = array($x->name, $count);
$p->begin_page_ext($doc_width, $doc_height, "group=content");
if ($p->setfont($font, 24) == 0)
throw new Exception("Error@setfont: " . $p->get_errmsg());
# put name of band in upper left-aligned corner.
$message = $x->name;
$p->show_xy($message, 24, $doc_height-48);
if ($p->setfont($font, 12) == 0)
throw new Exception("Error@setfont: " . $p->get_errmsg());
# add band info in a rectangular container, align center.
$band_info = "Genre: $x->genre\nFavorite song: $x->favoriteSong";
# NOTE: using fillcolor rather than ->setcolor does
# not require setting back to black.
$band_info_font = "font=$font fontsize=12 alignment=center ".
"fillcolor={spotname {PANTONE 222 U} 1}";
$textflow = $p->create_textflow($band_info, $band_info_font);
$p->fit_textflow($textflow, 50, 120, 550, 720, "");
# loading image from url is a bit more work. this section is that...
# we will load it and then put it in a temp pvf file
$image_data = file_get_contents($x->image);
$p->create_pvf("/pvf/image", $image_data, "");
# load image. auto == automatically detect type. could be jpeg, etc.
# NOTE: if image were local none of the pvf would be needed.
# instead the "/pvf/image" would be the image local name...
$image = $p->load_image("auto", "/pvf/image", "");
# temp pvf used, clear it for next run.
$p->delete_pvf("/pvf/image");
# finally place image.
$matchbox_name = $count."_image";
# options say: matchbox (for future access), boxsize for w/h, and finally,
# boxsize wouldn't work w/o some fitmethod. meet == scale to boxsize.
$fit_image_opts = "matchbox={name=$matchbox_name} ".
"boxsize={200 200} fitmethod=meet";
$p->fit_image($image, 200, 400, $fit_image_opts);
$p->close_image($image);
# add the url to the bottom of image using matchbox
if($p->info_matchbox($matchbox_name, 1, "exists")){
# x1, y1 = lower left. x2, y2 = lower right, etc.
$lower_left_x = $p->info_matchbox($matchbox_name, 1, "x1");
$lower_left_y = $p->info_matchbox($matchbox_name, 1, "y1");
$image_nm = explode("/", $x->image);
$p->show_xy($image_nm[count($image_nm)-1],
$lower_left_x,
$lower_left_y-12);
}
# add textflow, track pages.
$textflow_optlist = "font=$font fontsize=12";
$textflow = $p->create_textflow($x->lyrics, $textflow_optlist);
# can we just use the same return strings to continue here?
# as with fit_table? we can!
$result = $p->fit_textflow($textflow, 50, 50, 550, 370, "");
if ($result == "_error") {
die("Couldn't place table: " . PDF_get_errmsg($p));
}
# states of result of fit_table...
# I'm not finding this in the documentation, yet.
if($result == "_boxfull"){
if ($p->setfont($font, 12) == 0)
throw new Exception("Error@setfont: " . $p->get_errmsg());
$message = "Page $count";
$message_width = $p->stringwidth($message, $font, 12);
# cause to right-align in bottom corner.
$page_num_x = ($doc_width-$message_width)-24;
$page_num_y = $doc_height-($doc_height-24);
$p->show_xy($message, $page_num_x, $page_num_y);
$p->end_page_ext("");
do{
$p->begin_page_ext($doc_width, $doc_height, "group=content");
$count++;
if ($p->setfont($font, 24) == 0)
throw new Exception("Error@setfont: " . $p->get_errmsg());
$result = $p->fit_textflow($textflow, 50, 120, 550, 720, "");
if ($result == "_error") {
die("Couldn't place table: " . PDF_get_errmsg($p));
}
if ($p->setfont($font, 12) == 0)
throw new Exception("Error@setfont: " . $p->get_errmsg());
$message = "Page $count";
$message_width = $p->stringwidth($message, $font, 12);
# cause to right-align in bottom corner.
$page_num_x = ($doc_width-$message_width)-24;
$page_num_y = $doc_height-($doc_height-24);
$p->show_xy($message, $page_num_x, $page_num_y);
$p->end_page_ext("");
}while ($result == "_boxfull");
}else{
if ($p->setfont($font, 12) == 0)
throw new Exception("Error@setfont: " . $p->get_errmsg());
$message = "Page $count";
$message_width = $p->stringwidth($message, $font, 12);
# cause to right-align in bottom corner.
$page_num_x = ($doc_width-$message_width)-24;
$page_num_y = $doc_height-($doc_height-24);
$p->show_xy($message, $page_num_x, $page_num_y);
$p->end_page_ext("");
}
// $p->end_page_ext("");
}
# TOC information is ready, add page and contents.
$p->begin_page_ext($doc_width, $doc_height, "group=toc");
if ($p->setfont($font, 24) == 0)
throw new Exception("Error@setfont: " . $p->get_errmsg());
# put TOC label in upper left-aligned corner.
$message = "Table of Contents";
$p->show_xy($message, 24, $doc_height-48);
#
if ($p->setfont($font, 12) == 0)
throw new Exception("Error@setfont: " . $p->get_errmsg());
# we will make it pretty with these two:
#string str_repeat ( string $input , int $multiplier )
#int strlen ( string $string )
buildTOC($p, $toc, $font);
$p->end_page_ext("");
#
$p->end_document("");
# get and send buffer
$buf = $p->get_buffer();
$len = strlen($buf);
header("Content-type: application/pdf");
header("Content-Length: $len");
header("Content-Disposition: inline; filename=$file_name");
print $buf;
} catch (PDFlibException $e) {
die("PDFlib exception occurred:\n".
"[" . $e->get_errnum() . "] " . $e->get_apiname() .
": " . $e->get_errmsg() . "\n");
} catch (Exception $e) {
die($e->getMessage());
}

No comments:

Post a Comment