1"""
2Convert between any two formats using pandoc,
3and related filters
4"""
5
6import os
7
8from pandocfilters import Image, applyJSONFilters # type:ignore[import-untyped]
9
10from nbconvert.utils.base import NbConvertBase
11from nbconvert.utils.pandoc import pandoc
12
13__all__ = ["ConvertExplicitlyRelativePaths", "convert_pandoc"]
14
15
16def convert_pandoc(source, from_format, to_format, extra_args=None):
17 """Convert between any two formats using pandoc.
18
19 This function will raise an error if pandoc is not installed.
20 Any error messages generated by pandoc are printed to stderr.
21
22 Parameters
23 ----------
24 source : string
25 Input string, assumed to be valid in from_format.
26 from_format : string
27 Pandoc format of source.
28 to_format : string
29 Pandoc format for output.
30
31 Returns
32 -------
33 out : string
34 Output as returned by pandoc.
35 """
36 return pandoc(source, from_format, to_format, extra_args=extra_args)
37
38
39# When converting to pdf, explicitly relative references
40# like "./" and "../" doesn't work with TEXINPUTS.
41# So we need to convert them to absolute paths.
42# See https://github.com/jupyter/nbconvert/issues/1998
43class ConvertExplicitlyRelativePaths(NbConvertBase):
44 """A converter that handles relative path references."""
45
46 def __init__(self, texinputs=None, **kwargs):
47 """Initialize the converter."""
48 # texinputs should be the directory of the notebook file
49 self.nb_dir = os.path.abspath(texinputs) if texinputs else ""
50 self.ancestor_dirs = self.nb_dir.split("/")
51 super().__init__(**kwargs)
52
53 def __call__(self, source):
54 """Invoke the converter."""
55 # If this is not set for some reason, we can't do anything,
56 if self.nb_dir:
57 return applyJSONFilters([self.action], source)
58 return source
59
60 def action(self, key, value, frmt, meta):
61 """Perform the action."""
62 # Convert explicitly relative paths:
63 # ./path -> path (This should be visible to the latex engine since TEXINPUTS already has .)
64 # ../path -> /abs_path
65 # assuming all relative references are at the start of a given path
66 if key == "Image":
67 # Image seems to have this composition, according to https://github.com/jgm/pandoc-types
68 attr, caption, [filename, typedef] = value
69
70 if filename[:2] == "./":
71 filename = filename[2:]
72 elif filename[:3] == "../":
73 n_up = 0
74 while filename[:3] == "../":
75 n_up += 1
76 filename = filename[3:]
77 ancestors = "/".join(self.ancestor_dirs[:-n_up]) + "/"
78 filename = ancestors + filename
79 return Image(attr, caption, [filename, typedef])
80 # If not image, return "no change"
81 return None