Browse Source

Initial files

Marcos Dumay de Medeiros 6 years ago
commit
d6481cf670
8 changed files with 308 additions and 0 deletions
  1. 48 0
      .gitignore
  2. 4 0
      MANIFEST.in
  3. 0 0
      README
  4. 6 0
      README.rst
  5. 3 0
      setup.cfg
  6. 114 0
      setup.py
  7. 0 0
      src/officedocs/__init__.py
  8. 133 0
      src/officedocs/rtf.py

+ 48 - 0
.gitignore

@@ -0,0 +1,48 @@
+# Backup files
+*.~
+
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+
+# C extensions
+*.so
+
+# Distribution / packaging
+bin/
+build/
+develop-eggs/
+dist/
+eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+.tox/
+.coverage
+.cache
+nosetests.xml
+coverage.xml
+
+# Translations
+*.mo
+
+# Sphinx documentation
+docs/_build/
+
+#Virtualenv dir
+ENV/
+
+#Pycharm project
+.idea/

+ 4 - 0
MANIFEST.in

@@ -0,0 +1,4 @@
+include README.rst
+recursive-include src/officedocs *
+prune .git
+prune ENV

+ 0 - 0
README


+ 6 - 0
README.rst

@@ -0,0 +1,6 @@
+Office Document Generator
+=========================
+
+Tools for generating documments suitable for oppening in Microsoft Office.
+
+Currently, there's only an RTF template engine. Given an RTF document, with template expressions encoded inside brackets "[]", the officedocs.rtf.RtfTemplate class can evaluate those expressions, creating a file-like object to use elsewhere.

+ 3 - 0
setup.cfg

@@ -0,0 +1,3 @@
+[bdist_wheel]
+universal=1
+

+ 114 - 0
setup.py

@@ -0,0 +1,114 @@
+__author__ = 'marcos.medeiros'
+
+"""
+Officedocs installation script.
+"""
+
+# Always prefer setuptools over distutils
+from setuptools import setup, find_packages
+# To use a consistent encoding
+from codecs import open
+import os
+from os import path
+
+def _recursive_find(root, p):
+    full_p = path.join(root, p)
+    for f in os.listdir(full_p):
+        full_f = path.join(full_p, f)
+        val_f = path.join(p, f)
+        if path.isdir(full_f):
+            for ff in _recursive_find(root, val_f):
+                yield ff
+        else:
+             yield val_f
+
+
+here = path.abspath(path.dirname(__file__))
+docs = path.join('src', 'officedocs')
+docs_files = list(_recursive_find(os.path.join(here, docs), ''))
+
+# Get the long description from the relevant file
+with open(path.join(here, 'README.rst'), encoding='utf-8') as f:
+    long_description = f.read()
+
+setup(
+    name='officedocs',
+
+    version='0.0.1.dev1',
+
+    description='Quick creation of MS Office documents',
+    long_description=long_description,
+
+    # The project's main homepage.
+    url='https://marcosdumay.com/officedocs',
+
+    # Author details
+    author='Marcos Dumay de Medeiros',
+    author_email='marcos@marcosdumay.com',
+
+    # Choose your license
+    license='MIT',
+
+    # See https://pypi.python.org/pypi?%3Aaction=list_classifiers
+    classifiers=[
+        'Development Status :: 2 - Pre-Alpha',
+        'Intended Audience :: Developers',
+        'License :: OSI Approved :: MIT License',
+        'Natural Language :: English',
+        'Operating System :: OS Independent',
+        'Topic :: Office/Business :: Office Suites',
+        'Programming Language :: Python :: 2',
+        'Programming Language :: Python :: 2.6',
+        'Programming Language :: Python :: 2.7',
+        'Programming Language :: Python :: 3',
+        'Programming Language :: Python :: 3.2',
+        'Programming Language :: Python :: 3.3',
+        'Programming Language :: Python :: 3.4',
+    ],
+
+    # What does your project relate to?
+    keywords=('Microsoft Office Documents', 'RTF'),
+
+    # You can just specify the packages manually here if your project is
+    # simple. Or you can use find_packages().
+    packages=['officedocs'],
+    package_dir = {'': 'src'},
+
+    zip_safe = True,
+
+    # List run-time dependencies here.  These will be installed by pip when
+    # your project is installed. For an analysis of "install_requires" vs pip's
+    # requirements files see:
+    # https://packaging.python.org/en/latest/requirements.html
+    install_requires=[],
+
+    # List additional groups of dependencies here (e.g. development
+    # dependencies). You can install these using the following syntax,
+    # for example:
+    # $ pip install -e .[dev,test]
+    extras_require={},
+
+    # If there are data files included in your packages that need to be
+    # installed, specify them here.  If using Python 2.6 or less, then these
+    # have to be included in MANIFEST.in as well.
+
+    #include_package_data=True,
+    package_data={
+        'officedocs': docs_files,
+    },
+
+    # Although 'package_data' is the preferred approach, in some case you may
+    # need to place data files outside of your packages. See:
+    # http://docs.python.org/3.4/distutils/setupscript.html#installing-additional-files # noqa
+    # In this case, 'data_file' will be installed into '<sys.prefix>/my_data'
+    data_files=[],
+
+    # To provide executable scripts, use entry points in preference to the
+    # "scripts" keyword. Entry points provide cross-platform support and allow
+    # pip to create the appropriate form of executable for the target platform.
+    entry_points={
+        # 'console_scripts': [
+        #     'sample=sample:main',
+        # ],
+    },
+)

+ 0 - 0
src/officedocs/__init__.py


+ 133 - 0
src/officedocs/rtf.py

@@ -0,0 +1,133 @@
+__author__ = 'marcos.medeiros'
+
+
+class RtfTemplate(object):
+    """
+    A file object that when read fills an RTF template with context data.
+    The template variables must come between brackets "[]", instead of braces "{}",
+    and will be interpreted with the builtin format rules (that means you can reference
+    fields and elements, apply formatting rules (with comma ":") to the returned values, etc).
+
+    Use two open brackets to represent an open bracket. Close brackets without an equivalent
+    open bracket are repeated verbatim. Thus, to represent the text "[]", the original document
+    must contain the text "[[]".
+
+    Brackets were chosen because they aren't parsed by the RTF format, and thus can be inserted
+    directly from WYSIWYG editors.
+    """
+    def __init__(self, file, context):
+        """
+        Open the template for reading, replacing the variables with the values from the
+        given context.
+        :param file: Template that will be read.
+        :param context: Values that will be inserted at the place of the variables.
+        """
+        self.name = file
+        self.template = open(file, 'r')
+        self.encoding = self.template.encoding
+        self.context = context
+        self.pattern = ''
+        self.brackets = 0
+        self.partial = ''
+        self.softspace = 0
+
+    def format(self, template):
+        """
+        Formats the template with this object's context.
+        Notice that a template may contain only part of a variable,
+        what will make its value appear only at a later call of format.
+        :param template: Original template text.
+        :return: The formatted text.
+        """
+        ret = ''
+        for c in template:
+            if self.brackets == 0 and c != '[':
+                ret += c
+            else:
+                if c == '[':
+                    if self.brackets != 0:
+                        self.pattern += c
+                    self.brackets += 1
+                    if self.brackets == 2 and self.pattern == '[':
+                        ret += '['
+                        self.brackets = 0
+                        self.pattern = ''
+                elif c == ']':
+                    self.brackets -= 1
+                    if self.brackets == 0:
+                        ret += ('{'+self.pattern+'}').format(**self.context)
+                        self.pattern = ''
+                    else:
+                        self.pattern += c
+                else:
+                    self.pattern += c
+        return ret
+
+    def _next_with(self, func):
+        buf = func()
+        f = self.format(buf)
+        if buf and not f:
+            return self._next_with(func)
+        return f
+
+    def next(self):
+        return self._next_with(self.template.next)
+
+    def __next__(self):
+        return self._next_with(self.template.__next__)
+
+    def _read_by(self, func, size):
+        actual_size = size
+        if self.partial:
+            actual_size = size - len(self.partial)
+        if actual_size < 0 < size:
+            f = self.partial[0:size]
+            self.partial = self.partial[size:]
+            return f
+        buf = func(actual_size)
+        f = self.partial + self.format(buf)
+        if buf and not f:
+            return self.read(size)
+        if 0 < size < len(f):
+            self.partial = f[size:]
+            return f[0:size]
+        self.partial = ''
+        return f
+
+    def read(self, size=-1):
+        return self._read_by(self.template.read, size)
+
+    def readline(self, size):
+        return self._read_by(self.template.readline, size)
+
+    def close(self):
+        self.template.close()
+
+    def isatty(self):
+        return self.template.isatty()
+
+    def __iter__(self):
+        return self
+
+    @property
+    def closed(self):
+        return self.template.closed
+
+    @property
+    def newlines(self):
+        return self.template.newlines
+
+    @classmethod
+    def format_into(cls, template, context, file):
+        """
+        Formats the template, writing the resulting data into the given file.
+        :param template: Name of the template file.
+        :param context: Context where variables will be evaluated.
+        :param file: Name of the file that will be written with the formatted data.
+        """
+        t = RtfTemplate(template, context)
+        f = open(file, 'w')
+        for l in t:
+            f.write(l)
+        f.close()
+        t.close()