[Zope] Passing Parameters to External Methods

Asad Habib ahabib at engin.umich.edu
Thu Nov 17 10:02:12 EST 2005


Actually, I not receiving an error anymore but a pdf is not being 
generated either. When I test my External Method directly through the 
ZMI the only thing that is returned is the following line:

<__builtin__.html2pdf instance at 0x4a1e8f0>

I have tried passing in the parameters using the request object as well as 
hard-coding them in but in both cases a pdf is not generated and there is 
no traceback. The code for my External Method is as follows:

# Added by Asad Habib to allow use as an External Method from Zope
def createHtmlToPdf(self):
    return 
html2pdf('/Applications/Plone2/Library/Software/Zope270/Zope/error.html', 
'orbweaver.engin.umich.edu', 
'/Applications/Plone2/Library/Software/Zope270/Zope/error.pdf')

# This is based on a php script (c) Jason Rust <jrust at rustyparts.com>
# See that script for licensing
# Convert an HTML file to a PDF file using html2ps and ps2pdf

from tempfile import gettempdir, mkstemp
import os
import re
from re import IGNORECASE, DOTALL
from copy import copy

class html2pdf:
   def __init__(self, in_htmlFile, in_domain, in_pdfFile = None):
     """Constructor

     in_htmlFile The full path to the html file to convert
     in_domain The default domain name for images that have a relative path
     in_pdfFile (optional) The full path to the pdf file to output. If not 
given then we create a temporary name.
     """

     self.htmlFile = '' #The full path to the file we are parsing
     self.pdfFile = '' #The full path to the output file
     self.tmpDir = gettempdir() #The temporary directory to save 
intermediate files
     self.debug = False #Whether or not we are in debug mode
     self.htmlErrors = False #Whether we output html errors
     self.defaultDomain = '' #The default domain for relative images
     self.html2psPath = '/usr/bin/html2ps' #The path to the html2ps 
executable
     self.ps2pdfPath = '/usr/bin/ps2pdf' #The path to the ps2pdf executable
     self.getUrlPath = '/usr/bin/curl -i' #The path to your get URL 
program, including options to get headers
     self.useCSS = True #Whether or not to try and parse the CSS in the 
html file and use it in creating the pdf
     self.additionalCSS = '' #Other styles to use when parsing the page
     self.pageInColor = True #Show the page in color?
     self.grayScale = False #Show the images be in grayscale?
     self.scaleFactor = 1 #Scale factore for the page
     self.underlineLinks = None #Whether to underline links or not
     self.headers ={} #The header information  # self.headers was 
array('left' => '$T', 'right' => '$[author]')
     self.footers ={} #The footer information  #self.footers was 
array('center' => '- $N -')
     self.html2psrc = """
         option:
           titlepage: 0;         /* do not generate a title page */
           toc: 0;               /* no table of contents */
           colour: %pageInColor%; /* create the page in color */
           underline: %underlineLinks%;         /* underline links */
           grayscale: %grayScale%; /* Make images grayscale? */
           scaledoc: %scaleFactor%; /* Scale the document */
         }
         package:
           geturl: %getUrlPath%; /* path to the geturl */
         }
         showurl: 0;             /* do not show the url next to links */"""
      #Default html2ps configuration that we use (is parsed before being 
used, though)
     self.makeAbsoluteImageUrls = True
     """Whether HTML_ToPDF should replace all relative image paths in the
       input HTML document with the default domain or not. Switch this to
       false if you want to convert a HTML file which is located locally in 
the
       file system and is not reachable via HTTP but all the images used
       in the HTML file are located correctly according to their relative
       paths."""
     self.ps2pdfIncludePath = '' #Include path for ps2pdf (-I option), for 
example to specify where to search for font files, etc.
     self._htmlString = ''; #We use this to store the html file to a string 
for manipulation

     self.htmlFile = in_htmlFile
     self.defaultDomain = in_domain

     if in_pdfFile is None:
       tempFile = mkstemp(prefix = 'PDF-', dir = self.tmpDir)
       os.close(tempFile[0]) #close the file handle which is opened by 
mkstemp
       self.pdfFile = tempFile[1]
     else:
       self.pdfFile = in_pdfFile


#==================================================================================
   def addHtml2PsSettings(self, in_settings):
     """Adds on more html2ps settings to the end of the default set of 
settings."""
     self.html2psrc += "\n" + in_settings


#==================================================================================
   def setDebug(self, in_debug):
     """Sets the debug variable: true (debugging on) or false (debugging 
off)."""
     self.debug = in_debug


#==================================================================================
   def setHeader(self, in_attribute, in_value):
     """Sets a header.

     @param string $in_attribute One of the header attributes that html2ps 
accepts.  Most
                   common are left, center, right, font-family, font-size, 
color.
     @param string $in_value The attribute value.  Special values that can 
be set are $T
                   (document title), $N (page number), $D (current 
date/time), $U (current
                   url or filename), $[meta-name] (A meta-tag, such as 
$[author] to get
                   author meta tag)
     """
     self.headers[in_attribute] = in_value


#==================================================================================
   def setFooter(self, in_attribute, in_value):
     """Sets a footer.

     @param string $in_attribute One of the header attributes that html2ps 
accepts.  Most
                   common are left, center, right, font-family, font-size, 
color.
     @param string $in_value The attribute value.  Special values that can 
be set are $T
                   (document title), $N (page number), $D (current 
date/time), $U (current
                   url or filename), $[meta-name] (A meta-tag, such as 
$[author] to get
                   author meta tag)
     """
     self.footers[in_attribute] = in_value


#==================================================================================
   def setTmpDir(self, in_path):
     """Set the temporary directory path (full path).


     @param string $in_path The full path to the tmp dir
     """
     self.tmpDir = in_path


#==================================================================================
   def setUseColor(self, in_useColor):
     """Set whether to use color or not when creating the page (bool)."""
     self.pageInColor = in_useColor


#==================================================================================
   def setUseCSS(self, in_useCSS):
     """Set whether to try and use the CSS in the html page when creating 
the pdf file. (bool)"""
     self.useCSS = in_useCSS


#==================================================================================
   def setAdditionalCSS(self, in_css):
     """Set additional CSS to use when parsing the html file. (string)"""
     self.additionalCSS = in_css


#==================================================================================
   def setGetUrl(self, in_getUrl):
     """Sets the get url which is used for retrieving images from the html 
file
     needs to be the full path to the file with options to retrive the 
headers
     as well.

     in_getUrl = string of the get url program path
     """
     self.getUrlPath = in_getUrl


#==================================================================================
   def setGrayScale(self, in_grayScale):
     """Sets the gray scale option for images (bool: True if images should 
be grayscale)."""
     self.grayscale = in_grayScale


#==================================================================================
   def setUnderlineLinks(self, in_underline):
     """Sets the option to underline links or not (bool: True if links 
should be underlined)."""
     self.underlineLinks = in_underline


#==================================================================================
   def setScaleFactor(self, in_scale):
     """Sets the scale factor for the page.  Less than one makes it 
smaller, greater than one enlarges it."""
     self.scaleFactor = in_scale


#==================================================================================
   def setHtml2Ps(self, in_html2ps):
     """Sets the path to the html2ps program."""
     self.html2psPath = in_html2ps


#==================================================================================
   def setPs2Pdf(self, in_ps2pdf):
     """Sets the path to the ps2pdf program (string)."""
     self.ps2pdfPath = in_ps2pdf


#==================================================================================
   def setMakeAbsoluteImageURLs(self, in_makeAbsoluteImageURLs):
     """Sets the makeAbsoluteImageUrls variable (bool).

     Replace relative image URLs in the input HTML file with default 
domain?
     """
     self.makeAbsoluteImageURLs = in_makeAbsoluteImageURLs


#==================================================================================
   def setPs2pdfIncludePath(self, in_ps2pdfIncludePath):
     """Sets the ps2pdfIncludePath variable (string) include path for 
ps2pdf."""
     self.ps2pdfIncludePath = in_ps2pdfIncludePath


#==================================================================================
   def convert(self):
     """Convert the html file into a pdf file.

     Return the path to the pdf file.
     """

     #read the html file in so we can modify it
     htmlfile = open(self.htmlFile, 'rb')
     self._htmlString = htmlfile.read()
     htmlfile.close()

     #grab extra CSS
     self.additionalCSS += self._getCSSFromFile()

     #modify the conf file
     self._modifyConfFile()
     paperSize = self._getPaperSize()
     orientation = self._getOrientation()

     #try and replace relative images with the default domain
     if self.makeAbsoluteImageUrls:
       reImg = re.compile('<img (.*?)src=["\']((?!http\://).*?)["\']', 
IGNORECASE)
       absolute = '<img \\1 src="http://' + self.defaultDomain + '/\\2"'
       self._htmlString = reImg.sub(absolute, self._htmlString)

     #html2ps messes up on several form elements
     reInput = re.compile('<input 
(.*?)type=["\']?(hidden|submit|button|image|reset|file)["\']?.*?>', 
IGNORECASE)
     self._htmlString = reInput.sub('<input />', self._htmlString)

     a_tmpFiles = {}

     tempFile = mkstemp(prefix = 'CONF-', dir = self.tmpDir)
     os.write(tempFile[0], self.html2psrc)
     os.close(tempFile[0])
     a_tmpFiles['config'] = copy(tempFile[1]) #the conf file has to be an 
actual file

     self._dumpDebugInfo("html2ps config: self.html2psrc")

     #make the temporary html file.  We need an html extension for at least 
one version of html2ps
     tempFile = mkstemp(prefix = 'HTML-', dir = self.tmpDir, suffix = 
'.html')
     os.write(tempFile[0], self._htmlString)
     os.close(tempFile[0])
     a_tmpFiles['html'] = copy(tempFile[1])

     #need a temporary postscript file as well
     tempFile = mkstemp(prefix = 'PS-', dir = self.tmpDir)
     os.close(tempFile[0])
     a_tmpFiles['ps'] = copy(tempFile[1])

     cmd = self.html2psPath + ' ' + orientation + ' -f ' + 
a_tmpFiles['config'] + ' -o ' + a_tmpFiles['ps'] + ' ' + 
a_tmpFiles['html'] + ' 2>&1'
     retCode = os.system(cmd)
     self._dumpDebugInfo("html2ps command run: " + cmd)
     #self._dumpDebugInfo("html2ps output: " . result_string))

     if retCode != 0:
       self._cleanup(a_tmpFiles)
       print "Error: there was a problem running the html2ps command. 
Error code returned: " + str(retCode) + ".  setDebug() for more 
information."
       return

     cmd = self.ps2pdfPath + ' -sPAPERSIZE=' + paperSize + ' -I' + 
self.ps2pdfIncludePath + ' ' + ' -dAutoFilterColorImages=false 
-dColorImageFilter=/FlateEncode ' + a_tmpFiles['ps'] + " '" + self.pdfFile 
+ "' 2>&1"

     retCode = os.system(cmd)

     self._dumpDebugInfo("ps2pdf command run: " + cmd)
     #self._dumpDebugInfo("ps2pdf output: " + result_string)

     if retCode != 0:
       self._cleanup(a_tmpFiles)
       print "Error: there was a problem running the ps2pdf command.  Error 
code returned: " + str(retCode) + " setDebug() for more information."
       return

     self._cleanup(a_tmpFiles)
     return self.pdfFile



#==================================================================================
   def _modifyConfFile(self):
     """Modify the config file and put in our custom variables."""
     #first determine if we should try and figure out underline link 
option, based on css
     if self.underlineLinks is None:
       reLink = 
re.compile('a\:link:.*?text-decoration\:(.*?)none(.*?);(.*?)}', IGNORECASE 
or DOTALL)
       if reLink.search(self.additionalCSS):
         self.underlineLinks = False
       else:
         self.underlineLinks = True

     self.html2psrc = self.html2psrc.replace('%scaleFactor%', 
str(self.scaleFactor))
     self.html2psrc = self.html2psrc.replace('%getUrlPath%', 
str(self.getUrlPath))
     #we convert booleans into numbers
     self.html2psrc = self.html2psrc.replace('%pageInColor%', 
str(int(self.pageInColor)))
     self.html2psrc = self.html2psrc.replace('%grayScale%', 
str(int(self.grayScale)))
     self.html2psrc = self.html2psrc.replace('%underlineLinks%', 
str(int(self.underlineLinks)))

     #Add header and footer information
     self.html2psrc += "\nheader:\n" + 
self._processHeaderFooter(self.headers)
     self.html2psrc += "}\nfooter:\n" + 
self._processHeaderFooter(self.footers)
     self.html2psrc += '}'

     #Add in paper size if not present to ensure that headers/footer will 
always show
     rePage = re.compile('@page.*?{.*?size:\s*(.*?);', IGNORECASE or 
DOTALL)
     if not rePage.search(self.additionalCSS):
       self.additionalCSS += "\n at page:\n"
       self.additionalCSS += "  size: 8.5in 11in;\n"
       self.additionalCSS += "}\n"

     #add the global container
     self.html2psrc = """
         @html2ps:
           """ + self.html2psrc + """
         }
         """ + self.additionalCSS


#==================================================================================
   def _getCSSFromFile(self):
     """Try to get the CSS from the html file and use it in creating the
        PDF file.  If we find CSS we'll add it to the CSS string.

     return string Any CSS found
     """
     if (self.useCSS):
       cssFound = ''
       #first try to find inline styles
       reStyle = re.compile('<style.*?>(.*?)</style>', IGNORECASE or 
DOTALL)
       style_matches = reStyle.findall(self._htmlString)
       if style_matches:
         cssFound = style_matches[0]
         #replace it with nothing in the html since it messes up html2ps
         self._htmlString = reStyle.sub('', self._htmlString)
       else:
         reLink = re.compile('<link .*? 
href=["\'](.*?)["\'].*?text/css.*?>', IGNORECASE)
         link_matches = reLink.findall(self._htmlString)
         if link_matches:
           reHttp = re.compile('(^(?!http\://).*)', IGNORECASE)
           absolute = 'http://' + self.defaultDomain + '/\\1'
           cssFound = reHttp.sub(absolute, link_matches[0])

           fp = open(cssFound, "rb")
           cssFound = fp.read()
           fp.close()

       #only takes a:link attribute
       reLink = re.compile('a +{', IGNORECASE)
       cssFound = reLink.sub('a:link', cssFound)

       return cssFound
     else:
       return ''


#==================================================================================
   def _getPaperSize(self):
     """Tries to determine the specified paper size since ps2pdf needs to 
be told explicitly
     in some cases.  Right now handles letter, ledger, 11x17, and legal.

     return the page size string
     :NOTE: We don't support the html2ps paper block since the @page block
     is the new correct way to do it.
     """
     rePage = re.compile('@page.*?{.*?size:\s*(.*?);', IGNORECASE or 
DOTALL)
     matches = rePage.findall(self.html2psrc)
     if not matches:
       matches = ['8.5in 11in']
       #Take out any extra spaces
     matches[0] = matches[0].replace(' ', '')
     if matches[0] == '8.5in14in':
       size = 'legal'
     elif matches[0] == '11in17in':
       size = '11x17'
     elif matches[0] == '17in11in':
       size = 'ledger'
     elif matches[0] == 'a4':
       size = 'a4'
     else:
       size = 'letter'
     return size


#==================================================================================
   def _getOrientation(self):
     """Tries to determine the specified page orientaion since html2ps 
needs to be told explicitly.

     return The page orientation string
     """
     rePage = re.compile('@page.*?{.*?orientation:\s*(.*?);', IGNORECASE or 
DOTALL)
     matches = rePage.findall(self.html2psrc)
     if not matches:
       matches = ['portrait']

     if matches[0] == 'landscape':
       orientation = '--landscape'
     else:
       orientation = ''

     return orientation


#==================================================================================
   def _processHeaderFooter(self, in_data):
     """Process either a set of headers or footers.
     in_data: The header or footer data (dictionary)
     return the html2ps string of data
     """
     s_data = ''
     #If not using odd/even attributes then override them with the main 
left/right/center keys
     #to ensure that the desired headers/footers get in

     for s_key in ['left', 'right', 'center']:
       if s_key in in_data:
         if not (("odd-" + s_key) in in_data):
           in_data["odd-" + s_key] = in_data[s_key]
         if not (("even-" + s_key) in in_data):
           in_data["even-" + s_key] = in_data[s_key]

     for s in in_data.items():
       s_data += "  " + s[0] + ': "' + s[1] + '"\n'

     return s_data


#==================================================================================
   def _cleanup(self, in_files):
     """Cleans up the files we created during the script.

     array $in_files The array of temporary files
     """
     for file in in_files.items():
       if (self.debug):
         self._dumpDebugInfo(file[0] + ' file: ' + file[1] + ' (not 
removed)')
       else:
         os.unlink(file[1])


#==================================================================================
   def _dumpDebugInfo(self, in_info):
     """If debug is on it dumps the specified debug information to screen. 
Uses <pre> tags to save formatting of debug information.

     in_info: the debug info
     """
     if (self.debug):
       if (self.htmlErrors):
         print '<pre><span style="color: red;">DEBUG</span>: ' + in_info + 
'</pre>'
       else:
         print "DEBUG: " + in_info + "\n"


Any help would be greatly appreciated. Thanks.

- Asad



On Wed, 16 Nov 2005, Paul Winkler wrote:

> On Wed, Nov 16, 2005 at 11:26:10AM -0600, J Cameron Cooper wrote:
>> Asad Habib wrote:
>>> Hi Cameron. You are right but the self parameter is implicitly passed.
>>
>> It is implicitly passed, but must be explicitly defined::
>>
>>  def createHtmlToPdf(self, in_htmlFile, in_domain, in_pdfFile):
>>     return html2pdf(in_htmlFile, in_domain, in_pdfFile)
>>
>> When you say::
>>
>>    context.createHtmlToPdf(in_htmlFile, in_domain, in_pdfFile)
>>
>> Python ends up calling this method/function something like::
>>
>>    createHtmlToPdf(context, in_htmlFile, in_domain, in_pdfFile)
>>
>> Your message signature must agree.
>
> No, it's optional.  If you don't inlude "self" in the signature,
> it's not passed.  You can see this in the source of ExternalMethod.py:
>
>                if ((self._v_func_code.co_argcount-
>                     len(self._v_func_defaults or ()) - 1 == len(args))
>                    and self._v_func_code.co_varnames[0]=='self'):
>                    return f(self.aq_parent.this(), *args, **kw)
>
>
> I think the problem lies elsewhere. Traceback?
>
> -- 
>
> Paul Winkler
> http://www.slinkp.com
> _______________________________________________
> Zope maillist  -  Zope at zope.org
> http://mail.zope.org/mailman/listinfo/zope
> **   No cross posts or HTML encoding!  **
> (Related lists -
> http://mail.zope.org/mailman/listinfo/zope-announce
> http://mail.zope.org/mailman/listinfo/zope-dev )
>


More information about the Zope mailing list