I'm trying to do some PDF generation in PHP. I found mPDF which generally has worked quite well. One problem I'm having though is that I need to be able to set separate headers for even/odd pages for some sections. I don't think the function is working right. Here's my code:
// load mPDF
// --------------------------------------------------------------------------
include('mpdf.php');
$mpdf = new mPDF();
// generate a lot of content so it spans multiple pages
// --------------------------------------------------------------------------
$i = 0;
$out = '';
while ($i < 300)
{
$i++;
$out .= '<p>Lorem Ipsum</p>';
}
// set html header for odd pages, write html and output
// --------------------------------------------------------------------------
$mpdf->SetHTMLHeader('ODD {PAGENO}', 'O');
$mpdf->WriteHTML($out);
$mpdf->Output();
When I run this code it puts "ODD Page 3" or whatever page it's on... on every page. Not just the odd pages. It seems to not consider naturally broken pages as new pages for the purposes of even or odd, only manually broken ones. If I run the AddPage() function it will consider the next group an even page. But that could be 50 auto page breaks later.
Any suggestions on how to get it to set different headers for actual even/odd pages, not just after manual page breaks?
your forget to write
$mpdf->mirrorMargins = 1;
here is your working example
<?php
include("../mpdf.php");
$mpdf=new mPDF('utf-8','A4');
$mpdf->debug = true;
$mpdf->mirrorMargins = 1 ;// Use different Odd/Even headers and footers and mirror margins
// generate a lot of content so it spans multiple pages
// --------------------------------------------------------------------------
$i = 0;
$out = '';
while ($i < 300)
{
$i++;
$out .= '<p>Lorem Ipsum</p>';
}
// set html header for odd pages, write html and output
// --------------------------------------------------------------------------
$mpdf->SetHTMLHeader('{PAGENO}/{nb}', 'O',true);
/* Note: SetHTMLHeader() and SetHTMLFooter() without a side(2nd argument)
- sets ODD page header/footer only as default..so you can also write just
$mpdf->SetHTMLHeader('{PAGENO}/{nb}'); */
$mpdf->WriteHTML($out);
$mpdf->Output();
exit;
?>
Reference:
setHTMLHeader
Related
I have dynamic data. So my data can change
besides that I also have a data signature that cannot be separated.
This signature data must be on the same page. See the red mark in this image :
It is a unit and cannot be separated. Signature data
must be on the same page
My problem is because my data is dynamic. This makes the position of the signature data can be located in any position. See image below :
Because my data increases, the position of the headmaster in the signature data is separate
How do I make signature data (see picture 1), which red marks automatically move to the next page if the data is separate?
You can set manual page breaks with something like this:
<?php
//All lines are written from the sheet code at this moment
//The code will insert a page break and the repeated header
//Page margins
$sheet->getPageMargins()->setTop(0.5);
$sheet->getPageMargins()->setRight(0.75);
$sheet->getPageMargins()->setLeft(0.75);
$sheet->getPageMargins()->setBottom(1);
//Use fit to page for the horizontal direction
$sheet->getPageSetup()->setFitToWidth(1);
$sheet->getPageSetup()->setFitToHeight(0);
$headerItems = array(); //add your header items here as array
$headerRowsHeight = 0;// calculated height of header and images of the top
$rowCounter = 100; //get last written row
//add (other) modifier of page hight here
$pageHeight=25+50 + $headerRowsHeight; //Current used page height Header + Footer + $headerRowsHeight
$reset = $pageHeight; //If you will have the firstpage diffrent change reset value and/or pageheight
$pageMaxHeight = 980 ; //Maximale page height DIN A4 arround this
$pageClearance = 15; //Clearance of footer
//Iterate trough all written lines
for ($row = 1; $row <= $rowCounter; ++$row) {
$height=15.1; //standard row height
//get the row height
$dim = $sheet->getRowDimension($row)->getRowHeight();
//get special cell heights (non standard)
if($dim != -1){
$height=$dim;
}
//add height for line to pageheight = get current used space
$pageHeight = $pageHeight + $height;
//Check if the space is still in the range of page
$leftOverSpace = $pageMaxHeight-$pageHeight;
//Change $pageClearance to your preferd space before footer
if( $leftOverSpace < $pageClearance){
//Set pagebraek
$sheet->setBreak('A'.$row, \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet::BREAK_ROW);
//Reset page height
$pageHeight=$reset;
//Add page header to new page
createHeader($sheet, $row+1, $headeritems);
}
}
//Creates a header for every page
function createHeader($sheet, $row, $texts){
$count = $row;
// Iterate trough the header text array
foreach($texts as $text){
//Insert a new line with header
$sheet->insertNewRowBefore($count, 1);
//Do your header stuff here
$count++;
}
//Add two lines after the header
$sheet->insertNewRowBefore($count, 2);
//Return row number
return $count;
}
?>
Place not that the calculation is not 100% exact and that you must adapt this to your code.
I have seen quite a few questions on this but not definitive answer. Im using FPDF to dynamically create PDF with values form a DB, I only want a footer on the last page, seeing as its values are based on DB values the PDF could be 2,3,4 pages long. So i started messing around with the footter of FPDF like so
function Footer()
{
$this->SetY(-15);
$this->SetFont('Arial','I',8);
//Page number
$pagenumber = '{nb}';
if($this->PageNo() == 2){
$this->Cell(173,10, ' FOOTER TEST - '.$pagenumber, 0, 0);
}
}
This retruns the value "FOOTER TEST - 2" on the second page and if i set the if to "<=2" will put the same on page one and two, GREAT! it recognises $pagenumber as 2 or if i had 3 pages as 3, so it can see that i have however many pages, however, when i change the code to:
function Footer()
{
$this->SetY(-15);
$this->SetFont('Arial','I',8);
//Page number
$pagenumber = '{nb}';
if($this->PageNo() == $pagenumber){
$this->Cell(173,10, ' FOOTER TEST - '.$pagenumber, 0, 0);
}
}
Thinking that it output the last page once, adding that to the if statement will make it work, it doesn't display anything, like the if statement cant recognize the value for $pagenumber, is this because it is a string or int trying to compare to the opposite or something else?
Any help greatly appropriated.
Ian
{nb} is done as a late replacement after you have finished writing (as it doesn't know how many pages there are before that) whereas the footer is output whenever a page is complete, your best bet is to probably set a flag on the class to true when you have finished outputting everything and then check the flag in the footer
function Footer()
{
$this->SetY(-15);
$this->SetFont('Arial','I',8);
//Page number
if($this->isFinished){
$this->Cell(173,10, ' FOOTER TEST - {nb}', 0, 0);
}
}
and then in your PDF writing code
...
$pdf->isFinished = true;
Hope this helps
I'm late to the party too - LOL Paul is on the right track. I amended his solution to work with a while loop.
$pdf = new PDF();
$pdf->isFinished = false;
$pdf->AddPage();
// Add your page contents here
while($row1 = $rs1->fetch_assoc()){
// output...
$pdf->isFinished = false;
}
$pdf->isFinished = true;
Now In the Footer...
function Footer()
{
$this->SetY(-15);
if($this->isFinished){
$this->Cell(195,10, "Signature:_______________________________", 0, 0,'R');
}
}
I hope this helps someone.
If you use MultiCell() and AutoPageBreak, the content you're writing may create an automatic page break.
In this case, setting a boolean flag before a MultiCell() call can create the last footer expected on the last two pages instead of the very last one only.
At the end of your code, when you don't have anything else to write in your document, do the following :
$this->InFooter = true;
$this->SetY(-15);
$this->Write(5, "My end footer text");
$this->InFooter = false;
InFooter boolean tells to FPDF that we are writing content in the Footer and FPDF will not automatically create a page break.
SetY() is needed otherwise your content will be written outside the document.
I know i'm a little late to the party here but when I ran into this same problem it was because I was not calling the AliasNbPages() function. The example they give doesn't seem to include this in their code !
I just call it right after I make a new pdf:
$pdf = new PDF();
$pdf->AliasNbPages();
Now my {nb} is replaced with the total number of pages !
For people stumbling upon this question many years later: Danny's answer above can also be used to skip the footer (or header) on any arbitrary page. You just need to make sure that you set your skip-Boolean to true after adding the page, then add the content, and set it back to false in case you want a footer on the next pages.
$pdf = new PDF();
$pdf->isFinished = false;
// [...]
$pdf->AddPage();
$pdf->isFinished = true;
// Add your page contents here
$pdf->isFinished = false;
I posted a longer version here.
I would have liked to add this as a comment to Danny's answer. However, being new and low on reputation, I had to post my own. :/
Your Main class:
$o_pdf->AddPage(); //need to add first and set isFinished true
$o_pdf->isFinished = true;
And your FPDF extended class:
if($this->isFinished) {
//your footer on last page
}
Hope this will be useful for you!!!
I am writing a simple php crawler that gets data from a website and inserts it into my database. I start with a predefined url. Then I go through the the contents of the page (from php's file_get_contents) and eventually use file_get_contents on links of that page. The url's I am getting from the links are fine when I echo them and then open them from my browser on their own. However, when I use file_get_contents and then echo the result, the page does not appear correctly because of errors related to dynamically created server-side data from the site. The echo'd page contents do not include the listed data from the server that I need, because it cannot find necessary resources for the site.
It appears relative paths in the echo'd webpage are not allowing the desired content to be generated.
Can anyone point me in the right direction here?
Any help is appreciated!
Here is some of my code so far:
function crawl_all($url)
{
$main_page = file_get_contents($url);
while(strpos($main_page, '"fl"') > 0)
{
$subj_start = strpos($main_page, '"fl"'); // get start of subject row
$main_page = substr($main_page, $subj_start); // cut off everything before subject row
$link_start = strpos($main_page, 'href') + 6; // get the start of the subject link
$main_page = substr($main_page, $link_start); // cut off everything before subject link
$link_end = strpos($main_page, '">') - 1; // get the end of the subject link
$link_length = $link_end + 1;
$link = substr($main_page, 0, $link_length); // get the subject link
crawl_courses('https://whatever.com' . $link);
}
}
/* Crawls all the courses for a subject. */
function crawl_courses($url)
{
$subj_page = file_get_contents($url);
echo $url; // website looks fine when in opened in browser
echo $subj_page; // when echo'd, the page does not contain most of the server-side generated data i need
while(strpos($subj_page, '<td><a href') > 0)
{
$course_start = strpos($subj_page, '<td><a href');
$subj_page = substr($subj_page, $course_start);
$link_start = strpos($subj_page, 'href') + 6;
$subj_page = substr($subj_page, $link_start);
$link_end = strpos($subj_page, '">') - 1;
$link_length = $link_end + 1;
$link = substr($subj_page, 0, $link_length);
//crawl_professors('https://whatever.com' . $link);
}
}
Try advance html dom parser. It is here....
http://sourceforge.net/projects/advancedhtmldom/
I have a simple (excercise) website whose structure consist of a content selection menu and a content panel, where content is displayed when one the elements of the menu is selected.
The menu use links of the type ?content_id=... to pass the information over.
I would like to handle 404 codes on inexistent ids or missing contents but to do so I have to compute the content of the content panel befor the page loads, so that I can output the correct header before everything else. I ended up writing this piece of code at the beginning of my file and printing the $content variable when necessary. Anyway, I feel quiet uncomfortable with this approach since I have to stop the natural flow to catch the include output.
<?php
// if no content is specified in the address, select the home page (0)
$content_id = (isset($_GET['content_id'])) ? $_GET['content_id'] : 0;
//check if selected content exists and put the content inside the $content variable (to be used later)
if ($content_id < 0 OR $content_id > 2 OR !is_file('exContent/content'.$content_id.'.php')) {
header("HTTP/1.0 404 Not Found");
ob_start();
include 'exContent/noContent.php';
}
else {
ob_start();
include 'exContent/content'.$content_id.'.php';
}
$content = ob_get_contents(); ob_end_clean();
?>
I cannot figure out what's the best way to do this, I would be very happy if you can help me!
Could be a lot better but this is a start :)
$includeFile 'exContent/content'.$content_id.'.php';
if ($content_id < 0 OR $content_id > 2 OR !is_file( $includeFile )) {
header("HTTP/1.0 404 Not Found");
$includeFile = 'exContent/noContent.php';
}
ob_start()
include $includeFile;
$content = ob_get_contents();
ob_end_clean();
I have this project i'm working on and id like to add a really small list of nearby places using facebooks places in an iframe featured from touch.facebook.com I can easily just use touch.facebook.com/#/places_friends.php but then that loads the headers the and the other navigation bars for like messges, events ect bars and i just want the content.
I'm pretty sure from looking at the touch.facebook.com/#/places_friends.php source, all i need to load is the div "content" Anyway, i'm extremely new to php and im pretty sure what i think i'm trying to do is called web scraping.
For the sake of figuring things out on stackoverflow and not needing to worry about authentication or anything yet i want to load the login page to see if i can at least get the scraper to work. Once I have a working scraping code i'm pretty sure i can handle the rest. It has load everything inside the div. I've seen this done before so i know it is possible. and it will look exactly like what you see when you try to login at touch.facebook.com but without the blue facebook logo up top and thats what im trying to accomplish right here.
So here's the login page, im trying to load the div which contains the text boxes to login the actual login button. If it's done correctly we should just see those with no blur Facebook header bar above it.
I've tried
<?php
$page = file_get_contents('http://touch.facebook.com/login.php');
$doc = new DOMDocument();
$doc->loadHTML($page);
$divs = $doc->getElementsByTagName('div');
foreach($divs as $div) {
if ($div->getAttribute('id') === 'login_form') {
echo $div->nodeValue;
}
}
?>
all that does is load a blank page.
I've also tried using http://simplehtmldom.sourceforge.net/
and i modified the example basic selector to
<?php
include('../simple_html_dom.php');
$html = file_get_html('http://touch.facebook.com/login.php');
foreach($html->find('div#login_form') as $e)
echo $e->nodeValue;
?>
I've also tried
<?php
$stream = "http://touch.facebook.com/login.php";
$cnt = simplexml_load_file($stream);
$result = $cnt->xpath("/html/body/div[#id=login_form]");
for($i = 0; $i < $i < count($result); $i++){
echo $result[$i];
}
?>
that did not work either
$stream = "http://touch.facebook.com";
$cnt = simplexml_load_file($stream);
$result = $nct->xpath("/html/body/div[#id=content]");
for ($i = 0; $i < count($result); $i++){
echo $result[$i];
}
there was a syntax error in this line i removed it now just copy and paste and run this code
Im assuming that you can't use the facebook API, if you can, then I strongly suggest you use it, because you will save yourself from the whole scraping deal.
To scrape text the best tech is using xpath, if the html returned by touch.facebook.com is xhtml transitional, which it sould, the you should use xpath, a sample should look like this:
$stream = "http://touch.facebook.com";
$cnt = simplexml_load_file($stream);
$result = $nct->xpath("/html/body/div[#id=content]");
for ($i = 0; $i < $i < count($result); $i++){
echo $result[$i];
}
You need to learn about your comparison operators
=== is for comparing strictly, you should be using ==
if ($div->getAttribute('id') == 'login_form')
{
}
Scraping isn't always the best idea for capturing data else where. I would suggest using Facebook's API to retrieve the values your needing. Scraping will break any time Facebook decides to change their markup.
http://developers.facebook.com/docs/api
http://github.com/facebook/php-sdk/