summaryrefslogtreecommitdiff
path: root/support/gladtex/tests
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /support/gladtex/tests
Initial commit
Diffstat (limited to 'support/gladtex/tests')
-rw-r--r--support/gladtex/tests/test_cachedconverter.py143
-rw-r--r--support/gladtex/tests/test_caching.py149
-rw-r--r--support/gladtex/tests/test_htmlhandling.py404
-rw-r--r--support/gladtex/tests/test_imagecreation.py165
-rw-r--r--support/gladtex/tests/test_typesetting.py218
5 files changed, 1079 insertions, 0 deletions
diff --git a/support/gladtex/tests/test_cachedconverter.py b/support/gladtex/tests/test_cachedconverter.py
new file mode 100644
index 0000000000..b1c3bb786c
--- /dev/null
+++ b/support/gladtex/tests/test_cachedconverter.py
@@ -0,0 +1,143 @@
+#pylint: disable=too-many-public-methods,import-error,too-few-public-methods,missing-docstring,unused-variable
+import os
+import shutil
+import tempfile
+import unittest
+from unittest.mock import patch
+from gleetex import cachedconverter, image
+from gleetex.caching import JsonParserException
+from gleetex.image import remove_all
+
+def get_number_of_files(path):
+ return len(os.listdir(path))
+
+def mk_eqn(eqn, count=0, pos=(1,1)):
+ """Create formula. Each formula must look like this:
+ (eqn, pos, path, dsp, count) for self._convert_concurrently, this is a
+ shorthand with mocking a few values."""
+ return (pos, False, eqn)
+
+def write(path, content='dummy'):
+ with open(path, 'w', encoding='utf-8') as f:
+ f.write(str(content))
+
+class Tex2imgMock():
+ """Could use a proper mock, but this one allows a bit more tricking."""
+ def __init__(self, fmt):
+ self.__format = fmt
+ self.set_dpi = self.set_transparency = self.set_foreground_color \
+ = self.set_background_color = lambda x: None # do nothing
+
+ def create_dvi(self, dvi_fn):
+ with open(dvi_fn, 'w') as f:
+ f.write('dummy')
+
+ def create_image(self, dvi_fn):
+ if os.path.exists(dvi_fn):
+ os.remove(dvi_fn)
+ write(os.path.splitext(dvi_fn)[0] + '.' + self.__format.value)
+
+ def convert(self, tx, basename):
+ write(basename + '.tex', tx)
+ dvi = basename + '.dvi'
+ self.create_dvi(dvi)
+ self.create_image(dvi)
+ remove_all(dvi, basename + '.tex', basename + '.log', basename + '.aux')
+ return {'depth': 9, 'height': 8, 'width': 7}
+
+ def parse_log(self, _logdata):
+ return {}
+
+
+class TestCachedConverter(unittest.TestCase):
+ #pylint: disable=protected-access
+ def setUp(self):
+ self.original_directory = os.getcwd()
+ self.tmpdir = tempfile.mkdtemp()
+ os.chdir(self.tmpdir)
+
+
+ #pylint: disable=protected-access
+ def tearDown(self):
+ # restore static reference to converter
+ cachedconverter.CachedConverter._converter = image.Tex2img
+ os.chdir(self.original_directory)
+ shutil.rmtree(self.tmpdir, ignore_errors=True)
+
+
+
+ @patch('gleetex.image.Tex2img', Tex2imgMock)
+ def test_that_subdirectory_is_created(self):
+ c = cachedconverter.CachedConverter('subdirectory')
+ formula = ({}, True, '\\textbf{FOO!}')
+ c.convert_all([formula])
+ # one directory exists
+ self.assertEqual(get_number_of_files('.'), 1,
+ "Found the following files, expected only 'subdirectory': " + \
+ ', '.join(os.listdir('.')))
+ # subdirectory contains 1 image and a cache
+ self.assertEqual(get_number_of_files('subdirectory'), 2, "expected two"+\
+ " files, found instead " + repr(os.listdir('subdirectory')))
+
+ def test_that_unknown_options_trigger_exception(self):
+ c = cachedconverter.CachedConverter('subdirectory')
+ self.assertRaises(ValueError, c.set_option, 'cxzbiucxzbiuxzb', 'muh')
+
+ def test_that_invalid_caches_trigger_error_by_default(self):
+ with open('gladtex.cache', 'w') as f:
+ f.write('invalid cache')
+ with self.assertRaises(JsonParserException):
+ c = cachedconverter.CachedConverter('')
+
+ @patch('gleetex.image.Tex2img', Tex2imgMock)
+ def test_that_invalid_caches_get_removed_if_specified(self):
+ formulas = [mk_eqn('tau')]
+ with open('gladtex.cache', 'w') as f:
+ f.write('invalid cache')
+ c = cachedconverter.CachedConverter('.', keep_old_cache=False)
+ c.convert_all(formulas)
+ # cache got overridden
+ with open('gladtex.cache') as f:
+ self.assertFalse('invalid' in f.read())
+
+ @patch('gleetex.image.Tex2img', Tex2imgMock)
+ def test_that_converted_formulas_are_cached(self):
+ formulas = [mk_eqn('\\tau')]
+ c = cachedconverter.CachedConverter('.')
+ c.convert_all(formulas)
+ self.assertTrue(c.get_data_for('\\tau', False))
+
+
+ @patch('gleetex.image.Tex2img', Tex2imgMock)
+ def test_that_file_names_are_correctly_picked(self):
+ formulas = [mk_eqn('\\tau')]
+ write('eqn000.svg')
+ write('eqn001.svg')
+ c = cachedconverter.CachedConverter('')
+ to_convert = c._get_formulas_to_convert(formulas)
+ self.assertTrue(len(to_convert), 1)
+ self.assertEqual(to_convert[0][2], 'eqn002.svg')
+
+ @patch('gleetex.image.Tex2img', Tex2imgMock)
+ def test_that_all_converted_formulas_are_in_cache_and_meta_info_correct(self):
+ formulas = [mk_eqn('a_{%d}' % i, pos=(i,i), count=i) for i in range(4)]
+ c = cachedconverter.CachedConverter('.')
+ c.convert_all(formulas)
+ # expect all formulas and a gladtex cache to exist
+ self.assertEqual(get_number_of_files('.'), len(formulas)+1,
+ "present files:\n" + ', '.join(os.listdir('.')))
+ for pos, dsp, formula in formulas:
+ data = c.get_data_for(formula, False)
+ self.assertEqual(data['pos'], {'depth': 9, 'height': 8, 'width': 7},
+ "expected the pos as defined in the dummy class")
+
+ @patch('gleetex.image.Tex2img', Tex2imgMock)
+ def test_that_inline_math_and_display_math_results_in_different_formulas(self):
+ # two formulas, second is displaymath
+ formula = r'\sum_{i=0}^n x_i'
+ formulas = [((1,1), False, formula), ((3,1), True, formula)]
+ c = cachedconverter.CachedConverter('.')
+ c.convert_all(formulas)
+ # expect all formulas and a gladtex cache to exist
+ self.assertEqual(get_number_of_files('.'), len(formulas)+1,
+ "present files:\n%s" % ', '.join(os.listdir('.')))
diff --git a/support/gladtex/tests/test_caching.py b/support/gladtex/tests/test_caching.py
new file mode 100644
index 0000000000..dd3aebac77
--- /dev/null
+++ b/support/gladtex/tests/test_caching.py
@@ -0,0 +1,149 @@
+#pylint: disable=too-many-public-methods,import-error,too-few-public-methods,missing-docstring,unused-variable
+import os
+import shutil
+import tempfile
+import unittest
+from gleetex import caching
+
+def write(path, content):
+ with open(path, 'w', encoding='utf-8') as f:
+ f.write(content)
+
+class test_caching(unittest.TestCase):
+ def setUp(self):
+ self.pos = {'height' : 8, 'depth' : 2, 'width' : 666}
+ self.original_directory = os.getcwd()
+ self.tmpdir = tempfile.mkdtemp()
+ os.chdir(self.tmpdir)
+
+ def tearDown(self):
+ os.chdir(self.original_directory)
+ shutil.rmtree(self.tmpdir, ignore_errors=True)
+
+ def test_differently_spaced_formulas_are_the_same(self):
+ form1 = r'\tau \pi'
+ form2 = '\tau\\pi'
+ self.assertTrue(caching.normalize_formula(form1),
+ caching.normalize_formula(form2))
+
+ def test_trailing_and_leading_spaces_and_tabs_are_no_problem(self):
+ u = caching.normalize_formula
+ form1 = ' hi'
+ form2 = 'hi '
+ form3 = '\thi'
+ self.assertEqual(u(form1), u(form2))
+ self.assertEqual(u(form1), u(form3))
+
+ def test_that_empty_braces_are_ignored(self):
+ u = caching.normalize_formula
+ form1 = r'\sin{}x'
+ form2 = r'\sin x'
+ form3 = r'\sin{} x'
+ self.assertEqual(u(form1), u(form2))
+ self.assertEqual(u(form1), u(form3))
+ self.assertEqual(u(form2), u(form3))
+
+ def test_empty_cache_works_fine(self):
+ write('foo.png', 'muha')
+ c = caching.ImageCache('file.png')
+ formula = r"f(x) = \ln(x)"
+ c.add_formula(formula, self.pos, 'foo.png')
+ self.assertTrue(c.contains(formula, False))
+
+ def test_that_invalid_cach_entries_are_detected(self):
+ # entry is invalid if file doesn't exist
+ c = caching.ImageCache()
+ formula = r"f(x) = \ln(x)"
+ self.assertRaises(OSError, c.add_formula, formula, self.pos, 'file.png')
+
+ def test_that_correct_pos_and_path_are_returned_after_writing_the_cache_back(self):
+ c = caching.ImageCache()
+ formula = r"g(x) = \ln(x)"
+ write('file.png', 'dummy')
+ c.add_formula(formula, self.pos, 'file.png', displaymath=False)
+ c.write()
+ c = caching.ImageCache()
+ self.assertTrue(c.contains(formula, False))
+ data = c.get_data_for(formula, False)
+ self.assertEqual(data['pos'], self.pos)
+ self.assertEqual(data['path'], 'file.png')
+
+
+ def test_formulas_are_not_added_twice(self):
+ form1 = r'\ln(x) \neq e^x'
+ write('spass.png', 'binaryBinary_binary')
+ c = caching.ImageCache()
+ for i in range(1,10):
+ c.add_formula(form1, self.pos, 'spass.png')
+ self.assertEqual(len(c), 1)
+
+ def test_that_remove_actually_removes(self):
+ form1 = '\\int e^x dy'
+ write('happyness.png', 'binaryBinary_binary')
+ c = caching.ImageCache()
+ c.add_formula(form1, self.pos, 'happyness.png')
+ c.remove_formula(form1, False)
+ self.assertEqual(len(c), 0)
+
+ def test_removal_of_non_existing_formula_raises_exception(self):
+ c = caching.ImageCache()
+ self.assertRaises(KeyError, c.remove_formula, 'Haha!', False)
+
+ def test_that_invalid_version_is_detected(self):
+ c = caching.ImageCache('gladtex.cache')
+ c._ImageCache__set_version('invalid.stuff')
+ c.write()
+ self.assertRaises(caching.JsonParserException, caching.ImageCache, 'gladtex.cache')
+
+ def test_that_invalid_style_is_detected(self):
+ write('foo.png', "dummy")
+ c = caching.ImageCache('gladtex.cache')
+ c.add_formula('\\tau', self.pos, 'foo.png', False)
+ c.add_formula('\\theta', self.pos, 'foo.png', True)
+ self.assertRaises(ValueError, c.add_formula, '\\gamma', self.pos, 'foo.png',
+ 'some stuff')
+
+ def test_that_backslash_in_path_is_replaced_through_slash(self):
+ c = caching.ImageCache('gladtex.cache')
+ os.mkdir('bilder')
+ write(os.path.join('bilder', 'foo.png'), str(0xdeadbeef))
+ c.add_formula('\\tau', self.pos, 'bilder\\foo.png', False)
+ self.assertTrue('/' in c.get_data_for('\\tau', False)['path'])
+
+ def test_that_absolute_paths_trigger_OSError(self):
+ c = caching.ImageCache('gladtex.cache')
+ write('foo.png', "dummy")
+ fn = os.path.abspath('foo.png')
+ self.assertRaises(OSError, c.add_formula, '\\tau', self.pos,
+ fn, False)
+
+ def test_that_invalid_caches_are_removed_automatically_if_desired(self):
+ file_was_removed = lambda x: self.assertFalse(os.path.exists(x),
+ "expected that file %s was removed, but it still exists" % x)
+ write('gladtex.cache', 'some non-json rubbish')
+ c = caching.ImageCache('gladtex.cache', keep_old_cache=False)
+ file_was_removed('gladtex.cache')
+ # try the same in a subdirectory
+ os.mkdir('foo')
+ cache_path = os.path.join('foo', 'gladtex.cache')
+ eqn1_path = os.path.join('foo', 'eqn000.png')
+ eqn2_path = os.path.join('foo', 'eqn003.png')
+ write(cache_path, 'some non-json rubbish')
+ write(eqn1_path, 'binary')
+ write(eqn2_path, 'more binary')
+ c = caching.ImageCache(cache_path, keep_old_cache=False)
+ file_was_removed(cache_path)
+ file_was_removed(eqn1_path)
+ file_was_removed(eqn2_path)
+
+ def test_that_formulas_in_cache_with_no_file_raise_key_error(self):
+ c = caching.ImageCache('gladtex.cache', keep_old_cache=False)
+ write('foo.png', 'dummy')
+ c.add_formula('\\tau', self.pos, 'foo.png')
+ c.write()
+ os.remove('foo.png')
+ c = caching.ImageCache('gladtex.cache', keep_old_cache=False)
+ with self.assertRaises(KeyError):
+ c.get_data_for('foo.png', 'False')
+
+
diff --git a/support/gladtex/tests/test_htmlhandling.py b/support/gladtex/tests/test_htmlhandling.py
new file mode 100644
index 0000000000..359ef36965
--- /dev/null
+++ b/support/gladtex/tests/test_htmlhandling.py
@@ -0,0 +1,404 @@
+#pylint: disable=too-many-public-methods
+from functools import reduce
+import os, re, shutil, tempfile
+import unittest
+from gleetex import htmlhandling
+
+
+excl_filename = htmlhandling.HtmlImageFormatter.EXCLUSION_FILE_NAME
+
+HTML_SKELETON = '''<html><head><meta http-equiv="Content-Type" content="text/html; charset={0}" />
+</head><body>{1}</body>'''
+
+def read(file_name, mode='r', encoding='utf-8'):
+ """Read the file, return the string. Close file properly."""
+ with open(file_name, mode, encoding=encoding) as handle:
+ return handle.read()
+
+
+class HtmlparserTest(unittest.TestCase):
+ def setUp(self):
+ self.p = htmlhandling.EqnParser()
+
+ def test_start_tags_are_parsed_literally(self):
+ self.p.feed("<p i='o'>")
+ self.assertEqual(self.p.get_data()[0], "<p i='o'>",
+ "The HTML parser should copy start tags literally.")
+
+ def test_that_end_tags_are_copied_literally(self):
+ self.p.feed("</ p></P>")
+ self.assertEqual(''.join(self.p.get_data()), "</ p></P>")
+
+ def test_entities_are_unchanged(self):
+ self.p.feed("&#xa;")
+ self.assertEqual(self.p.get_data()[0], '&#xa;')
+
+ def test_charsets_are_copied(self):
+ self.p.feed('&gt;&rarr;')
+ self.assertEqual(''.join(self.p.get_data()[0]), '&gt;&rarr;')
+
+ def test_without_eqn_all_blocks_are_strings(self):
+ self.p.feed("<html>\n<head/><body><p>42</p><h1>blah</h1></body></html>")
+ self.assertTrue(reduce(lambda x,y: x and isinstance(y, str),
+ self.p.get_data()), "all chunks have to be strings")
+
+ def test_equation_is_detected(self):
+ self.p.feed('<eq>foo \\pi</eq>')
+ self.assertTrue(isinstance(self.p.get_data()[0], (tuple, list)))
+ self.assertEqual(self.p.get_data()[0][2], 'foo \\pi')
+
+ def test_tag_followed_by_eqn_is_correctly_recognized(self):
+ self.p.feed('<p foo="bar"><eq>bar</eq>')
+ self.assertEqual(self.p.get_data()[0], '<p foo="bar">')
+ self.assertTrue(isinstance(self.p.get_data(), list), "second item of data must be equation data list")
+
+ def test_document_with_tag_then_eqn_then_tag_works(self):
+ self.p.feed('<div style="invalid">bar</div><eq>baz</eq><sometag>')
+ eqn = None
+ # test should not depend on a specific position of equation, search for
+ # it
+ data = self.p.get_data()
+ for chunk in data:
+ if isinstance(chunk, (tuple, list)):
+ eqn = chunk
+ break
+ self.assertTrue(isinstance(data[0], str))
+ self.assertTrue(eqn is not None,
+ "No equation found, must be tuple/list object.")
+ self.assertTrue(isinstance(data[-1], str))
+
+ def test_equation_is_copied_literally(self):
+ self.p.feed('<eq ignore="me">my\nlittle\n\\tau</eq>')
+ self.assertEqual(self.p.get_data()[0][2], 'my\nlittle\n\\tau')
+
+ def test_unclosed_eqns_are_detected(self):
+ self.assertRaises(htmlhandling.ParseException, self.p.feed,
+ '<p><eq>\\endless\\formula')
+
+ def test_nested_formulas_trigger_exception(self):
+ self.assertRaises(htmlhandling.ParseException, self.p.feed,
+ "<eq>\\pi<eq></eq></eq>")
+ self.assertRaises(htmlhandling.ParseException, self.p.feed,
+ "<eq>\\pi<eq></p></eq>")
+
+ def test_formulas_without_displaymath_attribute_are_detected(self):
+ self.p.feed('<p><eq>\frac12</eq><br /><eq env="inline">bar</eq></p>')
+ formulas = [c for c in self.p.get_data() if isinstance(c, (tuple, list))]
+ self.assertEqual(len(formulas), 2) # there should be _2_ formulas
+ self.assertEqual(formulas[0][1], False) # no displaymath
+ self.assertEqual(formulas[1][1], False) # no displaymath
+
+ def test_that_unclosed_formulas_detected(self):
+ self.assertRaises(htmlhandling.ParseException, self.p.feed,
+ "<eq>\\pi<eq></p>")
+ self.assertRaises(htmlhandling.ParseException, self.p.feed,
+ "<eq>\\pi")
+
+
+ def test_formula_contains_only_formula(self):
+ p = htmlhandling.EqnParser()
+ p.feed("<p><eq>1<i<9</eq></p>")
+ formula = next(e for e in p.get_data() if isinstance(e, (list, tuple)))
+ self.assertEqual(formula[-1], "1<i<9")
+
+ p = htmlhandling.EqnParser()
+ p.feed('<p><eq env="displaymath">test</eq></p>')
+ formula = next(e for e in p.get_data() if isinstance(e, (list, tuple)))
+ self.assertEqual(formula[-1], "test")
+
+ p = htmlhandling.EqnParser()
+ p.feed("<p><eq>1<i<9</eq></p>")
+ formula = next(e for e in p.get_data() if isinstance(e, (list, tuple)))
+ self.assertEqual(formula[-1], "1<i<9")
+
+
+ def test_formula_with_html_sequences_are_unescaped(self):
+ self.p.feed('<eq>a&gt;b</eq>')
+ formula = self.p.get_data()[0]
+ self.assertEqual(formula[-1], "a>b")
+
+
+ def test_displaymath_is_recognized(self):
+ self.p.feed('<eq env="displaymath">\\sum\limits_{n=1}^{e^i} a^nl^n</eq>')
+ self.assertEqual(self.p.get_data()[0][1], True) # displaymath flag set
+
+ def test_encoding_is_parsed_from_HTML4(self):
+ iso8859_1 = HTML_SKELETON.format('iso-8859-15', 'öäüß').encode('iso-8859-1')
+ self.p.feed(iso8859_1)
+ self.assertEqual(self.p._EqnParser__encoding, 'iso-8859-15')
+
+ def test_encoding_is_parsed_from_HTML5(self):
+ document = r"""<!DOCTYPE html>
+ <html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
+ <head><meta charset="utf-8" />
+ <meta name="generator" content="pandoc" />
+ </head><body><p>hi</p></body></html>"""
+ self.p.feed(document.encode('utf-8'))
+ self.assertEqual(self.p._EqnParser__encoding.lower(), 'utf-8')
+
+
+ def test_strings_can_be_passed_tO_parser_as_well(self):
+ # no exception - everything is working as expected
+ self.p.feed(HTML_SKELETON.format('utf-8', 'æø'))
+
+class GetPositionTest(unittest.TestCase):
+ def test_that_line_number_is_correct(self):
+ self.assertEqual(htmlhandling.get_position('jojo', 0)[0], 0)
+ self.assertEqual(htmlhandling.get_position('jojo', 3)[0], 0)
+ self.assertEqual(htmlhandling.get_position('a\njojo', 3)[0], 1)
+ self.assertEqual(htmlhandling.get_position('a\n\njojo', 3)[0], 2)
+
+ def test_that_position_on_line_is_correct(self):
+ self.assertEqual(htmlhandling.get_position('jojo', 0)[1], 0)
+ self.assertEqual(htmlhandling.get_position('jojo', 3)[1], 3)
+ self.assertEqual(htmlhandling.get_position('a\njojo', 3)[1], 2)
+ self.assertEqual(htmlhandling.get_position('a\n\njojo', 3)[1], 1)
+
+
+
+
+class HtmlImageTest(unittest.TestCase):
+ def setUp(self):
+ self.pos = {'depth':99, 'height' : 88, 'width' : 77}
+ self.original_directory = os.getcwd()
+ self.tmpdir = tempfile.mkdtemp()
+ os.chdir(self.tmpdir)
+
+ def tearDown(self):
+ os.chdir(self.original_directory)
+ shutil.rmtree(self.tmpdir, ignore_errors=True)
+
+ def test_that_no_file_is_written_if_no_content(self):
+ with htmlhandling.HtmlImageFormatter('foo.html'):
+ pass
+ self.assertFalse(os.path.exists('foo.html') )
+
+ def test_file_if_written_when_content_exists(self):
+ with htmlhandling.HtmlImageFormatter() as img:
+ img.format_excluded(self.pos, '\\tau\\tau', 'foo.png')
+ self.assertTrue(os.path.exists(excl_filename) )
+
+ def test_written_file_starts_and_ends_more_or_less_properly(self):
+ with htmlhandling.HtmlImageFormatter('.') as img:
+ img.format_excluded(self.pos, '\\tau\\tau', 'foo.png')
+ data = read(htmlhandling.HtmlImageFormatter.EXCLUSION_FILE_NAME, 'r', encoding='utf-8')
+ self.assertTrue('<html' in data and '</html>' in data)
+ self.assertTrue('<body' in data and '</body>' in data)
+ # make sure encoding is specified
+ self.assertTrue('<meta' in data and 'charset=' in data)
+
+ def test_id_contains_no_special_characters(self):
+ data = htmlhandling.gen_id('\\tau!\'{}][~^')
+ for character in {'!', "'", '\\', '{', '}'}:
+ self.assertFalse(character in data)
+
+ def test_formula_can_consist_only_of_numbers_and_id_is_generated(self):
+ data = htmlhandling.gen_id('9*8*7=504')
+ self.assertTrue(data.startswith('form'))
+ self.assertTrue(data.endswith('504'))
+
+ def test_that_empty_ids_raise_exception(self):
+ self.assertRaises(ValueError, htmlhandling.gen_id, '')
+
+ def test_that_same_characters_are_not_repeated(self):
+ id = htmlhandling.gen_id("jo{{{{{{{{ha")
+ self.assertEqual(id, "jo_ha")
+
+ def test_that_ids_are_max_150_characters_wide(self):
+ id = htmlhandling.gen_id('\\alpha\\cdot\\gamma + ' * 999)
+ self.assertTrue(len(id) == 150)
+
+ def test_that_ids_start_with_letter(self):
+ id = htmlhandling.gen_id('{}\\[]ÖÖÖö9343...·tau')
+ self.assertTrue(id[0].isalpha())
+
+ def test_that_link_to_external_image_points_to_file_and_formula(self):
+ with htmlhandling.HtmlImageFormatter() as img:
+ formatted_img = img.format_excluded(self.pos, '\\tau\\tau', 'foo.png')
+ expected_id = htmlhandling.gen_id('\\tau\\tau')
+ external_file = read(excl_filename, 'r', encoding='utf-8')
+ # find linked formula path
+ href = re.search('href="(.*?)"', formatted_img)
+ self.assertTrue(href != None)
+ # extract path and id from it
+ self.assertTrue('#' in href.groups()[0])
+ path, id = href.groups()[0].split('#')
+ self.assertEqual(path, excl_filename)
+ self.assertEqual(id, expected_id)
+
+ # check external file
+ self.assertTrue('<p id' in external_file)
+ self.assertTrue('="'+expected_id in external_file)
+
+
+ def test_that_link_to_external_image_points_to_file_basepath_and_formula(self):
+ os.mkdir('basepath')
+ with htmlhandling.HtmlImageFormatter('basepath') as img:
+ formatted_img = img.format_excluded(self.pos, '\\tau\\tau', 'foo.png')
+ expected_id = htmlhandling.gen_id('\\tau\\tau')
+ # find linked formula path
+ href = re.search('href="(.*?)"', formatted_img)
+ self.assertTrue(href != None)
+ # extract path and id from it
+ self.assertTrue('#' in href.groups()[0])
+ path, id = href.groups()[0].split('#')
+ self.assertEqual(path, 'basepath/' + excl_filename)
+ self.assertEqual(id, expected_id)
+
+ def test_height_and_width_is_in_formatted_html_img_tag(self):
+ data = None
+ with htmlhandling.HtmlImageFormatter('foo.html') as img:
+ data = img.get_html_img(self.pos, '\\tau\\tau', 'foo.png')
+ self.assertTrue('height=' in data and str(self.pos['height']) in data)
+ self.assertTrue('width=' in data and str(self.pos['width']) in data)
+
+ def test_no_formula_gets_lost_when_reparsing_external_formula_file(self):
+ with htmlhandling.HtmlImageFormatter() as img:
+ img.format_excluded(self.pos, '\\tau' * 999, 'foo.png')
+ with htmlhandling.HtmlImageFormatter() as img:
+ img.format_excluded(self.pos, '\\pi' * 666, 'foo_2.png')
+ data = read(excl_filename)
+ self.assertTrue('\\tau' in data)
+ self.assertTrue('\\pi' in data)
+
+ def test_too_long_formulas_are_not_outsourced_if_not_configured(self):
+ with htmlhandling.HtmlImageFormatter('foo.html') as img:
+ img.format(self.pos, '\\tau' * 999, 'foo.png')
+ self.assertFalse(os.path.exists('foo.html'))
+
+ def test_that_too_long_formulas_get_outsourced_if_configured(self):
+ with htmlhandling.HtmlImageFormatter() as img:
+ img.set_max_formula_length(90)
+ img.set_exclude_long_formulas(True)
+ img.format(self.pos, '\\tau' * 999, 'foo.png')
+ self.assertTrue(os.path.exists(excl_filename))
+ data = read(htmlhandling.HtmlImageFormatter.EXCLUSION_FILE_NAME)
+ self.assertTrue('\\tau\\tau' in data)
+
+ def test_url_is_included(self):
+ prefix = "http://crustulus.de/blog"
+ with htmlhandling.HtmlImageFormatter('foo.html') as img:
+ img.set_url(prefix)
+ data = img.format(self.pos, '\epsilon<0', 'foo.png')
+ self.assertTrue( prefix in data)
+
+ def test_url_doesnt_contain_double_slashes(self):
+ prefix = "http://crustulus.de/blog/"
+ with htmlhandling.HtmlImageFormatter('foo.html') as img:
+ img.set_url(prefix)
+ data = img.format(self.pos, r'\gamma\text{strahlung}', 'foo.png')
+ self.assertFalse('//' in data.replace('http://','ignore'))
+
+ # depth is used as negative offset, so negative depth should result in
+ # positive offset
+ def test_that_negative_depth_results_in_positive_offset(self):
+ self.pos['depth'] = '-999'
+ with htmlhandling.HtmlImageFormatter('foo.html') as img:
+ data = img.format(self.pos, r'\gamma\text{strahlung}', 'foo.png')
+ self.assertTrue('align: ' + str(self.pos['depth'])[1:] in data)
+
+ def test_that_displaymath_is_set_or_unset(self):
+ with htmlhandling.HtmlImageFormatter('foo.html') as img:
+ data = img.format(self.pos, r'\gamma\text{strahlung}', 'foo.png',
+ True)
+ self.assertTrue('="displaymath' in data)
+ data = img.format(self.pos, r'\gamma\text{strahlung}', 'foo.png',
+ False)
+ self.assertTrue('="inlinemath' in data)
+
+ def test_that_alternative_css_class_is_set_correctly(self):
+ with htmlhandling.HtmlImageFormatter('foo.html') as img:
+ img.set_display_math_css_class('no1')
+ img.set_inline_math_css_class('no2')
+ data = img.format(self.pos, r'\gamma\text{strahlung}', 'foo.png',
+ True)
+ self.assertTrue('="no1"' in data)
+ data = img.format(self.pos, r'\gamma\text{strahlung}', 'foo.png',
+ False)
+ self.assertTrue('="no2' in data)
+
+ def test_that_unicode_is_replaced_if_requested(self):
+ with htmlhandling.HtmlImageFormatter('foo.html') as img:
+ img.set_replace_nonascii(True)
+ data = img.format(self.pos, '←', 'foo.png')
+ self.assertTrue('\\leftarrow' in data,
+ 'expected: "\\leftarrow" to be in "%s"' % data)
+
+ def test_that_unicode_is_kept_if_not_requested_to_replace(self):
+ with htmlhandling.HtmlImageFormatter('foo.html') as img:
+ img.set_replace_nonascii(False)
+ data = img.format(self.pos, '←', 'foo.png')
+ self.assertTrue('←' in data)
+
+ def test_formatting_commands_are_stripped(self):
+ with htmlhandling.HtmlImageFormatter('foo.html') as img:
+ data = img.format(self.pos, 'a\,b\,c\,d', 'foo.png')
+ self.assertTrue('a b c d' in data)
+ data = img.format(self.pos, 'a\,b\;c\ d', 'foo.png')
+ self.assertTrue('a b c d' in data)
+
+ data = img.format(self.pos, '\Big\{foo\Big\}', 'foo.png')
+ self.assertTrue('\{foo' in data and '\}' in data)
+ data = img.format(self.pos, r'\left\{foo\right\}', 'foo.png')
+ self.assertTrue('\{' in data and 'foo' in data and '\}' in data)
+
+
+def htmleqn(formula, hr=True):
+ """Format a formula to appear as if it would have been outsourced into an
+ external file."""
+ return '%s\n<p id="%s"><pre>%s</pre></span></p>\n' % (\
+ ('<hr/>' if hr else ''), htmlhandling.gen_id(formula), formula)
+
+
+class OutsourcingParserTest(unittest.TestCase):
+ def setUp(self):
+ self.html = ('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"' +
+ '\n "http://www.w3.org/TR/html4/strict.dtd">\n<html>\n<head>\n' +
+ '<meta http-equiv="content-type" content="text/html; charset=utf-8"/>' +
+ '<title>Outsourced Formulas</title></head>\n<body>\n<h1>heading</h1>')
+
+ def get_html(self, string):
+ """Create html string with head / tail und put the specified string into
+ it."""
+ return self.html + string + '\n</body>\n</html>'
+
+
+ def test_formulas_are_recognized(self):
+ data = self.get_html(htmleqn('\\tau'))
+ parser = htmlhandling.OutsourcedFormulaParser()
+ parser.feed(data)
+ self.assertEqual(len(parser.get_formulas()), 1)
+
+ def test_formula_doesnt_contain_surrounding_rubbish(self):
+ data = self.get_html(htmleqn('\\gamma'))
+ parser = htmlhandling.OutsourcedFormulaParser()
+ parser.feed(data)
+ self.assertEqual(len(parser.get_formulas()), 1)
+ key = next(iter(parser.get_formulas()))
+ par = parser.get_formulas()[key]
+ self.assertFalse('<h1' in par)
+ self.assertFalse('body>' in par)
+ self.assertFalse('hr' in par)
+
+ def test_that_header_is_parsed_correctly(self):
+ p = htmlhandling.OutsourcedFormulaParser()
+ p.feed(self.get_html(htmleqn('test123', False)))
+ head = p.get_head()
+ self.assertTrue('DOCTYPE' in head)
+ self.assertTrue('<html' in head)
+ self.assertTrue('<title' in head)
+ self.assertTrue('</title' in head)
+ self.assertTrue('</head' in head)
+ self.assertTrue('<meta' in head)
+ self.assertTrue('charset=' in head)
+
+ def test_multiple_formulas_are_recognized_correctly(self):
+ p = htmlhandling.OutsourcedFormulaParser()
+ p.feed(self.get_html(htmleqn('\\tau', False) + '\n' +
+ htmleqn('\\gamma') + '\n' + htmleqn('\\epsilon<0')))
+ forms = p.get_formulas()
+ self.assertEqual(len(forms), 3)
+ self.assertTrue('\\gamma' in forms.values())
+ self.assertTrue('\\gamma' in forms.values())
+ self.assertTrue('\\epsilon<0' in forms.values())
+
diff --git a/support/gladtex/tests/test_imagecreation.py b/support/gladtex/tests/test_imagecreation.py
new file mode 100644
index 0000000000..83f87b22d2
--- /dev/null
+++ b/support/gladtex/tests/test_imagecreation.py
@@ -0,0 +1,165 @@
+#pylint: disable=too-many-public-methods,import-error,too-few-public-methods,missing-docstring,unused-variable
+import os
+import pprint
+import shutil
+import tempfile
+import unittest
+from unittest.mock import patch
+from subprocess import SubprocessError
+
+import gleetex.image as image
+from gleetex.image import Format
+from gleetex.typesetting import LaTeXDocument as doc
+
+LATEX_ERROR_OUTPUT = r"""
+This is pdfTeX, Version 3.14159265-2.6-1.40.17 (TeX Live 2016/Debian) (preloaded format=latex)
+ restricted \write18 enabled.
+entering extended mode
+(./bla.tex
+LaTeX2e <2016/03/31> patch level 3
+Babel <3.9r> and hyphenation patterns for 10 language(s) loaded.
+(/usr/share/texlive/texmf-dist/tex/latex/base/article.cls
+Document Class: article 2014/09/29 v1.4h Standard LaTeX document class
+(/usr/share/texlive/texmf-dist/tex/latex/base/size10.clo)) (./bla.aux)
+! Undefined control sequence.
+<recently read> \foo
+
+l.3 $\foo
+ $
+No pages of output.
+Transcript written on bla.log.
+"""
+
+
+def call_dummy(_lklklklklk, **blah):
+ """Dummy to prohibit subprocess execution."""
+ return str(blah)
+
+
+
+
+
+#pylint: disable=unused-argument
+def latex_error_mock(_cmd, **quark):
+ """Mock an error case."""
+ raise SubprocessError(LATEX_ERROR_OUTPUT)
+
+#pylint: disable=unused-argument
+def dvipng_mock(cmd, **kwargs):
+ """Mock an error case."""
+ fn = None
+ try:
+ fn = next(e for e in cmd if e.endswith('.png'))
+ except StopIteration:
+ try:
+ fn = next(e for e in cmd if e.endswith('.dvi'))
+ except StopIteration:
+ pass
+ if fn:
+ with open(fn, 'w') as f:
+ f.write("test case")
+ return 'This is dvipng 1.14 Copyright 2002-2010 Jan-Ake Larsson\n ' + \
+ 'depth=3 height=9 width=22'
+
+def touch(files):
+ for file in files:
+ dirname = os.path.dirname(file)
+ if dirname and not os.path.exists(dirname):
+ os.makedirs(dirname)
+ with open(file, 'w') as f:
+ f.write('\n')
+
+
+class test_imagecreation(unittest.TestCase):
+ def setUp(self):
+ self.original_directory = os.getcwd()
+ self.tmpdir = tempfile.mkdtemp()
+ os.chdir(self.tmpdir)
+ image.Tex2img.call = call_dummy
+
+ def tearDown(self):
+ os.chdir(self.original_directory)
+ shutil.rmtree(self.tmpdir, ignore_errors=True)
+
+ @patch('gleetex.image.proc_call', latex_error_mock)
+ def test_that_error_of_incorrect_formula_is_parsed_correctly(self):
+ i = image.Tex2img(Format.Png)
+ try:
+ i.create_dvi(doc("\\foo"), 'foo.png')
+ except SubprocessError as e:
+ # expect undefined control sequence in error output
+ self.assertTrue('Undefined' in e.args[0])
+
+ @patch('gleetex.image.proc_call', call_dummy)
+ def test_that_intermediate_files_are_removed_after_successful_run(self):
+ files = ['foo.log', 'foo.aux', 'foo.tex']
+ touch(files)
+ i = image.Tex2img(Format.Png)
+ i.create_dvi(doc("\\frac\\pi\\tau"), 'foo.png')
+ for intermediate_file in files:
+ self.assertFalse(os.path.exists(intermediate_file), "File " +
+ intermediate_file + " should not exist.")
+
+ @patch('gleetex.image.proc_call', latex_error_mock)
+ def test_that_intermediate_files_are_removed_when_exception_is_raised(self):
+ files = ['foo.log', 'foo.aux', 'foo.tex']
+ touch(files)
+ # error case
+ i = image.Tex2img(Format.Png)
+ try:
+ i.convert(doc("\\foo"), 'foo')
+ except SubprocessError as e:
+ for intermediate_file in files:
+ self.assertFalse(os.path.exists(intermediate_file), "File " +
+ intermediate_file + " should not exist.")
+
+ @patch('gleetex.image.proc_call', dvipng_mock)
+ def test_intermediate_files_are_removed(self):
+ files = ['foo.tex', 'foo.log', 'foo.aux', 'foo.dvi']
+ touch(files)
+ i = image.Tex2img(Format.Png)
+ i.convert(doc('\\hat{x}'), 'foo')
+ for intermediate_file in files:
+ self.assertFalse(os.path.exists(intermediate_file))
+
+ @patch('gleetex.image.proc_call', latex_error_mock)
+ def test_intermediate_files_are_removed_when_exception_raised(self):
+ files = ['foo.tex', 'foo.log', 'foo.aux', 'foo.dvi']
+ touch(files)
+ i = image.Tex2img(Format.Png)
+ try:
+ i.convert(doc('\\hat{x}'), 'foo')
+ except SubprocessError:
+ self.assertFalse(os.path.exists('foo.tex'))
+ self.assertFalse(os.path.exists('foo.dvi'))
+ self.assertFalse(os.path.exists('foo.log'))
+ self.assertFalse(os.path.exists('foo.aux'))
+
+
+ @patch('gleetex.image.proc_call', lambda *x, **y: 'This is dvipng 1.14 ' + \
+ 'Copyright 2002-2010 Jan-Ake Larsson\n depth=3 height=9 width=22')
+ def test_that_values_for_positioning_png_are_returned(self):
+ i = image.Tex2img(Format.Png)
+ posdata = i.create_image('foo.dvi')
+ self.assertTrue('height' in posdata)
+ self.assertTrue('width' in posdata)
+
+
+ @patch('gleetex.image.proc_call', dvipng_mock)
+ def test_that_output_file_names_with_paths_are_ok_and_log_is_removed(self):
+ fname = lambda f: os.path.join('bilder', 'farce.' + f)
+ touch([fname('log'), fname('png')])
+ t = image.Tex2img(Format.Png)
+ t.convert(doc(r"\hat{es}\pi\pi\ldots"), fname('')[:-1])
+ self.assertFalse(os.path.exists("farce.log"))
+ self.assertTrue(os.path.exists(fname('png')),
+ "couldn't find file {}, directory structure:\n{}".format(
+ fname('png'), ''.join(pprint.pformat(list(os.walk('.'))))))
+ self.assertFalse(os.path.exists(fname('log')))
+
+
+class TestImageResolutionCorrectlyCalculated(unittest.TestCase):
+ def test_sizes_are_correctly_calculated(self):
+ self.assertEqual(int(image.fontsize2dpi(12)), 115)
+ self.assertEqual(int(image.fontsize2dpi(10)), 96)
+
diff --git a/support/gladtex/tests/test_typesetting.py b/support/gladtex/tests/test_typesetting.py
new file mode 100644
index 0000000000..374fe38511
--- /dev/null
+++ b/support/gladtex/tests/test_typesetting.py
@@ -0,0 +1,218 @@
+#pylint: disable=too-many-public-methods,import-error,too-few-public-methods,missing-docstring,unused-variable
+import unittest
+from gleetex.typesetting import LaTeXDocument
+import gleetex.typesetting as typesetting
+
+class test_typesetting(unittest.TestCase):
+ def test_formula_is_embedded(self):
+ formula = 'E = m \\cdot c^2'
+ doc = LaTeXDocument(formula)
+ self.assertTrue(formula in str(doc),
+ "formula must be contained in LaTeX typesetting as it was inserted.")
+
+ def test_if_displaymath_unset_correct_env_used(self):
+ doc = LaTeXDocument(r'A = \pi r^2')
+ doc.set_displaymath(False)
+ self.assertTrue('\\(' in str(doc))
+ self.assertTrue('\\)' in str(doc))
+
+ def test_if_displaymath_is_set_correct_env_used(self):
+ doc = LaTeXDocument(r'A = \pi r^2')
+ doc.set_displaymath(True)
+ self.assertTrue('\\[' in str(doc))
+ self.assertTrue('\\]' in str(doc))
+
+ def test_preamble_is_included(self):
+ preamble = '\\usepackage{eurosym}'
+ doc = LaTeXDocument('moooo')
+ doc.set_preamble_string(preamble)
+ self.assertTrue(preamble in str(doc))
+
+ def test_obviously_wrong_encoding_trigger_exception(self):
+ doc = LaTeXDocument('f00')
+ self.assertRaises(ValueError, doc.set_encoding, 'latin1:')
+ self.assertRaises(ValueError, doc.set_encoding, 'utf66')
+ # the following passes (assertRaisesNot)
+ doc.set_encoding('utf-8')
+
+ def test_that_latex_maths_env_is_used(self):
+ doc = LaTeXDocument('f00')
+ doc.set_latex_environment('flalign*')
+ self.assertTrue(r'\begin{flalign*}' in str(doc))
+ self.assertTrue(r'\end{flalign*}' in str(doc))
+
+################################################################################
+
+
+class test_replace_unicode_characters(unittest.TestCase):
+ def test_that_ascii_strings_are_returned_verbatim(self):
+ for string in ['abc.\\', '`~[]}{:<>']:
+ textmode = typesetting.replace_unicode_characters(string, False)
+ self.assertEqual(textmode, string, 'expected %s, got %s' % (string, textmode))
+ mathmode = typesetting.replace_unicode_characters(string, True)
+ self.assertEqual(textmode, string, 'expected %s, got %s' % (string, mathmode))
+
+ def test_that_alphabetical_characters_are_replaced_by_default(self):
+ textmode = typesetting.replace_unicode_characters('ö', False)
+ self.assertTrue('\\"' in textmode)
+ mathmode = typesetting.replace_unicode_characters('ö', True)
+ self.assertTrue('\\ddot' in mathmode)
+
+ def test_that_alphabetical_characters_are_kept_in_text_mode_if_specified(self):
+ self.assertEqual(typesetting.replace_unicode_characters('ö', False, # text mode
+ replace_alphabeticals=False), 'ö')
+ self.assertEqual(typesetting.replace_unicode_characters('æ', False,
+ replace_alphabeticals=False), 'æ')
+
+ def test_that_alphanumericals_are_replaced_in_mathmode_even_if_replace_alphabeticals_set(self):
+ self.assertNotEqual(typesetting.replace_unicode_characters('öäü', True,
+ replace_alphabeticals=True), 'öäü')
+ self.assertNotEqual(typesetting.replace_unicode_characters('æø', True,
+ replace_alphabeticals=True), 'æø')
+
+
+ def test_that_charachters_not_present_in_file_raise_exception(self):
+ with self.assertRaises(ValueError):
+ typesetting.replace_unicode_characters('€', True)
+
+ def test_that_formulas_are_replaced(self):
+ self.assertNotEqual(typesetting.replace_unicode_characters('π', True),
+ 'π')
+ self.assertNotEqual(typesetting.replace_unicode_characters('π', False),
+ 'π')
+
+class test_get_matching_brace(unittest.TestCase):
+ def test_closing_brace_found_when_only_one_brace_present(self):
+ text = 'text{ok}'
+ self.assertEqual(typesetting.get_matching_brace(text, 4), len(text) - 1)
+ self.assertEqual(typesetting.get_matching_brace(text + 'foo', 4), len(text) - 1)
+
+ def test_outer_brace_found(self):
+ text = 'text{o, bla\\"{o}dfdx.}ds'
+ self.assertEqual(typesetting.get_matching_brace(text, 4), len(text)-3)
+
+ def test_inner_brace_is_matched(self):
+ text = 'text{o, bla\\"{o}dfdx.}ds'
+ self.assertEqual(typesetting.get_matching_brace(text, 13), 15)
+
+ def test_that_unmatched_braces_raise_exception(self):
+ with self.assertRaises(ValueError):
+ typesetting.get_matching_brace('text{foooooooo', 4)
+ with self.assertRaises(ValueError):
+ typesetting.get_matching_brace('text{jo\"{o....}', 4)
+
+ def test_wrong_position_for_opening_brace_raises(self):
+ with self.assertRaises(ValueError):
+ typesetting.get_matching_brace('moo', 1)
+
+
+class test_escape_unicode_maths(unittest.TestCase):
+ """These tests assume that the tests written above work!"""
+ def test_that_mathmode_and_textmode_are_treated_differently(self):
+ math = typesetting.escape_unicode_maths('ö')
+ self.assertNotEqual(math, 'ö')
+ text = typesetting.escape_unicode_maths('\\text{ö}')
+ self.assertFalse('ö' in text)
+ # check whether characters got transcribed differently; it's enough to
+ # check one character of the generated sequence, they should differ
+ self.assertNotEqual(math[:2], text[6:8])
+
+ def test_that_flag_to_preserve_alphas_is_passed_through(self):
+ res = typesetting.escape_unicode_maths('\\text{ö}',
+ replace_alphabeticals=False)
+ self.assertEqual(res, '\\text{ö}')
+
+ def test_that_all_characters_are_preserved_when_no_replacements_happen(self):
+ text = 'This is a \\text{test} mate.'
+ self.assertEqual(typesetting.escape_unicode_maths(text), text)
+ self.assertEqual(typesetting.escape_unicode_maths(text,
+ replace_alphabeticals=False), text)
+ text = 'But yeah but no' * 20 + ', oh my god!'
+ self.assertEqual(typesetting.escape_unicode_maths(text), text)
+ self.assertEqual(typesetting.escape_unicode_maths(text,
+ replace_alphabeticals=False), text)
+
+ def test_that_everything_around_surrounded_character_is_preserved(self):
+ text = 'This is a \\text{über} test. ;)'
+ result = typesetting.escape_unicode_maths(text,
+ replace_alphabeticals=True)
+ ue_pos = text.index('ü')
+ # text in front is unchanged
+ self.assertEqual(result[:ue_pos], text[:ue_pos])
+ # find b character, which is the start of the remaining string
+ b_pos = result[ue_pos:].find('b') + ue_pos
+ # check that text after umlaut matches
+ self.assertEqual(result[b_pos:], text[ue_pos+1:])
+
+ text = 'But yeah but no' * 20 + ', oh my god!ø'
+ o_strok_pos = text.index('ø')
+ res = typesetting.escape_unicode_maths(text)
+ self.assertEqual(res[:o_strok_pos], text[:o_strok_pos])
+
+ def test_that_unknown_unicode_characters_raise_exception(self):
+ # you know that Santa Clause character? Seriously, if you don't know it,
+ # you should have a look. LaTeX does indeed not have command for this
+ # (2016, one never knows)
+ santa = chr(127877)
+ with self.assertRaises(typesetting.DocumentSerializationException):
+ typesetting.escape_unicode_maths(santa)
+
+ def test_that_two_text_environments_preserve_all_characters(self):
+ text = r'a\cdot b \text{equals} b\cdot c} \mbox{ is not equal } u^{v\cdot k}'
+ self.assertEqual(typesetting.escape_unicode_maths(text), text)
+
+ def test_color_names_in_backgroundare_accepted(self):
+ doc = LaTeXDocument(r'A = \pi r^2')
+ doc.set_background_color('cyan')
+ doc = str(doc)
+ self.assertTrue('pagecolor{cyan}' in doc,
+ "Expected \\pagecolor in document, got: %s" % doc)
+ self.assertTrue('\\color' not in doc)
+ self.assertFalse('definecolor' in doc)
+
+ def test_color_names_in_foregroundare_accepted(self):
+ doc = LaTeXDocument(r'A = \pi r^2')
+ doc.set_foreground_color('cyan')
+ doc = str(doc)
+ self.assertTrue('pagecolor' not in doc,
+ "Expected \\pagecolor in document, got: %s" % doc)
+ self.assertTrue('\\color{cyan' in doc,
+ "expeccted \\color{cyan, got:\n" + doc)
+ self.assertFalse('definecolor' in doc)
+
+ def test_hex_colours_with_leading_0s_work(self):
+ doc = LaTeXDocument(r'A = \pi r^2')
+ doc.set_background_color('00FFCC')
+ doc = str(doc)
+ self.assertTrue('pagecolor{background}' in doc,
+ "Expected \\pagecolor in document, got: %s" % doc)
+ self.assertTrue('definecolor' in doc)
+ self.assertTrue('00FFCC' in doc)
+
+ def test_color_rgb_in_foregroundare_accepted(self):
+ doc = LaTeXDocument(r'A = \pi r^2')
+ doc.set_foreground_color('FFAACC')
+ doc = str(doc)
+ self.assertTrue('pagecolor{}' not in doc,
+ "Expected \\pagecolor in document, got: %s" % doc)
+ self.assertTrue('\\color{foreground' in doc,
+ "document misses \\color command: %s" % doc)
+ self.assertTrue('definecolor' in doc)
+ self.assertTrue('FFAACC' in doc)
+
+ def test_color_rgb_in_backgroundare_accepted(self):
+ doc = LaTeXDocument(r'A = \pi r^2')
+ doc.set_background_color('FFAACC')
+ doc = str(doc)
+ self.assertTrue('pagecolor{background}' in doc,
+ "Expected \\pagecolor in document, got: %s" % doc)
+ self.assertTrue('\\color' not in doc)
+ self.assertTrue('definecolor' in doc)
+ self.assertTrue('FFAACC' in doc)
+
+ def test_no_colors_no_color_definitions(self):
+ doc = str(LaTeXDocument(r'A = \pi r^2'))
+ self.assertFalse('pagecolor' in doc)
+ self.assertFalse('\\color' in doc)
+ self.assertFalse('definecolor' in doc)
+