Thursday, August 11, 2011

PDFlib & PHP - (5) Add TOC

Using the code from blog PDFlib & PHP (4) I will eventually add a TOC between the Title page and the Content section. I'm expecting this to go smooth and quick because the group "toc" is already in the begin_document optslist.

The TOC is here! Whoop! But, I used an algorithm to say: you have this much space to fill, take the length of the section string, and the length of the page number string, then write out section, fill with periods or some filler in the amount of the difference of the "this much space"-(length of section+length of page number)... but, that doesn't layout as nice as I thought. I can see it is because periods don't take the width of the other characters... So, while working there is improvement to be done. I think it is time to break out: add_table_cell and see if I can't fit a table that lines up all pretty. That'll be tomorrow.



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);
# number reflect an A4 sized document.
$doc_width = 595;
$doc_height = 842;
# 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();
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 page n of E.
$message = "Page $count of $num_bands";
$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("");
}
# 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 )
$total_line_length = 70;
for($i = 0; $i < count($toc); $i++){
$section = $toc[$i][0];
$page_num = $toc[$i][1];
$periods = $total_line_length-(strlen($section)+strlen($page_num));
syslog(LOG_ERR, "strlen(section): ".strlen($section));
syslog(LOG_ERR, "strlen(page_num): ".strlen($page_num));
syslog(LOG_ERR, "PERIODS: ".$periods);
$message = $section.str_repeat('~', $periods).$page_num;
$message_width = $p->stringwidth($message, $font, 12);
$p->show_xy($message, 36, $doc_height-(24*($i+1))-60);
}
$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=blog_5.pdf");
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