Skip to content

Commit

Permalink
Support Tectonic engine and execute user-customizable command sequence (
Browse files Browse the repository at this point in the history
  • Loading branch information
akisaini authored Sep 26, 2024
1 parent 732b86b commit 47eae1d
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 27 deletions.
50 changes: 49 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,16 @@ and see the block like this in the output

## Customization

The extension defaults to running `xelatex` on the server.
The extension defaults to running the `xelatex` engine on the server.
This command may be customized (e.g., to use `pdflatex` instead) by customizing
your `jupyter_notebook_config.py` file:

```python
c.LatexConfig.latex_command = 'pdflatex'
```

The above configuration will compile a LaTeX document using the common predefined flags and options such as `-interaction` `-halt-on-error`, `-file-line-error`, `-synctex`. For more control over the command sequence, check the Manual Command Arguments configuration.

The extension defaults to running `bibtex` for generating a bibliography
if a `.bib` file is found. You can also configure the bibliography command
by setting
Expand All @@ -97,13 +99,59 @@ by setting
c.LatexConfig.bib_command = '<custom_bib_command>'
```

_New in 4.2.0_: `BibTeX` compilation is skipped if the following conditions are present:

- `c.LatexConfig.disable_bibtex` is explicitly set to `True` in the `jupyter_notebook_config.py` file
- There are no .bib files found in the folder

To render references (`\ref{...}`), such as equation or chapter numbers, you would
need to compile in multiple passes by setting

```python
c.LatexConfig.run_times = 2
```

_New in 4.2.0_: Manual Compile Command
For more advanced customizations, a complete command sequence can be specified using the `manual_cmd_args` configuration in the `jupyter_notebook_config.py` file. This allows to define the exact command and use options the extension will finally execute:

```python
c.LatexConfig.manual_cmd_args = [
'lualatex', # Specify the LaTeX engine (e.g., lualatex, pdflatex)
'-interaction=nonstopmode', # Continue compilation without stopping for errors
'-halt-on-error', # Stop compilation at the first error
'-file-line-error', # Print file and line number for errors
'-shell-escape', # Enable shell escape
'-synctex=1', # Enable SyncTeX for editor synchronization
'{filename}.tex' # Placeholder for the input file name
]
```

The only supported placeholder in the manual compile command is `{filename}`. It will be replaced by the name of the LaTeX file during compilation.

Additional tags and options can also be added to edit configuration values.

_New in 4.2.0_: Tectonic Engine Support
The extension now also supports the Tectonic engine for compiling LaTeX files. To use Tectonic as the default LaTeX engine cutomize the `jupyter_notebook_config.py file`:

```python
c.LatexConfig.latex_command = 'tectonic'
```

The default command sequence for Tectonic generates the output file in `pdf` format and uses the available `SyncTeX` flag.

For [advanced control](https://tectonic-typesetting.github.io/book/latest/v2cli/compile.html) over [Tectonic](https://github.com/tectonic-typesetting/tectonic), specify options in the manual_cmd_args setting:

```python
c.LatexConfig.manual_cmd_args = [
'tectonic',
'--outfmt=pdf', # Output format as PDF
'--synctex=1', # Enable SyncTeX for editor synchronization
'--outdir', # The directory in which to place output files
'--keep-logs', # Keep the log files generated during processing
'{filename}.tex' # Input .tex file
]
```

### Security and customizing shell escapes

LaTeX files have the ability to run arbitrary code by triggering external
Expand Down
68 changes: 46 additions & 22 deletions jupyterlab_latex/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def initialize(self, root_dir):
self.root_dir = root_dir


def build_tex_cmd_sequence(self, tex_base_name, run_bibtex=False):
def build_tex_cmd_sequence(self, tex_base_name):
"""Builds tuples that will be used to call LaTeX shell commands.
Parameters
Expand All @@ -83,6 +83,8 @@ def build_tex_cmd_sequence(self, tex_base_name, run_bibtex=False):
"""
c = LatexConfig(config=self.config)

engine_name = c.latex_command

escape_flag = ''
if c.shell_escape == 'allow':
Expand All @@ -94,35 +96,57 @@ def build_tex_cmd_sequence(self, tex_base_name, run_bibtex=False):

# Get the synctex query parameter, defaulting to
# 1 if it is not set or is invalid.
synctex = self.get_query_argument('synctex', default='1')
synctex = '1' if synctex != '0' else synctex

full_latex_sequence = (
c.latex_command,
escape_flag,
"-interaction=nonstopmode",
"-halt-on-error",
"-file-line-error",
f"-synctex={synctex}",
f"{tex_base_name}",
synctex = self.get_query_argument('synctex', default=True)

if c.manual_cmd_args:
# Replace placeholders with actual values
self.log.info("Using the manual command argument and buidling latex sequence.")
full_latex_sequence = [
# replace placeholders using format()
arg.format(filename=tex_base_name)
for arg in c.manual_cmd_args
]
elif engine_name == 'tectonic':
self.log.info("Using Tectonic for LaTeX compilation.")
full_latex_sequence = (
engine_name,
f"{tex_base_name}.tex", # input .tex file
"--outfmt=pdf", # specify output format (pdf in this case)
f"--synctex={'1' if synctex else '0'}" # to support SyncTeX (synchronization with the editor)
)

full_bibtex_sequence = (
c.bib_command,
f"{tex_base_name}",
else:
self.log.info("Using TeX Live (or compatible distribution) for LaTeX compilation.")
full_latex_sequence = (
engine_name,
escape_flag,
"-interaction=nonstopmode",
"-halt-on-error",
"-file-line-error",
f"-synctex={'1' if synctex else '0'}",
f"{tex_base_name}",
)

command_sequence = [full_latex_sequence]

if run_bibtex:
# Skip bibtex compilation if the following conditions are present
# - c.LatexConfig.disable_bibtex is explicitly set to True
# - tectonic engine is used
# - there are no .bib files found in the folder
if c.disable_bibtex or engine_name == 'tectonic' or not self.bib_condition():
# Repeat LaTeX command run_times times
command_sequence = command_sequence * c.run_times
else:
full_bibtex_sequence = (
c.bib_command,
f"{tex_base_name}",
)

command_sequence += [
full_bibtex_sequence,
full_latex_sequence,
full_latex_sequence,
]
else:
command_sequence = command_sequence * c.run_times

]

return command_sequence

def bib_condition(self):
Expand Down
15 changes: 11 additions & 4 deletions jupyterlab_latex/config.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
""" JupyterLab LaTex : live LaTeX editing for JupyterLab """

from traitlets import Unicode, CaselessStrEnum, Integer, Bool
from traitlets import Unicode, CaselessStrEnum, Integer, Bool, List as TraitletsList
from traitlets.config import Configurable

class LatexConfig(Configurable):
Expand All @@ -10,10 +10,13 @@ class LatexConfig(Configurable):
"""
latex_command = Unicode('xelatex', config=True,
help='The LaTeX command to use when compiling ".tex" files.')
disable_bibtex = Bool(default_value=False, config=True,
help='Whether to disable the BibTeX command sequence.')
bib_command = Unicode('bibtex', config=True,
help='The BibTeX command to use when compiling ".tex" files.')
synctex_command = Unicode('synctex', config=True,
help='The synctex command to use when syncronizing between .tex and .pdf files.')
help='The BibTeX command to use when compiling ".tex" files.' +\
'Only used if disable_bibtex is not set to True')
synctex_command = Bool('synctex', config=True,
help='Whether to use the synctex command when syncronizing between .tex and .pdf files.')
shell_escape = CaselessStrEnum(['restricted', 'allow', 'disallow'],
default_value='restricted', config=True,
help='Whether to allow shell escapes '+\
Expand All @@ -25,3 +28,7 @@ class LatexConfig(Configurable):
help='How many times to compile the ".tex" files.')
cleanup = Bool(default_value=True, config=True,
help='Whether to clean up ".out/.aux" files or not.')
# Add a new configuration option to hold user-defined commands
manual_cmd_args = TraitletsList(Unicode(), default_value=[], config=True,
help='A list of user-defined command-line arguments with placeholders for ' +
'filename ({filename})')

0 comments on commit 47eae1d

Please sign in to comment.