TransWikia.com

Compile Tex inside Microsoft C#.net

TeX - LaTeX Asked on February 20, 2021

I know there are some command-line applications like pdflatex that can help me to compile a LaTex document from C#.Net (using the System.Diagnostics.Process class) .

I wanna know that what’s the best command-line tool for compiling a TeX document ?

5 Answers

So we meet again, C#. :)

It's not so easy for to provide an answer, but I'll try.

Most of the time, we end up with complex documents which might require several runs in order to reach a steady state. So, it's likely you'll need more than one run to get the correct output.

If you stick with the engines like pdflatex, you'll probably need to ensure the miminal amount of calls to it and/or other helper programs (e.g, makeindex). That might be a burden, specially when you don't know the steps in advance.

Tools like latexmk and rubber were created to ease the compilation process in the sense of trying to guess if your document needs another run, or calling other programs. They even give up on compilation if you have a document which will never reach a steady state. So, they might make your life easier. But of course we have the human factor. :) Those tools might not work as expected (e.g, rubber doesn't recognize biber), so you might need to end up tuning those tools with configuration files or directives.

And now there's arara, a humble project of mine (sorry for the shameless self-promotion). This tool is different from latexmk and rubber because, well, it will execute only what you tell it to. There's no guessing, no auxiliary file parsing or other cool stuff. By adding directives to the source code, (e.g, % arara: pdflatex: { shell: yes }, arara will expand those instructions based on well defined rules to commands and then execute them. It might be powerful if you really know what it's going on in the bowels of your engine, otherwise it might be difficult to digest the concept. The project is still in heavy development, thanks to the great contributions of Marco Daniel (project collaborator) and Joseph Wright.

Now, back to your question. :)

It would be great if you tell us about your C# application. Sometimes, based on your needs, a more reliable solution is available. For example, Forgiver's answer is an interesting way of calling pdflatex. If you need a more generic configuration, you could use something among these lines:

using System;
using System.Xml;
using System.Diagnostics;

namespace MyProgram
{
    class MyTeXWrapper
    {
        static void Main(string[] args)
        {

            if (args.Length != 2)
            {
                Console.WriteLine("Sorry, I expect 2 arguments.");
                System.Environment.Exit(-1);
            }

            string command = "";
            string arguments = "";

            try
            {

                XmlDocument document = new XmlDocument();
                document.Load(args[1]);
                XmlElement rootElement = document.DocumentElement;
                XmlNodeList nodes = rootElement.ChildNodes;
                foreach (XmlNode node in nodes)
                {
                    if (node.Name.Equals("command"))
                    {
                        command = node.InnerText;
                    }
                    else
                    {
                        if (node.Name.Equals("arg"))
                        {
                            if (node.Attributes.Count == 1)
                            {
                                if (node.Attributes.Item(0).Name.Equals("value"))
                                {
                                    arguments = arguments + node.Attributes.Item(0).Value + " ";
                                }
                            }
                        }
                    }

                }

            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

            arguments = arguments + args[0];

            Process process = new Process();
            process.StartInfo.FileName = command;
            process.StartInfo.Arguments = arguments;
            process.StartInfo.UseShellExecute = false;
            process.Start();
            process.WaitForExit();
            if (process.ExitCode == 0)
            {
                Console.WriteLine("Command was successfully executed.");
            }
            else
            {
                Console.WriteLine("An error occurred.");
            }
        }
    }
}

The idea is to have a XML-based configuration file which will determine how the command should be invoked. A sample configuration file:

<configuration>
    <command>pdflatex</command>
    <arg value="--interaction=nonstopmode"/>
    <arg value="--shell-escape"/>
</configuration>

Consider the following tex file:

documentclass{article}

begin{document}

Hello world!

end{document}

Now, I can run my code with MyProgram.exe hello config.xml:

Program output

I use Mono. :)

Note that I used --interaction=nonstopmode as an argument. Depending of how your application should behave, having interactive commands might be a problem. For example, if our tex code above had a syntax typo, and without --interaction=nonstopmode, pdflatex would prompt you a message and wait for your input. For arara I redirected the output streams (stdout and stderr) to separated threads, but I'm still struggling with the input stream (stdin). As an example, TeXworks can prompt an input box if the program requires interaction:

TeXworks

Thankfully the TeX programs provide exit status, so you can check if the run was successful. If you have a very complex document, maybe it's wise to create a worker thread and let it handle the call, avoiding blocking your application (David mentioned in the chatroom that Frank's book took about 20 minutes to compile). :)

Now, which tool is the best? I have absolutely no idea. :) My thoughts: if you want to have more control on your workflow, stick with the original command line programs. Now, flexibility and ease to use are keywords here, give latexmk or rubber a try. :)

Correct answer by Paulo Cereda on February 20, 2021

Step 1: Create a batch file

To avoid recompiling the C# source code to be in sync with the (La)TeX compiler you want to use, it will be convenient to create a batch file. The batch file executes (La)TeX compiler behind the scene. Save the batch file anywhere and set the PATH system variable to the batch location.

The simplest batch may be as follows

rem %1 represents the file name with no extension.
pdflatex -shell-escape %1

Step 2: Create the C# code and compile

The simplest example is as follows:

   using System.Diagnostics;
   class MyApp
    {
        public static void Main(string[] args)
        {
            Process p1 = new Process();
            p1.StartInfo.FileName = "batch.bat";
            p1.StartInfo.Arguments = args[0];
            p1.StartInfo.UseShellExecute = false;

            p1.Start();
            p1.WaitForExit();
         }
     }

Compile it with C# compiler to get MyApp.exe for example.

Step 3: Finish, You are ready to make a try

MyApp.exe needs a single argument which is the TeX input file name with no extension.

From command prompt, you execute MyApp.exe TeXInputFileNameWithNoExtension.

Answered by kiss my armpit on February 20, 2021

I want to provide an answer which uses the tool arara.

The tool arara is well documented

A small blog post by Joseph write is also available: arara: Making LaTeX files your way

Based on the given links you can compile your .tex file with arara filename. The compiling process can be specified by rules inside your .tex. For example a simple |pdflatex| looks as follows:

% arara: pdflatex

arara offers the possibility to set up options directly inside the |.tex| file.

Give arara a try! ;-)

Answered by Marco Daniel on February 20, 2021

You can try this .NET library 'KaTeX Sharp Runner'. It wraps the JavaScript KaTeX library for use in .NET code and renders HTML. If HTML is not your target output then hopefully that can be parsed into the format you need.

https://github.com/appsoftwareltd/katex-sharp-runner

Answered by gb2d on February 20, 2021

I wrote a background worker to catch the stuff that gets written to the pdflatex log file. Then I check each written message for the word "Rerun". If that occurs, a rerun must be done. I think I did not entirely find this out myself, but found the principle somewhere.

I cut and pasted a bit quickly, but surely you can see what it does.

private void BackgroundWorker_DoWork(object worker, DoWorkEventArgs e)
{
    var latexCompiler = new Process()
    {
        StartInfo = new ProcessStartInfo()
        {
            FileName =
                Config.LatexCompiler,
            WindowStyle = ProcessWindowStyle.Minimized,
            Arguments = Config.LatexArgs
                        + $" -job-name={run} --recorder -record-package-usages=MyApp.packages -output-directory="{ProgramOutputFolder}{ProjectName}""
                        + $" "{ProgramOutputFolder}{LatexOut}"",
            RedirectStandardOutput = true,
            RedirectStandardError = false,
            UseShellExecute = false,
            CreateNoWindow = true
        }
    };
    latexCompiler.OutputDataReceived += 
        (sender, ev) =>
        {
            if (!doRerun && !string.IsNullOrEmpty(ev.Data))
            {
                if (ev.Data.Contains("Rerun"))
                {
                    doRerun = true;
                    latexCompiler.CancelOutputRead();
                }
            }
        };

    rerun = 0;
    var prgrs = progress;

    do
    {
        doRerun = false;
        latexCompiler.Start();
        latexCompiler.BeginOutputReadLine();
        latexCompiler.WaitForExit();
        latexCompiler.CancelOutputRead();
      
        rerun++; // may need more than 1 run to resolve references etc.

        ((BackgroundWorker) worker).ReportProgress(progress += (100 - prgrs) / 3);
    } while (rerun < 5 && doRerun); // max 5 runs for safety.

    // report progress to the main thread:
    ((BackgroundWorker) worker).ReportProgress(progress = 100);
  
    // sleep, so the "ready" message is displayed for some time:
    Thread.Sleep(2000);

    // now we may exit, so the "ready" message can be removed.
    // The PDF will be on disk.
    e.Result = latexCompiler.ExitCode; // should be 0.
}

Answered by Erik on February 20, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP