Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

First checkin

  • Loading branch information...
commit 12e54683867a2d189e32af76a8160455c95826e0 0 parents
@jterrace authored
Showing with 50,392 additions and 0 deletions.
  1. +2 −0  .gitignore
  2. +159 −0 Makefile
  3. +9 −0 abstract.rst
  4. +9 −0 acknowledgements.rst
  5. +8 −0 bibliography.rst
  6. BIN  ch-figs/fig/icons.png
  7. BIN  ch-figs/fig/overview.pdf
  8. +40,750 −0 ch-figs/fig/overview.svg
  9. BIN  ch-figs/fig/teddy_0_128.png
  10. BIN  ch-figs/fig/teddy_100_2048.png
  11. BIN  ch-figs/fig/teddy_25_256.png
  12. BIN  ch-figs/fig/teddy_50_512.png
  13. BIN  ch-figs/fig/teddy_75_1024.png
  14. BIN  ch-figs/fig/teddy_orig.png
  15. +190 −0 ch-figs/index.rst
  16. +50 −0 ch-intro/index.rst
  17. +57 −0 ch-lists/index.rst
  18. +21 −0 ch-math/index.rst
  19. +36 −0 ch-refs/index.rst
  20. +21 −0 ch-typography/headings.rst
  21. +13 −0 ch-typography/index.rst
  22. +34 −0 ch-typography/text.rst
  23. +273 −0 conf.py
  24. +8 −0 dedication.rst
  25. +16 −0 epilog.rst
  26. +81 −0 extensions/figtable.py
  27. +31 −0 extensions/fix_equation_ref.py
  28. +67 −0 extensions/html_mods.py
  29. +232 −0 extensions/latex_mods.py
  30. +614 −0 extensions/natbib/__init__.py
  31. +527 −0 extensions/natbib/latex_codec.py
  32. +175 −0 extensions/numfig.py
  33. +51 −0 extensions/numsec.py
  34. +39 −0 extensions/singlehtml_toc.py
  35. +60 −0 extensions/singletext.py
  36. +164 −0 extensions/subfig.py
  37. BIN  img/ccicon88x31.png
  38. +39 −0 index.rst
  39. +11 −0 index_tex.rst
  40. +460 −0 refs.bib
  41. +2 −0  requirements.txt
  42. +85 −0 static/colorbox/colorbox.css
  43. BIN  static/colorbox/images/border.png
  44. BIN  static/colorbox/images/controls.png
  45. BIN  static/colorbox/images/ie6/borderBottomCenter.png
  46. BIN  static/colorbox/images/ie6/borderBottomLeft.png
  47. BIN  static/colorbox/images/ie6/borderBottomRight.png
  48. BIN  static/colorbox/images/ie6/borderMiddleLeft.png
  49. BIN  static/colorbox/images/ie6/borderMiddleRight.png
  50. BIN  static/colorbox/images/ie6/borderTopCenter.png
  51. BIN  static/colorbox/images/ie6/borderTopLeft.png
  52. BIN  static/colorbox/images/ie6/borderTopRight.png
  53. BIN  static/colorbox/images/loading.gif
  54. BIN  static/colorbox/images/loading_background.png
  55. BIN  static/colorbox/images/overlay.png
  56. +4 −0 static/colorbox/jquery.colorbox-min.js
  57. +187 −0 static/custom.css
  58. +31 −0 templates/layout.html
  59. +3,892 −0 tex/Makefile
  60. +14 −0 tex/footer._tex
  61. +147 −0 tex/preamble._tex
  62. +236 −0 tex/puthesis.cls
  63. +1,097 −0 tex/refstyle.bst
  64. +481 −0 tex/sphinx.sty
  65. +9 −0 toc.txt
2  .gitignore
@@ -0,0 +1,2 @@
+_build
+
159 Makefile
@@ -0,0 +1,159 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+PAPER =
+BUILDDIR = _build
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
+
+help:
+ @echo "Please use \`make <target>' where <target> 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 " 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 " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
+
+clean:
+ -rm -rf $(BUILDDIR)/*
+
+html:
+ touch bibliography.rst && $(SPHINXBUILD) -a -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:
+ touch bibliography.rst && $(SPHINXBUILD) -t singlehtml -a -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/ContentConditioningandDistributionforDynamicVirtualWorlds.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/ContentConditioningandDistributionforDynamicVirtualWorlds.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/ContentConditioningandDistributionforDynamicVirtualWorlds"
+ @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/ContentConditioningandDistributionforDynamicVirtualWorlds"
+ @echo "# devhelp"
+
+epub:
+ $(SPHINXBUILD) -t epub -b epub -a $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+ @echo
+ @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+latex:
+ $(SPHINXBUILD) -t latex -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) -t latex -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through pdflatex..."
+ $(MAKE) -C $(BUILDDIR)/latex clean
+ $(MAKE) -C $(BUILDDIR)/latex
+ @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."
+
+singletext:
+ $(SPHINXBUILD) -b singletext $(ALLSPHINXOPTS) $(BUILDDIR)/singletext
+ @echo
+ @echo "Build finished. The text file is in $(BUILDDIR)/singletext."
+
+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."
9 abstract.rst
@@ -0,0 +1,9 @@
+:orphan:
+
+.. only:: html or text
+
+ Abstract
+ ========
+
+This project is a collection of extensions and monkey patches to Sphinx to
+better format a PhD thesis.
9 acknowledgements.rst
@@ -0,0 +1,9 @@
+:orphan:
+
+.. only:: html or text
+
+ Acknowledgements
+ ================
+
+Thanks to Sphinx for a kickass build system and docutils for the groundwork of
+multiple format output.
8 bibliography.rst
@@ -0,0 +1,8 @@
+:orphan:
+
+.. only:: html or text
+
+ Bibliography
+ ============
+
+.. cite:refs::
BIN  ch-figs/fig/icons.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  ch-figs/fig/overview.pdf
Binary file not shown
40,750 ch-figs/fig/overview.svg
40,750 additions, 0 deletions not shown
BIN  ch-figs/fig/teddy_0_128.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  ch-figs/fig/teddy_100_2048.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  ch-figs/fig/teddy_25_256.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  ch-figs/fig/teddy_50_512.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  ch-figs/fig/teddy_75_1024.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  ch-figs/fig/teddy_orig.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
190 ch-figs/index.rst
@@ -0,0 +1,190 @@
+.. _ch-figs:
+
+******************
+Figures and Tables
+******************
+
+Vector SVG Figures
+==================
+
+Vector figures are nicely supported. You should have a PDF file and an SVG
+file. The PDF will be used for the latex output and the SVG for the HTML
+output. The HTML output has a nice zoom feature using Colorbox.
+
+.. _fig-sirikata-overview:
+
+.. figure:: fig/overview.*
+ :alt: Sirikata System Overview
+ :width: 60%
+ :align: center
+
+ The Sirikata metaverse platform architecture.
+
+See an example in Figure |nbsp| :num:`fig-sirikata-overview`. I suggest making
+figures in something like Inkscape. If you have only a vector PDF, you can use
+pdf2svg to convert (``brew install pdf2svg`` or ``apt-get install pdf2svg``).
+
+Image Figures
+=============
+
+Regular rasterized images work fine too.
+
+.. _fig-open3dhub-icons:
+
+.. figure:: fig/icons.png
+ :alt: Open3DHub Browsing Interface
+ :width: 86.05%
+ :align: center
+
+ The Open3DHub website allows browsing of 3D meshes.
+
+A PNG example is shown in Figure |nbsp| :num:`fig-open3dhub-icons`.
+
+Subfigures
+==========
+
+The subfigure directives allow you to place multiple figures side-by-side in
+the document. Here's an example:
+
+.. subfigstart::
+
+.. _fig-cc-teddy-base:
+
+.. figure:: fig/teddy_0_128.png
+ :alt: Base Mesh + 128x128 Texture (334 KB)
+ :width: 90%
+ :align: center
+
+ Base Mesh + 128x128 Texture (334 KB)
+
+
+.. _fig-cc-teddy-25:
+
+.. figure:: fig/teddy_25_256.png
+ :alt: Base Mesh + 25% Stream + 256x256 Texture (568 KB)
+ :width: 90%
+ :align: center
+
+ Base Mesh + 25% Stream + 256x256 Texture (568 KB)
+
+
+.. _fig-cc-teddy-50:
+
+.. figure:: fig/teddy_50_512.png
+ :alt: Base Mesh + 50% Stream + 512x512 Texture (923 KB)
+ :width: 90%
+ :align: center
+
+ Base Mesh + 50% Stream + 512x512 Texture (923 KB)
+
+
+.. _fig-cc-teddy-75:
+
+.. figure:: fig/teddy_75_1024.png
+ :alt: Base Mesh + 75% Stream + 1024x1024 Texture (1755 KB)
+ :width: 90%
+ :align: center
+
+ Base Mesh + 75% Stream + 1024x1024 Texture (1755 KB)
+
+.. _fig-cc-teddy-100:
+
+.. figure:: fig/teddy_100_2048.png
+ :alt: Base Mesh + 100% Stream + 2048x2048 Texture (4385 KB)
+ :width: 90%
+ :align: center
+
+ Base Mesh + 100% Stream + 2048x2048 Texture (4385 KB)
+
+
+.. _fig-cc-teddy-original:
+
+.. figure:: fig/teddy_orig.png
+ :alt: Original Mesh (913 KB)
+ :width: 90%
+ :align: center
+
+ Original Mesh (913 KB)
+
+.. subfigend::
+ :width: 0.30
+ :alt: Example Model Resolutions
+ :label: fig-cc-teddy
+
+ Example of a teddy bear model at different resolutions of the
+ progressive format (1 draw call) and its original format (16 draw
+ calls). The size in KB assumes downloading progressively, |eg|
+ :num:`fig-cc-teddy-100`'s size includes lower-resolution textures.
+
+You can reference the entire Figure |nbsp| :num:`fig-cc-teddy` or one of its
+subfigures, |eg| Figure |nbsp| :num:`fig-cc-teddy-original`.
+
+Table
+=====
+
+Tables can be put inside the figtable directive which automatically numbers
+them, adds a caption, and adds a label.
+
+.. figtable::
+ :label: table-cc-file-size
+ :caption: Mean size of progressive format as a fraction of the
+ original across all test models, shown as a function of the
+ progressive stream downloaded and texture resolution.
+ :alt: Mean Size of Progressive Format
+ :spec: r r r r r r r
+
+ =========== ==== ==== ==== ==== ====
+ Progressive 128 256 512 1024 2048
+ =========== ==== ==== ==== ==== ====
+ 0% 0.53 0.63 0.81 1.03 1.35
+ 25% 0.65 0.75 0.97 1.16 1.45
+ 50% 0.74 0.85 1.02 1.26 1.58
+ 75% 0.79 0.95 1.11 1.34 1.70
+ 100% 0.88 0.99 1.20 1.44 1.82
+ =========== ==== ==== ==== ==== ====
+
+Table |nbsp| :num:`table-cc-file-size` has all right-aligned columns.
+
+.. figtable::
+ :label: table-mixed-align
+ :caption: This table has mixed alignment
+ :alt: Mixed Alignment Table
+ :spec: l r
+
+ ======================= =========================
+ Left Align Right Align
+ ======================= =========================
+ Some text is left align Followed by right-aligned
+ Some more text here And more text here
+ And even more text Also even more text here
+ ======================= =========================
+
+Table |nbsp| :num:`table-mixed-align` has one column left-aligned and one
+column right-aligned.
+
+Text Wrapping Table
+===================
+
+Text wrapping in tables work if you specify the width and either raggedleft or
+raggedright.
+
+.. figtable::
+ :label: fig-open3dhub-cfs
+ :caption: A list of Open3DHub's Cassandra column families and their descriptions
+ :alt: Open3DHub Cassandra Column Families
+ :spec: >{\raggedleft\arraybackslash}p{0.25\linewidth} p{0.65\linewidth}
+
+ ============================== ==============================================================================================================================
+ Column Family Description
+ ============================== ==============================================================================================================================
+ **Users** Stores a list of users who have authenticated with OpenID.
+ **Names** Stores a list of the 3D models in the database with their associated metadata.
+ **TempFiles** Temporarily stores the binary file data of uploaded files until they have been processed.
+ **Files** Stores the binary file data for uploaded and verified files.
+ **Sessions** Stores HTTP session information used by the Django framework to look up session state associated with a user's browser cookie.
+ **OpenIdAssocs, OpenIdNonces** Stores OpenID authentication information for users.
+ **CeleryResults** Stores the result of application processing tasks (see Section something).
+ **APIConsumers** Stores a list of consumers of the API for use with the OAuth protocol.
+ ============================== ==============================================================================================================================
+
+A text wrapping table example is shown in Figure |nbsp| :num:`fig-open3dhub-cfs`.
50 ch-intro/index.rst
@@ -0,0 +1,50 @@
+.. _ch-intro:
+
+************
+Introduction
+************
+
+Installation
+============
+
+Install the required Python packages::
+
+ pip install -r requirements.txt
+
+Building
+========
+
+You need ``make``. The following targets are supported:
+
+html
+ Builds HTML format, separated into sections
+singlehtml
+ Builds HTML format on a single page
+text
+ Builds text files, separated into sections
+singletext
+ Builds a single text file
+latexpdf
+ Builds into latex source files and then compiles into a PDF. Requires latex.
+
+Changes
+=======
+
+The following changes and additions have been made from vanilla Sphinx:
+
+* A cross-format bibtex bibliography based on sphinx-natbib
+* Tables that can go inside figures
+* Changed table formatting to look pretty, like booktabs
+* Improved alignment in table environment
+* Added support for short captions that show up in the "list of figures" section
+* Changed equation reference formatting from "(1)" to "1"
+* Full customization of latex preamble and style file
+* Numbered figures
+* Numbered section references
+* A singletext output that builds into a single text file, similar to singlehtml
+* A subfigure environment
+
+Documents Using sphinxtr
+========================
+
+* `Jeff Terrace's PhD Thesis <http://www.cs.princeton.edu/~jterrace/thesis/>`_
57 ch-lists/index.rst
@@ -0,0 +1,57 @@
+.. _ch-lists:
+
+*****
+Lists
+*****
+
+Unordered Lists
+===============
+
+* item 1
+* item 2
+
+ * item 3
+ * item 4
+
+* item 5
+
+Ordered Lists
+=============
+
+#. item 1
+#. item 2
+
+ a. item 3
+ b. item 4
+
+#. item 5
+
+Description Lists
+=================
+
+term1
+ definition 1
+term2
+ definition 2
+term3
+ definition 3
+
+Mixed
+=====
+
+term
+ definition
+
+ * list1
+ * list2
+
+term2
+ something
+
+ #. num1
+ #. num2
+
+ * list1
+ * list2
+
+ #. num3
21 ch-math/index.rst
@@ -0,0 +1,21 @@
+.. _ch-math:
+
+****
+Math
+****
+
+Math uses latex math syntax:
+
+.. math::
+
+ A^{''}_c =
+ \sqrt[3]{
+ (\frac{L^2_c}{\sum{L^2}})
+ (\frac{A_c}{\sum{A}})
+ (\frac{A'_c}{\sum{A'}})
+ } \cdot T
+
+Equations can have labels which you can reference |nbsp| :eq:`eq-cc-err-stop`.
+
+.. math:: \frac{log(1 + E_{current})}{log(1 + E_{max})}
+ :label: eq-cc-err-stop
36 ch-refs/index.rst
@@ -0,0 +1,36 @@
+.. _ch-refs:
+
+************************
+References and Citations
+************************
+
+You can reference a section by its label. This chapter is
+Chapter |nbsp| :ref:`ch-refs`.
+
+.. _sec-refs-sub1:
+
+Subsection
+==========
+
+This subsection is Section |nbsp| :ref:`sec-refs-sub1`.
+
+Citations
+=========
+
+COLLADA |nbsp| :cite:p:`collada` is a cool 3D file format. I wrote a paper about
+3D stuff |nbsp| :cite:p:`icmepaper`. The website we built is running |nbsp|
+:cite:p:`open3dhub`. The bibliography is in bibtex format.
+
+Footnotes
+=========
+
+Reference a footnote |nbsp| [#foot-something]_.
+
+External Links
+==============
+
+You can link to a `website <http://google.com/>`_.
+
+.. rubric:: Footnotes
+
+.. [#foot-something] This is a footnote at the end of the page or document.
21 ch-typography/headings.rst
@@ -0,0 +1,21 @@
+Headings
+========
+The title of this chapter, "Typography", is the first heading level. This
+section, "Headings", is the second level.
+
+Third-level
+-----------
+The third level heading. You probably shouldn't go beyond this because it just
+looks ridiculous, but you can anyway.
+
+Fourth-level
+^^^^^^^^^^^^
+The fourth level heading.
+
+Fifth-level
+"""""""""""
+The fifth level heading.
+
+.. rubric:: Rubric Heading
+
+A rubric heading is just a paragraph heading without document structure.
13 ch-typography/index.rst
@@ -0,0 +1,13 @@
+.. _ch-typography:
+
+**********
+Typography
+**********
+
+This chapter has typography stuff. This also shows how to have a toctree inside
+a chapter.
+
+.. toctree::
+
+ headings
+ text
34 ch-typography/text.rst
@@ -0,0 +1,34 @@
+Text
+====
+
+You can make *emphasized text*.
+
+You can make **bold text**.
+
+You can make ``fixed-width font``.
+
+You can make block quotes:
+
+ this is a block quote
+
+You can make code blocks::
+
+ this is a code block
+
+Some convenience substitutions are defined in the epilog:
+
+* |eg|
+* |ie|
+* |etal|
+* dash |dash|
+* non-breaking space -> |nbsp| <-
+
+You can have inline :sup:`superscript` or :sub:`subscript` text.
+
+Big quotes, also known as an epigraph:
+
+.. epigraph::
+
+ *Your avatar can look any way you want it to, up to the limitations of your equipment. If you're ugly, you can make your avatar beautiful. If you've just gotten out of bed, your avatar can be wearing beautiful clothes and professionally applied makeup. You can look like a gorilla or a dragon, or a giant talking penis in the Metaverse. Spend five minutes walking down the street, and you will see all of these.*
+
+ -- Neal Stephenson, Snow Crash
273 conf.py
@@ -0,0 +1,273 @@
+import sys, os
+
+# directory relative to this conf file
+CURDIR = os.path.abspath(os.path.dirname(__file__))
+# add custom extensions directory to python path
+sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'extensions'))
+
+# import the custom html and latex builders/translators/writers
+import html_mods
+import latex_mods
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# import order is important here
+extensions = [
+ 'fix_equation_ref',
+ 'sphinx.ext.mathjax',
+ 'sphinx.ext.ifconfig',
+ 'subfig',
+ 'numfig',
+ 'numsec',
+ 'natbib',
+ 'figtable',
+ 'singlehtml_toc',
+ 'singletext',
+ ]
+
+# 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'
+
+# General information about the project.
+project = u'The Sphinx Thesis Resource (sphinxtr)'
+author = u'Jeff Terrace'
+copyright = u'by %s, 2012.' % author
+version = '0.1'
+release = '0.1'
+
+# Turns on numbered figures for HTML output
+number_figures = True
+
+# configures bibliography
+# see http://wnielson.bitbucket.org/projects/sphinx-natbib/
+natbib = {
+ 'file': 'refs.bib',
+ 'brackets': '[]',
+ 'separator': ',',
+ 'style': 'numbers',
+ 'sort': True,
+}
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = [
+ '_build',
+ 'tex',
+ 'epilog.rst',
+ ]
+
+# The master toctree document.
+# Ideally, we wouldn't have to do this, but sphinx seems to have trouble with
+# directives inside only directives
+if tags.has('latex'):
+ master_doc = 'index_tex'
+ exclude_patterns.append('index.rst')
+else:
+ master_doc = 'index'
+ exclude_patterns.append('index_tex.rst')
+
+# A string of reStructuredText that will be included at the end of
+# every source file that is read.
+rst_epilog = open(os.path.join(CURDIR, 'epilog.rst'), 'r').read()
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+
+# -- 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 = 'sphinxdoc'
+
+# 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
+# "<project> v<release> documentation".
+html_title = "%s" % project
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+html_short_title = "Someone's PhD Thesis"
+
+# 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']
+
+# 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.
+template_files = ['localtoc.html', 'relations.html', 'sourcelink.html']
+if not tags.has('singlehtml'):
+ # only include search box for regular html, not single page html
+ template_files.append('searchbox.html')
+html_sidebars = {
+ '**': template_files,
+}
+
+# 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 = False
+
+# If false, no index is generated.
+html_use_index = False
+
+# 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 <link> 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
+
+# supresses the last dot in section numbers
+# changes "1. Introduction" -> "1 Introduction"
+# default string is ". "
+html_secnumber_suffix = " "
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'htmlhelpoutput'
+
+# location of mathjax script if you don't want to use CDN
+# mathjax_path = 'MathJax/MathJax.js?config=TeX-AMS-MML_HTMLorMML'
+
+
+
+
+# -- Options for LaTeX output --------------------------------------------------
+
+ADDITIONAL_PREAMBLE = """
+\input{preamble._tex}
+\usepackage{sphinx}
+"""
+
+ADDITIONAL_FOOTER = """
+\input{footer._tex}
+"""
+
+latex_elements = {
+ # The paper size ('letterpaper' or 'a4paper').
+ 'papersize': 'letterpaper',
+
+ # gets passed to \documentclass
+ # default options are single sided, double spaced
+ # you can change them with these options:
+ # * twoside
+ # * singlespace
+ 'classoptions': ',english,lof',
+
+ # The font size ('10pt', '11pt' or '12pt').
+ 'pointsize': '12pt',
+
+ # Additional stuff for the LaTeX preamble.
+ 'preamble': ADDITIONAL_PREAMBLE,
+
+ # Additional footer
+ 'footer': ADDITIONAL_FOOTER,
+
+ # disable font inclusion
+ 'fontpkg': '',
+ 'fontenc': '',
+
+ # disable fancychp
+ 'fncychap': '',
+
+ # get rid of the sphinx wrapper class file
+ 'wrapperclass': 'puthesis',
+
+ # override maketitle
+ 'maketitle': '\makefrontmatter',
+ 'tableofcontents': '',
+
+ # disable index printing
+ 'printindex': '',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual]).
+latex_documents = [
+ ('index_tex',
+ 'thesis.tex',
+ project,
+ author,
+ 'manual',
+ True),
+]
+
+latex_docclass = {
+ 'manual': 'puthesis',
+}
+
+latex_additional_files = [
+ 'tex/puthesis.cls',
+ 'tex/preamble._tex',
+ 'tex/footer._tex',
+ 'tex/sphinx.sty',
+ 'tex/Makefile',
+ 'tex/refstyle.bst',
+ 'refs.bib',
+]
+
+# 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 = False
+
8 dedication.rst
@@ -0,0 +1,8 @@
+:orphan:
+
+.. only:: html or text
+
+ Dedication
+ ----------
+
+To my parents.
16 epilog.rst
@@ -0,0 +1,16 @@
+
+.. |nbsp| unicode:: 0xA0
+ :trim:
+
+.. |eg| replace:: e.g.,
+
+.. |etal| replace:: et al.
+
+.. |ie| replace:: i.e.,
+
+.. |dash| unicode:: 0x2014
+ :trim:
+
+.. |br| raw:: html
+
+ <br />
81 extensions/figtable.py
@@ -0,0 +1,81 @@
+"""
+Adds a new directive called 'figtable' that creates a figure
+around a table.
+"""
+
+from docutils import nodes
+import docutils.parsers.rst.directives as directives
+from sphinx.util.compat import Directive
+from sphinx import addnodes
+
+class figtable(nodes.General, nodes.Element):
+ pass
+
+def visit_figtable_node(self, node):
+ pass
+
+def depart_figtable_node(self, node):
+ pass
+
+def visit_figtable_tex(self, node):
+ self.body.append('\n\n\\begin{figure}[tbp]\n\\capstart\n\\begin{center}\n')
+
+def depart_figtable_tex(self, node):
+ self.body.append('\n\\end{center}\n\\end{figure}\n')
+
+def visit_figtable_html(self, node):
+ atts = {'class': 'figure align-center'}
+ self.body.append(self.starttag(node, 'div', **atts) + '<center>')
+
+def depart_figtable_html(self, node):
+ self.body.append('</center></div>')
+
+class FigTableDirective(Directive):
+
+ has_content = True
+ optional_arguments = 3
+ final_argument_whitespace = True
+
+ option_spec = {'label': directives.uri,
+ 'spec': directives.unchanged,
+ 'caption': directives.unchanged,
+ 'alt': directives.unchanged}
+
+ def run(self):
+ label = self.options.get('label', None)
+ spec = self.options.get('spec', None)
+ caption = self.options.get('caption', None)
+ alt = self.options.get('alt', None)
+
+ figtable_node = figtable('', ids=[label] if label is not None else [])
+
+ if spec is not None:
+ table_spec_node = addnodes.tabular_col_spec()
+ table_spec_node['spec'] = spec
+ figtable_node.append(table_spec_node)
+
+ node = nodes.Element()
+ self.state.nested_parse(self.content, self.content_offset, node)
+ tablenode = node[0]
+ if alt is not None:
+ tablenode['alt'] = alt
+ figtable_node.append(tablenode)
+
+ if caption is not None:
+ caption_node = nodes.caption('', '', nodes.Text(caption))
+ figtable_node.append(caption_node)
+
+ if label is not None:
+ targetnode = nodes.target('', '', ids=[label])
+ figtable_node.append(targetnode)
+
+ return [figtable_node]
+
+def setup(app):
+ app.add_node(figtable,
+ html=(visit_figtable_html, depart_figtable_html),
+ singlehtml=(visit_figtable_html, depart_figtable_html),
+ latex=(visit_figtable_tex, depart_figtable_tex),
+ text=(visit_figtable_node, depart_figtable_node))
+
+ app.add_directive('figtable', FigTableDirective)
31 extensions/fix_equation_ref.py
@@ -0,0 +1,31 @@
+"""
+Fixes equation references from Sphinx math domain
+from Equation (1) to Equation 1, which is what they
+should be. Must be before sphinx.ext.math* in
+extensions list.
+"""
+
+from docutils import nodes
+import sphinx.ext.mathbase
+from sphinx.ext.mathbase import displaymath, eqref
+
+def number_equations(app, doctree, docname):
+ num = 0
+ numbers = {}
+ for node in doctree.traverse(displaymath):
+ if node['label'] is not None:
+ num += 1
+ node['number'] = num
+ numbers[node['label']] = num
+ else:
+ node['number'] = None
+ for node in doctree.traverse(eqref):
+ if node['target'] not in numbers:
+ continue
+ num = '%d' % numbers[node['target']]
+ node[0] = nodes.Text(num, num)
+
+sphinx.ext.mathbase.number_equations = number_equations
+
+def setup(app):
+ pass
67 extensions/html_mods.py
@@ -0,0 +1,67 @@
+import re
+from docutils import nodes
+import sphinx.writers.html
+
+BaseTranslator = sphinx.writers.html.SmartyPantsHTMLTranslator
+class CustomHTMLTranslator(BaseTranslator):
+ def visit_tabular_col_spec(self, node):
+ self.table_spec = re.split(r'[\s\|]+', node['spec'])
+ raise nodes.SkipNode
+
+ def bulk_text_processor(self, text):
+ if '~' in text:
+ text = text.replace('~', '&nbsp;')
+ return text
+
+ def visit_entry(self, node):
+ atts = {'class': []}
+ if isinstance(node.parent.parent, nodes.thead):
+ atts['class'].append('head')
+ if node.parent.parent.parent.stubs[node.parent.column]:
+ # "stubs" list is an attribute of the tgroup element
+ atts['class'].append('stub')
+ if atts['class']:
+ tagname = 'th'
+ atts['class'] = ' '.join(atts['class'])
+ else:
+ tagname = 'td'
+ del atts['class']
+ table_spec = getattr(self, 'table_spec', None)
+ if (tagname == 'td' or tagname == 'th') and table_spec is not None:
+ if len(table_spec) > node.parent.column:
+ colspec = table_spec[node.parent.column]
+
+ horiz_align = ''
+ vert_align = ''
+
+ if 'raggedright' in colspec or colspec == 'l':
+ horiz_align = ' align-left'
+ elif 'raggedleft' in colspec or colspec == 'r':
+ horiz_align = ' align-right'
+ elif 'center' in colspec or colspec == 'c':
+ horiz_align = ' align-center'
+
+ if 'p{' in colspec:
+ vert_align = ' align-top'
+ elif 'm{' in colspec:
+ vert_align = ' align-middle'
+ elif 'b{' in colspec:
+ vert_align = ' align-bottom'
+
+ align_type = {'l': 'left',
+ 'r': 'right',
+ 'c': 'center'}
+ atts['class'] = (atts.get('class', '') + horiz_align + vert_align)
+ node.parent.column += 1
+ if 'morerows' in node:
+ atts['rowspan'] = node['morerows'] + 1
+ if 'morecols' in node:
+ atts['colspan'] = node['morecols'] + 1
+ node.parent.column += node['morecols']
+ self.body.append(self.starttag(node, tagname, '', **atts))
+ self.context.append('</%s>\n' % tagname.lower())
+ if len(node) == 0: # empty cell
+ self.body.append('&nbsp;')
+ self.set_first_last(node)
+
+sphinx.writers.html.SmartyPantsHTMLTranslator = CustomHTMLTranslator
232 extensions/latex_mods.py
@@ -0,0 +1,232 @@
+# -*- coding: utf-8 -*-
+import os
+
+from docutils.io import FileOutput
+from docutils.frontend import OptionParser
+from docutils import nodes
+
+import sphinx.builders.latex
+from sphinx.util.smartypants import educate_quotes_latex
+from sphinx.writers.latex import LaTeXWriter
+from sphinx.util.console import bold
+from sphinx.util.osutil import copyfile
+from sphinx.util.texescape import tex_escape_map
+import sphinx.writers.latex
+
+# remove usepackage for sphinx here, we add it later in the preamble in conf.py
+sphinx.writers.latex.HEADER = sphinx.writers.latex.HEADER.replace('\usepackage{sphinx}', '')
+
+BaseTranslator = sphinx.writers.latex.LaTeXTranslator
+
+class DocTranslator(BaseTranslator):
+
+ def visit_caption(self, node):
+ caption_idx = node.parent.index(node)
+ if caption_idx > 0:
+ look_node = node.parent.children[caption_idx - 1]
+ else:
+ look_node = node.parent
+
+ short_caption = look_node.get('alt', '').translate(tex_escape_map)
+ if short_caption != "":
+ short_caption = '[%s]' % short_caption
+
+ self.in_caption += 1
+ self.body.append('\\caption%s{' % short_caption)
+ def depart_caption(self, node):
+ self.body.append('}')
+ self.in_caption -= 1
+
+ def visit_Text(self, node):
+ if self.verbatim is not None:
+ self.verbatim += node.astext()
+ else:
+ text = self.encode(node.astext())
+ if '\\textasciitilde{}' in text:
+ text = text.replace('\\textasciitilde{}', '~')
+ if not self.no_contractions:
+ text = educate_quotes_latex(text)
+ self.body.append(text)
+
+ def visit_table(self, node):
+ if self.table:
+ raise UnsupportedError(
+ '%s:%s: nested tables are not yet implemented.' %
+ (self.curfilestack[-1], node.line or ''))
+
+ self.table = sphinx.writers.latex.Table()
+ self.table.longtable = False
+ self.tablebody = []
+ self.tableheaders = []
+
+ # Redirect body output until table is finished.
+ self._body = self.body
+ self.body = self.tablebody
+
+ def depart_table(self, node):
+ self.body = self._body
+
+ if 'p{' in self.table.colspec or 'm{' in self.table.colspec or 'b{' in self.table.colspec:
+ self.body.append('\n\\bodyspacing\n')
+
+ self.body.append('\n\\begin{tabular}')
+
+ if self.table.colspec:
+ self.body.append(self.table.colspec)
+ else:
+ self.body.append('{|' + ('l|' * self.table.colcount) + '}')
+
+ self.body.append('\n')
+
+ if self.table.caption is not None:
+ for id in self.next_table_ids:
+ self.body.append(self.hypertarget(id, anchor=False))
+ self.next_table_ids.clear()
+
+ self.body.append('\\toprule\n')
+
+ self.body.extend(self.tableheaders)
+
+ self.body.append('\\midrule\n')
+
+ self.body.extend(self.tablebody)
+
+ self.body.append('\\bottomrule\n')
+
+ self.body.append('\n\\end{tabular}\n')
+
+ self.table = None
+ self.tablebody = None
+
+ def depart_row(self, node):
+ if self.previous_spanning_row == 1:
+ self.previous_spanning_row = 0
+ self.body.append('\\\\\n')
+ else:
+ self.body.append('\\\\\n')
+ self.table.rowcount += 1
+
+ def depart_literal_block(self, node):
+ code = self.verbatim.rstrip('\n')
+ lang = self.hlsettingstack[-1][0]
+ linenos = code.count('\n') >= self.hlsettingstack[-1][1] - 1
+ highlight_args = node.get('highlight_args', {})
+ if 'language' in node:
+ # code-block directives
+ lang = node['language']
+ highlight_args['force'] = True
+ if 'linenos' in node:
+ linenos = node['linenos']
+ def warner(msg):
+ self.builder.warn(msg, (self.curfilestack[-1], node.line))
+ hlcode = self.highlighter.highlight_block(code, lang, warn=warner,
+ linenos=linenos, **highlight_args)
+ hlcode = hlcode.replace('\$', '$')
+ hlcode = hlcode.replace('\%', '%')
+ # workaround for Unicode issue
+ hlcode = hlcode.replace(u'', u'@texteuro[]')
+ # must use original Verbatim environment and "tabular" environment
+ if self.table:
+ hlcode = hlcode.replace('\\begin{Verbatim}',
+ '\\begin{OriginalVerbatim}')
+ self.table.has_problematic = True
+ self.table.has_verbatim = True
+ # get consistent trailer
+ hlcode = hlcode.rstrip()[:-14] # strip \end{Verbatim}
+ hlcode = hlcode.rstrip() + '\n'
+ hlcode = '\n' + hlcode + '\\end{%sVerbatim}\n' % (self.table and 'Original' or '')
+ hlcode = hlcode.replace('Verbatim', 'lstlisting')
+ begin_bracket = hlcode.find('[')
+ end_bracket = hlcode.find(']')
+ hlcode = hlcode[:begin_bracket] + '[]' + hlcode[end_bracket+1:]
+ self.body.append(hlcode)
+ self.verbatim = None
+
+ def visit_figure(self, node):
+ ids = ''
+ for id in self.next_figure_ids:
+ ids += self.hypertarget(id, anchor=False)
+ self.next_figure_ids.clear()
+ if 'width' in node and node.get('align', '') in ('left', 'right'):
+ self.body.append('\\begin{wrapfigure}{%s}{%s}\n\\centering' %
+ (node['align'] == 'right' and 'r' or 'l',
+ node['width']))
+ self.context.append(ids + '\\end{wrapfigure}\n')
+ else:
+ if (not 'align' in node.attributes or
+ node.attributes['align'] == 'center'):
+ # centering does not add vertical space like center.
+ align = '\n\\centering'
+ align_end = ''
+ else:
+ # TODO non vertical space for other alignments.
+ align = '\\begin{flush%s}' % node.attributes['align']
+ align_end = '\\end{flush%s}' % node.attributes['align']
+ self.body.append('\\begin{figure}[tbp]%s\n' % align)
+ if any(isinstance(child, nodes.caption) for child in node):
+ self.body.append('\\capstart\n')
+ self.context.append(ids + align_end + '\\end{figure}\n')
+
+sphinx.writers.latex.LaTeXTranslator = DocTranslator
+
+class CustomLaTeXTranslator(DocTranslator):
+ def astext(self):
+ return (#HEADER % self.elements +
+ #self.highlighter.get_stylesheet() +
+ u''.join(self.body)
+ #'\n' + self.elements['footer'] + '\n' +
+ #self.generate_indices() +
+ #FOOTER % self.elements
+ )
+
+ def unknown_departure(self, node):
+ if node.tagname == 'only':
+ return
+ return super(CustomLaTeXTranslator, self).unknown_departure(node)
+
+ def unknown_visit(self, node):
+ if node.tagname == 'only':
+ return
+ return super(CustomLaTeXTranslator, self).unknown_visit(node)
+
+class CustomLaTeXBuilder(sphinx.builders.latex.LaTeXBuilder):
+
+ def write(self, *ignored):
+ super(CustomLaTeXBuilder, self).write(*ignored)
+
+ backup_translator = sphinx.writers.latex.LaTeXTranslator
+ sphinx.writers.latex.LaTeXTranslator = CustomLaTeXTranslator
+ backup_doc = sphinx.writers.latex.BEGIN_DOC
+ sphinx.writers.latex.BEGIN_DOC = ''
+
+ # output these as include files
+ for docname in ['abstract', 'dedication', 'acknowledgements']:
+ destination = FileOutput(
+ destination_path=os.path.join(self.outdir, '%s.inc' % docname),
+ encoding='utf-8')
+
+ docwriter = LaTeXWriter(self)
+ doctree = self.env.get_doctree(docname)
+
+ docsettings = OptionParser(
+ defaults=self.env.settings,
+ components=(docwriter,)).get_default_values()
+ doctree.settings = docsettings
+ docwriter.write(doctree, destination)
+
+ sphinx.writers.latex.LaTeXTranslator = backup_translator
+ sphinx.writers.latex.BEGIN_DOC = backup_doc
+
+ def finish(self, *args, **kwargs):
+ super(CustomLaTeXBuilder, self).finish(*args, **kwargs)
+ # copy additional files again *after* tex support files so we can override them!
+ if self.config.latex_additional_files:
+ self.info(bold('copying additional files again...'), nonl=1)
+ for filename in self.config.latex_additional_files:
+ self.info(' '+filename, nonl=1)
+ copyfile(os.path.join(self.confdir, filename),
+ os.path.join(self.outdir, os.path.basename(filename)))
+ self.info()
+
+# monkey patch the shit out of it
+sphinx.builders.latex.LaTeXBuilder = CustomLaTeXBuilder
614 extensions/natbib/__init__.py
@@ -0,0 +1,614 @@
+from docutils import nodes, transforms
+from docutils.parsers.rst import directives
+
+from sphinx import addnodes
+from sphinx.domains import Domain, ObjType
+from sphinx.locale import l_, _
+from sphinx.roles import XRefRole
+from sphinx.util.compat import Directive
+
+from pybtex.database.input import bibtex
+import pybtex.style.names.plain
+import pybtex.style.names.lastfirst
+import pybtex.backends.plaintext
+
+import collections
+import latex_codec
+import os
+import re
+
+# fix pybtex bug in some versions
+try:
+ import pybtex.utils
+
+ def _fixed_pybtex_get(self, item, default=None):
+ """A case insensitive get."""
+ try:
+ return self[self._keys[item.lower()]]
+ except KeyError:
+ return default
+
+ pybtex.utils.CaseInsensitiveDict.get = _fixed_pybtex_get
+except:
+ pass
+
+
+latex_codec.register()
+
+DEFAULT_CONF = {
+ 'file': '',
+ 'brackets': '()',
+ 'separator': ';',
+ 'style': 'authoryear', # 'numbers', 'super'
+ 'sort': False,
+ 'sort_compress': False
+}
+
+ROLES = [
+ 'p', 'ps', 'alp', 'alps',
+ 't', 'ts', 'alt', 'alts',
+ 'author', 'authors', 'year', 'yearpar', 'text', 'title'
+]
+
+KEY, PREV, NEXT = range(3)
+
+class OrderedSet(collections.MutableSet):
+ """
+ From: http://code.activestate.com/recipes/576694/
+ """
+ def __init__(self, iterable=None):
+ self.end = end = []
+ end += [None, end, end] # sentinel node for doubly linked list
+ self.map = {} # key --> [key, prev, next]
+ if iterable is not None:
+ self |= iterable
+
+ def __len__(self):
+ return len(self.map)
+
+ def __contains__(self, key):
+ return key in self.map
+
+ def add(self, key):
+ if key not in self.map:
+ end = self.end
+ curr = end[PREV]
+ curr[NEXT] = end[PREV] = self.map[key] = [key, curr, end]
+
+ def discard(self, key):
+ if key in self.map:
+ key, prev, next = self.map.pop(key)
+ prev[NEXT] = next
+ next[PREV] = prev
+
+ def __iter__(self):
+ end = self.end
+ curr = end[NEXT]
+ while curr is not end:
+ yield curr[KEY]
+ curr = curr[NEXT]
+
+ def __reversed__(self):
+ end = self.end
+ curr = end[PREV]
+ while curr is not end:
+ yield curr[KEY]
+ curr = curr[PREV]
+
+ def pop(self, last=True):
+ if not self:
+ raise KeyError('set is empty')
+ key = next(reversed(self)) if last else next(iter(self))
+ self.discard(key)
+ return key
+
+ def __repr__(self):
+ if not self:
+ return '%s()' % (self.__class__.__name__,)
+ return '%s(%r)' % (self.__class__.__name__, list(self))
+
+ def __eq__(self, other):
+ if isinstance(other, OrderedSet):
+ return len(self) == len(other) and list(self) == list(other)
+ return set(self) == set(other)
+
+ def __del__(self):
+ self.clear() # remove circular references
+
+
+SUBSUP_RE = re.compile(r'([\s\S^\^\_]*)([\^\_]){?([\S\s^}]*)}?')
+def latex_to_nodes(text):
+ """
+ Convert '_{}' and '^{}' text to node representation.
+ TODO: This doesn't work yet
+ """
+ return nodes.inline(text, text)
+ m = SUBSUP_RE.findall(text)
+ if m:
+ n += latex_to_nodes(m[0][0])
+ if m[0][1] == '^':
+ n += nodes.superscript(latex_to_nodes(m[0][2]))
+ elif m[0][1] == '_':
+ n += nodes.subscript(latex_to_nodes(m[0][2]))
+ else:
+ n = nodes.inline(text, text)
+ return n
+
+def parse_keys(rawtext):
+ # Get the keys and any pre- and post-ciation text
+ # Spaces nor commas are allowed in cite keys, so we split on commas
+ # first. This will give us a list of keys, however, the last item may have
+ # pre- and post-citation text (in brackets "[pre][post]")
+ #
+ # TODO: This isn't the best implementation and this should also
+ # handle errors
+ pre = u''
+ post = u''
+ keys = []
+ for k in rawtext.split(','):
+ k = k.strip() # Remove leading and trailing whitespace
+ k = k.split(' ', 1) # Split on the first space in the key, if any
+ if len(k) > 1:
+ # We have some extra text here
+ k, text = k
+ bo, bc = 0, 0
+ for c in text.strip():
+ if c == "[" and bo == bc:
+ bo += 1
+ elif c == "]" and bo == bc + 1:
+ bc += 1
+ else:
+ if bc == 0:
+ pre += c
+ else:
+ post += c
+ if bc == bo and bc == 1:
+ post = pre
+ pre = u''
+ else:
+ k = k[0]
+ keys.append(k)
+ return (keys, pre, post)
+
+
+class Citations(object):
+ def __init__(self, env):
+ self.conf = DEFAULT_CONF.copy()
+ self.conf.update(env.config.natbib)
+
+ self.file_name = None
+ self.parser = None
+ self.data = None
+ self.ref_map = {}
+
+ file_name = self.conf.get('file')
+ if file_name:
+ self.file_name = file_name
+ self.parser = bibtex.Parser()
+ self.data = self.parser.parse_file(self.file_name)
+
+ def get(self, key):
+ return self.data.entries.get(key)
+
+class CitationTransform(object):
+ """
+ This class is meant to be applied to a ``docutils.nodes.pending`` node when a
+ ``cite`` role is encountered. Later (during the resolve_xref stage) this
+ class can be used to generate the proper citation reference nodes for
+ insertion into the actual document.
+ """
+ def __init__(self, refs, pre, post, typ, global_keys, config):
+ self.refs = refs
+ self.pre = pre
+ self.post = post
+ self.config = config
+ self.typ = typ
+ self.global_keys = global_keys
+
+ def __repr__(self):
+ return '<%s>' % self.__str__()
+
+ def __str__(self):
+ return ','.join([r.key for r in self.refs])
+
+ def get_ref_num(self, key):
+ for i, k in enumerate(self.global_keys):
+ if k == key:
+ return i + 1
+
+ def get_author(self, authors, all_authors=False):
+ if len(authors) == 0:
+ author = ''
+ elif len(authors) > 2 and not all_authors:
+ author = u'%s et al.' % authors[0].last()[0]
+ else:
+ author = u"%s and %s" % (u', '.join([a.last()[0] for a in authors[:-1]]),
+ authors[-1].last()[0])
+ author = author.replace('{', '')
+ author = author.replace('}', '')
+ return author
+
+ def cite(self, cmd, refuri, global_keys=None):
+ """
+ Return a docutils Node consisting of properly formatted citations children
+ nodes.
+ """
+ if global_keys is not None:
+ self.global_keys = global_keys
+ bo, bc = self.config['brackets']
+ sep = u'%s ' % self.config['separator']
+ style = self.config['style']
+ all_auths = (cmd.endswith('s'))
+ alt = (cmd.startswith('alt') or \
+ (cmd.startswith('alp')) or \
+ (style == 'citeyear'))
+
+ if (cmd.startswith('p') or cmd == 'yearpar') and style != 'super':
+ node = nodes.inline(bo, bo, classes=['citation'])
+ else:
+ node = nodes.inline('', '', classes=['citation'])
+
+ if self.pre:
+ pre = u"%s " % self.pre.decode('latex')
+ node += nodes.inline(pre, pre, classes=['pre'])
+
+ for i, ref in enumerate(self.refs):
+ authors = ref.persons.get('author', [])
+ author_text = self.get_author(authors, all_auths).decode('latex')
+ lrefuri = refuri + '#citation-' + nodes.make_id(ref.key)
+
+ if i > 0 and i < len(self.refs):
+ if style == "authoryear":
+ node += nodes.inline(sep, sep)
+ else:
+ if style == "super":
+ node += nodes.superscript(', ', ', ')
+ else:
+ node += nodes.inline(', ', ', ')
+
+ if cmd == 'title':
+ title = ref.fields.get('title')
+ if title is None:
+ title = ref.fields.get('key', '')
+ author_text = title
+
+ if (style == "authoryear" and (cmd.startswith('p') or cmd.startswith('alp'))) or \
+ (cmd.startswith('t') or cmd.startswith('alt') or cmd.startswith('author')):
+ node += nodes.reference(author_text, author_text, internal=True, refuri=lrefuri)
+
+ if cmd.startswith('p') or cmd.startswith('alp'):
+ node += nodes.inline(', ', ', ')
+ else:
+ node += nodes.inline(' ', ' ')
+
+ # Add in either the year or the citation number
+ if cmd == 'title':
+ pass
+ elif cmd.startswith('author'):
+ pass
+ else:
+ if style != 'authoryear':
+ num = self.get_ref_num(ref.key)
+ else:
+ num = ref.fields.get('year')
+
+ refnode = nodes.reference(str(num), str(num), internal=True, refuri=lrefuri)
+
+ if cmd.startswith('t') and style != 'super':
+ node += nodes.inline(bo, bo)
+
+ if style == 'super':
+ node += nodes.superscript('', '', refnode)
+ else:
+ node += refnode
+
+ if cmd.startswith('t') and style != 'super':
+ node += nodes.inline(bc, bc)
+
+ if self.post:
+ post = u", %s" % self.post.decode('latex')
+ node += nodes.inline(post, post, classes=['post'])
+
+ if (cmd.startswith('p') or cmd == 'yearpar') and style != 'super':
+ node += nodes.inline(bc, bc, classes=['citation'])
+
+ return node
+
+
+def sort_references(refs, citations):
+ def sortkey(key):
+ # sort by author last names, but if no author, sort by title
+ citation = citations.get(key)
+ authorsort = u''.join(map(unicode, citation.persons.get('author', '')))
+ if len(authorsort) > 0:
+ authorsort = authorsort.replace('{', '')
+ authorsort = authorsort.replace('}', '')
+ return authorsort.upper()
+ titlesort = citation.fields.get('title', '')
+ titlesort = titlesort.replace('{', '')
+ titlesort = titlesort.replace('}', '')
+ return titlesort.upper()
+ sortedrefs = sorted(refs, key=sortkey)
+ return OrderedSet(sortedrefs)
+
+class CitationXRefRole(XRefRole):
+ def __call__(self, typ, rawtext, text, lineno, inliner, options={}, \
+ content=[]):
+ """
+ When a ``cite`` role is encountered, we replace it with a
+ ``docutils.nodes.pending`` node that uses a ``CitationTrasform`` for
+ generating the proper citation reference representation during the
+ resolve_xref phase.
+ """
+ rnodes = super(CitationXRefRole, self).__call__(typ, rawtext, text, lineno,
+ inliner, options, content)
+ rootnode = rnodes[0][0]
+
+ env = inliner.document.settings.env
+ citations = env.domains['cite'].citations
+
+ # Get the config at this point in the document
+ config = {}
+ for opt in ['style', 'brackets', 'separator', 'sort', 'sort_compress']:
+ config[opt] = env.temp_data.get("cite_%s" % opt, env.domaindata['cite']['conf'].get(opt, DEFAULT_CONF[opt]))
+
+ if typ == "cite:text":
+ # A ``text`` citation is unique because it doesn't reference a cite-key
+ keys = []
+ pre, post = text, ''
+ else:
+ keys, pre, post = parse_keys(text)
+ for key in keys:
+ if citations.get(key) is None:
+ env.warn(env.docname, "cite-key `%s` not found in bibtex file" % key, lineno)
+ continue
+ env.domaindata['cite']['keys'].add(key)
+ env.domaindata['cite']['keys'] = sort_references(env.domaindata['cite']['keys'], citations)
+
+ data = {'keys': keys,
+ 'pre': pre,
+ 'post': post,
+ 'typ': typ,
+ 'global_keys': env.domaindata['cite']['keys'],
+ 'config': config}
+
+ rootnode += nodes.pending(CitationTransform, data)
+ return [rootnode], []
+
+class CitationConfDirective(Directive):
+ """
+ Allows the user to change the citation style on a per-page or
+ per-block basis.
+ """
+ has_content = False
+ required_arguments = 0
+ optional_arguments = 1
+ option_spec = {
+ 'brackets': directives.unchanged,
+ 'separator': directives.unchanged,
+ 'style': directives.unchanged,
+ 'sort': directives.flag,
+ 'sort_compress': directives.flag,
+ }
+
+ def run(self):
+ env = self.state.document.settings.env
+
+ # TODO: verify options
+ if self.arguments:
+ env.temp_data['cite_style'] = self.arguments[0]
+ else:
+ env.temp_data['cite_style'] = self.options.get('style', DEFAULT_CONF['style'])
+
+ try:
+ self.options.pop('style')
+ except:
+ pass
+
+ for k, v in self.options.items():
+ env.temp_data['cite_%s' % k] = v
+
+ return []
+
+class CitationReferencesDirective(Directive):
+ """
+ Generates the actual reference list.
+ """
+ has_content = False
+ required_arguments = 0
+ optional_arguments = 0
+
+ # TODO: Implement support for multiple bib files
+ option_spec = {
+ 'path': directives.unchanged,
+ }
+
+ def get_reference_node(self, ref):
+ node = nodes.inline('', '', classes=[ref.type, 'reference'])
+
+ namestyler = pybtex.style.names.plain.NameStyle()
+ plaintext = pybtex.backends.plaintext.Backend()
+
+ # Authors
+ authors = ref.persons.get('author', [])
+ for i, author in enumerate(authors):
+ authortext = namestyler.format(author, abbr=True).format().render(plaintext)
+ authortext = authortext.replace('{', '')
+ authortext = authortext.replace('}', '')
+ authortext = authortext.decode('latex')
+ text = authortext
+
+ text = text.strip()
+ auth_node = latex_to_nodes(text)
+ auth_node['classes'].append('author')
+ node += auth_node
+
+ if i + 1 < len(authors):
+ node += nodes.inline(', ', ', ')
+ else:
+ ending = '%s ' % ('' if text.endswith('.') else '.')
+ node += nodes.inline(ending, ending)
+
+ # Title
+ title = ref.fields.get('title')
+ if title is None:
+ title = ref.fields.get('key')
+ if title:
+ title = title.decode('latex')
+ title = title.replace('{', '')
+ title = title.replace('}', '')
+ node += nodes.inline(title, title, classes=['title'])
+ node += nodes.inline('. ', '. ')
+
+ # @phdthesis
+ if ref.type == 'phdthesis':
+ school = ref.fields.get('school')
+ school = school.decode('latex')
+ text = 'PhD Thesis, %s, ' % school
+ node += nodes.inline(text, text)
+
+ # Publication
+ pub = ref.fields.get('journal')
+ if not pub:
+ pub = ref.fields.get('booktitle')
+ if pub:
+ pub = pub.decode('latex')
+ pub = pub.replace('{', '')
+ pub = pub.replace('}', '')
+ node += nodes.emphasis(pub, pub, classes=['publication'])
+ node += nodes.inline(', ', ', ')
+
+ vol = ref.fields.get('volume')
+ pages = ref.fields.get('pages')
+ year = ref.fields.get('year')
+
+ if pub is None:
+ howpub = ref.fields.get('howpublished')
+ if howpub is not None and howpub.startswith('\url{'):
+ url = howpub[5:-1]
+ refnode = nodes.reference('', '', internal=False, refuri=url)
+ refnode += nodes.Text(url, url)
+ node += refnode
+ if vol or pages or year:
+ node += nodes.inline(', ', ', ')
+
+ if vol:
+ vol = vol.decode('latex')
+ node += nodes.inline(vol, vol, classes=['volume'])
+ node += nodes.inline(':', ':')
+
+ if pages:
+ pages = pages.decode('latex')
+ node += nodes.inline(pages, pages, classes=['pages'])
+ node += nodes.inline(', ', ', ')
+
+ if year:
+ year = year.decode('latex')
+ node += nodes.inline(year, year, classes=['year'])
+ node += nodes.inline('.', '.')
+
+ return node
+
+ def run(self):
+ """
+ Generate the definition list that displays the actual references.
+ """
+ env = self.state.document.settings.env
+ keys = env.domaindata['cite']['keys']
+ env.domaindata['cite']['refdoc'] = env.docname
+
+ citations = env.domains['cite'].citations
+
+ # TODO: implement
+ #env.domaindata['cite']['refdocs'][env.docname] = Citations(env, path)
+
+ tbody = nodes.tbody('')
+ for i, key in enumerate(keys):
+ row = nodes.row('')
+ nid = "citation-%s" % nodes.make_id(key)
+ row['classes'].append('footnote')
+ row['ids'].append(nid)
+ row['names'].append(nid)
+
+ numcol = nodes.entry('', nodes.paragraph('', '[%d]' % (i + 1)))
+ definition = self.get_reference_node(citations.get(key))
+ refcol = nodes.entry('', nodes.paragraph('', '', definition))
+ row.extend([numcol, refcol])
+
+ tbody.append(row)
+
+ table_spec_node = addnodes.tabular_col_spec()
+ table_spec_node['spec'] = 'cl'
+
+ node = nodes.table('',
+ table_spec_node,
+ nodes.tgroup('',
+ nodes.colspec(colwidth=10, classes=['label']),
+ nodes.colspec(colwidth=90),
+ tbody))
+
+ return [node]
+
+class CitationDomain(Domain):
+ name = "cite"
+ label = "citation"
+
+ object_types = {
+ 'citation': ObjType(l_('citation'), *ROLES, searchprio= -1),
+ }
+
+ directives = {
+ 'conf': CitationConfDirective,
+ 'refs': CitationReferencesDirective
+ }
+ roles = dict([(r, CitationXRefRole()) for r in ROLES])
+
+ initial_data = {
+ 'keys': OrderedSet(), # Holds cite-keys in order of reference
+ 'conf': DEFAULT_CONF,
+ 'refdocs': {}
+ }
+
+ def __init__(self, env):
+ super(CitationDomain, self).__init__(env)
+
+ # Update conf
+ env.domaindata['cite']['conf'].update(env.config.natbib)
+
+ # TODO: warn if citations can't parse bibtex file
+ self.citations = Citations(env)
+
+ def resolve_xref(self, env, fromdocname, builder,
+ typ, target, node, contnode):
+
+ refdoc = env.domaindata['cite'].get('refdoc')
+ if not refdoc:
+ env.warn(fromdocname , 'no `refs` directive found; citations will have dead links', node.line)
+ refuri = ''
+ else:
+ refuri = builder.get_relative_uri(fromdocname, refdoc)
+
+ for nd in node.children:
+ if isinstance(nd, nodes.pending):
+ nd.details['refs'] = []
+
+ if builder.name == 'latex':
+ cite_keys = nd.details['keys']
+ cite_keys = ','.join(cite_keys)
+ cite_node = nodes.citation_reference(cite_keys, cite_keys)
+ return cite_node
+
+ for key in nd.details.pop('keys'):
+ ref = self.citations.get(key)
+ if ref is None:
+ continue
+ nd.details['refs'].append(ref)
+
+ transform = nd.transform(**nd.details)
+ node = transform.cite(typ, refuri, global_keys=env.domaindata['cite']['keys'])
+
+ return node
+
+def setup(app):
+ app.add_config_value('natbib', DEFAULT_CONF, 'env')
+ app.add_domain(CitationDomain)
527 extensions/natbib/latex_codec.py
@@ -0,0 +1,527 @@
+"""latex.py
+
+Character translation utilities for LaTeX-formatted text.
+
+Usage:
+ - unicode(string,'latex')
+ - ustring.decode('latex')
+are both available just by letting "import latex" find this file.
+ - unicode(string,'latex+latin1')
+ - ustring.decode('latex+latin1')
+where latin1 can be replaced by any other known encoding, also
+become available by calling latex.register().
+
+We also make public a dictionary latex_equivalents,
+mapping ord(unicode char) to LaTeX code.
+
+D. Eppstein, October 2003.
+"""
+
+from __future__ import generators
+import codecs
+import re
+from sets import Set
+
+def register():
+ """Enable encodings of the form 'latex+x' where x describes another encoding.
+ Unicode characters are translated to or from x when possible, otherwise
+ expanded to latex.
+ """
+ codecs.register(_registry)
+
+def getregentry():
+ """Encodings module API."""
+ return _registry('latex')
+
+def _registry(encoding):
+ if encoding == 'latex':
+ encoding = None
+ elif encoding.startswith('latex+'):
+ encoding = encoding[6:]
+ else:
+ return None
+
+ class Codec(codecs.Codec):
+ def encode(self,input,errors='strict'):
+ """Convert unicode string to latex."""
+ output = []
+ for c in input:
+ if encoding:
+ try:
+ output.append(c.encode(encoding))
+ continue
+ except:
+ pass
+ if ord(c) in latex_equivalents:
+ output.append(latex_equivalents[ord(c)])
+ else:
+ output += ['{\\char', str(ord(c)), '}']
+ return ''.join(output), len(input)
+
+ def decode(self,input,errors='strict'):
+ """Convert latex source string to unicode."""
+ if encoding:
+ input = unicode(input,encoding,errors)
+
+ # Note: we may get buffer objects here.
+ # It is not permussable to call join on buffer objects
+ # but we can make them joinable by calling unicode.
+ # This should always be safe since we are supposed
+ # to be producing unicode output anyway.
+ x = map(unicode,_unlatex(input))
+ return u''.join(x), len(input)
+
+ class StreamWriter(Codec,codecs.StreamWriter):
+ pass
+
+ class StreamReader(Codec,codecs.StreamReader):
+ pass
+
+ return (Codec().encode,Codec().decode,StreamReader,StreamWriter)
+
+def _tokenize(tex):
+ """Convert latex source into sequence of single-token substrings."""
+ start = 0
+ try:
+ # skip quickly across boring stuff
+ pos = _stoppers.finditer(tex).next().span()[0]
+ except StopIteration:
+ yield tex
+ return
+
+ while 1:
+ if pos > start:
+ yield tex[start:pos]
+ if tex[start] == '\\' and not (tex[pos-1].isdigit() and tex[start+1].isalpha()):
+ while pos < len(tex) and tex[pos].isspace(): # skip blanks after csname
+ pos += 1
+
+ while pos < len(tex) and tex[pos] in _ignore:
+ pos += 1 # flush control characters
+ if pos >= len(tex):
+ return
+ start = pos
+ if tex[pos:pos+2] in {'$$':None, '/~':None}: # protect ~ in urls
+ pos += 2
+ elif tex[pos].isdigit():
+ while pos < len(tex) and tex[pos].isdigit():
+ pos += 1
+ elif tex[pos] == '-':
+ while pos < len(tex) and tex[pos] == '-':
+ pos += 1
+ elif tex[pos] != '\\' or pos == len(tex) - 1:
+ pos += 1
+ elif not tex[pos+1].isalpha():
+ pos += 2
+ else:
+ pos += 1
+ while pos < len(tex) and tex[pos].isalpha():
+ pos += 1
+ if tex[start:pos] == '\\char' or tex[start:pos] == '\\accent':
+ while pos < len(tex) and tex[pos].isdigit():
+ pos += 1
+
+class _unlatex:
+ """Convert tokenized tex into sequence of unicode strings. Helper for decode()."""
+
+ def __iter__(self):
+ """Turn self into an iterator. It already is one, nothing to do."""
+ return self
+
+ def __init__(self,tex):
+ """Create a new token converter from a string."""
+ self.tex = tuple(_tokenize(tex)) # turn tokens into indexable list
+ self.pos = 0 # index of first unprocessed token
+ self.lastoutput = 'x' # lastoutput must always be nonempty string
+
+ def __getitem__(self,n):
+ """Return token at offset n from current pos."""
+ p = self.pos + n
+ t = self.tex
+ return p < len(t) and t[p] or None
+
+ def next(self):
+ """Find and return another piece of converted output."""
+ if self.pos >= len(self.tex):
+ raise StopIteration
+ nextoutput = self.chunk()
+ if self.lastoutput[0] == '\\' and self.lastoutput[-1].isalpha() and nextoutput[0].isalpha():
+ nextoutput = ' ' + nextoutput # add extra space to terminate csname
+ self.lastoutput = nextoutput
+ return nextoutput
+
+ def chunk(self):
+ """Grab another set of input tokens and convert them to an output string."""
+ for delta,c in self.candidates(0):
+ if c in _l2u:
+ self.pos += delta
+ return unichr(_l2u[c])
+ elif len(c) == 2 and c[1] == 'i' and (c[0],'\\i') in _l2u:
+ self.pos += delta # correct failure to undot i
+ return unichr(_l2u[(c[0],'\\i')])
+ elif len(c) == 1 and c[0].startswith('\\char') and c[0][5:].isdigit():
+ self.pos += delta
+ return unichr(int(c[0][5:]))
+
+ # nothing matches, just pass through token as-is
+ self.pos += 1
+ return self[-1]
+
+ def candidates(self,offset):
+ """Generate pairs delta,c where c is a token or tuple of tokens from tex
+ (after deleting extraneous brackets starting at pos) and delta
+ is the length of the tokens prior to bracket deletion.
+ """
+ t = self[offset]
+ if t in _blacklist:
+ return
+ elif t == '{':
+ for delta,c in self.candidates(offset+1):
+ if self[offset+delta+1] == '}':
+ yield delta+2,c
+ elif t == '\\mbox':
+ for delta,c in self.candidates(offset+1):
+ yield delta+1,c
+ elif t == '$' and self[offset+2] == '$':
+ yield 3, (t,self[offset+1],t)
+ else:
+ q = self[offset+1]
+ if q == '{' and self[offset+3] == '}':
+ yield 4, (t,self[offset+2])
+ elif q:
+ yield 2, (t,q)
+ yield 1, t
+
+latex_equivalents = {
+ 0x0009: ' ',
+ 0x000a: '\n',
+ 0x0023: '{\#}',
+ 0x0026: '{\&}',
+ 0x00a0: '{~}',
+ 0x00a1: '{!`}',
+ 0x00a2: '{\\not{c}}',
+ 0x00a3: '{\\pounds}',
+ 0x00a7: '{\\S}',
+ 0x00a8: '{\\"{}}',
+ 0x00a9: '{\\copyright}',
+ 0x00af: '{\\={}}',
+ 0x00ac: '{\\neg}',
+ 0x00ad: '{\\-}',
+ 0x00b0: '{\\mbox{$^\\circ$}}',
+ 0x00b1: '{\\mbox{$\\pm$}}',
+ 0x00b2: '{\\mbox{$^2$}}',
+ 0x00b3: '{\\mbox{$^3$}}',
+ 0x00b4: "{\\'{}}",
+ 0x00b5: '{\\mbox{$\\mu$}}',
+ 0x00b6: '{\\P}',
+ 0x00b7: '{\\mbox{$\\cdot$}}',
+ 0x00b8: '{\\c{}}',
+ 0x00b9: '{\\mbox{$^1$}}',
+ 0x00bf: '{?`}',
+ 0x00c0: '{\\`A}',
+ 0x00c1: "{\\'A}",
+ 0x00c2: '{\\^A}',
+ 0x00c3: '{\\~A}',
+ 0x00c4: '{\\"A}',
+ 0x00c5: '{\\AA}',
+ 0x00c6: '{\\AE}',
+ 0x00c7: '{\\c{C}}',
+ 0x00c8: '{\\`E}',