[pod+gen] Added POD rendering based on ODS templates. Integrated with gen.
This commit is contained in:
parent
d5d99b67eb
commit
43261fde60
|
@ -272,6 +272,12 @@ class ZopeInstaller:
|
||||||
appyType.template)
|
appyType.template)
|
||||||
if os.path.exists(fileName):
|
if os.path.exists(fileName):
|
||||||
setattr(appyTool, attrName, fileName)
|
setattr(appyTool, attrName, fileName)
|
||||||
|
# If the template is ods, set the default format to ods
|
||||||
|
# (because default is odt)
|
||||||
|
if fileName.endswith('.ods'):
|
||||||
|
formats = appyTool.getAttributeName('formats',
|
||||||
|
appyClass, appyType.name)
|
||||||
|
setattr(appyTool, formats, ['ods'])
|
||||||
appyTool.log('Imported "%s" in the tool in ' \
|
appyTool.log('Imported "%s" in the tool in ' \
|
||||||
'attribute "%s"'% (fileName, attrName))
|
'attribute "%s"'% (fileName, attrName))
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -94,6 +94,8 @@ appyLabels = [
|
||||||
('pdf', 'PDF'),
|
('pdf', 'PDF'),
|
||||||
('doc', 'DOC'),
|
('doc', 'DOC'),
|
||||||
('rtf', 'RTF'),
|
('rtf', 'RTF'),
|
||||||
|
('ods', 'ODS'),
|
||||||
|
('xls', 'XLS'),
|
||||||
('front_page_text', 'Welcome to this Appy-powered site.'),
|
('front_page_text', 'Welcome to this Appy-powered site.'),
|
||||||
('captcha_text', 'Please type "${text}" (without the double quotes) in the ' \
|
('captcha_text', 'Please type "${text}" (without the double quotes) in the ' \
|
||||||
'field besides, but without the character at position ' \
|
'field besides, but without the character at position ' \
|
||||||
|
|
BIN
gen/ui/ods.png
Normal file
BIN
gen/ui/ods.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 731 B |
BIN
gen/ui/xls.png
Normal file
BIN
gen/ui/xls.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 314 B |
|
@ -54,7 +54,7 @@ class ToolWrapper(AbstractWrapper):
|
||||||
(user.o.absolute_url(), user.title,access))
|
(user.o.absolute_url(), user.title,access))
|
||||||
return res + '\n'.join(rows) + '</table>'
|
return res + '\n'.join(rows) + '</table>'
|
||||||
|
|
||||||
podOutputFormats = ('odt', 'pdf', 'doc', 'rtf')
|
podOutputFormats = ('odt', 'pdf', 'doc', 'rtf', 'ods', 'xls')
|
||||||
def getPodOutputFormats(self):
|
def getPodOutputFormats(self):
|
||||||
'''Gets the available output formats for POD documents.'''
|
'''Gets the available output formats for POD documents.'''
|
||||||
return [(of, self.translate(of)) for of in self.podOutputFormats]
|
return [(of, self.translate(of)) for of in self.podOutputFormats]
|
||||||
|
@ -185,4 +185,36 @@ class ToolWrapper(AbstractWrapper):
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
failed.append(startObject)
|
failed.append(startObject)
|
||||||
return nb, failed
|
return nb, failed
|
||||||
|
|
||||||
|
def validate(self, new, errors):
|
||||||
|
'''Validates that uploaded POD templates and output types are
|
||||||
|
compatible.'''
|
||||||
|
page = self.request.get('page', 'main')
|
||||||
|
if page == 'documents':
|
||||||
|
# Check that uploaded templates and output formats are compatible.
|
||||||
|
for fieldName in dir(new):
|
||||||
|
# Ignore fields which are not POD templates.
|
||||||
|
if not fieldName.startswith('podTemplate'): continue
|
||||||
|
# Get the file name, either from the newly uploaded file or
|
||||||
|
# from the existing file stored in the database.
|
||||||
|
if getattr(new, fieldName):
|
||||||
|
fileName = getattr(new, fieldName).filename
|
||||||
|
else:
|
||||||
|
fileName = getattr(self, fieldName).name
|
||||||
|
# Get the extension of the uploaded file.
|
||||||
|
ext = os.path.splitext(fileName)[1][1:]
|
||||||
|
# Get the chosen output formats for this template.
|
||||||
|
formatsFieldName = 'formatsFor%s' % fieldName[14:]
|
||||||
|
formats = getattr(new, formatsFieldName)
|
||||||
|
error = False
|
||||||
|
if ext == 'odt':
|
||||||
|
error = ('ods' in formats) or ('xls' in formats)
|
||||||
|
elif ext == 'ods':
|
||||||
|
error = ('odt' in formats) or ('pdf' in formats) or \
|
||||||
|
('doc' in formats) or ('rtf' in formats)
|
||||||
|
if error:
|
||||||
|
msg = 'This (these) format(s) cannot be used with ' \
|
||||||
|
'this template.'
|
||||||
|
setattr(errors, formatsFieldName, msg)
|
||||||
|
return self._callCustom('validate', new, errors)
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
|
@ -51,15 +51,15 @@ DOC_NOT_FOUND = 'Document "%s" was not found.'
|
||||||
URL_NOT_FOUND = 'Doc URL "%s" is wrong. %s'
|
URL_NOT_FOUND = 'Doc URL "%s" is wrong. %s'
|
||||||
BAD_RESULT_TYPE = 'Bad result type "%s". Available types are %s.'
|
BAD_RESULT_TYPE = 'Bad result type "%s". Available types are %s.'
|
||||||
CANNOT_WRITE_RESULT = 'I cannot write result "%s". %s'
|
CANNOT_WRITE_RESULT = 'I cannot write result "%s". %s'
|
||||||
CONNECT_ERROR = 'Could not connect to OpenOffice on port %d. UNO ' \
|
CONNECT_ERROR = 'Could not connect to LibreOffice on port %d. UNO ' \
|
||||||
'(OpenOffice API) says: %s.'
|
'(LibreOffice API) says: %s.'
|
||||||
|
|
||||||
# Some constants ---------------------------------------------------------------
|
# Some constants ---------------------------------------------------------------
|
||||||
DEFAULT_PORT = 2002
|
DEFAULT_PORT = 2002
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
class Converter:
|
class Converter:
|
||||||
'''Converts a document readable by OpenOffice into pdf, doc, txt, rtf...'''
|
'''Converts a document readable by LibreOffice into pdf, doc, txt, rtf...'''
|
||||||
exeVariants = ('soffice.exe', 'soffice')
|
exeVariants = ('soffice.exe', 'soffice')
|
||||||
pathReplacements = {'program files': 'progra~1',
|
pathReplacements = {'program files': 'progra~1',
|
||||||
'openoffice.org 1': 'openof~1',
|
'openoffice.org 1': 'openof~1',
|
||||||
|
@ -72,9 +72,9 @@ class Converter:
|
||||||
self.resultType = resultType
|
self.resultType = resultType
|
||||||
self.resultFilter = self.getResultFilter()
|
self.resultFilter = self.getResultFilter()
|
||||||
self.resultUrl = self.getResultUrl()
|
self.resultUrl = self.getResultUrl()
|
||||||
self.ooContext = None
|
self.loContext = None
|
||||||
self.oo = None # The OpenOffice application object
|
self.oo = None # The LibreOffice application object
|
||||||
self.doc = None # The OpenOffice loaded document
|
self.doc = None # The LibreOffice loaded document
|
||||||
|
|
||||||
def getInputUrls(self, docPath):
|
def getInputUrls(self, docPath):
|
||||||
'''Returns the absolute path of the input file. In fact, it returns a
|
'''Returns the absolute path of the input file. In fact, it returns a
|
||||||
|
@ -100,7 +100,7 @@ class Converter:
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def getResultUrl(self):
|
def getResultUrl(self):
|
||||||
'''Returns the path of the result file in the format needed by OO. If
|
'''Returns the path of the result file in the format needed by LO. If
|
||||||
the result type and the input type are the same (ie the user wants to
|
the result type and the input type are the same (ie the user wants to
|
||||||
refresh indexes or some other action and not perform a real
|
refresh indexes or some other action and not perform a real
|
||||||
conversion), the result file is named
|
conversion), the result file is named
|
||||||
|
@ -126,7 +126,7 @@ class Converter:
|
||||||
raise ConverterError(CANNOT_WRITE_RESULT % (res, ioe))
|
raise ConverterError(CANNOT_WRITE_RESULT % (res, ioe))
|
||||||
|
|
||||||
def connect(self):
|
def connect(self):
|
||||||
'''Connects to OpenOffice'''
|
'''Connects to LibreOffice'''
|
||||||
if os.name == 'nt':
|
if os.name == 'nt':
|
||||||
import socket
|
import socket
|
||||||
import uno
|
import uno
|
||||||
|
@ -138,17 +138,17 @@ class Converter:
|
||||||
resolver = localContext.ServiceManager.createInstanceWithContext(
|
resolver = localContext.ServiceManager.createInstanceWithContext(
|
||||||
"com.sun.star.bridge.UnoUrlResolver", localContext)
|
"com.sun.star.bridge.UnoUrlResolver", localContext)
|
||||||
# Connect to the running office
|
# Connect to the running office
|
||||||
self.ooContext = resolver.resolve(
|
self.loContext = resolver.resolve(
|
||||||
'uno:socket,host=localhost,port=%d;urp;StarOffice.' \
|
'uno:socket,host=localhost,port=%d;urp;StarOffice.' \
|
||||||
'ComponentContext' % self.port)
|
'ComponentContext' % self.port)
|
||||||
# Is seems that we can't define a timeout for this method.
|
# Is seems that we can't define a timeout for this method.
|
||||||
# I need it because, for example, when a web server already listens
|
# I need it because, for example, when a web server already listens
|
||||||
# to the given port (thus, not a OpenOffice instance), this method
|
# to the given port (thus, not a LibreOffice instance), this method
|
||||||
# blocks.
|
# blocks.
|
||||||
smgr = self.ooContext.ServiceManager
|
smgr = self.loContext.ServiceManager
|
||||||
# Get the central desktop object
|
# Get the central desktop object
|
||||||
self.oo = smgr.createInstanceWithContext(
|
self.oo = smgr.createInstanceWithContext(
|
||||||
'com.sun.star.frame.Desktop', self.ooContext)
|
'com.sun.star.frame.Desktop', self.loContext)
|
||||||
except NoConnectException, nce:
|
except NoConnectException, nce:
|
||||||
raise ConverterError(CONNECT_ERROR % (self.port, nce))
|
raise ConverterError(CONNECT_ERROR % (self.port, nce))
|
||||||
|
|
||||||
|
@ -220,7 +220,7 @@ class Converter:
|
||||||
raise ConverterError(URL_NOT_FOUND % (self.docPath, iae))
|
raise ConverterError(URL_NOT_FOUND % (self.docPath, iae))
|
||||||
|
|
||||||
def convertDocument(self):
|
def convertDocument(self):
|
||||||
'''Calls OO to perform a document conversion. Note that the conversion
|
'''Calls LO to perform a document conversion. Note that the conversion
|
||||||
is not really done if the source and target documents have the same
|
is not really done if the source and target documents have the same
|
||||||
type.'''
|
type.'''
|
||||||
properties = []
|
properties = []
|
||||||
|
@ -238,7 +238,7 @@ class Converter:
|
||||||
self.doc.storeToURL(self.resultUrl, tuple(properties))
|
self.doc.storeToURL(self.resultUrl, tuple(properties))
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
'''Connects to OO, does the job and disconnects.'''
|
'''Connects to LO, does the job and disconnects.'''
|
||||||
self.connect()
|
self.connect()
|
||||||
self.loadDocument()
|
self.loadDocument()
|
||||||
self.convertDocument()
|
self.convertDocument()
|
||||||
|
@ -257,12 +257,12 @@ class ConverterScript:
|
||||||
' and outputType is the output format, that must be one of\n' \
|
' and outputType is the output format, that must be one of\n' \
|
||||||
' %s.\n' \
|
' %s.\n' \
|
||||||
' "python" should be a UNO-enabled Python interpreter (ie the ' \
|
' "python" should be a UNO-enabled Python interpreter (ie the ' \
|
||||||
' one which is included in the OpenOffice.org distribution).' % \
|
' one which is included in the LibreOffice distribution).' % \
|
||||||
str(FILE_TYPES.keys())
|
str(FILE_TYPES.keys())
|
||||||
def run(self):
|
def run(self):
|
||||||
optParser = OptionParser(usage=ConverterScript.usage)
|
optParser = OptionParser(usage=ConverterScript.usage)
|
||||||
optParser.add_option("-p", "--port", dest="port",
|
optParser.add_option("-p", "--port", dest="port",
|
||||||
help="The port on which OpenOffice runs " \
|
help="The port on which LibreOffice runs " \
|
||||||
"Default is %d." % DEFAULT_PORT,
|
"Default is %d." % DEFAULT_PORT,
|
||||||
default=DEFAULT_PORT, metavar="PORT", type='int')
|
default=DEFAULT_PORT, metavar="PORT", type='int')
|
||||||
(options, args) = optParser.parse_args()
|
(options, args) = optParser.parse_args()
|
||||||
|
|
141
pod/renderer.py
141
pod/renderer.py
|
@ -23,7 +23,7 @@ from UserDict import UserDict
|
||||||
|
|
||||||
import appy.pod, time, cgi
|
import appy.pod, time, cgi
|
||||||
from appy.pod import PodError
|
from appy.pod import PodError
|
||||||
from appy.shared import mimeTypesExts
|
from appy.shared import mimeTypes, mimeTypesExts
|
||||||
from appy.shared.xml_parser import XmlElement
|
from appy.shared.xml_parser import XmlElement
|
||||||
from appy.shared.utils import FolderDeleter, executeCommand
|
from appy.shared.utils import FolderDeleter, executeCommand
|
||||||
from appy.shared.utils import FileWrapper
|
from appy.shared.utils import FileWrapper
|
||||||
|
@ -40,10 +40,10 @@ RESULT_FILE_EXISTS = 'Result file "%s" exists.'
|
||||||
CANT_WRITE_RESULT = 'I cannot write result file "%s". %s'
|
CANT_WRITE_RESULT = 'I cannot write result file "%s". %s'
|
||||||
CANT_WRITE_TEMP_FOLDER = 'I cannot create temp folder "%s". %s'
|
CANT_WRITE_TEMP_FOLDER = 'I cannot create temp folder "%s". %s'
|
||||||
NO_PY_PATH = 'Extension of result file is "%s". In order to perform ' \
|
NO_PY_PATH = 'Extension of result file is "%s". In order to perform ' \
|
||||||
'conversion from ODT to this format we need to call OpenOffice. ' \
|
'conversion from ODT to this format we need to call LibreOffice. ' \
|
||||||
'But the Python interpreter which runs the current script does ' \
|
'But the Python interpreter which runs the current script does ' \
|
||||||
'not know UNO, the library that allows to connect to ' \
|
'not know UNO, the library that allows to connect to ' \
|
||||||
'OpenOffice in server mode. If you can\'t install UNO in this ' \
|
'LibreOffice in server mode. If you can\'t install UNO in this ' \
|
||||||
'Python interpreter, you can specify, in parameter ' \
|
'Python interpreter, you can specify, in parameter ' \
|
||||||
'"pythonWithUnoPath", the path to a UNO-enabled Python ' \
|
'"pythonWithUnoPath", the path to a UNO-enabled Python ' \
|
||||||
'interpreter. One such interpreter may be found in ' \
|
'interpreter. One such interpreter may be found in ' \
|
||||||
|
@ -57,12 +57,11 @@ BLANKS_IN_PATH = 'Blanks were found in path "%s". Please use the DOS-names ' \
|
||||||
BAD_RESULT_TYPE = 'Result "%s" has a wrong extension. Allowed extensions ' \
|
BAD_RESULT_TYPE = 'Result "%s" has a wrong extension. Allowed extensions ' \
|
||||||
'are: "%s".'
|
'are: "%s".'
|
||||||
CONVERT_ERROR = 'An error occurred during the conversion. %s'
|
CONVERT_ERROR = 'An error occurred during the conversion. %s'
|
||||||
BAD_OO_PORT = 'Bad OpenOffice port "%s". Make sure it is an integer.'
|
BAD_OO_PORT = 'Bad LibreOffice port "%s". Make sure it is an integer.'
|
||||||
XHTML_ERROR = 'An error occurred while rendering XHTML content.'
|
XHTML_ERROR = 'An error occurred while rendering XHTML content.'
|
||||||
WARNING_INCOMPLETE_ODT = 'Warning: your ODT file may not be complete (ie ' \
|
WARNING_INCOMPLETE_OD = 'Warning: your OpenDocument file may not be complete ' \
|
||||||
'imported documents may not be present). This is ' \
|
'(ie imported documents may not be present). This is because we could not ' \
|
||||||
'because we could not connect to OpenOffice in ' \
|
'connect to LibreOffice in server mode: %s'
|
||||||
'server mode: %s'
|
|
||||||
DOC_NOT_SPECIFIED = 'Please specify a document to import, either with a ' \
|
DOC_NOT_SPECIFIED = 'Please specify a document to import, either with a ' \
|
||||||
'stream (parameter "content") or with a path (parameter ' \
|
'stream (parameter "content") or with a path (parameter ' \
|
||||||
'"at")'
|
'"at")'
|
||||||
|
@ -92,6 +91,8 @@ STYLES_POD_FONTS = '<@style@:font-face @style@:name="PodStarSymbol" ' \
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
class Renderer:
|
class Renderer:
|
||||||
|
templateTypes = ('odt', 'ods') # Types of POD templates
|
||||||
|
|
||||||
def __init__(self, template, context, result, pythonWithUnoPath=None,
|
def __init__(self, template, context, result, pythonWithUnoPath=None,
|
||||||
ooPort=2002, stylesMapping={}, forceOoCall=False,
|
ooPort=2002, stylesMapping={}, forceOoCall=False,
|
||||||
finalizeFunction=None, overwriteExisting=False,
|
finalizeFunction=None, overwriteExisting=False,
|
||||||
|
@ -416,20 +417,9 @@ class Renderer:
|
||||||
FolderDeleter.delete(self.tempFolder)
|
FolderDeleter.delete(self.tempFolder)
|
||||||
raise po
|
raise po
|
||||||
|
|
||||||
def reportProblem(self, msg, resultType):
|
def callLibreOffice(self, resultName, resultType):
|
||||||
'''When trying to call OO in server mode for producing ODT
|
'''Call LibreOffice in server mode to convert or update the result.'''
|
||||||
(=forceOoCall=True), if an error occurs we still have an ODT to
|
loOutput = ''
|
||||||
return to the user. So we produce a warning instead of raising an
|
|
||||||
error.'''
|
|
||||||
if (resultType == 'odt') and self.forceOoCall:
|
|
||||||
print WARNING_INCOMPLETE_ODT % msg
|
|
||||||
else:
|
|
||||||
raise msg
|
|
||||||
|
|
||||||
def callOpenOffice(self, resultOdtName, resultType):
|
|
||||||
'''Call Open Office in server mode to convert or update the ODT
|
|
||||||
result.'''
|
|
||||||
ooOutput = ''
|
|
||||||
try:
|
try:
|
||||||
if (not isinstance(self.ooPort, int)) and \
|
if (not isinstance(self.ooPort, int)) and \
|
||||||
(not isinstance(self.ooPort, long)):
|
(not isinstance(self.ooPort, long)):
|
||||||
|
@ -437,7 +427,7 @@ class Renderer:
|
||||||
try:
|
try:
|
||||||
from appy.pod.converter import Converter, ConverterError
|
from appy.pod.converter import Converter, ConverterError
|
||||||
try:
|
try:
|
||||||
Converter(resultOdtName, resultType, self.ooPort).run()
|
Converter(resultName, resultType, self.ooPort).run()
|
||||||
except ConverterError, ce:
|
except ConverterError, ce:
|
||||||
raise PodError(CONVERT_ERROR % str(ce))
|
raise PodError(CONVERT_ERROR % str(ce))
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -449,35 +439,54 @@ class Renderer:
|
||||||
raise PodError(BLANKS_IN_PATH % self.pyPath)
|
raise PodError(BLANKS_IN_PATH % self.pyPath)
|
||||||
if not os.path.isfile(self.pyPath):
|
if not os.path.isfile(self.pyPath):
|
||||||
raise PodError(PY_PATH_NOT_FILE % self.pyPath)
|
raise PodError(PY_PATH_NOT_FILE % self.pyPath)
|
||||||
if resultOdtName.find(' ') != -1:
|
if resultName.find(' ') != -1:
|
||||||
qResultOdtName = '"%s"' % resultOdtName
|
qResultName = '"%s"' % resultName
|
||||||
else:
|
else:
|
||||||
qResultOdtName = resultOdtName
|
qResultName = resultName
|
||||||
convScript = '%s/converter.py' % \
|
convScript = '%s/converter.py' % \
|
||||||
os.path.dirname(appy.pod.__file__)
|
os.path.dirname(appy.pod.__file__)
|
||||||
if convScript.find(' ') != -1:
|
if convScript.find(' ') != -1:
|
||||||
convScript = '"%s"' % convScript
|
convScript = '"%s"' % convScript
|
||||||
cmd = '%s %s %s %s -p%d' % \
|
cmd = '%s %s %s %s -p%d' % \
|
||||||
(self.pyPath, convScript, qResultOdtName, resultType,
|
(self.pyPath, convScript, qResultName, resultType,
|
||||||
self.ooPort)
|
self.ooPort)
|
||||||
ooOutput = executeCommand(cmd)
|
loOutput = executeCommand(cmd)
|
||||||
except PodError, pe:
|
except PodError, pe:
|
||||||
# When trying to call OO in server mode for producing
|
# When trying to call LO in server mode for producing ODT or ODS
|
||||||
# ODT (=forceOoCall=True), if an error occurs we still
|
# (=forceOoCall=True), if an error occurs we have nevertheless
|
||||||
# have an ODT to return to the user. So we produce a
|
# an ODT or ODS to return to the user. So we produce a warning
|
||||||
# warning instead of raising an error.
|
# instead of raising an error.
|
||||||
if (resultType == 'odt') and self.forceOoCall:
|
if (resultType in self.templateTypes) and self.forceOoCall:
|
||||||
print WARNING_INCOMPLETE_ODT % str(pe)
|
print WARNING_INCOMPLETE_OD % str(pe)
|
||||||
else:
|
else:
|
||||||
raise pe
|
raise pe
|
||||||
return ooOutput
|
return loOutput
|
||||||
|
|
||||||
|
def getTemplateType(self):
|
||||||
|
'''Identifies the type of the pod template in self.template
|
||||||
|
(ods or odt). If self.template is a string, it is a file name and we
|
||||||
|
simply get its extension. Else, it is a binary file in a StringIO
|
||||||
|
instance, and we seek the mime type from the first bytes.'''
|
||||||
|
if isinstance(self.template, basestring):
|
||||||
|
res = os.path.splitext(self.template)[1][1:]
|
||||||
|
else:
|
||||||
|
# A StringIO instance
|
||||||
|
self.template.seek(0)
|
||||||
|
firstBytes = self.template.read(90)
|
||||||
|
firstBytes = firstBytes[firstBytes.index('mimetype')+8:]
|
||||||
|
if firstBytes.startswith(mimeTypes['ods']):
|
||||||
|
res = 'ods'
|
||||||
|
else:
|
||||||
|
# We suppose this is ODT
|
||||||
|
res = 'odt'
|
||||||
|
return res
|
||||||
|
|
||||||
def finalize(self):
|
def finalize(self):
|
||||||
'''Re-zip the result and potentially call OpenOffice if target format is
|
'''Re-zip the result and potentially call LibreOffice if target format
|
||||||
not ODT or if forceOoCall is True.'''
|
is not among self.templateTypes or if forceOoCall is True.'''
|
||||||
for odtFile in ('content.xml', 'styles.xml'):
|
for innerFile in ('content.xml', 'styles.xml'):
|
||||||
shutil.copy(os.path.join(self.tempFolder, odtFile),
|
shutil.copy(os.path.join(self.tempFolder, innerFile),
|
||||||
os.path.join(self.unzipFolder, odtFile))
|
os.path.join(self.unzipFolder, innerFile))
|
||||||
# Insert dynamic styles
|
# Insert dynamic styles
|
||||||
contentXml = os.path.join(self.unzipFolder, 'content.xml')
|
contentXml = os.path.join(self.unzipFolder, 'content.xml')
|
||||||
f = file(contentXml)
|
f = file(contentXml)
|
||||||
|
@ -493,27 +502,28 @@ class Renderer:
|
||||||
self.finalizeFunction(self.unzipFolder)
|
self.finalizeFunction(self.unzipFolder)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
print WARNING_FINALIZE_ERROR % str(e)
|
print WARNING_FINALIZE_ERROR % str(e)
|
||||||
# Re-zip the result.
|
# Re-zip the result, first as an OpenDocument file of the same type as
|
||||||
resExt = os.path.splitext(self.template)[1]
|
# the POD template (odt, ods...)
|
||||||
resultOdtName = os.path.join(self.tempFolder, 'result%s' % resExt)
|
resultExt = self.getTemplateType()
|
||||||
|
resultName = os.path.join(self.tempFolder, 'result.%s' % resultExt)
|
||||||
try:
|
try:
|
||||||
resultOdt = zipfile.ZipFile(resultOdtName,'w', zipfile.ZIP_DEFLATED)
|
resultZip = zipfile.ZipFile(resultName, 'w', zipfile.ZIP_DEFLATED)
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
resultOdt = zipfile.ZipFile(resultOdtName,'w')
|
resultZip = zipfile.ZipFile(resultName,'w')
|
||||||
# Insert first the file "mimetype" (uncompressed), in order to be
|
# Insert first the file "mimetype" (uncompressed), in order to be
|
||||||
# compliant with the OpenDocument Format specification, section 17.4,
|
# compliant with the OpenDocument Format specification, section 17.4,
|
||||||
# that expresses this restriction. Else, libraries like "magic", under
|
# that expresses this restriction. Else, libraries like "magic", under
|
||||||
# Linux/Unix, are unable to detect the correct mimetype for a pod result
|
# Linux/Unix, are unable to detect the correct mimetype for a pod result
|
||||||
# (it simply recognizes it as a "application/zip" and not a
|
# (it simply recognizes it as a "application/zip" and not a
|
||||||
# "application/vnd.oasis.opendocument.text)".
|
# "application/vnd.oasis.opendocument.text)".
|
||||||
resultOdt.write(os.path.join(self.unzipFolder, 'mimetype'),
|
resultZip.write(os.path.join(self.unzipFolder, 'mimetype'),
|
||||||
'mimetype', zipfile.ZIP_STORED)
|
'mimetype', zipfile.ZIP_STORED)
|
||||||
for dir, dirnames, filenames in os.walk(self.unzipFolder):
|
for dir, dirnames, filenames in os.walk(self.unzipFolder):
|
||||||
for f in filenames:
|
for f in filenames:
|
||||||
folderName = dir[len(self.unzipFolder)+1:]
|
folderName = dir[len(self.unzipFolder)+1:]
|
||||||
# Ignore file "mimetype" that was already inserted.
|
# Ignore file "mimetype" that was already inserted.
|
||||||
if (folderName == '') and (f == 'mimetype'): continue
|
if (folderName == '') and (f == 'mimetype'): continue
|
||||||
resultOdt.write(os.path.join(dir, f),
|
resultZip.write(os.path.join(dir, f),
|
||||||
os.path.join(folderName, f))
|
os.path.join(folderName, f))
|
||||||
if not dirnames and not filenames:
|
if not dirnames and not filenames:
|
||||||
# This is an empty leaf folder. We must create an entry in the
|
# This is an empty leaf folder. We must create an entry in the
|
||||||
|
@ -521,35 +531,34 @@ class Renderer:
|
||||||
folderName = dir[len(self.unzipFolder):]
|
folderName = dir[len(self.unzipFolder):]
|
||||||
zInfo = zipfile.ZipInfo("%s/" % folderName,time.localtime()[:6])
|
zInfo = zipfile.ZipInfo("%s/" % folderName,time.localtime()[:6])
|
||||||
zInfo.external_attr = 48
|
zInfo.external_attr = 48
|
||||||
resultOdt.writestr(zInfo, '')
|
resultZip.writestr(zInfo, '')
|
||||||
resultOdt.close()
|
resultZip.close()
|
||||||
resultType = os.path.splitext(self.result)[1]
|
resultType = os.path.splitext(self.result)[1].strip('.')
|
||||||
try:
|
try:
|
||||||
if (resultType == '.odt') and not self.forceOoCall:
|
if (resultType in self.templateTypes) and not self.forceOoCall:
|
||||||
# Simply move the ODT result to the result
|
# Simply move the ODT result to the result
|
||||||
os.rename(resultOdtName, self.result)
|
os.rename(resultName, self.result)
|
||||||
else:
|
else:
|
||||||
if resultType.startswith('.'): resultType = resultType[1:]
|
if resultType not in FILE_TYPES:
|
||||||
if not resultType in FILE_TYPES.keys():
|
|
||||||
raise PodError(BAD_RESULT_TYPE % (
|
raise PodError(BAD_RESULT_TYPE % (
|
||||||
self.result, FILE_TYPES.keys()))
|
self.result, FILE_TYPES.keys()))
|
||||||
# Call OpenOffice to perform the conversion or document update
|
# Call LibreOffice to perform the conversion or document update.
|
||||||
output = self.callOpenOffice(resultOdtName, resultType)
|
output = self.callLibreOffice(resultName, resultType)
|
||||||
# I (should) have the result. Move it to the correct name
|
# I (should) have the result. Move it to the correct name.
|
||||||
resPrefix = os.path.splitext(resultOdtName)[0] + '.'
|
resPrefix = os.path.splitext(resultName)[0]
|
||||||
if resultType == 'odt':
|
if resultType in self.templateTypes:
|
||||||
# converter.py has (normally!) created a second file
|
# converter.py has (normally!) created a second file
|
||||||
# suffixed .res.odt
|
# suffixed .res.[resultType]
|
||||||
resultName = resPrefix + 'res.odt'
|
finalResultName = '%s.res.%s' % (resPrefix, resultType)
|
||||||
if not os.path.exists(resultName):
|
if not os.path.exists(finalResultName):
|
||||||
resultName = resultOdtName
|
finalResultName = resultName
|
||||||
# In this case OO in server mode could not be called to
|
# In this case OO in server mode could not be called to
|
||||||
# update indexes, sections, etc.
|
# update indexes, sections, etc.
|
||||||
else:
|
else:
|
||||||
resultName = resPrefix + resultType
|
finalResultName = '%s.%s' % (resPrefix, resultType)
|
||||||
if not os.path.exists(resultName):
|
if not os.path.exists(finalResultName):
|
||||||
raise PodError(CONVERT_ERROR % output)
|
raise PodError(CONVERT_ERROR % output)
|
||||||
os.rename(resultName, self.result)
|
os.rename(finalResultName, self.result)
|
||||||
finally:
|
finally:
|
||||||
FolderDeleter.delete(self.tempFolder)
|
FolderDeleter.delete(self.tempFolder)
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
|
@ -4,20 +4,23 @@ import os.path
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
appyPath = os.path.realpath(os.path.dirname(appy.__file__))
|
appyPath = os.path.realpath(os.path.dirname(appy.__file__))
|
||||||
mimeTypes = {'odt': 'application/vnd.oasis.opendocument.text',
|
od = 'application/vnd.oasis.opendocument'
|
||||||
|
mimeTypes = {'odt': '%s.text' % od,
|
||||||
|
'ods': '%s.spreadsheet' % od,
|
||||||
'doc': 'application/msword',
|
'doc': 'application/msword',
|
||||||
'rtf': 'text/rtf',
|
'rtf': 'text/rtf',
|
||||||
'pdf': 'application/pdf'
|
'pdf': 'application/pdf'
|
||||||
}
|
}
|
||||||
mimeTypesExts = {
|
mimeTypesExts = {
|
||||||
'application/vnd.oasis.opendocument.text': 'odt',
|
'%s.text' % od: 'odt',
|
||||||
'application/msword' : 'doc',
|
'%s.spreadsheet' % od: 'ods',
|
||||||
'text/rtf' : 'rtf',
|
'application/msword': 'doc',
|
||||||
'application/pdf' : 'pdf',
|
'text/rtf': 'rtf',
|
||||||
'image/png' : 'png',
|
'application/pdf': 'pdf',
|
||||||
'image/jpeg' : 'jpg',
|
'image/png': 'png',
|
||||||
'image/pjpeg' : 'jpg',
|
'image/jpeg': 'jpg',
|
||||||
'image/gif' : 'gif'
|
'image/pjpeg': 'jpg',
|
||||||
|
'image/gif': 'gif'
|
||||||
}
|
}
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
Loading…
Reference in a new issue