diff --git a/.travis.yml b/.travis.yml index 4cb49e7d0..5a62758eb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,6 @@ addons: language: python python: - - 2.7 - 3.6 - 3.7 diff --git a/CHANGES.rst b/CHANGES.rst index 1d2d75866..055c85452 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,9 @@ {next} ------ +* Use content-based comparison in tests. (Issue #854) + + 0.97 (2020-05-09) ----------------- diff --git a/rst2pdf/tests/conftest.py b/rst2pdf/tests/conftest.py index 2ef9308e2..110500bf3 100644 --- a/rst2pdf/tests/conftest.py +++ b/rst2pdf/tests/conftest.py @@ -7,142 +7,86 @@ """ import glob -import hashlib import os import shlex import shutil import subprocess import tempfile +import fitz from packaging import version import pytest -import six ROOT_DIR = os.path.realpath(os.path.dirname(__file__)) INPUT_DIR = os.path.join(ROOT_DIR, 'input') OUTPUT_DIR = os.path.join(ROOT_DIR, 'output') -MD5_DIR = os.path.join(ROOT_DIR, 'md5') - - -class MD5Info(dict): - """Round-trip good, bad, unknown information to/from a .json file. - - For formatting reasons, the json module isn't used for writing, and since - we're not worried about security, we don't bother using it for reading, - either. - """ - - # Category to dump new data into - new_category = 'unknown' - # Categories which always should be in file - mandatory_categories = ['good', 'bad'] - - # Sentinel to make manual changes and diffs easy - sentinel = 'sentinel' - # An empty list is one which is truly empty or which has a sentinel - empty = [[], ['sentinel']] - # Suffix for file items - suffix = '_md5' - - def __init__(self): - self.__dict__ = self - self.changed = False - for name in self.mandatory_categories: - setattr(self, name + self.suffix, [self.sentinel]) - - def __str__(self): - """Return the string to output to the MD5 file.""" - result = [] - - for name, value in sorted(self.items()): - if not name.endswith(self.suffix): - continue - - result.append('%s = [' % name) - result.append( - ',\n'.join([" '%s'" % item for item in sorted(value)]) - ) - result.append(']\n') - - result.append('') - return '\n'.join(result) - - def find(self, checksum, new_category=new_category): - """Find the given checksum. - - find() has some serious side-effects. If the checksum is found, the - category it was found in is returned. If the checksum is not found, - then it is automagically added to the unknown category. In all cases, - the data is prepped to output to the file (if necessary), and - self.changed is set if the data is modified during this process. - Functional programming this isn't... - - A quick word about the 'sentinel'. This value starts with an 's', - which happens to sort > highest hexadecimal digit of 'f', so it is - always a the end of the list. - - The only reason for the sentinel is to make the database either to work - with. Both to modify (by moving an MD5 line from one category to - another) and to diff. This is because every hexadecimal line (every - line except the sentinel) is guaranteed to end with a comma. - """ - suffix = self.suffix - new_key = new_category + suffix - sentinel = set([self.sentinel]) - - # Create a dictionary of relevant current information - # in the database. - oldinfo = {k: v for k, v in self.items() if k.endswith(suffix)} - - # Create sets and strip the sentinels while - # working with the dictionary. - newinfo = {k: set(v) - sentinel for k, v in oldinfo.items()} - - # Create an inverse mapping of MD5s to key names - inverse = {} - for key, values in newinfo.items(): - for value in values: - inverse.setdefault(value, set()).add(key) - - # In general, inverse should be a function (there - # should only be one answer to the question "What - # key name goes with this MD5?") If not, - # either report an error, or just remove one of - # the possible answers if it is the same answer - # we give by default. - for value, keys in inverse.items(): - if len(keys) > 1 and new_key in keys: - keys.remove(new_key) - newinfo[new_key].remove(value) - - if len(keys) > 1: - raise SystemExit( - 'MD5 %s is stored in multiple categories: %s' % ( - value, ', '.join(keys), - ) - ) - - # Find the result in the dictionary. If it's not - # there we have to add it. - result, = inverse.get(checksum, [new_key]) - if result == new_key: - newinfo.setdefault(result, set()).add(checksum) - - # Create a canonical version of the dictionary, - # by adding sentinels and sorting the results. - for key, value in newinfo.items(): - newinfo[key] = sorted(value | sentinel) - - # See if we changed anything - if newinfo != oldinfo: - self.update(newinfo) - self.changed = True - - # And return the key associated with the MD5 - assert result.endswith(suffix), result - - return result[:-len(suffix)] +REFERENCE_DIR = os.path.join(ROOT_DIR, 'reference') + + +def _get_metadata(pdf): + metadata = pdf.metadata + + del metadata['creationDate'] + del metadata['modDate'] + + return metadata + + +def _get_pages(pdf): + pages = [] + + for page in pdf.pages(): + pages.append(page.getText('blocks')) + + return pages + + +def compare_pdfs(path_a, path_b): + pdf_a = fitz.open(path_a) + pdf_b = fitz.open(path_b) + + # sanity check + + assert pdf_a.isPDF + assert pdf_b.isPDF + + # compare metadata + + assert _get_metadata(pdf_a) == _get_metadata(pdf_b) + + # compare content + + pages_a = _get_pages(pdf_a) + pages_b = _get_pages(pdf_b) + + def fuzzy_coord_diff(coord_a, coord_b): + diff = abs(coord_a - coord_b) + assert diff / max(coord_a, coord_b) < 0.04 # allow an arbitrary diff + + def fuzzy_string_diff(string_a, string_b): + words_a = string_a.split() + words_b = string_a.split() + assert words_a == words_b + + assert len(pages_a) == len(pages_b) + for page_a, page_b in zip(pages_a, pages_b): + assert len(page_a) == len(page_b) + for block_a, block_b in zip(page_a, page_b): + # each block has the following format: + # + # (x0, y0, x1, y1, "lines in block", block_type, block_no) + # + # block_type and block_no should remain unchanged, but it's + # possible for the blocks to move around the document slightly and + # the text refold without breaking entirely + fuzzy_coord_diff(block_a[0], block_b[0]) + fuzzy_coord_diff(block_a[1], block_b[1]) + fuzzy_coord_diff(block_a[2], block_b[2]) + fuzzy_coord_diff(block_a[3], block_b[3]) + fuzzy_string_diff(block_a[4], block_b[4]) + assert block_a[5] == block_b[5] + assert block_a[6] == block_b[6] class File(pytest.File): @@ -178,6 +122,8 @@ def _build(self): raise NotImplementedError def runtest(self): + __tracebackhide__ = True + # if '.ignore' file present, skip test ignore_file = os.path.join(INPUT_DIR, self.name + '.ignore') @@ -187,25 +133,6 @@ def runtest(self): pytest.skip(ignore_reason) - # load MD5 info - - info = MD5Info() - - md5_file = os.path.join(MD5_DIR, self.name + '.json') - if os.path.exists(md5_file): - with open(md5_file, 'rb') as fh: - six.exec_(fh.read(), info) - - # if we have a PDF file output, we must have a MD5 checksum stored - - no_pdf = os.path.exists(os.path.join(INPUT_DIR, self.name + '.nopdf')) - - if info.good_md5 in ([], ['sentinel']) and not no_pdf: - pytest.fail( - 'Test has no known good output (open issue)', - pytrace=False, - ) - # run the actual test retcode, output = self._build() @@ -213,37 +140,47 @@ def runtest(self): # verify results if retcode: - pytest.fail( - 'Call failed with %d:\n\n%s' % (retcode, output), - pytrace=False, - ) + pytest.fail('Call failed with %d:\n\n%s' % (retcode, output)) + no_pdf = os.path.exists(os.path.join(INPUT_DIR, self.name + '.nopdf')) if no_pdf: return output_file = os.path.join(OUTPUT_DIR, self.name + '.pdf') + reference_file = os.path.join(REFERENCE_DIR, self.name + '.pdf') + if os.path.isdir(output_file): - output_files = list(glob.glob(os.path.join(output_file, '*.pdf'))) + assert os.path.isdir(reference_file), ( + 'Mismatch between type of output (dir) and reference (file)' + ) + output_files = glob.glob(os.path.join(output_file, '*.pdf')) + reference_files = glob.glob(os.path.join(reference_file, '*.pdf')) else: + assert os.path.isfile(reference_file), ( + 'Mismatch between type of output (file) and reference (dir)' + ) output_files = [output_file] + reference_files = [reference_file] - hashes = [] - for output_file in output_files: - with open(output_file, 'rb') as fh: - m = hashlib.md5() - m.update(fh.read()) - hashes.append(m.hexdigest()) + assert len(reference_files) == len(output_files), ( + 'Mismatch between number of files expected and number generated' + ) - result_type = info.find(' '.join(hashes), '') + reference_files.sort() + output_files.sort() - if result_type == 'bad': - pytest.fail('Generated a known bad checksum', pytrace=False) + for ref_pdf, out_pdf in zip(reference_files, output_files): + try: + compare_pdfs(ref_pdf, out_pdf) + except AssertionError as exc: + raise CompareException(exc) - if not result_type: - pytest.fail( - "Couldn't find a matching checksum for %s" % ' '.join(hashes), - pytrace=False, - ) + def repr_failure(self, excinfo): + """ called when self.runtest() raises an exception. """ + if isinstance(excinfo.value, CompareException): + return excinfo.exconly() + + return super(Item, self).repr_failure(excinfo) def reportinfo(self): return self.fspath, 0, self.name @@ -301,6 +238,8 @@ def _build(self): class SphinxItem(Item): def _build(self): + __tracebackhide__ = True + output_pdf = os.path.join(OUTPUT_DIR, self.name + '.pdf') output_log = os.path.join(OUTPUT_DIR, self.name + '.log') @@ -313,6 +252,7 @@ def _build(self): input_dir = os.path.join(INPUT_DIR, self.name) build_dir = tempfile.mkdtemp(prefix='rst2pdf-sphinx-') + cmd = ['sphinx-build', '-b', 'pdf', input_dir, build_dir] try: @@ -334,13 +274,17 @@ def _build(self): else: shutil.copytree(build_dir, output_pdf) else: - pytest.fail('Output PDF not generated', pytrace=False) + pytest.fail('Output PDF not generated') shutil.rmtree(build_dir) return retcode, output +class CompareException(Exception): + """Custom exception for error reporting.""" + + def pytest_collect_file(parent, path): if not (path.fnmatch('*/input') or path.fnmatch('*/input/*')): return diff --git a/rst2pdf/tests/input/sphinx-issue257/conf.py b/rst2pdf/tests/input/sphinx-issue257/conf.py index feae9dde5..8fa559cf7 100644 --- a/rst2pdf/tests/input/sphinx-issue257/conf.py +++ b/rst2pdf/tests/input/sphinx-issue257/conf.py @@ -34,7 +34,7 @@ source_encoding = 'utf-8' # The master toctree document. -master_doc = 'foobar' +master_doc = 'index' # General information about the project. project = u'Foobar' diff --git a/rst2pdf/tests/input/sphinx-issue257/foobar.rst b/rst2pdf/tests/input/sphinx-issue257/foobar.rst deleted file mode 100644 index f203c5352..000000000 --- a/rst2pdf/tests/input/sphinx-issue257/foobar.rst +++ /dev/null @@ -1,17 +0,0 @@ -Sphynx example document ------------------------ - -And now for the figure! - -.. aafig:: - - ---> | ^| | +++ - <--- | || --+-- +++ - <--> | |V | +++<- - __ __ ^ - | |__ +---+ |__| | - |box| .. - +---+ Xenophon - - - diff --git a/rst2pdf/tests/input/sphinx-issue318/test.rst b/rst2pdf/tests/input/sphinx-issue318/test.rst index 291beaddb..ec274bbbe 100644 --- a/rst2pdf/tests/input/sphinx-issue318/test.rst +++ b/rst2pdf/tests/input/sphinx-issue318/test.rst @@ -20,7 +20,7 @@ Contents: Describes a method without types. -.. cpp:function:: const T &array::operator[]() const +.. cpp:function:: const T& array::operator[] (int index) const Describes the constant indexing operator of a templated array. diff --git a/rst2pdf/tests/input/sphinx-issue364/index.rst b/rst2pdf/tests/input/sphinx-issue364/index.rst index 13b4ae54b..ffce61490 100644 --- a/rst2pdf/tests/input/sphinx-issue364/index.rst +++ b/rst2pdf/tests/input/sphinx-issue364/index.rst @@ -11,4 +11,3 @@ Index 1 A term in 2nd place :term:`term1` and :term:`term2` link to the glossary - diff --git a/rst2pdf/tests/input/sphinx-issue364/index2.rst b/rst2pdf/tests/input/sphinx-issue364/index2.rst index 3c8db98e1..0bf103569 100644 --- a/rst2pdf/tests/input/sphinx-issue364/index2.rst +++ b/rst2pdf/tests/input/sphinx-issue364/index2.rst @@ -1,6 +1,6 @@ -Index 2 - +:orphan: +Index 2 .. glossary:: @@ -11,4 +11,3 @@ Index 2 A term in 2nd place :term:`term3` and :term:`term2` link to the glossary - diff --git a/rst2pdf/tests/input/sphinx-multidoc/index.rst b/rst2pdf/tests/input/sphinx-multidoc/index.rst index 6356a1eed..2436c5533 100644 --- a/rst2pdf/tests/input/sphinx-multidoc/index.rst +++ b/rst2pdf/tests/input/sphinx-multidoc/index.rst @@ -1,7 +1,5 @@ Index 1 - - .. glossary:: term1 @@ -11,4 +9,3 @@ Index 1 A term in 2nd place :term:`term1` and :term:`term2` link to the glossary - diff --git a/rst2pdf/tests/input/sphinx-multidoc/index2.rst b/rst2pdf/tests/input/sphinx-multidoc/index2.rst index 3c8db98e1..0bf103569 100644 --- a/rst2pdf/tests/input/sphinx-multidoc/index2.rst +++ b/rst2pdf/tests/input/sphinx-multidoc/index2.rst @@ -1,6 +1,6 @@ -Index 2 - +:orphan: +Index 2 .. glossary:: @@ -11,4 +11,3 @@ Index 2 A term in 2nd place :term:`term3` and :term:`term2` link to the glossary - diff --git a/rst2pdf/tests/input/sphinx-repeat-table-rows/index.rst b/rst2pdf/tests/input/sphinx-repeat-table-rows/index.rst index c1cefed35..4a3c4558e 100644 --- a/rst2pdf/tests/input/sphinx-repeat-table-rows/index.rst +++ b/rst2pdf/tests/input/sphinx-repeat-table-rows/index.rst @@ -1,66 +1,67 @@ Testing pdf_repeat_table_rows ============================= -This table spans two pages. As the ``pdf_repeat_table_rows`` setting in ``conf.py`` is set to ``True``, we expect that the header is repeated on page 2. +This table spans two pages. As the ``pdf_repeat_table_rows`` setting in +``conf.py`` is set to ``True``, we expect that the header is repeated on page +2. +.. table:: + :widths:: 20 80 -.. widths:: 20 80 - -+-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| Column 1 | Column 2 | -+=============================+============================================================================================================================+ -| Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | -+-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | -+-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | -+-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | -+-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | -+-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | -+-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | -+-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | -+-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | -+-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | -+-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | -+-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | -+-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | -+-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | -+-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | -+-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | -+-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | -+-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | -+-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | -+-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | -+-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | -+-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | -+-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | -+-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | -+-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | -+-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | -+-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ -| Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | -+-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ - + +-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ + | Column 1 | Column 2 | + +=============================+============================================================================================================================+ + | Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | + +-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ + | Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | + +-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ + | Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | + +-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ + | Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | + +-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ + | Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | + +-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ + | Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | + +-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ + | Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | + +-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ + | Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | + +-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ + | Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | + +-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ + | Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | + +-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ + | Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | + +-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ + | Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | + +-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ + | Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | + +-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ + | Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | + +-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ + | Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | + +-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ + | Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | + +-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ + | Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | + +-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ + | Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | + +-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ + | Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | + +-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ + | Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | + +-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ + | Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | + +-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ + | Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | + +-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ + | Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | + +-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ + | Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | + +-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ + | Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | + +-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ + | Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | + +-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ + | Lorem ipsum dolor sit amet. | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at nunc scelerisque, tempor mauris sit amet, feugiat felis. | + +-----------------------------+----------------------------------------------------------------------------------------------------------------------------+ diff --git a/rst2pdf/tests/input/test_issue_175.txt b/rst2pdf/tests/input/test_issue_175.txt index 20030a8c1..e1120b2f9 100644 --- a/rst2pdf/tests/input/test_issue_175.txt +++ b/rst2pdf/tests/input/test_issue_175.txt @@ -1,6 +1,6 @@ .. raw:: pdf - Spacer 0 206mm + Spacer 0 216mm This is not very close to the bottom diff --git a/rst2pdf/tests/input/test_issue_288.ignore b/rst2pdf/tests/input/test_issue_288.ignore new file mode 100644 index 000000000..fcfa9781b --- /dev/null +++ b/rst2pdf/tests/input/test_issue_288.ignore @@ -0,0 +1 @@ +Returns different results in Python 2 and Python 3 due to Reportlab internals. diff --git a/rst2pdf/tests/input/test_issue_288.txt b/rst2pdf/tests/input/test_issue_288.txt index a7c167626..fb0f4c14e 100644 --- a/rst2pdf/tests/input/test_issue_288.txt +++ b/rst2pdf/tests/input/test_issue_288.txt @@ -22,7 +22,6 @@ time accessed. After that all back-end format specific layer operations will happen through this vtable which is cached in the ``layerObj`` struct. - Technical Solution ------------------ @@ -35,12 +34,11 @@ code. 2. Add the virtual table architecture The vtable will be initialized when the layer is accessed at the - first time. + first time. The vtable will be populated with dummies when it is created and these dummies will implement feasible default actions if there is any (e.g. do nothing) or return error by default. - 2.1. Standard functions which are found currently from ``maplayer.c`` 2.1.1. InitItemInfo @@ -49,14 +47,12 @@ code. int (*LayerInitItemInfo)(layerObj *layer); - 2.1.2. FreeItemInfo :: void (*LayerFreeItemInfo)(layerObj *layer); - 2.1.3. Open :: @@ -66,31 +62,29 @@ code. Currently there are two layers which accept more than the generic layer arg for LayerOpen function: - :: + :: - int msOGRLayerOpen(layerObj *layer, + int msOGRLayerOpen(layerObj *layer, const char *pszOverrideConnection); - int msWFSLayerOpen(layerObj *lp, - const char *pszGMLFilename, + int msWFSLayerOpen(layerObj *lp, + const char *pszGMLFilename, rectObj *defaultBBOX); - + However, these are called from ``msLayerOpen`` with ``NULL`` args, so I think that proposed interface for this virtual table function should be fine. - 2.1.4. IsOpen :: int (*LayerIsOpen)(layerObj *layer); - 2.1.5. WhichShapes :: - int (*LayerWhichShapes)(layerObj *layer, + int (*LayerWhichShapes)(layerObj *layer, rectObj rect); 2.1.6. NextShape @@ -99,46 +93,39 @@ code. int (*LayerNextShape)(layerObj *layer, shapeObj *shape); - 2.1.7. GetShape :: - int (*LayerGetShape)(layerObj *layer, shapeObj *shape, + int (*LayerGetShape)(layerObj *layer, shapeObj *shape, int tile, long record); - 2.1.8. LayerClose :: int (*LayerClose)(layerObj *layer); - 2.1.9. LayerGetItems :: int (*LayerGetItems)(layerObj *layer); - 2.1.10. GetExtent :: - int (*LayerGetExtent)(layerObj *layer, + int (*LayerGetExtent)(layerObj *layer, rectObj *extent); - 2.1.11. GetAutoStyle :: int (*LayerGetAutoStyle)(mapObj *map, layerObj *layer, - classObj *c, int tile, + classObj *c, int tile, long record); - - 2.2. New functions and/or fields for vtable @@ -158,12 +145,11 @@ code. int (*LayerCloseConnection)(layerObj *layer); - And the main place where this function will be called, is ``mapfile.c: 4822`` :: - + void msCloseConnections(mapObj *map) This function is needed because e.g. ``POSTGIS`` is @@ -180,7 +166,6 @@ code. */ msPOSTGISLayerResultClose(layer); - 2.2.2. SetTimeFilter This function is used to create a time filter for @@ -208,12 +193,10 @@ code. :: - int (*LayerSetTimeFilter)(layerObj *lp, - const char *timestring, + int (*LayerSetTimeFilter)(layerObj *lp, + const char *timestring, const char *timefield); - - 2.3. Extra functions to add to the vtable 2.3.1. FLTApplyFilterToLayer (``mapogcfilter.c: 1084``) @@ -230,7 +213,6 @@ code. if the conditions for this are met, otherwise they will use Non-SQL version of the function. - 2.3.2. layerObj->items allocation There will be vtable function for allocation ``items`` and @@ -242,7 +224,7 @@ code. :: - int (*CreateItems)(layerObj *) + int (*CreateItems)(layerObj *) which does the allocation and set numitems to correct value. @@ -253,7 +235,6 @@ code. ``msMYGISLayerOpen``. We will not provide this functionality through vtable. - 2.4. Interface functions for internal layers We have to add some new interface functions to access layers. @@ -262,27 +243,22 @@ code. We need a per layer type a function which will be called when the layer's vtable has to be initialized. These - functions will be + functions will be :: - :: - int msXXXInitializeLayerVirtualTable(layerObj *) where XXX will be name of the layer. This function is called anytime when the vtable isn't initialized and the layer is accessed at the first time. - 2.4.2 Function interface to change the connectiontype of layer - + To change the connection type of layer, it has to be done by function interface. Accessing directly ``connectiontype`` field is not supported anymore. To change the connectiontype and to connect to the new - layer, there will be following interface function - - :: + layer, there will be following interface function :: int msConnectLayer(int connectiontype, const char *library_str) @@ -292,10 +268,8 @@ code. prodive functionality for this new layer. For internal layer types this second argument is ignored. - - 3. Remove unwanted interfaces - + Frank Warmerdam proposed [FW1]_ that we remove all layer specific interface functions from ``map.h``. @@ -304,15 +278,12 @@ code. so that none of the layer type specific definitions need to appear in map.h any more. - .. [FW1] + .. [FW1] | ``Frank Warmerdam on the mapserver-dev:`` | ``Message-Id: `` | ``Date: Wed, 17 Aug 2005 22:31:09 -0400`` | ``Subject: Re: Mapserver Plug-in Infastructure: RFC and PATCH`` - - - Files and objects affected -------------------------- @@ -323,40 +294,34 @@ This proposal will affect at least following files and objects: * ``layerObj`` will contain new fields. There will be a new object ``vtableObj`` in the ``map.h``. - -* ``maplayer.c`` +* ``maplayer.c`` * Various changes, layer specific ``switch``-statements will go away, vtable handling and layers vtable initialization will be added. - * ``mapfile.c`` * Cleaning of ``msCheckConnection`` * Vtable for ``msCloseConnection`` - * ``mapogcfilter.c`` * Remove layer-logic from ``FLTApplyFilterToLayer`` - * ``mapXXX.c``, where ``XXX`` is the name of layer. * Add new initialization function * Add all new interface functions * Fix existing interface functions, if needed / add wrappers for - ``msOGRLayerOpen`` and ``msWFSLayerOpen``. - - + ``msOGRLayerOpen`` and ``msWFSLayerOpen``. Backwards compatibility issues ------------------------------ This is binary and source code level backward incompatible change. The proposal will remove some previously public functions, and add new -field(s) to the ``layerObj`` struct. +field(s) to the ``layerObj`` struct. This proposal is not MapScript backward compatible, it will break scripts which change directly ``connectiontype`` field in @@ -364,7 +329,6 @@ scripts which change directly ``connectiontype`` field in The proposal is MAP-file backward compatible. - Implementation Issues --------------------- @@ -379,16 +343,12 @@ Implementation Issues specific code from MapServer e.g. ``WMF``, ``WMS`` and ``GRATICULE`` are used as special cases from place to place. - - Bug ID ------ Bug 1477_ - -.. _1477: http://trac.osgeo.org/mapserver/ticket/1477 - +.. _1477: http://trac.osgeo.org/mapserver/ticket/1477 Voting history -------------- @@ -400,7 +360,6 @@ Voting +1: Howard Butler, Frank Warmerdam, Yewondwossen Assefa, Daniel Morissett Proposal passes and will move forward. - Open questions -------------- @@ -418,12 +377,9 @@ Open questions new functions to the struct if we refactor code more or found some logic which is ignored by oversight in this proposal. - * Are there any special issues with the raster query layer support which is handled via the layer API? - - -.. Local Variables: +.. Local Variables: mode: text End: diff --git a/rst2pdf/tests/reference/sphinx-issue318.pdf b/rst2pdf/tests/reference/sphinx-issue318.pdf index 3c244c981..cdc2a3c6a 100644 Binary files a/rst2pdf/tests/reference/sphinx-issue318.pdf and b/rst2pdf/tests/reference/sphinx-issue318.pdf differ diff --git a/rst2pdf/tests/reference/sphinx-repeat-table-rows.pdf b/rst2pdf/tests/reference/sphinx-repeat-table-rows.pdf index 2cce3f1ec..c10ef27ec 100644 Binary files a/rst2pdf/tests/reference/sphinx-repeat-table-rows.pdf and b/rst2pdf/tests/reference/sphinx-repeat-table-rows.pdf differ diff --git a/rst2pdf/tests/reference/test_753.pdf b/rst2pdf/tests/reference/test_753.pdf new file mode 100644 index 000000000..d9ffe4928 Binary files /dev/null and b/rst2pdf/tests/reference/test_753.pdf differ diff --git a/rst2pdf/tests/reference/test_aafigure.pdf b/rst2pdf/tests/reference/test_aafigure.pdf index 2121f59eb..47b042f69 100644 Binary files a/rst2pdf/tests/reference/test_aafigure.pdf and b/rst2pdf/tests/reference/test_aafigure.pdf differ diff --git a/rst2pdf/tests/reference/test_docutils_math.pdf b/rst2pdf/tests/reference/test_docutils_math.pdf new file mode 100644 index 000000000..faf10cd37 Binary files /dev/null and b/rst2pdf/tests/reference/test_docutils_math.pdf differ diff --git a/rst2pdf/tests/reference/test_issue_288.pdf b/rst2pdf/tests/reference/test_issue_288.pdf index 632151049..6b63e87d1 100644 Binary files a/rst2pdf/tests/reference/test_issue_288.pdf and b/rst2pdf/tests/reference/test_issue_288.pdf differ diff --git a/rst2pdf/tests/reference/test_issue_754.pdf b/rst2pdf/tests/reference/test_issue_754.pdf new file mode 100644 index 000000000..9fc62c62a Binary files /dev/null and b/rst2pdf/tests/reference/test_issue_754.pdf differ diff --git a/rst2pdf/tests/reference/test_ps_font.pdf b/rst2pdf/tests/reference/test_ps_font.pdf new file mode 100644 index 000000000..1ed22f12a Binary files /dev/null and b/rst2pdf/tests/reference/test_ps_font.pdf differ diff --git a/setup.py b/setup.py index 6412469b5..80fe9736b 100644 --- a/setup.py +++ b/setup.py @@ -28,7 +28,7 @@ def read(*rnames): except ImportError: install_requires.append('simplejson') -tests_require = ['pyPdf2'] +tests_require = ['pyPdf2', 'pymupdf'] sphinx_require = ['sphinx'] hyphenation_require = ['wordaxe>=1.0'] svgsupport_require = ['svglib']