appy.pod: xhtml2odt: ability to include images from img tags (anonymously). Non-anonymous solution for a Appy/Zope server only; function 'document': allow to specify size of images in cm or px, or via a 'style' tag; appy.gen: allow to upload images in ckeditor fields; improved error management.

This commit is contained in:
Gaetan Delannay 2012-01-04 18:03:46 +01:00
parent 98fafad14a
commit 2bd3fe1eeb
10 changed files with 206 additions and 54 deletions

View file

@ -2138,7 +2138,8 @@ class Pod(Type):
stylesMapping = self.stylesMapping stylesMapping = self.stylesMapping
rendererParams = {'template': StringIO.StringIO(template.content), rendererParams = {'template': StringIO.StringIO(template.content),
'context': podContext, 'result': tempFileName, 'context': podContext, 'result': tempFileName,
'stylesMapping': stylesMapping} 'stylesMapping': stylesMapping,
'imageResolver': tool.o.getApp()}
if tool.unoEnabledPython: if tool.unoEnabledPython:
rendererParams['pythonWithUnoPath'] = tool.unoEnabledPython rendererParams['pythonWithUnoPath'] = tool.unoEnabledPython
if tool.openOfficePort: if tool.openOfficePort:

View file

@ -21,7 +21,8 @@ homePage = '''
</tal:main> </tal:main>
''' '''
errorPage = ''' errorPage = '''
<tal:main define="tool python: context.config"> <tal:main define="tool python: context.config"
on-error="string: ServerError">
<html metal:use-macro="context/ui/template/macros/main"> <html metal:use-macro="context/ui/template/macros/main">
<div metal:fill-slot="content" tal:define="o python:options"> <div metal:fill-slot="content" tal:define="o python:options">
<p tal:condition="o/error_message" <p tal:condition="o/error_message"

View file

@ -1387,31 +1387,71 @@ class BaseMixin:
for name in templateName.split('/'): res = getattr(res, name) for name in templateName.split('/'): res = getattr(res, name)
return res return res
def download(self): def download(self, name=None):
'''Downloads the content of the file that is in the File field named '''Downloads the content of the file that is in the File field whose
p_name.''' name is in the request. This name can also represent an attribute
storing an image within a rich text field. If p_name is not given, it is retrieved
from the request.'''
name = self.REQUEST.get('name') name = self.REQUEST.get('name')
if not name: return if not name: return
if '_img_' not in name:
appyType = self.getAppyType(name) appyType = self.getAppyType(name)
if (not appyType.type =='File') or not appyType.isShowable(self,'view'): else:
return appyType = self.getAppyType(name.split('_img_')[0])
if not appyType.isShowable(self, 'view'):
from zExceptions import NotFound
raise NotFound()
theFile = getattr(self.aq_base, name, None) theFile = getattr(self.aq_base, name, None)
if theFile: if theFile:
response = self.REQUEST.RESPONSE response = self.REQUEST.RESPONSE
response.setHeader('Content-Disposition', 'inline;filename="%s"' % \ response.setHeader('Content-Disposition', 'inline;filename="%s"' % \
theFile.filename) theFile.filename)
# Define content type
if theFile.content_type:
response.setHeader('Content-Type', theFile.content_type)
response.setHeader('Cachecontrol', 'no-cache') response.setHeader('Cachecontrol', 'no-cache')
response.setHeader('Expires', 'Thu, 11 Dec 1975 12:05:05 GMT') response.setHeader('Expires', 'Thu, 11 Dec 1975 12:05:05 GMT')
return theFile.index_html(self.REQUEST, self.REQUEST.RESPONSE) return theFile.index_html(self.REQUEST, self.REQUEST.RESPONSE)
def allows(self, permission): def upload(self):
'''Receives an image uploaded by the user via ckeditor and stores it in
a special field on this object.'''
# Get the name of the rich text field for which an image must be stored.
params = self.REQUEST['QUERY_STRING'].split('&')
fieldName = params[0].split('=')[1]
ckNum = params[1].split('=')[1]
# We will store the image in a field named [fieldName]_img_[nb].
i = 1
attrName = '%s_img_%d' % (fieldName, i)
while True:
if not hasattr(self.aq_base, attrName):
break
else:
i += 1
attrName = '%s_img_%d' % (fieldName, i)
# Store the image. Create a fake File instance for doing the job.
fakeFile = gen.File(isImage=True)
fakeFile.name = attrName
fakeFile.store(self, self.REQUEST['upload'])
# Return the URL of the image.
url = '%s/download?name=%s' % (self.absolute_url(), attrName)
resp = "<script type='text/javascript'>window.parent.CKEDITOR.tools" \
".callFunction(%s, '%s');</script>" % (ckNum, url)
self.REQUEST.RESPONSE.write(resp)
def allows(self, permission, raiseError=False):
'''Has the logged user p_permission on p_self ?''' '''Has the logged user p_permission on p_self ?'''
return self.getUser().has_permission(permission, self) hasPermission = self.getUser().has_permission(permission, self)
if not hasPermission and raiseError:
from AccessControl import Unauthorized
raise Unauthorized
return hasPermission
def getEditorInit(self, name): def getEditorInit(self, name):
'''Gets the Javascrit init code for displaying a rich editor for '''Gets the Javascript init code for displaying a rich editor for
field named p_name.''' field named p_name.'''
return "CKEDITOR.replace('%s', {toolbar: 'Appy'})" % name return "CKEDITOR.replace('%s', {toolbar: 'Appy', filebrowserUploadUrl:"\
"'%s/upload'})" % (name, self.absolute_url())
def getCalendarInit(self, name, years): def getCalendarInit(self, name, years):
'''Gets the Javascript init code for displaying a calendar popup for '''Gets the Javascript init code for displaying a calendar popup for

View file

@ -2,6 +2,7 @@
<html metal:use-macro="context/ui/template/macros/main"> <html metal:use-macro="context/ui/template/macros/main">
<metal:fill fill-slot="content" <metal:fill fill-slot="content"
tal:define="contextObj context/getParentNode; tal:define="contextObj context/getParentNode;
dummy python: contextObj.allows('Modify portal content', raiseError=True);
errors request/errors | python:{}; errors request/errors | python:{};
layoutType python:'edit'; layoutType python:'edit';
layout python: contextObj.getPageLayout(layoutType); layout python: contextObj.getPageLayout(layoutType);
@ -9,7 +10,8 @@
phase phaseInfo/name; phase phaseInfo/name;
page request/page|python:'main'; page request/page|python:'main';
cssJs python: contextObj.getCssAndJs(contextObj.getAppyTypes(layoutType, page), layoutType); cssJs python: contextObj.getCssAndJs(contextObj.getAppyTypes(layoutType, page), layoutType);
confirmMsg request/confirmMsg | nothing;"> confirmMsg request/confirmMsg | nothing;"
tal:on-error="structure python: tool.manageError(error)">
<tal:comment replace="nothing">Include type-specific CSS and JS.</tal:comment> <tal:comment replace="nothing">Include type-specific CSS and JS.</tal:comment>
<link tal:repeat="cssFile cssJs/css" rel="stylesheet" type="text/css" <link tal:repeat="cssFile cssJs/css" rel="stylesheet" type="text/css"

View file

@ -2,13 +2,15 @@
<html metal:use-macro="context/ui/template/macros/main"> <html metal:use-macro="context/ui/template/macros/main">
<metal:fill fill-slot="content" <metal:fill fill-slot="content"
tal:define="contextObj python: context.getParentNode(); tal:define="contextObj python: context.getParentNode();
dummy python: contextObj.allows('View', raiseError=True);
portal_type python: context.portal_type.lower().replace(' ', '_'); portal_type python: context.portal_type.lower().replace(' ', '_');
errors python: req.get('errors', {}); errors python: req.get('errors', {});
layoutType python:'view'; layoutType python:'view';
layout python: contextObj.getPageLayout(layoutType); layout python: contextObj.getPageLayout(layoutType);
phaseInfo python: contextObj.getAppyPhases(currentOnly=True, layoutType='view'); phaseInfo python: contextObj.getAppyPhases(currentOnly=True, layoutType='view');
page req/page|python:'main'; page req/page|python:'main';
phase phaseInfo/name;"> phase phaseInfo/name;"
tal:on-error="structure python: tool.manageError(error)">
<metal:prologue use-macro="context/ui/page/macros/prologue"/> <metal:prologue use-macro="context/ui/page/macros/prologue"/>
<metal:show use-macro="context/ui/page/macros/show"/> <metal:show use-macro="context/ui/page/macros/show"/>
<metal:footer use-macro="context/ui/page/macros/footer"/> <metal:footer use-macro="context/ui/page/macros/footer"/>

View file

@ -17,10 +17,12 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA.
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
import os, os.path, time, shutil, struct, random import os, os.path, time, shutil, struct, random, urlparse
from appy.pod import PodError from appy.pod import PodError
from appy.pod.odf_parser import OdfEnvironment from appy.pod.odf_parser import OdfEnvironment
from appy.shared import mimeTypesExts
from appy.shared.utils import FileWrapper from appy.shared.utils import FileWrapper
from appy.shared.dav import Resource
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
FILE_NOT_FOUND = "'%s' does not exist or is not a file." FILE_NOT_FOUND = "'%s' does not exist or is not a file."
@ -32,28 +34,30 @@ PDF_TO_IMG_ERROR = 'A PDF file could not be converted into images. Please ' \
class DocImporter: class DocImporter:
'''Base class used for importing external content into a pod template (an '''Base class used for importing external content into a pod template (an
image, another pod template, another odt document...''' image, another pod template, another odt document...'''
def __init__(self, content, at, format, tempFolder, ns, fileNames): def __init__(self, content, at, format, renderer):
self.content = content self.content = content
# If content is None, p_at tells us where to find it (file system path, # If content is None, p_at tells us where to find it (file system path,
# url, etc) # url, etc)
self.at = at self.at = at
# Ensure this path exists. # Ensure this path exists, if it is a local path.
if at and not os.path.isfile(at): raise PodError(FILE_NOT_FOUND % at) if at and not at.startswith('http') and not os.path.isfile(at):
raise PodError(FILE_NOT_FOUND % at)
self.format = format self.format = format
self.res = u'' self.res = u''
self.ns = ns self.renderer = renderer
self.ns = renderer.currentParser.env.namespaces
# Unpack some useful namespaces # Unpack some useful namespaces
self.textNs = ns[OdfEnvironment.NS_TEXT] self.textNs = self.ns[OdfEnvironment.NS_TEXT]
self.linkNs = ns[OdfEnvironment.NS_XLINK] self.linkNs = self.ns[OdfEnvironment.NS_XLINK]
self.drawNs = ns[OdfEnvironment.NS_DRAW] self.drawNs = self.ns[OdfEnvironment.NS_DRAW]
self.svgNs = ns[OdfEnvironment.NS_SVG] self.svgNs = self.ns[OdfEnvironment.NS_SVG]
self.tempFolder = tempFolder self.tempFolder = renderer.tempFolder
self.importFolder = self.getImportFolder() self.importFolder = self.getImportFolder()
# Create the import folder if it does not exist. # Create the import folder if it does not exist.
if not os.path.exists(self.importFolder): os.mkdir(self.importFolder) if not os.path.exists(self.importFolder): os.mkdir(self.importFolder)
self.importPath = self.getImportPath(at, format) self.importPath = self.getImportPath(at, format)
# A link to the global fileNames dict (explained in renderer.py) # A link to the global fileNames dict (explained in renderer.py)
self.fileNames = fileNames self.fileNames = renderer.fileNames
if at: if at:
# Move the file within the ODT, if it is an image and if this image # Move the file within the ODT, if it is an image and if this image
# has not already been imported. # has not already been imported.
@ -84,6 +88,9 @@ class DocImporter:
'''Gets the path name of the file to dump on disk (within the ODT for '''Gets the path name of the file to dump on disk (within the ODT for
images, in a temp folder for docs).''' images, in a temp folder for docs).'''
if not format: if not format:
if at.startswith('http'):
format = '' # We will know it only after the HTTP GET.
else:
format = os.path.splitext(at)[1][1:] format = os.path.splitext(at)[1][1:]
fileName = 'f.%d.%f.%s' % (random.randint(0,10), time.time(), format) fileName = 'f.%d.%f.%s' % (random.randint(0,10), time.time(), format)
return os.path.abspath('%s/%s' % (self.importFolder, fileName)) return os.path.abspath('%s/%s' % (self.importFolder, fileName))
@ -136,8 +143,7 @@ class PdfImporter(DocImporter):
nextImage = '%s/%s%d.jpg' % (imagesFolder, self.imagePrefix, i) nextImage = '%s/%s%d.jpg' % (imagesFolder, self.imagePrefix, i)
if os.path.exists(nextImage): if os.path.exists(nextImage):
# Use internally an Image importer for doing this job. # Use internally an Image importer for doing this job.
imgImporter = ImageImporter(None, nextImage, 'jpg', imgImporter =ImageImporter(None, nextImage, 'jpg',self.renderer)
self.tempFolder, self.ns, self.fileNames)
imgImporter.setAnchor('paragraph') imgImporter.setAnchor('paragraph')
self.res += imgImporter.run() self.res += imgImporter.run()
os.remove(nextImage) os.remove(nextImage)
@ -199,17 +205,70 @@ class ImageImporter(DocImporter):
# Yes! # Yes!
i = importPath.rfind(self.pictFolder) + 1 i = importPath.rfind(self.pictFolder) + 1
return importPath[:i] + imagePath return importPath[:i] + imagePath
# If I am here, the image has not already been imported: copy it. # The image has not already been imported: copy it.
if not at.startswith('http'):
shutil.copy(at, importPath) shutil.copy(at, importPath)
return importPath return importPath
# The image must be retrieved via a URL. Try to perform a HTTP GET.
response = Resource(at).get()
if response.code == 200:
# At last, I can get the file format.
self.format = mimeTypesExts[response.headers['Content-Type']]
importPath += self.format
f = file(importPath, 'wb')
f.write(response.body)
f.close()
return importPath
# The HTTP GET did not work, maybe for security reasons (we probably
# have no permission to get the file). But maybe the URL was a local
# one, from an application server running this POD code. In this case,
# if an image resolver has been given to POD, use it to retrieve the
# image.
imageResolver = self.renderer.imageResolver
if not imageResolver:
# Return some default image explaining that the image wasn't found.
import appy.pod
podFolder = os.path.dirname(appy.pod.__file__)
img = os.path.join(podFolder, 'imageNotFound.jpg')
self.format = 'jpg'
importPath += self.format
f = file(img)
imageContent = f.read()
f.close()
f = file(importPath, 'wb')
f.write(imageContent)
f.close()
else:
# The imageResolver is a Zope application. From it, we will
# retrieve the object on which the image is stored and get
# the file to download.
urlParts = urlparse.urlsplit(at)
path = urlParts[2][1:]
obj = imageResolver.unrestrictedTraverse(path.split('/')[:-1])
zopeFile = getattr(obj, urlParts[3].split('=')[1])
appyFile = FileWrapper(zopeFile)
self.format = mimeTypesExts[appyFile.mimeType]
importPath += self.format
appyFile.dump(importPath)
return importPath
def setImageInfo(self, anchor, wrapInPara, size): def setImageInfo(self, anchor, wrapInPara, size, sizeUnit, style):
# Initialise anchor # Initialise anchor
if anchor not in self.anchorTypes: if anchor not in self.anchorTypes:
raise PodError(self.WRONG_ANCHOR % str(self.anchorTypes)) raise PodError(self.WRONG_ANCHOR % str(self.anchorTypes))
self.anchor = anchor self.anchor = anchor
self.wrapInPara = wrapInPara self.wrapInPara = wrapInPara
self.size = size self.size = size
self.sizeUnit = sizeUnit
# Put CSS attributes from p_style in a dict.
self.cssAttrs = {}
for attr in style.split(';'):
if not attr.strip(): continue
name, value = attr.strip().split(':')
value = value.strip()
if value.endswith('px'): value = value[:-2]
if value.isdigit(): value=int(value)
self.cssAttrs[name.strip()] = value
def run(self): def run(self):
# Some shorcuts for the used xml namespaces # Some shorcuts for the used xml namespaces
@ -222,19 +281,37 @@ class ImageImporter(DocImporter):
i = self.importPath.rfind(self.pictFolder) i = self.importPath.rfind(self.pictFolder)
imagePath = self.importPath[i+1:].replace('\\', '/') imagePath = self.importPath[i+1:].replace('\\', '/')
self.fileNames[imagePath] = self.at self.fileNames[imagePath] = self.at
# Compute image size, or retrieve it from self.size if given # Retrieve image size from self.size.
width = height = None
if self.size: if self.size:
width, height = self.size width, height = self.size
else: if self.sizeUnit == 'px':
# Convert it to cm
width = float(width) / pxToCm
height = float(height) / pxToCm
# Override self.size if 'height' or 'width' is found in self.cssAttrs
if 'width' in self.cssAttrs:
width = float(self.cssAttrs['width']) / pxToCm
if 'height' in self.cssAttrs:
height = float(self.cssAttrs['height']) / pxToCm
# If width and/or height is missing, compute it.
if not width or not height:
width, height = getSize(self.importPath, self.format) width, height = getSize(self.importPath, self.format)
if width != None: if width != None:
size = ' %s:width="%fcm" %s:height="%fcm"' % (s, width, s, height) size = ' %s:width="%fcm" %s:height="%fcm"' % (s, width, s, height)
else: else:
size = '' size = ''
image = '<%s:frame %s:name="%s" %s:z-index="0" %s:anchor-type="%s"%s>' \ if 'float' in self.cssAttrs:
'<%s:image %s:type="simple" %s:show="embed" %s:href="%s" ' \ floatValue = self.cssAttrs['float'].capitalize()
'%s:actuate="onLoad"/></%s:frame>' % (d, d, imageName, d, t, \ styleInfo = '%s:style-name="podImage%s" ' % (d, floatValue)
self.anchor, size, d, x, x, x, imagePath, x, d) self.anchor = 'char'
else:
styleInfo = ''
image = '<%s:frame %s%s:name="%s" %s:z-index="0" ' \
'%s:anchor-type="%s"%s><%s:image %s:type="simple" ' \
'%s:show="embed" %s:href="%s" %s:actuate="onLoad"/>' \
'</%s:frame>' % (d, styleInfo, d, imageName, d, t, self.anchor,
size, d, x, x, x, imagePath, x, d)
if hasattr(self, 'wrapInPara') and self.wrapInPara: if hasattr(self, 'wrapInPara') and self.wrapInPara:
image = '<%s:p>%s</%s:p>' % (t, image, t) image = '<%s:p>%s</%s:p>' % (t, image, t)
self.res += image self.res += image

BIN
pod/imageNotFound.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 861 B

View file

@ -94,7 +94,8 @@ STYLES_POD_FONTS = '<@style@:font-face @style@:name="PodStarSymbol" ' \
class Renderer: class Renderer:
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,
imageResolver=None):
'''This Python Open Document Renderer (PodRenderer) loads a document '''This Python Open Document Renderer (PodRenderer) loads a document
template (p_template) which is an ODT file with some elements template (p_template) which is an ODT file with some elements
written in Python. Based on this template and some Python objects written in Python. Based on this template and some Python objects
@ -128,7 +129,13 @@ class Renderer:
- If you set p_overwriteExisting to True, the renderer will overwrite - If you set p_overwriteExisting to True, the renderer will overwrite
the result file. Else, an exception will be thrown if the result file the result file. Else, an exception will be thrown if the result file
already exists.''' already exists.
- p_imageResolver allows POD to retrieve images, from "img" tags within
XHTML content. Indeed, POD may not be able (ie, may not have the
permission to) perform a HTTP GET on those images. Currently, the
resolver can only be a Zope application object.
'''
self.template = template self.template = template
self.templateZip = zipfile.ZipFile(template) self.templateZip = zipfile.ZipFile(template)
self.result = result self.result = result
@ -143,6 +150,7 @@ class Renderer:
self.forceOoCall = forceOoCall self.forceOoCall = forceOoCall
self.finalizeFunction = finalizeFunction self.finalizeFunction = finalizeFunction
self.overwriteExisting = overwriteExisting self.overwriteExisting = overwriteExisting
self.imageResolver = imageResolver
# Remember potential files or images that will be included through # Remember potential files or images that will be included through
# "do ... from document" statements: we will need to declare them in # "do ... from document" statements: we will need to declare them in
# META-INF/manifest.xml. Keys are file names as they appear within the # META-INF/manifest.xml. Keys are file names as they appear within the
@ -235,13 +243,12 @@ class Renderer:
for converting a chunk of XHTML content (p_xhtmlString) into a chunk for converting a chunk of XHTML content (p_xhtmlString) into a chunk
of ODT content.''' of ODT content.'''
stylesMapping = self.stylesManager.checkStylesMapping(stylesMapping) stylesMapping = self.stylesManager.checkStylesMapping(stylesMapping)
ns = self.currentParser.env.namespaces
# xhtmlString can only be a chunk of XHTML. So we must surround it a # xhtmlString can only be a chunk of XHTML. So we must surround it a
# tag in order to get a XML-compliant file (we need a root tag). # tag in order to get a XML-compliant file (we need a root tag).
if xhtmlString == None: xhtmlString = '' if xhtmlString == None: xhtmlString = ''
xhtmlContent = '<p>%s</p>' % xhtmlString xhtmlContent = '<p>%s</p>' % xhtmlString
return Xhtml2OdtConverter(xhtmlContent, encoding, self.stylesManager, return Xhtml2OdtConverter(xhtmlContent, encoding, self.stylesManager,
stylesMapping, ns).run() stylesMapping, self).run()
def renderText(self, text, encoding='utf-8', stylesMapping={}): def renderText(self, text, encoding='utf-8', stylesMapping={}):
'''Method that can be used (under the name 'text') into a pod template '''Method that can be used (under the name 'text') into a pod template
@ -262,7 +269,8 @@ class Renderer:
imageFormats = ('png', 'jpeg', 'jpg', 'gif') imageFormats = ('png', 'jpeg', 'jpg', 'gif')
ooFormats = ('odt',) ooFormats = ('odt',)
def importDocument(self, content=None, at=None, format=None, def importDocument(self, content=None, at=None, format=None,
anchor='as-char', wrapInPara=True, size=None): anchor='as-char', wrapInPara=True, size=None,
sizeUnit='cm', style=None):
'''If p_at is not None, it represents a path or url allowing to find '''If p_at is not None, it represents a path or url allowing to find
the document. If p_at is None, the content of the document is the document. If p_at is None, the content of the document is
supposed to be in binary format in p_content. The document supposed to be in binary format in p_content. The document
@ -274,9 +282,14 @@ class Renderer:
* p_wrapInPara, if true, wraps the resulting 'image' tag into a 'p' * p_wrapInPara, if true, wraps the resulting 'image' tag into a 'p'
tag; tag;
* p_size, if specified, is a tuple of float or integers * p_size, if specified, is a tuple of float or integers
(width, height) expressing size in centimeters. If not (width, height) expressing size in p_sizeUnit (see below).
specified, size will be computed from image info.''' If not specified, size will be computed from image info.
ns = self.currentParser.env.namespaces * p_sizeUnit is the unit for p_size elements, it can be "cm"
(centimeters) or "px" (pixels).
* If p_style is given, it is the content of a "style" attribute,
containing CSS attributes. If "width" and "heigth" attributes are
found there, they will override p_size and p_sizeUnit.
'''
importer = None importer = None
# Is there someting to import? # Is there someting to import?
if not content and not at: if not content and not at:
@ -297,16 +310,17 @@ class Renderer:
if format in self.ooFormats: if format in self.ooFormats:
importer = OdtImporter importer = OdtImporter
self.forceOoCall = True self.forceOoCall = True
elif format in self.imageFormats: elif (format in self.imageFormats) or not format:
# If the format can't be guessed, we suppose it is an image.
importer = ImageImporter importer = ImageImporter
isImage = True isImage = True
elif format == 'pdf': elif format == 'pdf':
importer = PdfImporter importer = PdfImporter
else: else:
raise PodError(DOC_WRONG_FORMAT % format) raise PodError(DOC_WRONG_FORMAT % format)
imp = importer(content, at, format, self.tempFolder, ns, self.fileNames) imp = importer(content, at, format, self)
# Initialise image-specific parameters # Initialise image-specific parameters
if isImage: imp.setImageInfo(anchor, wrapInPara, size) if isImage: imp.setImageInfo(anchor, wrapInPara, size, sizeUnit, style)
res = imp.run() res = imp.run()
return res return res

View file

@ -113,3 +113,9 @@
<@style@:list-level-properties @text@:space-before="2.5in" @text@:min-label-width="0.25in"/> <@style@:list-level-properties @text@:space-before="2.5in" @text@:min-label-width="0.25in"/>
</@text@:list-level-style-number> </@text@:list-level-style-number>
</@text@:list-style> </@text@:list-style>
<@style@:style @style@:name="podImageLeft" @style@:family="graphic" @style@:parent-style-name="Graphics">
<@style@:graphic-properties @style@:run-through="foreground" @style@:wrap="parallel" @style@:number-wrapped-paragraphs="no-limit" @style@:wrap-contour="false" @style@:vertical-pos="top" @style@:vertical-rel="paragraph" @style@:horizontal-pos="left" @style@:horizontal-rel="paragraph" @style@:mirror="none" @fo@:clip="rect(0cm, 0.3cm, 0cm, 0cm)"/>
</@style@:style>
<@style@:style @style@:name="podImageRight" @style@:family="graphic" @style@:parent-style-name="Graphics">
<@style@:graphic-properties @style@:run-through="foreground" @style@:wrap="parallel" @style@:number-wrapped-paragraphs="no-limit" @style@:wrap-contour="false" @style@:vertical-pos="top" @style@:vertical-rel="paragraph" @style@:horizontal-pos="right" @style@:horizontal-rel="paragraph" @style@:mirror="none" @fo@:clip="rect(0cm, 0.3cm, 0cm, 0cm)"/>
</@style@:style>

View file

@ -214,16 +214,18 @@ class XhtmlEnvironment(XmlEnvironment):
'ul_kwn': 'podBulletItemKeepWithNext', 'ul_kwn': 'podBulletItemKeepWithNext',
'ol_kwn': 'podNumberItemKeepWithNext'} 'ol_kwn': 'podNumberItemKeepWithNext'}
listStyles = {'ul': 'podBulletedList', 'ol': 'podNumberedList'} listStyles = {'ul': 'podBulletedList', 'ol': 'podNumberedList'}
def __init__(self, ns): def __init__(self, renderer):
XmlEnvironment.__init__(self) XmlEnvironment.__init__(self)
self.renderer = renderer
self.ns = renderer.currentParser.env.namespaces
self.res = u'' self.res = u''
self.currentContent = u'' self.currentContent = u''
self.currentElements = [] # Stack of currently walked elements self.currentElements = [] # Stack of currently walked elements
self.currentLists = [] # Stack of currently walked lists (ul or ol) self.currentLists = [] # Stack of currently walked lists (ul or ol)
self.currentTables = [] # Stack of currently walked tables self.currentTables = [] # Stack of currently walked tables
self.textNs = ns[OdfEnvironment.NS_TEXT] self.textNs = self.ns[OdfEnvironment.NS_TEXT]
self.linkNs = ns[OdfEnvironment.NS_XLINK] self.linkNs = self.ns[OdfEnvironment.NS_XLINK]
self.tableNs = ns[OdfEnvironment.NS_TABLE] self.tableNs = self.ns[OdfEnvironment.NS_TABLE]
self.ignore = False # Will be True when parsing parts of the XHTML that self.ignore = False # Will be True when parsing parts of the XHTML that
# must be ignored. # must be ignored.
@ -445,6 +447,12 @@ class XhtmlParser(XmlParser):
e.dumpString(' %s:number-columns-spanned="%s"' % \ e.dumpString(' %s:number-columns-spanned="%s"' % \
(e.tableNs, attrs['colspan'])) (e.tableNs, attrs['colspan']))
e.dumpString('>') e.dumpString('>')
elif elem == 'img':
style = None
if attrs.has_key('style'): style = attrs['style']
imgCode = e.renderer.importDocument(at=attrs['src'],
wrapInPara=False, style=style)
e.dumpString(imgCode)
elif elem in IGNORABLE_TAGS: elif elem in IGNORABLE_TAGS:
e.ignore = True e.ignore = True
@ -483,7 +491,8 @@ class XhtmlParser(XmlParser):
class Xhtml2OdtConverter: class Xhtml2OdtConverter:
'''Converts a chunk of XHTML into a chunk of ODT.''' '''Converts a chunk of XHTML into a chunk of ODT.'''
def __init__(self, xhtmlString, encoding, stylesManager, localStylesMapping, def __init__(self, xhtmlString, encoding, stylesManager, localStylesMapping,
ns): renderer):
self.renderer = renderer
self.xhtmlString = xhtmlString self.xhtmlString = xhtmlString
self.encoding = encoding # Todo: manage encoding that is not utf-8 self.encoding = encoding # Todo: manage encoding that is not utf-8
self.stylesManager = stylesManager self.stylesManager = stylesManager
@ -491,7 +500,7 @@ class Xhtml2OdtConverter:
self.globalStylesMapping = stylesManager.stylesMapping self.globalStylesMapping = stylesManager.stylesMapping
self.localStylesMapping = localStylesMapping self.localStylesMapping = localStylesMapping
self.odtChunk = None self.odtChunk = None
self.xhtmlParser = XhtmlParser(XhtmlEnvironment(ns), self) self.xhtmlParser = XhtmlParser(XhtmlEnvironment(renderer), self)
def run(self): def run(self):
self.xhtmlParser.parse(self.xhtmlString) self.xhtmlParser.parse(self.xhtmlString)