I am building an Angular 4 app with PHP API. Within the app users are able generate some sort of "magazine". This allows them to sort pages, edit content and add images but not in a WYSIWYG way, rather in a step by step "this option I selected is what I want" way.
So I end up with a lot of data stored in a MySQL database kind of "describing" how the final PDF should look like.
The problem is I have absolutely no idea how to generate a PDF. I know there are things like pdfmake or jsPDF as client side solutions or tcpdf (which seems to be in version transition for ever?!) as server side solution. But all of these are limited.
I think the best solution would be to generate some LaTeX code and generating some PDF out of it, due to the ability to use the variety of LaTeX commands instead of the limited commands for jsPDF or pdfmake.
Is there any kind of standard or best way to manage compiling LaTeX code using angular?
Which way to go? Server side or client side? The LaTeX and PDFs which are going to be created contain lots of images, and about 100-200 pages ...
For everyone else searching
CLSI seems to be a way of managing it. There is an still being maintained open source api to compile LaTeX files: CLSI ShareLaTeX
Thanks to mike42
Another very interesting example of compiling LaTeX with PHP ... which actually is the way to go in my case ... is to generate a .tex file which is a valid LaTeX file and a valid PHP file at once so the code ends up something like this:
% This file is a valid PHP file and also a valid LaTeX file
% When processed with LaTeX, it will generate a blank template
% Loading with PHP will fill it with details
\documentclass{article}
% Required for proper escaping
\usepackage{textcomp} % Symbols
\usepackage[T1]{fontenc} % Input format
% Because Unicode etc.
\usepackage{fontspec} % For loading fonts
\setmainfont{Liberation Serif} % Has a lot more symbols than Computer Modern
% Make placeholders visible
\newcommand{\placeholder}[1]{\textbf{$<$ #1 $>$}}
% Defaults for each variable
\newcommand{\test}{\placeholder{Data here}}
% Fill in
% <?php echo "\n" . "\\renewcommand{\\test}{" . LatexTemplate::escape($data['test']) . "}\n"; ?>
\begin{document}
\section{Data From PHP}
\test{}
\end{document}
If PHP safe mode is disabled and the server has xelatex / pdflatex installed execute the command on a file directly ...
First the filled-in LaTeX code needs to be stored in a temporary file by doing something like this:
/**
* Generate a PDF file using xelatex and pass it to the user
*/
public static function download($data, $template_file, $outp_file) {
// Pre-flight checks
if(!file_exists($template_file)) {
throw new Exception("Could not open template");
}
if(($f = tempnam(sys_get_temp_dir(), 'tex-')) === false) {
throw new Exception("Failed to create temporary file");
}
$tex_f = $f . ".tex";
$aux_f = $f . ".aux";
$log_f = $f . ".log";
$pdf_f = $f . ".pdf";
// Perform substitution of variables
ob_start();
include($template_file);
file_put_contents($tex_f, ob_get_clean());
}
After that the engine of choice should be executed to generate the output files:
// Run xelatex (Used because of native unicode and TTF font support)
$cmd = sprintf("xelatex -interaction nonstopmode -halt-on-error %s",
escapeshellarg($tex_f));
chdir(sys_get_temp_dir());
exec($cmd, $foo, $ret);
// No need for these files anymore
#unlink($tex_f);
#unlink($aux_f);
#unlink($log_f);
// Test here
if(!file_exists($pdf_f)) {
#unlink($f);
throw new Exception("Output was not generated and latex returned: $ret.");
}
Related
So my problems stems from trying to generate large PDF files but being hit by the memory limit / execution timeouts in PHP, the data is too great in volume to simply extend these limits so that solution is out of the question.
I have a background shell task running which handles all of this rendering and then alerts the user once the PDF has been completed.
In theory I would have a loop within this shell which would take in a chunk of data and render it to file, then take the next chunk and do the same. Once out of data to render, the file would then be written and completed ready to be served. This way the memory limit of PHP would not be hit as a manageable chunk will only ever be loaded.
I am currently using the CakePDF(v.3.5) plugin for CakePHP 3 (v.3.5.13) but am sturggling to find a solution which allows the rendering of some data and then adding more data to the same pdf.
Has anyone managed this before or is it out of scope of the plugin? Would another solution be to create multiple PDF files and then merge them together after all separate PDF's have been created?
This is more of a theoretical question if this would work and if anyone has managed it before. I don't have much code to show but if more detail is required then give me a shout and I will try and get something for you or some example code!
Thanks
I don't have direct experience with that version CakePdf, but under CakePHP 2.x I use the wkhtmltopdf engine which takes an .html output to produce the PDF.
If your shell generates such .html in chunks, it is easy to append.
Of course wkhtmltopdf is likely to put some load on the machine to produce the PDF, but since it's a binary, it happens outside of PHP's memory/time contraints.
That's certainly out of the scope of the plugin, it's built around the idea of rendering a single view to a single file, the interface doesn't support chunked creation of a single file, and if I'm not mistaken, none of the supported engines do support that either, at least not in a straightforward and efficient manner when it comes to large documents.
There's certainly lots of ways to do this, creating multiple PDFs and merging/concatenating them afterwards might be one of them, generating the source content in chunks, and passing it to a PDF renderer that can handle lots of content efficiently might be another one, and surely there also might be libraries out there that do explicitly support chunked creation of PDFs...
I thought I would post what I ended up doing for anyone in the future.
I used CakePDF to generate smaller PDF's which I stored in a tmp directory these are all under the limit of PHP's execution time and memory limits as I don't believe altering those provides a good solution. In this step I also saved the names of all of the PDF's generated for use in the next step.
The code for this looked something like:
while (!is_last_pdf) {
// Generate pdf in here with a portion of the data
$CakePdf = new CakePdf();
$CakePdf->template('page', 'default');
$CakePdf->viewVars(compact('data', 'other_stuff'));
// Save file name to array
$tmp_file_list[] = $file_name;
// Update the is_last_pdf variable
is_last_pdf = check_for_more_data();
}
From this I used GhostScript from within the Shell to merge all of the PDF files, the code for this looked something like this:
$output_path = 'output.pdf';
$file_list = '';
// Create a string of all the files to merge
foreach ($tmp_file_list as $file) {
$file_list .= $file . ' ';
}
// Execute GhostScript to merge all the files into the `output.pdf` file
exec('gs -dBATCH -dNOPAUSE -sDEVICE=pdfwrite -sOUTPUTFILE=' . $output_path . ' ' . $file_list);
All of the code here was in the Shell file responsible for creating the PDF.
Hope this helps someone :)
I have a function that uploads a file into a web storage and prior to saving the file on the storage system if the file is a pdf file i would like to determine how many pages a pdf file has.
Currently i have the following:
$pdftext = file_get_contents($path);
$num = preg_match_all("/\/Page\W/", $pdftext, $dummy);
return $num;
Where $path is the temporary path that i use with fopen to open the document
This function works at times but is not reliable. I know theres also this function
exec('/usr/bin/pdfinfo '.$pdf_file.' | awk \'/Pages/ {print $2}\'', $output);
But this requires the file to donwloaded on the server. Any ideas or suggestions to accomplish this?
PHP is a server-side language, meaning all processing happens on your server. There's no way for PHP to determine details of a file on the client side, it has no knowledge of it neither the required access to it.
So the answer to your question as it is now is: It's not possible. But you probably have a goal in mind why you want to check this, sharing this goal might help to get more constructive answers/suggestions.
As Oldskool already explained this is not possible with PHP on the client side. You would have to upload the PDF file to the server and then determine the amount of pages. There are libraries and command line tools that could accomplish this.
In case you don't want to upload the PDF file to the server (which seems to be the case here) you could use the pdf.js library. Now the client is able to determine the amount of pages in a PDF document on its own.
PDFJS.getDocument(data).then(function (doc) {
var numPages = doc.numPages;
}
There are other libraries as well but I'm not certain about their browser support (http://www.electronmedia.in/wp/pdf-page-count-javascript/)
Now you just submit the amount of pages from javascript to your php file that needs this information. In order to achive this you simply use ajax. In case you don't know ajax, just google it there are enough examples out there.
As a side note; Always remember to not trust the client. The client is able to modify the page count and send a completely different one.
For those of you running linux servers this actually is possible. You need the pdfinfo extension installed and using the function
$pages = exec('/usr/bin/pdfinfo '.$pdf_file.' | awk \'/Pages/ {print $2}\'', $output);
outputs the correct page number where $pdf_file is the temporary path on the server upon upload.
The reason it wasnt working for me was because i didnt have the PDFinfo installed.
I have been given an SWF to edit a link in the AS code.
The fact is the SWF uses some XML that is generated (actually retrieved) by PHP code from a database.
menuXML.load("/sub/page/dynamic.php?genre=" + genre);
so the point is we can use the same SWF 'mainfraim' and fill them with different animations/sources based on the link provided in dynamic.php?genre=###
Now, I've used Flash Decompiler Gold to extract all files in the SWF and can open it again in Adobe Flash to edit it. When done I enter CTRL+ENTER and there are immediately 4 compiler errors!! Errors:
1x < Unexpected 'if' encountered >
2x < Statement block must be terminated by '}' >
1x < Ecpected a field name after '.' operator. >
How can these errors be present, when the original SWF works perfectly??!
If I don't manage to solve this, I'll have to find out how to create an .php file the SWF tries to use which can select the proper resources (from a database I guess) to show them (using ?genre=###)
Thanks!
I'm not sure I understand your problem, but it seems like you need to change the url passed to the load method. It also seems like your swf is Actionscript 2.0.
Decompilers sort of work, but the fla file you can generate with a decompiler will seldom be useful to generate the same swf back. Sometimes the code is illegal, and almost always the graphics are screwed.
I once had to make some simple code changes (like changing a few urls and other simple stuff) to a swf, for which we had no sources (they were lost and there was no backup...).
I used flasm for this and it worked fine (also it wasn't as hard as I first supposed).
Flasm is not a decompiler, but a disassembler. It takes your swf, parses the actionscript bytecode and generates a text file with assembly-like code. You can edit that code and re-assemble the swf. It doesn't touch graphics and animations, so it was what I needed, and perhaps could work for you.
I've made a little test and it worked fine.
I started with this code in a fla:
var xml:XML = new XML();
xml.ignoreWhite = true;
xml.onLoad = function(ok:Boolean):Void {
if(ok) {
_debug_txt.text = "ok";
_debug_txt.text = xml;
} else {
_debug_txt.text = "error";
}
};
xml.load("/sub/page/dynamic.php");
Next, I opened a cmd prompt (I'm on Windows), cd to the directory that contains the swf and run:
flasm -d test_flasm.swf > test_flasm.flm
This disassembles the swf into a text file test_flasm.flm. I have added flasm to my executables path, but you can just use the full path to the flasm.exe instead.
The relevant part of the .flm file looks like this:
setMember
push '/sub/page/dynamic.php', 1, 'xml'
getVariable
push 'load'
callMethod
Yours may vary, but if you look for the url, you'll find it. Next, changed that url to:
setMember
push 'test.xml', 1, 'xml'
getVariable
push 'load'
callMethod
Then, I reassembled the swf using:
flasm -a test_flasm.flm
And now, test_flasm.swf loads "test.xml" instead of "/sub/page/dynamic.php".
Hope this helps.
I have an ebook in word that I convert to PDF before distributing to my clients. I'd like to dynamically insert their email address into all links in the ebook to allow them access to the members-only content on my site, and I'd like to do this on the fly, as part of the book download process.
I've briefly looked at http://us.php.net/pdf and FPDF, but I was wondering what specific technique I'd use to insert this data.
I was thinking I'd insert an email token string where I want the email address to go, and then use some function to update those tokens in the PDF document.
Can anyone point me in the right direction? I have php experience, but not with editing / generating pdf documents from php.
EDIT: Yes, this commercial script http://www.setasign.de/products/pdf-php-solutions/setapdf-linkreplacer/ does exactly what I needed.
So far it's looking like this is my best bet:
http://www.setasign.de/products/pdf-php-solutions/setapdf-linkreplacer/
Trying an eval copy of it, will update post with results.
you can do this with FPDI extension for FPDF http://www.setasign.de/products/pdf-php-solutions/fpdi/
it enables fpdf to import existing pdf files, though I'm not sure how can one replace links.
I'd say your best shot would be to generate the whole thing in php, or just save it in html, replace links in html, then convert html to pdf.
Without using the Adobe LiveCycle Designer, the easiest way to generate a custom PDF is to use an FDF file. There are tons of ways to do this, one of which is to download binaries from Adobe and install them on your server. But none of that is really needed. All you need is a pdf with fillable forms and a simple script that makes and FDF file. The FDF simply holds the data that needs to be filled in and a pointer to the pdf file to be filled in. I use this for our timesheets at work. The data goes into a web form, but must come out static and ugly and using a paper from from 30 years ago. Here's what your fdf file will look like (both with code and raw):
$file = "http://www.example.com/blankpdfform.pdf";
$data = "%FDF-1.2\n%âãÏÓ\n1 0 obj\n<< \n/FDF << /Fields [ ";
foreach($datafields as $field => $val) {
$data.='<< /T ('.$field.') /V ('.trim($val).')>> ';
}
$data.="] \n/F (".$file.") /ID [ <".md5(time()).">\n] >>".
" \n>> \nendobj\ntrailer\n".
"<<\n/Root 1 0 R \n\n>>\n%%EOF\n";
The end result being:
%FDF-1.2\n%âãÏÓ\n1 0 obj\n<< \n/FDF << /Fields [<< /T (email) /V (email#address.com)>>
/F ("http://www.example.com/blankpdfform.pdf") /ID [ <"SomeUniqueID">
] >> \nendobj\ntrailer<<
/Root 1 0 R
>>
%%EOF
If you have the PDF template with the email token stored in a file on the webserver, you can do this fairly easily. First you need to read in the file using PHP. You can do this using the file_get_contents method. Then use str_replace to replace the email token with the actual email. Finally, serve the file with the correct content-type.
$pdf = file_get_contents( 'template.pdf' );
$pdf = str_replace( '__EMAIL__TEMPLATE__', $userEmail, $pdf );
header( 'Content-type: application/pdf' );
print $pdf;
Doc links:
http://ca2.php.net/manual/en/function.file-get-contents.php
http://ca2.php.net/manual/en/function.str-replace.php
(I haven't actually tried this and you may run into some issues since PDF is a binary format, but in theory it should work...)
I want to display documents on my website. The server is hosted on a Debian machine. I was thinking I can allow the upload of support documents then use a Linux app or PHP app to convert the doc into PDF and display that in an HTML page. Are there any APIs or binaries that allow me to do this?
If it is an office document, one option would be to use openoffice in headless mode. See here for a python script that shows how: http://www.oooninja.com/2008/02/batch-command-line-file-conversion-with.html
If it is any other kind of document (e.g. your own XML document), then you would need to do a bit more work. I have had some success using XSL to define a translation to docbook format, then using docbook tools to generate the PDF (and various other formats). You could also use XSL to go straight to PDF if you need more precise control over how things look.
You can create a PDF print-to-file printer and send any number of documents to the printer via lpr.
function lpr($STR,$PRN,$TITLE) {
$prn=(isset($PRN) && strlen($PRN))?"$PRN":C_DEFAULTPRN ;
$title=(isset($TITLE))?"$TITLE":"stdin" . rand() ;
$CMDLINE="lpr -P $prn -T $title";
$pipe=popen("$CMDLINE" , 'w');
if (!$pipe) {print "pipe failed."; return ""; }
fwrite($pipe,$STR);
pclose($pipe);
} // lpr()
//open document...
//read into $source
lpr($source, "PDF", $title); //print to device
exit();
Also HTMLDOC can convert your HTML into a PDF.
A relatively new project, called phpLiveDocx can convert DOC to PDF (in addition to a number of other formats). It is a SOAP based service and can be used completely free of charge. For sample code to convert a DOC to PDF using phpLiveDocx, take a look at this recent blog post:
http://www.phplivedocx.org/2009/02/06/convert-doc-to-pdf-in-php/
Of course, as it is SOAP based, it can be used on all operating systems that support PHP :-)
An alternative method is to generate an HTML file that contains what you need in the pdf. Then use htmldoc to convert it to a PDF.
http://www.easysw.com/htmldoc/
It actually is much easier than directly manipulating the objects in a PDF doc.
Pear has a PHP PDF class. See:
http://pear.php.net/package/File_PDF
http://pear.php.net/package/File_PDF/docs/latest/apidoc/File_PDF/File_PDF.html