diff --git a/doc/macroprocessor/macroprocessor.tex b/doc/macroprocessor/macroprocessor.tex
index cf547c872d699f7d6fea28558c0d6beaf6b42e4e..3c9c61f4c437a7f23c482324aa829e49b8e53a1b 100644
--- a/doc/macroprocessor/macroprocessor.tex
+++ b/doc/macroprocessor/macroprocessor.tex
@@ -8,9 +8,11 @@
 
 \usetheme{Boadilla}
 
+\graphicspath{{../logos/}}
+
 \title{The Dynare Macro Processor}
 \author{Sébastien Villemot}
-\pgfdeclareimage[height=0.8cm]{logo}{../logos/dlogo}
+\pgfdeclareimage[height=0.8cm]{logo}{dlogo}
 \institute[Dynare Team]{\pgfuseimage{logo}}
 
 \date{23 May 2023}
diff --git a/doc/preprocessor/preprocessor.tex b/doc/preprocessor/preprocessor.tex
index e56d5e5511c6b28eebe51c3726f498c65ff7475b..1fb7874cfd2ede75b5079a19d44a6ad7e01a4bea 100644
--- a/doc/preprocessor/preprocessor.tex
+++ b/doc/preprocessor/preprocessor.tex
@@ -10,13 +10,15 @@
 \usepackage[english]{babel}
 \usepackage[utf8]{inputenc}
 
-\titlegraphic{\includegraphics{../logos/dlogo.png}}
+\graphicspath{{../logos}}
+
+\titlegraphic{\includegraphics{dlogo.png}}
 
 \title{The Dynare Preprocessor}
 
 \author[S. Villemot, H.Bastani]{Sébastien Villemot \and Houtan Bastani}
 
-\institute[CEPREMAP]{\includegraphics[scale=0.15]{../logos/cepremap.jpg}}
+\institute[CEPREMAP]{\includegraphics[scale=0.15]{cepremap.jpg}}
 
 \date{1 February 2017}
 
diff --git a/meson.build b/meson.build
index a5ee3687f39a99f6155d1a8c35eb8ecd4aaa791b..1d8ede8584396f619c62a65f270de457767b376c 100644
--- a/meson.build
+++ b/meson.build
@@ -1,5 +1,5 @@
 # TODO for reaching feature parity with current autotools-based build system:
-# - Build and install documentation (with a configuration option to disable it explicitly)
+# - Configuration option to disable documentation
 # - Add -Wold-style-cast flag except when building flex-generated files
 
 project('dynare-preprocessor', 'cpp',
@@ -73,3 +73,35 @@ executable('dynare-preprocessor', main_src, flex_src, flexlexer_h, bison_src,
            include_directories : incdir, dependencies : boost_dep,
            link_args : get_option('prefer_static') ? '-static' : '',
            install : true)
+
+## LaTeX stuff
+
+latexmk = find_program('latexmk')
+
+# We have to set TEXINPUTS because the current directory is not the source
+# directory when latexmk is invoked (and using the -cd option in combination
+# with -outdir/-auxdir does not work in all cases because @OUTDIR@ and
+# @PRIVATE_DIR@ can be relative paths)
+
+# Without the -g flag, latexmk remembers a previous build failure and will refuse
+# to recompile even if the error has been fixed in the TeX source
+
+# The \graphicspath{} command does not compute directories relative to TEXINPUTS,
+# so add these manually
+logos_dir = meson.current_source_dir() + '/doc/logos'
+
+custom_target('macroprocessor.pdf',
+              output : 'macroprocessor.pdf',
+              input : 'doc/macroprocessor/macroprocessor.tex',
+              command : [ latexmk, '-pdf', '-g', '-auxdir=@PRIVATE_DIR@', '@INPUT@'],
+              env : { 'TEXINPUTS': meson.current_source_dir() + '/doc/macroprocessor:' + logos_dir + ':' },
+              install : true,
+              install_dir : 'share/doc/dynare')
+
+custom_target('preprocessor.pdf',
+              output : 'preprocessor.pdf',
+              input : 'doc/preprocessor/preprocessor.tex',
+              command : [ latexmk, '-pdf', '-g', '-auxdir=@PRIVATE_DIR@', '@INPUT@'],
+              env : { 'TEXINPUTS': meson.current_source_dir() + '/doc/preprocessor:' + logos_dir + ':' },
+              install : true,
+              install_dir : 'share/doc/dynare')