[Checkins] SVN: z3c.filetype/trunk/ added mimetype converters
Nikolay Kim
fafhrd at datacom.kz
Wed Dec 19 08:09:29 EST 2007
Log message for revision 82357:
added mimetype converters
Changed:
U z3c.filetype/trunk/CHANGES.txt
U z3c.filetype/trunk/src/z3c/filetype/README.txt
U z3c.filetype/trunk/src/z3c/filetype/__init__.py
U z3c.filetype/trunk/src/z3c/filetype/adapters.py
U z3c.filetype/trunk/src/z3c/filetype/api.py
U z3c.filetype/trunk/src/z3c/filetype/configure.zcml
A z3c.filetype/trunk/src/z3c/filetype/converter.txt
A z3c.filetype/trunk/src/z3c/filetype/converters/
A z3c.filetype/trunk/src/z3c/filetype/converters/__init__.py
A z3c.filetype/trunk/src/z3c/filetype/converters/configure.zcml
A z3c.filetype/trunk/src/z3c/filetype/converters/gif.py
A z3c.filetype/trunk/src/z3c/filetype/converters/jpg.py
A z3c.filetype/trunk/src/z3c/filetype/converters/jpggif.py
A z3c.filetype/trunk/src/z3c/filetype/converters/jpgpng.py
A z3c.filetype/trunk/src/z3c/filetype/converters/pilimage.py
A z3c.filetype/trunk/src/z3c/filetype/converters/png.py
A z3c.filetype/trunk/src/z3c/filetype/converters/pnggif.py
A z3c.filetype/trunk/src/z3c/filetype/converters/utils.py
D z3c.filetype/trunk/src/z3c/filetype/event.py
U z3c.filetype/trunk/src/z3c/filetype/interfaces/__init__.py
A z3c.filetype/trunk/src/z3c/filetype/interfaces/api.py
A z3c.filetype/trunk/src/z3c/filetype/interfaces/converter.py
A z3c.filetype/trunk/src/z3c/filetype/interfaces/magic.py
U z3c.filetype/trunk/src/z3c/filetype/magic.py
U z3c.filetype/trunk/src/z3c/filetype/size.py
U z3c.filetype/trunk/src/z3c/filetype/tests.py
A z3c.filetype/trunk/src/z3c/filetype/typeablefile.py
-=-
Modified: z3c.filetype/trunk/CHANGES.txt
===================================================================
--- z3c.filetype/trunk/CHANGES.txt 2007-12-19 10:59:55 UTC (rev 82356)
+++ z3c.filetype/trunk/CHANGES.txt 2007-12-19 13:09:28 UTC (rev 82357)
@@ -5,7 +5,14 @@
After
=====
+2007/12/19 1.2.0
+================
+ - code cleanup
+ - use IFileData interface for data access
+ - mimetype converting
+
+
2007/12/06 1.1.0
================
@@ -13,4 +20,3 @@
- mimetype extension for audio/mpeg layer 3
- removed setup.cfg
- added CHANGES.txt
-
Modified: z3c.filetype/trunk/src/z3c/filetype/README.txt
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/README.txt 2007-12-19 10:59:55 UTC (rev 82356)
+++ z3c.filetype/trunk/src/z3c/filetype/README.txt 2007-12-19 13:09:28 UTC (rev 82357)
@@ -7,12 +7,14 @@
This package provides a way to get interfaces that are provided based
on their content, filename or mime-type.
- >>> from z3c.filetype import api
+ >>> from z3c.filetype import api, typeablefile
+ >>> from zope import event, component
+ >>> from zope.component import eventtesting
We take some files for demonstration from the testdata directory.
>>> import os
- >>> testData = os.path.join(os.path.dirname(api.__file__),'testdata')
+ >>> testData = os.path.join(os.path.dirname(api.__file__), 'testdata')
>>> fileNames = sorted(os.listdir(testData))
@@ -68,7 +70,10 @@
>>> sorted(api.getInterfacesFor(f))
[<InterfaceClass z3c.filetype.interfaces.filetypes.ITARFile>]
- >>> sorted(api.getInterfacesFor(filename="x.png"))
+ >>> sorted(api.getInterfacesForFile(os.path.join(testData, 'test.tar')))
+ [<InterfaceClass z3c.filetype.interfaces.filetypes.ITARFile>]
+
+ >>> sorted(api.getInterfacesForFilename('x.png'))
[<InterfaceClass z3c.filetype.interfaces.filetypes.IPNGFile>]
>>> sorted(api.getInterfacesFor(f, filename="x.png"))
@@ -87,7 +92,7 @@
>>> f.name
'...test.tar'
- >>> sorted(api.getInterfacesFor(f.name))
+ >>> sorted(api.getInterfacesForFilename(f.name))
[<InterfaceClass z3c.filetype.interfaces.filetypes.ITARFile>]
@@ -98,34 +103,28 @@
object. This object needs to implement ITypeableFile. So let us setup
the event handling.
- >>> from zope.component import eventtesting
- >>> eventtesting.setUp()
-
>>> from z3c.filetype import interfaces
>>> from zope import interface
>>> class Foo(object):
... interface.implements(interfaces.ITypeableFile)
... def __init__(self, f):
- ... self.data = f
+ ... f.seek(0)
+ ... self.data = f.read()
>>> foo = Foo(f)
There is also an event handler registered for IObjectCreatedEvent and
-IObjectModified on ITypeableFile. We register them here in the test.
-
- >>> from zope import component
- >>> component.provideHandler(api.handleCreated)
- >>> component.provideHandler(api.handleModified)
-
+IObjectModified on ITypeableFile.
So we need to fire an IObjectCreatedEvent. Which is normally done by a
factory.
>>> from zope.lifecycleevent import ObjectCreatedEvent
>>> from zope.lifecycleevent import ObjectModifiedEvent
- >>> from zope.event import notify
+
>>> eventtesting.clearEvents()
- >>> notify(ObjectCreatedEvent(foo))
+ >>> event.notify(ObjectCreatedEvent(foo))
+
>>> sorted(eventtesting.getEvents())
- [<z3c.filetype.event.FileTypeModifiedEvent object at ...>,
+ [<z3c.filetype.interfaces.FileTypeModifiedEvent object at ...>,
<zope.app.event.objectevent.ObjectCreatedEvent object at ...>]
The object now implements the according interface. This is achieved by
@@ -146,16 +145,15 @@
If we change the object the interface changes too. We need to fire
an IObjectModifiedevent. Which is normally done by the implementation.
- >>> foo.data = file(os.path.join(testData,'test.flv'), 'rb')
+ >>> foo.data = file(os.path.join(testData,'test.flv'), 'rb').read()
>>> eventtesting.clearEvents()
- >>>
- >>> notify(ObjectModifiedEvent(foo))
+ >>> event.notify(ObjectModifiedEvent(foo))
Now we have two events, one we fired and one from our handler.
>>> eventtesting.getEvents()
[<zope.app.event.objectevent.ObjectModifiedEvent object at ...>,
- <z3c.filetype.event.FileTypeModifiedEvent object at ...>]
+ <z3c.filetype.interfaces.FileTypeModifiedEvent object at ...>]
Now the file should implement another filetype.
@@ -163,20 +161,22 @@
[<InterfaceClass z3c.filetype.interfaces.filetypes.IFLVFile>]
-IFileType adapters
-==================
+IContentType adapters
+=====================
-There is also an adapter from ITypedFile to IFileType, which can be
+There is also an adapter from ITypedFile to IContentType, which can be
used to get the default content type for the interface.
>>> from z3c.filetype import adapters
- >>> component.provideAdapter(adapters.TypedFileType)
+ >>> component.provideAdapter(adapters.ContentType)
+
>>> for name in fileNames:
- ... if name==".svn": continue
+ ... if name==".svn":
+ ... continue
... path = os.path.join(testData, name)
... i = Foo(file(path, 'rb'))
- ... notify(ObjectModifiedEvent(i))
- ... print name + " --> " + interfaces.IFileType(i).contentType
+ ... event.notify(ObjectModifiedEvent(i))
+ ... print name + " --> " + interfaces.IContentType(i).contentType
DS_Store --> application/octet-stream
IMG_0504.JPG --> image/jpeg
faces_gray.avi --> video/x-msvideo
@@ -210,22 +210,22 @@
>>> component.provideAdapter(size.PNGFileSized)
>>> component.provideAdapter(size.JPGFileSized)
- >>> foo.data = file(os.path.join(testData,'thumbnailImage_small.jpeg'), 'rb')
- >>> notify(ObjectModifiedEvent(foo))
+ >>> foo.data = file(os.path.join(testData,'thumbnailImage_small.jpeg'), 'rb').read()
+ >>> event.notify(ObjectModifiedEvent(foo))
>>> ISized(foo).sizeForDisplay().mapping
{'width': '120', 'height': '90', 'size': '3'}
- >>> foo.data = file(os.path.join(testData,'test.png'), 'rb')
- >>> notify(ObjectModifiedEvent(foo))
+ >>> foo.data = file(os.path.join(testData,'test.png'), 'rb').read()
+ >>> event.notify(ObjectModifiedEvent(foo))
>>> ISized(foo).sizeForDisplay().mapping
{'width': '279', 'height': '19', 'size': '4'}
- >>> foo.data = file(os.path.join(testData,'logo.gif'), 'rb')
- >>> notify(ObjectModifiedEvent(foo))
+ >>> foo.data = file(os.path.join(testData,'logo.gif'), 'rb').read()
+ >>> event.notify(ObjectModifiedEvent(foo))
>>> ISized(foo).sizeForDisplay().mapping
{'width': '201', 'height': '54', 'size': '2'}
- >>> foo.data = file(os.path.join(testData,'IMG_0504.JPG'), 'rb')
- >>> notify(ObjectModifiedEvent(foo))
+ >>> foo.data = file(os.path.join(testData,'IMG_0504.JPG'), 'rb').read()
+ >>> event.notify(ObjectModifiedEvent(foo))
>>> ISized(foo).sizeForDisplay().mapping
{'width': '1600', 'height': '1200', 'size': '499'}
Modified: z3c.filetype/trunk/src/z3c/filetype/__init__.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/__init__.py 2007-12-19 10:59:55 UTC (rev 82356)
+++ z3c.filetype/trunk/src/z3c/filetype/__init__.py 2007-12-19 13:09:28 UTC (rev 82357)
@@ -1 +1 @@
-#package
+# This file is necessary to make this directory a package.
Modified: z3c.filetype/trunk/src/z3c/filetype/adapters.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/adapters.py 2007-12-19 10:59:55 UTC (rev 82356)
+++ z3c.filetype/trunk/src/z3c/filetype/adapters.py 2007-12-19 13:09:28 UTC (rev 82357)
@@ -18,12 +18,12 @@
from zope import interface, component
import interfaces
-from interfaces import filetypes
+from interfaces.filetypes import MT, ITypedFile
-class TypedFileType(object):
- component.adapts(filetypes.ITypedFile)
- interface.implements(interfaces.IFileType)
+class ContentType(object):
+ component.adapts(ITypedFile)
+ interface.implements(interfaces.IContentType)
def __init__(self, context):
self.context = context
@@ -32,9 +32,13 @@
def contentType(self):
decl = interface.Declaration(
*interface.directlyProvidedBy(self.context))
+
for iface in decl.flattened():
- if not issubclass(iface, filetypes.ITypedFile):
+ if not iface.extends(ITypedFile):
continue
- mt = iface.queryTaggedValue(filetypes.MT)
+
+ mt = iface.queryTaggedValue(MT)
if mt is not None:
return mt
+
+ return 'application/octet-stream'
Modified: z3c.filetype/trunk/src/z3c/filetype/api.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/api.py 2007-12-19 10:59:55 UTC (rev 82356)
+++ z3c.filetype/trunk/src/z3c/filetype/api.py 2007-12-19 13:09:28 UTC (rev 82357)
@@ -15,16 +15,15 @@
$Id$
"""
-from StringIO import StringIO
-from zope import interface, component, event
+from zope import interface
+from zope import component, event
from zope.proxy import removeAllProxies
from zope.contenttype import guess_content_type
-from zope.lifecycleevent.interfaces import IObjectCreatedEvent
-from zope.lifecycleevent.interfaces import IObjectModifiedEvent
from z3c.filetype import magic, interfaces
+from z3c.filetype.interfaces.api import IAPI
from z3c.filetype.interfaces import filetypes
-from z3c.filetype.event import FileTypeModifiedEvent
+from z3c.filetype.interfaces.converter import IConverter, ConverterNotFound
magicFile = magic.MagicFile()
@@ -43,40 +42,69 @@
return res
-def getInterfacesFor(file=None, filename=None, mimeType=None):
+def getInterfacesFor(file, filename=None, mimeType=None):
"""returns a sequence of interfaces that are provided by file like
objects (file argument) or string with an optional
- filename as name or mimeType as mime-type
+ filename as name or mimeType as mime-type
"""
+ file.seek(0)
+
ifaces = set()
- if file is not None:
- for mt in magicFile.detect(file):
- ifaces.update(byMimeType(mt))
+ for mt in magicFile.detect(file):
+ ifaces.update(byMimeType(mt))
if mimeType is not None:
ifaces.update(byMimeType(mimeType))
if filename is not None and not ifaces:
- t = guess_content_type(filename)[0]
+ mt = guess_content_type(filename)[0]
# dont trust this here because zope does not recognize some
# binary files.
- if t and not t == 'text/x-unknown-content-type':
- ifaces.update(byMimeType(t))
+ if mt not in ('application/octet-stream',
+ 'text/x-unknown-content-type'):
+ ifaces.update(byMimeType(mt))
if not ifaces:
ifaces.add(filetypes.IBinaryFile)
+
return InterfaceSet(*ifaces)
+def getInterfacesForFile(filename, mimeType=None):
+ file = open(filename, 'r')
+ try:
+ ifaces = getInterfacesFor(file, filename, mimeType)
+ finally:
+ file.close()
+ return ifaces
+
+
+def getInterfacesForFilename(filename):
+ ifaces = set()
+ mt = guess_content_type(filename)[0]
+ if mt not in ('application/octet-stream',
+ 'text/x-unknown-content-type'):
+ ifaces.update(byMimeType(mt))
+ if not ifaces:
+ ifaces.add(filetypes.IBinaryFile)
+ return InterfaceSet(*ifaces)
+
+
def applyInterfaces(obj):
- assert(interfaces.ITypeableFile.providedBy(obj))
+ raw = interfaces.IFileData(obj, None)
+ if raw is None:
+ return False
- ifaces = InterfaceSet(*getInterfacesFor(obj.data))
+ data = raw.open('r')
+
+ ifaces = InterfaceSet(*getInterfacesFor(data))
provided = set(interface.directlyProvidedBy(obj))
for iface in provided:
if not issubclass(iface, filetypes.ITypedFile):
ifaces.add(iface)
+ data.close()
+
ifaces = set(ifaces)
if ifaces and (ifaces != provided):
clean_obj = removeAllProxies(obj)
@@ -85,9 +113,11 @@
if not iface.isOrExtends(filetypes.ITypedFile):
ifaces.add(iface)
- interface.directlyProvides(clean_obj, ifaces)
- event.notify(FileTypeModifiedEvent(obj))
- return True
+ if ifaces:
+ interface.directlyProvides(clean_obj, ifaces)
+ event.notify(interfaces.FileTypeModifiedEvent(obj))
+ return True
+
return False
@@ -131,16 +161,32 @@
return iter(self._data)
- at component.adapter(interfaces.ITypeableFile, IObjectModifiedEvent)
-def handleModified(typeableFile, event):
- """handles modification of data"""
- if interfaces.IFileTypeModifiedEvent.providedBy(event):
- # do nothing if this is already a filetype modification event
- return
- applyInterfaces(typeableFile)
+def convert(obj, mimetype, name=None, converter=IConverter, *args, **kw):
+ from z3c.filetype import typeablefile
+ dest = typeablefile.TypeableFile()
+ interface.directlyProvides(dest, mimetype)
- at component.adapter(interfaces.ITypeableFile, IObjectCreatedEvent)
-def handleCreated(typeableFile, event):
- """handles modification of data"""
- applyInterfaces(typeableFile)
+ if name is None:
+ for name, converter in component.getAdapters((obj, dest), converter):
+ converter.convert(*args, **kw)
+ return dest
+ raise ConverterNotFound(
+ "converter not found for (%s, %s)"%(obj, mimetype))
+ else:
+ if isinstance(name, basestring):
+ name = [name]
+
+ for nm in name:
+ converter = component.queryMultiAdapter((obj, dest), converter, nm)
+ if converter is not None:
+ converter.convert(*args, **kw)
+ return dest
+
+ if converter is None:
+ raise ConverterNotFound(
+ "converter not found for (%s, %s)"%(obj, mimetype))
+
+
+interface.moduleProvides(IAPI)
+__all__ = tuple(IAPI)
Modified: z3c.filetype/trunk/src/z3c/filetype/configure.zcml
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/configure.zcml 2007-12-19 10:59:55 UTC (rev 82356)
+++ z3c.filetype/trunk/src/z3c/filetype/configure.zcml 2007-12-19 13:09:28 UTC (rev 82357)
@@ -1,19 +1,23 @@
<configure
xmlns="http://namespaces.zope.org/zope"
+ xmlns:zcml="http://namespaces.zope.org/zcml"
i18n_domain="zope">
- <subscriber for=".interfaces.ITypeableFile
- zope.lifecycleevent.interfaces.IObjectModifiedEvent"
- handler=".api.handleModified"/>
+ <adapter factory=".adapters.ContentType" />
- <subscriber for=".interfaces.ITypeableFile
- zope.lifecycleevent.interfaces.IObjectCreatedEvent"
- handler=".api.handleCreated"/>
+ <adapter factory=".typeablefile.TypeableFileData" />
+ <subscriber handler=".typeablefile.handleCreated" />
+ <subscriber handler=".typeablefile.handleModified" />
- <adapter factory=".adapters.TypedFileType"/>
-
<adapter factory=".size.PNGFileSized" />
<adapter factory=".size.JPGFileSized" />
<adapter factory=".size.GIFFileSized" />
+ <class class="z3c.blobfile.file.File"
+ zcml:condition="installed z3c.blobfile">
+ <implements interface=".interfaces.IFileData" />
+ </class>
+
+ <include package=".converters" />
+
</configure>
Added: z3c.filetype/trunk/src/z3c/filetype/converter.txt
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/converter.txt (rev 0)
+++ z3c.filetype/trunk/src/z3c/filetype/converter.txt 2007-12-19 13:09:28 UTC (rev 82357)
@@ -0,0 +1,72 @@
+ Converting
+~~~~~~~~~~~~~~
+
+We can convert data, for this we should register converter for required types
+
+ >>> from zope import component, interface
+ >>> from z3c.filetype import api, interfaces
+ >>> from z3c.filetype.interfaces import filetypes
+ >>> from z3c.filetype.converters import jpggif
+
+Now we need load files
+
+ >>> import os.path
+ >>> testData = os.path.join(os.path.dirname(api.__file__), 'testdata')
+
+ >>> from z3c.filetype.typeablefile import TypeableFile
+ >>> img1 = TypeableFile(file(os.path.join(testData, 'logo.gif'), 'rb').read())
+
+ >>> sorted((interface.directlyProvidedBy(img1)))
+ [<InterfaceClass z3c.filetype.interfaces.filetypes.IGIFFile>]
+
+ >>> img2 = api.convert(img1, filetypes.IJPGFile)
+ Traceback (most recent call last):
+ ...
+ ConverterNotFound: ...
+
+Let's register simple image converters
+
+ >>> component.provideAdapter(jpggif.GIFtoJPEGConverter)
+ >>> component.provideAdapter(jpggif.JPEGtoGIFConverter)
+
+ >>> img2 = api.convert(img1, filetypes.IJPGFile)
+ >>> sorted((interface.directlyProvidedBy(img2)))
+ [<InterfaceClass z3c.filetype.interfaces.filetypes.IJPGFile>]
+
+Image converter can scale image
+
+ >>> interfaces.IImageSized(img2).getImageSize()
+ (201, 54)
+
+converter save width/height aspect retio
+
+ >>> img2 = api.convert(img1, filetypes.IJPGFile, width=100, height=54)
+ >>> interfaces.IImageSized(img2).getImageSize()
+ (100, 26)
+
+ >>> img2 = api.convert(img1, filetypes.IJPGFile, width=100, height=40)
+ >>> interfaces.IImageSized(img2).getImageSize()
+ (100, 26)
+
+ >>> img2 = api.convert(img1, filetypes.IJPGFile, scale=0.5)
+ >>> interfaces.IImageSized(img2).getImageSize()
+ (101, 27)
+
+Named converters, we can pass converter `name`. It can be string or sequence
+of names.
+
+ >>> img2 = api.convert(img1, filetypes.IJPGFile, name='MyConverter')
+ Traceback (most recent call last):
+ ...
+ ConverterNotFound: ...
+
+ >>> component.provideAdapter(jpggif.GIFtoJPEGConverter, name='MyConverter')
+
+Simple case with only one name
+
+ >>> img2 = api.convert(img1, filetypes.IJPGFile, name='MyConverter')
+
+With multple names, it will try get `MyConverter` if it is not
+exists it will try `PIL` etc.
+
+ >>> img2 = api.convert(img1, filetypes.IJPGFile, name=['MyConverter', 'PIL', ''])
Added: z3c.filetype/trunk/src/z3c/filetype/converters/__init__.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/converters/__init__.py (rev 0)
+++ z3c.filetype/trunk/src/z3c/filetype/converters/__init__.py 2007-12-19 13:09:28 UTC (rev 82357)
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.
Property changes on: z3c.filetype/trunk/src/z3c/filetype/converters/__init__.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.filetype/trunk/src/z3c/filetype/converters/configure.zcml
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/converters/configure.zcml (rev 0)
+++ z3c.filetype/trunk/src/z3c/filetype/converters/configure.zcml 2007-12-19 13:09:28 UTC (rev 82357)
@@ -0,0 +1,32 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ xmlns:zcml="http://namespaces.zope.org/zcml">
+
+ <zcml:configure zcml:condition="installed z3c.filetype.converters.jpg">
+ <adapter name="PIL" factory=".jpg.JPEGtoJPEGConverter" />
+ </zcml:configure>
+
+ <zcml:configure zcml:condition="installed z3c.filetype.converters.gif">
+ <adapter name="PIL" factory=".gif.GIFtoGIFConverter" />
+ </zcml:configure>
+
+ <zcml:configure zcml:condition="installed z3c.filetype.converters.png">
+ <adapter name="PIL" factory=".png.PNGtoPNGConverter" />
+ </zcml:configure>
+
+ <zcml:configure zcml:condition="installed z3c.filetype.converters.jpggif">
+ <adapter name="PIL" factory=".jpggif.GIFtoJPEGConverter" />
+ <adapter name="PIL" factory=".jpggif.JPEGtoGIFConverter" />
+ </zcml:configure>
+
+ <zcml:configure zcml:condition="installed z3c.filetype.converters.jpgpng">
+ <adapter name="PIL" factory=".jpgpng.PNGtoJPEGConverter" />
+ <adapter name="PIL" factory=".jpgpng.JPEGtoPNGConverter" />
+ </zcml:configure>
+
+ <zcml:configure zcml:condition="installed z3c.filetype.converters.pnggif">
+ <adapter name="PIL" factory=".pnggif.PNGtoGIFConverter" />
+ <adapter name="PIL" factory=".pnggif.GIFtoPNGConverter" />
+ </zcml:configure>
+
+</configure>
Added: z3c.filetype/trunk/src/z3c/filetype/converters/gif.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/converters/gif.py (rev 0)
+++ z3c.filetype/trunk/src/z3c/filetype/converters/gif.py 2007-12-19 13:09:28 UTC (rev 82357)
@@ -0,0 +1,38 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+""" JPEG -> JPEG
+
+$Id$
+"""
+from zope import component
+from z3c.filetype.interfaces import filetypes
+from z3c.filetype.converters.utils import log
+from z3c.filetype.converters.pilimage import PILImageConverter
+
+import PIL.Image
+
+# check encoders
+try:
+ PIL.Image.core.gif_decoder
+ PIL.Image.core.gif_encoder
+except:
+ log("GIF converters are not available. PIL doesn't support this formats")
+ raise ImportError()
+
+
+class GIFtoGIFConverter(PILImageConverter):
+ component.adapts(filetypes.IGIFFile, filetypes.IGIFFile)
+
+ _from_format = 'gif'
+ _dest_format = 'gif'
Property changes on: z3c.filetype/trunk/src/z3c/filetype/converters/gif.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.filetype/trunk/src/z3c/filetype/converters/jpg.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/converters/jpg.py (rev 0)
+++ z3c.filetype/trunk/src/z3c/filetype/converters/jpg.py 2007-12-19 13:09:28 UTC (rev 82357)
@@ -0,0 +1,38 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+""" JPEG -> JPEG
+
+$Id$
+"""
+from zope import component
+from z3c.filetype.interfaces import filetypes
+from z3c.filetype.converters.utils import log
+from z3c.filetype.converters.pilimage import PILImageConverter
+
+import PIL.Image
+
+# check encoders
+try:
+ PIL.Image.core.jpeg_decoder
+ PIL.Image.core.jpeg_encoder
+except:
+ log("JPEG converters are not available. PIL doesn't support this formats")
+ raise ImportError()
+
+
+class JPEGtoJPEGConverter(PILImageConverter):
+ component.adapts(filetypes.IJPGFile, filetypes.IJPGFile)
+
+ _from_format = 'jpeg'
+ _dest_format = 'jpeg'
Property changes on: z3c.filetype/trunk/src/z3c/filetype/converters/jpg.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.filetype/trunk/src/z3c/filetype/converters/jpggif.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/converters/jpggif.py (rev 0)
+++ z3c.filetype/trunk/src/z3c/filetype/converters/jpggif.py 2007-12-19 13:09:28 UTC (rev 82357)
@@ -0,0 +1,48 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+""" GIF -> JPEG and JPEG - > GIF converters
+
+$Id$
+"""
+from zope import component
+from z3c.filetype.interfaces import filetypes
+from z3c.filetype.converters.utils import log
+from z3c.filetype.converters.pilimage import PILImageConverter
+
+import PIL.Image
+
+# check encoders
+try:
+ PIL.Image.core.gif_decoder
+ PIL.Image.core.gif_encoder
+ PIL.Image.core.jpeg_decoder
+ PIL.Image.core.jpeg_encoder
+except:
+ log("JPEG/GIF converters are not available. PIL doesn't support this formats")
+ raise ImportError()
+
+
+class JPEGtoGIFConverter(PILImageConverter):
+ component.adapts(filetypes.IJPGFile, filetypes.IGIFFile)
+
+ _from_format = 'jpeg'
+ _dest_format = 'gif'
+
+
+class GIFtoJPEGConverter(PILImageConverter):
+ component.adapts(filetypes.IGIFFile, filetypes.IJPGFile)
+
+ _from_format = 'gif'
+ _dest_format = 'jpeg'
+
Property changes on: z3c.filetype/trunk/src/z3c/filetype/converters/jpggif.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.filetype/trunk/src/z3c/filetype/converters/jpgpng.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/converters/jpgpng.py (rev 0)
+++ z3c.filetype/trunk/src/z3c/filetype/converters/jpgpng.py 2007-12-19 13:09:28 UTC (rev 82357)
@@ -0,0 +1,47 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+""" PNG -> JPEG and JPEG - > PNG converters
+
+$Id$
+"""
+from zope import component
+from z3c.filetype.interfaces import filetypes
+from z3c.filetype.converters.utils import log
+from z3c.filetype.converters.pilimage import PILImageConverter
+
+import PIL.Image
+
+# check encoders
+try:
+ PIL.Image.core.png_decoder
+ PIL.Image.core.png_encoder
+ PIL.Image.core.jpeg_decoder
+ PIL.Image.core.jpeg_encoder
+except:
+ log("JPEG/PNG converters are not available. PIL doesn't support this formats")
+ raise ImportError()
+
+
+class JPEGtoPNGConverter(PILImageConverter):
+ component.adapts(filetypes.IJPGFile, filetypes.IPNGFile)
+
+ _from_format = 'jpeg'
+ _dest_format = 'png'
+
+
+class PNGtoJPEGConverter(PILImageConverter):
+ component.adapts(filetypes.IPNGFile, filetypes.IJPGFile)
+
+ _from_format = 'png'
+ _dest_format = 'jpeg'
Property changes on: z3c.filetype/trunk/src/z3c/filetype/converters/jpgpng.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.filetype/trunk/src/z3c/filetype/converters/pilimage.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/converters/pilimage.py (rev 0)
+++ z3c.filetype/trunk/src/z3c/filetype/converters/pilimage.py 2007-12-19 13:09:28 UTC (rev 82357)
@@ -0,0 +1,82 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+""" PIL based image converter
+
+$Id$
+"""
+from cStringIO import StringIO
+
+from zope import interface
+from z3c.filetype.interfaces import IFileData
+from z3c.filetype.interfaces.converter import \
+ ISimpleImageConverter, ConverterException
+from z3c.filetype.converters.utils import log
+
+try:
+ import PIL.Image
+except:
+ log("Python Imaging library is not available.")
+ raise
+
+
+class PILImageConverter(object):
+ interface.implements(ISimpleImageConverter)
+
+ _from_format = 'raw'
+ _dest_format = 'raw'
+
+ def __init__(self, context, destination):
+ self.context = context
+ self.destination = destination
+
+ def convert(self, width=None, height=None, scale=0, quality=88):
+ """ convert image """
+ file = IFileData(self.context, None)
+ if file is None:
+ raise ConverterException("Can't get data from context.")
+
+ data = file.open('r')
+ try:
+ image = PIL.Image.open(data)
+
+ if image.mode == '1':
+ image = image.convert('L')
+ elif image.mode == 'P':
+ image = image.convert('RGBA')
+
+ # get width, height
+ orig_size = image.size
+ if width is None: width = orig_size[0]
+ if height is None: height = orig_size[1]
+
+ # Auto-scaling support
+ if scale:
+ width = int(round(int(width) * scale))
+ height = int(round(int(height) * scale))
+
+ #convert image
+ pilfilter = PIL.Image.NEAREST
+ if PIL.Image.VERSION >= "1.1.3":
+ pilfilter = PIL.Image.ANTIALIAS
+
+ image.thumbnail((width, height), pilfilter)
+
+ newfile = StringIO()
+ image.save(newfile, self._dest_format, quality=quality)
+ self.destination.data = newfile.getvalue()
+ except Exception, e:
+ data.close()
+ raise ConverterException(str(e))
+
+ data.close()
Property changes on: z3c.filetype/trunk/src/z3c/filetype/converters/pilimage.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.filetype/trunk/src/z3c/filetype/converters/png.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/converters/png.py (rev 0)
+++ z3c.filetype/trunk/src/z3c/filetype/converters/png.py 2007-12-19 13:09:28 UTC (rev 82357)
@@ -0,0 +1,38 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+""" PNG -> PNG
+
+$Id$
+"""
+from zope import component
+from z3c.filetype.interfaces import filetypes
+from z3c.filetype.converters.utils import log
+from z3c.filetype.converters.pilimage import PILImageConverter
+
+import PIL.Image
+
+# check encoders
+try:
+ PIL.Image.core.png_decoder
+ PIL.Image.core.png_encoder
+except:
+ log("PNG converters are not available. PIL doesn't support this formats")
+ raise ImportError()
+
+
+class PNGtoPNGConverter(PILImageConverter):
+ component.adapts(filetypes.IPNGFile, filetypes.IPNGFile)
+
+ _from_format = 'png'
+ _dest_format = 'png'
Property changes on: z3c.filetype/trunk/src/z3c/filetype/converters/png.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.filetype/trunk/src/z3c/filetype/converters/pnggif.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/converters/pnggif.py (rev 0)
+++ z3c.filetype/trunk/src/z3c/filetype/converters/pnggif.py 2007-12-19 13:09:28 UTC (rev 82357)
@@ -0,0 +1,47 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+""" GIF -> PNG and PNG - > GIF converters
+
+$Id$
+"""
+from zope import component
+from z3c.filetype.interfaces import filetypes
+from z3c.filetype.converters.utils import log
+from z3c.filetype.converters.pilimage import PILImageConverter
+
+import PIL.Image
+
+# check encoders
+try:
+ PIL.Image.core.gif_decoder
+ PIL.Image.core.gif_encoder
+ PIL.Image.core.png_decoder
+ PIL.Image.core.png_encoder
+except:
+ log("PNG/GIF converters are not available. PIL doesn't support this formats")
+ raise ImportError()
+
+
+class PNGtoGIFConverter(PILImageConverter):
+ component.adapts(filetypes.IPNGFile, filetypes.IGIFFile)
+
+ _from_format = 'png'
+ _dest_format = 'gif'
+
+
+class GIFtoPNGConverter(PILImageConverter):
+ component.adapts(filetypes.IGIFFile, filetypes.IPNGFile)
+
+ _from_format = 'gif'
+ _dest_format = 'png'
Property changes on: z3c.filetype/trunk/src/z3c/filetype/converters/pnggif.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.filetype/trunk/src/z3c/filetype/converters/utils.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/converters/utils.py (rev 0)
+++ z3c.filetype/trunk/src/z3c/filetype/converters/utils.py 2007-12-19 13:09:28 UTC (rev 82357)
@@ -0,0 +1,22 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+
+$Id$
+"""
+import logging, sys
+
+def log(msg='', subsystem='z3c.filetype.converters'):
+ log = logging.getLogger(subsystem)
+ log.log(logging.INFO, msg)
Property changes on: z3c.filetype/trunk/src/z3c/filetype/converters/utils.py
___________________________________________________________________
Name: svn:keywords
+ Id
Deleted: z3c.filetype/trunk/src/z3c/filetype/event.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/event.py 2007-12-19 10:59:55 UTC (rev 82356)
+++ z3c.filetype/trunk/src/z3c/filetype/event.py 2007-12-19 13:09:28 UTC (rev 82357)
@@ -1,24 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2007 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-##############################################################################
-"""
-
-$Id$
-"""
-from zope import interface
-from zope.lifecycleevent import ObjectModifiedEvent
-from z3c.filetype import interfaces
-
-
-class FileTypeModifiedEvent(ObjectModifiedEvent):
- interface.implements(interfaces.IFileTypeModifiedEvent)
Modified: z3c.filetype/trunk/src/z3c/filetype/interfaces/__init__.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/interfaces/__init__.py 2007-12-19 10:59:55 UTC (rev 82356)
+++ z3c.filetype/trunk/src/z3c/filetype/interfaces/__init__.py 2007-12-19 13:09:28 UTC (rev 82357)
@@ -17,45 +17,41 @@
"""
from zope import interface, schema
from zope.size.interfaces import ISized
+from zope.lifecycleevent import ObjectModifiedEvent
from zope.lifecycleevent.interfaces import IObjectModifiedEvent
-class MagicError(Exception):
- pass
+class IMimetypeType(interface.interfaces.IInterface):
+ """ mimetype type """
-class MagicTestError(MagicError):
- pass
+class IContentType(interface.Interface):
+ contentType = schema.TextLine(
+ title = u'Content Type')
-class OffsetError(MagicError):
- pass
+class IFileData(interface.Interface):
-class MagicFileError(MagicError):
- pass
+ def open(mode='r'):
+ """ Open file, returns file(-like) object for handling the data """
-class IMimetypeType(interface.interfaces.IInterface):
- """ mimetype type """
+class IImageSized(ISized):
+ def getImageSize():
+ """ return width, height of image """
-class IFileType(interface.Interface):
- contentType = schema.TextLine(title = u'Content Type')
-
-
-class IFileTypeModifiedEvent(IObjectModifiedEvent):
- """This event is fired when the filetypes change on an object"""
-
-
class ITypeableFile(interface.Interface):
"""A file object that is typeable"""
data = interface.Attribute('Data of the file')
-class IImageSized(ISized):
+class IFileTypeModifiedEvent(IObjectModifiedEvent):
+ """This event is fired when the filetypes change on an object"""
- def getImageSize():
- """ return width, height of image """
+
+class FileTypeModifiedEvent(ObjectModifiedEvent):
+ interface.implements(IFileTypeModifiedEvent)
Added: z3c.filetype/trunk/src/z3c/filetype/interfaces/api.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/interfaces/api.py (rev 0)
+++ z3c.filetype/trunk/src/z3c/filetype/interfaces/api.py 2007-12-19 13:09:28 UTC (rev 82357)
@@ -0,0 +1,43 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+""" api interfaces
+
+$Id$
+"""
+from zope import interface
+
+
+class IAPI(interface.Interface):
+ """ filetype api """
+
+ def byMimeType(mt):
+ """returns interfaces implemented by mimeType"""
+
+ def getInterfacesFor(file, filename=None, mimeType=None):
+ """ returns a sequence of interfaces that are provided by file like
+ objects (file argument) with an optional
+ filename as name or mimeType as mime-type """
+
+ def getInterfacesForFile(filename, mimeType=None):
+ """ returns a sequence of interfaces that are provided by file
+ with filename as name and optional mimeType as mime-type """
+
+ def getInterfacesForFilename(filename):
+ """ returns a sequence of interfaces that are provided by filename """
+
+ def applyInterfaces(obj):
+ """ detect object mimetypes and set mimetype interfaces """
+
+ def convert(obj, mimetype):
+ """ convert obj to destination mimetype """
Property changes on: z3c.filetype/trunk/src/z3c/filetype/interfaces/api.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.filetype/trunk/src/z3c/filetype/interfaces/converter.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/interfaces/converter.py (rev 0)
+++ z3c.filetype/trunk/src/z3c/filetype/interfaces/converter.py 2007-12-19 13:09:28 UTC (rev 82357)
@@ -0,0 +1,50 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+""" converter interfaces
+
+$Id$
+"""
+from zope import interface
+
+
+class ConverterException(Exception):
+ """ base converter exception """
+
+
+class ConverterNotFound(Exception):
+ """ required converter not found """
+
+
+class IConverterModule(interface.Interface):
+ """ converter module """
+
+ def convert(context, mimetype):
+ """ convert `context` data to mimetype data, return ITypeableFile object """
+
+
+class IConverter(interface.Interface):
+ """ base interface for data converter """
+
+ def __init__(context, destination):
+ """ adapter """
+
+ def convert():
+ """ convert data """
+
+
+class ISimpleImageConverter(IConverter):
+ """ simple image converter """
+
+ def convert(width=None, height=None, scale=0):
+ """ convert image """
Property changes on: z3c.filetype/trunk/src/z3c/filetype/interfaces/converter.py
___________________________________________________________________
Name: svn:keywords
+ Id
Added: z3c.filetype/trunk/src/z3c/filetype/interfaces/magic.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/interfaces/magic.py (rev 0)
+++ z3c.filetype/trunk/src/z3c/filetype/interfaces/magic.py 2007-12-19 13:09:28 UTC (rev 82357)
@@ -0,0 +1,32 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+""" magic file interfaces
+
+$Id$
+"""
+
+class MagicError(Exception):
+ pass
+
+
+class MagicTestError(MagicError):
+ pass
+
+
+class OffsetError(MagicError):
+ pass
+
+
+class MagicFileError(MagicError):
+ pass
Property changes on: z3c.filetype/trunk/src/z3c/filetype/interfaces/magic.py
___________________________________________________________________
Name: svn:keywords
+ Id
Modified: z3c.filetype/trunk/src/z3c/filetype/magic.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/magic.py 2007-12-19 10:59:55 UTC (rev 82356)
+++ z3c.filetype/trunk/src/z3c/filetype/magic.py 2007-12-19 13:09:28 UTC (rev 82357)
@@ -1,9 +1,11 @@
# Found on a russian zope mailing list, and modified to fix bugs in parsing
# the magic file and string making, cleanup
# -- Daniel Berlin <dberlin at dberlin.org>
-import sys, struct, time, re, pprint, os
+import sys, os.path
+import struct, time, re, pprint
-from interfaces import OffsetError, MagicFileError, MagicTestError
+from z3c.filetype.interfaces.magic import \
+ OffsetError, MagicFileError, MagicTestError
default_magic = os.path.join(os.path.dirname(__file__), 'magic.mime')
@@ -19,85 +21,53 @@
KnownTypes = {
- 'byte':_handle('@b'),
- 'byte':_handle('@B'),
- 'ubyte':_handle('@B'),
- 'string':('s',0,None),
- 'pstring':_handle('p'),
- 'short':_handle('@h'),
- 'beshort':_handle('>h'),
- 'leshort':_handle('<h'),
- 'short':_handle('@H'),
- 'beshort':_handle('>H'),
- 'leshort':_handle('<H'),
- 'ushort':_handle('@H'),
- 'ubeshort':_handle('>H'),
- 'uleshort':_handle('<H'),
+ 'byte': _handle('@b'),
+ 'byte': _handle('@B'),
+ 'ubyte': _handle('@B'),
+ 'string': ('s', 0, None),
+ 'pstring': _handle('p'),
+ 'short': _handle('@h'),
+ 'beshort': _handle('>h'),
+ 'leshort': _handle('<h'),
+ 'short': _handle('@H'),
+ 'beshort': _handle('>H'),
+ 'leshort': _handle('<H'),
+ 'ushort': _handle('@H'),
+ 'ubeshort': _handle('>H'),
+ 'uleshort': _handle('<H'),
- 'long':_handle('@l'),
- 'belong':_handle('>l'),
- 'lelong':_handle('<l'),
- 'ulong':_handle('@L'),
- 'ubelong':_handle('>L'),
- 'ulelong':_handle('<L'),
+ 'long': _handle('@l'),
+ 'belong': _handle('>l'),
+ 'lelong': _handle('<l'),
+ 'ulong': _handle('@L'),
+ 'ubelong': _handle('>L'),
+ 'ulelong': _handle('<L'),
- 'date':_handle('=l'),
- 'bedate':_handle('>l'),
- 'ledate':_handle('<l'),
- 'ldate':_handle('=l',_ldate_adjust),
- 'beldate':_handle('>l',_ldate_adjust),
- 'leldate':_handle('<l',_ldate_adjust),
+ 'date': _handle('=l'),
+ 'bedate': _handle('>l'),
+ 'ledate': _handle('<l'),
+ 'ldate': _handle('=l', _ldate_adjust),
+ 'beldate': _handle('>l', _ldate_adjust),
+ 'leldate': _handle('<l', _ldate_adjust),
}
def has_format(s):
n = 0
l = None
- for c in s :
- if c == '%' :
- if l == '%' : n -= 1
- else : n += 1
+ for c in s:
+ if c == '%':
+ if l == '%': n -= 1
+ else : n += 1
l = c
return n
-def read_asciiz(file, size=None, pos=None):
- s = []
- if pos :
- file.seek( pos, 0 )
-
- if size is not None :
- s = [file.read( size ).split('\0')[0]]
- else:
- while 1 :
- c = file.read(1)
- if (not c) or (ord(c)==0) or (c=='\n') : break
- s.append (c)
-
- return ''.join(s)
-
-
-def a2i(v,base=0):
- if v[-1:] in 'lL' : v = v[:-1]
- return int( v, base )
-
-_cmap = {
- '\\' : '\\',
- '0' : '\0',
-}
-for c in range(ord('a'),ord('z')+1) :
- try : e = eval('"\\%c"' % chr(c))
- except ValueError : pass
- else : _cmap[chr(c)] = e
-else:
- del c
- del e
-
def make_string(s):
# hack, is this the right way?
- s = s.replace('\\<','<')
- s = s.replace('\\ ',' ')
- return eval( '"'+s.replace('"','\\"')+'"')
+ s = s.replace('\\<', '<')
+ s = s.replace('\\ ', ' ')
+ return eval('"%s"'%s.replace('"', '\\"'))
class MagicTest:
@@ -112,25 +82,29 @@
self.nmod = None
self.offset, self.type, self.test, self.message = \
offset, mtype, test, message
- if self.mtype == 'true' : return # XXX hack to enable level skips
- if test[-1:]=='\\' and test[-2:]!='\\\\' :
+
+ if self.mtype == 'true':
+ return # XXX hack to enable level skips
+
+ if (test[-1:] == '\\') and (test[-2:] != '\\\\'):
self.test += 'n' # looks like someone wanted EOL to match?
- if mtype[:6]=='string' :
+
+ if mtype[:6] == 'string':
if '/' in mtype : # for strings
self.type, self.smod = \
mtype[:mtype.find('/')], mtype[mtype.find('/')+1:]
else:
- for nm in '&+-' :
- if nm in mtype : # for integer-based
- self.nmod, self.type, self.mask = (
- nm,
- mtype[:mtype.find(nm)],
- # convert mask to int, autodetect base
- int( mtype[mtype.find(nm)+1:], 0 )
- )
+ for nm in '&+-':
+ if nm in mtype: # for integer-based
+ self.nmod = nm
+ self.type = mtype[:mtype.find(nm)]
+
+ # convert mask to int, autodetect base
+ self.mask = int(mtype[mtype.find(nm)+1:], 0)
break
- self.struct, self.size, self.cast = KnownTypes[ self.type ]
+ self.struct, self.size, self.cast = KnownTypes[self.type]
+
def __str__(self):
return '%s %s %s %s' % (
self.offset, self.mtype, self.mtest, self.message)
@@ -142,15 +116,11 @@
'\t'*self.level, pprint.pformat(self.subtests)
)
- def run(self,file):
+ def run(self, file):
result = ''
- do_close = 0
+
try:
- if isinstance(file, basestring):
- file = open(file, 'r', BUFFER_SIZE)
- do_close = 1
-
- if self.mtype != 'true' :
+ if self.mtype != 'true':
data = self.read(file)
try:
last = file.tell()
@@ -161,55 +131,74 @@
else:
data = last = None
- if self.check(data) :
- result = self.message+' '
- if has_format( result ) : result %= data
- for test in self.subtests :
+ if self.check(data):
+ result = self.message + ' '
+
+ if has_format(result):
+ result %= data
+
+ for test in self.subtests:
m = test.run(file)
- if m is not None : result += m
- return make_string( result )
- finally:
- if do_close :
- file.close()
+ if m is not None:
+ result += m
+ return make_string(result)
+ except Exception, e:
+ #log_info('Type detection: %s'%str(e)
+ pass
+
+ def a2i(self, v, base=0):
+ if v[-1:] in 'lL':
+ v = v[:-1]
+ return int(v, base)
+
def get_mod_and_value(self):
- if self.type[-6:] == 'string' :
- if self.test[0] in '=<>' :
- mod, value = self.test[0], make_string( self.test[1:] )
+ if self.type[-6:] == 'string':
+ if self.test[0] in '=<>':
+ mod = self.test[0]
+ value = make_string(self.test[1:])
else:
- mod, value = '=', make_string( self.test )
+ mod = '='
+ value = make_string( self.test )
else:
- if self.test[0] in '=<>&^' :
- mod, value = self.test[0], a2i(self.test[1:])
+ if self.test[0] in '=<>&^':
+ mod = self.test[0]
+ value = self.a2i(self.test[1:])
elif self.test[0] == 'x':
mod = self.test[0]
value = 0
else:
- mod, value = '=', a2i(self.test)
+ mod = '='
+ value = self.a2i(self.test)
return mod, value
def read(self, file):
- file.seek( self.offset(file), 0 ) # SEEK_SET
+ file.seek(self.offset(file), 0) # SEEK_SET
try:
data = rdata = None
# XXX self.size might be 0 here...
- if self.size == 0 :
+ if self.size == 0:
# this is an ASCIIZ string...
size = None
- if self.test != '>\\0' : # magic's hack for string read...
+ if self.test != '>\\0': # magic's hack for string read...
value = self.get_mod_and_value()[1]
size = (value=='\0') and None or len(value)
- rdata = data = read_asciiz( file, size=size )
+
+ rdata = data = self.read_asciiz(file, size=size)
else:
- rdata = file.read( self.size )
- if not rdata or (len(rdata)!=self.size) : return None
- data = struct.unpack( self.struct, rdata )[0] # XXX hack??
+ rdata = file.read(self.size)
+ if not rdata or (len(rdata) != self.size):
+ return None
+
+ data = struct.unpack(self.struct, rdata)[0] # XXX hack??
except:
raise MagicTestError('@%s struct=%s size=%d rdata=%s' % (
self.offset, `self.struct`, self.size, `rdata`))
- if self.cast : data = self.cast( data )
- if self.mask :
+ if self.cast:
+ data = self.cast(data)
+
+ if self.mask:
try:
if self.nmod == '&':
data &= self.mask
@@ -225,7 +214,20 @@
return data
- def check(self,data):
+ def read_asciiz(self, file, size=None):
+ s = []
+ if size is not None :
+ s = [file.read(size).split('\0')[0]]
+ else:
+ while 1:
+ c = file.read(1)
+ if (not c) or (ord(c) == 0) or (c == '\n'):
+ break
+ s.append (c)
+
+ return ''.join(s)
+
+ def check(self, data):
if self.mtype == 'true' :
return '' # not None !
@@ -233,17 +235,20 @@
if self.type[-6:] == 'string' :
# "something like\tthis\n"
if self.smod:
- #import pdb;pdb.set_trace()
xdata = data
- if 'b' in self.smod : # all blanks are optional
- xdata = ''.join( data.split() )
- value = ''.join( value.split() )
- if 'c' in self.smod : # all blanks are optional
+
+ if 'b' in self.smod: # all blanks are optional
+ xdata = ''.join(data.split())
+ value = ''.join(value.split())
+
+ if 'c' in self.smod: # all blanks are optional
xdata = xdata.upper()
value = value.upper()
- if 'B' in self.smod : # compact blanks
- data = ' '.join( data.split() )
- if ' ' not in data : return None
+
+ if 'B' in self.smod: # compact blanks
+ data = ' '.join(data.split())
+ if ' ' not in data:
+ return None
else:
xdata = data
try:
@@ -253,40 +258,46 @@
elif mod == '&' : result = data & value
elif mod == '^' : result = (data & (~value)) == 0
elif mod == 'x' : result = 1
- else : raise MagicTestError(self.test)
- if result :
+ else:
+ raise MagicTestError(self.test)
+
+ if result:
zdata, zval = `data`, `value`
- if self.mtype[-6:]!='string' :
- try: zdata, zval = hex(data), hex(value)
- except: zdata, zval = `data`, `value`
- if 0 : print >>sys.stderr, '%s @%s %s:%s %s %s => %s (%s)' % (
- '>'*self.level, self.offset,
- zdata, self.mtype, `mod`, zval, `result`,
- self.message
- )
+
+ if self.mtype[-6:] != 'string':
+ try:
+ zdata = hex(data)
+ zval = hex(value)
+ except:
+ zdata = `data`
+ zval = `value`
+
return result
except:
raise MagicTestError('mtype=%s data=%s mod=%s value=%s' % (
`self.mtype`, `data`, `mod`, `value`))
def add(self,mt):
- if not isinstance(mt,MagicTest) :
- raise MagicTestError((mt,'incorrect subtest type %s'%(type(mt),)))
- if mt.level == self.level+1 :
- self.subtests.append( mt )
- elif self.subtests :
- self.subtests[-1].add( mt )
- elif mt.level > self.level+1 :
+ if not isinstance(mt, MagicTest):
+ raise MagicTestError((mt, 'incorrect subtest type %s'%(type(mt),)))
+
+ if mt.level == self.level+1:
+ self.subtests.append(mt)
+
+ elif self.subtests:
+ self.subtests[-1].add(mt)
+
+ elif mt.level > self.level+1:
# it's possible to get level 3 just after level 1 !!! :-(
level = self.level + 1
- while level < mt.level :
- xmt = MagicTest(None,'true','x','',line=self.line,level=level)
- self.add( xmt )
+ while level < mt.level:
+ xmt = MagicTest(None, 'true', 'x', '', line=self.line, level=level)
+ self.add(xmt)
level += 1
else:
- self.add( mt ) # retry...
+ self.add(mt) # retry...
else:
- raise MagicTestError((mt,'incorrect subtest level %s'%(`mt.level`,)))
+ raise MagicTestError((mt, 'incorrect subtest level %s'%(`mt.level`,)))
def last_test(self):
return self.subtests[-1]
@@ -330,30 +341,40 @@
def __init__(self,s):
self.source = s
self.value = None
- self.relative = 0
+ self.relative = False
self.base = self.type = self.sign = self.offs = None
- m = Offset.pattern0.match( s )
- if m : # just a number
- if s[0] == '&' :
- self.relative, self.value = 1, int( s[1:], 0 )
+
+ m = Offset.pattern0.match(s)
+ if m: # just a number
+ if s[0] == '&':
+ self.relative = True
+ self.value = int(s[1:], 0)
else:
self.value = int( s, 0 )
return
- m = Offset.pattern1.match( s )
- if m : # real indirect offset
+
+ m = Offset.pattern1.match(s)
+ if m: # real indirect offset
try:
self.base = m.group('base')
- if self.base[0] == '&' :
- self.relative, self.base = 1, int( self.base[1:], 0 )
+ if self.base[0] == '&':
+ self.relative = True
+ self.base = int(self.base[1:], 0)
else:
- self.base = int( self.base, 0 )
- if m.group('type') : self.type = m.group('type')[1:]
+ self.base = int(self.base, 0)
+
+ if m.group('type'):
+ self.type = m.group('type')[1:]
+
self.sign = m.group('sign')
- if m.group('off') : self.offs = int( m.group('off'), 0 )
- if self.sign == '-' : self.offs = 0 - self.offs
+ if m.group('off'):
+ self.offs = int( m.group('off'), 0 )
+
+ if self.sign == '-':
+ self.offs = 0 - self.offs
except:
- print >>sys.stderr, '$$', m.groupdict()
- raise
+ raise OffsetError(m.groupdict())
+
return
raise OffsetError(`s`)
@@ -369,7 +390,10 @@
frmt = Offset.pos_format.get(self.type, 'I')
size = struct.calcsize(frmt)
data = struct.unpack(frmt, file.read(size))
- if self.offs : data += self.offs
+
+ if self.offs:
+ data += self.offs
+
return data
finally:
file.seek(pos, 0)
Modified: z3c.filetype/trunk/src/z3c/filetype/size.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/size.py 2007-12-19 10:59:55 UTC (rev 82356)
+++ z3c.filetype/trunk/src/z3c/filetype/size.py 2007-12-19 13:09:28 UTC (rev 82357)
@@ -15,13 +15,12 @@
$Id$
"""
-import os
-import stat
-import struct
+import os, struct
import zope.i18nmessageid
from zope import component, interface
from zope.size import byteDisplay
-from z3c.filetype.interfaces import filetypes, IImageSized
+from zope.cachedescriptors.property import Lazy
+from z3c.filetype.interfaces import filetypes, IImageSized, IFileData
_ = zope.i18nmessageid.MessageFactory("zope")
@@ -29,16 +28,16 @@
class ImageFileSized(object):
interface.implements(IImageSized)
- def __init__(self, image):
- self._image = image
+ def __init__(self, context):
+ self.context = context
- @property
+ @Lazy
def bytes(self):
- try:
- return len(self._image.data)
- except TypeError:
- data = self._image.data
- return int(os.fstat(data.fileno())[stat.ST_SIZE])
+ file = IFileData(self.context).open('r')
+ file.seek(0, 2)
+ len = file.tell()
+ file.close()
+ return len
def sizeForSorting(self):
'''See `ISized`'''
@@ -54,6 +53,7 @@
w = '?'
if h < 0:
h = '?'
+
byte_size = byteDisplay(self.bytes)
mapping = byte_size.mapping
if mapping is None:
@@ -68,9 +68,11 @@
component.adapts(filetypes.IGIFFile)
def getImageSize(self):
- data = self._image.data
- data.seek(0)
- data = data.read(24)
+ file = IFileData(self.context).open('r')
+ file.seek(0)
+ data = file.read(24)
+ file.close()
+
size = len(data)
width = -1
height = -1
@@ -79,6 +81,7 @@
w, h = struct.unpack("<HH", data[6:10])
width = int(w)
height = int(h)
+
return width, height
@@ -86,9 +89,11 @@
component.adapts(filetypes.IPNGFile)
def getImageSize(self):
- data = self._image.data
- data.seek(0)
- data = data.read(24)
+ file = IFileData(self.context).open('r')
+ file.seek(0)
+ data = file.read(24)
+ file.close()
+
size = len(data)
height = -1
width = -1
@@ -112,8 +117,9 @@
component.adapts(filetypes.IJPGFile)
def getImageSize(self):
- data = self._image.data
+ data = IFileData(self.context).open('r')
data.seek(2)
+
size = self.bytes
height = -1
width = -1
@@ -122,17 +128,21 @@
w = -1
h = -1
while (b and ord(b) != 0xDA):
- while (ord(b) != 0xFF):
+ while (ord(b) != 0xFF):
b = data.read(1)
- while (ord(b) == 0xFF):
+
+ while (ord(b) == 0xFF):
b = data.read(1)
+
if (ord(b) >= 0xC0 and ord(b) <= 0xC3):
data.read(3)
h, w = struct.unpack(">HH", data.read(4))
break
else:
data.read(int(struct.unpack(">H", data.read(2))[0])-2)
+
b = data.read(1)
+
width = int(w)
height = int(h)
@@ -141,4 +151,5 @@
except ValueError:
pass
+ data.close()
return width, height
Modified: z3c.filetype/trunk/src/z3c/filetype/tests.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/tests.py 2007-12-19 10:59:55 UTC (rev 82356)
+++ z3c.filetype/trunk/src/z3c/filetype/tests.py 2007-12-19 13:09:28 UTC (rev 82357)
@@ -15,17 +15,38 @@
$Id$
"""
-import doctest
-import unittest
+import doctest, unittest
+from zope.app.testing import setup
+from zope import component
+from z3c.filetype import size, typeablefile
+
+def setUp(test):
+ setup.placelessSetUp()
+
+ component.provideAdapter(size.JPGFileSized)
+ component.provideAdapter(size.GIFFileSized)
+ component.provideAdapter(size.PNGFileSized)
+
+ component.provideAdapter(typeablefile.TypeableFileData)
+ component.provideHandler(typeablefile.handleCreated)
+ component.provideHandler(typeablefile.handleModified)
+
+
def test_suite():
return unittest.TestSuite((
doctest.DocFileSuite(
'README.txt',
+ setUp=setUp, tearDown=setup.placelessTearDown,
optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
),
doctest.DocFileSuite(
+ 'converter.txt',
+ setUp=setUp, tearDown=setup.placelessTearDown,
+ optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+ ),
+ doctest.DocFileSuite(
'magic.txt',
optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
),
Added: z3c.filetype/trunk/src/z3c/filetype/typeablefile.py
===================================================================
--- z3c.filetype/trunk/src/z3c/filetype/typeablefile.py (rev 0)
+++ z3c.filetype/trunk/src/z3c/filetype/typeablefile.py 2007-12-19 13:09:28 UTC (rev 82357)
@@ -0,0 +1,57 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+""" ITypeableFile implementation
+
+$Id$
+"""
+from cStringIO import StringIO
+from zope import interface, component
+from zope.lifecycleevent.interfaces import IObjectCreatedEvent
+from zope.lifecycleevent.interfaces import IObjectModifiedEvent
+
+from z3c.filetype import api, interfaces
+
+
+class TypeableFile(object):
+ interface.implements(interfaces.ITypeableFile)
+
+ def __init__(self, data=''):
+ self.data = data
+ if data:
+ api.applyInterfaces(self)
+
+
+class TypeableFileData(object):
+ interface.implements(interfaces.IFileData)
+ component.adapts(interfaces.ITypeableFile)
+
+ def __init__(self, context):
+ self.context = context
+
+ def open(self, mode=''):
+ return StringIO(self.context.data)
+
+
+ at component.adapter(interfaces.ITypeableFile, IObjectCreatedEvent)
+def handleCreated(typeableFile, event):
+ """handles modification of data"""
+ api.applyInterfaces(typeableFile)
+
+
+ at component.adapter(interfaces.ITypeableFile, IObjectModifiedEvent)
+def handleModified(typeableFile, event):
+ """handles modification of data"""
+ if interfaces.IFileTypeModifiedEvent.providedBy(event):
+ return
+ api.applyInterfaces(typeableFile)
Property changes on: z3c.filetype/trunk/src/z3c/filetype/typeablefile.py
___________________________________________________________________
Name: svn:keywords
+ Id
More information about the Checkins
mailing list