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')
 | 
						|
 |