diff --git a/.gitignore b/.gitignore
index db4561e..222cbff 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,54 +1,4 @@
-# Byte-compiled / optimized / DLL files
-__pycache__/
-*.py[cod]
-
-# C extensions
-*.so
-
-# Distribution / packaging
-.Python
-env/
-build/
-develop-eggs/
-dist/
-downloads/
-eggs/
-lib/
-lib64/
-parts/
-sdist/
-var/
-*.egg-info/
-.installed.cfg
-*.egg
-
-# PyInstaller
-# Usually these files are written by a python script from a template
-# before PyInstaller builds the exe, so as to inject date/other infos into it.
-*.manifest
-*.spec
-
-# Installer logs
-pip-log.txt
-pip-delete-this-directory.txt
-
-# Unit test / coverage reports
-htmlcov/
-.tox/
-.coverage
-.cache
-nosetests.xml
-coverage.xml
-
-# Translations
-*.mo
-*.pot
-
-# Django stuff:
-*.log
-
-# Sphinx documentation
-docs/_build/
-
-# PyBuilder
-target/
+backup.sh
+*.pyc
+yael/__pycache__
+docs/build
diff --git a/LICENSE b/LICENSE
index c5e3e5a..8201a4c 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
The MIT License (MIT)
-Copyright (c) 2015 Alberto Pettarin
+Copyright (c) 2015 Alberto Pettarin (alberto@albertopettarin.it)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index 7819447..ae78327 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,84 @@
-# yael
-yael (Yet Another EPUB Library) is a Python library for reading, manipulating, and writing EPUB 2/3 files
+# yael
+
+**yael** (Yet Another EPUB Library) is a Python library for reading, manipulating, and writing EPUB 2/3 files.
+
+* Version: 0.0.1
+* Date: 2015-02-26
+* Developer: [Alberto Pettarin](http://www.albertopettarin.it/) ([contact](http://www.albertopettarin.it/contact.html))
+* License: the MIT License (MIT)
+
+
+## Usage
+
+This library is currently in **development**.
+
+The reading part is essentially complete, while the editing/writing is missing.
+
+Please do **NOT** download/use this code in production until it reaches v1.0.0.
+
+Feedback is welcome at any version number!
+
+See [`test/publication_test.py`](test/publication_test.py) and
+[`test/simpleepub_test.py`](test/simpleepub_test.py) for usage examples.
+
+
+## Documentation
+
+Online: [http://www.albertopettarin.it/yael/](http://www.albertopettarin.it/yael/)
+
+Generated from the source (requires sphinx):
+
+```
+$ cd docs
+$ make clean
+$ make html
+```
+
+
+## License
+
+**yael** is released under the terms of the MIT License. See the LICENSE file.
+
+
+## Supported Features
+
+Parsing nearly all of EPUB 2 and 3 OCF/OPF specifications, including:
+
+* EPUB 2 ` ` and `` OPF elements
+* EPUB 3 ` ` refines
+* EPUB 3 Media Overlays (SMIL files)
+* EPUB 3 Navigation Document (including `` elements other than `toc` and `landmarks`)
+* EPUB 3 Multiple Renditions
+* Asset obfuscation with either Adobe or IDPF algorithms
+
+Other useful stuff:
+
+* Loadind/saving publications in compressed, uncompressed, or in-memory form
+* Retrieving `viewport` information from Content Documents in FXL EPUB files
+* Building sub-objects from strings
+* Outputting a JSON representation of any object
+* Resolution of MARC Relator values
+
+
+## Limitations and Missing Features
+
+* EPUB OCF elements not parsed: `manifest.xml`, `rights.xml`, `signatures.xml`
+* EPUB 3 OPF elements not parsed: ``, ``
+* No EPUB CFI support
+* No editing of SVG Content Documents
+
+
+## Similar Libraries
+
+* [epub](https://pypi.python.org/pypi/epub)
+* [epubzilla](https://pypi.python.org/pypi/Epubzilla)
+* [py-clave](https://github.com/gabalese/py-clave)
+
+
+## Acknowledgments
+
+Many thanks to:
+
+* _Yael_, of course.
+
+[![Analytics](https://ga-beacon.appspot.com/UA-52776738-1/yael)](http://www.albertopettarin.it)
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 0000000..2661670
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,183 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+PAPER =
+BUILDDIR = build
+
+# User-friendly check for sphinx-build
+ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
+$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
+endif
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext
+
+help:
+ @echo "Please use \`make ' where is one of"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files named index.html in directories"
+ @echo " singlehtml to make a single large HTML file"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " qthelp to make HTML files and a qthelp project"
+ @echo " devhelp to make HTML files and a Devhelp project"
+ @echo " epub to make an epub"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " latexpdf to make LaTeX files and run them through pdflatex"
+ @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
+ @echo " text to make text files"
+ @echo " man to make manual pages"
+ @echo " texinfo to make Texinfo files"
+ @echo " info to make Texinfo files and run them through makeinfo"
+ @echo " gettext to make PO message catalogs"
+ @echo " changes to make an overview of all changed/added/deprecated items"
+ @echo " xml to make Docutils-native XML files"
+ @echo " pseudoxml to make pseudoxml-XML files for display purposes"
+ @echo " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
+ @echo " coverage to run coverage check of the documentation (if enabled)"
+
+clean:
+ rm -rf $(BUILDDIR)/*
+
+html:
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+ $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+singlehtml:
+ $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+ @echo
+ @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+pickle:
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+ @echo
+ @echo "Build finished; now you can process the pickle files."
+
+json:
+ $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+ @echo
+ @echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+ @echo
+ @echo "Build finished; now you can run HTML Help Workshop with the" \
+ ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+ $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+ @echo
+ @echo "Build finished; now you can run "qcollectiongenerator" with the" \
+ ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+ @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/yael.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/yael.qhc"
+
+devhelp:
+ $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+ @echo
+ @echo "Build finished."
+ @echo "To view the help file:"
+ @echo "# mkdir -p $$HOME/.local/share/devhelp/yael"
+ @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/yael"
+ @echo "# devhelp"
+
+epub:
+ $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+ @echo
+ @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+latex:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo
+ @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+ @echo "Run \`make' in that directory to run these through (pdf)latex" \
+ "(use \`make latexpdf' here to do that automatically)."
+
+latexpdf:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through pdflatex..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+latexpdfja:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through platex and dvipdfmx..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+text:
+ $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+ @echo
+ @echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+man:
+ $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+ @echo
+ @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+texinfo:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo
+ @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+ @echo "Run \`make' in that directory to run these through makeinfo" \
+ "(use \`make info' here to do that automatically)."
+
+info:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo "Running Texinfo files through makeinfo..."
+ make -C $(BUILDDIR)/texinfo info
+ @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+gettext:
+ $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+ @echo
+ @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
+changes:
+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+ @echo
+ @echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+ $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+ @echo "Testing of doctests in the sources finished, look at the " \
+ "results in $(BUILDDIR)/doctest/output.txt."
+
+coverage:
+ $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
+ @echo "Testing of coverage in the sources finished, look at the " \
+ "results in $(BUILDDIR)/coverage/python.txt."
+
+xml:
+ $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
+ @echo
+ @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
+
+pseudoxml:
+ $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
+ @echo
+ @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
diff --git a/docs/make.bat b/docs/make.bat
new file mode 100644
index 0000000..b30e30c
--- /dev/null
+++ b/docs/make.bat
@@ -0,0 +1,263 @@
+@ECHO OFF
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+ set SPHINXBUILD=sphinx-build
+)
+set BUILDDIR=build
+set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source
+set I18NSPHINXOPTS=%SPHINXOPTS% source
+if NOT "%PAPER%" == "" (
+ set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
+ set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
+)
+
+if "%1" == "" goto help
+
+if "%1" == "help" (
+ :help
+ echo.Please use `make ^` where ^ is one of
+ echo. html to make standalone HTML files
+ echo. dirhtml to make HTML files named index.html in directories
+ echo. singlehtml to make a single large HTML file
+ echo. pickle to make pickle files
+ echo. json to make JSON files
+ echo. htmlhelp to make HTML files and a HTML help project
+ echo. qthelp to make HTML files and a qthelp project
+ echo. devhelp to make HTML files and a Devhelp project
+ echo. epub to make an epub
+ echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
+ echo. text to make text files
+ echo. man to make manual pages
+ echo. texinfo to make Texinfo files
+ echo. gettext to make PO message catalogs
+ echo. changes to make an overview over all changed/added/deprecated items
+ echo. xml to make Docutils-native XML files
+ echo. pseudoxml to make pseudoxml-XML files for display purposes
+ echo. linkcheck to check all external links for integrity
+ echo. doctest to run all doctests embedded in the documentation if enabled
+ echo. coverage to run coverage check of the documentation if enabled
+ goto end
+)
+
+if "%1" == "clean" (
+ for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
+ del /q /s %BUILDDIR%\*
+ goto end
+)
+
+
+REM Check if sphinx-build is available and fallback to Python version if any
+%SPHINXBUILD% 2> nul
+if errorlevel 9009 goto sphinx_python
+goto sphinx_ok
+
+:sphinx_python
+
+set SPHINXBUILD=python -m sphinx.__init__
+%SPHINXBUILD% 2> nul
+if errorlevel 9009 (
+ echo.
+ echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+ echo.installed, then set the SPHINXBUILD environment variable to point
+ echo.to the full path of the 'sphinx-build' executable. Alternatively you
+ echo.may add the Sphinx directory to PATH.
+ echo.
+ echo.If you don't have Sphinx installed, grab it from
+ echo.http://sphinx-doc.org/
+ exit /b 1
+)
+
+:sphinx_ok
+
+
+if "%1" == "html" (
+ %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/html.
+ goto end
+)
+
+if "%1" == "dirhtml" (
+ %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
+ goto end
+)
+
+if "%1" == "singlehtml" (
+ %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
+ goto end
+)
+
+if "%1" == "pickle" (
+ %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the pickle files.
+ goto end
+)
+
+if "%1" == "json" (
+ %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the JSON files.
+ goto end
+)
+
+if "%1" == "htmlhelp" (
+ %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run HTML Help Workshop with the ^
+.hhp project file in %BUILDDIR%/htmlhelp.
+ goto end
+)
+
+if "%1" == "qthelp" (
+ %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run "qcollectiongenerator" with the ^
+.qhcp project file in %BUILDDIR%/qthelp, like this:
+ echo.^> qcollectiongenerator %BUILDDIR%\qthelp\yael.qhcp
+ echo.To view the help file:
+ echo.^> assistant -collectionFile %BUILDDIR%\qthelp\yael.ghc
+ goto end
+)
+
+if "%1" == "devhelp" (
+ %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished.
+ goto end
+)
+
+if "%1" == "epub" (
+ %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The epub file is in %BUILDDIR%/epub.
+ goto end
+)
+
+if "%1" == "latex" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "latexpdf" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ cd %BUILDDIR%/latex
+ make all-pdf
+ cd %~dp0
+ echo.
+ echo.Build finished; the PDF files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "latexpdfja" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ cd %BUILDDIR%/latex
+ make all-pdf-ja
+ cd %~dp0
+ echo.
+ echo.Build finished; the PDF files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "text" (
+ %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The text files are in %BUILDDIR%/text.
+ goto end
+)
+
+if "%1" == "man" (
+ %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The manual pages are in %BUILDDIR%/man.
+ goto end
+)
+
+if "%1" == "texinfo" (
+ %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
+ goto end
+)
+
+if "%1" == "gettext" (
+ %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
+ goto end
+)
+
+if "%1" == "changes" (
+ %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.The overview file is in %BUILDDIR%/changes.
+ goto end
+)
+
+if "%1" == "linkcheck" (
+ %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Link check complete; look for any errors in the above output ^
+or in %BUILDDIR%/linkcheck/output.txt.
+ goto end
+)
+
+if "%1" == "doctest" (
+ %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Testing of doctests in the sources finished, look at the ^
+results in %BUILDDIR%/doctest/output.txt.
+ goto end
+)
+
+if "%1" == "coverage" (
+ %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Testing of coverage in the sources finished, look at the ^
+results in %BUILDDIR%/coverage/python.txt.
+ goto end
+)
+
+if "%1" == "xml" (
+ %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The XML files are in %BUILDDIR%/xml.
+ goto end
+)
+
+if "%1" == "pseudoxml" (
+ %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
+ goto end
+)
+
+:end
diff --git a/docs/source/asset.rst b/docs/source/asset.rst
new file mode 100644
index 0000000..782efa2
--- /dev/null
+++ b/docs/source/asset.rst
@@ -0,0 +1,6 @@
+Asset
+=====
+
+.. automodule:: yael.asset
+ :members:
+ :private-members:
diff --git a/docs/source/conf.py b/docs/source/conf.py
new file mode 100644
index 0000000..a5bc31c
--- /dev/null
+++ b/docs/source/conf.py
@@ -0,0 +1,286 @@
+# -*- coding: utf-8 -*-
+#
+# yael documentation build configuration file, created by
+# sphinx-quickstart on Tue Feb 17 14:13:03 2015.
+#
+# This file is execfile()d with the current directory set to its
+# containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys
+import os
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.insert(0, os.path.abspath('.'))
+
+sys.path.insert(0, os.path.abspath('../../'))
+
+# -- General configuration ------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+ 'sphinx.ext.autodoc',
+ 'sphinx.ext.doctest',
+ 'sphinx.ext.todo',
+ 'sphinx.ext.coverage',
+ 'sphinx.ext.ifconfig',
+ 'sphinx.ext.viewcode',
+]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'yael'
+copyright = u'2015, Alberto Pettarin'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '0.1'
+# The full version, including alpha/beta/rc tags.
+release = '0.0.1'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = []
+
+# The reST default role (used for this markup: `text`) to use for all
+# documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+# If true, keep warnings as "system message" paragraphs in the built documents.
+#keep_warnings = False
+
+
+# -- Options for HTML output ----------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+html_theme = 'default'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# " v documentation".
+#html_title = None
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# Add any extra paths that contain custom files (such as robots.txt or
+# .htaccess) here, relative to this directory. These files are copied
+# directly to the root of the documentation.
+#html_extra_path = []
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Language to be used for generating the HTML full-text search index.
+# Sphinx supports the following languages:
+# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'
+# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr'
+#html_search_language = 'en'
+
+# A dictionary with options for the search language support, empty by default.
+# Now only 'ja' uses this config value
+#html_search_options = {'type': 'default'}
+
+# The name of a javascript file (relative to the configuration directory) that
+# implements a search results scorer. If empty, the default will be used.
+#html_search_scorer = 'scorer.js'
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'yaeldoc'
+
+# -- Options for LaTeX output ---------------------------------------------
+
+latex_elements = {
+# The paper size ('letterpaper' or 'a4paper').
+#'papersize': 'letterpaper',
+
+# The font size ('10pt', '11pt' or '12pt').
+#'pointsize': '10pt',
+
+# Additional stuff for the LaTeX preamble.
+#'preamble': '',
+
+# Latex figure (float) alignment
+#'figure_align': 'htbp',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+# author, documentclass [howto, manual, or own class]).
+latex_documents = [
+ ('index', 'yael.tex', u'yael Documentation',
+ u'Alberto Pettarin', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output ---------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ ('index', 'yael', u'yael Documentation',
+ [u'Alberto Pettarin'], 1)
+]
+
+# If true, show URL addresses after external links.
+#man_show_urls = False
+
+
+# -- Options for Texinfo output -------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ ('index', 'yael', u'yael Documentation',
+ u'Alberto Pettarin', 'yael', 'yael (Yet Another EPUB Library) is a Python library for reading, manipulating, and writing EPUB 2/3 files.',
+ 'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+#texinfo_appendices = []
+
+# If false, no module index is generated.
+#texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#texinfo_show_urls = 'footnote'
+
+# If true, do not generate a @detailmenu in the "Top" node's menu.
+#texinfo_no_detailmenu = False
diff --git a/docs/source/container.rst b/docs/source/container.rst
new file mode 100644
index 0000000..7626b55
--- /dev/null
+++ b/docs/source/container.rst
@@ -0,0 +1,6 @@
+Container
+=========
+
+.. automodule:: yael.container
+ :members:
+ :private-members:
diff --git a/docs/source/dc.rst b/docs/source/dc.rst
new file mode 100644
index 0000000..27b6af7
--- /dev/null
+++ b/docs/source/dc.rst
@@ -0,0 +1,6 @@
+DC
+==
+
+.. automodule:: yael.dc
+ :members:
+ :private-members:
diff --git a/docs/source/element.rst b/docs/source/element.rst
new file mode 100644
index 0000000..d3aae5e
--- /dev/null
+++ b/docs/source/element.rst
@@ -0,0 +1,6 @@
+Element
+=======
+
+.. automodule:: yael.element
+ :members:
+ :private-members:
diff --git a/docs/source/encdata.rst b/docs/source/encdata.rst
new file mode 100644
index 0000000..d483298
--- /dev/null
+++ b/docs/source/encdata.rst
@@ -0,0 +1,6 @@
+EncData
+=======
+
+.. automodule:: yael.encdata
+ :members:
+ :private-members:
diff --git a/docs/source/enckey.rst b/docs/source/enckey.rst
new file mode 100644
index 0000000..a91cbc1
--- /dev/null
+++ b/docs/source/enckey.rst
@@ -0,0 +1,6 @@
+EncKey
+======
+
+.. automodule:: yael.enckey
+ :members:
+ :private-members:
diff --git a/docs/source/encryption.rst b/docs/source/encryption.rst
new file mode 100644
index 0000000..5673e50
--- /dev/null
+++ b/docs/source/encryption.rst
@@ -0,0 +1,6 @@
+Encryption
+==========
+
+.. automodule:: yael.encryption
+ :members:
+ :private-members:
diff --git a/docs/source/epub.rst b/docs/source/epub.rst
new file mode 100644
index 0000000..0381dd2
--- /dev/null
+++ b/docs/source/epub.rst
@@ -0,0 +1,6 @@
+EPUB
+====
+
+.. automodule:: yael.epub
+ :members:
+ :private-members:
diff --git a/docs/source/index.rst b/docs/source/index.rst
new file mode 100644
index 0000000..4ae1f8b
--- /dev/null
+++ b/docs/source/index.rst
@@ -0,0 +1,76 @@
+Package **yael**
+================
+
+.. automodule:: yael
+
+
+Usage
+-----
+
+Lots of examples will be presented here.
+
+
+Classes
+-------
+
+.. toctree::
+ :maxdepth: 3
+
+ asset
+ container
+ dc
+ element
+ encdata
+ enckey
+ encryption
+ epub
+ jsonable
+ manifestation
+ marcrelator
+ mediatype
+ metadata
+ moaudio
+ modocument
+ mopar
+ moseq
+ motext
+ namespace
+ navdocument
+ navelement
+ navnode
+ ncxtoc
+ ncxtocnode
+ obfuscation
+ opfdc
+ opfguide
+ opfitem
+ opfitemref
+ opflink
+ opfmanifest
+ opfmeta2
+ opfmeta3
+ opfmetadata
+ opfmetadatum
+ opfpacdocument
+ opfreference
+ opfspine
+ pacdocument
+ parsing
+ publication
+ rendition
+ rmdocument
+ rmlocation
+ rmpoint
+ simpleepub
+ util
+
+
+
+Indices and Tables
+------------------
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
+
+
diff --git a/docs/source/jsonable.rst b/docs/source/jsonable.rst
new file mode 100644
index 0000000..23509ac
--- /dev/null
+++ b/docs/source/jsonable.rst
@@ -0,0 +1,6 @@
+JSONAble
+========
+
+.. automodule:: yael.jsonable
+ :members:
+ :private-members:
diff --git a/docs/source/manifestation.rst b/docs/source/manifestation.rst
new file mode 100644
index 0000000..277c7f8
--- /dev/null
+++ b/docs/source/manifestation.rst
@@ -0,0 +1,6 @@
+Manifestation
+=============
+
+.. automodule:: yael.manifestation
+ :members:
+ :private-members:
diff --git a/docs/source/marcrelator.rst b/docs/source/marcrelator.rst
new file mode 100644
index 0000000..09c4f15
--- /dev/null
+++ b/docs/source/marcrelator.rst
@@ -0,0 +1,6 @@
+MARCRelator
+===========
+
+.. automodule:: yael.marcrelator
+ :members:
+ :private-members:
diff --git a/docs/source/mediatype.rst b/docs/source/mediatype.rst
new file mode 100644
index 0000000..ec50b56
--- /dev/null
+++ b/docs/source/mediatype.rst
@@ -0,0 +1,6 @@
+MediaType
+=========
+
+.. automodule:: yael.mediatype
+ :members:
+ :private-members:
diff --git a/docs/source/metadata.rst b/docs/source/metadata.rst
new file mode 100644
index 0000000..ace8727
--- /dev/null
+++ b/docs/source/metadata.rst
@@ -0,0 +1,6 @@
+Metadata
+========
+
+.. automodule:: yael.metadata
+ :members:
+ :private-members:
diff --git a/docs/source/moaudio.rst b/docs/source/moaudio.rst
new file mode 100644
index 0000000..ef3efb7
--- /dev/null
+++ b/docs/source/moaudio.rst
@@ -0,0 +1,6 @@
+MOAudio
+=======
+
+.. automodule:: yael.moaudio
+ :members:
+ :private-members:
diff --git a/docs/source/modocument.rst b/docs/source/modocument.rst
new file mode 100644
index 0000000..e86be95
--- /dev/null
+++ b/docs/source/modocument.rst
@@ -0,0 +1,6 @@
+MODocument
+==========
+
+.. automodule:: yael.modocument
+ :members:
+ :private-members:
diff --git a/docs/source/mopar.rst b/docs/source/mopar.rst
new file mode 100644
index 0000000..674f9a2
--- /dev/null
+++ b/docs/source/mopar.rst
@@ -0,0 +1,6 @@
+MOPar
+=====
+
+.. automodule:: yael.mopar
+ :members:
+ :private-members:
diff --git a/docs/source/moseq.rst b/docs/source/moseq.rst
new file mode 100644
index 0000000..1dc507a
--- /dev/null
+++ b/docs/source/moseq.rst
@@ -0,0 +1,6 @@
+MOSeq
+=====
+
+.. automodule:: yael.moseq
+ :members:
+ :private-members:
diff --git a/docs/source/motext.rst b/docs/source/motext.rst
new file mode 100644
index 0000000..2fffce5
--- /dev/null
+++ b/docs/source/motext.rst
@@ -0,0 +1,6 @@
+MOText
+======
+
+.. automodule:: yael.motext
+ :members:
+ :private-members:
diff --git a/docs/source/namespace.rst b/docs/source/namespace.rst
new file mode 100644
index 0000000..30bbf44
--- /dev/null
+++ b/docs/source/namespace.rst
@@ -0,0 +1,6 @@
+Namespace
+=========
+
+.. automodule:: yael.namespace
+ :members:
+ :private-members:
diff --git a/docs/source/navdocument.rst b/docs/source/navdocument.rst
new file mode 100644
index 0000000..d369a8d
--- /dev/null
+++ b/docs/source/navdocument.rst
@@ -0,0 +1,6 @@
+NavDocument
+===========
+
+.. automodule:: yael.navdocument
+ :members:
+ :private-members:
diff --git a/docs/source/navelement.rst b/docs/source/navelement.rst
new file mode 100644
index 0000000..6859d9c
--- /dev/null
+++ b/docs/source/navelement.rst
@@ -0,0 +1,6 @@
+NavElement
+==========
+
+.. automodule:: yael.navelement
+ :members:
+ :private-members:
diff --git a/docs/source/navnode.rst b/docs/source/navnode.rst
new file mode 100644
index 0000000..c06fd51
--- /dev/null
+++ b/docs/source/navnode.rst
@@ -0,0 +1,6 @@
+NavNode
+=======
+
+.. automodule:: yael.navnode
+ :members:
+ :private-members:
diff --git a/docs/source/ncxtoc.rst b/docs/source/ncxtoc.rst
new file mode 100644
index 0000000..0ef6243
--- /dev/null
+++ b/docs/source/ncxtoc.rst
@@ -0,0 +1,6 @@
+NCXToc
+======
+
+.. automodule:: yael.ncxtoc
+ :members:
+ :private-members:
diff --git a/docs/source/ncxtocnode.rst b/docs/source/ncxtocnode.rst
new file mode 100644
index 0000000..2c916f2
--- /dev/null
+++ b/docs/source/ncxtocnode.rst
@@ -0,0 +1,6 @@
+NCXTocNode
+==========
+
+.. automodule:: yael.ncxtocnode
+ :members:
+ :private-members:
diff --git a/docs/source/obfuscation.rst b/docs/source/obfuscation.rst
new file mode 100644
index 0000000..59985a3
--- /dev/null
+++ b/docs/source/obfuscation.rst
@@ -0,0 +1,6 @@
+Obfuscation
+===========
+
+.. automodule:: yael.obfuscation
+ :members:
+ :private-members:
diff --git a/docs/source/opfdc.rst b/docs/source/opfdc.rst
new file mode 100644
index 0000000..ea6270e
--- /dev/null
+++ b/docs/source/opfdc.rst
@@ -0,0 +1,6 @@
+OPFDC
+=====
+
+.. automodule:: yael.opfdc
+ :members:
+ :private-members:
diff --git a/docs/source/opfguide.rst b/docs/source/opfguide.rst
new file mode 100644
index 0000000..1d4ea3a
--- /dev/null
+++ b/docs/source/opfguide.rst
@@ -0,0 +1,6 @@
+OPFGuide
+========
+
+.. automodule:: yael.opfguide
+ :members:
+ :private-members:
diff --git a/docs/source/opfitem.rst b/docs/source/opfitem.rst
new file mode 100644
index 0000000..1fe4700
--- /dev/null
+++ b/docs/source/opfitem.rst
@@ -0,0 +1,6 @@
+OPFItem
+=======
+
+.. automodule:: yael.opfitem
+ :members:
+ :private-members:
diff --git a/docs/source/opfitemref.rst b/docs/source/opfitemref.rst
new file mode 100644
index 0000000..62a2d7c
--- /dev/null
+++ b/docs/source/opfitemref.rst
@@ -0,0 +1,6 @@
+OPFItemref
+==========
+
+.. automodule:: yael.opfitemref
+ :members:
+ :private-members:
diff --git a/docs/source/opflink.rst b/docs/source/opflink.rst
new file mode 100644
index 0000000..e864d87
--- /dev/null
+++ b/docs/source/opflink.rst
@@ -0,0 +1,6 @@
+OPFLink
+=======
+
+.. automodule:: yael.opflink
+ :members:
+ :private-members:
diff --git a/docs/source/opfmanifest.rst b/docs/source/opfmanifest.rst
new file mode 100644
index 0000000..ac03d0e
--- /dev/null
+++ b/docs/source/opfmanifest.rst
@@ -0,0 +1,6 @@
+OPFManifest
+===========
+
+.. automodule:: yael.opfmanifest
+ :members:
+ :private-members:
diff --git a/docs/source/opfmeta2.rst b/docs/source/opfmeta2.rst
new file mode 100644
index 0000000..4ac1443
--- /dev/null
+++ b/docs/source/opfmeta2.rst
@@ -0,0 +1,6 @@
+OPFMeta2
+========
+
+.. automodule:: yael.opfmeta2
+ :members:
+ :private-members:
diff --git a/docs/source/opfmeta3.rst b/docs/source/opfmeta3.rst
new file mode 100644
index 0000000..005eafa
--- /dev/null
+++ b/docs/source/opfmeta3.rst
@@ -0,0 +1,6 @@
+OPFMeta3
+========
+
+.. automodule:: yael.opfmeta3
+ :members:
+ :private-members:
diff --git a/docs/source/opfmetadata.rst b/docs/source/opfmetadata.rst
new file mode 100644
index 0000000..e07efcf
--- /dev/null
+++ b/docs/source/opfmetadata.rst
@@ -0,0 +1,6 @@
+OPFMetadata
+===========
+
+.. automodule:: yael.opfmetadata
+ :members:
+ :private-members:
diff --git a/docs/source/opfmetadatum.rst b/docs/source/opfmetadatum.rst
new file mode 100644
index 0000000..50a53f0
--- /dev/null
+++ b/docs/source/opfmetadatum.rst
@@ -0,0 +1,6 @@
+OPFMetadatum
+============
+
+.. automodule:: yael.opfmetadatum
+ :members:
+ :private-members:
diff --git a/docs/source/opfpacdocument.rst b/docs/source/opfpacdocument.rst
new file mode 100644
index 0000000..99a73ec
--- /dev/null
+++ b/docs/source/opfpacdocument.rst
@@ -0,0 +1,6 @@
+OPFPacDocument
+==================
+
+.. automodule:: yael.opfpacdocument
+ :members:
+ :private-members:
diff --git a/docs/source/opfreference.rst b/docs/source/opfreference.rst
new file mode 100644
index 0000000..02e1554
--- /dev/null
+++ b/docs/source/opfreference.rst
@@ -0,0 +1,6 @@
+OPFReference
+============
+
+.. automodule:: yael.opfreference
+ :members:
+ :private-members:
diff --git a/docs/source/opfspine.rst b/docs/source/opfspine.rst
new file mode 100644
index 0000000..fd7f86e
--- /dev/null
+++ b/docs/source/opfspine.rst
@@ -0,0 +1,6 @@
+OPFSpine
+========
+
+.. automodule:: yael.opfspine
+ :members:
+ :private-members:
diff --git a/docs/source/pacdocument.rst b/docs/source/pacdocument.rst
new file mode 100644
index 0000000..7ac6a13
--- /dev/null
+++ b/docs/source/pacdocument.rst
@@ -0,0 +1,6 @@
+PacDocument
+===============
+
+.. automodule:: yael.pacdocument
+ :members:
+ :private-members:
diff --git a/docs/source/parsing.rst b/docs/source/parsing.rst
new file mode 100644
index 0000000..8dc9085
--- /dev/null
+++ b/docs/source/parsing.rst
@@ -0,0 +1,6 @@
+Parsing
+=======
+
+.. automodule:: yael.parsing
+ :members:
+ :private-members:
diff --git a/docs/source/publication.rst b/docs/source/publication.rst
new file mode 100644
index 0000000..866f88b
--- /dev/null
+++ b/docs/source/publication.rst
@@ -0,0 +1,6 @@
+Publication
+===========
+
+.. automodule:: yael.publication
+ :members:
+ :private-members:
diff --git a/docs/source/rendition.rst b/docs/source/rendition.rst
new file mode 100644
index 0000000..9cc1057
--- /dev/null
+++ b/docs/source/rendition.rst
@@ -0,0 +1,6 @@
+Rendition
+=========
+
+.. automodule:: yael.rendition
+ :members:
+ :private-members:
diff --git a/docs/source/rmdocument.rst b/docs/source/rmdocument.rst
new file mode 100644
index 0000000..d3a07bc
--- /dev/null
+++ b/docs/source/rmdocument.rst
@@ -0,0 +1,6 @@
+RMDocument
+==========
+
+.. automodule:: yael.rmdocument
+ :members:
+ :private-members:
diff --git a/docs/source/rmlocation.rst b/docs/source/rmlocation.rst
new file mode 100644
index 0000000..9d0b559
--- /dev/null
+++ b/docs/source/rmlocation.rst
@@ -0,0 +1,6 @@
+RMLocation
+==========
+
+.. automodule:: yael.rmlocation
+ :members:
+ :private-members:
diff --git a/docs/source/rmpoint.rst b/docs/source/rmpoint.rst
new file mode 100644
index 0000000..63f5837
--- /dev/null
+++ b/docs/source/rmpoint.rst
@@ -0,0 +1,6 @@
+RMPoint
+=======
+
+.. automodule:: yael.rmpoint
+ :members:
+ :private-members:
diff --git a/docs/source/simpleepub.rst b/docs/source/simpleepub.rst
new file mode 100644
index 0000000..d651c42
--- /dev/null
+++ b/docs/source/simpleepub.rst
@@ -0,0 +1,6 @@
+SimpleEPUB
+==========
+
+.. automodule:: yael.simpleepub
+ :members:
+ :private-members:
diff --git a/docs/source/util.rst b/docs/source/util.rst
new file mode 100644
index 0000000..14ee9cf
--- /dev/null
+++ b/docs/source/util.rst
@@ -0,0 +1,6 @@
+util
+====
+
+.. automodule:: yael.util
+ :members:
+ :private-members:
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..ca5ed9f
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,16 @@
+#!/usr/bin/env python
+
+from distutils.core import setup
+
+setup(
+ name='yael',
+ version='0.0.1',
+ author='Alberto Pettarin',
+ author_email='alberto@albertopettarin.it',
+ packages=['yael'],
+ url='https://github.com/pettarin/yael',
+ license='LICENSE',
+ description='yael (Yet Another EPUB Library) is a Python library for reading, manipulating, and writing EPUB 2/3 files.',
+ long_description=open('README.md').read(),
+ install_requires='lxml >= 3.4.0, simplejson >= 3.6.0',
+)
diff --git a/test/__init__.py b/test/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/test/publication_test.py b/test/publication_test.py
new file mode 100644
index 0000000..53c21c5
--- /dev/null
+++ b/test/publication_test.py
@@ -0,0 +1,223 @@
+#!/usr/bin/python
+
+"""
+Publication test
+"""
+
+# standard modules
+import os
+import sys
+
+# yael modules
+# TODO find a better way to do this
+PROJECT_DIRECTORY = os.path.dirname(
+ os.path.dirname(os.path.realpath(sys.argv[0])))
+sys.path.append(PROJECT_DIRECTORY)
+from yael import DC
+from yael import MediaType
+from yael import OPFMeta3
+from yael import Parsing
+from yael import Publication
+from yael import SimpleEPUB
+
+__author__ = "Alberto Pettarin"
+__copyright__ = "Copyright 2015, Alberto Pettarin (www.albertopettarin.it)"
+__license__ = "MIT"
+__version__ = "0.0.1"
+__email__ = "alberto@albertopettarin.it"
+__status__ = "Development"
+
+def usage():
+ print("")
+ print("$ ./%s path/to/dir [--no-mo]" % sys.argv[0])
+ print("$ ./%s path/to/file.epub [--no-mo]" % sys.argv[0])
+ print("")
+
+def main():
+ if (len(sys.argv) > 2) and (sys.argv[2] == "--no-mo"):
+ p = Publication(
+ path=sys.argv[1],
+ parsing_options=[Parsing.NO_MEDIA_OVERLAY])
+ elif len(sys.argv) > 1:
+ p = Publication(
+ path=sys.argv[1],
+ parsing_options=[])
+ else:
+ usage()
+ return
+
+ print("")
+ print("Manifestation = %s" % p.manifestation)
+ print("Size = %s" % p.size)
+ print("Unique identifier = %s" % p.unique_identifier)
+ print("dcterms:modified = %s" % p.dcterms_modified)
+ print("Release identifier = %s" % p.release_identifier)
+ print("Version = %s" % p.version)
+ print("")
+ print("Renditions = %d" % (len(p.container.renditions)))
+ print("")
+ print("Internal path cover = %s" % p.internal_path_cover_image)
+ print("")
+
+ # print the JSON string of this publication
+ # pretty, 4-chars indented, keys not sorted, entries with empty/null values not removed
+ #print(p)
+
+ # print the JSON string of this publication, compressed
+ #print(p.json_string())
+
+ # print the JSON string of this publication, compressed and clean
+ #print(p.json_string(clean=True))
+
+ # print the JSON string of this publication
+ # pretty, 4-chars indented, sorted keys, entries with empty/null values removed
+ #print(p.json_string(pretty=True, indent=2, sort=True, clean=True))
+
+ # print toc (default rendition)
+ # nav toc (if EPUB 3) or ncx tco (if EPUB 2)
+ #print(p.container.default_rendition.toc)
+
+ # print ncx toc (default rendition)
+ #print(p.container.default_rendition.ncx_toc)
+
+ # print landmarks (default rendition)
+ #print(p.container.default_rendition.landmarks)
+
+ # metadatum with id="aut"
+ #print(p.container.default_rendition.pac_document.metadata.metadatum_by_id("aut"))
+
+ # dc:subject metadata
+ #for metadatum in p.container.default_rendition.pac_document.metadata.metadata_by_tag(DC.E_NS_SUBJECT):
+ # print(metadatum)
+
+ # get the value for the media:active-class metadatum
+ #metadata = p.container.default_rendition.pac_document.metadata.metadata_by_property(OPFMeta3.V_MEDIA_ACTIVE_CLASS)
+ #if len(metadata) > 0:
+ # print(metadata[0].v_text)
+
+ # get the values for the media:narrator metadata
+ #for metadatum in p.container.default_rendition.pac_document.metadata.metadata_by_property(OPFMeta3.V_MEDIA_NARRATOR):
+ # print(metadatum.v_text)
+
+ # get the values for the media:duration metadata
+ #for metadatum in p.container.default_rendition.pac_document.metadata.metadata_by_property(OPFMeta3.V_MEDIA_DURATION):
+ # if metadatum.v_refines == None:
+ # print("Total: %s" % metadatum.v_text)
+ # else:
+ # print("Ref %s: %s" % (metadatum.v_refines, metadatum.v_text))
+
+ # all PNG images
+ #for img in p.container.default_rendition.pac_document.manifest.items_by_media_type("image/png"):
+ # print(img)
+
+ # all JPEG images
+ #for img in p.container.default_rendition.pac_document.manifest.items_by_media_type(MediaType.JPEG):
+ # print(img)
+
+ # all images
+ #for img in p.container.default_rendition.pac_document.manifest.image_items:
+ # print(img)
+
+ # all manifest items with scripted property
+ #for item in p.container.default_rendition.pac_document.manifest.scripted_items:
+ # print(item)
+
+ # all Media Overlay documents
+ #for item in p.container.default_rendition.pac_document.manifest.mo_items:
+ # print(item)
+
+ # all files referenced in the manifest, in order
+ #for i_p_file in p.container.default_rendition.pac_document.files_referenced_manifest:
+ # print(i_p_file)
+
+ # all files referenced in the spine, in order
+ #for i_p_file in p.container.default_rendition.pac_document.files_referenced_spine:
+ # print(i_p_file)
+
+ # all files referenced in the spine, with linear="yes" or omitted, in order
+ #for i_p_file in p.container.default_rendition.pac_document.files_referenced_spine_linear:
+ # print(i_p_file)
+
+ # for all MO documents, print the referenced audio files
+ #for mo_doc in p.container.default_rendition.mo_documents:
+ # print(mo_doc.internal_path)
+ # for audio_file in mo_doc.referenced_audio_files:
+ # print(" + %s" % audio_file)
+ # print("")
+
+ # for all MO documents, print the referenced text fragment identifiers, in order
+ #for mo_doc in p.container.default_rendition.mo_documents:
+ # print(mo_doc.internal_path)
+ # for text_id in mo_doc.referenced_fragment_identifiers:
+ # print(" + %s" % text_id)
+ # print("")
+
+ # for all MO documents, print the referenced text fragment identifiers,
+ # grouped by text file
+ #for mo_doc in p.container.default_rendition.mo_documents:
+ # grouped = mo_doc.grouped_referenced_fragment_identifiers
+ # for key in grouped:
+ # print(" + %s" % key)
+ # for text_id in grouped[key]:
+ # print(" + %s" % text_id)
+ # print("")
+
+ # test Multiple Renditions
+ #if len(p.container.renditions) > 1:
+ # print("Publication Unique ID: %s" % p.unique_identifier)
+ # print("Publication Release ID: %s" % p.release_identifier)
+ # print("Rendition 1 Unique ID: %s" % p.container.renditions[0].pac_document.v_unique_identifier)
+ # print("Rendition 2 Unique ID: %s" % p.container.renditions[1].pac_document.v_unique_identifier)
+ # print("")
+ # print("Rendition Mapping Document\n%s" % p.container.rm_document.json_string(clean=True, pretty=True))
+
+ # extract the cover image and save it to /tmp/extracted_cover.png
+ #i_p_cover = p.internal_path_cover_image
+ #if (i_p_cover != None) and (i_p_cover in p.assets):
+ # print("Extracting '%s' ..." % i_p_cover)
+ # blob = p.assets[i_p_cover].contents
+ # output_file = open("/tmp/extracted_cover.png", mode="wb")
+ # output_file.write(blob)
+ # output_file.close()
+ # print("Extracted /tmp/extracted_cover.png")
+
+ # iterates over the spine, printing the contents
+ # of the referenced content documents
+ #for itemref in p.container.default_rendition.pac_document.spine.itemrefs:
+ # item = p.container.default_rendition.pac_document.manifest.item_by_id(itemref.v_idref)
+ # if item != None:
+ # print("")
+ # print("%s (linear? %s)" % (itemref.v_idref, not (itemref.v_linear == "no")))
+ # print("")
+ # print(item.contents)
+ # print("")
+
+ # copy all the assets into a single directory /tmp/foo/ ("flatten")
+ #if not os.path.exists("/tmp/foo"):
+ # os.mkdir("/tmp/foo")
+ #for i_p_asset in p.assets.keys():
+ # destination = os.path.join("/tmp/foo", os.path.basename(i_p_asset))
+ # print("Copying '%s' into '%s'..." % (i_p_asset, destination))
+ # fil = open(destination, "wb")
+ # fil.write(p.assets[i_p_asset].contents)
+ # fil.close()
+ # print("Done")
+
+ # print all the assets obfuscated with the IDPF algorithm
+ #if p.encryption != None:
+ # print("Assets obfuscated with the Adobe algorithm")
+ # for i_p_asset in p.encryption.adobe_obfuscated_assets:
+ # print(" + %s" % i_p_asset)
+ # print("")
+ # print("Assets obfuscated with the IDPF algorithm")
+ # for i_p_asset in p.encryption.idpf_obfuscated_assets:
+ # print(" + %s" % i_p_asset)
+ # print("")
+
+
+
+if __name__ == '__main__':
+ main()
+
+
+
diff --git a/test/simpleepub_test.py b/test/simpleepub_test.py
new file mode 100644
index 0000000..6086b9e
--- /dev/null
+++ b/test/simpleepub_test.py
@@ -0,0 +1,114 @@
+#!/usr/bin/python
+
+"""
+SimpleEPUB test
+"""
+
+# standard modules
+import os
+import sys
+
+# yael modules
+# TODO find a better way to do this
+PROJECT_DIRECTORY = os.path.dirname(
+ os.path.dirname(os.path.realpath(sys.argv[0])))
+sys.path.append(PROJECT_DIRECTORY)
+from yael import Parsing
+from yael import SimpleEPUB
+
+__author__ = "Alberto Pettarin"
+__copyright__ = "Copyright 2015, Alberto Pettarin (www.albertopettarin.it)"
+__license__ = "MIT"
+__version__ = "0.0.1"
+__email__ = "alberto@albertopettarin.it"
+__status__ = "Development"
+
+def usage():
+ print("")
+ print("$ ./%s path/to/dir [--no-mo]" % sys.argv[0])
+ print("$ ./%s path/to/file.epub [--no-mo]" % sys.argv[0])
+ print("")
+
+def main():
+ if (len(sys.argv) > 2) and (sys.argv[2] == "--no-mo"):
+ p = SimpleEPUB(
+ path=sys.argv[1],
+ parsing_options=[Parsing.NO_MEDIA_OVERLAY])
+ elif len(sys.argv) > 1:
+ p = SimpleEPUB(
+ path=sys.argv[1],
+ parsing_options=[])
+ else:
+ usage()
+ return
+
+ print("")
+ print("Manifestation = %s" % p.manifestation)
+ print("Version = %s" % p.version)
+ print("Unique identifier = %s" % p.unique_identifier)
+ print("dcterms:modified = %s" % p.dcterms_modified)
+ print("Release identifier = %s" % p.release_identifier)
+ print("")
+ print("Internal path cover = %s" % p.internal_path_cover_image)
+ print("")
+ print("Title = %s" % p.title)
+ print("Language = %s" % p.language)
+ print("Author = %s" % p.author)
+ print("Date = %s" % p.date)
+ print("Description = %s" % p.description)
+ print("Publisher = %s" % p.publisher)
+ print("Rights = %s" % p.rights)
+ print("Source = %s" % p.source)
+ print("Subjects = %s" % ", ".join(sorted(p.subjects)))
+ print("Type = %s" % p.type)
+ print("")
+
+ # print the TOC
+ #print(p.toc)
+
+ # print the TOC with resolved paths
+ #print(p.resolved_toc)
+
+ # print the landmarks
+ #print(p.landmarks)
+
+ # print the landmarks with resolved paths
+ #print(p.resolved_landmarks)
+
+ # print the spine
+ #print("Spine")
+ #for i_p_spine_item in p.resolved_spine:
+ # print(i_p_spine_item)
+ #print("")
+
+ # print the spine (linear="yes" or omitted)
+ #print("Spine (linear)")
+ #for i_p_spine_item in p.resolved_spine_linear:
+ # print(i_p_spine_item)
+ #print("")
+
+ # get the contents of META-INF/container.xml
+ #print("Contents of META-INF/container.xml")
+ #print("")
+ #print(p.asset_contents("META-INF/container.xml"))
+ #print("")
+
+ # get the contents of another asset
+ #print("Contents of OEBPS/Text/p001.xhtml")
+ #print("")
+ #print(p.asset_contents("OEBPS/Text/p001.xhtml"))
+ #print("")
+
+ # extract cover image to /tmp/extracted_cover.jpg
+ #cover_image = p.cover_image
+ #if cover_image != None:
+ # output_file = open("/tmp/extracted_cover.jpg", "wb")
+ # output_file.write(cover_image)
+ # output_file.close()
+ # print("Cover image extracted to '/tmp/extracted_cover.jpg' ...")
+
+if __name__ == '__main__':
+ main()
+
+
+
diff --git a/yael/__init__.py b/yael/__init__.py
new file mode 100644
index 0000000..8dd636e
--- /dev/null
+++ b/yael/__init__.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python
+# coding=utf-8
+
+"""
+**yael** (Yet Another EPUB Library) is a Python library
+for reading, manipulating, and writing EPUB 2/3 files.
+"""
+
+from yael.asset import Asset
+from yael.container import Container
+from yael.dc import DC
+from yael.element import Element
+from yael.encryption import Encryption
+from yael.enckey import EncKey
+from yael.epub import EPUB
+from yael.jsonable import JSONAble
+from yael.manifestation import Manifestation
+from yael.marcrelator import MARCRelator
+from yael.mediatype import MediaType
+from yael.metadata import Metadata
+from yael.moaudio import MOAudio
+from yael.modocument import MODocument
+from yael.mopar import MOPar
+from yael.moseq import MOSeq
+from yael.motext import MOText
+from yael.namespace import Namespace
+from yael.navdocument import NavDocument
+from yael.navelement import NavElement
+from yael.navnode import NavNode
+from yael.ncxtoc import NCXToc
+from yael.ncxtocnode import NCXTocNode
+from yael.obfuscation import Obfuscation
+from yael.opfdc import OPFDC
+from yael.opfguide import OPFGuide
+from yael.opfitem import OPFItem
+from yael.opfitemref import OPFItemref
+from yael.opflink import OPFLink
+from yael.opfmanifest import OPFManifest
+from yael.opfmeta2 import OPFMeta2
+from yael.opfmeta3 import OPFMeta3
+from yael.opfmetadata import OPFMetadata
+from yael.opfmetadatum import OPFMetadatum
+from yael.opfpacdocument import OPFPacDocument
+from yael.opfreference import OPFReference
+from yael.opfspine import OPFSpine
+from yael.pacdocument import PacDocument
+from yael.parsing import Parsing
+from yael.publication import Publication
+from yael.rendition import Rendition
+from yael.rmdocument import RMDocument
+from yael.rmlocation import RMLocation
+from yael.rmpoint import RMPoint
+from yael.simpleepub import SimpleEPUB
+import yael.util
+
+__author__ = "Alberto Pettarin"
+__copyright__ = "Copyright 2015, Alberto Pettarin (www.albertopettarin.it)"
+__license__ = "MIT"
+__version__ = "0.0.1"
+__email__ = "alberto@albertopettarin.it"
+__status__ = "Development"
+
diff --git a/yael/asset.py b/yael/asset.py
new file mode 100644
index 0000000..3983e5b
--- /dev/null
+++ b/yael/asset.py
@@ -0,0 +1,233 @@
+#!/usr/bin/env python
+# coding=utf-8
+
+"""
+An asset (file) of a Publication.
+
+The asset is identified by its `internal_path`
+inside the virtual container of the publication.
+
+The contents of the asset can be specified:
+
+1. by storing its bytes in the `data` property
+2. by specifying the `absolute_path` to a file on disk
+3. by specifying the `absolute_path` (pointing to a directory on disk)
+ and the corresponding `relative_path` (to a file)
+4. by specifying the `absolute_path` (pointing to a ZIP file on disk)
+ and the corresponding `relative_path` (the ZIP entry name)
+"""
+
+import os
+import zipfile
+
+from yael.jsonable import JSONAble
+import yael.util
+
+__author__ = "Alberto Pettarin"
+__copyright__ = "Copyright 2015, Alberto Pettarin (www.albertopettarin.it)"
+__license__ = "MIT"
+__version__ = "0.0.1"
+__email__ = "alberto@albertopettarin.it"
+__status__ = "Development"
+
+class Asset(JSONAble):
+ """
+ Build an asset.
+
+ :param absolute_path: the absolute path
+ :type absolute_path: str
+ :param relative_path: the relative path
+ :type relative_path: str
+ :param internal_path: the internal path
+ :type internal_path: str
+ :param data: a data (bytes) object
+ :type data: bytes
+
+ """
+
+ def __init__(
+ self,
+ absolute_path=None,
+ relative_path=None,
+ internal_path=None,
+ data=None):
+ self.absolute_path = absolute_path
+ self.relative_path = relative_path
+ self.internal_path = internal_path
+ self.data = data
+ self.obfuscation_algorithm = None
+ self.obfuscation_key = None
+
+ def json_object(self, recursive=True):
+ obj = {
+ "absolute_path": self.absolute_path,
+ "relative_path": self.relative_path,
+ "internal_path": self.internal_path,
+ "data": (self.data == None),
+ "obfuscation_algorithm": self.obfuscation_algorithm,
+ "obfuscation_key": self.obfuscation_key,
+ }
+ return obj
+
+ @property
+ def absolute_path(self):
+ """
+ The absolute path of the container of this asset
+ (if `relative_path` is not None) or
+ the absolute path of this asset
+ (if `relative_path` is None).
+
+ :rtype: str
+ """
+ return self.__absolute_path
+
+ @absolute_path.setter
+ def absolute_path(self, absolute_path):
+ self.__absolute_path = absolute_path
+
+ @property
+ def relative_path(self):
+ """
+ The path of this asset,
+ relative to the container root
+ specified by `absolute_path`.
+
+ :rtype: str
+ """
+ return self.__relative_path
+
+ @relative_path.setter
+ def relative_path(self, relative_path):
+ self.__relative_path = relative_path
+
+ @property
+ def internal_path(self):
+ """
+ The internal path of this asset,
+ relative to the virtual container root.
+
+ :rtype: str
+ """
+ return self.__internal_path
+
+ @internal_path.setter
+ def internal_path(self, internal_path):
+ self.__internal_path = internal_path
+
+ @property
+ def data(self):
+ """
+ The contents (i.e., bytes) of this asset,
+ set programmatically.
+
+ :rtype: bytes
+ """
+ return self.__data
+
+ @data.setter
+ def data(self, data):
+ self.__data = data
+
+ @property
+ def obfuscation_algorithm(self):
+ """
+ The obfuscation algorithm to be used
+ or None if the asset should not be obfuscated.
+
+ :rtype: :class:`yael.obfuscation.Obfuscation`
+ """
+ return self.__obfuscation_algorithm
+
+ @obfuscation_algorithm.setter
+ def obfuscation_algorithm(self, obfuscation_algorithm):
+ self.__obfuscation_algorithm = obfuscation_algorithm
+
+ @property
+ def obfuscation_key(self):
+ """
+ The obfuscation key to be used
+ or None if the asset should not be obfuscated.
+
+ :rtype: str
+ """
+ return self.__obfuscation_key
+
+ @obfuscation_key.setter
+ def obfuscation_key(self, obfuscation_key):
+ self.__obfuscation_key = obfuscation_key
+
+ @property
+ def contents(self):
+ """
+ The contents of this asset.
+
+ The return value is obtained by either reading
+ the `data` property (case 1), or by suitably
+ reading the contents from the file system
+ (cases 2, 3, and 4).
+
+ If the asset is obfuscated, this function
+ will run the (un)obfuscation algorithm
+ on the raw_contents.
+
+ :rtype: bytes
+ """
+
+ raw_data = self.raw_contents
+ if self.obfuscation_key == None:
+ return raw_data
+
+ return yael.util.obfuscate_data(
+ data=raw_data,
+ key=self.obfuscation_key,
+ algorithm=self.obfuscation_algorithm)
+
+ @property
+ def raw_contents(self):
+ """
+ The raw contents of this asset.
+
+ The return value is obtained by either reading
+ the `data` property (case 1), or by suitably
+ reading the contents from the file system
+ (cases 2, 3, and 4).
+
+ :rtype: bytes
+ """
+
+ if self.data != None:
+ return self.data
+
+ try:
+ if (
+ (self.absolute_path != None) and
+ (os.path.exists(self.absolute_path))):
+ if (
+ (os.path.isdir(self.absolute_path)) or
+ (self.relative_path == None)):
+
+ if self.relative_path == None:
+ # uncompressed, abs pointing to a file
+ a_p_asset = self.absolute_path
+ else:
+ # uncompressed, abs + rel
+ a_p_asset = yael.util.norm_join(
+ self.absolute_path,
+ self.relative_path)
+ fil = open(a_p_asset, mode="rb")
+ string = fil.read()
+ fil.close()
+ return string
+ else:
+ # compressed
+ zip_file = zipfile.ZipFile(self.absolute_path, mode="r")
+ string = zip_file.read(self.relative_path)
+ zip_file.close()
+ return string
+ except:
+ pass
+
+ return None
+
+
+
diff --git a/yael/container.py b/yael/container.py
new file mode 100644
index 0000000..e8cdbfa
--- /dev/null
+++ b/yael/container.py
@@ -0,0 +1,205 @@
+#!/usr/bin/env python
+# coding=utf-8
+
+"""
+The `META-INF/container.xml` file, storing:
+
+1. the Rendition objects
+2. the Rendition Mapping Document
+"""
+
+from yael.element import Element
+from yael.jsonable import JSONAble
+from yael.mediatype import MediaType
+from yael.namespace import Namespace
+from yael.rendition import Rendition
+from yael.rmdocument import RMDocument
+import yael.util
+
+__author__ = "Alberto Pettarin"
+__copyright__ = "Copyright 2015, Alberto Pettarin (www.albertopettarin.it)"
+__license__ = "MIT"
+__version__ = "0.0.1"
+__email__ = "alberto@albertopettarin.it"
+__status__ = "Development"
+
+class Container(Element):
+ """
+ Build the `META-INF/container.xml` file
+ or parse it from `string` or `obj`.
+ """
+
+ A_ACCESSMODE = "accessMode"
+ A_FULL_PATH = "full-path"
+ A_HREF = "href"
+ A_LABEL = "label"
+ A_LANGUAGE = "language"
+ A_LAYOUT = "layout"
+ A_MEDIA = "media"
+ A_MEDIA_TYPE = "media-type"
+ A_MEDIA_TYPE = "media-type"
+ A_REL = "rel"
+ A_NS_ACCESSMODE = "{{{0}}}{1}".format(Namespace.RENDITION, A_ACCESSMODE)
+ A_NS_LABEL = "{{{0}}}{1}".format(Namespace.RENDITION, A_LABEL)
+ A_NS_LANGUAGE = "{{{0}}}{1}".format(Namespace.RENDITION, A_LANGUAGE)
+ A_NS_LAYOUT = "{{{0}}}{1}".format(Namespace.RENDITION, A_LAYOUT)
+ A_NS_MEDIA = "{{{0}}}{1}".format(Namespace.RENDITION, A_MEDIA)
+ E_CONTAINER = "container"
+ E_LINK = "link"
+ E_ROOTFILE = "rootfile"
+ E_ROOTFILES = "rootfiles"
+ V_ACCESSMODE_AUDITORY = "auditory"
+ V_ACCESSMODE_TACTILE = "tactile"
+ V_ACCESSMODE_TEXTUAL = "textual"
+ V_ACCESSMODE_VISUAL = "visual"
+ V_LAYOUT_PRE_PAGINATED = "pre-paginated"
+ V_LAYOUT_REFLOWABLE = "reflowable"
+ V_REL_MAPPING = "mapping"
+
+ def __init__(self, internal_path=None, obj=None, string=None):
+ self.renditions = []
+ self.rm_document = None
+ Element.__init__(
+ self,
+ internal_path=internal_path,
+ obj=obj,
+ string=string)
+
+ def json_object(self, recursive=True):
+ obj = {
+ "internal_path": self.internal_path,
+ "renditions": len(self.renditions),
+ "rm_document": (self.rm_document == None),
+ }
+ if recursive:
+ obj["renditions"] = JSONAble.safe(self.renditions)
+ obj["rm_document"] = JSONAble.safe(self.rm_document)
+ return obj
+
+ def parse_object(self, obj):
+ try:
+ # locate element
+ container_arr = yael.util.query_xpath(
+ obj=obj,
+ query="/{0}:{1}",
+ args=['c', Container.E_CONTAINER],
+ nsp={'c': Namespace.CONTAINER},
+ required=Container.E_CONTAINER)
+ container = container_arr[0]
+
+ # locate elements
+ rootfile_arr = yael.util.query_xpath(
+ obj=container,
+ query="{0}:{1}/{0}:{2}",
+ args=['c', Container.E_ROOTFILES, Container.E_ROOTFILE],
+ nsp={'c': Namespace.CONTAINER},
+ required=None)
+ for rootfile in rootfile_arr:
+ self._parse_rootfile(rootfile)
+
+ # locate optional element
+ link_arr = yael.util.query_xpath(
+ obj=container,
+ query="{0}:{1}",
+ args=['c', Container.E_LINK],
+ nsp={'c': Namespace.CONTAINER},
+ required=None)
+ for link in link_arr:
+ self._parse_link(link)
+ except:
+ raise Exception("Error while parsing the given object")
+
+ def add_rendition(self, rendition):
+ """
+ Add a Rendition to this Container.
+
+ :param rendition: Rendition to be added
+ :type rendition: :class:`yael.rendition.Rendition`
+
+ """
+
+ self.renditions.append(rendition)
+
+ @property
+ def renditions(self):
+ """
+ The list of Rendition objects in this Container.
+
+ :rtype: list of :class:`yael.rendition.Rendition`
+ """
+
+ return self.__renditions
+
+ @renditions.setter
+ def renditions(self, renditions):
+ self.__renditions = renditions
+
+ @property
+ def rm_document(self):
+ """
+ The Rendition Mapping Document object in this Container,
+ or None if it is not present.
+
+ :rtype: :class:`yael.rmdocument.RMDocument`
+ """
+
+ return self.__rm_document
+
+ @rm_document.setter
+ def rm_document(self, rm_document):
+ self.__rm_document = rm_document
+
+ @property
+ def default_rendition(self):
+ """
+ The Default Rendition object in this Container,
+ or None if there are no Renditions.
+
+ :rtype: :class:`yael.rendition.Rendition`
+ """
+
+ return yael.util.safe_first(self.renditions)
+
+ def _parse_rootfile(self, obj):
+ """
+ Parse the given node object,
+ and append the parsed Rendition to this Container.
+ """
+
+ # required attributes
+ full_path = obj.get(Container.A_FULL_PATH)
+ media_type = obj.get(Container.A_MEDIA_TYPE)
+
+ if (full_path != None) and (media_type != None):
+ r_obj = Rendition(internal_path=full_path)
+ r_obj.v_full_path = full_path
+ r_obj.v_media_type = media_type
+
+ # multiple renditions
+ r_obj.v_rendition_accessmode = obj.get(Container.A_NS_ACCESSMODE)
+ r_obj.v_rendition_label = obj.get(Container.A_NS_LABEL)
+ r_obj.v_rendition_language = obj.get(Container.A_NS_LANGUAGE)
+ r_obj.v_rendition_layout = obj.get(Container.A_NS_LAYOUT)
+ r_obj.v_rendition_media = obj.get(Container.A_NS_MEDIA)
+
+ self.renditions.append(r_obj)
+
+ def _parse_link(self, obj):
+ """
+ Parse the given node object,
+ and append the parsed RMDocument
+ to this Container.
+ """
+
+ # required attributes for rendition mapping document
+ rel = obj.get(Container.A_REL)
+ href = obj.get(Container.A_HREF)
+ media_type = obj.get(Container.A_MEDIA_TYPE)
+ if ((rel == Container.V_REL_MAPPING) and
+ (media_type == MediaType.XHTML) and
+ (href != None)):
+ self.rm_document = RMDocument(internal_path=href)
+ return None
+
+
+
diff --git a/yael/dc.py b/yael/dc.py
new file mode 100644
index 0000000..a22c726
--- /dev/null
+++ b/yael/dc.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env python
+# coding=utf-8
+
+"""
+Dublin Core constants.
+"""
+
+from yael.namespace import Namespace
+
+__author__ = "Alberto Pettarin"
+__copyright__ = "Copyright 2015, Alberto Pettarin (www.albertopettarin.it)"
+__license__ = "MIT"
+__version__ = "0.0.1"
+__email__ = "alberto@albertopettarin.it"
+__status__ = "Development"
+
+class DC:
+ """
+ Enumeration of Dublin Core constants.
+
+ See the source code for the complete list.
+ """
+
+ E_CONTRIBUTOR = "contributor"
+ E_COVERAGE = "coverage"
+ E_CREATOR = "creator"
+ E_DATE = "date"
+ E_DESCRIPTION = "description"
+ E_FORMAT = "format"
+ E_IDENTIFIER = "identifier"
+ E_LANGUAGE = "language"
+ E_PUBLISHER = "publisher"
+ E_RELATION = "relation"
+ E_RIGHTS = "rights"
+ E_SOURCE = "source"
+ E_SUBJECT = "subject"
+ E_TITLE = "title"
+ E_TYPE = "type"
+ E_NS_CONTRIBUTOR = "{{{0}}}{1}".format(Namespace.DC, E_CONTRIBUTOR)
+ E_NS_COVERAGE = "{{{0}}}{1}".format(Namespace.DC, E_COVERAGE)
+ E_NS_CREATOR = "{{{0}}}{1}".format(Namespace.DC, E_CREATOR)
+ E_NS_DATE = "{{{0}}}{1}".format(Namespace.DC, E_DATE)
+ E_NS_DESCRIPTION = "{{{0}}}{1}".format(Namespace.DC, E_DESCRIPTION)
+ E_NS_FORMAT = "{{{0}}}{1}".format(Namespace.DC, E_FORMAT)
+ E_NS_IDENTIFIER = "{{{0}}}{1}".format(Namespace.DC, E_IDENTIFIER)
+ E_NS_LANGUAGE = "{{{0}}}{1}".format(Namespace.DC, E_LANGUAGE)
+ E_NS_PUBLISHER = "{{{0}}}{1}".format(Namespace.DC, E_PUBLISHER)
+ E_NS_RELATION = "{{{0}}}{1}".format(Namespace.DC, E_RELATION)
+ E_NS_RIGHTS = "{{{0}}}{1}".format(Namespace.DC, E_RIGHTS)
+ E_NS_SOURCE = "{{{0}}}{1}".format(Namespace.DC, E_SOURCE)
+ E_NS_SUBJECT = "{{{0}}}{1}".format(Namespace.DC, E_SUBJECT)
+ E_NS_TITLE = "{{{0}}}{1}".format(Namespace.DC, E_TITLE)
+ E_NS_TYPE = "{{{0}}}{1}".format(Namespace.DC, E_TYPE)
+ V_DCTERMS_MODIFIED = "dcterms:modified"
+
+ ALL_ELEMENTS = [
+ E_CONTRIBUTOR,
+ E_COVERAGE,
+ E_CREATOR,
+ E_DATE,
+ E_DESCRIPTION,
+ E_FORMAT,
+ E_IDENTIFIER,
+ E_LANGUAGE,
+ E_PUBLISHER,
+ E_RELATION,
+ E_RIGHTS,
+ E_SOURCE,
+ E_SUBJECT,
+ E_TITLE,
+ E_TYPE,
+ ]
+
+ ALL_NS_ELEMENTS = [
+ E_NS_CONTRIBUTOR,
+ E_NS_COVERAGE,
+ E_NS_CREATOR,
+ E_NS_DATE,
+ E_NS_DESCRIPTION,
+ E_NS_FORMAT,
+ E_NS_IDENTIFIER,
+ E_NS_LANGUAGE,
+ E_NS_PUBLISHER,
+ E_NS_RELATION,
+ E_NS_RIGHTS,
+ E_NS_SOURCE,
+ E_NS_SUBJECT,
+ E_NS_TITLE,
+ E_NS_TYPE,
+ ]
+
+
+
diff --git a/yael/element.py b/yael/element.py
new file mode 100644
index 0000000..7025b2a
--- /dev/null
+++ b/yael/element.py
@@ -0,0 +1,119 @@
+#!/usr/bin/env python
+# coding=utf-8
+
+"""
+A generic file/XML node element.
+
+It can be created
+by programmatically populating its instance variables,
+or by parsing a string or an XML node object.
+
+This class is "abstract",
+assigning the task of implementing
+the abstract function
+:func:`yael.element.Element.parse_object`
+to each concrete subclass,
+according to the suitable element semantics.
+"""
+
+import lxml.etree
+
+from yael.jsonable import JSONAble
+
+__author__ = "Alberto Pettarin"
+__copyright__ = "Copyright 2015, Alberto Pettarin (www.albertopettarin.it)"
+__license__ = "MIT"
+__version__ = "0.0.1"
+__email__ = "alberto@albertopettarin.it"
+__status__ = "Development"
+
+class Element(JSONAble):
+ """
+ Build a generic file/XML element,
+ or parse it from `obj` or `string`.
+
+ :param internal_path: a string representing the path of the element
+ inside the (possibly, virtual) EPUB container
+ :type internal_path: str
+ :param obj: an XML (`lxml`) node object to be parsed to build
+ this Element
+ :type obj: object
+ :param string: a string to be parsed to build this Element
+ :type string: str
+
+ """
+
+ def __init__(self, internal_path=None, obj=None, string=None):
+ self.asset = None
+ self.internal_path = internal_path
+ if string != None:
+ self.parse_string(string)
+ elif obj != None:
+ self.parse_object(obj)
+
+ def parse_string(self, string):
+ """
+ Build element by parsing the given string `string`.
+
+ :param string: a string representation to be parsed
+ :type string: str
+
+ """
+
+ try:
+ root = lxml.etree.fromstring(string)
+ self.parse_object(root)
+ except:
+ raise Exception("Error while parsing the given string")
+
+ def parse_object(self, obj):
+ """
+ Build element by parsing the given XML node.
+
+ :param obj: an XML node to be parsed
+ :type obj: `lxml` node object
+
+ """
+
+ return
+
+ @property
+ def internal_path(self):
+ """
+ A string representing the path of the element
+ inside the (possibly, virtual) EPUB Container.
+ It should be None for elements not representing files
+ inside the EPUB Container.
+
+ :rtype: str
+ """
+
+ return self.__internal_path
+
+ @internal_path.setter
+ def internal_path(self, internal_path):
+ self.__internal_path = internal_path
+
+ @property
+ def asset(self):
+ """
+ The :class:`yael.asset.Asset` associated with this element.
+ """
+ return self.__asset
+
+ @asset.setter
+ def asset(self, asset):
+ self.__asset = asset
+
+ @property
+ def contents(self):
+ """
+ The contents of the associated asset.
+
+ :rtype: bytes
+ """
+ if self.asset != None:
+ return self.asset.contents
+
+
+
diff --git a/yael/encdata.py b/yael/encdata.py
new file mode 100644
index 0000000..dfdcde6
--- /dev/null
+++ b/yael/encdata.py
@@ -0,0 +1,160 @@
+#!/usr/bin/env python
+# coding=utf-8
+
+"""
+A element.
+
+Note: this class might be incomplete and/or need refactoring.
+"""
+
+from yael.element import Element
+from yael.namespace import Namespace
+import yael.util
+
+__author__ = "Alberto Pettarin"
+__copyright__ = "Copyright 2015, Alberto Pettarin (www.albertopettarin.it)"
+__license__ = "MIT"
+__version__ = "0.0.1"
+__email__ = "alberto@albertopettarin.it"
+__status__ = "Development"
+
+class EncData(Element):
+ """
+ Build a element or
+ parse it from `obj` or `string`.
+ """
+
+ A_ALGORITHM = "Algorithm"
+ A_ID = "Id"
+ A_TYPE = "Type"
+ A_URI = "URI"
+ E_CIPHERDATA = "CipherData"
+ E_CIPHERREFERENCE = "CipherReference"
+ E_ENCRYPTIONMETHOD = "EncryptionMethod"
+ E_KEYINFO = "KeyInfo"
+ E_RETRIEVALMETHOD = "RetrievalMethod"
+ V_ENCRYPTIONMETHOD_IDPF = "http://www.idpf.org/2008/embedding"
+ V_ENCRYPTIONMETHOD_ADOBE = "http://ns.adobe.com/pdf/enc#RC"
+
+ def __init__(self, internal_path=None, obj=None, string=None):
+ self.v_cipher_reference_uri = None
+ self.v_encryption_method_algorithm = None
+ self.v_id = None
+ self.v_retrieval_method_type = None
+ self.v_retrieval_method_uri = None
+ Element.__init__(
+ self,
+ internal_path=internal_path,
+ obj=obj,
+ string=string)
+
+ def json_object(self, recursive=True):
+ obj = {
+ "Id": self.v_id,
+ "ds:RetrievalMethod Type": self.v_retrieval_method_type,
+ "ds:RetrievalMethod URI": self.v_retrieval_method_uri,
+ "enc:CipherReference URI": self.v_cipher_reference_uri,
+ "enc:EncryptionMethod Algorithm":
+ self.v_encryption_method_algorithm,
+ }
+ return obj
+
+ def parse_object(self, obj):
+ try:
+ self.v_id = obj.get(EncData.A_ID)
+
+ # locate element
+ encryption_method_arr = yael.util.query_xpath(
+ obj=obj,
+ query="{0}:{1}",
+ args=['c', EncData.E_ENCRYPTIONMETHOD],
+ nsp={'c': Namespace.ENC},
+ required=None)
+ if len(encryption_method_arr) > 0:
+ self.v_encryption_method_algorithm = (
+ encryption_method_arr[0].get(EncData.A_ALGORITHM))
+
+ # locate element
+ retrieval_method_arr = yael.util.query_xpath(
+ obj=obj,
+ query="{0}:{1}/{0}:{2}",
+ args=['d', EncData.E_KEYINFO, EncData.E_RETRIEVALMETHOD],
+ nsp={'d': Namespace.DS},
+ required=None)
+ if len(retrieval_method_arr) > 0:
+ self.v_retrieval_method_type = retrieval_method_arr[0].get(
+ EncData.A_TYPE)
+ self.v_retrieval_method_uri = retrieval_method_arr[0].get(
+ EncData.A_URI)
+
+ # locate element
+ cipher_reference_arr = yael.util.query_xpath(
+ obj=obj,
+ query="{0}:{1}/{0}:{2}",
+ args=['e', EncData.E_CIPHERDATA, EncData.E_CIPHERREFERENCE],
+ nsp={'e': Namespace.ENC},
+ required=None)
+ if len(cipher_reference_arr) > 0:
+ self.v_cipher_reference_uri = cipher_reference_arr[0].get(
+ EncData.A_URI)
+
+ except:
+ raise Exception("Error while parsing the given object")
+
+ @property
+ def v_id(self):
+ """
+ The value of the `Id` attribute of .
+ """
+ return self.__v_id
+
+ @v_id.setter
+ def v_id(self, v_id):
+ self.__v_id = v_id
+
+ @property
+ def v_retrieval_method_type(self):
+ """
+ The value of the `Type` attribute of .
+ """
+ return self.__v_retrieval_method_type
+
+ @v_retrieval_method_type.setter
+ def v_retrieval_method_type(self, v_retrieval_method_type):
+ self.__v_retrieval_method_type = v_retrieval_method_type
+
+ @property
+ def v_cipher_reference_uri(self):
+ """
+ The value of the `URI` attribute of .
+ """
+ return self.__v_cipher_reference_uri
+
+ @v_cipher_reference_uri.setter
+ def v_cipher_reference_uri(self, v_cipher_reference_uri):
+ self.__v_cipher_reference_uri = v_cipher_reference_uri
+
+ @property
+ def v_encryption_method_algorithm(self):
+ """
+ The value of the `Algorithm` attribute of .
+ """
+ return self.__v_encryption_method_algorithm
+
+ @v_encryption_method_algorithm.setter
+ def v_encryption_method_algorithm(self, v_encryption_method_algorithm):
+ self.__v_encryption_method_algorithm = v_encryption_method_algorithm
+
+ @property
+ def v_retrieval_method_uri(self):
+ """
+ The value of the `URI` attribute of .
+ """
+ return self.__v_retrieval_method_uri
+
+ @v_retrieval_method_uri.setter
+ def v_retrieval_method_uri(self, v_retrieval_method_uri):
+ self.__v_retrieval_method_uri = v_retrieval_method_uri
+
+
+
diff --git a/yael/enckey.py b/yael/enckey.py
new file mode 100644
index 0000000..a234141
--- /dev/null
+++ b/yael/enckey.py
@@ -0,0 +1,129 @@
+#!/usr/bin/env python
+# coding=utf-8
+
+"""
+A element.
+
+Note: this class might be incomplete and/or need refactoring.
+"""
+
+from yael.element import Element
+from yael.namespace import Namespace
+import yael.util
+
+__author__ = "Alberto Pettarin"
+__copyright__ = "Copyright 2015, Alberto Pettarin (www.albertopettarin.it)"
+__license__ = "MIT"
+__version__ = "0.0.1"
+__email__ = "alberto@albertopettarin.it"
+__status__ = "Development"
+
+class EncKey(Element):
+ """
+ Build a element or
+ parse it from `obj` or `string`.
+ """
+
+ A_ALGORITHM = "Algorithm"
+ A_ID = "Id"
+ A_URI = "URI"
+ E_CIPHERDATA = "CipherData"
+ E_CIPHERVALUE = "CipherValue"
+ E_ENCRYPTIONMETHOD = "EncryptionMethod"
+ E_KEYINFO = "KeyInfo"
+ E_KEYNAME = "KeyName"
+
+ def __init__(self, internal_path=None, obj=None, string=None):
+ self.v_cipher_value = None
+ self.v_encryption_method_algorithm = None
+ self.v_id = None
+ self.v_key_name = None
+ Element.__init__(
+ self,
+ internal_path=internal_path,
+ obj=obj,
+ string=string)
+
+ def json_object(self, recursive=True):
+ obj = {
+ "Id": self.v_id,
+ "ds:KeyName": self.v_key_name,
+ "enc:CipherValue": self.v_cipher_value,
+ "enc:EncryptionMethod Algorithm":
+ self.v_encryption_method_algorithm,
+ }
+ return obj
+
+ def parse_object(self, obj):
+ try:
+ self.v_id = obj.get(EncKey.A_ID)
+
+ # locate element
+ encryption_method_arr = yael.util.query_xpath(
+ obj=obj,
+ query="{0}:{1}",
+ args=['c', EncKey.E_ENCRYPTIONMETHOD],
+ nsp={'c': Namespace.ENC},
+ required=None)
+ if len(encryption_method_arr) > 0:
+ self.v_encryption_method_algorithm = (
+ encryption_method_arr[0].get(EncKey.A_ALGORITHM))
+
+ # locate element
+ key_name_arr = yael.util.query_xpath(
+ obj=obj,
+ query="{0}:{1}/{0}:{2}",
+ args=['d', EncKey.E_KEYINFO, EncKey.E_KEYNAME],
+ nsp={'d': Namespace.DS},
+ required=None)
+ if len(key_name_arr) > 0:
+ self.v_key_name = key_name_arr[0].text
+
+ # locate element
+ cipher_value_arr = yael.util.query_xpath(
+ obj=obj,
+ query="{0}:{1}/{0}:{2}",
+ args=['e', EncKey.E_CIPHERDATA, EncKey.E_CIPHERVALUE],
+ nsp={'e': Namespace.ENC},
+ required=None)
+ if len(cipher_value_arr) > 0:
+ self.v_cipher_value = cipher_value_arr[0].text
+
+ except:
+ raise Exception("Error while parsing the given object")
+
+ @property
+ def v_cipher_value(self):
+ """
+ The value of the
+ """
+ return self.__v_cipher_value
+
+ @v_cipher_value.setter
+ def v_cipher_value(self, v_cipher_value):
+ self.__v_cipher_value = v_cipher_value
+
+ @property
+ def v_encryption_method_algorithm(self):
+ """
+ The value of the `Algorithm` attribute of .
+ """
+ return self.__v_encryption_method_algorithm
+
+ @v_encryption_method_algorithm.setter
+ def v_encryption_method_algorithm(self, v_encryption_method_algorithm):
+ self.__v_encryption_method_algorithm = v_encryption_method_algorithm
+
+ @property
+ def v_key_name(self):
+ """
+ The value of the .
+ """
+ return self.__v_key_name
+
+ @v_key_name.setter
+ def v_key_name(self, v_key_name):
+ self.__v_key_name = v_key_name
+
+
+
diff --git a/yael/encryption.py b/yael/encryption.py
new file mode 100644
index 0000000..61d75c6
--- /dev/null
+++ b/yael/encryption.py
@@ -0,0 +1,147 @@
+#!/usr/bin/env python
+# coding=utf-8
+
+"""
+The `META-INF/encryption.xml` file, holding
+information about encrypted/obfuscated assets.
+"""
+
+from yael.jsonable import JSONAble
+from yael.element import Element
+from yael.encdata import EncData
+from yael.enckey import EncKey
+from yael.namespace import Namespace
+import yael.util
+
+__author__ = "Alberto Pettarin"
+__copyright__ = "Copyright 2015, Alberto Pettarin (www.albertopettarin.it)"
+__license__ = "MIT"
+__version__ = "0.0.1"
+__email__ = "alberto@albertopettarin.it"
+__status__ = "Development"
+
+class Encryption(Element):
+ """
+ Build the `META-INF/encryption.xml` file or
+ parse it from `obj` or `string`.
+ """
+
+ E_ENCRYPTEDDATA = "EncryptedData"
+ E_ENCRYPTEDKEY = "EncryptedKey"
+ E_ENCRYPTION = "encryption"
+
+ def __init__(self, internal_path=None, obj=None, string=None):
+ self.encrypted_datas = []
+ self.encrypted_keys = []
+ Element.__init__(
+ self,
+ internal_path=internal_path,
+ obj=obj,
+ string=string)
+
+ def json_object(self, recursive=True):
+ obj = {
+ "encrypted_datas": len(self.encrypted_datas),
+ "encrypted_keys": len(self.encrypted_keys),
+ }
+ if recursive:
+ obj["encrypted_datas"] = JSONAble.safe(self.encrypted_datas)
+ obj["encrypted_keys"] = JSONAble.safe(self.encrypted_keys)
+ return obj
+
+ def parse_object(self, obj):
+ try:
+ # locate element
+ encryption_arr = yael.util.query_xpath(
+ obj=obj,
+ query="/{0}:{1}",
+ args=['c', Encryption.E_ENCRYPTION],
+ nsp={'c': Namespace.CONTAINER},
+ required=Encryption.E_ENCRYPTION)
+ encryption = encryption_arr[0]
+
+ # locate elements
+ enc_key_arr = yael.util.query_xpath(
+ obj=encryption,
+ query="{0}:{1}",
+ args=['e', Encryption.E_ENCRYPTEDKEY],
+ nsp={'e': Namespace.ENC},
+ required=None)
+ for enc_key in enc_key_arr:
+ enc_key_parsed = None
+ try:
+ enc_key_parsed = EncKey(obj=enc_key)
+ self.encrypted_keys.append(enc_key_parsed)
+ except:
+ pass
+
+ # locate elements
+ enc_data_arr = yael.util.query_xpath(
+ obj=encryption,
+ query="{0}:{1}",
+ args=['e', Encryption.E_ENCRYPTEDDATA],
+ nsp={'e': Namespace.ENC},
+ required=None)
+ for enc_data in enc_data_arr:
+ enc_data_parsed = None
+ try:
+ enc_data_parsed = EncData(obj=enc_data)
+ self.encrypted_datas.append(enc_data_parsed)
+ except:
+ pass
+
+ except:
+ raise Exception("Error while parsing the given object")
+
+ @property
+ def encrypted_datas(self):
+ """
+ The children.
+
+ :rtype: list of :class:`yael.encdata.EncData`
+ """
+ return self.__encrypted_datas
+
+ @encrypted_datas.setter
+ def encrypted_datas(self, encrypted_datas):
+ self.__encrypted_datas = encrypted_datas
+
+ @property
+ def encrypted_keys(self):
+ """
+ The children.
+
+ :rtype: list of :class:`yael.enckey.EncKey`
+ """
+ return self.__encrypted_keys
+
+ @encrypted_keys.setter
+ def encrypted_keys(self, encrypted_keys):
+ self.__encrypted_keys = encrypted_keys
+
+ @property
+ def adobe_obfuscated_assets(self):
+ """
+ The list of internal paths of assets
+ obfuscated with the Adobe algorithm.
+
+ :rtype: list of str
+ """
+ return list(e.v_cipher_reference_uri for e in self.encrypted_datas if (
+ (e.v_encryption_method_algorithm == (
+ EncData.V_ENCRYPTIONMETHOD_ADOBE)) and
+ (e.v_cipher_reference_uri != None)))
+
+ @property
+ def idpf_obfuscated_assets(self):
+ """
+ The list of internal paths of assets
+ obfuscated with the IDPF algorithm.
+
+ :rtype: list of str
+ """
+ return list(e.v_cipher_reference_uri for e in self.encrypted_datas if (
+ (e.v_encryption_method_algorithm == (
+ EncData.V_ENCRYPTIONMETHOD_IDPF)) and
+ (e.v_cipher_reference_uri != None)))
+
diff --git a/yael/epub.py b/yael/epub.py
new file mode 100644
index 0000000..3bf898c
--- /dev/null
+++ b/yael/epub.py
@@ -0,0 +1,201 @@
+#!/usr/bin/env python
+# coding=utf-8
+
+"""
+EPUB constants.
+"""
+
+__author__ = "Alberto Pettarin"
+__copyright__ = "Copyright 2015, Alberto Pettarin (www.albertopettarin.it)"
+__license__ = "MIT"
+__version__ = "0.0.1"
+__email__ = "alberto@albertopettarin.it"
+__status__ = "Development"
+
+class EPUB:
+ """
+ Enumeration of EPUB constants:
+ reserved internal paths and EPUB 3 Semantic Vocabulary.
+
+ See the source code for the complete list.
+ """
+
+ INTERNAL_PATH_CONTAINER_XML = "META-INF/container.xml"
+ INTERNAL_PATH_ENCRYPTION_XML = "META-INF/encryption.xml"
+ INTERNAL_PATH_MANIFEST_XML = "META-INF/manifest.xml"
+ INTERNAL_PATH_METADATA_XML = "META-INF/metadata.xml"
+ INTERNAL_PATH_META_INF = "META-INF"
+ INTERNAL_PATH_MIMETYPE = "mimetype"
+ INTERNAL_PATH_RIGHTS_XML = "META-INF/rights.xml"
+ INTERNAL_PATH_SIGNATURES_XML = "META-INF/signatures.xml"
+
+ VOCABULARY = {
+ # Document partitions
+ "cover" : "cover",
+ "frontmatter" : "frontmatter",
+ "bodymatter" : "bodymatter",
+ "backmatter" : "backmatter",
+
+ # Document divisions
+ "volume" : "volume",
+ "part" : "part",
+ "chapter" : "chapter",
+ "subchapter" : "subchapter",
+ "division" : "division",
+
+ # Document sections and components
+ "abstract" : "abstract",
+ "foreword" : "foreword",
+ "preface" : "preface",
+ "prologue" : "prologue",
+ "introduction" : "introduction",
+ "preamble" : "preamble",
+ "conclusion" : "conclusion",
+ "epilogue" : "epilogue",
+ "afterword" : "afterword",
+ "epigraph" : "epigraph",
+
+ # Document navigation
+ "toc" : "toc",
+ "toc-brief" : "toc-brief",
+ "landmarks" : "landmarks",
+ "loa" : "loa",
+ "loi" : "loi",
+ "lot" : "lot",
+ "lov" : "lov",
+
+ # Document reference sections
+ "appendix" : "appendix",
+ "colophon" : "colophon",
+ "credits" : "credits",
+ "keywords" : "keywords",
+ # Indexes
+ "index" : "index",
+ "index-headnotes" : "index-headnotes",
+ "index-legend" : "index-legend",
+ "index-group" : "index-group",
+ "index-entry-list" : "index-entry-list",
+ "index-entry" : "index-entry",
+ "index-term" : "index-term",
+ "index-editor-note" : "index-editor-note",
+ "index-locator" : "index-locator",
+ "index-locator-list" : "index-locator-list",
+ "index-locator-range" : "index-locator-range",
+ "index-xref-preferred" : "index-xref-preferred",
+ "index-xref-related" : "index-xref-related",
+ "index-term-category" : "index-term-category",
+ "index-term-categories" : "index-term-categories",
+ # Glossaries
+ "glossary" : "glossary",
+ "glossterm" : "glossterm",
+ "glossdef" : "glossdef",
+ # Bibliographies
+ "bibliography" : "bibliography",
+ "biblioentry" : "biblioentry",
+
+ # Preliminary sections and components
+ "titlepage" : "titlepage",
+ "halftitlepage" : "halftitlepage",
+ "copyright-page" : "copyright-page",
+ "seriespage" : "seriespage",
+ "acknowledgments" : "acknowledgments",
+ "imprint" : "imprint",
+ "imprimatur" : "imprimatur",
+ "contributors" : "contributors",
+ "other-credits" : "other-credits",
+ "errata" : "errata",
+ "dedication" : "dedication",
+ "revision-history" : "revision-history",
+
+ # Complementary content
+ "case-study" : "case-study",
+ "help" : "help",
+ "marginalia" : "marginalia",
+ "notice" : "notice",
+ "pullquote" : "pullquote",
+ "sidebar" : "sidebar",
+ "warning" : "warning",
+
+ # Titles and headings
+ "halftitle" : "halftitle",
+ "fulltitle" : "fulltitle",
+ "covertitle" : "covertitle",
+ "title" : "title",
+ "subtitle" : "subtitle",
+ "label" : "label",
+ "ordinal" : "ordinal",
+ "bridgehead" : "bridgehead",
+
+ # Educational content
+ # Learning objectives
+ "learning-objective" : "learning-objective",
+ "learning-objectives" : "learning-objectives",
+ "learning-outcome" : "learning-outcome",
+ "learning-outcomes" : "learning-outcomes",
+ "learning-resource" : "learning-resource",
+ "learning-resources" : "learning-resources",
+ "learning-standard" : "learning-standard",
+ "learning-standards" : "learning-standards",
+ # Testing
+ "answer" : "answer",
+ "answers" : "answers",
+ "assessment" : "assessment",
+ "assessments" : "assessments",
+ "feedback" : "feedback",
+ "fill-in-the-blank-problem" : "fill-in-the-blank-problem",
+ "general-problem" : "general-problem",
+ "qna" : "qna",
+ "match-problem" : "match-problem",
+ "multiple-choice-problem" : "multiple-choice-problem",
+ "practice" : "practice",
+ "practices" : "practices",
+ "question" : "question",
+ "true-false-problem" : "true-false-problem",
+
+ # Comics
+ "panel" : "panel",
+ "panel-group" : "panel-group",
+ "balloon" : "balloon",
+ "text-area" : "text-area",
+ "sound-area" : "sound-area",
+
+ # Notes and annotations
+ "annotation" : "annotation",
+ "note" : "note",
+ "footnote" : "footnote",
+ "rearnote" : "rearnote",
+ "footnotes" : "footnotes",
+ "rearnotes" : "rearnotes",
+
+ # References
+ "annoref" : "annoref",
+ "biblioref" : "biblioref",
+ "glossref" : "glossref",
+ "noteref" : "noteref",
+ "referrer" : "referrer",
+
+ # Document text
+ "credit" : "credit",
+ "keyword" : "keyword",
+ "topic-sentence" : "topic-sentence",
+ "concluding-sentence" : "concluding-sentence",
+
+ # Pagination
+ "pagebreak" : "pagebreak",
+ "page-list" : "page-list",
+
+ # Tables
+ "table" : "table",
+ "table-row" : "table-row",
+ "table-cell" : "table-cell",
+
+ # Lists
+ "list" : "list",
+ "list-item" : "list-item",
+
+ # Figures
+ "figure" : "figure",
+ }
+
+
+
diff --git a/yael/jsonable.py b/yael/jsonable.py
new file mode 100644
index 0000000..7c5cec1
--- /dev/null
+++ b/yael/jsonable.py
@@ -0,0 +1,168 @@
+#!/usr/bin/env python
+# coding=utf-8
+
+"""
+A generic object which
+has a JSON object/string representation.
+
+This class is "abstract",
+assigning the task of implementing
+the abstract function
+:func:`yael.jsonable.JSONAble.json_object`
+to each concrete subclass,
+according to the suitable element semantics.
+"""
+
+import json
+
+__author__ = "Alberto Pettarin"
+__copyright__ = "Copyright 2015, Alberto Pettarin (www.albertopettarin.it)"
+__license__ = "MIT"
+__version__ = "0.0.1"
+__email__ = "alberto@albertopettarin.it"
+__status__ = "Development"
+
+class JSONAble(object):
+ """
+ A generic object which has a JSON object/string representation.
+ """
+
+ def json_object(self, recursive=True):
+ """
+ To be implemented in concrete subclasses.
+
+ :param recursive: if True, append JSON sub-objects
+ :type recursive: boolean
+ :returns: object that can be output as a JSON string
+ :rtype: dictionary
+
+ """
+
+ return
+
+ def __str__(self):
+ return self.json_string(pretty=True)
+
+ def json_string(
+ self,
+ recursive=True,
+ pretty=False,
+ indent=4,
+ sort=False,
+ clean=False):
+ """
+ Format a JSON string representation of the object.
+
+ :param recursive: if True, append JSON sub-objects
+ :type recursive: boolean
+ :param pretty: if True, pretty print the string
+ :type pretty: boolean
+ :param indent: the number of spaces for each indentation level
+ :type indent: integer
+ :param sort: if True, sort the keys
+ :type sort: boolean
+ :param clean: if True, remove None values and empty
+ lists/dictionaries
+ :type clean: boolean
+ :returns: a JSON representation of the object
+ :rtype: str
+
+ """
+
+ try:
+ obj = self.json_object(recursive=recursive)
+ if clean:
+ obj = JSONAble.clean(obj)
+ if pretty:
+ return json.dumps(
+ obj,
+ sort_keys=sort,
+ indent=indent,
+ separators=(',', ': '))
+ else:
+ return json.dumps(obj)
+ except:
+ pass
+ return "{}"
+
+ @staticmethod
+ def safe(obj):
+ """
+ Return a JSON-safe representation of the given object.
+
+ If `obj` is a list, return a list whose elements
+ are the safe(...) version of the original elements,
+ otherwise return the result of
+ :func:`yael.jsonable.JSONAble.json_object`.
+
+ The result might be None, if `obj` is invalid.
+
+ :param obj: the object to represent
+ :type obj: JSONAble object or a list of JSONAble objects
+ :returs: a JSON-safe representation of the object
+ :rtype: object
+
+ """
+
+ if obj != None:
+ try:
+ if isinstance(obj, list):
+ accumulator = []
+ for obj_elem in obj:
+ accumulator.append(JSONAble.safe(obj_elem))
+ return accumulator
+ else:
+ return obj.json_object()
+ except:
+ pass
+ return None
+
+ # TODO find a better way of doing this
+ @staticmethod
+ def clean(obj):
+ """
+ Recursively "clean" the given object by removing:
+
+ 1. None values,
+ 2. empty dictionaries, and
+ 3. empty lists.
+
+ Note that this function works on the given object,
+ altering it in place.
+ Pass a copy of the original object if you
+ want to avoid side effects.
+
+ :param obj: the object to clean
+ :type obj: value, array or dictionary
+ :returns: a cleaned version (possibly, None) of the given object
+ :rtype: object
+
+ """
+
+ if isinstance(obj, dict):
+ if len(obj) < 1:
+ obj = None
+ return obj
+ for key, value in obj.items():
+ if value is None:
+ del obj[key]
+ elif isinstance(value, dict) or isinstance(value, list):
+ if JSONAble.clean(value) == None:
+ del obj[key]
+ elif isinstance(obj, list):
+ if len(obj) < 1:
+ obj = None
+ return obj
+ tmp = []
+ for value in obj:
+ clean_value = JSONAble.clean(value)
+ if clean_value != None:
+ tmp.append(clean_value)
+ if len(tmp) == 0:
+ obj = None
+ else:
+ obj = tmp
+ return obj
+
+
+
diff --git a/yael/manifestation.py b/yael/manifestation.py
new file mode 100644
index 0000000..e901f2d
--- /dev/null
+++ b/yael/manifestation.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+# coding=utf-8
+
+"""
+Manifestation constants.
+"""
+
+__author__ = "Alberto Pettarin"
+__copyright__ = "Copyright 2015, Alberto Pettarin (www.albertopettarin.it)"
+__license__ = "MIT"
+__version__ = "0.0.1"
+__email__ = "alberto@albertopettarin.it"
+__status__ = "Development"
+
+class Manifestation:
+ """
+ Enumeration of values for the
+ manifestation of a :class:`yael.publication.Publication`.
+ """
+
+ COMPRESSED = "compressed"
+ """ The :class:`yael.publication.Publication`
+ is loaded from a compressed file. """
+
+ MEMORY = "memory"
+ """ The :class:`yael.publication.Publication`
+ is being built programmatically. """
+
+ UNCOMPRESSED = "uncompressed"
+ """ The :class:`yael.publication.Publication`
+ is loaded from an uncompressed directory. """
+
+
+
diff --git a/yael/marcrelator.py b/yael/marcrelator.py
new file mode 100644
index 0000000..ef95ff1
--- /dev/null
+++ b/yael/marcrelator.py
@@ -0,0 +1,251 @@
+#!/usr/bin/env python
+# coding=utf-8
+
+"""
+MARC Relator code constants.
+"""
+
+__author__ = "Alberto Pettarin"
+__copyright__ = "Copyright 2015, Alberto Pettarin (www.albertopettarin.it)"
+__license__ = "MIT"
+__version__ = "0.0.1"
+__email__ = "alberto@albertopettarin.it"
+__status__ = "Development"
+
+class MARCRelator:
+ """
+ Enumeration of MARC Relator code constants.
+
+ See the source for the complete list of codes
+ and the corresponding descriptions.
+ """
+
+ # A dictionary mapping 3-letter codes to the corresponding description.
+ CODES = {
+ "acp" : "Art copyist",
+ "act" : "Actor",
+ "adp" : "Adapter",
+ "aft" : "Author of afterword, colophon, etc.",
+ "anl" : "Analyst",
+ "anm" : "Animator",
+ "ann" : "Annotator",
+ "ant" : "Bibliographic antecedent",
+ "app" : "Applicant",
+ "aqt" : "Author in quotations or text abstracts",
+ "arc" : "Architect",
+ "ard" : "Artistic director",
+ "arr" : "Arranger",
+ "art" : "Artist",
+ "asg" : "Assignee",
+ "asn" : "Associated name",
+ "att" : "Attributed name",
+ "auc" : "Auctioneer",
+ "aud" : "Author of dialog",
+ "aui" : "Author of introduction",
+ "aus" : "Author of screenplay",
+ "aut" : "Author",
+ "bdd" : "Binding designer",
+ "bjd" : "Bookjacket designer",
+ "bkd" : "Book designer",
+ "bkp" : "Book producer",
+ "blw" : "Blurb writer",
+ "bnd" : "Binder",
+ "bpd" : "Bookplate designer",
+ "bsl" : "Bookseller",
+ "ccp" : "Conceptor",
+ "chr" : "Choreographer",
+ "clb" : "Collaborator",
+ "cli" : "Client",
+ "cll" : "Calligrapher",
+ "clr" : "Colorist",
+ "clt" : "Collotyper",
+ "cmm" : "Commentator",
+ "cmp" : "Composer",
+ "cmt" : "Compositor",
+ "cng" : "Cinematographer",
+ "cnd" : "Conductor",
+ "cns" : "Censor",
+ "coe" : "Contestant-appellee",
+ "col" : "Collector",
+ "com" : "Compiler",
+ "con" : "Conservator",
+ "cos" : "Contestant",
+ "cot" : "Contestant-appellant",
+ "cov" : "Cover designer",
+ "cpc" : "Copyright claimant",
+ "cpe" : "Complainant-appellee",
+ "cph" : "Copyright holder",
+ "cpl" : "Complainant",
+ "cpt" : "Complainant-appellant",
+ "cre" : "Creator",
+ "crp" : "Correspondent",
+ "crr" : "Corrector",
+ "csl" : "Consultant",
+ "csp" : "Consultant to a project",
+ "cst" : "Costume designer",
+ "ctb" : "Contributor",
+ "cte" : "Contestee-appellee",
+ "ctg" : "Cartographer",
+ "ctr" : "Contractor",
+ "cts" : "Contestee",
+ "ctt" : "Contestee-appellant",
+ "cur" : "Curator",
+ "cwt" : "Commentator for written text",
+ "dfd" : "Defendant",
+ "dfe" : "Defendant-appellee",
+ "dft" : "Defendant-appellant",
+ "dgg" : "Degree grantor",
+ "dis" : "Dissertant",
+ "dln" : "Delineator",
+ "dnc" : "Dancer",
+ "dnr" : "Donor",
+ "dpb" : "Distribution place",
+ "dpc" : "Depicted",
+ "dpt" : "Depositor",
+ "drm" : "Draftsman",
+ "drt" : "Director",
+ "dsr" : "Designer",
+ "dst" : "Distributor",
+ "dtc" : "Data contributor",
+ "dte" : "Dedicatee",
+ "dtm" : "Data manager",
+ "dto" : "Dedicator",
+ "dub" : "Dubious author",
+ "edt" : "Editor",
+ "egr" : "Engraver",
+ "elg" : "Electrician",
+ "elt" : "Electrotyper",
+ "eng" : "Engineer",
+ "etr" : "Etcher",
+ "evp" : "Event place",
+ "exp" : "Expert",
+ "fac" : "Facsimilist",
+ "fld" : "Field director",
+ "flm" : "Film editor",
+ "fmo" : "Former owner",
+ "fpy" : "First party",
+ "fnd" : "Funder",
+ "frg" : "Forger",
+ "gis" : "Geographic information specialist",
+ "grt" : "Graphic technician # discontinued",
+ "hnr" : "Honoree",
+ "hst" : "Host",
+ "ill" : "Illustrator",
+ "ilu" : "Illuminator",
+ "ins" : "Inscriber",
+ "inv" : "Inventor",
+ "itr" : "Instrumentalist",
+ "ive" : "Interviewee",
+ "ivr" : "Interviewer",
+ "lbr" : "Laboratory",
+ "lbt" : "Librettist",
+ "ldr" : "Laboratory director",
+ "led" : "Lead",
+ "lee" : "Libelee-appellee",
+ "lel" : "Libelee",
+ "len" : "Lender",
+ "let" : "Libelee-appellant",
+ "lgd" : "Lighting designer",
+ "lie" : "Libelant-appellee",
+ "lil" : "Libelant",
+ "lit" : "Libelant-appellant",
+ "lsa" : "Landscape architect",
+ "lse" : "Licensee",
+ "lso" : "Licensor",
+ "ltg" : "Lithographer",
+ "lyr" : "Lyricist",
+ "mcp" : "Music copyist",
+ "mfp" : "Manufacture place",
+ "mfr" : "Manufacturer",
+ "mdc" : "Metadata contact",
+ "mod" : "Moderator",
+ "mon" : "Monitor",
+ "mrb" : "Marbler",
+ "mrk" : "Markup editor",
+ "msd" : "Musical director",
+ "mte" : "Metal-engraver",
+ "mus" : "Musician",
+ "nrt" : "Narrator",
+ "opn" : "Opponent",
+ "org" : "Originator",
+ "orm" : "Organizer of meeting",
+ "oth" : "Other",
+ "own" : "Owner",
+ "pat" : "Patron",
+ "pbd" : "Publishing director",
+ "pbl" : "Publisher",
+ "pdr" : "Project director",
+ "pfr" : "Proofreader",
+ "pht" : "Photographer",
+ "plt" : "Platemaker",
+ "pma" : "Permitting agency",
+ "pmn" : "Production manager",
+ "pop" : "Printer of plates",
+ "ppm" : "Papermaker",
+ "ppt" : "Puppeteer",
+ "prc" : "Process contact",
+ "prd" : "Production personnel",
+ "prf" : "Performer",
+ "prg" : "Programmer",
+ "prm" : "Printmaker",
+ "pro" : "Producer",
+ "prp" : "Production place",
+ "prt" : "Printer",
+ "pta" : "Patent applicant",
+ "pte" : "Plaintiff-appellee",
+ "ptf" : "Plaintiff",
+ "pth" : "Patent holder",
+ "ptt" : "Plaintiff-appellant",
+ "pup" : "Publication place",
+ "rbr" : "Rubricator",
+ "rce" : "Recording engineer",
+ "rcp" : "Recipient",
+ "red" : "Redactor",
+ "ren" : "Renderer",
+ "res" : "Researcher",
+ "rev" : "Reviewer",
+ "rps" : "Repository",
+ "rpt" : "Reporter",
+ "rpy" : "Responsible party",
+ "rse" : "Respondent-appellee",
+ "rsg" : "Restager",
+ "rsp" : "Respondent",
+ "rst" : "Respondent-appellant",
+ "rth" : "Research team head",
+ "rtm" : "Research team member",
+ "sad" : "Scientific advisor",
+ "sce" : "Scenarist",
+ "scl" : "Sculptor",
+ "scr" : "Scribe",
+ "sds" : "Sound designer",
+ "sec" : "Secretary",
+ "sgn" : "Signer",
+ "sht" : "Supporting host",
+ "sng" : "Singer",
+ "spk" : "Speaker",
+ "spn" : "Sponsor",
+ "spy" : "Second party",
+ "srv" : "Surveyor",
+ "std" : "Set designer",
+ "stl" : "Storyteller",
+ "stm" : "Stage manager",
+ "stn" : "Standards body",
+ "str" : "Stereotyper",
+ "tcd" : "Technical director",
+ "tch" : "Teacher",
+ "ths" : "Thesis advisor",
+ "trc" : "Transcriber",
+ "trl" : "Translator",
+ "tyd" : "Type designer",
+ "tyg" : "Typographer",
+ "uvp" : "University place",
+ "vdg" : "Videographer",
+ "voc" : "Vocalist",
+ "wam" : "Writer of accompanying material",
+ "wdc" : "Woodcutter",
+ "wde" : "Wood-engraver",
+ "wit" : "Witness",
+ }
+
+
+
diff --git a/yael/mediatype.py b/yael/mediatype.py
new file mode 100644
index 0000000..22fb888
--- /dev/null
+++ b/yael/mediatype.py
@@ -0,0 +1,180 @@
+#!/usr/bin/env python
+# coding=utf-8
+
+"""
+Media Type constants.
+"""
+
+__author__ = "Alberto Pettarin"
+__copyright__ = "Copyright 2015, Alberto Pettarin (www.albertopettarin.it)"
+__license__ = "MIT"
+__version__ = "0.0.1"
+__email__ = "alberto@albertopettarin.it"
+__status__ = "Development"
+
+class MediaType:
+ """
+ Enumeration of Media Type constants.
+
+ See the source code for the complete list.
+ """
+
+ # EPUB 3 Core Media Types
+ CSS = "text/css"
+ GIF = "image/gif"
+ JPEG = "image/jpeg"
+ JPG = "image/jpeg" # alias
+ JS = "text/javascript"
+ MP3 = "audio/mpeg" # alias
+ MP4_AUDIO = "audio/mp4"
+ MPEG_AUDIO = "audio/mpeg"
+ NCX = "application/x-dtbncx+xml"
+ OTF = "application/vnd.ms-opentype"
+ OTF_ALT = "application/x-font-opentype"
+ OTF_ALT_2 = "application/font-otf"
+ OTF_ALT_3 = "application/x-font-otf"
+ PLS = "application/pls+xml"
+ PNG = "image/png"
+ SMIL = "application/smil+xml"
+ SVG = "image/svg+xml"
+ WOFF = "application/font-woff"
+ WOFF_ALT = "application/x-font-woff"
+ XHTML = "application/xhtml+xml"
+
+
+ # other useful Media Types
+ AAC = "audio/x-aac"
+ AVI = "video/avi"
+ BMP = "image/bmp"
+ CSV = "text/csv"
+ DJVU = "image/vnd.djvu"
+ DTD = "application/xml-dtd"
+ DVI = "application/x-dvi"
+ EOT = "application/vnd.ms-fontobject"
+ EPUB = "application/epub+zip"
+ FLAC = "audio/flac"
+ FLV = "video/x-flv"
+ GZIP = "application/gzip"
+ HTML = "text/html"
+ JSON = "application/json"
+ MATROSKA = "video/x-matroska"
+ MD = "text/x-markdown"
+ MP4_VIDEO = "video/mp4"
+ MPEG_VIDEO = "video/mpeg"
+ OCTET_STREAM = "application/octet-stream"
+ OGG = "audio/ogg"
+ OPF = "application/oebps-package+xml"
+ PDF = "application/pdf"
+ PS = "application/postscript"
+ QT = "video/quicktime"
+ RDF = "application/rdf+xml"
+ RSS = "application/rss+xml"
+ RTF = "text/rtf"
+ SFNT = "application/font-sfnt"
+ SOAP = "application/soap+xml"
+ SPEEX = "audio/speex"
+ TAR = "application/x-tar"
+ TIFF = "image/tiff"
+ TTF = "application/x-font-ttf"
+ TTF_ALT = "application/x-font-truetype"
+ TXT = "text/plain"
+ VORBIS = "audio/vorbis"
+ WAV = "audio/vnd.wave"
+ WEBM_AUDIO = "audio/webm"
+ WEBM_VIDEO = "video/webm"
+ WMV = "video/x-ms-wmv"
+ XML = "application/xml" # or "text/xml"
+ Z7 = "application/x-7z-compressed"
+ ZIP = "application/zip"
+
+ FONTS = [
+ EOT,
+ OTF,
+ OTF_ALT,
+ OTF_ALT_2,
+ OTF_ALT_3,
+ SFNT,
+ SVG,
+ TTF,
+ TTF_ALT,
+ WOFF,
+ WOFF_ALT
+ ]
+
+ CONTENT_DOCUMENTS = [
+ SVG,
+ XHTML
+ ]
+
+
+ @staticmethod
+ def is_audio(media_type):
+ """
+ Determine if the given Media Type is associated with an audio format.
+
+ :param media_type: a Media Type string
+ :type media_type: str
+ :returns: True if `media_type` starts with `audio/`
+
+ """
+
+ return media_type.startswith("audio/")
+
+
+ @staticmethod
+ def is_content_document(media_type):
+ """
+ Determine if the given Media Type is associated
+ with an EPUB Content Document.
+
+ :param media_type: a Media Type string
+ :type media_type: str
+ :returns: True if `media_type` is a format listed
+ in `CONTENT_DOCUMENTS`
+
+ """
+ return media_type in MediaType.CONTENT_DOCUMENTS
+
+
+ @staticmethod
+ def is_font(media_type):
+ """
+ Determine if the given Media Type is associated with a font format.
+
+ :param media_type: a Media Type string
+ :type media_type: str
+ :returns: True if `media_type` is a format listed in `FONTS`
+
+ """
+ return media_type in MediaType.FONTS
+
+
+ @staticmethod
+ def is_image(media_type):
+ """
+ Determine if the given Media Type is associated with an image format.
+
+ :param media_type: a Media Type string
+ :type media_type: str
+ :returns: True if `media_type` starts with `image/`
+
+ """
+
+ return media_type.startswith("image/")
+
+
+ @staticmethod
+ def is_video(media_type):
+ """
+ Determine if the given Media Type is associated with a video format.
+
+ :param media_type: a Media Type string
+ :type media_type: str
+ :returns: True if `media_type` starts with `video/`
+
+ """
+
+ return media_type.startswith("video/")
+
+
+
diff --git a/yael/metadata.py b/yael/metadata.py
new file mode 100644
index 0000000..d05892e
--- /dev/null
+++ b/yael/metadata.py
@@ -0,0 +1,138 @@
+#!/usr/bin/env python
+# coding=utf-8
+
+"""
+The `META-INF/metadata.xml` file, holding
+the publication-wise metadata
+(EPUB 3).
+"""
+
+from yael.element import Element
+from yael.namespace import Namespace
+import yael.util
+
+__author__ = "Alberto Pettarin"
+__copyright__ = "Copyright 2015, Alberto Pettarin (www.albertopettarin.it)"
+__license__ = "MIT"
+__version__ = "0.0.1"
+__email__ = "alberto@albertopettarin.it"
+__status__ = "Development"
+
+class Metadata(Element):
+ """
+ Build the `META-INF/metadata.xml` file or
+ parse it from `obj` or `string`.
+ """
+
+ A_ID = "id"
+ A_PROPERTY = "property"
+ A_UNIQUE_IDENTIFIER = "unique-identifier"
+ E_IDENTIFIER = "identifier"
+ E_META = "meta"
+ E_METADATA = "metadata"
+ V_DCTERMS_MODIFIED = "dcterms:modified"
+
+ def __init__(self, internal_path=None, obj=None, string=None):
+ self.v_dcterms_modified = None
+ self.v_unique_identifier = None
+ Element.__init__(
+ self,
+ internal_path=internal_path,
+ obj=obj,
+ string=string)
+
+ def json_object(self, recursive=True):
+ obj = {
+ "internal_path": self.internal_path,
+ "unique_identifier": self.v_unique_identifier,
+ "dcterms_modified": self.v_dcterms_modified,
+ "release_dentifier": self.v_release_identifier
+ }
+ return obj
+
+ def parse_object(self, obj):
+ try:
+ # locate element
+ metadata_arr = yael.util.query_xpath(
+ obj=obj,
+ query="/{0}:{1}",
+ args=['m', Metadata.E_METADATA],
+ nsp={'m': Namespace.METADATA},
+ required=Metadata.E_METADATA)
+ metadata = metadata_arr[0]
+
+ # get unique-identifier id
+ u_i_id = metadata.get(Metadata.A_UNIQUE_IDENTIFIER)
+
+ # locate elements
+ identifier_arr = yael.util.query_xpath(
+ obj=metadata,
+ query="{0}:{1}",
+ args=['d', Metadata.E_IDENTIFIER],
+ nsp={'d': Namespace.DC},
+ required=None)
+ for identifier in identifier_arr:
+ i_id = identifier.get(Metadata.A_ID)
+ if i_id == u_i_id:
+ self.v_unique_identifier = yael.util.safe_strip(
+ identifier.text)
+
+ # locate optional element
+ meta_arr = yael.util.query_xpath(
+ obj=metadata,
+ query="{0}:{1}",
+ args=['m', Metadata.E_META],
+ nsp={'m': Namespace.METADATA},
+ required=None)
+ for meta in meta_arr:
+ prop = meta.get(Metadata.A_PROPERTY)
+ if prop == Metadata.V_DCTERMS_MODIFIED:
+ self.v_dcterms_modified = yael.util.safe_strip(meta.text)
+ except:
+ raise Exception("Error while parsing the given object")
+
+ @property
+ def v_dcterms_modified(self):
+ """
+ The dcterms:modified value.
+
+ :rtype: str
+ """
+ return self.__v_dcterms_modified
+
+ @v_dcterms_modified.setter
+ def v_dcterms_modified(self, v_dcterms_modified):
+ self.__v_dcterms_modified = v_dcterms_modified
+
+ @property
+ def v_unique_identifier(self):
+ """
+ The unique-identifier attribute.
+
+ :rtype: str
+ """
+ return self.__v_unique_identifier
+
+ @v_unique_identifier.setter
+ def v_unique_identifier(self, v_unique_identifier):
+ self.__v_unique_identifier = v_unique_identifier
+
+ @property
+ def v_release_identifier(self):
+ """
+ The Release Identifier, that is,
+ the concatenation of the Unique Identifier
+ and the dcterms:modified date.
+
+ :rtype: str
+ """
+ if self.v_dcterms_modified != None:
+ return "%s@%s" % (
+ self.v_unique_identifier,
+ self.v_dcterms_modified)
+ return self.v_unique_identifier
+
+
+
+
+
diff --git a/yael/moaudio.py b/yael/moaudio.py
new file mode 100644
index 0000000..a899d1a
--- /dev/null
+++ b/yael/moaudio.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python
+# coding=utf-8
+
+"""
+A Media Overlay element.
+"""
+
+from yael.element import Element
+
+__author__ = "Alberto Pettarin"
+__copyright__ = "Copyright 2015, Alberto Pettarin (www.albertopettarin.it)"
+__license__ = "MIT"
+__version__ = "0.0.1"
+__email__ = "alberto@albertopettarin.it"
+__status__ = "Development"
+
+class MOAudio(Element):
+ """
+ Build a Media Overlay element or
+ parse it from `obj` or `string`.
+ """
+
+ A_CLIPBEGIN = "clipBegin"
+ A_CLIPEND = "clipEnd"
+ A_ID = "id"
+ A_SRC = "src"
+
+ def __init__(self, internal_path=None, obj=None, string=None):
+ self.v_clip_begin = None
+ self.v_clip_end = None
+ self.v_id = None
+ self.v_src = None
+ Element.__init__(
+ self,
+ internal_path=internal_path,
+ obj=obj,
+ string=string)
+
+ def json_object(self, recursive=True):
+ obj = {
+ "clip_begin": self.v_clip_begin,
+ "clip_end": self.v_clip_end,
+ "id": self.v_id,
+ "src": self.v_src,
+ }
+ return obj
+
+ def parse_object(self, obj):
+ self.v_clip_begin = obj.get(MOAudio.A_CLIPBEGIN)
+ self.v_clip_end = obj.get(MOAudio.A_CLIPEND)
+ self.v_id = obj.get(MOAudio.A_ID)
+ self.v_src = obj.get(MOAudio.A_SRC)
+
+ @property
+ def v_clip_begin(self):
+ """
+ The value of the `clipBegin` attribute.
+
+ :rtype: str
+ """
+ return self.__v_clip_begin
+
+ @v_clip_begin.setter
+ def v_clip_begin(self, v_clip_begin):
+ self.__v_clip_begin = v_clip_begin
+
+ @property
+ def v_clip_end(self):
+ """
+ The value of the `clipEnd` attribute.
+
+ :rtype: str
+ """
+ return self.__v_clip_end
+
+ @v_clip_end.setter
+ def v_clip_end(self, v_clip_end):
+ self.__v_clip_end = v_clip_end
+
+ @property
+ def v_id(self):
+ """
+ The value of the `id` attribute.
+
+ :rtype: str
+ """
+ return self.__v_id
+
+ @v_id.setter
+ def v_id(self, v_id):
+ self.__v_id = v_id
+
+ @property
+ def v_src(self):
+ """
+ The value of the `src` attribute.
+
+ :rtype: str
+ """
+ return self.__v_src
+
+ @v_src.setter
+ def v_src(self, v_src):
+ self.__v_src = v_src
+
+
+
diff --git a/yael/modocument.py b/yael/modocument.py
new file mode 100644
index 0000000..3e0d99c
--- /dev/null
+++ b/yael/modocument.py
@@ -0,0 +1,272 @@
+#!/usr/bin/env python
+# coding=utf-8
+
+"""
+A Media Overlay Document (SMIL file).
+"""
+
+from yael.element import Element
+from yael.jsonable import JSONAble
+from yael.moaudio import MOAudio
+from yael.mopar import MOPar
+from yael.moseq import MOSeq
+from yael.motext import MOText
+from yael.namespace import Namespace
+import yael.util
+
+__author__ = "Alberto Pettarin"
+__copyright__ = "Copyright 2015, Alberto Pettarin (www.albertopettarin.it)"
+__license__ = "MIT"
+__version__ = "0.0.1"
+__email__ = "alberto@albertopettarin.it"
+__status__ = "Development"
+
+class MODocument(Element):
+ """
+ Build a Media Overlay Document (SMIL file) or
+ parse it from `obj` or `string`.
+ """
+
+ A_ID = "id"
+ A_PREFIX = "prefix"
+ A_VERSION = "version"
+ A_NS_PREFIX = "{{{0}}}{1}".format(Namespace.EPUB, A_PREFIX)
+ E_BODY = "body"
+ E_HEAD = "head"
+ E_SMIL = "smil"
+
+ def __init__(self, internal_path=None, obj=None, string=None):
+ self.v_epub_prefix = None
+ self.v_id = None
+ self.v_version = None
+ self.head = None # TODO currently not parsed
+ self.body = None
+ Element.__init__(
+ self,
+ internal_path=internal_path,
+ obj=obj,
+ string=string)
+
+ def json_object(self, recursive=True):
+ obj = {
+ "epub_prefix": self.v_epub_prefix,
+ "id": self.v_id,
+ "version": self.v_version,
+ }
+ if recursive:
+ obj["head"] = JSONAble.safe(self.head)
+ obj["body"] = JSONAble.safe(self.body)
+ return obj
+
+ def parse_object(self, obj):
+ smil_arr = yael.util.query_xpath(
+ obj=obj,
+ query="/{0}:{1}",
+ args=["s", MODocument.E_SMIL],
+ nsp={"s": Namespace.SMIL, "e": Namespace.EPUB},
+ required=MODocument.E_SMIL)
+ smil = smil_arr[0]
+
+ self.v_id = smil.get(MODocument.A_ID)
+ self.v_epub_prefix = smil.get(MODocument.A_NS_PREFIX)
+ self.v_version = smil.get(MODocument.A_VERSION)
+
+ # locate element
+ #head_arr = yael.util.query_xpath(
+ # obj=smil,
+ # query="{0}:{1}",
+ # args=["s", MODocument.E_HEAD],
+ # nsp={"s": Namespace.SMIL, "e": Namespace.EPUB},
+ # required=None)
+ #if (len(head_arr) > 0):
+ # self.head = ...
+
+ # locate element
+ body_arr = yael.util.query_xpath(
+ obj=smil,
+ query="{0}:{1}",
+ args=["s", MODocument.E_BODY],
+ nsp={"s": Namespace.SMIL, "e": Namespace.EPUB},
+ required=MODocument.E_BODY)
+ self.body = MOSeq(obj=body_arr[0])
+
+ @property
+ def v_epub_prefix(self):
+ """
+ The `epub:prefix` attribute of the element.
+
+ :rtype: str
+ """
+ return self.__v_epub_prefix
+
+ @v_epub_prefix.setter
+ def v_epub_prefix(self, v_epub_prefix):
+ self.__v_epub_prefix = v_epub_prefix
+
+ @property
+ def v_id(self):
+ """
+ The `id` attribute of the element.
+
+ :rtype: str
+ """
+ return self.__v_id
+
+ @v_id.setter
+ def v_id(self, v_id):
+ self.__v_id = v_id
+
+ @property
+ def v_version(self):
+ """
+ The `version` attribute of the element.
+
+ :rtype: str
+ """
+ return self.__v_version
+
+ @v_version.setter
+ def v_version(self, v_version):
+ self.__v_version = v_version
+
+ @property
+ def body(self):
+ """
+ The `body` child of the element.
+
+ :rtype: yael.moseq.MOSeq
+ """
+ return self.__body
+
+ @body.setter
+ def body(self, body):
+ self.__body = body
+
+ @property
+ def head(self):
+ """
+ The `head` child of the element.
+
+ Currently not parsed, as per specs, it is empty.
+
+ :rtype: None
+ """
+ return self.__head
+
+ @head.setter
+ def head(self, head):
+ self.__head = head
+
+ @property
+ def references_embedded_audio_video(self):
+ """
+ True if this Media Overlay Document
+ references Embedded Audio and Video
+ (i.e., if it contains a
+ with a child but no child).
+
+ :rtype: boolean
+ """
+
+ try:
+ return self._references_embedded_audio_video(self.body.children)
+ except:
+ pass
+ return False
+
+ @property
+ def referenced_audio_files(self):
+ """
+ The list of audio files referenced
+ by elements in this Media Overlay Document.
+
+ :rtype: list of str
+ """
+
+ try:
+ return list(self._referenced_audio_files(self.body.children, {}))
+ except:
+ pass
+ return []
+
+ @property
+ def referenced_fragment_identifiers(self):
+ """
+ The list of fragment identifiers referenced
+ by elements in this Media Overlay Document.
+
+ :rtype: list of str
+ """
+
+ try:
+ return self._referenced_fragment_identifiers(self.body.children, [])
+ except:
+ pass
+ return []
+
+ @property
+ def grouped_referenced_fragment_identifiers(self):
+ """
+ A dictionary containing the fragment identifiers referenced
+ by elements in this Media Overlay Document.
+ Each key is the path to the Content Document,
+ and the corresponding value is a list of fragment indentifiers
+ in that Content Document.
+
+ :rtype: dict
+ """
+
+ try:
+ fis = self._referenced_fragment_identifiers(self.body.children, [])
+ grouped = {}
+ for fid in fis:
+ dic = yael.util.split_reference(fid)
+ if ("base" in dic) and ("fragment" in dic):
+ base = dic["base"]
+ fragment = dic["fragment"]
+ if base not in grouped:
+ grouped[base] = []
+ grouped[base].append(fragment)
+ return grouped
+ except:
+ pass
+ return {}
+
+ def _references_embedded_audio_video(self, elements):
+ for element in elements:
+ has_embedded = False
+ if isinstance(element, MOSeq):
+ has_embedded = self._references_embedded_audio_video(
+ element.children)
+ elif isinstance(element, MOPar):
+ has_embedded = not element.has_audio_child
+ if has_embedded:
+ return True
+ return False
+
+ def _referenced_audio_files(self, elements, dictionary):
+ for element in elements:
+ if isinstance(element, MOSeq) or isinstance(element, MOPar):
+ self._referenced_audio_files(element.children, dictionary)
+ elif isinstance(element, MOAudio):
+ path = element.v_src
+ if self.internal_path != None:
+ path = yael.util.norm_join_parent(self.internal_path, path)
+ dictionary[path] = True
+ return dictionary
+
+ def _referenced_fragment_identifiers(self, elements, accumulator):
+ for element in elements:
+ if isinstance(element, MOSeq) or isinstance(element, MOPar):
+ self._referenced_fragment_identifiers(
+ element.children,
+ accumulator)
+ elif isinstance(element, MOText):
+ path = element.v_src
+ if self.internal_path != None:
+ path = yael.util.norm_join_parent(self.internal_path, path)
+ accumulator.append(path)
+ return accumulator
+
+
+
diff --git a/yael/mopar.py b/yael/mopar.py
new file mode 100644
index 0000000..a3ee498
--- /dev/null
+++ b/yael/mopar.py
@@ -0,0 +1,139 @@
+#!/usr/bin/env python
+# coding=utf-8
+
+"""
+A Media Overlay element.
+
+Besides its own attributes, it should have
+either a (:class:`yael.motext.MOText`) child,
+or
+both a (:class:`yael.motext.MOText`)
+and an (:class:`yael.moaudio.MOAudio`) children.
+"""
+
+from yael.element import Element
+from yael.jsonable import JSONAble
+from yael.moaudio import MOAudio
+from yael.motext import MOText
+from yael.namespace import Namespace
+
+__author__ = "Alberto Pettarin"
+__copyright__ = "Copyright 2015, Alberto Pettarin (www.albertopettarin.it)"
+__license__ = "MIT"
+__version__ = "0.0.1"
+__email__ = "alberto@albertopettarin.it"
+__status__ = "Development"
+
+class MOPar(Element):
+ """
+ Build a Media Overlay element or
+ parse it from `obj` or `string`.
+ """
+
+ A_ID = "id"
+ A_TYPE = "type"
+ A_NS_TYPE = "{{{0}}}{1}".format(Namespace.EPUB, A_TYPE)
+ E_AUDIO = "audio"
+ E_TEXT = "text"
+ E_NS_AUDIO = "{{{0}}}{1}".format(Namespace.SMIL, E_AUDIO)
+ E_NS_TEXT = "{{{0}}}{1}".format(Namespace.SMIL, E_TEXT)
+
+ def __init__(self, internal_path=None, obj=None, string=None):
+ self.v_epub_type = None
+ self.v_id = None
+ self.children = []
+ Element.__init__(
+ self,
+ internal_path=internal_path,
+ obj=obj,
+ string=string)
+
+ def json_object(self, recursive=True):
+ obj = {
+ "epub_type": self.v_epub_type,
+ "id": self.v_id,
+ "children": len(self.children),
+ }
+ if recursive:
+ obj["children"] = JSONAble.safe(self.children)
+ return obj
+
+ def parse_object(self, obj):
+ self.v_epub_type = obj.get(MOPar.A_NS_TYPE)
+ self.v_id = obj.get(MOPar.A_ID)
+
+ # process children
+ for child in obj:
+ if child.tag == MOPar.E_NS_TEXT:
+ self.add_child(MOText(obj=child))
+ if child.tag == MOPar.E_NS_AUDIO:
+ self.add_child(MOAudio(obj=child))
+
+ def add_child(self, child):
+ """
+ Add the given child to this .
+
+ :param child: the or child to be added
+ :type child: :class:`yael.moaudio.MOAudio` or
+ :class:`yael.motext.MOText`
+
+ """
+ self.children.append(child)
+
+ @property
+ def v_epub_type(self):
+ """
+ The value of the `epub:type` attribute.
+
+ :rtype: str
+ """
+ return self.__v_epub_type
+
+ @v_epub_type.setter
+ def v_epub_type(self, v_epub_type):
+ self.__v_epub_type = v_epub_type
+
+ @property
+ def v_id(self):
+ """
+ The value of the `id` attribute.
+
+ :rtype: str
+ """
+ return self.__v_id
+
+ @v_id.setter
+ def v_id(self, v_id):
+ self.__v_id = v_id
+
+ @property
+ def children(self):
+ """
+ The children elements of this .
+
+ Note: this is implemented as a list
+ (instead of two instance variables, one for and one for )
+ to accommodate a bright future when the spec will allow
+ multiple children elements, e.g. several for each .
+
+ :rtype: list of :class:`yael.moaudio.MOAudio` and
+ :class:`yael.motext.MOText`
+ """
+ return self.__children
+
+ @children.setter
+ def children(self, children):
+ self.__children = children
+
+ @property
+ def has_audio_child(self):
+ """
+ True if this has an child.
+
+ :rtype: boolean
+ """
+ lis = list(e for e in self.children if isinstance(e, MOAudio))
+ return len(lis) > 0
+
+
+
diff --git a/yael/moseq.py b/yael/moseq.py
new file mode 100644
index 0000000..eba250b
--- /dev/null
+++ b/yael/moseq.py
@@ -0,0 +1,137 @@
+#!/usr/bin/env python
+# coding=utf-8
+
+"""
+A Media Overlay element.
+
+Besides its own attributes,
+it holds a list of (:class:`yael.moseq.MOSeq`)
+and (:class:`yael.mopar.MOPar`)
+children elements.
+"""
+
+from yael.element import Element
+from yael.jsonable import JSONAble
+from yael.mopar import MOPar
+from yael.namespace import Namespace
+
+__author__ = "Alberto Pettarin"
+__copyright__ = "Copyright 2015, Alberto Pettarin (www.albertopettarin.it)"
+__license__ = "MIT"
+__version__ = "0.0.1"
+__email__ = "alberto@albertopettarin.it"
+__status__ = "Development"
+
+class MOSeq(Element):
+ """
+ Build a Media Overlay element or
+ parse it from `obj` or `string`.
+ """
+
+ A_ID = "id"
+ A_TEXTREF = "textref"
+ A_TYPE = "type"
+ A_NS_TEXTREF = "{{{0}}}{1}".format(Namespace.EPUB, A_TEXTREF)
+ A_NS_TYPE = "{{{0}}}{1}".format(Namespace.EPUB, A_TYPE)
+ E_PAR = "par"
+ E_SEQ = "seq"
+ E_NS_PAR = "{{{0}}}{1}".format(Namespace.SMIL, E_PAR)
+ E_NS_SEQ = "{{{0}}}{1}".format(Namespace.SMIL, E_SEQ)
+
+ def __init__(self, internal_path=None, obj=None, string=None):
+ self.v_epub_textref = None
+ self.v_epub_type = None
+ self.v_id = None
+ self.children = []
+ Element.__init__(
+ self,
+ internal_path=internal_path,
+ obj=obj,
+ string=string)
+
+ def json_object(self, recursive=True):
+ obj = {
+ "epub_textref": self.v_epub_textref,
+ "epub_type": self.v_epub_type,
+ "id": self.v_id,
+ "children": len(self.children),
+ }
+ if recursive:
+ obj["children"] = JSONAble.safe(self.children)
+ return obj
+
+ def parse_object(self, obj):
+ self.v_epub_textref = obj.get(MOSeq.A_NS_TEXTREF)
+ self.v_epub_type = obj.get(MOSeq.A_NS_TYPE)
+ self.v_id = obj.get(MOSeq.A_ID)
+ # process children
+ for child in obj:
+ if child.tag == MOSeq.E_NS_SEQ:
+ self.add_child(MOSeq(obj=child))
+ if child.tag == MOSeq.E_NS_PAR:
+ self.add_child(MOPar(obj=child))
+
+ def add_child(self, child):
+ """
+ Add the given child to this .
+
+ :param child: the or child to be added
+ :type child: :class:`yael.moseq.MOSeq` or :class:`yael.mopar.MOPar`
+
+ """
+ self.children.append(child)
+
+ @property
+ def v_epub_textref(self):
+ """
+ The value of the `epub:textref` attribute.
+
+ :rtype: str
+ """
+ return self.__v_epub_textref
+
+ @v_epub_textref.setter
+ def v_epub_textref(self, v_epub_textref):
+ self.__v_epub_textref = v_epub_textref
+
+ @property
+ def v_epub_type(self):
+ """
+ The value of the `epub:type` attribute.
+
+ :rtype: str
+ """
+ return self.__v_epub_type
+
+ @v_epub_type.setter
+ def v_epub_type(self, v_epub_type):
+ self.__v_epub_type = v_epub_type
+
+ @property
+ def v_id(self):
+ """
+ The value of the `id` attribute.
+
+ :rtype: str
+ """
+ return self.__v_id
+
+ @v_id.setter
+ def v_id(self, v_id):
+ self.__v_id = v_id
+
+ @property
+ def children(self):
+ """
+ The list of children elements.
+
+ :rtype: list of :class:`yael.moseq.MOSeq` and :class:`yael.mopar.MOPar`
+ """
+ return self.__children
+
+ @children.setter
+ def children(self, children):
+ self.__children = children
+
+
+
diff --git a/yael/motext.py b/yael/motext.py
new file mode 100644
index 0000000..30cc80f
--- /dev/null
+++ b/yael/motext.py
@@ -0,0 +1,73 @@
+#!/usr/bin/env python
+# coding=utf-8
+
+"""
+A Media Overlay element.
+"""
+
+from yael.element import Element
+
+__author__ = "Alberto Pettarin"
+__copyright__ = "Copyright 2015, Alberto Pettarin (www.albertopettarin.it)"
+__license__ = "MIT"
+__version__ = "0.0.1"
+__email__ = "alberto@albertopettarin.it"
+__status__ = "Development"
+
+class MOText(Element):
+ """
+ Build a Media Overlay element or
+ parse it from `obj` or `string`.
+ """
+
+ A_ID = "id"
+ A_SRC = "src"
+
+ def __init__(self, internal_path=None, obj=None, string=None):
+ self.v_id = None
+ self.v_src = None
+ Element.__init__(
+ self,
+ internal_path=internal_path,
+ obj=obj,
+ string=string)
+
+ def json_object(self, recursive=True):
+ obj = {
+ "id": self.v_id,
+ "src": self.v_src
+ }
+ return obj
+
+ def parse_object(self, obj):
+ self.v_id = obj.get(MOText.A_ID)
+ self.v_src = obj.get(MOText.A_SRC)
+
+ @property
+ def v_id(self):
+ """
+ The value of the `id` attribute.
+
+ :rtype: str
+ """
+ return self.__v_id
+
+ @v_id.setter
+ def v_id(self, v_id):
+ self.__v_id = v_id
+
+ @property
+ def v_src(self):
+ """
+ The value of the `src` attribute.
+
+ :rtype: str
+ """
+ return self.__v_src
+
+ @v_src.setter
+ def v_src(self, v_src):
+ self.__v_src = v_src
+
+
+
diff --git a/yael/namespace.py b/yael/namespace.py
new file mode 100644
index 0000000..04f481d
--- /dev/null
+++ b/yael/namespace.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python
+# coding=utf-8
+
+"""
+Namespace constants.
+"""
+
+__author__ = "Alberto Pettarin"
+__copyright__ = "Copyright 2015, Alberto Pettarin (www.albertopettarin.it)"
+__license__ = "MIT"
+__version__ = "0.0.1"
+__email__ = "alberto@albertopettarin.it"
+__status__ = "Development"
+
+class Namespace:
+ """
+ Enumeration of Namespace constants.
+
+ See the source code for the complete list.
+ """
+
+ CONTAINER = "urn:oasis:names:tc:opendocument:xmlns:container"
+ DC = "http://purl.org/dc/elements/1.1/"
+ DCTERMS = "http://purl.org/dc/terms/"
+ DS = "http://www.w3.org/2000/09/xmldsig#"
+ ENC = "http://www.w3.org/2001/04/xmlenc#"
+ EPUB = "http://www.idpf.org/2007/ops"
+ INKSCAPE = "http://www.inkscape.org/namespaces/inkscape"
+ METADATA = "http://www.idpf.org/2013/metadata"
+ NCX = "http://www.daisy.org/z3986/2005/ncx/"
+ OPF = "http://www.idpf.org/2007/opf"
+ RENDITION = "http://www.idpf.org/2013/rendition"
+ SMIL = "http://www.w3.org/ns/SMIL"
+ SOPODI = "http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ SVG = "http://www.w3.org/2000/svg"
+ XHTML = "http://www.w3.org/1999/xhtml"
+ XLINK = "http://www.w3.org/1999/xlink"
+ XML = "http://www.w3.org/XML/1998/namespace"
+ XSI = "http://www.w3.org/2001/XMLSchema-instance"
+
+
+
diff --git a/yael/navdocument.py b/yael/navdocument.py
new file mode 100644
index 0000000..f827bd7
--- /dev/null
+++ b/yael/navdocument.py
@@ -0,0 +1,148 @@
+#!/usr/bin/env python
+# coding=utf-8
+
+"""
+A representation of the Navigation Document.
+
+Basically, it is a collection of (NavElement) objects.
+"""
+
+from yael.element import Element
+from yael.jsonable import JSONAble
+from yael.namespace import Namespace
+from yael.navelement import NavElement
+import yael.util
+
+__author__ = "Alberto Pettarin"
+__copyright__ = "Copyright 2015, Alberto Pettarin (www.albertopettarin.it)"
+__license__ = "MIT"
+__version__ = "0.0.1"
+__email__ = "alberto@albertopettarin.it"
+__status__ = "Development"
+
+class NavDocument(Element):
+ """
+ Build the Navigation Document or
+ parse it from `obj` or `string`.
+ """
+
+ E_NAV = "nav"
+ V_NAV_LANDMARKS = "landmarks"
+ V_NAV_LOA = "loa"
+ V_NAV_LOI = "loi"
+ V_NAV_LOT = "lot"
+ V_NAV_LOV = "lov"
+ V_NAV_PAGE_LIST = "page-list"
+ V_NAV_TOC = "toc"
+
+ def __init__(self, internal_path=None, obj=None, string=None):
+ self.navs = []
+ Element.__init__(
+ self,
+ internal_path=internal_path,
+ obj=obj,
+ string=string)
+
+ def json_object(self, recursive=True):
+ obj = {
+ "internal_path": self.internal_path,
+ "navs": len(self.navs),
+ }
+ if recursive:
+ obj["navs"] = JSONAble.safe(self.navs)
+ return obj
+
+ def parse_object(self, obj):
+ nav_arr = yael.util.query_xpath(
+ obj=obj,
+ query="//{0}:{1}",
+ args=["x", NavDocument.E_NAV],
+ nsp={"x": Namespace.XHTML, "e": Namespace.EPUB},
+ required=None)
+ for nav in nav_arr:
+ nav_parsed = None
+ try:
+ nav_parsed = NavElement(
+ internal_path=self.internal_path,
+ obj=nav)
+ except:
+ pass
+ if nav_parsed != None:
+ self.add_nav(nav_parsed)
+
+ def add_nav(self, nav):
+ """
+ Add the given to this Navigation Document.
+
+ :param nav: the to be added
+ :type nav: NavElement
+ """
+ self.navs.append(nav)
+
+ @property
+ def navs(self):
+ """
+ The list of objects in this Navigation Document.
+
+ :rtype: list of :class:`yael.navelement.NavElement` objects
+ """
+ return self.__navs
+
+ @navs.setter
+ def navs(self, navs):
+ self.__navs = navs
+
+ def nav_by_id(self, v_id):
+ """
+ Return the child with given `id`.
+
+ :param v_id: the desired `id`
+ :type v_id: str
+ :returns: the child with given id, or None if not found
+ :rtype: NavElement
+ """
+ lis = list(e for e in self.navs if e.v_id == v_id)
+ return yael.util.safe_first(lis)
+
+ def nav_by_epub_type(self, v_epub_type):
+ """
+ Return the child with given `epub:type`.
+
+ :param v_epub_type: the desired `epub:type`
+ :type v_epub_type: str
+ :returns: the child with given epub:type,
+ or None if not found
+ :rtype: NavElement
+ """
+ lis = list(e for e in self.navs if e.v_epub_type == v_epub_type)
+ return yael.util.safe_first(lis)
+
+ @property
+ def landmarks(self):
+ """
+ The landmarks element (None if not found).
+
+ :rtype: :class:`yael.navelement.NavElement`
+ """
+ return self.nav_by_epub_type(NavDocument.V_NAV_LANDMARKS)
+
+ @property
+ def page_list(self):
+ """
+ The page-list element (None if not found).
+
+ :rtype: :class:`yael.navelement.NavElement`
+ """
+ return self.nav_by_epub_type(NavDocument.V_NAV_PAGE_LIST)
+
+ @property
+ def toc(self):
+ """
+ The toc element (None if not found).
+
+ :rtype: :class:`yael.navelement.NavElement`
+ """
+ return self.nav_by_epub_type(NavDocument.V_NAV_TOC)
+
+
+
diff --git a/yael/navelement.py b/yael/navelement.py
new file mode 100644
index 0000000..3dfb208
--- /dev/null
+++ b/yael/navelement.py
@@ -0,0 +1,153 @@
+#!/usr/bin/env python
+# coding=utf-8
+
+"""
+A element in the Navigation Document.
+"""
+
+from yael.element import Element
+from yael.jsonable import JSONAble
+from yael.namespace import Namespace
+from yael.navnode import NavNode
+import yael.util
+
+__author__ = "Alberto Pettarin"
+__copyright__ = "Copyright 2015, Alberto Pettarin (www.albertopettarin.it)"
+__license__ = "MIT"
+__version__ = "0.0.1"
+__email__ = "alberto@albertopettarin.it"
+__status__ = "Development"
+
+class NavElement(Element):
+ """
+ Build a element in the Navigation Document
+ or parse it from `obj` or `string`.
+ """
+
+ A_EPUB_TYPE = "type"
+ A_ID = "id"
+ A_NS_EPUB_TYPE = "{{{0}}}{1}".format(Namespace.EPUB, A_EPUB_TYPE)
+ E_HX = ["h1", "h2", "h3", "h4", "h5", "h6"]
+ E_LI = "li"
+ E_OL = "ol"
+
+ def __init__(self, internal_path=None, obj=None, string=None):
+ self.v_epub_type = None
+ self.v_id = None
+ self.children = []
+ self.title = None
+ Element.__init__(
+ self,
+ internal_path=internal_path,
+ obj=obj,
+ string=string)
+
+ def json_object(self, recursive=True):
+ obj = {
+ "internal_path": self.internal_path,
+ "epub_type": self.v_epub_type,
+ "id": self.v_id,
+ "children": len(self.children),
+ "title": self.title,
+ }
+ if recursive:
+ obj["children"] = JSONAble.safe(self.children)
+ return obj
+
+ def parse_object(self, obj):
+ self.v_epub_type = obj.get(NavElement.A_NS_EPUB_TYPE)
+ self.v_id = obj.get(NavElement.A_ID)
+
+ # parse title (if any)
+ # it can be any