[tryton-debian-vcs] relatorio branch debian updated. debian/0.7.1-1-4-ga40dc33

Mathias Behrle tryton-debian-vcs at alioth.debian.org
Fri Jan 19 18:08:19 UTC 2018


The following commit has been merged in the debian branch:
https://alioth.debian.org/plugins/scmgit/cgi-bin/gitweb.cgi/?p=tryton/relatorio.git;a=commitdiff;h=debian/0.7.1-1-4-ga40dc33

commit a40dc33eb437bb34f0e21572e956c60c0fcb7073
Author: Mathias Behrle <mathiasb at m9s.biz>
Date:   Fri Jan 19 18:42:27 2018 +0100

    Releasing debian version 0.8.0-1~exp1.
    
    Signed-off-by: Mathias Behrle <mathiasb at m9s.biz>

diff --git a/debian/changelog b/debian/changelog
index bb2894b..57c7b54 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,11 @@
+relatorio (0.8.0-1~exp1) experimental; urgency=medium
+
+  * Merging upstream version 0.8.0.
+  * Remove 01-add-pypi-magic.patch.
+  * Add python[3]-magic 2:0.4.15-1~exp1 to (Build)-Depends.
+
+ -- Mathias Behrle <mathiasb at m9s.biz>  Fri, 19 Jan 2018 18:38:25 +0100
+
 relatorio (0.7.1-1) unstable; urgency=medium
 
   * Use the preferred https URL format in the copyright file.
commit 43a62306e008fd571432be0b8be1b5d658808573
Author: Mathias Behrle <mathiasb at m9s.biz>
Date:   Fri Jan 19 18:38:09 2018 +0100

    Add python[3]-magic 2:0.4.15-1~exp1 to (Build)-Depends.

diff --git a/debian/control b/debian/control
index 2a62a90..70e03c7 100644
--- a/debian/control
+++ b/debian/control
@@ -10,12 +10,14 @@ Build-Depends:
  python-genshi,
  python-lxml,
  python-nose,
+ python-magic (>= 2:0.4.15-1~exp1),
  python-pycha,
  python-setuptools,
  python-yaml,
  python3,
  python3-genshi,
  python3-lxml,
+ python3-magic (>= 2:0.4.15-1~exp1),
  python3-nose,
  python3-setuptools,
  python3-yaml,
@@ -30,6 +32,7 @@ Architecture: all
 Depends:
  python-genshi,
  python-lxml,
+ python-magic (>= 2:0.4.15-1~exp1),
  python-pkg-resources,
  ${misc:Depends},
  ${python:Depends},
@@ -46,6 +49,7 @@ Architecture: all
 Depends:
  python3-genshi,
  python3-lxml,
+ python3-magic (>= 2:0.4.15-1~exp1),
  python3-pkg-resources,
  ${misc:Depends},
  ${python3:Depends},
commit 13411dce45525ca5f4be35e24db9c3254f69dae5
Author: Mathias Behrle <mathiasb at m9s.biz>
Date:   Fri Jan 19 18:30:44 2018 +0100

    Remove 01-add-pypi-magic.patch.
    
    python-magic from pypi with compatibility layer for file-magic has hit
    experimental. Thanks to Adam Hupp and Christoph Biedl!

diff --git a/debian/patches/01-add-pypi-magic.patch b/debian/patches/01-add-pypi-magic.patch
deleted file mode 100644
index 804bdb9..0000000
--- a/debian/patches/01-add-pypi-magic.patch
+++ /dev/null
@@ -1,343 +0,0 @@
-Description: Add python-magic code copy from Pypi.
- Adding this code copy because of the (hopefully) ongoing merge
- of python-magic(file) and python-magic(pypi). The first being available
- in Debian as part of the file package, the latter is currently discussed
- to replace the former by adding a compatibility layer.
- This patch is subject to be removed, once python-magic from pypi (or an
- equivalent alternative) is available.
- Relevant discussions:
- https://lists.debian.org/debian-python/2017/09/msg00008.html
- https://lists.debian.org/debian-python/2017/09/msg00015.html
- https://lists.debian.org/debian-python/2017/10/msg00021.html
-Author: Mathias Behrle <mathiasb at m9s.biz>
-Bug-Debian: https://bugs.debian.org/877849
-Forwarded: not-needed
-Last-Update: 2017-11-02
-
---- /dev/null
-+++ b/thirdparty/magic/magic.py
-@@ -0,0 +1,296 @@
-+"""
-+magic is a wrapper around the libmagic file identification library.
-+
-+See README for more information.
-+
-+Usage:
-+
-+>>> import magic
-+>>> magic.from_file("testdata/test.pdf")
-+'PDF document, version 1.2'
-+>>> magic.from_file("testdata/test.pdf", mime=True)
-+'application/pdf'
-+>>> magic.from_buffer(open("testdata/test.pdf").read(1024))
-+'PDF document, version 1.2'
-+>>>
-+
-+
-+"""
-+
-+import sys
-+import glob
-+import os.path
-+import ctypes
-+import ctypes.util
-+import threading
-+
-+from ctypes import c_char_p, c_int, c_size_t, c_void_p
-+
-+
-+class MagicException(Exception):
-+    def __init__(self, message):
-+        super(MagicException, self).__init__(message)
-+        self.message = message
-+
-+
-+class Magic:
-+    """
-+    Magic is a wrapper around the libmagic C library.
-+
-+    """
-+
-+    def __init__(self, mime=False, magic_file=None, mime_encoding=False,
-+                 keep_going=False, uncompress=False):
-+        """
-+        Create a new libmagic wrapper.
-+
-+        mime - if True, mimetypes are returned instead of textual descriptions
-+        mime_encoding - if True, codec is returned
-+        magic_file - use a mime database other than the system default
-+        keep_going - don't stop at the first match, keep going
-+        uncompress - Try to look inside compressed files.
-+        """
-+        self.flags = MAGIC_NONE
-+        if mime:
-+            self.flags |= MAGIC_MIME
-+        if mime_encoding:
-+            self.flags |= MAGIC_MIME_ENCODING
-+        if keep_going:
-+            self.flags |= MAGIC_CONTINUE
-+
-+        if uncompress:
-+            self.flags |= MAGIC_COMPRESS
-+
-+        self.cookie = magic_open(self.flags)
-+        self.lock = threading.Lock()
-+        
-+        magic_load(self.cookie, magic_file)
-+
-+    def from_buffer(self, buf):
-+        """
-+        Identify the contents of `buf`
-+        """
-+        with self.lock:
-+            try:
-+                return maybe_decode(magic_buffer(self.cookie, buf))
-+            except MagicException as e:
-+                return self._handle509Bug(e)
-+
-+    def from_file(self, filename):
-+        # raise FileNotFoundException or IOError if the file does not exist
-+        with open(filename):
-+            pass
-+        with self.lock:
-+            try:
-+                return maybe_decode(magic_file(self.cookie, filename))
-+            except MagicException as e:
-+                return self._handle509Bug(e)
-+
-+    def _handle509Bug(self, e):
-+        # libmagic 5.09 has a bug where it might fail to identify the
-+        # mimetype of a file and returns null from magic_file (and
-+        # likely _buffer), but also does not return an error message.
-+        if e.message is None and (self.flags & MAGIC_MIME):
-+            return "application/octet-stream"
-+        else:
-+            raise e
-+        
-+    def __del__(self):
-+        # no _thread_check here because there can be no other
-+        # references to this object at this point.
-+
-+        # during shutdown magic_close may have been cleared already so
-+        # make sure it exists before using it.
-+
-+        # the self.cookie check should be unnecessary and was an
-+        # incorrect fix for a threading problem, however I'm leaving
-+        # it in because it's harmless and I'm slightly afraid to
-+        # remove it.
-+        if self.cookie and magic_close:
-+            magic_close(self.cookie)
-+            self.cookie = None
-+
-+_instances = {}
-+
-+def _get_magic_type(mime):
-+    i = _instances.get(mime)
-+    if i is None:
-+        i = _instances[mime] = Magic(mime=mime)
-+    return i
-+
-+def from_file(filename, mime=False):
-+    """"
-+    Accepts a filename and returns the detected filetype.  Return
-+    value is the mimetype if mime=True, otherwise a human readable
-+    name.
-+
-+    >>> magic.from_file("testdata/test.pdf", mime=True)
-+    'application/pdf'
-+    """
-+    m = _get_magic_type(mime)
-+    return m.from_file(filename)
-+
-+def from_buffer(buffer, mime=False):
-+    """
-+    Accepts a binary string and returns the detected filetype.  Return
-+    value is the mimetype if mime=True, otherwise a human readable
-+    name.
-+
-+    >>> magic.from_buffer(open("testdata/test.pdf").read(1024))
-+    'PDF document, version 1.2'
-+    """
-+    m = _get_magic_type(mime)
-+    return m.from_buffer(buffer)
-+
-+
-+
-+
-+libmagic = None
-+# Let's try to find magic or magic1
-+dll = ctypes.util.find_library('magic') or ctypes.util.find_library('magic1') or ctypes.util.find_library('cygmagic-1')
-+
-+# This is necessary because find_library returns None if it doesn't find the library
-+if dll:
-+    libmagic = ctypes.CDLL(dll)
-+
-+if not libmagic or not libmagic._name:
-+    windows_dlls = ['magic1.dll','cygmagic-1.dll']
-+    platform_to_lib = {'darwin': ['/opt/local/lib/libmagic.dylib',
-+                                  '/usr/local/lib/libmagic.dylib'] +
-+                         # Assumes there will only be one version installed
-+                         glob.glob('/usr/local/Cellar/libmagic/*/lib/libmagic.dylib'),
-+                       'win32': windows_dlls,
-+                       'cygwin': windows_dlls,
-+                       'linux': ['libmagic.so.1'],    # fallback for some Linuxes (e.g. Alpine) where library search does not work
-+                      }
-+    platform = 'linux' if sys.platform.startswith('linux') else sys.platform
-+    for dll in platform_to_lib.get(platform, []):
-+        try:
-+            libmagic = ctypes.CDLL(dll)
-+            break
-+        except OSError:
-+            pass
-+
-+if not libmagic or not libmagic._name:
-+    # It is better to raise an ImportError since we are importing magic module
-+    raise ImportError('failed to find libmagic.  Check your installation')
-+
-+magic_t = ctypes.c_void_p
-+
-+def errorcheck_null(result, func, args):
-+    if result is None:
-+        err = magic_error(args[0])
-+        raise MagicException(err)
-+    else:
-+        return result
-+
-+def errorcheck_negative_one(result, func, args):
-+    if result is -1:
-+        err = magic_error(args[0])
-+        raise MagicException(err)
-+    else:
-+        return result
-+
-+
-+# return str on python3.  Don't want to unconditionally
-+# decode because that results in unicode on python2
-+def maybe_decode(s):
-+    if str == bytes:
-+        return s
-+    else:
-+        return s.decode('utf-8')
-+    
-+def coerce_filename(filename):
-+    if filename is None:
-+        return None
-+
-+    # ctypes will implicitly convert unicode strings to bytes with
-+    # .encode('ascii').  If you use the filesystem encoding 
-+    # then you'll get inconsistent behavior (crashes) depending on the user's
-+    # LANG environment variable
-+    is_unicode = (sys.version_info[0] <= 2 and
-+                  isinstance(filename, unicode)) or \
-+                  (sys.version_info[0] >= 3 and
-+                   isinstance(filename, str))
-+    if is_unicode:
-+        return filename.encode('utf-8')
-+    else:
-+        return filename
-+
-+magic_open = libmagic.magic_open
-+magic_open.restype = magic_t
-+magic_open.argtypes = [c_int]
-+
-+magic_close = libmagic.magic_close
-+magic_close.restype = None
-+magic_close.argtypes = [magic_t]
-+
-+magic_error = libmagic.magic_error
-+magic_error.restype = c_char_p
-+magic_error.argtypes = [magic_t]
-+
-+magic_errno = libmagic.magic_errno
-+magic_errno.restype = c_int
-+magic_errno.argtypes = [magic_t]
-+
-+_magic_file = libmagic.magic_file
-+_magic_file.restype = c_char_p
-+_magic_file.argtypes = [magic_t, c_char_p]
-+_magic_file.errcheck = errorcheck_null
-+
-+def magic_file(cookie, filename):
-+    return _magic_file(cookie, coerce_filename(filename))
-+
-+_magic_buffer = libmagic.magic_buffer
-+_magic_buffer.restype = c_char_p
-+_magic_buffer.argtypes = [magic_t, c_void_p, c_size_t]
-+_magic_buffer.errcheck = errorcheck_null
-+
-+def magic_buffer(cookie, buf):
-+    return _magic_buffer(cookie, buf, len(buf))
-+
-+
-+_magic_load = libmagic.magic_load
-+_magic_load.restype = c_int
-+_magic_load.argtypes = [magic_t, c_char_p]
-+_magic_load.errcheck = errorcheck_negative_one
-+
-+def magic_load(cookie, filename):
-+    return _magic_load(cookie, coerce_filename(filename))
-+
-+magic_setflags = libmagic.magic_setflags
-+magic_setflags.restype = c_int
-+magic_setflags.argtypes = [magic_t, c_int]
-+
-+magic_check = libmagic.magic_check
-+magic_check.restype = c_int
-+magic_check.argtypes = [magic_t, c_char_p]
-+
-+magic_compile = libmagic.magic_compile
-+magic_compile.restype = c_int
-+magic_compile.argtypes = [magic_t, c_char_p]
-+
-+
-+
-+MAGIC_NONE = 0x000000 # No flags
-+MAGIC_DEBUG = 0x000001 # Turn on debugging
-+MAGIC_SYMLINK = 0x000002 # Follow symlinks
-+MAGIC_COMPRESS = 0x000004 # Check inside compressed files
-+MAGIC_DEVICES = 0x000008 # Look at the contents of devices
-+MAGIC_MIME = 0x000010 # Return a mime string
-+MAGIC_MIME_ENCODING = 0x000400 # Return the MIME encoding
-+MAGIC_CONTINUE = 0x000020 # Return all matches
-+MAGIC_CHECK = 0x000040 # Print warnings to stderr
-+MAGIC_PRESERVE_ATIME = 0x000080 # Restore access time on exit
-+MAGIC_RAW = 0x000100 # Don't translate unprintable chars
-+MAGIC_ERROR = 0x000200 # Handle ENOENT etc as real errors
-+
-+MAGIC_NO_CHECK_COMPRESS = 0x001000 # Don't check for compressed files
-+MAGIC_NO_CHECK_TAR = 0x002000 # Don't check for tar files
-+MAGIC_NO_CHECK_SOFT = 0x004000 # Don't check magic entries
-+MAGIC_NO_CHECK_APPTYPE = 0x008000 # Don't check application type
-+MAGIC_NO_CHECK_ELF = 0x010000 # Don't check for elf details
-+MAGIC_NO_CHECK_ASCII = 0x020000 # Don't check for ascii files
-+MAGIC_NO_CHECK_TROFF = 0x040000 # Don't check ascii/troff
-+MAGIC_NO_CHECK_FORTRAN = 0x080000 # Don't check ascii/fortran
-+MAGIC_NO_CHECK_TOKENS = 0x100000 # Don't check ascii/tokens
---- a/MANIFEST.in
-+++ b/MANIFEST.in
-@@ -6,3 +6,4 @@
- include relatorio/tests/*.odt
- include relatorio/tests/*.png
- include relatorio/tests/templates/*.tmpl
-+include thirdparty/magic/*
---- /dev/null
-+++ b/thirdparty/__init__.py
-@@ -0,0 +1 @@
-+# placeholder
-\ No newline at end of file
---- /dev/null
-+++ b/thirdparty/magic/__init__.py
-@@ -0,0 +1 @@
-+# placeholder
-\ No newline at end of file
---- a/relatorio/templates/opendocument.py
-+++ b/relatorio/templates/opendocument.py
-@@ -873,7 +873,7 @@
- 
- def extract_images(child, namespaces, start=0):
-     "Extract draw:image with binary-data and replace by href"
--    import magic
-+    from thirdparty.magic import magic
-     images = []
-     for i, image in enumerate(
-             child.xpath('//draw:image', namespaces=namespaces), start):
diff --git a/debian/patches/series b/debian/patches/series
deleted file mode 100644
index 3fa1687..0000000
--- a/debian/patches/series
+++ /dev/null
@@ -1 +0,0 @@
-01-add-pypi-magic.patch
commit 7647e5c8cf62cafb8f8fb66409f3aadb77e9aa89
Author: Mathias Behrle <mathiasb at m9s.biz>
Date:   Fri Jan 19 18:26:07 2018 +0100

    Merging upstream version 0.8.0.

diff --git a/.drone.yml b/.drone.yml
new file mode 100644
index 0000000..494e28b
--- /dev/null
+++ b/.drone.yml
@@ -0,0 +1,27 @@
+clone:
+    hg:
+        image: plugins/hg
+
+pipeline:
+    tox:
+        image: ${IMAGE}
+        commands:
+            - pip install tox
+            - apt-get update
+            - apt-get install -y python-cairo
+            - tox -e "${TOXENV}"
+        volumes:
+             - cache:/root/.cache
+
+matrix:
+    include:
+        - IMAGE: python:2.7
+          TOXENV: py27
+        - IMAGE: python:3.3
+          TOXENV: py33
+        - IMAGE: python:3.4
+          TOXENV: py34
+        - IMAGE: python:3.5
+          TOXENV: py35
+        - IMAGE: python:3.6
+          TOXENV: py36
diff --git a/CHANGES b/CHANGES
index 4b6c809..1cc4da5 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,9 @@
+0.8.0 - 20171204
+* Do not guess_type on styled cell content
+* Remove type attributes when guessing type
+* Add support for Python 3.6
+* Remove soft-page-break
+
 0.7.1 - 20171008
 * Remove warning when import plugin fails
 * Apply the guess type function on the correct node
diff --git a/PKG-INFO b/PKG-INFO
index 2c3578f..88e41da 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: relatorio
-Version: 0.7.1
+Version: 0.8.0
 Summary: A templating library able to output odt and pdf files
 Home-page: http://relatorio.tryton.org/
 Author: Cedric Krier
@@ -21,14 +21,6 @@ Description: Relatorio
         
         The documenation is provided at http://relatorio.readthedocs.org/
         
-        Note on PyCha
-        =============
-        
-        Since the 0.4.0 release, pycha upstream author included most of our patches. So
-        it is not necessary anymore to use our friendly fork of the project.
-        
-        For SVG support, you need PyCha 0.4.2 or later.
-        
 Platform: UNKNOWN
 Classifier: Development Status :: 4 - Beta
 Classifier: Intended Audience :: Developers
diff --git a/README b/README
index 76e37f3..2be76db 100644
--- a/README
+++ b/README
@@ -12,11 +12,3 @@ Documenation
 ============
 
 The documenation is provided at http://relatorio.readthedocs.org/
-
-Note on PyCha
-=============
-
-Since the 0.4.0 release, pycha upstream author included most of our patches. So
-it is not necessary anymore to use our friendly fork of the project.
-
-For SVG support, you need PyCha 0.4.2 or later.
diff --git a/doc/conf.py b/doc/conf.py
index 433badb..19ef52e 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -48,9 +48,9 @@ copyright = u'2015, Nicolas Évrard, CGaëtan de Menten, Cédric Krier'
 # built documents.
 #
 # The short X.Y version.
-version = '0.7'
+version = '0.8'
 # The full version, including alpha/beta/rc tags.
-release = '0.7.1'
+release = '0.8.0'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
diff --git a/examples/basic.odt b/examples/basic.odt
new file mode 100644
index 0000000..f82485e
Binary files /dev/null and b/examples/basic.odt differ
diff --git a/examples/basic.tex b/examples/basic.tex
new file mode 100644
index 0000000..5316a30
--- /dev/null
+++ b/examples/basic.tex
@@ -0,0 +1,21 @@
+\enableregime [utf-8]
+\mainlanguage [fr]
+
+\starttext
+
+Dear $o.customer.name,
+
+Here is the list of your purchases:
+
+\starttable[|l|l|l|l|l|]
+\HL
+\NC Name \VL Reference \VL Quantity \VL Unit Price \VL Amount \SR
+\HL
+{% for line in o.lines%} \
+\NC $line.item.name \VL $line.item.reference \VL $line.quantity
+\VL $line.item.price \VL $line.amount \SR
+\HL \
+{% end %}
+\stoptable
+
+\stoptext
diff --git a/examples/bouteille.png b/examples/bouteille.png
new file mode 100644
index 0000000..ce076c3
Binary files /dev/null and b/examples/bouteille.png differ
diff --git a/examples/columns.odt b/examples/columns.odt
new file mode 100644
index 0000000..4363581
Binary files /dev/null and b/examples/columns.odt differ
diff --git a/examples/common.py b/examples/common.py
new file mode 100644
index 0000000..1fd898f
--- /dev/null
+++ b/examples/common.py
@@ -0,0 +1,43 @@
+from os.path import join, dirname
+
+
+class Invoice(dict):
+
+    @property
+    def total(self):
+        return sum(l['amount'] for l in self['lines'])
+
+    @property
+    def vat(self):
+        return self.total * 0.21
+
+
+inv = Invoice(customer={'name': 'John Bonham',
+                        'address': {'street': 'Smirnov street',
+                                    'zip': 1000,
+                                    'city': 'Montreux'}},
+              lines=[{'item': {'name': 'Vodka 70cl',
+                               'reference': 'VDKA-001',
+                               'price': 10.34},
+                      'quantity': 7,
+                      'amount': 7 * 10.34},
+                     {'item': {'name': 'Cognac 70cl',
+                               'reference': 'CGNC-067',
+                               'price': 13.46},
+                      'quantity': 12,
+                      'amount': 12 * 13.46},
+                     {'item': {'name': 'Sparkling water 25cl',
+                               'reference': 'WATR-007',
+                               'price': 4},
+                      'quantity': 1,
+                      'amount': 4},
+                     {'item': {'name': 'Good customer',
+                               'reference': 'BONM-001',
+                               'price': -20},
+                      'quantity': 1,
+                      'amount': -20},
+                    ],
+              id='MZY-20080703',
+              status='late',
+              bottle=(open(join(dirname(__file__), 'bouteille.png'), 'rb'),
+                  'image/png'))
diff --git a/examples/complicated.odt b/examples/complicated.odt
new file mode 100644
index 0000000..c36ce6a
Binary files /dev/null and b/examples/complicated.odt differ
diff --git a/examples/demo_chart.py b/examples/demo_chart.py
new file mode 100644
index 0000000..f7fb567
--- /dev/null
+++ b/examples/demo_chart.py
@@ -0,0 +1,23 @@
+from os.path import abspath, join, dirname
+from relatorio import Report
+
+# test data
+from common import inv
+
+if __name__ == '__main__':
+    pie_report = Report(abspath(join(dirname(__file__), 'pie_chart')),
+            'image/png')
+    open(join(dirname(__file__), 'pie.png'), 'wb').write(
+            pie_report(o=inv).render().getvalue())
+    hbar_report = Report(abspath(join(dirname(__file__), 'hbar_chart')),
+            'image/svg')
+    open(join(dirname(__file__), 'hbar.svg'), 'wb').write(
+            hbar_report(o=inv).render().getvalue())
+    vbar_report = Report(abspath(join(dirname(__file__), 'vbar_chart')),
+            'image/svg')
+    open(join(dirname(__file__), 'vbar.svg'), 'wb').write(
+            vbar_report(o=inv).render().getvalue())
+    line_report = Report(abspath(join(dirname(__file__), 'line_chart')),
+            'image/png')
+    open(join(dirname(__file__), 'line.png'), 'wb').write(
+            line_report(o=inv).render().getvalue())
diff --git a/examples/demo_context.py b/examples/demo_context.py
new file mode 100644
index 0000000..5551532
--- /dev/null
+++ b/examples/demo_context.py
@@ -0,0 +1,14 @@
+from os.path import abspath, join, dirname
+from relatorio import Report
+
+# test data
+from common import inv
+
+# PDF
+if __name__ == '__main__':
+    print "generating output_basic.pdf... ",
+    report = Report(abspath(join(dirname(__file__), 'basic.tex')),
+        'application/pdf')
+    content = report(o=inv).render().getvalue()
+    open(join(dirname(__file__), 'output_basic.pdf'), 'wb').write(content)
+    print "done"
diff --git a/examples/demo_odf.py b/examples/demo_odf.py
new file mode 100644
index 0000000..66c4286
--- /dev/null
+++ b/examples/demo_odf.py
@@ -0,0 +1,68 @@
+from os.path import abspath, join, dirname
+from relatorio import Report
+from relatorio.templates import opendocument
+
+# test data
+from common import inv
+
+ODT_MIME = 'application/vnd.oasis.opendocument.text'
+ODS_MIME = 'application/vnd.oasis.opendocument.spreadsheet'
+ODP_MIME = 'application/vnd.oasis.opendocument.presentation'
+
+if __name__ == '__main__':
+    pwd = dirname(__file__)
+    # ODT
+    print "generating output_basic.odt... ",
+    report = Report(abspath(join(dirname(__file__), 'basic.odt')), ODT_MIME)
+    content = report(o=inv).render().getvalue()
+    open(join(pwd, 'output_basic.odt'), 'wb').write(content)
+    print "done"
+
+    # we could also use an opendocument template directly
+    print "generating output_template_basic.odt... ",
+    template = opendocument.Template(source='',
+        filepath=abspath(join(pwd, 'basic.odt')))
+    content = template.generate(o=inv).render().getvalue()
+    open(join(pwd, 'output_template_basic.odt'), 'wb').write(content)
+    print "done"
+
+    print "generating output_complicated.odt... ",
+    # Add a chart to the invoice
+    inv['chart'] = (
+        Report(abspath(join(pwd, 'pie_chart')), 'image/png'), 'image/png')
+    report = Report(abspath(join(pwd, 'complicated.odt')), ODT_MIME)
+    try:
+        content = report(o=inv).render().getvalue()
+    except NotImplementedError:
+        print "skipped"
+    else:
+        open(join(pwd, 'output_complicated.odt'), 'wb').write(content)
+        print "done"
+
+    print "generating output_columns.odt... ",
+    report = Report(abspath(join(pwd, 'columns.odt')), ODT_MIME)
+    lst = [[], ['i'], ['a', 'b'], [1, 2, 3], ['I', 'II', 'III', 'IV']]
+    titles = ['first', 'second', 'third', 'fourth']
+    content = report(titles=titles, lst=lst).render().getvalue()
+    open(join(pwd, 'output_columns.odt'), 'wb').write(content)
+    print "done"
+
+    # ODS
+    print "generating output_pivot.ods... ",
+    report = Report(abspath(join(pwd, 'pivot.ods')), ODS_MIME)
+    content = report(o=inv).render().getvalue()
+    open(join(pwd, 'output_pivot.ods'), 'wb').write(content)
+    print "done"
+
+    print "generating output_sheets.ods... ",
+    report = Report(abspath(join(pwd, 'demo_sheets.ods')), ODS_MIME)
+    content = report(lst=lst).render().getvalue()
+    open(join(pwd, 'output_sheets.ods'), 'wb').write(content)
+    print "done"
+
+    # ODP
+    print "generating output_presentation.odp... ",
+    report = Report(abspath(join(pwd, 'presentation.odp')), ODP_MIME)
+    content = report(o=inv).render().getvalue()
+    open(join(pwd, 'output_presentation.odp'), 'wb').write(content)
+    print "done"
diff --git a/examples/demo_repository.py b/examples/demo_repository.py
new file mode 100644
index 0000000..cb1e35a
--- /dev/null
+++ b/examples/demo_repository.py
@@ -0,0 +1,32 @@
+import relatorio
+from common import Invoice, inv
+from os.path import join, dirname
+
+ODT_MIME = 'application/vnd.oasis.opendocument.text'
+ODS_MIME = 'application/vnd.oasis.opendocument.spreadsheet'
+ODP_MIME = 'application/vnd.oasis.opendocument.presentation'
+
+repository = relatorio.ReportRepository()
+repository.add_report(Invoice, ODT_MIME, 'basic.odt', report_name='basic')
+repository.add_report(Invoice, ODT_MIME,
+                      'complicated.odt', report_name='complicated')
+repository.add_report(Invoice, ODS_MIME, 'pivot.ods', report_name='pivot')
+repository.add_report(Invoice, ODP_MIME,
+                      'presentation.odp', report_name='presentation')
+repository.add_report(Invoice, 'image/png', 'pie_chart', report_name='pie')
+
+if __name__ == '__main__':
+    # Add a chart to the invoice
+    inv['chart'] = repository.by_id(Invoice, 'pie')[:2]
+
+    # Generate all reports on the invoice class
+    for report_name, ext in (('basic', '.odt'),
+                             ('complicated', '.odt'),
+                             ('pivot', '.ods'),
+                             ('presentation', '.odp')):
+        filename = 'output_%s%s' % (report_name, ext)
+        print "generating '%s'..." % filename,
+        report, mimetype, desc = repository.by_id(Invoice, report_name)
+        data = report(o=inv).render().getvalue()
+        open(join(dirname(__file__), filename), 'wb').write(data)
+        print "done"
diff --git a/examples/demo_sheets.ods b/examples/demo_sheets.ods
new file mode 100644
index 0000000..5797de4
Binary files /dev/null and b/examples/demo_sheets.ods differ
diff --git a/examples/hbar_chart b/examples/hbar_chart
new file mode 100644
index 0000000..0f6607b
--- /dev/null
+++ b/examples/hbar_chart
@@ -0,0 +1,32 @@
+options:
+  width: 600
+  height: 500
+  legend: 
+    hide: false
+    position: 
+      right: 40
+  padding: {bottom: 70, left: 70, right: 10, top: 10}
+  axis:
+    y:
+      interval: 15
+      padding: 5
+    x:
+      ticks:
+      {% for idx, line in enumerate(o.lines) %}
+        - v: $idx
+          label: $line.item.name
+      {% end %}
+chart:
+  type: hbar
+  output_type: svg
+  dataset:
+    - - Sales
+      -
+      {% for idx, line in enumerate(o.lines) %}
+        - [$idx, $line.amount]
+      {% end %}
+    - - Absolute sales
+      -
+      {% for idx, line in enumerate(o.lines) %}
+        - [$idx, ${abs(line.amount)}]
+      {% end %}
diff --git a/examples/line_chart b/examples/line_chart
new file mode 100644
index 0000000..1457845
--- /dev/null
+++ b/examples/line_chart
@@ -0,0 +1,27 @@
+options:
+  width: 600
+  height: 500
+  legend: 
+    hide: false
+    position: 
+      right: 40
+  padding: {bottom: 70, left: 70, right: 10, top: 10}
+  axis:
+    y:
+      interval: 10
+      padding: 10
+    x:
+      ticks:
+      {% for idx, line in enumerate(o.lines) %}
+        - v: $idx
+          label: $line.item.name
+      {% end %}
+chart:
+  type: line
+  output_type: png
+  dataset:
+    - - Sales
+      -
+      {% for idx, line in enumerate(o.lines) %}
+        - [$idx, $line.amount]
+      {% end %}
diff --git a/examples/pie_chart b/examples/pie_chart
new file mode 100644
index 0000000..4b24054
--- /dev/null
+++ b/examples/pie_chart
@@ -0,0 +1,15 @@
+options:
+    width: 600
+    height: 400
+    background: {hide: true}
+    legend: {hide: true}
+    axis: {labelFontSize: 14}
+    padding: {bottom: 10, left: 10, right: 10, top: 10}
+chart:
+    type: pie
+    output_type: png
+    dataset:
+    {% for line in o.lines %}
+      - - ${line.item.name}
+        - - [0, $line.amount]
+    {% end %}
diff --git a/examples/pivot.ods b/examples/pivot.ods
new file mode 100644
index 0000000..0d0b40f
Binary files /dev/null and b/examples/pivot.ods differ
diff --git a/examples/presentation.odp b/examples/presentation.odp
new file mode 100644
index 0000000..7d08b92
Binary files /dev/null and b/examples/presentation.odp differ
diff --git a/examples/vbar_chart b/examples/vbar_chart
new file mode 100644
index 0000000..56c6790
--- /dev/null
+++ b/examples/vbar_chart
@@ -0,0 +1,32 @@
+options:
+  width: 600
+  height: 500
+  legend: 
+    hide: false
+    position: 
+      right: 40
+  padding: {bottom: 70, left: 70, right: 10, top: 10}
+  axis:
+    y:
+      interval: 20
+      padding: 5
+    x:
+      ticks:
+      {% for idx, line in enumerate(o.lines) %}
+        - v: $idx
+          label: $line.item.name
+      {% end %}
+chart:
+  type: vbar
+  output_type: svg
+  dataset:
+    - - Sales
+      -
+      {% for idx, line in enumerate(o.lines) %}
+        - [$idx, $line.amount]
+      {% end %}
+    - - Absolute sales
+      -
+      {% for idx, line in enumerate(o.lines) %}
+        - [$idx, ${abs(line.amount)}]
+      {% end %}
diff --git a/relatorio.egg-info/PKG-INFO b/relatorio.egg-info/PKG-INFO
index 2c3578f..88e41da 100644
--- a/relatorio.egg-info/PKG-INFO
+++ b/relatorio.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: relatorio
-Version: 0.7.1
+Version: 0.8.0
 Summary: A templating library able to output odt and pdf files
 Home-page: http://relatorio.tryton.org/
 Author: Cedric Krier
@@ -21,14 +21,6 @@ Description: Relatorio
         
         The documenation is provided at http://relatorio.readthedocs.org/
         
-        Note on PyCha
-        =============
-        
-        Since the 0.4.0 release, pycha upstream author included most of our patches. So
-        it is not necessary anymore to use our friendly fork of the project.
-        
-        For SVG support, you need PyCha 0.4.2 or later.
-        
 Platform: UNKNOWN
 Classifier: Development Status :: 4 - Beta
 Classifier: Intended Audience :: Developers
diff --git a/relatorio.egg-info/SOURCES.txt b/relatorio.egg-info/SOURCES.txt
index 5d118bc..07d1fee 100644
--- a/relatorio.egg-info/SOURCES.txt
+++ b/relatorio.egg-info/SOURCES.txt
@@ -1,3 +1,6 @@
+.drone.yml
+.hgignore
+.hgtags
 AUTHORS
 CHANGES
 LICENSE
@@ -5,6 +8,7 @@ MANIFEST.in
 README
 setup.cfg
 setup.py
+tox.ini
 doc/Makefile
 doc/basic.png
 doc/basic_generated.png
@@ -22,6 +26,23 @@ doc/pivot.png
 doc/pivot_rendered.png
 doc/quickexample.rst
 doc/relatorio_basic.png
+examples/basic.odt
+examples/basic.tex
+examples/bouteille.png
+examples/columns.odt
+examples/common.py
+examples/complicated.odt
+examples/demo_chart.py
+examples/demo_context.py
+examples/demo_odf.py
+examples/demo_repository.py
+examples/demo_sheets.ods
+examples/hbar_chart
+examples/line_chart
+examples/pie_chart
+examples/pivot.ods
+examples/presentation.odp
+examples/vbar_chart
 relatorio/__init__.py
 relatorio/reporting.py
 relatorio.egg-info/PKG-INFO
diff --git a/relatorio/__init__.py b/relatorio/__init__.py
index f2bb8da..4a9e345 100644
--- a/relatorio/__init__.py
+++ b/relatorio/__init__.py
@@ -12,5 +12,5 @@ and report together, find reports by mimetypes/name/python objects.
 from .reporting import MIMETemplateLoader, ReportRepository, Report
 from . import templates
 
-__version__ = '0.7.1'
+__version__ = '0.8.0'
 __all__ = ['MIMETemplateLoader', 'ReportRepository', 'Report', 'templates']
diff --git a/relatorio/templates/opendocument.py b/relatorio/templates/opendocument.py
index b72a075..c25284a 100644
--- a/relatorio/templates/opendocument.py
+++ b/relatorio/templates/opendocument.py
@@ -211,6 +211,26 @@ def wrap_nodes_between(first, last, new_parent):
     old_parent.remove(last)
 
 
+def remove_node_keeping_tail(node):
+    """Remove the node from the tree but keeping tail by appending to the
+    previous or parent node.
+    """
+    parent = node.getparent()
+    if node.tail:
+        previous = node.getprevious()
+        if previous is not None:
+            if not previous.tail:
+                previous.tail = node.tail
+            else:
+                previous.tail += node.tail
+        else:
+            if not parent.text:
+                parent.text = node.tail
+            else:
+                parent.text += node.tail
+    parent.remove(node)
+
+
 def update_py_attrs(node, value):
     """An helper function to update py_attrs of a node.
     """
@@ -311,6 +331,7 @@ class Template(MarkupTemplate):
         self.namespaces['py'] = GENSHI_URI
         self.namespaces['relatorio'] = RELATORIO_URI
 
+        self._remove_soft_page_break(tree)
         self._invert_style(tree)
         self._handle_relatorio_tags(tree)
         self._handle_images(tree)
@@ -318,6 +339,17 @@ class Template(MarkupTemplate):
         self._escape_values(tree)
         return BytesIO(lxml.etree.tostring(tree))
 
+    def _remove_soft_page_break(self, tree):
+        "remove soft-page-break tag and use-soft-page-break attribute"
+        xpath_expr = "//text:soft-page-break"
+        for node in tree.xpath(xpath_expr, namespaces=self.namespaces):
+            remove_node_keeping_tail(node)
+
+        xpath_expr = "//office:text[@text:use-soft-page-breaks]"
+        text_namespace = self.namespaces['text']
+        for node in tree.xpath(xpath_expr, namespaces=self.namespaces):
+            node.attrib.pop('{%s}use-soft-page-breaks' % text_namespace)
+
     def _invert_style(self, tree):
         "inverts the text:a and text:span"
         xpath_expr = "//text:a[starts-with(@xlink:href, 'relatorio://')]" \
@@ -396,6 +428,16 @@ class Template(MarkupTemplate):
         table_namespace = self.namespaces['table']
         table_row_tag = '{%s}table-row' % table_namespace
         table_cell_tag = '{%s}table-cell' % table_namespace
+        text_namespace = self.namespaces['text']
+        text_style_attributes = [s % text_namespace for s in [
+                '{%s}class-names', '{%s}cond-style-name', '{%s}style-name']]
+
+        office_value = '{%s}value' % self.namespaces['office']
+        office_valuetype = '{%s}value-type' % self.namespaces['office']
+        if 'calcext' in self.namespaces:
+            calcext_valuetype = '{%s}value-type' % self.namespaces['calcext']
+        else:
+            calcext_valuetype = None
 
         py_replace = '{%s}replace' % GENSHI_URI
 
@@ -473,13 +515,17 @@ class Template(MarkupTemplate):
                 # remove the directive node
                 r_node.getparent().remove(r_node)
             else:
+                def has_style(node):
+                    return any(attr in node.attrib
+                               for attr in text_style_attributes)
                 # It's not a genshi statement it's a python expression
                 parent = r_node.getparent()
                 grand_parent = parent.getparent()
                 # Guess type only if it is the only value in the cell
+                # and its parent has no style
                 if (grand_parent is None
                         or grand_parent.tag != table_cell_tag
-                        ) or len(parent) != 1:
+                        ) or len(grand_parent) != 1 or has_style(parent):
                     r_node.attrib[py_replace] = expr
                     continue
 
@@ -491,6 +537,11 @@ class Template(MarkupTemplate):
                 dico = ('__relatorio_guess_type('
                         '__relatorio_store_cache(%s, %s))')
                 update_py_attrs(grand_parent, dico % (cache_id, expr))
+                for attr in [office_value,
+                             office_valuetype,
+                             calcext_valuetype]:
+                    if attr:
+                        grand_parent.attrib.pop(attr, None)
 
     def _handle_column_loops(self, statement, ancestor, opening,
                              outer_o_node, outer_c_node):
diff --git a/relatorio/tests/test_odt.py b/relatorio/tests/test_odt.py
index 2b22087..ca5f5b4 100644
--- a/relatorio/tests/test_odt.py
+++ b/relatorio/tests/test_odt.py
@@ -23,7 +23,7 @@
 
 import os
 import unittest
-from io import StringIO
+from io import StringIO, BytesIO
 
 import lxml.etree
 from genshi.filters import Translator
@@ -31,7 +31,7 @@ from genshi.core import PI
 from genshi.template.eval import UndefinedError
 
 from relatorio.templates.opendocument import Template, GENSHI_EXPR,\
-    GENSHI_URI, RELATORIO_URI, fod2od
+    GENSHI_URI, RELATORIO_URI, fod2od, remove_node_keeping_tail
 
 OO_TABLE_NS = "urn:oasis:names:tc:opendocument:xmlns:table:1.0"
 
@@ -339,3 +339,76 @@ class TestOOTemplating(unittest.TestCase):
         with open(filepath, mode='rb') as source:
             oot = Template(source)
             oot.generate(**self.data)
+
+
+class TestRemoveNodeKeepingTail(unittest.TestCase):
+
+    def test_without_tail(self):
+        "Testing remove_node_keeping_tail without tail"
+        xml = b'''<parent><target/></parent>'''
+        tree = lxml.etree.parse(BytesIO(xml))
+        target = tree.getroot()[0]
+
+        remove_node_keeping_tail(target)
+
+        self.assertEqual(lxml.etree.tostring(tree), b'''<parent/>''')
+
+    def test_with_tail(self):
+        "Testing remove_node_keeping_tail with tail"
+        xml = b'''<parent><target/>tail</parent>'''
+        tree = lxml.etree.parse(BytesIO(xml))
+        target = tree.getroot()[0]
+
+        remove_node_keeping_tail(target)
+
+        self.assertEqual(
+            lxml.etree.tostring(tree),
+            b'''<parent>tail</parent>''')
+
+    def test_with_tail_and_parent_text(self):
+        "Testing remove_node_keeping_tail with tail and parent text"
+        xml = b'''<parent>text<target/>tail</parent>'''
+        tree = lxml.etree.parse(BytesIO(xml))
+        target = tree.getroot()[0]
+
+        remove_node_keeping_tail(target)
+
+        self.assertEqual(
+            lxml.etree.tostring(tree),
+            b'''<parent>texttail</parent>''')
+
+    def test_without_tail_and_with_previous(self):
+        "Testing remove_node_keeping_tail without tail and with previous"
+        xml = b'''<parent><previous/><target/></parent>'''
+        tree = lxml.etree.parse(BytesIO(xml))
+        target = tree.getroot()[1]
+
+        remove_node_keeping_tail(target)
+
+        self.assertEqual(
+            lxml.etree.tostring(tree),
+            b'''<parent><previous/></parent>''')
+
+    def test_with_tail_and_previous(self):
+        "Testing remove_node_keeping_tail with tail and previous"
+        xml = b'''<parent><previous/><target/>tail</parent>'''
+        tree = lxml.etree.parse(BytesIO(xml))
+        target = tree.getroot()[1]
+
+        remove_node_keeping_tail(target)
+
+        self.assertEqual(
+            lxml.etree.tostring(tree),
+            b'''<parent><previous/>tail</parent>''')
+
+    def test_with_tail_and_previous_tail(self):
+        "Testing remove_node_keeping_tail with tail and previous tail"
+        xml = b'''<parent><previous/>tail<target/>tail</parent>'''
+        tree = lxml.etree.parse(BytesIO(xml))
+        target = tree.getroot()[1]
+
+        remove_node_keeping_tail(target)
+
+        self.assertEqual(
+            lxml.etree.tostring(tree),
+            b'''<parent><previous/>tailtail</parent>''')
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..792676f
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,13 @@
+# Tox (http://tox.testrun.org/) is a tool for running tests
+# in multiple virtualenvs. This configuration file will run the
+# test suite on all supported python versions. To use it, "pip install tox"
+# and then run "tox" from this directory.
+
+[tox]
+envlist = py26, py27, py32, py33, py34, py35, py36, pypy
+
+[testenv]
+commands = {envpython} setup.py test
+deps =
+    pyyaml
+    pycha
-- 
relatorio



More information about the tryton-debian-vcs mailing list