Dynamic text on TCPDF-Laravel footer - php

First of all I'm 90% sure this is me not knowing some basic stuff about how php static methods work, so please forgive my ignorance. I also could not find any info on this on SO, so hopefully this won't be a duplicate.
I'm working on a project with laravel and using the TCPDF-Laravel extension, and I have a footer that needs to say different things based on a condidition:
if ($someCondition) {
$footerText = <<<EOD
text if true
EOD;
} else {
$footerText = <<<EOD
text if false
EOD;
}
PDF::setFooterCallback(function($pdf,$footerText){
$pdf->SetY(-15);
$pdf->Write(0, $footerText, '', 0, 'L', true, 0, false, false, 0);
});
But for some reason, again probably my ignorance, this doesn't run. It says 'too few arguments given', as if $footerText wasn't defined or was null.
I have already tried declaring the variable before the if bracket, declaring the function outside the setFooterCallBack, and using $this->footerText but it didn't work.

Ok to make this simple.
PDF::setFooterCallback() requires a callable as a first argument. That's where you pass in function($pdf) {}. In order to expand the functions scope for a variable like $footerText, you have to use use to inherit it like so: function($pdf) use($footerText).
The reason why you get this error is because you set $footerText as a second argument after $pdf, which setFooterCallback does not know of and therefor, it's not set and calls a too few arguments exception which is normal.
// outputs "Howdy'ho!"
(function($what) {
echo $what;
})("Howdy'ho!");
// will throw an exception `too few arguments`
(function($what) {
echo $what;
})();
// will output null, because it can be called without an argument.
(function($what = null) {
echo $what;
})();
So theoretically, setting $footerText to a default null would work, but this is of course not what you intended to do.
PDF::setFooterCallback(function($pdf, $footerText = null){
$pdf->SetY(-15);
$pdf->Write(0, $footerText, '', 0, 'L', true, 0, false, false, 0);
});
Just inherit the variable $footerText using use.
PDF::setFooterCallback(function($pdf) use(footerText) {
$pdf->SetY(-15);
$pdf->Write(0, $footerText, '', 0, 'L', true, 0, false, false, 0);
});

Related

Send dynamic data to Header function in TCPDF

As I need to get some dynamic content to my page header. So, let me know the way to send data through parameters. I have not found how to call, send parameters to the header function. Please help me to solve this..
How can I call Header() function with parameters?
I need to send data through parameters to Header() function.
This can be accomplished by setting a new property of the TCPDF class. The property will need to be set before the AddPage() method is called for the next page. Before creating a new property you may want to check the TCPDF documentation for an existing property that may be useful. Searching “get” will allow you to quickly find them.
Be careful to give the new property a unique name, so you don’t change an existing property of TCPDF. You may want to include a check for the property in case one were to be added in a future version.
Setting a parameter of the Header() method is more difficult because it is called through a series of other methods (AddPage(), startPage(), setHeader()).
Example
This example sets a new string for each page header with the new CustomHeaderText property. The example will run inside the TCPDF examples directory.
<?php
require_once('tcpdf_include.php');
class MYPDF extends TCPDF
{
public function Header()
{
$this->Write(0, $this->CustomHeaderText);
}
}
$pdf = new MYPDF();
$pdf->CustomHeaderText = "Header Page 1";
$pdf->AddPage();
$pdf->writeHTMLCell(0, 0, '', 30, '<p>Page 1 Content</p>', 0, 1, 0, true, '', true);
$pdf->CustomHeaderText = "Header Page 2";
$pdf->AddPage();
$pdf->writeHTMLCell(0, 0, '', 30, '<p>Page 2 Content</p>', 0, 1, 0, true, '', true);
$pdf->Output('example.pdf', 'I');
You can do this by adding a new property in your extended class MYPDF in this example
<?php
require 'tcpdf.php';
class MYPDF extends TCPDF {
protected $company;
public function setCompany($var){
$this->company = $var;
}
// Page footer
public function Footer() {
// Position at 15 mm from bottom
$this->SetY(-15);
// Set font
$this->SetFont('helvetica', 'I', 8);
// setCompany Text
$this->Cell(0, 10, $this->company, 0, false, 'C', 0, '', 0, false, 'T', 'M');
}
}
To access this
// create new PDF document
$pdf = new MYPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
$pdf->setCompany("My Company");

What is the meaning of $channel->wait() in RabbitMQ

I am quite new in RabbitMQ. I'm working with php-amqplib library with codeigniter, and still wondering about some knowledge which I am lacking.
Why $channel->wait() is used?
Why it is always reside inside an endless while loop?
How to/Can I bypass the Infinite while loop.
Like In a situation when one user of my project wants to broadcast new campaign to 100k leads, the second user gets effected if he has some 100 mails to be sent, The second has to wait for 100k mails to get delivered first then the last user gets his turn.
I need a solution for Concurrent Consumers, who works smoothly without affecting the other
Here is my code snippet:
public function campaign2(){
$this->load->library('mylibrary');
for( $i=1;$i<=5;$i++ ) {
$url = "http://localhost/myproject/rabbit/waiting";
$param = array('index' => $i);
$this->waiting($i);
}
}
public function waiting($i)
{
ini_set('memory_limit','400M');
ini_set('max_execution_time', 0);
ini_set('display_errors', 1);
${'conn_'.$i} = connectRabbit();
${'channel_'.$i} = ${'conn_'.$i}->channel();
${'channel_'.$i}->exchange_declare('ha-local-campaign-'.$i.'-exchange', 'fanout', false, true, false);
$q = populateQueueName('campaign-'.$i);
${'channel_'.$i}->queue_declare($q, false, true, false, false);
${'channel_'.$i}->queue_bind($q, 'ha-local-campaign-'.$i.'-exchange', 'priority.'.$i);
$consumer_tag = 'campaign_consumer' ;
function process_message($msg) {
echo 'Mail Sent';
$msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']);
}
function shutdown($channel, $conn){
echo '['.date('H:i:s').'] Campaign consumer - Shutdown!!';
}
${'channel_'.$i}->basic_consume($q, $consumer_tag, false, false, true, false,'process_message');
while(1) {
${'channel_'.$i}->wait();
}
register_shutdown_function('shutdown', ${'channel_'.$i}, ${'conn_'.$i});
}
If anyone kindly guide me through the process I will be grateful.
When you call $channel->wait() you are:
Inspecting the channel's queues to see if there are pending messages.
For each message you are going to call the registered callback for the corresponding channel's callback.
From the "hello world example", step by step::
// First, you define `$callback` as a function receiving
// one parameter (the _message_).
$callback = function($msg) {
echo " [x] Received ", $msg->body, "\n";
};
// Then, you assign `$callback` the the "hello" queue.
$channel->basic_consume('hello', '', false, true, false, false, $callback);
// Finally: While I have any callbacks defined for the channel,
while(count($channel->callbacks)) {
// inspect the queue and call the corresponding callbacks
//passing the message as a parameter
$channel->wait();
}
// This is an infinite loop: if there are any callbacks,
// it'll run forever unless you interrupt script's execution.
Have your second user send use a different queue. You can have as many queues as you want.

how TCPDF prevent the extra blank page

I have create class to make page by using TCPDF.
I need to convert HTML to pdf, so I using writeHTML and AcceptPageBreak().
The $html is Dynamically changed, could be very long.
class MY_TCPDF extends TCPDF{
public function makePage($html){
$head_image="header.jpg";
$this->SetMargins(PDF_MARGIN_LEFT, 70, PDF_MARGIN_RIGHT);
$this->setPrintHeader(false);
$this->AddPage();
// get the current page break margin
$bMargin = $this->getBreakMargin();
// get current auto-page-break mode
$auto_page_break = $this->getAutoPageBreak();
// disable auto-page-break
$this->SetAutoPageBreak(false, 0);
// set bacground image
$img_file = $head_image;
$this->Image($img_file, 0, 0, 210, 68, '', '', '', false, 300, '', false, false, 0);
// restore auto-page-break status
//$this->SetAutoPageBreak($auto_page_break, PDF_MARGIN_BOTTOM);
// set the starting point for the page content
$this->setPageMark();
$this->writeHTML($html, true, false, true, false, '');
$this->lastPage();
ob_start();
//Close and output PDF document
$this->Output('my.pdf', 'I');
ob_end_flush();
}
public function AcceptPageBreak() {
$this->SetMargins(PDF_MARGIN_LEFT, 10, PDF_MARGIN_RIGHT);
$this->AddPage();
return false;
}
}
The problem is I genenrate PDF, but alway has a extra blank page in the end of the PDF.
I tried use $this->delete($this->getPage()) ,but it only remove last page which has content and the extra blank page remain. this seems writeHTML will create a page break after it.
how to prevent this extra blank page?
Try this deletePage function
$lastPage = $this->getPage();
$this->deletePage($lastPage);
Instead Delete use deletePage
I had the same Problem:
I fixed it with:
class TCPDFextended extends \TCPDF {
public function Output($name = 'doc.pdf', $dest = 'I')
{
$this->tcpdflink = false;
return parent::Output($name, $dest);
}
}
You should check your $html variable.
1) If it could contains any <html />, <head />, <title />, <body /> tag then please remove them and just take html contents after and before <body />.
2) You should avoid any css, js link file within $html content.
3) Finally you should use $html=utf8_encode($html); just before $this->writeHTML($html, true, false, true, false, '');.
4) You may need to adjust your MARGIN_LEFT, MARGIN_TOP, MARGIN_RIGHT and MARGIN_BOTTOM to solve such problems. Please check $this->SetMargins(PDF_MARGIN_LEFT, PDF_MARGIN_TOP, PDF_MARGIN_RIGHT); and $this->SetAutoPageBreak(TRUE, PDF_MARGIN_BOTTOM);.
Hopefully it can solve your problem.
My answer is similar to #kanti. I think we can set the default to false even before the Output generation.
Background. The extra page we see is basically
"If true print TCPDF meta link".
so by default the TCPDF::$tcpdflink = true , is set true.
All we need is
class My_PDF extends TCPDF {
public function changeTheDefault($tcpdflink) {
$this->tcpdflink = $tcpdflink;
}
}
call your public function later when you need it. ...
$get_pdf = new My_PDF (your_parameters);
$get_pdf->changeTheDefault(false); # changes the default to false
Good Luck.
Check also the height of your enclosing div.
It should not be 100%.
Try to remove any height property from the CSS style of the enclosing div ( I mean the div which encloses all the content).
The problem is the 4th parameter (unicode = true) in your create_pdf.php file. This parameter is passed into tcpdf.php on line 1838
$pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'ISO-8859-1', false);
change it to false.

symfony pass resource identifier to template from action

my action:
public function executePreview(sfWebRequest $request)
{
$this->setLayout('layout_preview');
$text='';
$text= $request->getGetParameter('text');
$this->img='';
$this->img2=$this->createImage(array('font_size'=>10, 'line_spacing'=>1.5,'font'=>'dejavu', 'angle'=>10,
'preset'=>'BRCA', 'text'=>$text));
}
public function createImage( $params)
{
$x=0;
$y=0;
$interval= $params['font_size']*$params['line_spacing'];
$src_image= imagecreatefromjpeg(sfConfig::get('sf_web_dir').'/images/'.$params['preset'].'.jpg');
$black = imagecolorallocate($src_image, 0, 0, 0);
$lines=explode("\n", $params['text']);
putenv('GDFONTPATH='.join(':',sfConfig::get('app_font_path')));
$fontname=$params['font'].'.ttf';
foreach($lines as $i=>$line):
imagettftext($src_image, $params['font_size'], $params['angle'], $x, $y+$i*$interval, $black, $fontname, $line);
endforeach;
return $src_image;
}
in my template:
<?php
imagejpeg($img2);
?>
but when trying to GET /modulename/preview?preview=something&text=somethingelse, it gives an error. viewing the source of the webpage obtained, I see :
<title>InvalidArgumentException: Unable to escape value "NULL".</title>
Can I not pass resource identifier to the template? What can be a way around that? I need this createImage function elsewhere also, I'm just trying to follow DRY
imagejpeg creates the image and returns a boolean, i don't get why you call it in your view, it should stay to your action, actually i wouldn't use view at all in your case.
try something like :
$this->getResponse()->setHttpHeader('Content-type', 'image/jpeg');
$this->getResponse()->setContent($src_image);
$this->setLayout(false);
or directly setting header with PHP and returning sfView::HEADER_ONLY

HTML Purifier: Converting <body> to <div>

Premise
I'd like to use HTML Purifier to transform <body> tags to <div> tags, to preserve inline styling on the <body> element, e.g. <body style="background:color#000000;">Hi there.</body> would turn to <div style="background:color#000000;">Hi there.</div>. I'm looking at a combination of a custom tag and a TagTransform class.
Current setup
In my configuration section, I'm currently doing this:
$htmlDef = $this->configuration->getHTMLDefinition(true);
// defining the element to avoid triggering 'Element 'body' is not supported'
$bodyElem = $htmlDef->addElement('body', 'Block', 'Flow', 'Core');
$bodyElem->excludes = array('body' => true);
// add the transformation rule
$htmlDef->info_tag_transform['body'] = new HTMLPurifier_TagTransform_Simple('div');
...as well as allowing <body> and its style (and class, and id) attribute via the configuration directives (they're part of a working, large list that's parsed into HTML.AllowedElements and HTML.AllowedAttributes).
I've turned definition caching off.
$config->set('Cache.DefinitionImpl', null);
Unfortunately, in this setup, it seems like HTMLPurifier_TagTransform_Simple never has its transform() method called.
HTML.Parent?
I presume the culprit is my HTML.Parent, which is set to 'div' since, quite naturally, <div> does not allow a child <body> element. However, setting HTML.Parent to 'html' nets me:
ErrorException: Cannot use unrecognized element as parent
Adding...
$htmlElem = $htmlDef->addElement('html', 'Block', 'Flow', 'Core');
$htmlElem->excludes = array('html' => true);
...gets rid of that error message but still doesn't transform the tag - it's removed instead.
Adding...
$htmlElem = $htmlDef->addElement('html', 'Block', 'Custom: head?, body', 'Core');
$htmlElem->excludes = array('html' => true);
...also does nothing, because it nets me an error message:
ErrorException: Trying to get property of non-object
[...]/library/HTMLPurifier/Strategy/FixNesting.php:237
[...]/library/HTMLPurifier/Strategy/Composite.php:18
[...]/library/HTMLPurifier.php:181
[...]
I'm still tweaking around with the last option now, trying to figure out the exact syntax I need to provide, but if someone knows how to help me based on their own past experience, I'd appreciate any pointers in the right direction.
HTML.TidyLevel?
As the only other culprit I can imagine it being, my HTML.TidyLevel is set to 'heavy'. I've yet to try all possible constellations on this, but so far, this is making no difference.
(Since I've only been touching this secondarily, I struggle to recall which constellations I've already tried, lest I would list them here, but as it is I lack confidence I wouldn't miss something I've done or misreport something. I might edit this section later when I've done some dedicated testing, though!)
Full Configuration
My configuration data is stored in JSON and then parsed into HTML Purifier. Here's the file:
{
"CSS" : {
"MaxImgLength" : "800px"
},
"Core" : {
"CollectErrors" : true,
"HiddenElements" : {
"script" : true,
"style" : true,
"iframe" : true,
"noframes" : true
},
"RemoveInvalidImg" : false
},
"Filter" : {
"ExtractStyleBlocks" : true
},
"HTML" : {
"MaxImgLength" : 800,
"TidyLevel" : "heavy",
"Doctype" : "XHTML 1.0 Transitional",
"Parent" : "html"
},
"Output" : {
"TidyFormat" : true
},
"Test" : {
"ForceNoIconv" : true
},
"URI" : {
"AllowedSchemes" : {
"http" : true,
"https" : true,
"mailto" : true,
"ftp" : true
},
"DisableExternalResources" : true
}
}
(URI.Base, URI.Munge and Cache.SerializerPath are also set, but I've removed them in this paste. Also, HTML.Parent caveat: As mentioned, usually, this is set to 'div'.)
This code is the reason why what you're doing doesn't work:
/**
* Takes a string of HTML (fragment or document) and returns the content
* #todo Consider making protected
*/
public function extractBody($html) {
$matches = array();
$result = preg_match('!<body[^>]*>(.*)</body>!is', $html, $matches);
if ($result) {
return $matches[1];
} else {
return $html;
}
}
You can turn it off using %Core.ConvertDocumentToFragment as false; if the rest of your code is bugfree, it should work straight from there. I don't believe your bodyElem definition is necessary.j
Wouldn't it be much easier to do:
$search = array('<body', 'body>');
$replace = array('<div', 'div>');
$html = '<body style="background:color#000000;">Hi there.</body>';
echo str_replace($search, $replace, $html);
>> '<div style="background:color#000000;">Hi there.</div>';

Categories