HTML2PDF partie 1 : avec HTMLDOC
Par macadames le 24/03/2005 11:17
Catégories : CMF,Plone,Zope
Sources :
- HowTo sur zopeur.org :http://zopeur.org/Members/dams/html2pdf
- Echanges sur le forum :http://afpy.org/zope/forum_zope/forum_general/122276652908
Installation de HTMLDOC :
Le mieux est de compiler les sources pour avoir la dernière version à jour de htmldoc.
On peut consulter :http://www.easysw.com/htmldoc/, attention certains binaires sont payants.
Pour trouver les sources ou des binaires > Google.
Sous Linux, il suffit souvent de faire :
Script html2pdf.py :
Ce script est une compilation de plein de trucs trouvés sur le web, des trésors cachés de certains cvs, du script de dams, et des modifs testées en live sur le forum Afpy avec fcarlier tellement tenace dans l'échange qu'il a fait avancer la chose . Donc créer un fichier html2pdf.py dans votre_instance_zope/Extensions, voici le code :# Python imports
from StringIO import StringIO
import sys
import os
import time
import re
import urlparse
import AccessControl
import tempfile
import cgi
# CMF imports
from Products.CMFCore.utils import getToolByName
# Zope imports
from zLOG import LOG, ERROR
def html2pdf(self):
"""
Convert html document to PDF
"""
obj_url = self.absolute_url()
utool = getToolByName(self, 'portal_url')
uidtool = getToolByName(self, 'uid_catalog')
portal = utool.getPortalObject()
portal_path = utool.getPortalPath()
portal_url = utool()
temp_path = tempfile.gettempdir()
temp_id = 'PRINT.%s' % str(time.time())
files_to_unlink = []
pdf_data = None
# Method to replace link by new one
def replace_url(match):
"""Compute local url
"""
url = str(match.group('url'))
if match.group('protocol') is not None:
url = '%s%s' % (match.group('protocol'), url)
else:
try:
url=urlparse.urljoin (obj_url, url)
except:
pass
return 'src="%s"' % url
return match.group(0)
filepath = '%s/%s.html' % (temp_path, temp_id)
html = self.pdfView()
# traduction UTF8 vers ISO si necessaire
# errors="strict"
# encodageSite="utf-8"
# html = unicode(html, encodageSite, errors).encode("iso-8859-1", errors)
# Suppression documentActions
# Special Plone car htmldoc fait des trucs pas beaux avec
# on peut utiliser la méthode pour supprimer d'autres blocs qui passent mal
try:
del_docactions = re.compile('(.*)(<div class="documentActions">.*?</div>)(..*)', re.I |re.DOTALL)
matchs = del_docactions.search( html )
html = '%s%s' % (matchs.group(1), matchs.group(3))
except:
pass
abs_url = re.compile('src\s*=\s*([\'\"])(?P<protocol>(ht|f)tps?)?(?P<url>[^\"\']*)\\1', re.IGNORECASE)
html = abs_url.sub(replace_url, html)
try:
fp = open(filepath, "w")
fp.write(html)
finally:
fp.close()
# pour Windoz si ghtmldoc.exe est dans E:\htmldoc
# (stin,stout) = os.popen2('E:/htmldoc/ghtmldoc.exe --header "..." --footer "..." --webpage -t pdf14 --quiet --jpeg "%s"' % filepath , 'b')
# pour linux
(stin,stout) = os.popen2('htmldoc --header "..." --footer "..." --webpage -t pdf14 --quiet --jpeg "%s"' % filepath , 'b')
stin.close()
pdf_data = stout.read()
stout.close()
files_to_unlink.append(filepath)
for file_to_unlink in files_to_unlink:
os.unlink(file_to_unlink)
self.REQUEST.RESPONSE.setHeader('Content-type','application/pdf')
self.REQUEST.RESPONSE.setHeader('Content-disposition','inline; filename="%s.pdf"' % (self.getId()))
return pdf_data
External Method dans Zope :
Dans votre ZMI > votre portail créer une External Method avec les paramètres suivants :
# Ne pas tenir compte de ces 2 lignes (pour bluffer la coloration syntaxique)
# from Products.CMFCore.utils import getToolByName
Id html2pdf
Title html2pdf
Module Name html2pdf
Function Name html2pdf
Et cliquer sur Add
Portal actions (spécial Plone) :
-
Ajoutez une image dans portal_skin/Custom : (download pdf_icon.gif)
- Creer un bouton : Allez dans portal_actionicons, ajoutez une nouvelle Action Icon en bas de page
[plone] [html2pdf] [create PDF file] [ 0] [pdf_icon.gif]
- Ajouter une action au portail. Pour cela allez dans votre_portail/portal_actions et ajoutez les elements suivants :
Name Cree un fichier pdf a la volee
Action string:${object_url}/html2pdf
Condition (vide)
Permission View
Category document_actions
Visible (coché)
Template viewPdf et autres modifs sur templates (special Plone) :
Allez dans la ZMI votre_portail/portal_skins/Custom et créer un Page Template avec l'Id pdfView . Vous pouvez aussi créer le Template pdfView.pt sur le FileSystem si votre skin est sur le FS (ce qui est plus propre). Ce template fait appel aux différents templates de view pour les portal_types de Plone, ce qui permet de garder la logique de Plone et de ne pas tout réécrire. Cette logique n'est pas forcément la meilleure mais elle suffit pour htmldoc.
Code de viewPdf.pt :
<metal:page define-macro="master"><metal:doctype define-slot="doctype"><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"></metal:doctype>
<metal:block define-slot="top_slot" />
<metal:block use-macro="here/global_defines/macros/defines" />
<html xmlns="http://www.w3.org/1999/xhtml"
xml:lang="en"
lang="en"
tal:attributes="lang site_properties/default_language|default;
xml:lang site_properties/default_language|default;">
<head metal:use-macro="here/header/macros/html_header">
<metal:baseslot define-slot="base">
<metal:fillbase fill-slot="base">
<base href="" tal:attributes="href here/renderBase" />
</metal:fillbase>
</metal:baseslot>
<metal:headslot fill-slot="head_slot"
tal:define="language here/Language;
lang language | site_properties/default_language;
charset site_properties/default_charset|string:utf-8">
<metal:cache use-macro="here/global_cache_settings/macros/cacheheaders">
Get the global cache headers located in global_cache_settings.
</metal:cache>
<metal:headslot define-slot="head_slot" />
<!-- A slot where you can insert elements in the header from a template -->
</metal:headslot>
<!-- Ne sert à rien tant que htmldoc ne comprend pas les css
<metal:cssslot fill-slot="css_slot">
<link rel="stylesheet"
type="text/css"
href=""
tal:attributes="href string:$portal_url/pdfView.css" />
</metal:cssslot>
-- >
<metal:javascriptslot fill-slot="javascript_head_slot">
<!-- A slot where you can insert javascript in the header from a template -->
<metal:javascriptslot define-slot="javascript_head_slot" />
</metal:javascriptslot>
</head>
<body tal:define="typeContent here/portal_type;
initial_view_action python: portal.portal_types.getTypeInfo(typeContent).immediate_view;
view_action python: here.pdfViewMacro();
view_macro python:path('here/%s/macros/main|nothing' % view_action);">
<tal:principal metal:use-macro="view_macro" />
</body>
</html>
</metal:page>
Créer un script Python toujours dans Custom ou sur le FS appelé pdViewMacro (ou pdfViewMacro.py ...) qui sert à donner la bonne macro de view, on a pas trouvé de méthode standard avec Plone sauf le immediate_view qui est rarement correctement informé ...
# Ne pas tenir compte de ces 2 lignes (pour bluffer la coloration syntaxique)
# from Products.CMFCore.utils import getToolByName
# renvoie le bon template pour la macro
# attention nécessite une petite modif pour folder_listing ou plonarticle_view
# il faut ajouter un define macro main comme dans
# les autres templates
# types non traités : 'Favorite','Large Plone Folder','Plone Site'
if context.meta_type=='Document':
return 'document_view'
elif context.meta_type=='CMF Event':
return 'event_view'
elif context.meta_type=='Portal File':
return 'file_view'
elif context.meta_type in ('Workgroup','Plone Folder', 'monTypeFolder'):
return 'folder_listing'
elif context.meta_type in ('Portal Image','Photo','CMF ZPhoto'):
return 'image_view'
elif context.meta_type=='Link':
return 'link_view'
elif context.meta_type=='News Item':
return 'newsitem_view'
elif context.meta_type=='Portal Topic':
return 'topic_view'
elif context.meta_type=='PloneArticle':
return 'PloneArticle_view'
elif context.meta_type=='Newsletter':
return 'Newsletter_view'
elif context.meta_type=='NewsletterTheme':
return 'NewsletterTheme_view'
elif context.meta_type=='NewsletterTopic':
return 'NewsletterTopic_view'
elif context.meta_type == 'monType':
return 'monType_view'
else :
return ''
Pour que le template de pdfView fonctionne il faut que les différents templates de view soient normalisés. Par exemple pour ploneArticle_view et pour folder_listing il faut ajouter deux lignes. Par exemple avec folder_listing, ajouter après la ligne
<div metal:fill-slot="main">
<tal:main-macro metal:define-macro="main">
Et bien sûr fermer le block tal:main avant le bloc div en bas de page
</tal:main>
Limites de htmldoc
Les limites de htmldoc sont nombreuses : il ne connait pas les styles css et traduit mal certaines balises html très standard comme les tableaux (cellules toujours centrées en horizontal) ... Inutile d'imaginer faire un truc "pro" avec cette méthode.
Donc en partie 2, on essaie un produit plus sympa : reportlab avec RMLpages Templates et trml2pdf








Home Page : http://www.sopinspace.com/products/Plone2Pdf-prod/fr
Download : http://www.sopinspace.com/products/Plone2Pdf_releases/
Réponses à ce commentaire