diff --git a/ietf/templates/doc/document_bibtex.bib b/ietf/templates/doc/document_bibtex.bib index 0a5895f99..19d3bfebf 100644 --- a/ietf/templates/doc/document_bibtex.bib +++ b/ietf/templates/doc/document_bibtex.bib @@ -1,6 +1,7 @@ {% spaceless %} - +{% autoescape off %} {% load ietf_filters %} +{% load textfilters %} {% if doc.get_state_slug == "rfc" %} {% if doc.stream|slugify == "legacy" %} @@ -24,13 +25,14 @@ publisher = {% templatetag openbrace %}Internet Engineering Task Force{% templatetag closebrace %}, note = {% templatetag openbrace %}Work in Progress{% templatetag closebrace %}, url = {% templatetag openbrace %}https://datatracker.ietf.org/doc/html/{{doc.name}}-{{doc.rev}}{% templatetag closebrace %},{% endif %} - author = {% templatetag openbrace %}{% for author in doc.documentauthor_set.all %}{{ author.person.name}}{% if not forloop.last %} and {% endif %}{% endfor %}{% templatetag closebrace %}, - title = {% templatetag openbrace %}{% templatetag openbrace %}{{doc.title}}{% templatetag closebrace %}{% templatetag closebrace %}, + author = {% templatetag openbrace %}{% for author in doc.documentauthor_set.all %}{{ author.person.name|texescape}}{% if not forloop.last %} and {% endif %}{% endfor %}{% templatetag closebrace %}, + title = {% templatetag openbrace %}{% templatetag openbrace %}{{doc.title|texescape}}{% templatetag closebrace %}{% templatetag closebrace %}, pagetotal = {{ doc.pages }}, year = {{ doc.pub_date.year }}, month = {{ doc.pub_date|date:"b" }},{% if not doc.rfc_number or doc.pub_date.day == 1 and doc.pub_date.month == 4 %} day = {{ doc.pub_date.day }},{% endif %} - abstract = {% templatetag openbrace %}{{ doc.abstract|clean_whitespace }}{% templatetag closebrace %}, + abstract = {% templatetag openbrace %}{{ doc.abstract|clean_whitespace|texescape }}{% templatetag closebrace %}, {% templatetag closebrace %} +{% endautoescape %} {% endspaceless %} diff --git a/ietf/utils/templatetags/textfilters.py b/ietf/utils/templatetags/textfilters.py index 1ee5e20c4..0a065bc6e 100644 --- a/ietf/utils/templatetags/textfilters.py +++ b/ietf/utils/templatetags/textfilters.py @@ -1,11 +1,13 @@ from __future__ import unicode_literals -from django.template.library import Library +from django import template from django.template.defaultfilters import stringfilter -from ietf.utils.text import xslugify as _xslugify +import debug # pyflakes:ignore -register = Library() +from ietf.utils.text import xslugify as _xslugify, texescape + +register = template.Library() @register.filter(is_safe=True) @stringfilter @@ -26,3 +28,40 @@ def format(format, values): tmp[f.name] = getattr(values, f.name) values = tmp return format.format(**values) + +# ---------------------------------------------------------------------- + +# from django.utils.safestring import mark_safe +# class TeXEscapeNode(template.Node): +# """TeX escaping, rather than html escaping. +# +# Mostly, this tag is _not_ the right thing to use in a template that produces TeX +# markup, as it will escape all the markup characters, which is not what you want. +# Use the '|texescape' filter instead on text fragments where escaping is needed +# """ +# def __init__(self, nodelist): +# self.nodelist = nodelist +# +# def render(self, context): +# saved_autoescape = context.autoescape +# context.autoescape = False +# text = self.nodelist.render(context) +# text = texescape(text) +# context.autoescape = saved_autoescape +# return mark_safe(text) +# +# @register.tag('texescape') +# def do_texescape(parser, token): +# args = token.contents.split() +# if len(args) != 1: +# raise TemplateSyntaxError("'texescape' tag takes no arguments.") +# nodelist = parser.parse(('endtexescape',)) +# parser.delete_first_token() +# return TeXEscapeNode(nodelist) + +@register.filter('texescape') +@stringfilter +def texescape_filter(value): + "A TeX escape filter" + return texescape(value) + diff --git a/ietf/utils/texescape.py b/ietf/utils/texescape.py new file mode 100644 index 000000000..0c73fea96 --- /dev/null +++ b/ietf/utils/texescape.py @@ -0,0 +1,136 @@ +# -*- coding: utf-8 -*- +# Copied from https://github.com/sphinx-doc/sphinx/blob/master/sphinx/util/texescape.py +# Copyright and license as indicated in the original and below +""" + sphinx.util.texescape + ~~~~~~~~~~~~~~~~~~~~~ + + TeX escaping helper. + + :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from __future__ import unicode_literals + +tex_replacements = [ + # map TeX special chars + ('$', r'\$'), + ('%', r'\%'), + ('&', r'\&'), + ('#', r'\#'), + ('_', r'\_'), + ('{', r'\{'), + ('}', r'\}'), + ('[', r'{[}'), + (']', r'{]}'), + ('`', r'{}`'), + ('\\', r'\textbackslash{}'), + ('~', r'\textasciitilde{}'), + ('<', r'\textless{}'), + ('>', r'\textgreater{}'), + ('^', r'\textasciicircum{}'), + # map special Unicode characters to TeX commands + ('¶', r'\P{}'), + ('§', r'\S{}'), + ('€', r'\texteuro{}'), + ('∞', r'\(\infty\)'), + ('±', r'\(\pm\)'), + ('→', r'\(\rightarrow\)'), + ('‣', r'\(\rightarrow\)'), + ('✓', r'\(\checkmark\)'), + # used to separate -- in options + ('', r'{}'), + # map some special Unicode characters to similar ASCII ones + ('⎽', r'\_'), + ('–', r'\textendash{}'), + ('|', r'\textbar{}'), + ('ℯ', r'e'), + ('ⅈ', r'i'), + ('⁰', r'\(\sp{\text{0}}\)'), + ('¹', r'\(\sp{\text{1}}\)'), + ('²', r'\(\sp{\text{2}}\)'), + ('³', r'\(\sp{\text{3}}\)'), + ('⁴', r'\(\sp{\text{4}}\)'), + ('⁵', r'\(\sp{\text{5}}\)'), + ('⁶', r'\(\sp{\text{6}}\)'), + ('⁷', r'\(\sp{\text{7}}\)'), + ('⁸', r'\(\sp{\text{8}}\)'), + ('⁹', r'\(\sp{\text{9}}\)'), + ('₀', r'\(\sb{\text{0}}\)'), + ('₁', r'\(\sb{\text{1}}\)'), + ('₂', r'\(\sb{\text{2}}\)'), + ('₃', r'\(\sb{\text{3}}\)'), + ('₄', r'\(\sb{\text{4}}\)'), + ('₅', r'\(\sb{\text{5}}\)'), + ('₆', r'\(\sb{\text{6}}\)'), + ('₇', r'\(\sb{\text{7}}\)'), + ('₈', r'\(\sb{\text{8}}\)'), + ('₉', r'\(\sb{\text{9}}\)'), + # map Greek alphabet + ('α', r'\(\alpha\)'), + ('β', r'\(\beta\)'), + ('γ', r'\(\gamma\)'), + ('δ', r'\(\delta\)'), + ('ε', r'\(\epsilon\)'), + ('ζ', r'\(\zeta\)'), + ('η', r'\(\eta\)'), + ('θ', r'\(\theta\)'), + ('ι', r'\(\iota\)'), + ('κ', r'\(\kappa\)'), + ('λ', r'\(\lambda\)'), + ('μ', r'\(\mu\)'), + ('ν', r'\(\nu\)'), + ('ξ', r'\(\xi\)'), + ('ο', r'o'), + ('π', r'\(\pi\)'), + ('ρ', r'\(\rho\)'), + ('σ', r'\(\sigma\)'), + ('τ', r'\(\tau\)'), + ('υ', '\\(\\upsilon\\)'), + ('φ', r'\(\phi\)'), + ('χ', r'\(\chi\)'), + ('ψ', r'\(\psi\)'), + ('ω', r'\(\omega\)'), + ('Α', r'A'), + ('Β', r'B'), + ('Γ', r'\(\Gamma\)'), + ('Δ', r'\(\Delta\)'), + ('Ε', r'E'), + ('Ζ', r'Z'), + ('Η', r'H'), + ('Θ', r'\(\Theta\)'), + ('Ι', r'I'), + ('Κ', r'K'), + ('Λ', r'\(\Lambda\)'), + ('Μ', r'M'), + ('Ν', r'N'), + ('Ξ', r'\(\Xi\)'), + ('Ο', r'O'), + ('Π', r'\(\Pi\)'), + ('Ρ', r'P'), + ('Σ', r'\(\Sigma\)'), + ('Τ', r'T'), + ('Υ', '\\(\\Upsilon\\)'), + ('Φ', r'\(\Phi\)'), + ('Χ', r'X'), + ('Ψ', r'\(\Psi\)'), + ('Ω', r'\(\Omega\)'), + ('Ω', r'\(\Omega\)'), +] + +tex_escape_map = {} +tex_replace_map = {} +tex_hl_escape_map_new = {} + + +def init(): + # type: () -> None + for a, b in tex_replacements: + tex_escape_map[ord(a)] = b + tex_replace_map[ord(a)] = '_' + + for a, b in tex_replacements: + if a in '[]{}\\': + continue + tex_hl_escape_map_new[ord(a)] = b diff --git a/ietf/utils/text.py b/ietf/utils/text.py index 60f08272e..9993fe0f4 100644 --- a/ietf/utils/text.py +++ b/ietf/utils/text.py @@ -11,6 +11,8 @@ from django.utils.safestring import mark_safe import debug # pyflakes:ignore +from texescape import init as texescape_init, tex_escape_map + @keep_lazy(six.text_type) def xslugify(value): """ @@ -174,3 +176,9 @@ def dict_to_text(d): for k, v in d.items(): t += "%s: %s\n" % (k, v) return t + +def texescape(s): + if not tex_escape_map: + texescape_init() + t = s.translate(tex_escape_map) + return t