Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"sources" : [
{
"usage" : "toplevel",
"filename" : "main.tex"
}
],
"process" : {
"compiler" : "pdflatex"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@MISC{abc,
author = "Someone",
title = {Some Title},
howpublished = {It got published!},
year = {2025},
note = {Betelgeuse}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
\documentclass{article}
\usepackage[backend=bibtex]{biblatex}
\addbibresource{foo.bib}
\begin{document}
(Type your content here.) \cite{abc} Hell WOrld \cite{foobar} So we go
\printbibliography
\end{document}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"sources" : [
{
"usage" : "toplevel",
"filename" : "main.tex"
}
],
"process" : {
"compiler" : "pdflatex"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@MISC{abc,
author = "Someone",
title = {Some Title},
howpublished = {It got published!},
year = {2025},
note = {Betelgeuse}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
\documentclass{article}
\begin{document}
(Type your content here.) \cite{abc} Hell WOrld \cite{foobar} so it goes.
\bibliographystyle{plain}
\bibliography{foo}
\end{document}
Binary file not shown.
22 changes: 22 additions & 0 deletions tex2pdf-service/tests/test_docker.py
Original file line number Diff line number Diff line change
Expand Up @@ -741,3 +741,25 @@ def test_first_line(docker_container, ts):
meta, status = submit_tarball(url, tarball, outcome, api_args={"auto_detect": "true", "ts": ts})
assert meta is not None
assert meta.get("pdf_file") == "first-line.pdf"


@pytest.mark.integration
def test_missing_cite_bibtex(docker_container):
url = docker_container + "/convert"
tarball = os.path.join(SELF_DIR, "fixture/tarballs/missing-cite-bibtex/missing-cite-bibtex.tar.gz")
outcome = os.path.join(SELF_DIR, "output/missing-cite-bibtex.outcome.tar.gz")
meta, status = submit_tarball(url, tarball, outcome, api_args={"auto_detect": "false"})
assert meta is not None
# compilation must fail on missing citations
assert meta.get("status") == "fail"


@pytest.mark.integration
def test_missing_cite_biber(docker_container):
url = docker_container + "/convert"
tarball = os.path.join(SELF_DIR, "fixture/tarballs/missing-cite-biber/missing-cite-biber.tar.gz")
outcome = os.path.join(SELF_DIR, "output/missing-cite-biber.outcome.tar.gz")
meta, status = submit_tarball(url, tarball, outcome, api_args={"auto_detect": "false"})
assert meta is not None
# compilation must fail on missing citations
assert meta.get("status") == "fail"
35 changes: 31 additions & 4 deletions tex2pdf-service/tex2pdf/tex_to_pdf_converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ def _run_base_engine_necessary_times(
logger.warning("Last run had changing labels, but we exhausted the MAX_LATEX_RUNS limit.")
else:
status = "fail"
citation_missing = False
for line in run["log"].splitlines():
for error_needle, error_msg in error_needles:
if error_needle.search(line):
Expand All @@ -298,7 +299,7 @@ def _run_base_engine_necessary_times(
)
return outcome
for rerun_needle in rerun_needles:
if line.find(rerun_needle) >= 0:
if rerun_needle.search(line):
# Need retry
logger.debug(f"Found rerun needle {rerun_needle}")
if iteration == iteration_list[-1]:
Expand All @@ -308,11 +309,35 @@ def _run_base_engine_necessary_times(
else:
status = "fail"
break
if MISSING_CITE_RE.search(line):
logger.debug(f"MISSING_CITE: {MISSING_CITE_RE} found in line {line}")
citation_missing = True
else:
logger.debug(f"MISSING_CITE: {MISSING_CITE_RE} not found in line {line}")
run["iteration"] = iteration
outcome.update({"runs": self.runs, "status": status, "step": step})
if status == "success":
break

# if no rerun needle is found, we would return now, but there might
# be still unresolved references. In this case, error out.
logger.debug(f"Checking for citation_missing = {citation_missing}")
if citation_missing:
# Note! We need to delete the PDF/DVI file otherwise "upstream" Converter
# believes all is fine and continues with success!
name = run[base_format]["name"]
artifact_file = os.path.join(in_dir, name)
if os.path.exists(artifact_file):
logger.debug("Output %s deleted due to failed run", name)
os.unlink(artifact_file)
run[base_format] = file_props(artifact_file)
outcome.update(
{
"status": "fail",
"reason": "Missing citation reference.",
}
)
return outcome
else:
break
return outcome

def _exec_cmd(
Expand Down Expand Up @@ -623,8 +648,10 @@ def select_converter_class(zzrm: ZeroZeroReadMe | None) -> type[BaseConverter]:
bad_for_pdftex_packages = {pname: True for pname in ["fontspec"]}
bad_for_tex_packages = {pname: True for pname in ["fontspec"]}

# Thanks biblatex to slightly change the warning message from a ` to a ' ... wonderful trick!
MISSING_CITE_RE = re.compile("LaTeX Warning: Citation [`'].*' on page [0-9]* undefined on input line [0-9]*\.")
rerun_needles = [
"Rerun to get cross-references right\.",
re.compile("Rerun to get cross-references right\."),
]
error_needles = [
(
Expand Down
Loading