You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
170 lines
5.7 KiB
170 lines
5.7 KiB
# vim: fileencoding=utf-8
|
|
|
|
# Copyright (c) 2006-2019 Andrey Golovigin
|
|
#
|
|
# Permission is hereby granted, free of charge, to any person obtaining
|
|
# a copy of this software and associated documentation files (the
|
|
# "Software"), to deal in the Software without restriction, including
|
|
# without limitation the rights to use, copy, modify, merge, publish,
|
|
# distribute, sublicense, and/or sell copies of the Software, and to
|
|
# permit persons to whom the Software is furnished to do so, subject to
|
|
# the following conditions:
|
|
#
|
|
# The above copyright notice and this permission notice shall be
|
|
# included in all copies or substantial portions of the Software.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
from __future__ import unicode_literals
|
|
|
|
import codecs
|
|
|
|
from pybtex.bibtex.exceptions import BibTeXError
|
|
from pybtex.bibtex.utils import scan_bibtex_string
|
|
from pybtex.database.output import BaseWriter
|
|
|
|
|
|
class Writer(BaseWriter):
|
|
"""Outputs BibTeX markup"""
|
|
|
|
unicode_io = True
|
|
|
|
def quote(self, s):
|
|
r"""
|
|
>>> w = Writer()
|
|
>>> print(w.quote('The World'))
|
|
"The World"
|
|
>>> print(w.quote(r'The \emph{World}'))
|
|
"The \emph{World}"
|
|
>>> print(w.quote(r'The "World"'))
|
|
{The "World"}
|
|
>>> try:
|
|
... print(w.quote(r'The {World'))
|
|
... except BibTeXError as error:
|
|
... print(error)
|
|
String has unmatched braces: The {World
|
|
"""
|
|
|
|
self.check_braces(s)
|
|
if '"' not in s:
|
|
return '"%s"' % s
|
|
else:
|
|
return '{%s}' % s
|
|
|
|
def check_braces(self, s):
|
|
"""
|
|
Raise an exception if the given string has unmatched braces.
|
|
|
|
>>> w = Writer()
|
|
>>> w.check_braces('Cat eats carrots.')
|
|
>>> w.check_braces('Cat eats {carrots}.')
|
|
>>> w.check_braces('Cat eats {carrots{}}.')
|
|
>>> w.check_braces('')
|
|
>>> w.check_braces('end}')
|
|
>>> try:
|
|
... w.check_braces('{')
|
|
... except BibTeXError as error:
|
|
... print(error)
|
|
String has unmatched braces: {
|
|
>>> w.check_braces('{test}}')
|
|
>>> try:
|
|
... w.check_braces('{{test}')
|
|
... except BibTeXError as error:
|
|
... print(error)
|
|
String has unmatched braces: {{test}
|
|
|
|
"""
|
|
|
|
tokens = list(scan_bibtex_string(s))
|
|
if tokens:
|
|
end_brace_level = tokens[-1][1]
|
|
if end_brace_level != 0:
|
|
raise BibTeXError('String has unmatched braces: %s' % s)
|
|
|
|
def _encode(self, text):
|
|
r"""Encode text as LaTeX.
|
|
|
|
>>> w = Writer(encoding='ASCII')
|
|
>>> print(w._encode(u'1970–1971.'))
|
|
1970--1971.
|
|
|
|
>>> w = Writer(encoding='UTF-8')
|
|
>>> print(w._encode(u'1970–1971.'))
|
|
1970–1971.
|
|
|
|
>>> w = Writer(encoding='UTF-8')
|
|
>>> print(w._encode(u'100% noir'))
|
|
100\% noir
|
|
"""
|
|
import latexcodec # NOQA
|
|
|
|
return codecs.encode(text, 'ulatex+{}'.format(self.encoding))
|
|
|
|
def _encode_with_comments(self, text):
|
|
r"""Encode text as LaTeX, preserve comments.
|
|
|
|
>>> w = Writer(encoding='ASCII')
|
|
>>> print(w._encode_with_comments(u'1970–1971. %% † RIP †'))
|
|
1970--1971. %% \dag\ RIP \dag
|
|
|
|
>>> w = Writer(encoding='UTF-8')
|
|
>>> print(w._encode_with_comments(u'1970–1971. %% † RIP †'))
|
|
1970–1971. %% † RIP †
|
|
"""
|
|
return u'%'.join(self._encode(part) for part in text.split(u'%'))
|
|
|
|
def _write_field(self, stream, type, value):
|
|
stream.write(u',\n %s = %s' % (type, self.quote(self._encode(value))))
|
|
|
|
def _format_name(self, stream, person):
|
|
def join(l):
|
|
return ' '.join([name for name in l if name])
|
|
first = person.get_part_as_text('first')
|
|
middle = person.get_part_as_text('middle')
|
|
prelast = person.get_part_as_text('prelast')
|
|
last = person.get_part_as_text('last')
|
|
lineage = person.get_part_as_text('lineage')
|
|
s = ''
|
|
if last:
|
|
s += join([prelast, last])
|
|
if lineage:
|
|
s += ', %s' % lineage
|
|
if first or middle:
|
|
s += ', '
|
|
s += join([first, middle])
|
|
return s
|
|
|
|
def _write_persons(self, stream, persons, role):
|
|
# persons = getattr(entry, role + 's')
|
|
if persons:
|
|
names = u' and '.join(self._format_name(stream, person) for person in persons)
|
|
self._write_field(stream, role, names)
|
|
|
|
def _write_preamble(self, stream, preamble):
|
|
if preamble:
|
|
stream.write(u'@preamble{%s}\n\n' % self.quote(self._encode_with_comments(preamble)))
|
|
|
|
def write_stream(self, bib_data, stream):
|
|
|
|
self._write_preamble(stream, bib_data.preamble)
|
|
|
|
first = True
|
|
for key, entry in bib_data.entries.items():
|
|
if not first:
|
|
stream.write(u'\n')
|
|
first = False
|
|
|
|
stream.write(u'@%s' % entry.original_type)
|
|
stream.write(u'{%s' % key)
|
|
# for role in ('author', 'editor'):
|
|
for role, persons in entry.persons.items():
|
|
self._write_persons(stream, persons, role)
|
|
for type, value in entry.fields.items():
|
|
self._write_field(stream, type, value)
|
|
stream.write(u'\n}\n')
|
|
|