Hello, dear habrovchane!
Not so long ago, I had to face a typical task - to create documents with user data based on ODT templates using PHP. It sounds very trivial, but I had to suffer a lot. The fact is that none of the available means, one way or another, did not fit. Some libraries formed a crooked document, others did not support Russian fonts, and still others moved pictures in the Harlem Shake style. So I had to "ride".
So, the task in brief:
')
- Process ODT template. Replace placeholders with custom values
- Convert to pdf. Show user
STEP 1. Process the ODT template. Replace placeholdersIt's no secret that ODT is a regular archive with xml on board. All the pictures are hidden in a folder, the name of which can be anything, just to be referred to in the description file. We will not go into details: it is enough just to say that content.xml is responsible for the main content of the document, manifest.xml is responsible for the “descriptive” part. I draw your attention that the styles of the text do not interest us (at least under the conditions of this task). Digging a little deeper into these xml, we derive an algorithm:
- Unpack archive
- To replace the text: the content.xml parsim, replace the placeholder with the necessary values
- For images: load our images into the folder (create it inside the unpacked .odt document), parse content.xml, replace placeholders with a view frame
<draw:frame draw:style-name="a0" draw:name="'.$file_name.'" text:anchor-type="as-char" svg:x="0in" svg:y="0in" svg:width="'.$width.'in" svg:height="'.$height.'in" style:rel-width="scale" style:rel-height="scale"> <draw:image xlink:href="'.self::_ImgDir.DIRECTORY_SEPARATOR.$file_name.'" xlink:type="simple" xlink:show="embed" xlink:actuate="onLoad"/> <svg:title/> <svg:desc/> </draw:frame>
Next, add to the end of the manifest.xml block of the form
<manifest:file-entry manifest:full-path="'.self::_ImgDir.DIRECTORY_SEPARATOR.$file_name.'" manifest:media-type="image/'.$ext.'"/>
- Archive the result back to ODT.
The algorithm is clearly simplified, but effective and easy. On its basis, the odtFormat class (on the knees) was written. A little help on how to use it:
Initialization $odtformat = new odtFormat(“$doc_path”, "$temp_dir");
$ doc_path - path to the .odt template
$ temp_dir - folder in which temporary files will be stored.
Insert text $odtformat->SetText(“$name”, “$value”);
$ name is the name of the placeholder
$ value - user value
Insert image $odtformat->SetImage(“$name”, “$img_path” ,$width, $height);
$ name - placeholder'a name
$ img_path - path to the image
$ width - the desired width of the image in the document (if you do not specify the width of the original)
$ height - the desired length of the image in the document (if not set - the length of the original)
Saving the document
$odtformat->SaveToDisk(“$path_to_save”);
$ path_to_save - where we will save (path + file name)
I do not cite the remaining methods, everything can be seen in the source code. Moreover, most of the auxiliary methods are taken from public sources. And the code itself is as simple as two kopecks. I will give only some settings:
const _SeporatorLeft = '{{';
Separates placeholder from text on the left.
const _SeporatorRight = '}}';
Separates placeholder from text to the right.
const _ImgDir = 'media';
Folder name with custom images
Well, we can say that the .odt file, filled with the data we need, has already been formed. Time for the second stage.
Attention! In order to replace placeholders correctly, when adding them to a document, use “Clear Format”. The class used for MS World 2013. But something tells me that the content of the odt is the same in other versions.
STEP 2. Convert to pdf. Showing to userI must say that I did not find the ideal solution. Essentially, there are practically no solutions. Break the "Internet", stumbled upon a handful of heavyweights, zend lotions and just junk. So, I will tell everything as it was.
First of all, I tried to use online services. First was the Google Docs. It's all clear. Simply display the document on the page through the iframe, avoiding the conversion itself.
Example:
<iframe src="http://docs.google.com/viewer?url=http%3A%2F%2F127.0.0.1%2Fa.odt&embedded=true" width="600" height="780" style="border: none;"></iframe>
Minuses:- Display speed
- Curve Formatting
- When using third-party libraries to form odt, a strange white sheet appeared at the beginning of the document.
- Not pdf
- Online
Pros:Obviously, this decision did not last long. It is worth looking in the direction of Microsoft with their Office Apps. An interesting hint was converting pdf as a print version (built-in online service function). Thus, Microsoft can convert the file and show it right there.
Example:
<iframe src="http://co1-word-view.officeapps.live.com/wv/WordViewer/request.pdf?WOPIsrc=http%3A%2F%2Fco1%2D15%2Dview%2Dwopi%2Ewopi%2Elive%2Enet%3A808%2Foh%2Fwopi%2Ffiles%2F%40%2FwFileId%3FwFileId%3Dhttp://127.0.0.1/a.odt&type=printpdf" width="600" height="780" style="border: none;"></iframe>
Minuses- Insanely long
- The decision itself is crooked.
- Unpredictable behavior in different browsers
- So it was not possible to get rid of the print window
- Online
prosNot finding more decent online options, it was decided to use the server tools. Libreoffice copes well with this task. It has a built-in command-line document converter. The idea was to throw the generated odt into a folder, transfer it to the key to exec, display the ready-made pdfs that are in the same folder. Suppose we have already done apt-get install libreoffice. It remains only to add one line of code:
exec(“libreoffice --headless --invisible --convert-to pdf $full_path_to_file --outdir $full_path_to_dir”);
$ full_path_to_file - full path to files (/var/www/*.odt)
$ full_path_to_dir - full path to save folder (/ var / www / result /)
How to show pdf in an iframe, I think you yourself know.
Minuses- I need access to the server
- Heavy libreoffice package
- exec (similar commands in code are not very good)
pros- Marked increase in speed
- High-quality formatting
- Ease of use of finished documents
- Absolutely local solution
ConclusionWith all the shortcomings of the solution, the goals are fulfilled. I hope that this article will help to avoid some difficulties in performing such a task. All good coding!
LinksOdtFormat libraryAbout LibreOfficePS I apologize for the disgusting formatting.