Mercurial > repos > shellac > guppy_basecaller
diff env/lib/python3.7/site-packages/docutils/utils/math/math2html.py @ 5:9b1c78e6ba9c draft default tip
"planemo upload commit 6c0a8142489327ece472c84e558c47da711a9142"
author | shellac |
---|---|
date | Mon, 01 Jun 2020 08:59:25 -0400 |
parents | 79f47841a781 |
children |
line wrap: on
line diff
--- a/env/lib/python3.7/site-packages/docutils/utils/math/math2html.py Thu May 14 16:47:39 2020 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5383 +0,0 @@ -#! /usr/bin/env python -# -*- coding: utf-8 -*- - -# math2html: convert LaTeX equations to HTML output. -# -# Copyright (C) 2009-2011 Alex Fernández -# -# Released under the terms of the `2-Clause BSD license'_, in short: -# Copying and distribution of this file, with or without modification, -# are permitted in any medium without royalty provided the copyright -# notice and this notice are preserved. -# This file is offered as-is, without any warranty. -# -# .. _2-Clause BSD license: http://www.spdx.org/licenses/BSD-2-Clause - -# Based on eLyXer: convert LyX source files to HTML output. -# http://alexfernandez.github.io/elyxer/ - -# --end-- -# Alex 20101110 -# eLyXer standalone formula conversion to HTML. - -import codecs -import datetime -import gettext -import io -import os.path -import sys -import unicodedata - -if sys.version_info >= (3, 0): - from urllib.parse import quote_plus -else: - from urllib import quote_plus - - -if sys.version_info >= (3, 0): - unicode = str #noqa - basestring = str # noqa - file = io.IOBase # noqa - - -class Trace(object): - "A tracing class" - - debugmode = False - quietmode = False - showlinesmode = False - - prefix = None - - def debug(cls, message): - "Show a debug message" - if not Trace.debugmode or Trace.quietmode: - return - Trace.show(message, sys.stdout) - - def message(cls, message): - "Show a trace message" - if Trace.quietmode: - return - if Trace.prefix and Trace.showlinesmode: - message = Trace.prefix + message - Trace.show(message, sys.stdout) - - def error(cls, message): - "Show an error message" - message = '* ' + message - if Trace.prefix and Trace.showlinesmode: - message = Trace.prefix + message - Trace.show(message, sys.stderr) - - def fatal(cls, message): - "Show an error message and terminate" - Trace.error('FATAL: ' + message) - exit(-1) - - def show(cls, message, channel): - "Show a message out of a channel" - if sys.version_info < (3, 0): - message = message.encode('utf-8') - channel.write(message + '\n') - - debug = classmethod(debug) - message = classmethod(message) - error = classmethod(error) - fatal = classmethod(fatal) - show = classmethod(show) - - -class BibStylesConfig(object): - "Configuration class from elyxer.config file" - - abbrvnat = { - u'@article': u'$authors. $title. <i>$journal</i>,{ {$volume:}$pages,} $month $year.{ doi: $doi.}{ URL <a href="$url">$url</a>.}{ $note.}', - u'cite': u'$surname($year)', - u'default': u'$authors. <i>$title</i>. $publisher, $year.{ URL <a href="$url">$url</a>.}{ $note.}', - } - - alpha = { - u'@article': u'$authors. $title.{ <i>$journal</i>{, {$volume}{($number)}}{: $pages}{, $year}.}{ <a href="$url">$url</a>.}{ <a href="$filename">$filename</a>.}{ $note.}', - u'cite': u'$Sur$YY', - u'default': u'$authors. $title.{ <i>$journal</i>,} $year.{ <a href="$url">$url</a>.}{ <a href="$filename">$filename</a>.}{ $note.}', - } - - authordate2 = { - u'@article': u'$authors. $year. $title. <i>$journal</i>, <b>$volume</b>($number), $pages.{ URL <a href="$url">$url</a>.}{ $note.}', - u'@book': u'$authors. $year. <i>$title</i>. $publisher.{ URL <a href="$url">$url</a>.}{ $note.}', - u'cite': u'$surname, $year', - u'default': u'$authors. $year. <i>$title</i>. $publisher.{ URL <a href="$url">$url</a>.}{ $note.}', - } - - default = { - u'@article': u'$authors: “$title”, <i>$journal</i>,{ pp. $pages,} $year.{ URL <a href="$url">$url</a>.}{ $note.}', - u'@book': u'{$authors: }<i>$title</i>{ ($editor, ed.)}.{{ $publisher,} $year.}{ URL <a href="$url">$url</a>.}{ $note.}', - u'@booklet': u'$authors: <i>$title</i>.{{ $publisher,} $year.}{ URL <a href="$url">$url</a>.}{ $note.}', - u'@conference': u'$authors: “$title”, <i>$journal</i>,{ pp. $pages,} $year.{ URL <a href="$url">$url</a>.}{ $note.}', - u'@inbook': u'$authors: <i>$title</i>.{{ $publisher,} $year.}{ URL <a href="$url">$url</a>.}{ $note.}', - u'@incollection': u'$authors: <i>$title</i>{ in <i>$booktitle</i>{ ($editor, ed.)}}.{{ $publisher,} $year.}{ URL <a href="$url">$url</a>.}{ $note.}', - u'@inproceedings': u'$authors: “$title”, <i>$booktitle</i>,{ pp. $pages,} $year.{ URL <a href="$url">$url</a>.}{ $note.}', - u'@manual': u'$authors: <i>$title</i>.{{ $publisher,} $year.}{ URL <a href="$url">$url</a>.}{ $note.}', - u'@mastersthesis': u'$authors: <i>$title</i>.{{ $publisher,} $year.}{ URL <a href="$url">$url</a>.}{ $note.}', - u'@misc': u'$authors: <i>$title</i>.{{ $publisher,}{ $howpublished,} $year.}{ URL <a href="$url">$url</a>.}{ $note.}', - u'@phdthesis': u'$authors: <i>$title</i>.{{ $publisher,} $year.}{ URL <a href="$url">$url</a>.}{ $note.}', - u'@proceedings': u'$authors: “$title”, <i>$journal</i>,{ pp. $pages,} $year.{ URL <a href="$url">$url</a>.}{ $note.}', - u'@techreport': u'$authors: <i>$title</i>, $year.{ URL <a href="$url">$url</a>.}{ $note.}', - u'@unpublished': u'$authors: “$title”, <i>$journal</i>, $year.{ URL <a href="$url">$url</a>.}{ $note.}', - u'cite': u'$index', - u'default': u'$authors: <i>$title</i>.{{ $publisher,} $year.}{ URL <a href="$url">$url</a>.}{ $note.}', - } - - defaulttags = { - u'YY': u'??', u'authors': u'', u'surname': u'', - } - - ieeetr = { - u'@article': u'$authors, “$title”, <i>$journal</i>, vol. $volume, no. $number, pp. $pages, $year.{ URL <a href="$url">$url</a>.}{ $note.}', - u'@book': u'$authors, <i>$title</i>. $publisher, $year.{ URL <a href="$url">$url</a>.}{ $note.}', - u'cite': u'$index', - u'default': u'$authors, “$title”. $year.{ URL <a href="$url">$url</a>.}{ $note.}', - } - - plain = { - u'@article': u'$authors. $title.{ <i>$journal</i>{, {$volume}{($number)}}{:$pages}{, $year}.}{ URL <a href="$url">$url</a>.}{ $note.}', - u'@book': u'$authors. <i>$title</i>. $publisher,{ $month} $year.{ URL <a href="$url">$url</a>.}{ $note.}', - u'@incollection': u'$authors. $title.{ In <i>$booktitle</i> {($editor, ed.)}.} $publisher,{ $month} $year.{ URL <a href="$url">$url</a>.}{ $note.}', - u'@inproceedings': u'$authors. $title. { <i>$booktitle</i>{, {$volume}{($number)}}{:$pages}{, $year}.}{ URL <a href="$url">$url</a>.}{ $note.}', - u'cite': u'$index', - u'default': u'{$authors. }$title.{{ $publisher,} $year.}{ URL <a href="$url">$url</a>.}{ $note.}', - } - - vancouver = { - u'@article': u'$authors. $title. <i>$journal</i>, $year{;{<b>$volume</b>}{($number)}{:$pages}}.{ URL: <a href="$url">$url</a>.}{ $note.}', - u'@book': u'$authors. $title. {$publisher, }$year.{ URL: <a href="$url">$url</a>.}{ $note.}', - u'cite': u'$index', - u'default': u'$authors. $title; {$publisher, }$year.{ $howpublished.}{ URL: <a href="$url">$url</a>.}{ $note.}', - } - -class BibTeXConfig(object): - "Configuration class from elyxer.config file" - - replaced = { - u'--': u'—', u'..': u'.', - } - -class ContainerConfig(object): - "Configuration class from elyxer.config file" - - endings = { - u'Align': u'\\end_layout', u'BarredText': u'\\bar', - u'BoldText': u'\\series', u'Cell': u'</cell', - u'ChangeDeleted': u'\\change_unchanged', - u'ChangeInserted': u'\\change_unchanged', u'ColorText': u'\\color', - u'EmphaticText': u'\\emph', u'Hfill': u'\\hfill', u'Inset': u'\\end_inset', - u'Layout': u'\\end_layout', u'LyXFooter': u'\\end_document', - u'LyXHeader': u'\\end_header', u'Row': u'</row', u'ShapedText': u'\\shape', - u'SizeText': u'\\size', u'StrikeOut': u'\\strikeout', - u'TextFamily': u'\\family', u'VersalitasText': u'\\noun', - } - - extracttext = { - u'allowed': [u'StringContainer', u'Constant', u'FormulaConstant',], - u'cloned': [u'',], - u'extracted': [u'PlainLayout', u'TaggedText', u'Align', u'Caption', u'TextFamily', u'EmphaticText', u'VersalitasText', u'BarredText', u'SizeText', u'ColorText', u'LangLine', u'Formula', u'Bracket', u'RawText', u'BibTag', u'FormulaNumber', u'AlphaCommand', u'EmptyCommand', u'OneParamFunction', u'SymbolFunction', u'TextFunction', u'FontFunction', u'CombiningFunction', u'DecoratingFunction', u'FormulaSymbol', u'BracketCommand', u'TeXCode',], - } - - startendings = { - u'\\begin_deeper': u'\\end_deeper', u'\\begin_inset': u'\\end_inset', - u'\\begin_layout': u'\\end_layout', - } - - starts = { - u'': u'StringContainer', u'#LyX': u'BlackBox', u'</lyxtabular': u'BlackBox', - u'<cell': u'Cell', u'<column': u'Column', u'<row': u'Row', - u'\\align': u'Align', u'\\bar': u'BarredText', - u'\\bar default': u'BlackBox', u'\\bar no': u'BlackBox', - u'\\begin_body': u'BlackBox', u'\\begin_deeper': u'DeeperList', - u'\\begin_document': u'BlackBox', u'\\begin_header': u'LyXHeader', - u'\\begin_inset Argument': u'ShortTitle', - u'\\begin_inset Box': u'BoxInset', u'\\begin_inset Branch': u'Branch', - u'\\begin_inset Caption': u'Caption', - u'\\begin_inset CommandInset bibitem': u'BiblioEntry', - u'\\begin_inset CommandInset bibtex': u'BibTeX', - u'\\begin_inset CommandInset citation': u'BiblioCitation', - u'\\begin_inset CommandInset href': u'URL', - u'\\begin_inset CommandInset include': u'IncludeInset', - u'\\begin_inset CommandInset index_print': u'PrintIndex', - u'\\begin_inset CommandInset label': u'Label', - u'\\begin_inset CommandInset line': u'LineInset', - u'\\begin_inset CommandInset nomencl_print': u'PrintNomenclature', - u'\\begin_inset CommandInset nomenclature': u'NomenclatureEntry', - u'\\begin_inset CommandInset ref': u'Reference', - u'\\begin_inset CommandInset toc': u'TableOfContents', - u'\\begin_inset ERT': u'ERT', u'\\begin_inset Flex': u'FlexInset', - u'\\begin_inset Flex Chunkref': u'NewfangledChunkRef', - u'\\begin_inset Flex Marginnote': u'SideNote', - u'\\begin_inset Flex Sidenote': u'SideNote', - u'\\begin_inset Flex URL': u'FlexURL', u'\\begin_inset Float': u'Float', - u'\\begin_inset FloatList': u'ListOf', u'\\begin_inset Foot': u'Footnote', - u'\\begin_inset Formula': u'Formula', - u'\\begin_inset FormulaMacro': u'FormulaMacro', - u'\\begin_inset Graphics': u'Image', - u'\\begin_inset Index': u'IndexReference', - u'\\begin_inset Info': u'InfoInset', - u'\\begin_inset LatexCommand bibitem': u'BiblioEntry', - u'\\begin_inset LatexCommand bibtex': u'BibTeX', - u'\\begin_inset LatexCommand cite': u'BiblioCitation', - u'\\begin_inset LatexCommand citealt': u'BiblioCitation', - u'\\begin_inset LatexCommand citep': u'BiblioCitation', - u'\\begin_inset LatexCommand citet': u'BiblioCitation', - u'\\begin_inset LatexCommand htmlurl': u'URL', - u'\\begin_inset LatexCommand index': u'IndexReference', - u'\\begin_inset LatexCommand label': u'Label', - u'\\begin_inset LatexCommand nomenclature': u'NomenclatureEntry', - u'\\begin_inset LatexCommand prettyref': u'Reference', - u'\\begin_inset LatexCommand printindex': u'PrintIndex', - u'\\begin_inset LatexCommand printnomenclature': u'PrintNomenclature', - u'\\begin_inset LatexCommand ref': u'Reference', - u'\\begin_inset LatexCommand tableofcontents': u'TableOfContents', - u'\\begin_inset LatexCommand url': u'URL', - u'\\begin_inset LatexCommand vref': u'Reference', - u'\\begin_inset Marginal': u'SideNote', - u'\\begin_inset Newline': u'NewlineInset', - u'\\begin_inset Newpage': u'NewPageInset', u'\\begin_inset Note': u'Note', - u'\\begin_inset OptArg': u'ShortTitle', - u'\\begin_inset Phantom': u'PhantomText', - u'\\begin_inset Quotes': u'QuoteContainer', - u'\\begin_inset Tabular': u'Table', u'\\begin_inset Text': u'InsetText', - u'\\begin_inset VSpace': u'VerticalSpace', u'\\begin_inset Wrap': u'Wrap', - u'\\begin_inset listings': u'Listing', - u'\\begin_inset script': u'ScriptInset', u'\\begin_inset space': u'Space', - u'\\begin_layout': u'Layout', u'\\begin_layout Abstract': u'Abstract', - u'\\begin_layout Author': u'Author', - u'\\begin_layout Bibliography': u'Bibliography', - u'\\begin_layout Chunk': u'NewfangledChunk', - u'\\begin_layout Description': u'Description', - u'\\begin_layout Enumerate': u'ListItem', - u'\\begin_layout Itemize': u'ListItem', u'\\begin_layout List': u'List', - u'\\begin_layout LyX-Code': u'LyXCode', - u'\\begin_layout Plain': u'PlainLayout', - u'\\begin_layout Standard': u'StandardLayout', - u'\\begin_layout Title': u'Title', u'\\begin_preamble': u'LyXPreamble', - u'\\change_deleted': u'ChangeDeleted', - u'\\change_inserted': u'ChangeInserted', - u'\\change_unchanged': u'BlackBox', u'\\color': u'ColorText', - u'\\color inherit': u'BlackBox', u'\\color none': u'BlackBox', - u'\\emph default': u'BlackBox', u'\\emph off': u'BlackBox', - u'\\emph on': u'EmphaticText', u'\\emph toggle': u'EmphaticText', - u'\\end_body': u'LyXFooter', u'\\family': u'TextFamily', - u'\\family default': u'BlackBox', u'\\family roman': u'BlackBox', - u'\\hfill': u'Hfill', u'\\labelwidthstring': u'BlackBox', - u'\\lang': u'LangLine', u'\\length': u'InsetLength', - u'\\lyxformat': u'LyXFormat', u'\\lyxline': u'LyXLine', - u'\\newline': u'Newline', u'\\newpage': u'NewPage', - u'\\noindent': u'BlackBox', u'\\noun default': u'BlackBox', - u'\\noun off': u'BlackBox', u'\\noun on': u'VersalitasText', - u'\\paragraph_spacing': u'BlackBox', u'\\series bold': u'BoldText', - u'\\series default': u'BlackBox', u'\\series medium': u'BlackBox', - u'\\shape': u'ShapedText', u'\\shape default': u'BlackBox', - u'\\shape up': u'BlackBox', u'\\size': u'SizeText', - u'\\size normal': u'BlackBox', u'\\start_of_appendix': u'StartAppendix', - u'\\strikeout default': u'BlackBox', u'\\strikeout on': u'StrikeOut', - } - - string = { - u'startcommand': u'\\', - } - - table = { - u'headers': [u'<lyxtabular', u'<features',], - } - -class EscapeConfig(object): - "Configuration class from elyxer.config file" - - chars = { - u'\n': u'', u' -- ': u' — ', u' --- ': u' — ', u'\'': u'’', u'`': u'‘', - } - - commands = { - u'\\InsetSpace \\space{}': u' ', u'\\InsetSpace \\thinspace{}': u' ', - u'\\InsetSpace ~': u' ', u'\\SpecialChar \\-': u'', - u'\\SpecialChar \\@.': u'.', u'\\SpecialChar \\ldots{}': u'…', - u'\\SpecialChar \\menuseparator': u' ▷ ', - u'\\SpecialChar \\nobreakdash-': u'-', u'\\SpecialChar \\slash{}': u'/', - u'\\SpecialChar \\textcompwordmark{}': u'', u'\\backslash': u'\\', - } - - entities = { - u'&': u'&', u'<': u'<', u'>': u'>', - } - - html = { - u'/>': u'>', - } - - iso885915 = { - u' ': u' ', u' ': u' ', u' ': u' ', - } - - nonunicode = { - u' ': u' ', - } - -class FormulaConfig(object): - "Configuration class from elyxer.config file" - - alphacommands = { - u'\\AA': u'Å', u'\\AE': u'Æ', - u'\\AmS': u'<span class="versalitas">AmS</span>', u'\\Angstroem': u'Å', - u'\\DH': u'Ð', u'\\Koppa': u'Ϟ', u'\\L': u'Ł', u'\\Micro': u'µ', u'\\O': u'Ø', - u'\\OE': u'Œ', u'\\Sampi': u'Ϡ', u'\\Stigma': u'Ϛ', u'\\TH': u'Þ', - u'\\aa': u'å', u'\\ae': u'æ', u'\\alpha': u'α', u'\\beta': u'β', - u'\\delta': u'δ', u'\\dh': u'ð', u'\\digamma': u'ϝ', u'\\epsilon': u'ϵ', - u'\\eta': u'η', u'\\eth': u'ð', u'\\gamma': u'γ', u'\\i': u'ı', - u'\\imath': u'ı', u'\\iota': u'ι', u'\\j': u'ȷ', u'\\jmath': u'ȷ', - u'\\kappa': u'κ', u'\\koppa': u'ϟ', u'\\l': u'ł', u'\\lambda': u'λ', - u'\\mu': u'μ', u'\\nu': u'ν', u'\\o': u'ø', u'\\oe': u'œ', u'\\omega': u'ω', - u'\\phi': u'φ', u'\\pi': u'π', u'\\psi': u'ψ', u'\\rho': u'ρ', - u'\\sampi': u'ϡ', u'\\sigma': u'σ', u'\\ss': u'ß', u'\\stigma': u'ϛ', - u'\\tau': u'τ', u'\\tcohm': u'Ω', u'\\textcrh': u'ħ', u'\\th': u'þ', - u'\\theta': u'θ', u'\\upsilon': u'υ', u'\\varDelta': u'∆', - u'\\varGamma': u'Γ', u'\\varLambda': u'Λ', u'\\varOmega': u'Ω', - u'\\varPhi': u'Φ', u'\\varPi': u'Π', u'\\varPsi': u'Ψ', u'\\varSigma': u'Σ', - u'\\varTheta': u'Θ', u'\\varUpsilon': u'Υ', u'\\varXi': u'Ξ', - u'\\varbeta': u'ϐ', u'\\varepsilon': u'ε', u'\\varkappa': u'ϰ', - u'\\varphi': u'φ', u'\\varpi': u'ϖ', u'\\varrho': u'ϱ', u'\\varsigma': u'ς', - u'\\vartheta': u'ϑ', u'\\xi': u'ξ', u'\\zeta': u'ζ', - } - - array = { - u'begin': u'\\begin', u'cellseparator': u'&', u'end': u'\\end', - u'rowseparator': u'\\\\', - } - - bigbrackets = { - u'(': [u'⎛', u'⎜', u'⎝',], u')': [u'⎞', u'⎟', u'⎠',], u'[': [u'⎡', u'⎢', u'⎣',], - u']': [u'⎤', u'⎥', u'⎦',], u'{': [u'⎧', u'⎪', u'⎨', u'⎩',], u'|': [u'|',], - u'}': [u'⎫', u'⎪', u'⎬', u'⎭',], u'∥': [u'∥',], - } - - bigsymbols = { - u'∑': [u'⎲', u'⎳',], u'∫': [u'⌠', u'⌡',], - } - - bracketcommands = { - u'\\left': u'span class="symbol"', - u'\\left.': u'<span class="leftdot"></span>', - u'\\middle': u'span class="symbol"', u'\\right': u'span class="symbol"', - u'\\right.': u'<span class="rightdot"></span>', - } - - combiningfunctions = { - u'\\"': u'̈', u'\\\'': u'́', u'\\^': u'̂', u'\\`': u'̀', u'\\acute': u'́', - u'\\bar': u'̄', u'\\breve': u'̆', u'\\c': u'̧', u'\\check': u'̌', - u'\\dddot': u'⃛', u'\\ddot': u'̈', u'\\dot': u'̇', u'\\grave': u'̀', - u'\\hat': u'̂', u'\\mathring': u'̊', u'\\overleftarrow': u'⃖', - u'\\overrightarrow': u'⃗', u'\\r': u'̊', u'\\s': u'̩', - u'\\textcircled': u'⃝', u'\\textsubring': u'̥', u'\\tilde': u'̃', - u'\\v': u'̌', u'\\vec': u'⃗', u'\\~': u'̃', - } - - commands = { - u'\\ ': u' ', u'\\!': u'', u'\\#': u'#', u'\\$': u'$', u'\\%': u'%', - u'\\&': u'&', u'\\,': u' ', u'\\:': u' ', u'\\;': u' ', u'\\AC': u'∿', - u'\\APLcomment': u'⍝', u'\\APLdownarrowbox': u'⍗', u'\\APLinput': u'⍞', - u'\\APLinv': u'⌹', u'\\APLleftarrowbox': u'⍇', u'\\APLlog': u'⍟', - u'\\APLrightarrowbox': u'⍈', u'\\APLuparrowbox': u'⍐', u'\\Box': u'□', - u'\\Bumpeq': u'≎', u'\\CIRCLE': u'●', u'\\Cap': u'⋒', - u'\\CapitalDifferentialD': u'ⅅ', u'\\CheckedBox': u'☑', u'\\Circle': u'○', - u'\\Coloneqq': u'⩴', u'\\ComplexI': u'ⅈ', u'\\ComplexJ': u'ⅉ', - u'\\Corresponds': u'≙', u'\\Cup': u'⋓', u'\\Delta': u'Δ', u'\\Diamond': u'◇', - u'\\Diamondblack': u'◆', u'\\Diamonddot': u'⟐', u'\\DifferentialD': u'ⅆ', - u'\\Downarrow': u'⇓', u'\\EUR': u'€', u'\\Euler': u'ℇ', - u'\\ExponetialE': u'ⅇ', u'\\Finv': u'Ⅎ', u'\\Game': u'⅁', u'\\Gamma': u'Γ', - u'\\Im': u'ℑ', u'\\Join': u'⨝', u'\\LEFTCIRCLE': u'◖', u'\\LEFTcircle': u'◐', - u'\\LHD': u'◀', u'\\Lambda': u'Λ', u'\\Lbag': u'⟅', u'\\Leftarrow': u'⇐', - u'\\Lleftarrow': u'⇚', u'\\Longleftarrow': u'⟸', - u'\\Longleftrightarrow': u'⟺', u'\\Longrightarrow': u'⟹', u'\\Lparen': u'⦅', - u'\\Lsh': u'↰', u'\\Mapsfrom': u'⇐|', u'\\Mapsto': u'|⇒', u'\\Omega': u'Ω', - u'\\P': u'¶', u'\\Phi': u'Φ', u'\\Pi': u'Π', u'\\Pr': u'Pr', u'\\Psi': u'Ψ', - u'\\Qoppa': u'Ϙ', u'\\RHD': u'▶', u'\\RIGHTCIRCLE': u'◗', - u'\\RIGHTcircle': u'◑', u'\\Rbag': u'⟆', u'\\Re': u'ℜ', u'\\Rparen': u'⦆', - u'\\Rrightarrow': u'⇛', u'\\Rsh': u'↱', u'\\S': u'§', u'\\Sigma': u'Σ', - u'\\Square': u'☐', u'\\Subset': u'⋐', u'\\Sun': u'☉', u'\\Supset': u'⋑', - u'\\Theta': u'Θ', u'\\Uparrow': u'⇑', u'\\Updownarrow': u'⇕', - u'\\Upsilon': u'Υ', u'\\Vdash': u'⊩', u'\\Vert': u'∥', u'\\Vvdash': u'⊪', - u'\\XBox': u'☒', u'\\Xi': u'Ξ', u'\\Yup': u'⅄', u'\\\\': u'<br/>', - u'\\_': u'_', u'\\aleph': u'ℵ', u'\\amalg': u'∐', u'\\anchor': u'⚓', - u'\\angle': u'∠', u'\\aquarius': u'♒', u'\\arccos': u'arccos', - u'\\arcsin': u'arcsin', u'\\arctan': u'arctan', u'\\arg': u'arg', - u'\\aries': u'♈', u'\\arrowbullet': u'➢', u'\\ast': u'∗', u'\\asymp': u'≍', - u'\\backepsilon': u'∍', u'\\backprime': u'‵', u'\\backsimeq': u'⋍', - u'\\backslash': u'\\', u'\\ballotx': u'✗', u'\\barwedge': u'⊼', - u'\\because': u'∵', u'\\beth': u'ℶ', u'\\between': u'≬', u'\\bigcap': u'∩', - u'\\bigcirc': u'○', u'\\bigcup': u'∪', u'\\bigodot': u'⊙', - u'\\bigoplus': u'⊕', u'\\bigotimes': u'⊗', u'\\bigsqcup': u'⊔', - u'\\bigstar': u'★', u'\\bigtriangledown': u'▽', u'\\bigtriangleup': u'△', - u'\\biguplus': u'⊎', u'\\bigvee': u'∨', u'\\bigwedge': u'∧', - u'\\biohazard': u'☣', u'\\blacklozenge': u'⧫', u'\\blacksmiley': u'☻', - u'\\blacksquare': u'■', u'\\blacktriangle': u'▲', - u'\\blacktriangledown': u'▼', u'\\blacktriangleleft': u'◂', - u'\\blacktriangleright': u'▶', u'\\blacktriangleup': u'▴', u'\\bot': u'⊥', - u'\\bowtie': u'⋈', u'\\box': u'▫', u'\\boxast': u'⧆', u'\\boxbar': u'◫', - u'\\boxbox': u'⧈', u'\\boxbslash': u'⧅', u'\\boxcircle': u'⧇', - u'\\boxdot': u'⊡', u'\\boxminus': u'⊟', u'\\boxplus': u'⊞', - u'\\boxslash': u'⧄', u'\\boxtimes': u'⊠', u'\\bullet': u'•', - u'\\bumpeq': u'≏', u'\\cancer': u'♋', u'\\cap': u'∩', u'\\capricornus': u'♑', - u'\\cat': u'⁀', u'\\cdot': u'⋅', u'\\cdots': u'⋯', u'\\cent': u'¢', - u'\\centerdot': u'∙', u'\\checkmark': u'✓', u'\\chi': u'χ', u'\\circ': u'∘', - u'\\circeq': u'≗', u'\\circlearrowleft': u'↺', u'\\circlearrowright': u'↻', - u'\\circledR': u'®', u'\\circledast': u'⊛', u'\\circledbslash': u'⦸', - u'\\circledcirc': u'⊚', u'\\circleddash': u'⊝', u'\\circledgtr': u'⧁', - u'\\circledless': u'⧀', u'\\clubsuit': u'♣', u'\\colon': u': ', u'\\coloneqq': u'≔', - u'\\complement': u'∁', u'\\cong': u'≅', u'\\coprod': u'∐', - u'\\copyright': u'©', u'\\cos': u'cos', u'\\cosh': u'cosh', u'\\cot': u'cot', - u'\\coth': u'coth', u'\\csc': u'csc', u'\\cup': u'∪', u'\\curlyvee': u'⋎', - u'\\curlywedge': u'⋏', u'\\curvearrowleft': u'↶', - u'\\curvearrowright': u'↷', u'\\dag': u'†', u'\\dagger': u'†', - u'\\daleth': u'ℸ', u'\\dashleftarrow': u'⇠', u'\\dashv': u'⊣', - u'\\ddag': u'‡', u'\\ddagger': u'‡', u'\\ddots': u'⋱', u'\\deg': u'deg', - u'\\det': u'det', u'\\diagdown': u'╲', u'\\diagup': u'╱', - u'\\diameter': u'⌀', u'\\diamond': u'◇', u'\\diamondsuit': u'♦', - u'\\dim': u'dim', u'\\div': u'÷', u'\\divideontimes': u'⋇', - u'\\dotdiv': u'∸', u'\\doteq': u'≐', u'\\doteqdot': u'≑', u'\\dotplus': u'∔', - u'\\dots': u'…', u'\\doublebarwedge': u'⌆', u'\\downarrow': u'↓', - u'\\downdownarrows': u'⇊', u'\\downharpoonleft': u'⇃', - u'\\downharpoonright': u'⇂', u'\\dsub': u'⩤', u'\\earth': u'♁', - u'\\eighthnote': u'♪', u'\\ell': u'ℓ', u'\\emptyset': u'∅', - u'\\eqcirc': u'≖', u'\\eqcolon': u'≕', u'\\eqsim': u'≂', u'\\euro': u'€', - u'\\exists': u'∃', u'\\exp': u'exp', u'\\fallingdotseq': u'≒', - u'\\fcmp': u'⨾', u'\\female': u'♀', u'\\flat': u'♭', u'\\forall': u'∀', - u'\\fourth': u'⁗', u'\\frown': u'⌢', u'\\frownie': u'☹', u'\\gcd': u'gcd', - u'\\gemini': u'♊', u'\\geq)': u'≥', u'\\geqq': u'≧', u'\\geqslant': u'≥', - u'\\gets': u'←', u'\\gg': u'≫', u'\\ggg': u'⋙', u'\\gimel': u'ℷ', - u'\\gneqq': u'≩', u'\\gnsim': u'⋧', u'\\gtrdot': u'⋗', u'\\gtreqless': u'⋚', - u'\\gtreqqless': u'⪌', u'\\gtrless': u'≷', u'\\gtrsim': u'≳', - u'\\guillemotleft': u'«', u'\\guillemotright': u'»', u'\\hbar': u'ℏ', - u'\\heartsuit': u'♥', u'\\hfill': u'<span class="hfill"> </span>', - u'\\hom': u'hom', u'\\hookleftarrow': u'↩', u'\\hookrightarrow': u'↪', - u'\\hslash': u'ℏ', u'\\idotsint': u'<span class="bigsymbol">∫⋯∫</span>', - u'\\iiint': u'<span class="bigsymbol">∭</span>', - u'\\iint': u'<span class="bigsymbol">∬</span>', u'\\imath': u'ı', - u'\\inf': u'inf', u'\\infty': u'∞', u'\\intercal': u'⊺', - u'\\interleave': u'⫴', u'\\invamp': u'⅋', u'\\invneg': u'⌐', - u'\\jmath': u'ȷ', u'\\jupiter': u'♃', u'\\ker': u'ker', u'\\land': u'∧', - u'\\landupint': u'<span class="bigsymbol">∱</span>', u'\\lang': u'⟪', - u'\\langle': u'⟨', u'\\lblot': u'⦉', u'\\lbrace': u'{', u'\\lbrace)': u'{', - u'\\lbrack': u'[', u'\\lceil': u'⌈', u'\\ldots': u'…', u'\\leadsto': u'⇝', - u'\\leftarrow)': u'←', u'\\leftarrowtail': u'↢', u'\\leftarrowtobar': u'⇤', - u'\\leftharpoondown': u'↽', u'\\leftharpoonup': u'↼', - u'\\leftleftarrows': u'⇇', u'\\leftleftharpoons': u'⥢', u'\\leftmoon': u'☾', - u'\\leftrightarrow': u'↔', u'\\leftrightarrows': u'⇆', - u'\\leftrightharpoons': u'⇋', u'\\leftthreetimes': u'⋋', u'\\leo': u'♌', - u'\\leq)': u'≤', u'\\leqq': u'≦', u'\\leqslant': u'≤', u'\\lessdot': u'⋖', - u'\\lesseqgtr': u'⋛', u'\\lesseqqgtr': u'⪋', u'\\lessgtr': u'≶', - u'\\lesssim': u'≲', u'\\lfloor': u'⌊', u'\\lg': u'lg', u'\\lgroup': u'⟮', - u'\\lhd': u'⊲', u'\\libra': u'♎', u'\\lightning': u'↯', u'\\limg': u'⦇', - u'\\liminf': u'liminf', u'\\limsup': u'limsup', u'\\ll': u'≪', - u'\\llbracket': u'⟦', u'\\llcorner': u'⌞', u'\\lll': u'⋘', u'\\ln': u'ln', - u'\\lneqq': u'≨', u'\\lnot': u'¬', u'\\lnsim': u'⋦', u'\\log': u'log', - u'\\longleftarrow': u'⟵', u'\\longleftrightarrow': u'⟷', - u'\\longmapsto': u'⟼', u'\\longrightarrow': u'⟶', u'\\looparrowleft': u'↫', - u'\\looparrowright': u'↬', u'\\lor': u'∨', u'\\lozenge': u'◊', - u'\\lrcorner': u'⌟', u'\\ltimes': u'⋉', u'\\lyxlock': u'', u'\\male': u'♂', - u'\\maltese': u'✠', u'\\mapsfrom': u'↤', u'\\mapsto': u'↦', - u'\\mathcircumflex': u'^', u'\\max': u'max', u'\\measuredangle': u'∡', - u'\\medbullet': u'⚫', u'\\medcirc': u'⚪', u'\\mercury': u'☿', u'\\mho': u'℧', - u'\\mid': u'∣', u'\\min': u'min', u'\\models': u'⊨', u'\\mp': u'∓', - u'\\multimap': u'⊸', u'\\nLeftarrow': u'⇍', u'\\nLeftrightarrow': u'⇎', - u'\\nRightarrow': u'⇏', u'\\nVDash': u'⊯', u'\\nabla': u'∇', - u'\\napprox': u'≉', u'\\natural': u'♮', u'\\ncong': u'≇', u'\\nearrow': u'↗', - u'\\neg': u'¬', u'\\neg)': u'¬', u'\\neptune': u'♆', u'\\nequiv': u'≢', - u'\\newline': u'<br/>', u'\\nexists': u'∄', u'\\ngeqslant': u'≱', - u'\\ngtr': u'≯', u'\\ngtrless': u'≹', u'\\ni': u'∋', u'\\ni)': u'∋', - u'\\nleftarrow': u'↚', u'\\nleftrightarrow': u'↮', u'\\nleqslant': u'≰', - u'\\nless': u'≮', u'\\nlessgtr': u'≸', u'\\nmid': u'∤', u'\\nolimits': u'', - u'\\nonumber': u'', u'\\not': u'¬', u'\\not<': u'≮', u'\\not=': u'≠', - u'\\not>': u'≯', u'\\notbackslash': u'⍀', u'\\notin': u'∉', u'\\notni': u'∌', - u'\\notslash': u'⌿', u'\\nparallel': u'∦', u'\\nprec': u'⊀', - u'\\nrightarrow': u'↛', u'\\nsim': u'≁', u'\\nsimeq': u'≄', - u'\\nsqsubset': u'⊏̸', u'\\nsubseteq': u'⊈', u'\\nsucc': u'⊁', - u'\\nsucccurlyeq': u'⋡', u'\\nsupset': u'⊅', u'\\nsupseteq': u'⊉', - u'\\ntriangleleft': u'⋪', u'\\ntrianglelefteq': u'⋬', - u'\\ntriangleright': u'⋫', u'\\ntrianglerighteq': u'⋭', u'\\nvDash': u'⊭', - u'\\nvdash': u'⊬', u'\\nwarrow': u'↖', u'\\odot': u'⊙', - u'\\officialeuro': u'€', u'\\oiiint': u'<span class="bigsymbol">∰</span>', - u'\\oiint': u'<span class="bigsymbol">∯</span>', - u'\\oint': u'<span class="bigsymbol">∮</span>', - u'\\ointclockwise': u'<span class="bigsymbol">∲</span>', - u'\\ointctrclockwise': u'<span class="bigsymbol">∳</span>', - u'\\ominus': u'⊖', u'\\oplus': u'⊕', u'\\oslash': u'⊘', u'\\otimes': u'⊗', - u'\\owns': u'∋', u'\\parallel': u'∥', u'\\partial': u'∂', u'\\pencil': u'✎', - u'\\perp': u'⊥', u'\\pisces': u'♓', u'\\pitchfork': u'⋔', u'\\pluto': u'♇', - u'\\pm': u'±', u'\\pointer': u'➪', u'\\pointright': u'☞', u'\\pounds': u'£', - u'\\prec': u'≺', u'\\preccurlyeq': u'≼', u'\\preceq': u'≼', - u'\\precsim': u'≾', u'\\prime': u'′', u'\\prompto': u'∝', u'\\qoppa': u'ϙ', - u'\\qquad': u' ', u'\\quad': u' ', u'\\quarternote': u'♩', - u'\\radiation': u'☢', u'\\rang': u'⟫', u'\\rangle': u'⟩', u'\\rblot': u'⦊', - u'\\rbrace': u'}', u'\\rbrace)': u'}', u'\\rbrack': u']', u'\\rceil': u'⌉', - u'\\recycle': u'♻', u'\\rfloor': u'⌋', u'\\rgroup': u'⟯', u'\\rhd': u'⊳', - u'\\rightangle': u'∟', u'\\rightarrow)': u'→', u'\\rightarrowtail': u'↣', - u'\\rightarrowtobar': u'⇥', u'\\rightharpoondown': u'⇁', - u'\\rightharpoonup': u'⇀', u'\\rightharpooondown': u'⇁', - u'\\rightharpooonup': u'⇀', u'\\rightleftarrows': u'⇄', - u'\\rightleftharpoons': u'⇌', u'\\rightmoon': u'☽', - u'\\rightrightarrows': u'⇉', u'\\rightrightharpoons': u'⥤', - u'\\rightthreetimes': u'⋌', u'\\rimg': u'⦈', u'\\risingdotseq': u'≓', - u'\\rrbracket': u'⟧', u'\\rsub': u'⩥', u'\\rtimes': u'⋊', - u'\\sagittarius': u'♐', u'\\saturn': u'♄', u'\\scorpio': u'♏', - u'\\searrow': u'↘', u'\\sec': u'sec', u'\\second': u'″', u'\\setminus': u'∖', - u'\\sharp': u'♯', u'\\simeq': u'≃', u'\\sin': u'sin', u'\\sinh': u'sinh', - u'\\sixteenthnote': u'♬', u'\\skull': u'☠', u'\\slash': u'∕', - u'\\smallsetminus': u'∖', u'\\smalltriangledown': u'▿', - u'\\smalltriangleleft': u'◃', u'\\smalltriangleright': u'▹', - u'\\smalltriangleup': u'▵', u'\\smile': u'⌣', u'\\smiley': u'☺', - u'\\spadesuit': u'♠', u'\\spddot': u'¨', u'\\sphat': u'', - u'\\sphericalangle': u'∢', u'\\spot': u'⦁', u'\\sptilde': u'~', - u'\\sqcap': u'⊓', u'\\sqcup': u'⊔', u'\\sqsubset': u'⊏', - u'\\sqsubseteq': u'⊑', u'\\sqsupset': u'⊐', u'\\sqsupseteq': u'⊒', - u'\\square': u'□', u'\\sslash': u'⫽', u'\\star': u'⋆', u'\\steaming': u'☕', - u'\\subseteqq': u'⫅', u'\\subsetneqq': u'⫋', u'\\succ': u'≻', - u'\\succcurlyeq': u'≽', u'\\succeq': u'≽', u'\\succnsim': u'⋩', - u'\\succsim': u'≿', u'\\sun': u'☼', u'\\sup': u'sup', u'\\supseteqq': u'⫆', - u'\\supsetneqq': u'⫌', u'\\surd': u'√', u'\\swarrow': u'↙', - u'\\swords': u'⚔', u'\\talloblong': u'⫾', u'\\tan': u'tan', - u'\\tanh': u'tanh', u'\\taurus': u'♉', u'\\textasciicircum': u'^', - u'\\textasciitilde': u'~', u'\\textbackslash': u'\\', - u'\\textcopyright': u'©\'', u'\\textdegree': u'°', u'\\textellipsis': u'…', - u'\\textemdash': u'—', u'\\textendash': u'—', u'\\texteuro': u'€', - u'\\textgreater': u'>', u'\\textless': u'<', u'\\textordfeminine': u'ª', - u'\\textordmasculine': u'º', u'\\textquotedblleft': u'“', - u'\\textquotedblright': u'”', u'\\textquoteright': u'’', - u'\\textregistered': u'®', u'\\textrightarrow': u'→', - u'\\textsection': u'§', u'\\texttrademark': u'™', - u'\\texttwosuperior': u'²', u'\\textvisiblespace': u' ', - u'\\therefore': u'∴', u'\\third': u'‴', u'\\top': u'⊤', u'\\triangle': u'△', - u'\\triangleleft': u'⊲', u'\\trianglelefteq': u'⊴', u'\\triangleq': u'≜', - u'\\triangleright': u'▷', u'\\trianglerighteq': u'⊵', - u'\\twoheadleftarrow': u'↞', u'\\twoheadrightarrow': u'↠', - u'\\twonotes': u'♫', u'\\udot': u'⊍', u'\\ulcorner': u'⌜', u'\\unlhd': u'⊴', - u'\\unrhd': u'⊵', u'\\unrhl': u'⊵', u'\\uparrow': u'↑', - u'\\updownarrow': u'↕', u'\\upharpoonleft': u'↿', u'\\upharpoonright': u'↾', - u'\\uplus': u'⊎', u'\\upuparrows': u'⇈', u'\\uranus': u'♅', - u'\\urcorner': u'⌝', u'\\vDash': u'⊨', u'\\varclubsuit': u'♧', - u'\\vardiamondsuit': u'♦', u'\\varheartsuit': u'♥', u'\\varnothing': u'∅', - u'\\varspadesuit': u'♤', u'\\vdash': u'⊢', u'\\vdots': u'⋮', u'\\vee': u'∨', - u'\\vee)': u'∨', u'\\veebar': u'⊻', u'\\vert': u'∣', u'\\virgo': u'♍', - u'\\warning': u'⚠', u'\\wasylozenge': u'⌑', u'\\wedge': u'∧', - u'\\wedge)': u'∧', u'\\wp': u'℘', u'\\wr': u'≀', u'\\yen': u'¥', - u'\\yinyang': u'☯', u'\\{': u'{', u'\\|': u'∥', u'\\}': u'}', - } - - decoratedcommand = { - } - - decoratingfunctions = { - u'\\overleftarrow': u'⟵', u'\\overrightarrow': u'⟶', u'\\widehat': u'^', - } - - endings = { - u'bracket': u'}', u'complex': u'\\]', u'endafter': u'}', - u'endbefore': u'\\end{', u'squarebracket': u']', - } - - environments = { - u'align': [u'r', u'l',], u'eqnarray': [u'r', u'c', u'l',], - u'gathered': [u'l', u'l',], - } - - fontfunctions = { - u'\\boldsymbol': u'b', u'\\mathbb': u'span class="blackboard"', - u'\\mathbb{A}': u'𝔸', u'\\mathbb{B}': u'𝔹', u'\\mathbb{C}': u'ℂ', - u'\\mathbb{D}': u'𝔻', u'\\mathbb{E}': u'𝔼', u'\\mathbb{F}': u'𝔽', - u'\\mathbb{G}': u'𝔾', u'\\mathbb{H}': u'ℍ', u'\\mathbb{J}': u'𝕁', - u'\\mathbb{K}': u'𝕂', u'\\mathbb{L}': u'𝕃', u'\\mathbb{N}': u'ℕ', - u'\\mathbb{O}': u'𝕆', u'\\mathbb{P}': u'ℙ', u'\\mathbb{Q}': u'ℚ', - u'\\mathbb{R}': u'ℝ', u'\\mathbb{S}': u'𝕊', u'\\mathbb{T}': u'𝕋', - u'\\mathbb{W}': u'𝕎', u'\\mathbb{Z}': u'ℤ', u'\\mathbf': u'b', - u'\\mathcal': u'span class="scriptfont"', u'\\mathcal{B}': u'ℬ', - u'\\mathcal{E}': u'ℰ', u'\\mathcal{F}': u'ℱ', u'\\mathcal{H}': u'ℋ', - u'\\mathcal{I}': u'ℐ', u'\\mathcal{L}': u'ℒ', u'\\mathcal{M}': u'ℳ', - u'\\mathcal{R}': u'ℛ', u'\\mathfrak': u'span class="fraktur"', - u'\\mathfrak{C}': u'ℭ', u'\\mathfrak{F}': u'𝔉', u'\\mathfrak{H}': u'ℌ', - u'\\mathfrak{I}': u'ℑ', u'\\mathfrak{R}': u'ℜ', u'\\mathfrak{Z}': u'ℨ', - u'\\mathit': u'i', u'\\mathring{A}': u'Å', u'\\mathring{U}': u'Ů', - u'\\mathring{a}': u'å', u'\\mathring{u}': u'ů', u'\\mathring{w}': u'ẘ', - u'\\mathring{y}': u'ẙ', u'\\mathrm': u'span class="mathrm"', - u'\\mathscr': u'span class="scriptfont"', u'\\mathscr{B}': u'ℬ', - u'\\mathscr{E}': u'ℰ', u'\\mathscr{F}': u'ℱ', u'\\mathscr{H}': u'ℋ', - u'\\mathscr{I}': u'ℐ', u'\\mathscr{L}': u'ℒ', u'\\mathscr{M}': u'ℳ', - u'\\mathscr{R}': u'ℛ', u'\\mathsf': u'span class="mathsf"', - u'\\mathtt': u'tt', - } - - hybridfunctions = { - u'\\addcontentsline': [u'{$p!}{$q!}{$r!}', u'f0{}', u'ignored',], - u'\\addtocontents': [u'{$p!}{$q!}', u'f0{}', u'ignored',], - u'\\backmatter': [u'', u'f0{}', u'ignored',], - u'\\binom': [u'{$1}{$2}', u'f2{(}f0{f1{$1}f1{$2}}f2{)}', u'span class="binom"', u'span class="binomstack"', u'span class="bigsymbol"',], - u'\\boxed': [u'{$1}', u'f0{$1}', u'span class="boxed"',], - u'\\cfrac': [u'[$p!]{$1}{$2}', u'f0{f3{(}f1{$1}f3{)/(}f2{$2}f3{)}}', u'span class="fullfraction"', u'span class="numerator align-$p"', u'span class="denominator"', u'span class="ignored"',], - u'\\color': [u'{$p!}{$1}', u'f0{$1}', u'span style="color: $p;"',], - u'\\colorbox': [u'{$p!}{$1}', u'f0{$1}', u'span class="colorbox" style="background: $p;"',], - u'\\dbinom': [u'{$1}{$2}', u'(f0{f1{f2{$1}}f1{f2{ }}f1{f2{$2}}})', u'span class="binomial"', u'span class="binomrow"', u'span class="binomcell"',], - u'\\dfrac': [u'{$1}{$2}', u'f0{f3{(}f1{$1}f3{)/(}f2{$2}f3{)}}', u'span class="fullfraction"', u'span class="numerator"', u'span class="denominator"', u'span class="ignored"',], - u'\\displaystyle': [u'{$1}', u'f0{$1}', u'span class="displaystyle"',], - u'\\fancyfoot': [u'[$p!]{$q!}', u'f0{}', u'ignored',], - u'\\fancyhead': [u'[$p!]{$q!}', u'f0{}', u'ignored',], - u'\\fbox': [u'{$1}', u'f0{$1}', u'span class="fbox"',], - u'\\fboxrule': [u'{$p!}', u'f0{}', u'ignored',], - u'\\fboxsep': [u'{$p!}', u'f0{}', u'ignored',], - u'\\fcolorbox': [u'{$p!}{$q!}{$1}', u'f0{$1}', u'span class="boxed" style="border-color: $p; background: $q;"',], - u'\\frac': [u'{$1}{$2}', u'f0{f3{(}f1{$1}f3{)/(}f2{$2}f3{)}}', u'span class="fraction"', u'span class="numerator"', u'span class="denominator"', u'span class="ignored"',], - u'\\framebox': [u'[$p!][$q!]{$1}', u'f0{$1}', u'span class="framebox align-$q" style="width: $p;"',], - u'\\frontmatter': [u'', u'f0{}', u'ignored',], - u'\\href': [u'[$o]{$u!}{$t!}', u'f0{$t}', u'a href="$u"',], - u'\\hspace': [u'{$p!}', u'f0{ }', u'span class="hspace" style="width: $p;"',], - u'\\leftroot': [u'{$p!}', u'f0{ }', u'span class="leftroot" style="width: $p;px"',], - u'\\mainmatter': [u'', u'f0{}', u'ignored',], - u'\\markboth': [u'{$p!}{$q!}', u'f0{}', u'ignored',], - u'\\markright': [u'{$p!}', u'f0{}', u'ignored',], - u'\\nicefrac': [u'{$1}{$2}', u'f0{f1{$1}⁄f2{$2}}', u'span class="fraction"', u'sup class="numerator"', u'sub class="denominator"', u'span class="ignored"',], - u'\\parbox': [u'[$p!]{$w!}{$1}', u'f0{1}', u'div class="Boxed" style="width: $w;"',], - u'\\raisebox': [u'{$p!}{$1}', u'f0{$1.font}', u'span class="raisebox" style="vertical-align: $p;"',], - u'\\renewenvironment': [u'{$1!}{$2!}{$3!}', u'',], - u'\\rule': [u'[$v!]{$w!}{$h!}', u'f0/', u'hr class="line" style="width: $w; height: $h;"',], - u'\\scriptscriptstyle': [u'{$1}', u'f0{$1}', u'span class="scriptscriptstyle"',], - u'\\scriptstyle': [u'{$1}', u'f0{$1}', u'span class="scriptstyle"',], - u'\\sqrt': [u'[$0]{$1}', u'f0{f1{$0}f2{√}f4{(}f3{$1}f4{)}}', u'span class="sqrt"', u'sup class="root"', u'span class="radical"', u'span class="root"', u'span class="ignored"',], - u'\\stackrel': [u'{$1}{$2}', u'f0{f1{$1}f2{$2}}', u'span class="stackrel"', u'span class="upstackrel"', u'span class="downstackrel"',], - u'\\tbinom': [u'{$1}{$2}', u'(f0{f1{f2{$1}}f1{f2{ }}f1{f2{$2}}})', u'span class="binomial"', u'span class="binomrow"', u'span class="binomcell"',], - u'\\textcolor': [u'{$p!}{$1}', u'f0{$1}', u'span style="color: $p;"',], - u'\\textstyle': [u'{$1}', u'f0{$1}', u'span class="textstyle"',], - u'\\thispagestyle': [u'{$p!}', u'f0{}', u'ignored',], - u'\\unit': [u'[$0]{$1}', u'$0f0{$1.font}', u'span class="unit"',], - u'\\unitfrac': [u'[$0]{$1}{$2}', u'$0f0{f1{$1.font}⁄f2{$2.font}}', u'span class="fraction"', u'sup class="unit"', u'sub class="unit"',], - u'\\uproot': [u'{$p!}', u'f0{ }', u'span class="uproot" style="width: $p;px"',], - u'\\url': [u'{$u!}', u'f0{$u}', u'a href="$u"',], - u'\\vspace': [u'{$p!}', u'f0{ }', u'span class="vspace" style="height: $p;"',], - } - - hybridsizes = { - u'\\binom': u'$1+$2', u'\\cfrac': u'$1+$2', u'\\dbinom': u'$1+$2+1', - u'\\dfrac': u'$1+$2', u'\\frac': u'$1+$2', u'\\tbinom': u'$1+$2+1', - } - - labelfunctions = { - u'\\label': u'a name="#"', - } - - limitcommands = { - u'\\biginterleave': u'⫼', u'\\bigsqcap': u'⨅', u'\\fint': u'⨏', - u'\\iiiint': u'⨌', u'\\int': u'∫', u'\\intop': u'∫', u'\\lim': u'lim', - u'\\prod': u'∏', u'\\smallint': u'∫', u'\\sqint': u'⨖', u'\\sum': u'∑', - u'\\varointclockwise': u'∲', u'\\varprod': u'⨉', u'\\zcmp': u'⨟', - u'\\zhide': u'⧹', u'\\zpipe': u'⨠', u'\\zproject': u'⨡', - } - - misccommands = { - u'\\limits': u'LimitPreviousCommand', u'\\newcommand': u'MacroDefinition', - u'\\renewcommand': u'MacroDefinition', - u'\\setcounter': u'SetCounterFunction', u'\\tag': u'FormulaTag', - u'\\tag*': u'FormulaTag', u'\\today': u'TodayCommand', - } - - modified = { - u'\n': u'', u' ': u'', u'$': u'', u'&': u' ', u'\'': u'’', u'+': u' + ', - u',': u', ', u'-': u' − ', u'/': u' ⁄ ', u':': u' : ', u'<': u' < ', - u'=': u' = ', u'>': u' > ', u'@': u'', u'~': u'', - } - - onefunctions = { - u'\\Big': u'span class="bigsymbol"', u'\\Bigg': u'span class="hugesymbol"', - u'\\bar': u'span class="bar"', u'\\begin{array}': u'span class="arraydef"', - u'\\big': u'span class="symbol"', u'\\bigg': u'span class="largesymbol"', - u'\\bigl': u'span class="bigsymbol"', u'\\bigr': u'span class="bigsymbol"', - u'\\centering': u'span class="align-center"', - u'\\ensuremath': u'span class="ensuremath"', - u'\\hphantom': u'span class="phantom"', - u'\\noindent': u'span class="noindent"', - u'\\overbrace': u'span class="overbrace"', - u'\\overline': u'span class="overline"', - u'\\phantom': u'span class="phantom"', - u'\\underbrace': u'span class="underbrace"', u'\\underline': u'u', - u'\\vphantom': u'span class="phantom"', - } - - spacedcommands = { - u'\\Bot': u'⫫', u'\\Doteq': u'≑', u'\\DownArrowBar': u'⤓', - u'\\DownLeftTeeVector': u'⥞', u'\\DownLeftVectorBar': u'⥖', - u'\\DownRightTeeVector': u'⥟', u'\\DownRightVectorBar': u'⥗', - u'\\Equal': u'⩵', u'\\LeftArrowBar': u'⇤', u'\\LeftDownTeeVector': u'⥡', - u'\\LeftDownVectorBar': u'⥙', u'\\LeftTeeVector': u'⥚', - u'\\LeftTriangleBar': u'⧏', u'\\LeftUpTeeVector': u'⥠', - u'\\LeftUpVectorBar': u'⥘', u'\\LeftVectorBar': u'⥒', - u'\\Leftrightarrow': u'⇔', u'\\Longmapsfrom': u'⟽', u'\\Longmapsto': u'⟾', - u'\\MapsDown': u'↧', u'\\MapsUp': u'↥', u'\\Nearrow': u'⇗', - u'\\NestedGreaterGreater': u'⪢', u'\\NestedLessLess': u'⪡', - u'\\NotGreaterLess': u'≹', u'\\NotGreaterTilde': u'≵', - u'\\NotLessTilde': u'≴', u'\\Nwarrow': u'⇖', u'\\Proportion': u'∷', - u'\\RightArrowBar': u'⇥', u'\\RightDownTeeVector': u'⥝', - u'\\RightDownVectorBar': u'⥕', u'\\RightTeeVector': u'⥛', - u'\\RightTriangleBar': u'⧐', u'\\RightUpTeeVector': u'⥜', - u'\\RightUpVectorBar': u'⥔', u'\\RightVectorBar': u'⥓', - u'\\Rightarrow': u'⇒', u'\\Same': u'⩶', u'\\Searrow': u'⇘', - u'\\Swarrow': u'⇙', u'\\Top': u'⫪', u'\\UpArrowBar': u'⤒', u'\\VDash': u'⊫', - u'\\approx': u'≈', u'\\approxeq': u'≊', u'\\backsim': u'∽', u'\\barin': u'⋶', - u'\\barleftharpoon': u'⥫', u'\\barrightharpoon': u'⥭', u'\\bij': u'⤖', - u'\\coloneq': u'≔', u'\\corresponds': u'≙', u'\\curlyeqprec': u'⋞', - u'\\curlyeqsucc': u'⋟', u'\\dashrightarrow': u'⇢', u'\\dlsh': u'↲', - u'\\downdownharpoons': u'⥥', u'\\downuparrows': u'⇵', - u'\\downupharpoons': u'⥯', u'\\drsh': u'↳', u'\\eqslantgtr': u'⪖', - u'\\eqslantless': u'⪕', u'\\equiv': u'≡', u'\\ffun': u'⇻', u'\\finj': u'⤕', - u'\\ge': u'≥', u'\\geq': u'≥', u'\\ggcurly': u'⪼', u'\\gnapprox': u'⪊', - u'\\gneq': u'⪈', u'\\gtrapprox': u'⪆', u'\\hash': u'⋕', u'\\iddots': u'⋰', - u'\\implies': u' ⇒ ', u'\\in': u'∈', u'\\le': u'≤', u'\\leftarrow': u'←', - u'\\leftarrowtriangle': u'⇽', u'\\leftbarharpoon': u'⥪', - u'\\leftrightarrowtriangle': u'⇿', u'\\leftrightharpoon': u'⥊', - u'\\leftrightharpoondown': u'⥐', u'\\leftrightharpoonup': u'⥎', - u'\\leftrightsquigarrow': u'↭', u'\\leftslice': u'⪦', - u'\\leftsquigarrow': u'⇜', u'\\leftupdownharpoon': u'⥑', u'\\leq': u'≤', - u'\\lessapprox': u'⪅', u'\\llcurly': u'⪻', u'\\lnapprox': u'⪉', - u'\\lneq': u'⪇', u'\\longmapsfrom': u'⟻', u'\\multimapboth': u'⧟', - u'\\multimapdotbothA': u'⊶', u'\\multimapdotbothB': u'⊷', - u'\\multimapinv': u'⟜', u'\\nVdash': u'⊮', u'\\ne': u'≠', u'\\neq': u'≠', - u'\\ngeq': u'≱', u'\\nleq': u'≰', u'\\nni': u'∌', u'\\not\\in': u'∉', - u'\\notasymp': u'≭', u'\\npreceq': u'⋠', u'\\nsqsubseteq': u'⋢', - u'\\nsqsupseteq': u'⋣', u'\\nsubset': u'⊄', u'\\nsucceq': u'⋡', - u'\\pfun': u'⇸', u'\\pinj': u'⤔', u'\\precapprox': u'⪷', u'\\preceqq': u'⪳', - u'\\precnapprox': u'⪹', u'\\precnsim': u'⋨', u'\\propto': u'∝', - u'\\psur': u'⤀', u'\\rightarrow': u'→', u'\\rightarrowtriangle': u'⇾', - u'\\rightbarharpoon': u'⥬', u'\\rightleftharpoon': u'⥋', - u'\\rightslice': u'⪧', u'\\rightsquigarrow': u'⇝', - u'\\rightupdownharpoon': u'⥏', u'\\sim': u'~', u'\\strictfi': u'⥼', - u'\\strictif': u'⥽', u'\\subset': u'⊂', u'\\subseteq': u'⊆', - u'\\subsetneq': u'⊊', u'\\succapprox': u'⪸', u'\\succeqq': u'⪴', - u'\\succnapprox': u'⪺', u'\\supset': u'⊃', u'\\supseteq': u'⊇', - u'\\supsetneq': u'⊋', u'\\times': u'×', u'\\to': u'→', - u'\\updownarrows': u'⇅', u'\\updownharpoons': u'⥮', u'\\upupharpoons': u'⥣', - u'\\vartriangleleft': u'⊲', u'\\vartriangleright': u'⊳', - } - - starts = { - u'beginafter': u'}', u'beginbefore': u'\\begin{', u'bracket': u'{', - u'command': u'\\', u'comment': u'%', u'complex': u'\\[', u'simple': u'$', - u'squarebracket': u'[', u'unnumbered': u'*', - } - - symbolfunctions = { - u'^': u'sup', u'_': u'sub', - } - - textfunctions = { - u'\\mbox': u'span class="mbox"', u'\\text': u'span class="text"', - u'\\textbf': u'b', u'\\textipa': u'span class="textipa"', u'\\textit': u'i', - u'\\textnormal': u'span class="textnormal"', - u'\\textrm': u'span class="textrm"', - u'\\textsc': u'span class="versalitas"', - u'\\textsf': u'span class="textsf"', u'\\textsl': u'i', u'\\texttt': u'tt', - u'\\textup': u'span class="normal"', - } - - unmodified = { - u'characters': [u'.', u'*', u'€', u'(', u')', u'[', u']', u'·', u'!', u';', u'|', u'§', u'"',], - } - - urls = { - u'googlecharts': u'http://chart.googleapis.com/chart?cht=tx&chl=', - } - -class GeneralConfig(object): - "Configuration class from elyxer.config file" - - version = { - u'date': u'2015-02-26', u'lyxformat': u'413', u'number': u'1.2.5', - } - -class HeaderConfig(object): - "Configuration class from elyxer.config file" - - parameters = { - u'beginpreamble': u'\\begin_preamble', u'branch': u'\\branch', - u'documentclass': u'\\textclass', u'endbranch': u'\\end_branch', - u'endpreamble': u'\\end_preamble', u'language': u'\\language', - u'lstset': u'\\lstset', u'outputchanges': u'\\output_changes', - u'paragraphseparation': u'\\paragraph_separation', - u'pdftitle': u'\\pdf_title', u'secnumdepth': u'\\secnumdepth', - u'tocdepth': u'\\tocdepth', - } - - styles = { - u'article': [u'article', u'aastex', u'aapaper', u'acmsiggraph', u'sigplanconf', u'achemso', u'amsart', u'apa', u'arab-article', u'armenian-article', u'article-beamer', u'chess', u'dtk', u'elsarticle', u'heb-article', u'IEEEtran', u'iopart', u'kluwer', u'scrarticle-beamer', u'scrartcl', u'extarticle', u'paper', u'mwart', u'revtex4', u'spie', u'svglobal3', u'ltugboat', u'agu-dtd', u'jgrga', u'agums', u'entcs', u'egs', u'ijmpc', u'ijmpd', u'singlecol-new', u'doublecol-new', u'isprs', u'tarticle', u'jsarticle', u'jarticle', u'jss', u'literate-article', u'siamltex', u'cl2emult', u'llncs', u'svglobal', u'svjog', u'svprobth',], - u'book': [u'book', u'amsbook', u'scrbook', u'extbook', u'tufte-book', u'report', u'extreport', u'scrreprt', u'memoir', u'tbook', u'jsbook', u'jbook', u'mwbk', u'svmono', u'svmult', u'treport', u'jreport', u'mwrep',], - } - -class ImageConfig(object): - "Configuration class from elyxer.config file" - - converters = { - u'imagemagick': u'convert[ -density $scale][ -define $format:use-cropbox=true] "$input" "$output"', - u'inkscape': u'inkscape "$input" --export-png="$output"', - u'lyx': u'lyx -C "$input" "$output"', - } - - cropboxformats = { - u'.eps': u'ps', u'.pdf': u'pdf', u'.ps': u'ps', - } - - formats = { - u'default': u'.png', u'vector': [u'.svg', u'.eps',], - } - -class LayoutConfig(object): - "Configuration class from elyxer.config file" - - groupable = { - u'allowed': [u'StringContainer', u'Constant', u'TaggedText', u'Align', u'TextFamily', u'EmphaticText', u'VersalitasText', u'BarredText', u'SizeText', u'ColorText', u'LangLine', u'Formula',], - } - -class NewfangleConfig(object): - "Configuration class from elyxer.config file" - - constants = { - u'chunkref': u'chunkref{', u'endcommand': u'}', u'endmark': u'>', - u'startcommand': u'\\', u'startmark': u'=<', - } - -class NumberingConfig(object): - "Configuration class from elyxer.config file" - - layouts = { - u'ordered': [u'Chapter', u'Section', u'Subsection', u'Subsubsection', u'Paragraph',], - u'roman': [u'Part', u'Book',], - } - - sequence = { - u'symbols': [u'*', u'**', u'†', u'‡', u'§', u'§§', u'¶', u'¶¶', u'#', u'##',], - } - -class StyleConfig(object): - "Configuration class from elyxer.config file" - - hspaces = { - u'\\enskip{}': u' ', u'\\hfill{}': u'<span class="hfill"> </span>', - u'\\hspace*{\\fill}': u' ', u'\\hspace*{}': u'', u'\\hspace{}': u' ', - u'\\negthinspace{}': u'', u'\\qquad{}': u' ', u'\\quad{}': u' ', - u'\\space{}': u' ', u'\\thinspace{}': u' ', u'~': u' ', - } - - quotes = { - u'ald': u'»', u'als': u'›', u'ard': u'«', u'ars': u'‹', u'eld': u'“', - u'els': u'‘', u'erd': u'”', u'ers': u'’', u'fld': u'«', - u'fls': u'‹', u'frd': u'»', u'frs': u'›', u'gld': u'„', u'gls': u'‚', - u'grd': u'“', u'grs': u'‘', u'pld': u'„', u'pls': u'‚', u'prd': u'”', - u'prs': u'’', u'sld': u'”', u'srd': u'”', - } - - referenceformats = { - u'eqref': u'(@↕)', u'formatted': u'¶↕', u'nameref': u'$↕', u'pageref': u'#↕', - u'ref': u'@↕', u'vpageref': u'on-page#↕', u'vref': u'@on-page#↕', - } - - size = { - u'ignoredtexts': [u'col', u'text', u'line', u'page', u'theight', u'pheight',], - } - - vspaces = { - u'bigskip': u'<div class="bigskip"> </div>', - u'defskip': u'<div class="defskip"> </div>', - u'medskip': u'<div class="medskip"> </div>', - u'smallskip': u'<div class="smallskip"> </div>', - u'vfill': u'<div class="vfill"> </div>', - } - -class TOCConfig(object): - "Configuration class from elyxer.config file" - - extractplain = { - u'allowed': [u'StringContainer', u'Constant', u'TaggedText', u'Align', u'TextFamily', u'EmphaticText', u'VersalitasText', u'BarredText', u'SizeText', u'ColorText', u'LangLine', u'Formula',], - u'cloned': [u'',], u'extracted': [u'',], - } - - extracttitle = { - u'allowed': [u'StringContainer', u'Constant', u'Space',], - u'cloned': [u'TextFamily', u'EmphaticText', u'VersalitasText', u'BarredText', u'SizeText', u'ColorText', u'LangLine', u'Formula',], - u'extracted': [u'PlainLayout', u'TaggedText', u'Align', u'Caption', u'StandardLayout', u'FlexInset',], - } - -class TagConfig(object): - "Configuration class from elyxer.config file" - - barred = { - u'under': u'u', - } - - family = { - u'sans': u'span class="sans"', u'typewriter': u'tt', - } - - flex = { - u'CharStyle:Code': u'span class="code"', - u'CharStyle:MenuItem': u'span class="menuitem"', - u'Code': u'span class="code"', u'MenuItem': u'span class="menuitem"', - u'Noun': u'span class="noun"', u'Strong': u'span class="strong"', - } - - group = { - u'layouts': [u'Quotation', u'Quote',], - } - - layouts = { - u'Center': u'div', u'Chapter': u'h?', u'Date': u'h2', u'Paragraph': u'div', - u'Part': u'h1', u'Quotation': u'blockquote', u'Quote': u'blockquote', - u'Section': u'h?', u'Subsection': u'h?', u'Subsubsection': u'h?', - } - - listitems = { - u'Enumerate': u'ol', u'Itemize': u'ul', - } - - notes = { - u'Comment': u'', u'Greyedout': u'span class="greyedout"', u'Note': u'', - } - - script = { - u'subscript': u'sub', u'superscript': u'sup', - } - - shaped = { - u'italic': u'i', u'slanted': u'i', u'smallcaps': u'span class="versalitas"', - } - -class TranslationConfig(object): - "Configuration class from elyxer.config file" - - constants = { - u'Appendix': u'Appendix', u'Book': u'Book', u'Chapter': u'Chapter', - u'Paragraph': u'Paragraph', u'Part': u'Part', u'Section': u'Section', - u'Subsection': u'Subsection', u'Subsubsection': u'Subsubsection', - u'abstract': u'Abstract', u'bibliography': u'Bibliography', - u'figure': u'figure', u'float-algorithm': u'Algorithm ', - u'float-figure': u'Figure ', u'float-listing': u'Listing ', - u'float-table': u'Table ', u'float-tableau': u'Tableau ', - u'footnotes': u'Footnotes', u'generated-by': u'Document generated by ', - u'generated-on': u' on ', u'index': u'Index', - u'jsmath-enable': u'Please enable JavaScript on your browser.', - u'jsmath-requires': u' requires JavaScript to correctly process the mathematics on this page. ', - u'jsmath-warning': u'Warning: ', u'list-algorithm': u'List of Algorithms', - u'list-figure': u'List of Figures', u'list-table': u'List of Tables', - u'list-tableau': u'List of Tableaux', u'main-page': u'Main page', - u'next': u'Next', u'nomenclature': u'Nomenclature', - u'on-page': u' on page ', u'prev': u'Prev', u'references': u'References', - u'toc': u'Table of Contents', u'toc-for': u'Contents for ', u'up': u'Up', - } - - languages = { - u'american': u'en', u'british': u'en', u'deutsch': u'de', u'dutch': u'nl', - u'english': u'en', u'french': u'fr', u'ngerman': u'de', u'russian': u'ru', - u'spanish': u'es', - } - - -class CommandLineParser(object): - "A parser for runtime options" - - def __init__(self, options): - self.options = options - - def parseoptions(self, args): - "Parse command line options" - if len(args) == 0: - return None - while len(args) > 0 and args[0].startswith('--'): - key, value = self.readoption(args) - if not key: - return 'Option ' + value + ' not recognized' - if not value: - return 'Option ' + key + ' needs a value' - setattr(self.options, key, value) - return None - - def readoption(self, args): - "Read the key and value for an option" - arg = args[0][2:] - del args[0] - if '=' in arg: - key = self.readequalskey(arg, args) - else: - key = arg.replace('-', '') - if not hasattr(self.options, key): - return None, key - current = getattr(self.options, key) - if isinstance(current, bool): - return key, True - # read value - if len(args) == 0: - return key, None - if args[0].startswith('"'): - initial = args[0] - del args[0] - return key, self.readquoted(args, initial) - value = args[0].decode('utf-8') - del args[0] - if isinstance(current, list): - current.append(value) - return key, current - return key, value - - def readquoted(self, args, initial): - "Read a value between quotes" - Trace.error('Oops') - value = initial[1:] - while len(args) > 0 and not args[0].endswith('"') and not args[0].startswith('--'): - Trace.error('Appending ' + args[0]) - value += ' ' + args[0] - del args[0] - if len(args) == 0 or args[0].startswith('--'): - return None - value += ' ' + args[0:-1] - return value - - def readequalskey(self, arg, args): - "Read a key using equals" - split = arg.split('=', 1) - key = split[0] - value = split[1] - args.insert(0, value) - return key - - -class Options(object): - "A set of runtime options" - - instance = None - - location = None - nocopy = False - copyright = False - debug = False - quiet = False - version = False - hardversion = False - versiondate = False - html = False - help = False - showlines = True - unicode = False - iso885915 = False - css = [] - favicon = '' - title = None - directory = None - destdirectory = None - toc = False - toctarget = '' - tocfor = None - forceformat = None - lyxformat = False - target = None - splitpart = None - memory = True - lowmem = False - nobib = False - converter = 'imagemagick' - raw = False - jsmath = None - mathjax = None - nofooter = False - simplemath = False - template = None - noconvert = False - notoclabels = False - letterfoot = True - numberfoot = False - symbolfoot = False - hoverfoot = True - marginfoot = False - endfoot = False - supfoot = True - alignfoot = False - footnotes = None - imageformat = None - copyimages = False - googlecharts = False - embedcss = [] - - branches = dict() - - def parseoptions(self, args): - "Parse command line options" - Options.location = args[0] - del args[0] - parser = CommandLineParser(Options) - result = parser.parseoptions(args) - if result: - Trace.error(result) - self.usage() - self.processoptions() - - def processoptions(self): - "Process all options parsed." - if Options.help: - self.usage() - if Options.version: - self.showversion() - if Options.hardversion: - self.showhardversion() - if Options.versiondate: - self.showversiondate() - if Options.lyxformat: - self.showlyxformat() - if Options.splitpart: - try: - Options.splitpart = int(Options.splitpart) - if Options.splitpart <= 0: - Trace.error('--splitpart requires a number bigger than zero') - self.usage() - except: - Trace.error('--splitpart needs a numeric argument, not ' + Options.splitpart) - self.usage() - if Options.lowmem or Options.toc or Options.tocfor: - Options.memory = False - self.parsefootnotes() - if Options.forceformat and not Options.imageformat: - Options.imageformat = Options.forceformat - if Options.imageformat == 'copy': - Options.copyimages = True - if Options.css == []: - Options.css = ['http://elyxer.nongnu.org/lyx.css'] - if Options.favicon == '': - pass # no default favicon - if Options.html: - Options.simplemath = True - if Options.toc and not Options.tocfor: - Trace.error('Option --toc is deprecated; use --tocfor "page" instead') - Options.tocfor = Options.toctarget - if Options.nocopy: - Trace.error('Option --nocopy is deprecated; it is no longer needed') - if Options.jsmath: - Trace.error('Option --jsmath is deprecated; use --mathjax instead') - # set in Trace if necessary - for param in dir(Trace): - if param.endswith('mode'): - setattr(Trace, param, getattr(self, param[:-4])) - - def usage(self): - "Show correct usage" - Trace.error('Usage: ' + os.path.basename(Options.location) + ' [options] [filein] [fileout]') - Trace.error('Convert LyX input file "filein" to HTML file "fileout".') - Trace.error('If filein (or fileout) is not given use standard input (or output).') - Trace.error('Main program of the eLyXer package (http://elyxer.nongnu.org/).') - self.showoptions() - - def parsefootnotes(self): - "Parse footnotes options." - if not Options.footnotes: - return - Options.marginfoot = False - Options.letterfoot = False - Options.hoverfoot = False - options = Options.footnotes.split(',') - for option in options: - footoption = option + 'foot' - if hasattr(Options, footoption): - setattr(Options, footoption, True) - else: - Trace.error('Unknown footnotes option: ' + option) - if not Options.endfoot and not Options.marginfoot and not Options.hoverfoot: - Options.hoverfoot = True - if not Options.numberfoot and not Options.symbolfoot: - Options.letterfoot = True - - def showoptions(self): - "Show all possible options" - Trace.error(' Common options:') - Trace.error(' --help: show this online help') - Trace.error(' --quiet: disables all runtime messages') - Trace.error('') - Trace.error(' Advanced options:') - Trace.error(' --debug: enable debugging messages (for developers)') - Trace.error(' --version: show version number and release date') - Trace.error(' --lyxformat: return the highest LyX version supported') - Trace.error(' Options for HTML output:') - Trace.error(' --title "title": set the generated page title') - Trace.error(' --css "file.css": use a custom CSS file') - Trace.error(' --embedcss "file.css": embed styles from a CSS file into the output') - Trace.error(' --favicon "icon.ico": insert the specified favicon in the header.') - Trace.error(' --html: output HTML 4.0 instead of the default XHTML') - Trace.error(' --unicode: full Unicode output') - Trace.error(' --iso885915: output a document with ISO-8859-15 encoding') - Trace.error(' --nofooter: remove the footer "generated by eLyXer"') - Trace.error(' --simplemath: do not generate fancy math constructions') - Trace.error(' Options for image output:') - Trace.error(' --directory "img_dir": look for images in the specified directory') - Trace.error(' --destdirectory "dest": put converted images into this directory') - Trace.error(' --imageformat ".ext": image output format, or "copy" to copy images') - Trace.error(' --noconvert: do not convert images, use in original locations') - Trace.error(' --converter "inkscape": use an alternative program to convert images') - Trace.error(' Options for footnote display:') - Trace.error(' --numberfoot: mark footnotes with numbers instead of letters') - Trace.error(' --symbolfoot: mark footnotes with symbols (*, **...)') - Trace.error(' --hoverfoot: show footnotes as hovering text (default)') - Trace.error(' --marginfoot: show footnotes on the page margin') - Trace.error(' --endfoot: show footnotes at the end of the page') - Trace.error(' --supfoot: use superscript for footnote markers (default)') - Trace.error(' --alignfoot: use aligned text for footnote markers') - Trace.error(' --footnotes "options": specify several comma-separated footnotes options') - Trace.error(' Available options are: "number", "symbol", "hover", "margin", "end",') - Trace.error(' "sup", "align"') - Trace.error(' Advanced output options:') - Trace.error(' --splitpart "depth": split the resulting webpage at the given depth') - Trace.error(' --tocfor "page": generate a TOC that points to the given page') - Trace.error(' --target "frame": make all links point to the given frame') - Trace.error(' --notoclabels: omit the part labels in the TOC, such as Chapter') - Trace.error(' --lowmem: do the conversion on the fly (conserve memory)') - Trace.error(' --raw: generate HTML without header or footer.') - Trace.error(' --mathjax remote: use MathJax remotely to display equations') - Trace.error(' --mathjax "URL": use MathJax from the given URL to display equations') - Trace.error(' --googlecharts: use Google Charts to generate formula images') - Trace.error(' --template "file": use a template, put everything in <!--$content-->') - Trace.error(' --copyright: add a copyright notice at the bottom') - Trace.error(' Deprecated options:') - Trace.error(' --toc: (deprecated) create a table of contents') - Trace.error(' --toctarget "page": (deprecated) generate a TOC for the given page') - Trace.error(' --nocopy: (deprecated) maintained for backwards compatibility') - Trace.error(' --jsmath "URL": use jsMath from the given URL to display equations') - sys.exit() - - def showversion(self): - "Return the current eLyXer version string" - string = 'eLyXer version ' + GeneralConfig.version['number'] - string += ' (' + GeneralConfig.version['date'] + ')' - Trace.error(string) - sys.exit() - - def showhardversion(self): - "Return just the version string" - Trace.message(GeneralConfig.version['number']) - sys.exit() - - def showversiondate(self): - "Return just the version dte" - Trace.message(GeneralConfig.version['date']) - sys.exit() - - def showlyxformat(self): - "Return just the lyxformat parameter" - Trace.message(GeneralConfig.version['lyxformat']) - sys.exit() - -class BranchOptions(object): - "A set of options for a branch" - - def __init__(self, name): - self.name = name - self.options = {'color':'#ffffff'} - - def set(self, key, value): - "Set a branch option" - if not key.startswith(ContainerConfig.string['startcommand']): - Trace.error('Invalid branch option ' + key) - return - key = key.replace(ContainerConfig.string['startcommand'], '') - self.options[key] = value - - def isselected(self): - "Return if the branch is selected" - if not 'selected' in self.options: - return False - return self.options['selected'] == '1' - - def __unicode__(self): - "String representation" - return 'options for ' + self.name + ': ' + unicode(self.options) - - if sys.version_info >= (3, 0): - __str__ = __unicode__ - - -class Cloner(object): - "An object used to clone other objects." - - def clone(cls, original): - "Return an exact copy of an object." - "The original object must have an empty constructor." - return cls.create(original.__class__) - - def create(cls, type): - "Create an object of a given class." - clone = type.__new__(type) - clone.__init__() - return clone - - clone = classmethod(clone) - create = classmethod(create) - -class ContainerExtractor(object): - "A class to extract certain containers." - - def __init__(self, config): - "The config parameter is a map containing three lists: allowed, copied and extracted." - "Each of the three is a list of class names for containers." - "Allowed containers are included as is into the result." - "Cloned containers are cloned and placed into the result." - "Extracted containers are looked into." - "All other containers are silently ignored." - self.allowed = config['allowed'] - self.cloned = config['cloned'] - self.extracted = config['extracted'] - - def extract(self, container): - "Extract a group of selected containers from elyxer.a container." - list = [] - locate = lambda c: c.__class__.__name__ in self.allowed + self.cloned - recursive = lambda c: c.__class__.__name__ in self.extracted - process = lambda c: self.process(c, list) - container.recursivesearch(locate, recursive, process) - return list - - def process(self, container, list): - "Add allowed containers, clone cloned containers and add the clone." - name = container.__class__.__name__ - if name in self.allowed: - list.append(container) - elif name in self.cloned: - list.append(self.safeclone(container)) - else: - Trace.error('Unknown container class ' + name) - - def safeclone(self, container): - "Return a new container with contents only in a safe list, recursively." - clone = Cloner.clone(container) - clone.output = container.output - clone.contents = self.extract(container) - return clone - - - - - - -class Parser(object): - "A generic parser" - - def __init__(self): - self.begin = 0 - self.parameters = dict() - - def parseheader(self, reader): - "Parse the header" - header = reader.currentline().split() - reader.nextline() - self.begin = reader.linenumber - return header - - def parseparameter(self, reader): - "Parse a parameter" - if reader.currentline().strip().startswith('<'): - key, value = self.parsexml(reader) - self.parameters[key] = value - return - split = reader.currentline().strip().split(' ', 1) - reader.nextline() - if len(split) == 0: - return - key = split[0] - if len(split) == 1: - self.parameters[key] = True - return - if not '"' in split[1]: - self.parameters[key] = split[1].strip() - return - doublesplit = split[1].split('"') - self.parameters[key] = doublesplit[1] - - def parsexml(self, reader): - "Parse a parameter in xml form: <param attr1=value...>" - strip = reader.currentline().strip() - reader.nextline() - if not strip.endswith('>'): - Trace.error('XML parameter ' + strip + ' should be <...>') - split = strip[1:-1].split() - if len(split) == 0: - Trace.error('Empty XML parameter <>') - return None, None - key = split[0] - del split[0] - if len(split) == 0: - return key, dict() - attrs = dict() - for attr in split: - if not '=' in attr: - Trace.error('Erroneous attribute for ' + key + ': ' + attr) - attr += '="0"' - parts = attr.split('=') - attrkey = parts[0] - value = parts[1].split('"')[1] - attrs[attrkey] = value - return key, attrs - - def parseending(self, reader, process): - "Parse until the current ending is found" - if not self.ending: - Trace.error('No ending for ' + unicode(self)) - return - while not reader.currentline().startswith(self.ending): - process() - - def parsecontainer(self, reader, contents): - container = self.factory.createcontainer(reader) - if container: - container.parent = self.parent - contents.append(container) - - def __unicode__(self): - "Return a description" - return self.__class__.__name__ + ' (' + unicode(self.begin) + ')' - - if sys.version_info >= (3, 0): - __str__ = __unicode__ - - -class LoneCommand(Parser): - "A parser for just one command line" - - def parse(self, reader): - "Read nothing" - return [] - -class TextParser(Parser): - "A parser for a command and a bit of text" - - stack = [] - - def __init__(self, container): - Parser.__init__(self) - self.ending = None - if container.__class__.__name__ in ContainerConfig.endings: - self.ending = ContainerConfig.endings[container.__class__.__name__] - self.endings = [] - - def parse(self, reader): - "Parse lines as long as they are text" - TextParser.stack.append(self.ending) - self.endings = TextParser.stack + [ContainerConfig.endings['Layout'], - ContainerConfig.endings['Inset'], self.ending] - contents = [] - while not self.isending(reader): - self.parsecontainer(reader, contents) - return contents - - def isending(self, reader): - "Check if text is ending" - current = reader.currentline().split() - if len(current) == 0: - return False - if current[0] in self.endings: - if current[0] in TextParser.stack: - TextParser.stack.remove(current[0]) - else: - TextParser.stack = [] - return True - return False - -class ExcludingParser(Parser): - "A parser that excludes the final line" - - def parse(self, reader): - "Parse everything up to (and excluding) the final line" - contents = [] - self.parseending(reader, lambda: self.parsecontainer(reader, contents)) - return contents - -class BoundedParser(ExcludingParser): - "A parser bound by a final line" - - def parse(self, reader): - "Parse everything, including the final line" - contents = ExcludingParser.parse(self, reader) - # skip last line - reader.nextline() - return contents - -class BoundedDummy(Parser): - "A bound parser that ignores everything" - - def parse(self, reader): - "Parse the contents of the container" - self.parseending(reader, lambda: reader.nextline()) - # skip last line - reader.nextline() - return [] - -class StringParser(Parser): - "Parses just a string" - - def parseheader(self, reader): - "Do nothing, just take note" - self.begin = reader.linenumber + 1 - return [] - - def parse(self, reader): - "Parse a single line" - contents = reader.currentline() - reader.nextline() - return contents - -class InsetParser(BoundedParser): - "Parses a LyX inset" - - def parse(self, reader): - "Parse inset parameters into a dictionary" - startcommand = ContainerConfig.string['startcommand'] - while reader.currentline() != '' and not reader.currentline().startswith(startcommand): - self.parseparameter(reader) - return BoundedParser.parse(self, reader) - - - - - - -class ContainerOutput(object): - "The generic HTML output for a container." - - def gethtml(self, container): - "Show an error." - Trace.error('gethtml() not implemented for ' + unicode(self)) - - def isempty(self): - "Decide if the output is empty: by default, not empty." - return False - -class EmptyOutput(ContainerOutput): - - def gethtml(self, container): - "Return empty HTML code." - return [] - - def isempty(self): - "This output is particularly empty." - return True - -class FixedOutput(ContainerOutput): - "Fixed output" - - def gethtml(self, container): - "Return constant HTML code" - return container.html - -class ContentsOutput(ContainerOutput): - "Outputs the contents converted to HTML" - - def gethtml(self, container): - "Return the HTML code" - html = [] - if container.contents == None: - return html - for element in container.contents: - if not hasattr(element, 'gethtml'): - Trace.error('No html in ' + element.__class__.__name__ + ': ' + unicode(element)) - return html - html += element.gethtml() - return html - -class TaggedOutput(ContentsOutput): - "Outputs an HTML tag surrounding the contents." - - tag = None - breaklines = False - empty = False - - def settag(self, tag, breaklines=False, empty=False): - "Set the value for the tag and other attributes." - self.tag = tag - if breaklines: - self.breaklines = breaklines - if empty: - self.empty = empty - return self - - def setbreaklines(self, breaklines): - "Set the value for breaklines." - self.breaklines = breaklines - return self - - def gethtml(self, container): - "Return the HTML code." - if self.empty: - return [self.selfclosing(container)] - html = [self.open(container)] - html += ContentsOutput.gethtml(self, container) - html.append(self.close(container)) - return html - - def open(self, container): - "Get opening line." - if not self.checktag(container): - return '' - open = '<' + self.tag + '>' - if self.breaklines: - return open + '\n' - return open - - def close(self, container): - "Get closing line." - if not self.checktag(container): - return '' - close = '</' + self.tag.split()[0] + '>' - if self.breaklines: - return '\n' + close + '\n' - return close - - def selfclosing(self, container): - "Get self-closing line." - if not self.checktag(container): - return '' - selfclosing = '<' + self.tag + '/>' - if self.breaklines: - return selfclosing + '\n' - return selfclosing - - def checktag(self, container): - "Check that the tag is valid." - if not self.tag: - Trace.error('No tag in ' + unicode(container)) - return False - if self.tag == '': - return False - return True - -class FilteredOutput(ContentsOutput): - "Returns the output in the contents, but filtered:" - "some strings are replaced by others." - - def __init__(self): - "Initialize the filters." - self.filters = [] - - def addfilter(self, original, replacement): - "Add a new filter: replace the original by the replacement." - self.filters.append((original, replacement)) - - def gethtml(self, container): - "Return the HTML code" - result = [] - html = ContentsOutput.gethtml(self, container) - for line in html: - result.append(self.filter(line)) - return result - - def filter(self, line): - "Filter a single line with all available filters." - for original, replacement in self.filters: - if original in line: - line = line.replace(original, replacement) - return line - -class StringOutput(ContainerOutput): - "Returns a bare string as output" - - def gethtml(self, container): - "Return a bare string" - return [container.string] - - -class LineReader(object): - "Reads a file line by line" - - def __init__(self, filename): - if isinstance(filename, file): - self.file = filename - else: - self.file = codecs.open(filename, 'rU', 'utf-8') - self.linenumber = 1 - self.lastline = None - self.current = None - self.mustread = True - self.depleted = False - try: - self.readline() - except UnicodeDecodeError: - # try compressed file - import gzip - self.file = gzip.open(filename, 'rb') - self.readline() - - def setstart(self, firstline): - "Set the first line to read." - for i in range(firstline): - self.file.readline() - self.linenumber = firstline - - def setend(self, lastline): - "Set the last line to read." - self.lastline = lastline - - def currentline(self): - "Get the current line" - if self.mustread: - self.readline() - return self.current - - def nextline(self): - "Go to next line" - if self.depleted: - Trace.fatal('Read beyond file end') - self.mustread = True - - def readline(self): - "Read a line from elyxer.file" - self.current = self.file.readline() - if not isinstance(self.file, codecs.StreamReaderWriter): - self.current = self.current.decode('utf-8') - if len(self.current) == 0: - self.depleted = True - self.current = self.current.rstrip('\n\r') - self.linenumber += 1 - self.mustread = False - Trace.prefix = 'Line ' + unicode(self.linenumber) + ': ' - if self.linenumber % 1000 == 0: - Trace.message('Parsing') - - def finished(self): - "Find out if the file is finished" - if self.lastline and self.linenumber == self.lastline: - return True - if self.mustread: - self.readline() - return self.depleted - - def close(self): - self.file.close() - -class LineWriter(object): - "Writes a file as a series of lists" - - file = False - - def __init__(self, filename): - if isinstance(filename, file): - self.file = filename - self.filename = None - else: - self.filename = filename - - def write(self, strings): - "Write a list of strings" - for string in strings: - if not isinstance(string, basestring): - Trace.error('Not a string: ' + unicode(string) + ' in ' + unicode(strings)) - return - self.writestring(string) - - def writestring(self, string): - "Write a string" - if not self.file: - self.file = codecs.open(self.filename, 'w', "utf-8") - if self.file == sys.stdout and sys.version_info < (3, 0): - string = string.encode('utf-8') - self.file.write(string) - - def writeline(self, line): - "Write a line to file" - self.writestring(line + '\n') - - def close(self): - self.file.close() - - - - - - -class Globable(object): - """A bit of text which can be globbed (lumped together in bits). - Methods current(), skipcurrent(), checkfor() and isout() have to be - implemented by subclasses.""" - - leavepending = False - - def __init__(self): - self.endinglist = EndingList() - - def checkbytemark(self): - "Check for a Unicode byte mark and skip it." - if self.finished(): - return - if ord(self.current()) == 0xfeff: - self.skipcurrent() - - def isout(self): - "Find out if we are out of the position yet." - Trace.error('Unimplemented isout()') - return True - - def current(self): - "Return the current character." - Trace.error('Unimplemented current()') - return '' - - def checkfor(self, string): - "Check for the given string in the current position." - Trace.error('Unimplemented checkfor()') - return False - - def finished(self): - "Find out if the current text has finished." - if self.isout(): - if not self.leavepending: - self.endinglist.checkpending() - return True - return self.endinglist.checkin(self) - - def skipcurrent(self): - "Return the current character and skip it." - Trace.error('Unimplemented skipcurrent()') - return '' - - def glob(self, currentcheck): - "Glob a bit of text that satisfies a check on the current char." - glob = '' - while not self.finished() and currentcheck(): - glob += self.skipcurrent() - return glob - - def globalpha(self): - "Glob a bit of alpha text" - return self.glob(lambda: self.current().isalpha()) - - def globnumber(self): - "Glob a row of digits." - return self.glob(lambda: self.current().isdigit()) - - def isidentifier(self): - "Return if the current character is alphanumeric or _." - if self.current().isalnum() or self.current() == '_': - return True - return False - - def globidentifier(self): - "Glob alphanumeric and _ symbols." - return self.glob(self.isidentifier) - - def isvalue(self): - "Return if the current character is a value character:" - "not a bracket or a space." - if self.current().isspace(): - return False - if self.current() in '{}()': - return False - return True - - def globvalue(self): - "Glob a value: any symbols but brackets." - return self.glob(self.isvalue) - - def skipspace(self): - "Skip all whitespace at current position." - return self.glob(lambda: self.current().isspace()) - - def globincluding(self, magicchar): - "Glob a bit of text up to (including) the magic char." - glob = self.glob(lambda: self.current() != magicchar) + magicchar - self.skip(magicchar) - return glob - - def globexcluding(self, excluded): - "Glob a bit of text up until (excluding) any excluded character." - return self.glob(lambda: self.current() not in excluded) - - def pushending(self, ending, optional = False): - "Push a new ending to the bottom" - self.endinglist.add(ending, optional) - - def popending(self, expected = None): - "Pop the ending found at the current position" - if self.isout() and self.leavepending: - return expected - ending = self.endinglist.pop(self) - if expected and expected != ending: - Trace.error('Expected ending ' + expected + ', got ' + ending) - self.skip(ending) - return ending - - def nextending(self): - "Return the next ending in the queue." - nextending = self.endinglist.findending(self) - if not nextending: - return None - return nextending.ending - -class EndingList(object): - "A list of position endings" - - def __init__(self): - self.endings = [] - - def add(self, ending, optional = False): - "Add a new ending to the list" - self.endings.append(PositionEnding(ending, optional)) - - def pickpending(self, pos): - "Pick any pending endings from a parse position." - self.endings += pos.endinglist.endings - - def checkin(self, pos): - "Search for an ending" - if self.findending(pos): - return True - return False - - def pop(self, pos): - "Remove the ending at the current position" - if pos.isout(): - Trace.error('No ending out of bounds') - return '' - ending = self.findending(pos) - if not ending: - Trace.error('No ending at ' + pos.current()) - return '' - for each in reversed(self.endings): - self.endings.remove(each) - if each == ending: - return each.ending - elif not each.optional: - Trace.error('Removed non-optional ending ' + each) - Trace.error('No endings left') - return '' - - def findending(self, pos): - "Find the ending at the current position" - if len(self.endings) == 0: - return None - for index, ending in enumerate(reversed(self.endings)): - if ending.checkin(pos): - return ending - if not ending.optional: - return None - return None - - def checkpending(self): - "Check if there are any pending endings" - if len(self.endings) != 0: - Trace.error('Pending ' + unicode(self) + ' left open') - - def __unicode__(self): - "Printable representation" - string = 'endings [' - for ending in self.endings: - string += unicode(ending) + ',' - if len(self.endings) > 0: - string = string[:-1] - return string + ']' - - if sys.version_info >= (3, 0): - __str__ = __unicode__ - - -class PositionEnding(object): - "An ending for a parsing position" - - def __init__(self, ending, optional): - self.ending = ending - self.optional = optional - - def checkin(self, pos): - "Check for the ending" - return pos.checkfor(self.ending) - - def __unicode__(self): - "Printable representation" - string = 'Ending ' + self.ending - if self.optional: - string += ' (optional)' - return string - - if sys.version_info >= (3, 0): - __str__ = __unicode__ - - -class Position(Globable): - """A position in a text to parse. - Including those in Globable, functions to implement by subclasses are: - skip(), identifier(), extract(), isout() and current().""" - - def __init__(self): - Globable.__init__(self) - - def skip(self, string): - "Skip a string" - Trace.error('Unimplemented skip()') - - def identifier(self): - "Return an identifier for the current position." - Trace.error('Unimplemented identifier()') - return 'Error' - - def extract(self, length): - "Extract the next string of the given length, or None if not enough text," - "without advancing the parse position." - Trace.error('Unimplemented extract()') - return None - - def checkfor(self, string): - "Check for a string at the given position." - return string == self.extract(len(string)) - - def checkforlower(self, string): - "Check for a string in lower case." - extracted = self.extract(len(string)) - if not extracted: - return False - return string.lower() == self.extract(len(string)).lower() - - def skipcurrent(self): - "Return the current character and skip it." - current = self.current() - self.skip(current) - return current - - def __next__(self): - "Advance the position and return the next character." - self.skipcurrent() - return self.current() - - if sys.version_info < (3, 0): - next = __next__ - - def checkskip(self, string): - "Check for a string at the given position; if there, skip it" - if not self.checkfor(string): - return False - self.skip(string) - return True - - def error(self, message): - "Show an error message and the position identifier." - Trace.error(message + ': ' + self.identifier()) - -class TextPosition(Position): - "A parse position based on a raw text." - - def __init__(self, text): - "Create the position from elyxer.some text." - Position.__init__(self) - self.pos = 0 - self.text = text - self.checkbytemark() - - def skip(self, string): - "Skip a string of characters." - self.pos += len(string) - - def identifier(self): - "Return a sample of the remaining text." - length = 30 - if self.pos + length > len(self.text): - length = len(self.text) - self.pos - return '*' + self.text[self.pos:self.pos + length] + '*' - - def isout(self): - "Find out if we are out of the text yet." - return self.pos >= len(self.text) - - def current(self): - "Return the current character, assuming we are not out." - return self.text[self.pos] - - def extract(self, length): - "Extract the next string of the given length, or None if not enough text." - if self.pos + length > len(self.text): - return None - return self.text[self.pos : self.pos + length] - -class FilePosition(Position): - "A parse position based on an underlying file." - - def __init__(self, filename): - "Create the position from a file." - Position.__init__(self) - self.reader = LineReader(filename) - self.pos = 0 - self.checkbytemark() - - def skip(self, string): - "Skip a string of characters." - length = len(string) - while self.pos + length > len(self.reader.currentline()): - length -= len(self.reader.currentline()) - self.pos + 1 - self.nextline() - self.pos += length - - def currentline(self): - "Get the current line of the underlying file." - return self.reader.currentline() - - def nextline(self): - "Go to the next line." - self.reader.nextline() - self.pos = 0 - - def linenumber(self): - "Return the line number of the file." - return self.reader.linenumber + 1 - - def identifier(self): - "Return the current line and line number in the file." - before = self.reader.currentline()[:self.pos - 1] - after = self.reader.currentline()[self.pos:] - return 'line ' + unicode(self.getlinenumber()) + ': ' + before + '*' + after - - def isout(self): - "Find out if we are out of the text yet." - if self.pos > len(self.reader.currentline()): - if self.pos > len(self.reader.currentline()) + 1: - Trace.error('Out of the line ' + self.reader.currentline() + ': ' + unicode(self.pos)) - self.nextline() - return self.reader.finished() - - def current(self): - "Return the current character, assuming we are not out." - if self.pos == len(self.reader.currentline()): - return '\n' - if self.pos > len(self.reader.currentline()): - Trace.error('Out of the line ' + self.reader.currentline() + ': ' + unicode(self.pos)) - return '*' - return self.reader.currentline()[self.pos] - - def extract(self, length): - "Extract the next string of the given length, or None if not enough text." - if self.pos + length > len(self.reader.currentline()): - return None - return self.reader.currentline()[self.pos : self.pos + length] - - - -class Container(object): - "A container for text and objects in a lyx file" - - partkey = None - parent = None - begin = None - - def __init__(self): - self.contents = list() - - def process(self): - "Process contents" - pass - - def gethtml(self): - "Get the resulting HTML" - html = self.output.gethtml(self) - if isinstance(html, basestring): - Trace.error('Raw string ' + html) - html = [html] - return self.escapeall(html) - - def escapeall(self, lines): - "Escape all lines in an array according to the output options." - result = [] - for line in lines: - if Options.html: - line = self.escape(line, EscapeConfig.html) - if Options.iso885915: - line = self.escape(line, EscapeConfig.iso885915) - line = self.escapeentities(line) - elif not Options.unicode: - line = self.escape(line, EscapeConfig.nonunicode) - result.append(line) - return result - - def escape(self, line, replacements = EscapeConfig.entities): - "Escape a line with replacements from elyxer.a map" - pieces = sorted(replacements.keys()) - # do them in order - for piece in pieces: - if piece in line: - line = line.replace(piece, replacements[piece]) - return line - - def escapeentities(self, line): - "Escape all Unicode characters to HTML entities." - result = '' - pos = TextPosition(line) - while not pos.finished(): - if ord(pos.current()) > 128: - codepoint = hex(ord(pos.current())) - if codepoint == '0xd835': - codepoint = hex(ord(next(pos)) + 0xf800) - result += '&#' + codepoint[1:] + ';' - else: - result += pos.current() - pos.skipcurrent() - return result - - def searchall(self, type): - "Search for all embedded containers of a given type" - list = [] - self.searchprocess(type, lambda container: list.append(container)) - return list - - def searchremove(self, type): - "Search for all containers of a type and remove them" - list = self.searchall(type) - for container in list: - container.parent.contents.remove(container) - return list - - def searchprocess(self, type, process): - "Search for elements of a given type and process them" - self.locateprocess(lambda container: isinstance(container, type), process) - - def locateprocess(self, locate, process): - "Search for all embedded containers and process them" - for container in self.contents: - container.locateprocess(locate, process) - if locate(container): - process(container) - - def recursivesearch(self, locate, recursive, process): - "Perform a recursive search in the container." - for container in self.contents: - if recursive(container): - container.recursivesearch(locate, recursive, process) - if locate(container): - process(container) - - def extracttext(self): - "Extract all text from elyxer.allowed containers." - result = '' - constants = ContainerExtractor(ContainerConfig.extracttext).extract(self) - for constant in constants: - result += constant.string - return result - - def group(self, index, group, isingroup): - "Group some adjoining elements into a group" - if index >= len(self.contents): - return - if hasattr(self.contents[index], 'grouped'): - return - while index < len(self.contents) and isingroup(self.contents[index]): - self.contents[index].grouped = True - group.contents.append(self.contents[index]) - self.contents.pop(index) - self.contents.insert(index, group) - - def remove(self, index): - "Remove a container but leave its contents" - container = self.contents[index] - self.contents.pop(index) - while len(container.contents) > 0: - self.contents.insert(index, container.contents.pop()) - - def tree(self, level = 0): - "Show in a tree" - Trace.debug(" " * level + unicode(self)) - for container in self.contents: - container.tree(level + 1) - - def getparameter(self, name): - "Get the value of a parameter, if present." - if not name in self.parameters: - return None - return self.parameters[name] - - def getparameterlist(self, name): - "Get the value of a comma-separated parameter as a list." - paramtext = self.getparameter(name) - if not paramtext: - return [] - return paramtext.split(',') - - def hasemptyoutput(self): - "Check if the parent's output is empty." - current = self.parent - while current: - if current.output.isempty(): - return True - current = current.parent - return False - - def __unicode__(self): - "Get a description" - if not self.begin: - return self.__class__.__name__ - return self.__class__.__name__ + '@' + unicode(self.begin) - - if sys.version_info >= (3, 0): - __str__ = __unicode__ - - -class BlackBox(Container): - "A container that does not output anything" - - def __init__(self): - self.parser = LoneCommand() - self.output = EmptyOutput() - self.contents = [] - -class LyXFormat(BlackBox): - "Read the lyxformat command" - - def process(self): - "Show warning if version < 276" - version = int(self.header[1]) - if version < 276: - Trace.error('Warning: unsupported old format version ' + str(version)) - if version > int(GeneralConfig.version['lyxformat']): - Trace.error('Warning: unsupported new format version ' + str(version)) - -class StringContainer(Container): - "A container for a single string" - - parsed = None - - def __init__(self): - self.parser = StringParser() - self.output = StringOutput() - self.string = '' - - def process(self): - "Replace special chars from elyxer.the contents." - if self.parsed: - self.string = self.replacespecial(self.parsed) - self.parsed = None - - def replacespecial(self, line): - "Replace all special chars from elyxer.a line" - replaced = self.escape(line, EscapeConfig.entities) - replaced = self.changeline(replaced) - if ContainerConfig.string['startcommand'] in replaced and len(replaced) > 1: - # unprocessed commands - if self.begin: - message = 'Unknown command at ' + unicode(self.begin) + ': ' - else: - message = 'Unknown command: ' - Trace.error(message + replaced.strip()) - return replaced - - def changeline(self, line): - line = self.escape(line, EscapeConfig.chars) - if not ContainerConfig.string['startcommand'] in line: - return line - line = self.escape(line, EscapeConfig.commands) - return line - - def extracttext(self): - "Return all text." - return self.string - - def __unicode__(self): - "Return a printable representation." - result = 'StringContainer' - if self.begin: - result += '@' + unicode(self.begin) - ellipsis = '...' - if len(self.string.strip()) <= 15: - ellipsis = '' - return result + ' (' + self.string.strip()[:15] + ellipsis + ')' - - if sys.version_info >= (3, 0): - __str__ = __unicode__ - - -class Constant(StringContainer): - "A constant string" - - def __init__(self, text): - self.contents = [] - self.string = text - self.output = StringOutput() - - def __unicode__(self): - return 'Constant: ' + self.string - - if sys.version_info >= (3, 0): - __str__ = __unicode__ - - -class TaggedText(Container): - "Text inside a tag" - - output = None - - def __init__(self): - self.parser = TextParser(self) - self.output = TaggedOutput() - - def complete(self, contents, tag, breaklines=False): - "Complete the tagged text and return it" - self.contents = contents - self.output.tag = tag - self.output.breaklines = breaklines - return self - - def constant(self, text, tag, breaklines=False): - "Complete the tagged text with a constant" - constant = Constant(text) - return self.complete([constant], tag, breaklines) - - def __unicode__(self): - "Return a printable representation." - if not hasattr(self.output, 'tag'): - return 'Emtpy tagged text' - if not self.output.tag: - return 'Tagged <unknown tag>' - return 'Tagged <' + self.output.tag + '>' - - if sys.version_info >= (3, 0): - __str__ = __unicode__ - - -class DocumentParameters(object): - "Global parameters for the document." - - pdftitle = None - indentstandard = False - tocdepth = 10 - startinglevel = 0 - maxdepth = 10 - language = None - bibliography = None - outputchanges = False - displaymode = False - - - - - - -class FormulaParser(Parser): - "Parses a formula" - - def parseheader(self, reader): - "See if the formula is inlined" - self.begin = reader.linenumber + 1 - type = self.parsetype(reader) - if not type: - reader.nextline() - type = self.parsetype(reader) - if not type: - Trace.error('Unknown formula type in ' + reader.currentline().strip()) - return ['unknown'] - return [type] - - def parsetype(self, reader): - "Get the formula type from the first line." - if reader.currentline().find(FormulaConfig.starts['simple']) >= 0: - return 'inline' - if reader.currentline().find(FormulaConfig.starts['complex']) >= 0: - return 'block' - if reader.currentline().find(FormulaConfig.starts['unnumbered']) >= 0: - return 'block' - if reader.currentline().find(FormulaConfig.starts['beginbefore']) >= 0: - return 'numbered' - return None - - def parse(self, reader): - "Parse the formula until the end" - formula = self.parseformula(reader) - while not reader.currentline().startswith(self.ending): - stripped = reader.currentline().strip() - if len(stripped) > 0: - Trace.error('Unparsed formula line ' + stripped) - reader.nextline() - reader.nextline() - return formula - - def parseformula(self, reader): - "Parse the formula contents" - simple = FormulaConfig.starts['simple'] - if simple in reader.currentline(): - rest = reader.currentline().split(simple, 1)[1] - if simple in rest: - # formula is $...$ - return self.parsesingleliner(reader, simple, simple) - # formula is multiline $...$ - return self.parsemultiliner(reader, simple, simple) - if FormulaConfig.starts['complex'] in reader.currentline(): - # formula of the form \[...\] - return self.parsemultiliner(reader, FormulaConfig.starts['complex'], - FormulaConfig.endings['complex']) - beginbefore = FormulaConfig.starts['beginbefore'] - beginafter = FormulaConfig.starts['beginafter'] - if beginbefore in reader.currentline(): - if reader.currentline().strip().endswith(beginafter): - current = reader.currentline().strip() - endsplit = current.split(beginbefore)[1].split(beginafter) - startpiece = beginbefore + endsplit[0] + beginafter - endbefore = FormulaConfig.endings['endbefore'] - endafter = FormulaConfig.endings['endafter'] - endpiece = endbefore + endsplit[0] + endafter - return startpiece + self.parsemultiliner(reader, startpiece, endpiece) + endpiece - Trace.error('Missing ' + beginafter + ' in ' + reader.currentline()) - return '' - begincommand = FormulaConfig.starts['command'] - beginbracket = FormulaConfig.starts['bracket'] - if begincommand in reader.currentline() and beginbracket in reader.currentline(): - endbracket = FormulaConfig.endings['bracket'] - return self.parsemultiliner(reader, beginbracket, endbracket) - Trace.error('Formula beginning ' + reader.currentline() + ' is unknown') - return '' - - def parsesingleliner(self, reader, start, ending): - "Parse a formula in one line" - line = reader.currentline().strip() - if not start in line: - Trace.error('Line ' + line + ' does not contain formula start ' + start) - return '' - if not line.endswith(ending): - Trace.error('Formula ' + line + ' does not end with ' + ending) - return '' - index = line.index(start) - rest = line[index + len(start):-len(ending)] - reader.nextline() - return rest - - def parsemultiliner(self, reader, start, ending): - "Parse a formula in multiple lines" - formula = '' - line = reader.currentline() - if not start in line: - Trace.error('Line ' + line.strip() + ' does not contain formula start ' + start) - return '' - index = line.index(start) - line = line[index + len(start):].strip() - while not line.endswith(ending): - formula += line + '\n' - reader.nextline() - line = reader.currentline() - formula += line[:-len(ending)] - reader.nextline() - return formula - -class MacroParser(FormulaParser): - "A parser for a formula macro." - - def parseheader(self, reader): - "See if the formula is inlined" - self.begin = reader.linenumber + 1 - return ['inline'] - - def parse(self, reader): - "Parse the formula until the end" - formula = self.parsemultiliner(reader, self.parent.start, self.ending) - reader.nextline() - return formula - - -class FormulaBit(Container): - "A bit of a formula" - - type = None - size = 1 - original = '' - - def __init__(self): - "The formula bit type can be 'alpha', 'number', 'font'." - self.contents = [] - self.output = ContentsOutput() - - def setfactory(self, factory): - "Set the internal formula factory." - self.factory = factory - return self - - def add(self, bit): - "Add any kind of formula bit already processed" - self.contents.append(bit) - self.original += bit.original - bit.parent = self - - def skiporiginal(self, string, pos): - "Skip a string and add it to the original formula" - self.original += string - if not pos.checkskip(string): - Trace.error('String ' + string + ' not at ' + pos.identifier()) - - def computesize(self): - "Compute the size of the bit as the max of the sizes of all contents." - if len(self.contents) == 0: - return 1 - self.size = max([element.size for element in self.contents]) - return self.size - - def clone(self): - "Return a copy of itself." - return self.factory.parseformula(self.original) - - def __unicode__(self): - "Get a string representation" - return self.__class__.__name__ + ' read in ' + self.original - - if sys.version_info >= (3, 0): - __str__ = __unicode__ - - -class TaggedBit(FormulaBit): - "A tagged string in a formula" - - def constant(self, constant, tag): - "Set the constant and the tag" - self.output = TaggedOutput().settag(tag) - self.add(FormulaConstant(constant)) - return self - - def complete(self, contents, tag, breaklines = False): - "Set the constant and the tag" - self.contents = contents - self.output = TaggedOutput().settag(tag, breaklines) - return self - - def selfcomplete(self, tag): - "Set the self-closing tag, no contents (as in <hr/>)." - self.output = TaggedOutput().settag(tag, empty = True) - return self - -class FormulaConstant(Constant): - "A constant string in a formula" - - def __init__(self, string): - "Set the constant string" - Constant.__init__(self, string) - self.original = string - self.size = 1 - self.type = None - - def computesize(self): - "Compute the size of the constant: always 1." - return self.size - - def clone(self): - "Return a copy of itself." - return FormulaConstant(self.original) - - def __unicode__(self): - "Return a printable representation." - return 'Formula constant: ' + self.string - - if sys.version_info >= (3, 0): - __str__ = __unicode__ - - -class RawText(FormulaBit): - "A bit of text inside a formula" - - def detect(self, pos): - "Detect a bit of raw text" - return pos.current().isalpha() - - def parsebit(self, pos): - "Parse alphabetic text" - alpha = pos.globalpha() - self.add(FormulaConstant(alpha)) - self.type = 'alpha' - -class FormulaSymbol(FormulaBit): - "A symbol inside a formula" - - modified = FormulaConfig.modified - unmodified = FormulaConfig.unmodified['characters'] - - def detect(self, pos): - "Detect a symbol" - if pos.current() in FormulaSymbol.unmodified: - return True - if pos.current() in FormulaSymbol.modified: - return True - return False - - def parsebit(self, pos): - "Parse the symbol" - if pos.current() in FormulaSymbol.unmodified: - self.addsymbol(pos.current(), pos) - return - if pos.current() in FormulaSymbol.modified: - self.addsymbol(FormulaSymbol.modified[pos.current()], pos) - return - Trace.error('Symbol ' + pos.current() + ' not found') - - def addsymbol(self, symbol, pos): - "Add a symbol" - self.skiporiginal(pos.current(), pos) - self.contents.append(FormulaConstant(symbol)) - -class FormulaNumber(FormulaBit): - "A string of digits in a formula" - - def detect(self, pos): - "Detect a digit" - return pos.current().isdigit() - - def parsebit(self, pos): - "Parse a bunch of digits" - digits = pos.glob(lambda: pos.current().isdigit()) - self.add(FormulaConstant(digits)) - self.type = 'number' - -class Comment(FormulaBit): - "A LaTeX comment: % to the end of the line." - - start = FormulaConfig.starts['comment'] - - def detect(self, pos): - "Detect the %." - return pos.current() == self.start - - def parsebit(self, pos): - "Parse to the end of the line." - self.original += pos.globincluding('\n') - -class WhiteSpace(FormulaBit): - "Some white space inside a formula." - - def detect(self, pos): - "Detect the white space." - return pos.current().isspace() - - def parsebit(self, pos): - "Parse all whitespace." - self.original += pos.skipspace() - - def __unicode__(self): - "Return a printable representation." - return 'Whitespace: *' + self.original + '*' - - if sys.version_info >= (3, 0): - __str__ = __unicode__ - - -class Bracket(FormulaBit): - "A {} bracket inside a formula" - - start = FormulaConfig.starts['bracket'] - ending = FormulaConfig.endings['bracket'] - - def __init__(self): - "Create a (possibly literal) new bracket" - FormulaBit.__init__(self) - self.inner = None - - def detect(self, pos): - "Detect the start of a bracket" - return pos.checkfor(self.start) - - def parsebit(self, pos): - "Parse the bracket" - self.parsecomplete(pos, self.innerformula) - return self - - def parsetext(self, pos): - "Parse a text bracket" - self.parsecomplete(pos, self.innertext) - return self - - def parseliteral(self, pos): - "Parse a literal bracket" - self.parsecomplete(pos, self.innerliteral) - return self - - def parsecomplete(self, pos, innerparser): - "Parse the start and end marks" - if not pos.checkfor(self.start): - Trace.error('Bracket should start with ' + self.start + ' at ' + pos.identifier()) - return None - self.skiporiginal(self.start, pos) - pos.pushending(self.ending) - innerparser(pos) - self.original += pos.popending(self.ending) - self.computesize() - - def innerformula(self, pos): - "Parse a whole formula inside the bracket" - while not pos.finished(): - self.add(self.factory.parseany(pos)) - - def innertext(self, pos): - "Parse some text inside the bracket, following textual rules." - specialchars = list(FormulaConfig.symbolfunctions.keys()) - specialchars.append(FormulaConfig.starts['command']) - specialchars.append(FormulaConfig.starts['bracket']) - specialchars.append(Comment.start) - while not pos.finished(): - if pos.current() in specialchars: - self.add(self.factory.parseany(pos)) - if pos.checkskip(' '): - self.original += ' ' - else: - self.add(FormulaConstant(pos.skipcurrent())) - - def innerliteral(self, pos): - "Parse a literal inside the bracket, which does not generate HTML." - self.literal = '' - while not pos.finished() and not pos.current() == self.ending: - if pos.current() == self.start: - self.parseliteral(pos) - else: - self.literal += pos.skipcurrent() - self.original += self.literal - -class SquareBracket(Bracket): - "A [] bracket inside a formula" - - start = FormulaConfig.starts['squarebracket'] - ending = FormulaConfig.endings['squarebracket'] - - def clone(self): - "Return a new square bracket with the same contents." - bracket = SquareBracket() - bracket.contents = self.contents - return bracket - - -class MathsProcessor(object): - "A processor for a maths construction inside the FormulaProcessor." - - def process(self, contents, index): - "Process an element inside a formula." - Trace.error('Unimplemented process() in ' + unicode(self)) - - def __unicode__(self): - "Return a printable description." - return 'Maths processor ' + self.__class__.__name__ - - if sys.version_info >= (3, 0): - __str__ = __unicode__ - - -class FormulaProcessor(object): - "A processor specifically for formulas." - - processors = [] - - def process(self, bit): - "Process the contents of every formula bit, recursively." - self.processcontents(bit) - self.processinsides(bit) - self.traversewhole(bit) - - def processcontents(self, bit): - "Process the contents of a formula bit." - if not isinstance(bit, FormulaBit): - return - bit.process() - for element in bit.contents: - self.processcontents(element) - - def processinsides(self, bit): - "Process the insides (limits, brackets) in a formula bit." - if not isinstance(bit, FormulaBit): - return - for index, element in enumerate(bit.contents): - for processor in self.processors: - processor.process(bit.contents, index) - # continue with recursive processing - self.processinsides(element) - - def traversewhole(self, formula): - "Traverse over the contents to alter variables and space units." - last = None - for bit, contents in self.traverse(formula): - if bit.type == 'alpha': - self.italicize(bit, contents) - elif bit.type == 'font' and last and last.type == 'number': - bit.contents.insert(0, FormulaConstant(u' ')) - last = bit - - def traverse(self, bit): - "Traverse a formula and yield a flattened structure of (bit, list) pairs." - for element in bit.contents: - if hasattr(element, 'type') and element.type: - yield (element, bit.contents) - elif isinstance(element, FormulaBit): - for pair in self.traverse(element): - yield pair - - def italicize(self, bit, contents): - "Italicize the given bit of text." - index = contents.index(bit) - contents[index] = TaggedBit().complete([bit], 'i') - - - - -class Formula(Container): - "A LaTeX formula" - - def __init__(self): - self.parser = FormulaParser() - self.output = TaggedOutput().settag('span class="formula"') - - def process(self): - "Convert the formula to tags" - if self.header[0] == 'inline': - DocumentParameters.displaymode = False - else: - DocumentParameters.displaymode = True - self.output.settag('div class="formula"', True) - if Options.jsmath: - self.jsmath() - elif Options.mathjax: - self.mathjax() - elif Options.googlecharts: - self.googlecharts() - else: - self.classic() - - def jsmath(self): - "Make the contents for jsMath." - if self.header[0] != 'inline': - self.output = TaggedOutput().settag('div class="math"') - else: - self.output = TaggedOutput().settag('span class="math"') - self.contents = [Constant(self.parsed)] - - def mathjax(self): - "Make the contents for MathJax." - self.output.tag = 'span class="MathJax_Preview"' - tag = 'script type="math/tex' - if self.header[0] != 'inline': - tag += ';mode=display' - self.contents = [TaggedText().constant(self.parsed, tag + '"', True)] - - def googlecharts(self): - "Make the contents using Google Charts http://code.google.com/apis/chart/." - url = FormulaConfig.urls['googlecharts'] + quote_plus(self.parsed) - img = '<img class="chart" src="' + url + '" alt="' + self.parsed + '"/>' - self.contents = [Constant(img)] - - def classic(self): - "Make the contents using classic output generation with XHTML and CSS." - whole = FormulaFactory().parseformula(self.parsed) - FormulaProcessor().process(whole) - whole.parent = self - self.contents = [whole] - - def parse(self, pos): - "Parse using a parse position instead of self.parser." - if pos.checkskip('$$'): - self.parsedollarblock(pos) - elif pos.checkskip('$'): - self.parsedollarinline(pos) - elif pos.checkskip('\\('): - self.parseinlineto(pos, '\\)') - elif pos.checkskip('\\['): - self.parseblockto(pos, '\\]') - else: - pos.error('Unparseable formula') - self.process() - return self - - def parsedollarinline(self, pos): - "Parse a $...$ formula." - self.header = ['inline'] - self.parsedollar(pos) - - def parsedollarblock(self, pos): - "Parse a $$...$$ formula." - self.header = ['block'] - self.parsedollar(pos) - if not pos.checkskip('$'): - pos.error('Formula should be $$...$$, but last $ is missing.') - - def parsedollar(self, pos): - "Parse to the next $." - pos.pushending('$') - self.parsed = pos.globexcluding('$') - pos.popending('$') - - def parseinlineto(self, pos, limit): - "Parse a \\(...\\) formula." - self.header = ['inline'] - self.parseupto(pos, limit) - - def parseblockto(self, pos, limit): - "Parse a \\[...\\] formula." - self.header = ['block'] - self.parseupto(pos, limit) - - def parseupto(self, pos, limit): - "Parse a formula that ends with the given command." - pos.pushending(limit) - self.parsed = pos.glob(lambda: True) - pos.popending(limit) - - def __unicode__(self): - "Return a printable representation." - if self.partkey and self.partkey.number: - return 'Formula (' + self.partkey.number + ')' - return 'Unnumbered formula' - - if sys.version_info >= (3, 0): - __str__ = __unicode__ - - -class WholeFormula(FormulaBit): - "Parse a whole formula" - - def detect(self, pos): - "Not outside the formula is enough." - return not pos.finished() - - def parsebit(self, pos): - "Parse with any formula bit" - while not pos.finished(): - self.add(self.factory.parseany(pos)) - -class FormulaFactory(object): - "Construct bits of formula" - - # bit types will be appended later - types = [FormulaSymbol, RawText, FormulaNumber, Bracket, Comment, WhiteSpace] - skippedtypes = [Comment, WhiteSpace] - defining = False - - def __init__(self): - "Initialize the map of instances." - self.instances = dict() - - def detecttype(self, type, pos): - "Detect a bit of a given type." - if pos.finished(): - return False - return self.instance(type).detect(pos) - - def instance(self, type): - "Get an instance of the given type." - if not type in self.instances or not self.instances[type]: - self.instances[type] = self.create(type) - return self.instances[type] - - def create(self, type): - "Create a new formula bit of the given type." - return Cloner.create(type).setfactory(self) - - def clearskipped(self, pos): - "Clear any skipped types." - while not pos.finished(): - if not self.skipany(pos): - return - return - - def skipany(self, pos): - "Skip any skipped types." - for type in self.skippedtypes: - if self.instance(type).detect(pos): - return self.parsetype(type, pos) - return None - - def parseany(self, pos): - "Parse any formula bit at the current location." - for type in self.types + self.skippedtypes: - if self.detecttype(type, pos): - return self.parsetype(type, pos) - Trace.error('Unrecognized formula at ' + pos.identifier()) - return FormulaConstant(pos.skipcurrent()) - - def parsetype(self, type, pos): - "Parse the given type and return it." - bit = self.instance(type) - self.instances[type] = None - returnedbit = bit.parsebit(pos) - if returnedbit: - return returnedbit.setfactory(self) - return bit - - def parseformula(self, formula): - "Parse a string of text that contains a whole formula." - pos = TextPosition(formula) - whole = self.create(WholeFormula) - if whole.detect(pos): - whole.parsebit(pos) - return whole - # no formula found - if not pos.finished(): - Trace.error('Unknown formula at: ' + pos.identifier()) - whole.add(TaggedBit().constant(formula, 'span class="unknown"')) - return whole - - -class Translator(object): - "Reads the configuration file and tries to find a translation." - "Otherwise falls back to the messages in the config file." - - instance = None - - def translate(cls, key): - "Get the translated message for a key." - return cls.instance.getmessage(key) - - translate = classmethod(translate) - - def __init__(self): - self.translation = None - self.first = True - - def findtranslation(self): - "Find the translation for the document language." - self.langcodes = None - if not DocumentParameters.language: - Trace.error('No language in document') - return - if not DocumentParameters.language in TranslationConfig.languages: - Trace.error('Unknown language ' + DocumentParameters.language) - return - if TranslationConfig.languages[DocumentParameters.language] == 'en': - return - langcodes = [TranslationConfig.languages[DocumentParameters.language]] - try: - self.translation = gettext.translation('elyxer', None, langcodes) - except IOError: - Trace.error('No translation for ' + unicode(langcodes)) - - def getmessage(self, key): - "Get the translated message for the given key." - if self.first: - self.findtranslation() - self.first = False - message = self.getuntranslated(key) - if not self.translation: - return message - try: - message = self.translation.ugettext(message) - except IOError: - pass - return message - - def getuntranslated(self, key): - "Get the untranslated message." - if not key in TranslationConfig.constants: - Trace.error('Cannot translate ' + key) - return key - return TranslationConfig.constants[key] - -Translator.instance = Translator() - - - -class NumberCounter(object): - "A counter for numbers (by default)." - "The type can be changed to return letters, roman numbers..." - - name = None - value = None - mode = None - master = None - - letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' - symbols = NumberingConfig.sequence['symbols'] - romannumerals = [ - ('M', 1000), ('CM', 900), ('D', 500), ('CD', 400), ('C', 100), - ('XC', 90), ('L', 50), ('XL', 40), ('X', 10), ('IX', 9), ('V', 5), - ('IV', 4), ('I', 1) - ] - - def __init__(self, name): - "Give a name to the counter." - self.name = name - - def setmode(self, mode): - "Set the counter mode. Can be changed at runtime." - self.mode = mode - return self - - def init(self, value): - "Set an initial value." - self.value = value - - def gettext(self): - "Get the next value as a text string." - return unicode(self.value) - - def getletter(self): - "Get the next value as a letter." - return self.getsequence(self.letters) - - def getsymbol(self): - "Get the next value as a symbol." - return self.getsequence(self.symbols) - - def getsequence(self, sequence): - "Get the next value from elyxer.a sequence." - return sequence[(self.value - 1) % len(sequence)] - - def getroman(self): - "Get the next value as a roman number." - result = '' - number = self.value - for numeral, value in self.romannumerals: - if number >= value: - result += numeral * (number / value) - number = number % value - return result - - def getvalue(self): - "Get the current value as configured in the current mode." - if not self.mode or self.mode in ['text', '1']: - return self.gettext() - if self.mode == 'A': - return self.getletter() - if self.mode == 'a': - return self.getletter().lower() - if self.mode == 'I': - return self.getroman() - if self.mode == '*': - return self.getsymbol() - Trace.error('Unknown counter mode ' + self.mode) - return self.gettext() - - def getnext(self): - "Increase the current value and get the next value as configured." - if not self.value: - self.value = 0 - self.value += 1 - return self.getvalue() - - def reset(self): - "Reset the counter." - self.value = 0 - - def __unicode__(self): - "Return a printable representation." - result = 'Counter ' + self.name - if self.mode: - result += ' in mode ' + self.mode - return result - - if sys.version_info >= (3, 0): - __str__ = __unicode__ - - -class DependentCounter(NumberCounter): - "A counter which depends on another one (the master)." - - def setmaster(self, master): - "Set the master counter." - self.master = master - self.last = self.master.getvalue() - return self - - def getnext(self): - "Increase or, if the master counter has changed, restart." - if self.last != self.master.getvalue(): - self.reset() - value = NumberCounter.getnext(self) - self.last = self.master.getvalue() - return value - - def getvalue(self): - "Get the value of the combined counter: master.dependent." - return self.master.getvalue() + '.' + NumberCounter.getvalue(self) - -class NumberGenerator(object): - "A number generator for unique sequences and hierarchical structures. Used in:" - " * ordered part numbers: Chapter 3, Section 5.3." - " * unique part numbers: Footnote 15, Bibliography cite [15]." - " * chaptered part numbers: Figure 3.15, Equation (8.3)." - " * unique roman part numbers: Part I, Book IV." - - chaptered = None - generator = None - - romanlayouts = [x.lower() for x in NumberingConfig.layouts['roman']] - orderedlayouts = [x.lower() for x in NumberingConfig.layouts['ordered']] - - counters = dict() - appendix = None - - def deasterisk(self, type): - "Remove the possible asterisk in a layout type." - return type.replace('*', '') - - def isunique(self, type): - "Find out if the layout type corresponds to a unique part." - return self.isroman(type) - - def isroman(self, type): - "Find out if the layout type should have roman numeration." - return self.deasterisk(type).lower() in self.romanlayouts - - def isinordered(self, type): - "Find out if the layout type corresponds to an (un)ordered part." - return self.deasterisk(type).lower() in self.orderedlayouts - - def isnumbered(self, type): - "Find out if the type for a layout corresponds to a numbered layout." - if '*' in type: - return False - if self.isroman(type): - return True - if not self.isinordered(type): - return False - if self.getlevel(type) > DocumentParameters.maxdepth: - return False - return True - - def isunordered(self, type): - "Find out if the type contains an asterisk, basically." - return '*' in type - - def getlevel(self, type): - "Get the level that corresponds to a layout type." - if self.isunique(type): - return 0 - if not self.isinordered(type): - Trace.error('Unknown layout type ' + type) - return 0 - type = self.deasterisk(type).lower() - level = self.orderedlayouts.index(type) + 1 - return level - DocumentParameters.startinglevel - - def getparttype(self, type): - "Obtain the type for the part: without the asterisk, " - "and switched to Appendix if necessary." - if NumberGenerator.appendix and self.getlevel(type) == 1: - return 'Appendix' - return self.deasterisk(type) - - def generate(self, type): - "Generate a number for a layout type." - "Unique part types such as Part or Book generate roman numbers: Part I." - "Ordered part types return dot-separated tuples: Chapter 5, Subsection 2.3.5." - "Everything else generates unique numbers: Bibliography [1]." - "Each invocation results in a new number." - return self.getcounter(type).getnext() - - def getcounter(self, type): - "Get the counter for the given type." - type = type.lower() - if not type in self.counters: - self.counters[type] = self.create(type) - return self.counters[type] - - def create(self, type): - "Create a counter for the given type." - if self.isnumbered(type) and self.getlevel(type) > 1: - index = self.orderedlayouts.index(type) - above = self.orderedlayouts[index - 1] - master = self.getcounter(above) - return self.createdependent(type, master) - counter = NumberCounter(type) - if self.isroman(type): - counter.setmode('I') - return counter - - def getdependentcounter(self, type, master): - "Get (or create) a counter of the given type that depends on another." - if not type in self.counters or not self.counters[type].master: - self.counters[type] = self.createdependent(type, master) - return self.counters[type] - - def createdependent(self, type, master): - "Create a dependent counter given the master." - return DependentCounter(type).setmaster(master) - - def startappendix(self): - "Start appendices here." - firsttype = self.orderedlayouts[DocumentParameters.startinglevel] - counter = self.getcounter(firsttype) - counter.setmode('A').reset() - NumberGenerator.appendix = True - -class ChapteredGenerator(NumberGenerator): - "Generate chaptered numbers, as in Chapter.Number." - "Used in equations, figures: Equation (5.3), figure 8.15." - - def generate(self, type): - "Generate a number which goes with first-level numbers (chapters). " - "For the article classes a unique number is generated." - if DocumentParameters.startinglevel > 0: - return NumberGenerator.generator.generate(type) - chapter = self.getcounter('Chapter') - return self.getdependentcounter(type, chapter).getnext() - - -NumberGenerator.chaptered = ChapteredGenerator() -NumberGenerator.generator = NumberGenerator() - - - - - - -class ContainerSize(object): - "The size of a container." - - width = None - height = None - maxwidth = None - maxheight = None - scale = None - - def set(self, width = None, height = None): - "Set the proper size with width and height." - self.setvalue('width', width) - self.setvalue('height', height) - return self - - def setmax(self, maxwidth = None, maxheight = None): - "Set max width and/or height." - self.setvalue('maxwidth', maxwidth) - self.setvalue('maxheight', maxheight) - return self - - def readparameters(self, container): - "Read some size parameters off a container." - self.setparameter(container, 'width') - self.setparameter(container, 'height') - self.setparameter(container, 'scale') - self.checkvalidheight(container) - return self - - def setparameter(self, container, name): - "Read a size parameter off a container, and set it if present." - value = container.getparameter(name) - self.setvalue(name, value) - - def setvalue(self, name, value): - "Set the value of a parameter name, only if it's valid." - value = self.processparameter(value) - if value: - setattr(self, name, value) - - def checkvalidheight(self, container): - "Check if the height parameter is valid; otherwise erase it." - heightspecial = container.getparameter('height_special') - if self.height and self.extractnumber(self.height) == '1' and heightspecial == 'totalheight': - self.height = None - - def processparameter(self, value): - "Do the full processing on a parameter." - if not value: - return None - if self.extractnumber(value) == '0': - return None - for ignored in StyleConfig.size['ignoredtexts']: - if ignored in value: - value = value.replace(ignored, '') - return value - - def extractnumber(self, text): - "Extract the first number in the given text." - result = '' - decimal = False - for char in text: - if char.isdigit(): - result += char - elif char == '.' and not decimal: - result += char - decimal = True - else: - return result - return result - - def checkimage(self, width, height): - "Check image dimensions, set them if possible." - if width: - self.maxwidth = unicode(width) + 'px' - if self.scale and not self.width: - self.width = self.scalevalue(width) - if height: - self.maxheight = unicode(height) + 'px' - if self.scale and not self.height: - self.height = self.scalevalue(height) - if self.width and not self.height: - self.height = 'auto' - if self.height and not self.width: - self.width = 'auto' - - def scalevalue(self, value): - "Scale the value according to the image scale and return it as unicode." - scaled = value * int(self.scale) / 100 - return unicode(int(scaled)) + 'px' - - def removepercentwidth(self): - "Remove percent width if present, to set it at the figure level." - if not self.width: - return None - if not '%' in self.width: - return None - width = self.width - self.width = None - if self.height == 'auto': - self.height = None - return width - - def addstyle(self, container): - "Add the proper style attribute to the output tag." - if not isinstance(container.output, TaggedOutput): - Trace.error('No tag to add style, in ' + unicode(container)) - if not self.width and not self.height and not self.maxwidth and not self.maxheight: - # nothing to see here; move along - return - tag = ' style="' - tag += self.styleparameter('width') - tag += self.styleparameter('maxwidth') - tag += self.styleparameter('height') - tag += self.styleparameter('maxheight') - if tag[-1] == ' ': - tag = tag[:-1] - tag += '"' - container.output.tag += tag - - def styleparameter(self, name): - "Get the style for a single parameter." - value = getattr(self, name) - if value: - return name.replace('max', 'max-') + ': ' + value + '; ' - return '' - - - -class QuoteContainer(Container): - "A container for a pretty quote" - - def __init__(self): - self.parser = BoundedParser() - self.output = FixedOutput() - - def process(self): - "Process contents" - self.type = self.header[2] - if not self.type in StyleConfig.quotes: - Trace.error('Quote type ' + self.type + ' not found') - self.html = ['"'] - return - self.html = [StyleConfig.quotes[self.type]] - -class LyXLine(Container): - "A Lyx line" - - def __init__(self): - self.parser = LoneCommand() - self.output = FixedOutput() - - def process(self): - self.html = ['<hr class="line" />'] - -class EmphaticText(TaggedText): - "Text with emphatic mode" - - def process(self): - self.output.tag = 'i' - -class ShapedText(TaggedText): - "Text shaped (italic, slanted)" - - def process(self): - self.type = self.header[1] - if not self.type in TagConfig.shaped: - Trace.error('Unrecognized shape ' + self.header[1]) - self.output.tag = 'span' - return - self.output.tag = TagConfig.shaped[self.type] - -class VersalitasText(TaggedText): - "Text in versalitas" - - def process(self): - self.output.tag = 'span class="versalitas"' - -class ColorText(TaggedText): - "Colored text" - - def process(self): - self.color = self.header[1] - self.output.tag = 'span class="' + self.color + '"' - -class SizeText(TaggedText): - "Sized text" - - def process(self): - self.size = self.header[1] - self.output.tag = 'span class="' + self.size + '"' - -class BoldText(TaggedText): - "Bold text" - - def process(self): - self.output.tag = 'b' - -class TextFamily(TaggedText): - "A bit of text from elyxer.a different family" - - def process(self): - "Parse the type of family" - self.type = self.header[1] - if not self.type in TagConfig.family: - Trace.error('Unrecognized family ' + type) - self.output.tag = 'span' - return - self.output.tag = TagConfig.family[self.type] - -class Hfill(TaggedText): - "Horizontall fill" - - def process(self): - self.output.tag = 'span class="hfill"' - -class BarredText(TaggedText): - "Text with a bar somewhere" - - def process(self): - "Parse the type of bar" - self.type = self.header[1] - if not self.type in TagConfig.barred: - Trace.error('Unknown bar type ' + self.type) - self.output.tag = 'span' - return - self.output.tag = TagConfig.barred[self.type] - -class LangLine(TaggedText): - "A line with language information" - - def process(self): - "Only generate a span with lang info when the language is recognized." - lang = self.header[1] - if not lang in TranslationConfig.languages: - self.output = ContentsOutput() - return - isolang = TranslationConfig.languages[lang] - self.output = TaggedOutput().settag('span lang="' + isolang + '"', False) - -class InsetLength(BlackBox): - "A length measure inside an inset." - - def process(self): - self.length = self.header[1] - -class Space(Container): - "A space of several types" - - def __init__(self): - self.parser = InsetParser() - self.output = FixedOutput() - - def process(self): - self.type = self.header[2] - if self.type not in StyleConfig.hspaces: - Trace.error('Unknown space type ' + self.type) - self.html = [' '] - return - self.html = [StyleConfig.hspaces[self.type]] - length = self.getlength() - if not length: - return - self.output = TaggedOutput().settag('span class="hspace"', False) - ContainerSize().set(length).addstyle(self) - - def getlength(self): - "Get the space length from elyxer.the contents or parameters." - if len(self.contents) == 0 or not isinstance(self.contents[0], InsetLength): - return None - return self.contents[0].length - -class VerticalSpace(Container): - "An inset that contains a vertical space." - - def __init__(self): - self.parser = InsetParser() - self.output = FixedOutput() - - def process(self): - "Set the correct tag" - self.type = self.header[2] - if self.type not in StyleConfig.vspaces: - self.output = TaggedOutput().settag('div class="vspace" style="height: ' + self.type + ';"', True) - return - self.html = [StyleConfig.vspaces[self.type]] - -class Align(Container): - "Bit of aligned text" - - def __init__(self): - self.parser = ExcludingParser() - self.output = TaggedOutput().setbreaklines(True) - - def process(self): - self.output.tag = 'div class="' + self.header[1] + '"' - -class Newline(Container): - "A newline" - - def __init__(self): - self.parser = LoneCommand() - self.output = FixedOutput() - - def process(self): - "Process contents" - self.html = ['<br/>\n'] - -class NewPage(Newline): - "A new page" - - def process(self): - "Process contents" - self.html = ['<p><br/>\n</p>\n'] - -class Separator(Container): - "A separator string which is not extracted by extracttext()." - - def __init__(self, constant): - self.output = FixedOutput() - self.contents = [] - self.html = [constant] - -class StrikeOut(TaggedText): - "Striken out text." - - def process(self): - "Set the output tag to strike." - self.output.tag = 'strike' - -class StartAppendix(BlackBox): - "Mark to start an appendix here." - "From this point on, all chapters become appendices." - - def process(self): - "Activate the special numbering scheme for appendices, using letters." - NumberGenerator.generator.startappendix() - - - - - - -class Link(Container): - "A link to another part of the document" - - anchor = None - url = None - type = None - page = None - target = None - destination = None - title = None - - def __init__(self): - "Initialize the link, add target if configured." - self.contents = [] - self.parser = InsetParser() - self.output = LinkOutput() - if Options.target: - self.target = Options.target - - def complete(self, text, anchor = None, url = None, type = None, title = None): - "Complete the link." - self.contents = [Constant(text)] - if anchor: - self.anchor = anchor - if url: - self.url = url - if type: - self.type = type - if title: - self.title = title - return self - - def computedestination(self): - "Use the destination link to fill in the destination URL." - if not self.destination: - return - self.url = '' - if self.destination.anchor: - self.url = '#' + self.destination.anchor - if self.destination.page: - self.url = self.destination.page + self.url - - def setmutualdestination(self, destination): - "Set another link as destination, and set its destination to this one." - self.destination = destination - destination.destination = self - - def __unicode__(self): - "Return a printable representation." - result = 'Link' - if self.anchor: - result += ' #' + self.anchor - if self.url: - result += ' to ' + self.url - return result - - if sys.version_info >= (3, 0): - __str__ = __unicode__ - - -class URL(Link): - "A clickable URL" - - def process(self): - "Read URL from elyxer.parameters" - target = self.escape(self.getparameter('target')) - self.url = target - type = self.getparameter('type') - if type: - self.url = self.escape(type) + target - name = self.getparameter('name') - if not name: - name = target - self.contents = [Constant(name)] - -class FlexURL(URL): - "A flexible URL" - - def process(self): - "Read URL from elyxer.contents" - self.url = self.extracttext() - -class LinkOutput(ContainerOutput): - "A link pointing to some destination" - "Or an anchor (destination)" - - def gethtml(self, link): - "Get the HTML code for the link" - type = link.__class__.__name__ - if link.type: - type = link.type - tag = 'a class="' + type + '"' - if link.anchor: - tag += ' name="' + link.anchor + '"' - if link.destination: - link.computedestination() - if link.url: - tag += ' href="' + link.url + '"' - if link.target: - tag += ' target="' + link.target + '"' - if link.title: - tag += ' title="' + link.title + '"' - return TaggedOutput().settag(tag).gethtml(link) - - - - - -class Postprocessor(object): - "Postprocess a container keeping some context" - - stages = [] - - def __init__(self): - self.stages = StageDict(Postprocessor.stages, self) - self.current = None - self.last = None - - def postprocess(self, next): - "Postprocess a container and its contents." - self.postrecursive(self.current) - result = self.postcurrent(next) - self.last = self.current - self.current = next - return result - - def postrecursive(self, container): - "Postprocess the container contents recursively" - if not hasattr(container, 'contents'): - return - if len(container.contents) == 0: - return - if hasattr(container, 'postprocess'): - if not container.postprocess: - return - postprocessor = Postprocessor() - contents = [] - for element in container.contents: - post = postprocessor.postprocess(element) - if post: - contents.append(post) - # two rounds to empty the pipeline - for i in range(2): - post = postprocessor.postprocess(None) - if post: - contents.append(post) - container.contents = contents - - def postcurrent(self, next): - "Postprocess the current element taking into account next and last." - stage = self.stages.getstage(self.current) - if not stage: - return self.current - return stage.postprocess(self.last, self.current, next) - -class StageDict(object): - "A dictionary of stages corresponding to classes" - - def __init__(self, classes, postprocessor): - "Instantiate an element from elyxer.each class and store as a dictionary" - instances = self.instantiate(classes, postprocessor) - self.stagedict = dict([(x.processedclass, x) for x in instances]) - - def instantiate(self, classes, postprocessor): - "Instantiate an element from elyxer.each class" - stages = [x.__new__(x) for x in classes] - for element in stages: - element.__init__() - element.postprocessor = postprocessor - return stages - - def getstage(self, element): - "Get the stage for a given element, if the type is in the dict" - if not element.__class__ in self.stagedict: - return None - return self.stagedict[element.__class__] - - - -class Label(Link): - "A label to be referenced" - - names = dict() - lastlayout = None - - def __init__(self): - Link.__init__(self) - self.lastnumbered = None - - def process(self): - "Process a label container." - key = self.getparameter('name') - self.create(' ', key) - self.lastnumbered = Label.lastlayout - - def create(self, text, key, type = 'Label'): - "Create the label for a given key." - self.key = key - self.complete(text, anchor = key, type = type) - Label.names[key] = self - if key in Reference.references: - for reference in Reference.references[key]: - reference.destination = self - return self - - def findpartkey(self): - "Get the part key for the latest numbered container seen." - numbered = self.numbered(self) - if numbered and numbered.partkey: - return numbered.partkey - return '' - - def numbered(self, container): - "Get the numbered container for the label." - if container.partkey: - return container - if not container.parent: - if self.lastnumbered: - return self.lastnumbered - return None - return self.numbered(container.parent) - - def __unicode__(self): - "Return a printable representation." - if not hasattr(self, 'key'): - return 'Unnamed label' - return 'Label ' + self.key - - if sys.version_info >= (3, 0): - __str__ = __unicode__ - - -class Reference(Link): - "A reference to a label." - - references = dict() - key = 'none' - - def process(self): - "Read the reference and set the arrow." - self.key = self.getparameter('reference') - if self.key in Label.names: - self.direction = u'↑' - label = Label.names[self.key] - else: - self.direction = u'↓' - label = Label().complete(' ', self.key, 'preref') - self.destination = label - self.formatcontents() - if not self.key in Reference.references: - Reference.references[self.key] = [] - Reference.references[self.key].append(self) - - def formatcontents(self): - "Format the reference contents." - formatkey = self.getparameter('LatexCommand') - if not formatkey: - formatkey = 'ref' - self.formatted = u'↕' - if formatkey in StyleConfig.referenceformats: - self.formatted = StyleConfig.referenceformats[formatkey] - else: - Trace.error('Unknown reference format ' + formatkey) - self.replace(u'↕', self.direction) - self.replace('#', '1') - self.replace('on-page', Translator.translate('on-page')) - partkey = self.destination.findpartkey() - # only if partkey and partkey.number are not null, send partkey.number - self.replace('@', partkey and partkey.number) - self.replace(u'¶', partkey and partkey.tocentry) - if not '$' in self.formatted or not partkey or not partkey.titlecontents: - # there is a $ left, but it should go away on preprocessing - self.contents = [Constant(self.formatted)] - return - pieces = self.formatted.split('$') - self.contents = [Constant(pieces[0])] - for piece in pieces[1:]: - self.contents += partkey.titlecontents - self.contents.append(Constant(piece)) - - def replace(self, key, value): - "Replace a key in the format template with a value." - if not key in self.formatted: - return - if not value: - value = '' - self.formatted = self.formatted.replace(key, value) - - def __unicode__(self): - "Return a printable representation." - return 'Reference ' + self.key - - if sys.version_info >= (3, 0): - __str__ = __unicode__ - - -class FormulaCommand(FormulaBit): - "A LaTeX command inside a formula" - - types = [] - start = FormulaConfig.starts['command'] - commandmap = None - - def detect(self, pos): - "Find the current command." - return pos.checkfor(FormulaCommand.start) - - def parsebit(self, pos): - "Parse the command." - command = self.extractcommand(pos) - bit = self.parsewithcommand(command, pos) - if bit: - return bit - if command.startswith('\\up') or command.startswith('\\Up'): - upgreek = self.parseupgreek(command, pos) - if upgreek: - return upgreek - if not self.factory.defining: - Trace.error('Unknown command ' + command) - self.output = TaggedOutput().settag('span class="unknown"') - self.add(FormulaConstant(command)) - return None - - def parsewithcommand(self, command, pos): - "Parse the command type once we have the command." - for type in FormulaCommand.types: - if command in type.commandmap: - return self.parsecommandtype(command, type, pos) - return None - - def parsecommandtype(self, command, type, pos): - "Parse a given command type." - bit = self.factory.create(type) - bit.setcommand(command) - returned = bit.parsebit(pos) - if returned: - return returned - return bit - - def extractcommand(self, pos): - "Extract the command from elyxer.the current position." - if not pos.checkskip(FormulaCommand.start): - pos.error('Missing command start ' + FormulaCommand.start) - return - if pos.finished(): - return self.emptycommand(pos) - if pos.current().isalpha(): - # alpha command - command = FormulaCommand.start + pos.globalpha() - # skip mark of short command - pos.checkskip('*') - return command - # symbol command - return FormulaCommand.start + pos.skipcurrent() - - def emptycommand(self, pos): - """Check for an empty command: look for command disguised as ending. - Special case against '{ \\{ \\} }' situation.""" - command = '' - if not pos.isout(): - ending = pos.nextending() - if ending and pos.checkskip(ending): - command = ending - return FormulaCommand.start + command - - def parseupgreek(self, command, pos): - "Parse the Greek \\up command.." - if len(command) < 4: - return None - if command.startswith('\\up'): - upcommand = '\\' + command[3:] - elif pos.checkskip('\\Up'): - upcommand = '\\' + command[3:4].upper() + command[4:] - else: - Trace.error('Impossible upgreek command: ' + command) - return - upgreek = self.parsewithcommand(upcommand, pos) - if upgreek: - upgreek.type = 'font' - return upgreek - -class CommandBit(FormulaCommand): - "A formula bit that includes a command" - - def setcommand(self, command): - "Set the command in the bit" - self.command = command - if self.commandmap: - self.original += command - self.translated = self.commandmap[self.command] - - def parseparameter(self, pos): - "Parse a parameter at the current position" - self.factory.clearskipped(pos) - if pos.finished(): - return None - parameter = self.factory.parseany(pos) - self.add(parameter) - return parameter - - def parsesquare(self, pos): - "Parse a square bracket" - self.factory.clearskipped(pos) - if not self.factory.detecttype(SquareBracket, pos): - return None - bracket = self.factory.parsetype(SquareBracket, pos) - self.add(bracket) - return bracket - - def parseliteral(self, pos): - "Parse a literal bracket." - self.factory.clearskipped(pos) - if not self.factory.detecttype(Bracket, pos): - if not pos.isvalue(): - Trace.error('No literal parameter found at: ' + pos.identifier()) - return None - return pos.globvalue() - bracket = Bracket().setfactory(self.factory) - self.add(bracket.parseliteral(pos)) - return bracket.literal - - def parsesquareliteral(self, pos): - "Parse a square bracket literally." - self.factory.clearskipped(pos) - if not self.factory.detecttype(SquareBracket, pos): - return None - bracket = SquareBracket().setfactory(self.factory) - self.add(bracket.parseliteral(pos)) - return bracket.literal - - def parsetext(self, pos): - "Parse a text parameter." - self.factory.clearskipped(pos) - if not self.factory.detecttype(Bracket, pos): - Trace.error('No text parameter for ' + self.command) - return None - bracket = Bracket().setfactory(self.factory).parsetext(pos) - self.add(bracket) - return bracket - -class EmptyCommand(CommandBit): - "An empty command (without parameters)" - - commandmap = FormulaConfig.commands - - def parsebit(self, pos): - "Parse a command without parameters" - self.contents = [FormulaConstant(self.translated)] - -class SpacedCommand(CommandBit): - "An empty command which should have math spacing in formulas." - - commandmap = FormulaConfig.spacedcommands - - def parsebit(self, pos): - "Place as contents the command translated and spaced." - self.contents = [FormulaConstant(u' ' + self.translated + u' ')] - -class AlphaCommand(EmptyCommand): - "A command without paramters whose result is alphabetical" - - commandmap = FormulaConfig.alphacommands - - def parsebit(self, pos): - "Parse the command and set type to alpha" - EmptyCommand.parsebit(self, pos) - self.type = 'alpha' - -class OneParamFunction(CommandBit): - "A function of one parameter" - - commandmap = FormulaConfig.onefunctions - simplified = False - - def parsebit(self, pos): - "Parse a function with one parameter" - self.output = TaggedOutput().settag(self.translated) - self.parseparameter(pos) - self.simplifyifpossible() - - def simplifyifpossible(self): - "Try to simplify to a single character." - if self.original in self.commandmap: - self.output = FixedOutput() - self.html = [self.commandmap[self.original]] - self.simplified = True - -class SymbolFunction(CommandBit): - "Find a function which is represented by a symbol (like _ or ^)" - - commandmap = FormulaConfig.symbolfunctions - - def detect(self, pos): - "Find the symbol" - return pos.current() in SymbolFunction.commandmap - - def parsebit(self, pos): - "Parse the symbol" - self.setcommand(pos.current()) - pos.skip(self.command) - self.output = TaggedOutput().settag(self.translated) - self.parseparameter(pos) - -class TextFunction(CommandBit): - "A function where parameters are read as text." - - commandmap = FormulaConfig.textfunctions - - def parsebit(self, pos): - "Parse a text parameter" - self.output = TaggedOutput().settag(self.translated) - self.parsetext(pos) - - def process(self): - "Set the type to font" - self.type = 'font' - -class LabelFunction(CommandBit): - "A function that acts as a label" - - commandmap = FormulaConfig.labelfunctions - - def parsebit(self, pos): - "Parse a literal parameter" - self.key = self.parseliteral(pos) - - def process(self): - "Add an anchor with the label contents." - self.type = 'font' - self.label = Label().create(' ', self.key, type = 'eqnumber') - self.contents = [self.label] - # store as a Label so we know it's been seen - Label.names[self.key] = self.label - -class FontFunction(OneParamFunction): - "A function of one parameter that changes the font" - - commandmap = FormulaConfig.fontfunctions - - def process(self): - "Simplify if possible using a single character." - self.type = 'font' - self.simplifyifpossible() - -FormulaFactory.types += [FormulaCommand, SymbolFunction] -FormulaCommand.types = [ - AlphaCommand, EmptyCommand, OneParamFunction, FontFunction, LabelFunction, - TextFunction, SpacedCommand, - ] - - - - - - - - - - - - -class BigSymbol(object): - "A big symbol generator." - - symbols = FormulaConfig.bigsymbols - - def __init__(self, symbol): - "Create the big symbol." - self.symbol = symbol - - def getpieces(self): - "Get an array with all pieces." - if not self.symbol in self.symbols: - return [self.symbol] - if self.smalllimit(): - return [self.symbol] - return self.symbols[self.symbol] - - def smalllimit(self): - "Decide if the limit should be a small, one-line symbol." - if not DocumentParameters.displaymode: - return True - if len(self.symbols[self.symbol]) == 1: - return True - return Options.simplemath - -class BigBracket(BigSymbol): - "A big bracket generator." - - def __init__(self, size, bracket, alignment='l'): - "Set the size and symbol for the bracket." - self.size = size - self.original = bracket - self.alignment = alignment - self.pieces = None - if bracket in FormulaConfig.bigbrackets: - self.pieces = FormulaConfig.bigbrackets[bracket] - - def getpiece(self, index): - "Return the nth piece for the bracket." - function = getattr(self, 'getpiece' + unicode(len(self.pieces))) - return function(index) - - def getpiece1(self, index): - "Return the only piece for a single-piece bracket." - return self.pieces[0] - - def getpiece3(self, index): - "Get the nth piece for a 3-piece bracket: parenthesis or square bracket." - if index == 0: - return self.pieces[0] - if index == self.size - 1: - return self.pieces[-1] - return self.pieces[1] - - def getpiece4(self, index): - "Get the nth piece for a 4-piece bracket: curly bracket." - if index == 0: - return self.pieces[0] - if index == self.size - 1: - return self.pieces[3] - if index == (self.size - 1)/2: - return self.pieces[2] - return self.pieces[1] - - def getcell(self, index): - "Get the bracket piece as an array cell." - piece = self.getpiece(index) - span = 'span class="bracket align-' + self.alignment + '"' - return TaggedBit().constant(piece, span) - - def getcontents(self): - "Get the bracket as an array or as a single bracket." - if self.size == 1 or not self.pieces: - return self.getsinglebracket() - rows = [] - for index in range(self.size): - cell = self.getcell(index) - rows.append(TaggedBit().complete([cell], 'span class="arrayrow"')) - return [TaggedBit().complete(rows, 'span class="array"')] - - def getsinglebracket(self): - "Return the bracket as a single sign." - if self.original == '.': - return [TaggedBit().constant('', 'span class="emptydot"')] - return [TaggedBit().constant(self.original, 'span class="symbol"')] - - - - - - -class FormulaEquation(CommandBit): - "A simple numbered equation." - - piece = 'equation' - - def parsebit(self, pos): - "Parse the array" - self.output = ContentsOutput() - self.add(self.factory.parsetype(WholeFormula, pos)) - -class FormulaCell(FormulaCommand): - "An array cell inside a row" - - def setalignment(self, alignment): - self.alignment = alignment - self.output = TaggedOutput().settag('span class="arraycell align-' + alignment +'"', True) - return self - - def parsebit(self, pos): - self.factory.clearskipped(pos) - if pos.finished(): - return - self.add(self.factory.parsetype(WholeFormula, pos)) - -class FormulaRow(FormulaCommand): - "An array row inside an array" - - cellseparator = FormulaConfig.array['cellseparator'] - - def setalignments(self, alignments): - self.alignments = alignments - self.output = TaggedOutput().settag('span class="arrayrow"', True) - return self - - def parsebit(self, pos): - "Parse a whole row" - index = 0 - pos.pushending(self.cellseparator, optional=True) - while not pos.finished(): - cell = self.createcell(index) - cell.parsebit(pos) - self.add(cell) - index += 1 - pos.checkskip(self.cellseparator) - if len(self.contents) == 0: - self.output = EmptyOutput() - - def createcell(self, index): - "Create the cell that corresponds to the given index." - alignment = self.alignments[index % len(self.alignments)] - return self.factory.create(FormulaCell).setalignment(alignment) - -class MultiRowFormula(CommandBit): - "A formula with multiple rows." - - def parserows(self, pos): - "Parse all rows, finish when no more row ends" - self.rows = [] - first = True - for row in self.iteraterows(pos): - if first: - first = False - else: - # intersparse empty rows - self.addempty() - row.parsebit(pos) - self.addrow(row) - self.size = len(self.rows) - - def iteraterows(self, pos): - "Iterate over all rows, end when no more row ends" - rowseparator = FormulaConfig.array['rowseparator'] - while True: - pos.pushending(rowseparator, True) - row = self.factory.create(FormulaRow) - yield row.setalignments(self.alignments) - if pos.checkfor(rowseparator): - self.original += pos.popending(rowseparator) - else: - return - - def addempty(self): - "Add an empty row." - row = self.factory.create(FormulaRow).setalignments(self.alignments) - for index, originalcell in enumerate(self.rows[-1].contents): - cell = row.createcell(index) - cell.add(FormulaConstant(u' ')) - row.add(cell) - self.addrow(row) - - def addrow(self, row): - "Add a row to the contents and to the list of rows." - self.rows.append(row) - self.add(row) - -class FormulaArray(MultiRowFormula): - "An array within a formula" - - piece = 'array' - - def parsebit(self, pos): - "Parse the array" - self.output = TaggedOutput().settag('span class="array"', False) - self.parsealignments(pos) - self.parserows(pos) - - def parsealignments(self, pos): - "Parse the different alignments" - # vertical - self.valign = 'c' - literal = self.parsesquareliteral(pos) - if literal: - self.valign = literal - # horizontal - literal = self.parseliteral(pos) - self.alignments = [] - for l in literal: - self.alignments.append(l) - -class FormulaMatrix(MultiRowFormula): - "A matrix (array with center alignment)." - - piece = 'matrix' - - def parsebit(self, pos): - "Parse the matrix, set alignments to 'c'." - self.output = TaggedOutput().settag('span class="array"', False) - self.valign = 'c' - self.alignments = ['c'] - self.parserows(pos) - -class FormulaCases(MultiRowFormula): - "A cases statement" - - piece = 'cases' - - def parsebit(self, pos): - "Parse the cases" - self.output = ContentsOutput() - self.alignments = ['l', 'l'] - self.parserows(pos) - for row in self.contents: - for cell in row.contents: - cell.output.settag('span class="case align-l"', True) - cell.contents.append(FormulaConstant(u' ')) - array = TaggedBit().complete(self.contents, 'span class="bracketcases"', True) - brace = BigBracket(len(self.contents), '{', 'l') - self.contents = brace.getcontents() + [array] - -class EquationEnvironment(MultiRowFormula): - "A \\begin{}...\\end equation environment with rows and cells." - - def parsebit(self, pos): - "Parse the whole environment." - self.output = TaggedOutput().settag('span class="environment"', False) - environment = self.piece.replace('*', '') - if environment in FormulaConfig.environments: - self.alignments = FormulaConfig.environments[environment] - else: - Trace.error('Unknown equation environment ' + self.piece) - self.alignments = ['l'] - self.parserows(pos) - -class BeginCommand(CommandBit): - "A \\begin{}...\\end command and what it entails (array, cases, aligned)" - - commandmap = {FormulaConfig.array['begin']:''} - - types = [FormulaEquation, FormulaArray, FormulaCases, FormulaMatrix] - - def parsebit(self, pos): - "Parse the begin command" - command = self.parseliteral(pos) - bit = self.findbit(command) - ending = FormulaConfig.array['end'] + '{' + command + '}' - pos.pushending(ending) - bit.parsebit(pos) - self.add(bit) - self.original += pos.popending(ending) - self.size = bit.size - - def findbit(self, piece): - "Find the command bit corresponding to the \\begin{piece}" - for type in BeginCommand.types: - if piece.replace('*', '') == type.piece: - return self.factory.create(type) - bit = self.factory.create(EquationEnvironment) - bit.piece = piece - return bit - -FormulaCommand.types += [BeginCommand] - - - -class CombiningFunction(OneParamFunction): - - commandmap = FormulaConfig.combiningfunctions - - def parsebit(self, pos): - "Parse a combining function." - self.type = 'alpha' - combining = self.translated - parameter = self.parsesingleparameter(pos) - if not parameter: - Trace.error('Empty parameter for combining function ' + self.command) - elif len(parameter.extracttext()) != 1: - Trace.error('Applying combining function ' + self.command + ' to invalid string "' + parameter.extracttext() + '"') - self.contents.append(Constant(combining)) - - def parsesingleparameter(self, pos): - "Parse a parameter, or a single letter." - self.factory.clearskipped(pos) - if pos.finished(): - Trace.error('Error while parsing single parameter at ' + pos.identifier()) - return None - if self.factory.detecttype(Bracket, pos) \ - or self.factory.detecttype(FormulaCommand, pos): - return self.parseparameter(pos) - letter = FormulaConstant(pos.skipcurrent()) - self.add(letter) - return letter - -class DecoratingFunction(OneParamFunction): - "A function that decorates some bit of text" - - commandmap = FormulaConfig.decoratingfunctions - - def parsebit(self, pos): - "Parse a decorating function" - self.type = 'alpha' - symbol = self.translated - self.symbol = TaggedBit().constant(symbol, 'span class="symbolover"') - self.parameter = self.parseparameter(pos) - self.output = TaggedOutput().settag('span class="withsymbol"') - self.contents.insert(0, self.symbol) - self.parameter.output = TaggedOutput().settag('span class="undersymbol"') - self.simplifyifpossible() - -class LimitCommand(EmptyCommand): - "A command which accepts limits above and below, in display mode." - - commandmap = FormulaConfig.limitcommands - - def parsebit(self, pos): - "Parse a limit command." - pieces = BigSymbol(self.translated).getpieces() - self.output = TaggedOutput().settag('span class="limits"') - for piece in pieces: - self.contents.append(TaggedBit().constant(piece, 'span class="limit"')) - -class LimitPreviousCommand(LimitCommand): - "A command to limit the previous command." - - commandmap = None - - def parsebit(self, pos): - "Do nothing." - self.output = TaggedOutput().settag('span class="limits"') - self.factory.clearskipped(pos) - - def __unicode__(self): - "Return a printable representation." - return 'Limit previous command' - - if sys.version_info >= (3, 0): - __str__ = __unicode__ - - -class LimitsProcessor(MathsProcessor): - "A processor for limits inside an element." - - def process(self, contents, index): - "Process the limits for an element." - if Options.simplemath: - return - if self.checklimits(contents, index): - self.modifylimits(contents, index) - if self.checkscript(contents, index) and self.checkscript(contents, index + 1): - self.modifyscripts(contents, index) - - def checklimits(self, contents, index): - "Check if the current position has a limits command." - if not DocumentParameters.displaymode: - return False - if self.checkcommand(contents, index + 1, LimitPreviousCommand): - self.limitsahead(contents, index) - return False - if not isinstance(contents[index], LimitCommand): - return False - return self.checkscript(contents, index + 1) - - def limitsahead(self, contents, index): - "Limit the current element based on the next." - contents[index + 1].add(contents[index].clone()) - contents[index].output = EmptyOutput() - - def modifylimits(self, contents, index): - "Modify a limits commands so that the limits appear above and below." - limited = contents[index] - subscript = self.getlimit(contents, index + 1) - limited.contents.append(subscript) - if self.checkscript(contents, index + 1): - superscript = self.getlimit(contents, index + 1) - else: - superscript = TaggedBit().constant(u' ', 'sup class="limit"') - limited.contents.insert(0, superscript) - - def getlimit(self, contents, index): - "Get the limit for a limits command." - limit = self.getscript(contents, index) - limit.output.tag = limit.output.tag.replace('script', 'limit') - return limit - - def modifyscripts(self, contents, index): - "Modify the super- and subscript to appear vertically aligned." - subscript = self.getscript(contents, index) - # subscript removed so instead of index + 1 we get index again - superscript = self.getscript(contents, index) - scripts = TaggedBit().complete([superscript, subscript], 'span class="scripts"') - contents.insert(index, scripts) - - def checkscript(self, contents, index): - "Check if the current element is a sub- or superscript." - return self.checkcommand(contents, index, SymbolFunction) - - def checkcommand(self, contents, index, type): - "Check for the given type as the current element." - if len(contents) <= index: - return False - return isinstance(contents[index], type) - - def getscript(self, contents, index): - "Get the sub- or superscript." - bit = contents[index] - bit.output.tag += ' class="script"' - del contents[index] - return bit - -class BracketCommand(OneParamFunction): - "A command which defines a bracket." - - commandmap = FormulaConfig.bracketcommands - - def parsebit(self, pos): - "Parse the bracket." - OneParamFunction.parsebit(self, pos) - - def create(self, direction, character): - "Create the bracket for the given character." - self.original = character - self.command = '\\' + direction - self.contents = [FormulaConstant(character)] - return self - -class BracketProcessor(MathsProcessor): - "A processor for bracket commands." - - def process(self, contents, index): - "Convert the bracket using Unicode pieces, if possible." - if Options.simplemath: - return - if self.checkleft(contents, index): - return self.processleft(contents, index) - - def processleft(self, contents, index): - "Process a left bracket." - rightindex = self.findright(contents, index + 1) - if not rightindex: - return - size = self.findmax(contents, index, rightindex) - self.resize(contents[index], size) - self.resize(contents[rightindex], size) - - def checkleft(self, contents, index): - "Check if the command at the given index is left." - return self.checkdirection(contents[index], '\\left') - - def checkright(self, contents, index): - "Check if the command at the given index is right." - return self.checkdirection(contents[index], '\\right') - - def checkdirection(self, bit, command): - "Check if the given bit is the desired bracket command." - if not isinstance(bit, BracketCommand): - return False - return bit.command == command - - def findright(self, contents, index): - "Find the right bracket starting at the given index, or 0." - depth = 1 - while index < len(contents): - if self.checkleft(contents, index): - depth += 1 - if self.checkright(contents, index): - depth -= 1 - if depth == 0: - return index - index += 1 - return None - - def findmax(self, contents, leftindex, rightindex): - "Find the max size of the contents between the two given indices." - sliced = contents[leftindex:rightindex] - return max([element.size for element in sliced]) - - def resize(self, command, size): - "Resize a bracket command to the given size." - character = command.extracttext() - alignment = command.command.replace('\\', '') - bracket = BigBracket(size, character, alignment) - command.output = ContentsOutput() - command.contents = bracket.getcontents() - -class TodayCommand(EmptyCommand): - "Shows today's date." - - commandmap = None - - def parsebit(self, pos): - "Parse a command without parameters" - self.output = FixedOutput() - self.html = [datetime.date.today().strftime('%b %d, %Y')] - - -FormulaCommand.types += [ - DecoratingFunction, CombiningFunction, LimitCommand, BracketCommand, - ] - -FormulaProcessor.processors += [ - LimitsProcessor(), BracketProcessor(), - ] - - - -class ParameterDefinition(object): - "The definition of a parameter in a hybrid function." - "[] parameters are optional, {} parameters are mandatory." - "Each parameter has a one-character name, like {$1} or {$p}." - "A parameter that ends in ! like {$p!} is a literal." - "Example: [$1]{$p!} reads an optional parameter $1 and a literal mandatory parameter p." - - parambrackets = [('[', ']'), ('{', '}')] - - def __init__(self): - self.name = None - self.literal = False - self.optional = False - self.value = None - self.literalvalue = None - - def parse(self, pos): - "Parse a parameter definition: [$0], {$x}, {$1!}..." - for (opening, closing) in ParameterDefinition.parambrackets: - if pos.checkskip(opening): - if opening == '[': - self.optional = True - if not pos.checkskip('$'): - Trace.error('Wrong parameter name, did you mean $' + pos.current() + '?') - return None - self.name = pos.skipcurrent() - if pos.checkskip('!'): - self.literal = True - if not pos.checkskip(closing): - Trace.error('Wrong parameter closing ' + pos.skipcurrent()) - return None - return self - Trace.error('Wrong character in parameter template: ' + pos.skipcurrent()) - return None - - def read(self, pos, function): - "Read the parameter itself using the definition." - if self.literal: - if self.optional: - self.literalvalue = function.parsesquareliteral(pos) - else: - self.literalvalue = function.parseliteral(pos) - if self.literalvalue: - self.value = FormulaConstant(self.literalvalue) - elif self.optional: - self.value = function.parsesquare(pos) - else: - self.value = function.parseparameter(pos) - - def __unicode__(self): - "Return a printable representation." - result = 'param ' + self.name - if self.value: - result += ': ' + unicode(self.value) - else: - result += ' (empty)' - return result - - if sys.version_info >= (3, 0): - __str__ = __unicode__ - - -class ParameterFunction(CommandBit): - "A function with a variable number of parameters defined in a template." - "The parameters are defined as a parameter definition." - - def readparams(self, readtemplate, pos): - "Read the params according to the template." - self.params = dict() - for paramdef in self.paramdefs(readtemplate): - paramdef.read(pos, self) - self.params['$' + paramdef.name] = paramdef - - def paramdefs(self, readtemplate): - "Read each param definition in the template" - pos = TextPosition(readtemplate) - while not pos.finished(): - paramdef = ParameterDefinition().parse(pos) - if paramdef: - yield paramdef - - def getparam(self, name): - "Get a parameter as parsed." - if not name in self.params: - return None - return self.params[name] - - def getvalue(self, name): - "Get the value of a parameter." - return self.getparam(name).value - - def getliteralvalue(self, name): - "Get the literal value of a parameter." - param = self.getparam(name) - if not param or not param.literalvalue: - return None - return param.literalvalue - -class HybridFunction(ParameterFunction): - """ - A parameter function where the output is also defined using a template. - The template can use a number of functions; each function has an associated - tag. - Example: [f0{$1},span class="fbox"] defines a function f0 which corresponds - to a span of class fbox, yielding <span class="fbox">$1</span>. - Literal parameters can be used in tags definitions: - [f0{$1},span style="color: $p;"] - yields <span style="color: $p;">$1</span>, where $p is a literal parameter. - Sizes can be specified in hybridsizes, e.g. adding parameter sizes. By - default the resulting size is the max of all arguments. Sizes are used - to generate the right parameters. - A function followed by a single / is output as a self-closing XHTML tag: - [f0/,hr] - will generate <hr/>. - """ - - commandmap = FormulaConfig.hybridfunctions - - def parsebit(self, pos): - "Parse a function with [] and {} parameters" - readtemplate = self.translated[0] - writetemplate = self.translated[1] - self.readparams(readtemplate, pos) - self.contents = self.writeparams(writetemplate) - self.computehybridsize() - - def writeparams(self, writetemplate): - "Write all params according to the template" - return self.writepos(TextPosition(writetemplate)) - - def writepos(self, pos): - "Write all params as read in the parse position." - result = [] - while not pos.finished(): - if pos.checkskip('$'): - param = self.writeparam(pos) - if param: - result.append(param) - elif pos.checkskip('f'): - function = self.writefunction(pos) - if function: - function.type = None - result.append(function) - elif pos.checkskip('('): - result.append(self.writebracket('left', '(')) - elif pos.checkskip(')'): - result.append(self.writebracket('right', ')')) - else: - result.append(FormulaConstant(pos.skipcurrent())) - return result - - def writeparam(self, pos): - "Write a single param of the form $0, $x..." - name = '$' + pos.skipcurrent() - if not name in self.params: - Trace.error('Unknown parameter ' + name) - return None - if not self.params[name]: - return None - if pos.checkskip('.'): - self.params[name].value.type = pos.globalpha() - return self.params[name].value - - def writefunction(self, pos): - "Write a single function f0,...,fn." - tag = self.readtag(pos) - if not tag: - return None - if pos.checkskip('/'): - # self-closing XHTML tag, such as <hr/> - return TaggedBit().selfcomplete(tag) - if not pos.checkskip('{'): - Trace.error('Function should be defined in {}') - return None - pos.pushending('}') - contents = self.writepos(pos) - pos.popending() - if len(contents) == 0: - return None - return TaggedBit().complete(contents, tag) - - def readtag(self, pos): - "Get the tag corresponding to the given index. Does parameter substitution." - if not pos.current().isdigit(): - Trace.error('Function should be f0,...,f9: f' + pos.current()) - return None - index = int(pos.skipcurrent()) - if 2 + index > len(self.translated): - Trace.error('Function f' + unicode(index) + ' is not defined') - return None - tag = self.translated[2 + index] - if not '$' in tag: - return tag - for variable in self.params: - if variable in tag: - param = self.params[variable] - if not param.literal: - Trace.error('Parameters in tag ' + tag + ' should be literal: {' + variable + '!}') - continue - if param.literalvalue: - value = param.literalvalue - else: - value = '' - tag = tag.replace(variable, value) - return tag - - def writebracket(self, direction, character): - "Return a new bracket looking at the given direction." - return self.factory.create(BracketCommand).create(direction, character) - - def computehybridsize(self): - "Compute the size of the hybrid function." - if not self.command in HybridSize.configsizes: - self.computesize() - return - self.size = HybridSize().getsize(self) - # set the size in all elements at first level - for element in self.contents: - element.size = self.size - -class HybridSize(object): - "The size associated with a hybrid function." - - configsizes = FormulaConfig.hybridsizes - - def getsize(self, function): - "Read the size for a function and parse it." - sizestring = self.configsizes[function.command] - for name in function.params: - if name in sizestring: - size = function.params[name].value.computesize() - sizestring = sizestring.replace(name, unicode(size)) - if '$' in sizestring: - Trace.error('Unconverted variable in hybrid size: ' + sizestring) - return 1 - return eval(sizestring) - - -FormulaCommand.types += [HybridFunction] - - - - - - - - - -class HeaderParser(Parser): - "Parses the LyX header" - - def parse(self, reader): - "Parse header parameters into a dictionary, return the preamble." - contents = [] - self.parseending(reader, lambda: self.parseline(reader, contents)) - # skip last line - reader.nextline() - return contents - - def parseline(self, reader, contents): - "Parse a single line as a parameter or as a start" - line = reader.currentline() - if line.startswith(HeaderConfig.parameters['branch']): - self.parsebranch(reader) - return - elif line.startswith(HeaderConfig.parameters['lstset']): - LstParser().parselstset(reader) - return - elif line.startswith(HeaderConfig.parameters['beginpreamble']): - contents.append(self.factory.createcontainer(reader)) - return - # no match - self.parseparameter(reader) - - def parsebranch(self, reader): - "Parse all branch definitions." - branch = reader.currentline().split()[1] - reader.nextline() - subparser = HeaderParser().complete(HeaderConfig.parameters['endbranch']) - subparser.parse(reader) - options = BranchOptions(branch) - for key in subparser.parameters: - options.set(key, subparser.parameters[key]) - Options.branches[branch] = options - - def complete(self, ending): - "Complete the parser with the given ending." - self.ending = ending - return self - -class PreambleParser(Parser): - "A parser for the LyX preamble." - - preamble = [] - - def parse(self, reader): - "Parse the full preamble with all statements." - self.ending = HeaderConfig.parameters['endpreamble'] - self.parseending(reader, lambda: self.parsepreambleline(reader)) - return [] - - def parsepreambleline(self, reader): - "Parse a single preamble line." - PreambleParser.preamble.append(reader.currentline()) - reader.nextline() - -class LstParser(object): - "Parse global and local lstparams." - - globalparams = dict() - - def parselstset(self, reader): - "Parse a declaration of lstparams in lstset." - paramtext = self.extractlstset(reader) - if not '{' in paramtext: - Trace.error('Missing opening bracket in lstset: ' + paramtext) - return - lefttext = paramtext.split('{')[1] - croppedtext = lefttext[:-1] - LstParser.globalparams = self.parselstparams(croppedtext) - - def extractlstset(self, reader): - "Extract the global lstset parameters." - paramtext = '' - while not reader.finished(): - paramtext += reader.currentline() - reader.nextline() - if paramtext.endswith('}'): - return paramtext - Trace.error('Could not find end of \\lstset settings; aborting') - - def parsecontainer(self, container): - "Parse some lstparams from elyxer.a container." - container.lstparams = LstParser.globalparams.copy() - paramlist = container.getparameterlist('lstparams') - container.lstparams.update(self.parselstparams(paramlist)) - - def parselstparams(self, paramlist): - "Process a number of lstparams from elyxer.a list." - paramdict = dict() - for param in paramlist: - if not '=' in param: - if len(param.strip()) > 0: - Trace.error('Invalid listing parameter ' + param) - else: - key, value = param.split('=', 1) - paramdict[key] = value - return paramdict - - - - -class MacroDefinition(CommandBit): - "A function that defines a new command (a macro)." - - macros = dict() - - def parsebit(self, pos): - "Parse the function that defines the macro." - self.output = EmptyOutput() - self.parameternumber = 0 - self.defaults = [] - self.factory.defining = True - self.parseparameters(pos) - self.factory.defining = False - Trace.debug('New command ' + self.newcommand + ' (' + \ - unicode(self.parameternumber) + ' parameters)') - self.macros[self.newcommand] = self - - def parseparameters(self, pos): - "Parse all optional parameters (number of parameters, default values)" - "and the mandatory definition." - self.newcommand = self.parsenewcommand(pos) - # parse number of parameters - literal = self.parsesquareliteral(pos) - if literal: - self.parameternumber = int(literal) - # parse all default values - bracket = self.parsesquare(pos) - while bracket: - self.defaults.append(bracket) - bracket = self.parsesquare(pos) - # parse mandatory definition - self.definition = self.parseparameter(pos) - - def parsenewcommand(self, pos): - "Parse the name of the new command." - self.factory.clearskipped(pos) - if self.factory.detecttype(Bracket, pos): - return self.parseliteral(pos) - if self.factory.detecttype(FormulaCommand, pos): - return self.factory.create(FormulaCommand).extractcommand(pos) - Trace.error('Unknown formula bit in defining function at ' + pos.identifier()) - return 'unknown' - - def instantiate(self): - "Return an instance of the macro." - return self.definition.clone() - -class MacroParameter(FormulaBit): - "A parameter from elyxer.a macro." - - def detect(self, pos): - "Find a macro parameter: #n." - return pos.checkfor('#') - - def parsebit(self, pos): - "Parse the parameter: #n." - if not pos.checkskip('#'): - Trace.error('Missing parameter start #.') - return - self.number = int(pos.skipcurrent()) - self.original = '#' + unicode(self.number) - self.contents = [TaggedBit().constant('#' + unicode(self.number), 'span class="unknown"')] - -class MacroFunction(CommandBit): - "A function that was defined using a macro." - - commandmap = MacroDefinition.macros - - def parsebit(self, pos): - "Parse a number of input parameters." - self.output = FilteredOutput() - self.values = [] - macro = self.translated - self.parseparameters(pos, macro) - self.completemacro(macro) - - def parseparameters(self, pos, macro): - "Parse as many parameters as are needed." - self.parseoptional(pos, list(macro.defaults)) - self.parsemandatory(pos, macro.parameternumber - len(macro.defaults)) - if len(self.values) < macro.parameternumber: - Trace.error('Missing parameters in macro ' + unicode(self)) - - def parseoptional(self, pos, defaults): - "Parse optional parameters." - optional = [] - while self.factory.detecttype(SquareBracket, pos): - optional.append(self.parsesquare(pos)) - if len(optional) > len(defaults): - break - for value in optional: - default = defaults.pop() - if len(value.contents) > 0: - self.values.append(value) - else: - self.values.append(default) - self.values += defaults - - def parsemandatory(self, pos, number): - "Parse a number of mandatory parameters." - for index in range(number): - parameter = self.parsemacroparameter(pos, number - index) - if not parameter: - return - self.values.append(parameter) - - def parsemacroparameter(self, pos, remaining): - "Parse a macro parameter. Could be a bracket or a single letter." - "If there are just two values remaining and there is a running number," - "parse as two separater numbers." - self.factory.clearskipped(pos) - if pos.finished(): - return None - if self.factory.detecttype(FormulaNumber, pos): - return self.parsenumbers(pos, remaining) - return self.parseparameter(pos) - - def parsenumbers(self, pos, remaining): - "Parse the remaining parameters as a running number." - "For example, 12 would be {1}{2}." - number = self.factory.parsetype(FormulaNumber, pos) - if not len(number.original) == remaining: - return number - for digit in number.original: - value = self.factory.create(FormulaNumber) - value.add(FormulaConstant(digit)) - value.type = number - self.values.append(value) - return None - - def completemacro(self, macro): - "Complete the macro with the parameters read." - self.contents = [macro.instantiate()] - replaced = [False] * len(self.values) - for parameter in self.searchall(MacroParameter): - index = parameter.number - 1 - if index >= len(self.values): - Trace.error('Macro parameter index out of bounds: ' + unicode(index)) - return - replaced[index] = True - parameter.contents = [self.values[index].clone()] - for index in range(len(self.values)): - if not replaced[index]: - self.addfilter(index, self.values[index]) - - def addfilter(self, index, value): - "Add a filter for the given parameter number and parameter value." - original = '#' + unicode(index + 1) - value = ''.join(self.values[0].gethtml()) - self.output.addfilter(original, value) - -class FormulaMacro(Formula): - "A math macro defined in an inset." - - def __init__(self): - self.parser = MacroParser() - self.output = EmptyOutput() - - def __unicode__(self): - "Return a printable representation." - return 'Math macro' - - if sys.version_info >= (3, 0): - __str__ = __unicode__ - - -FormulaFactory.types += [ MacroParameter ] - -FormulaCommand.types += [ - MacroFunction, - ] - - - -def math2html(formula): - "Convert some TeX math to HTML." - factory = FormulaFactory() - whole = factory.parseformula(formula) - FormulaProcessor().process(whole) - whole.process() - return ''.join(whole.gethtml()) - -def main(): - "Main function, called if invoked from elyxer.the command line" - args = sys.argv - Options().parseoptions(args) - if len(args) != 1: - Trace.error('Usage: math2html.py escaped_string') - exit() - result = math2html(args[0]) - Trace.message(result) - -if __name__ == '__main__': - main() -