在 Microsoft C#.net 中编译 Tex

在 Microsoft C#.net 中编译 Tex

我知道有一些命令行应用程序(例如 pdflatex)可以帮助我从 C#.Net 编译 LaTex 文档(使用 System.Diagnostics.Process 类)。

我想知道编译 TeX 文档的最佳命令行工具是什么?

答案1

所以我们又见面了,C#。:)

给出答案并不容易,但我会尝试。

大多数情况下,我们得到的文档很复杂,可能需要多次运行才能达到稳定状态。因此,您可能需要多次运行才能获得正确的输出。

如果您坚持使用 之类的引擎pdflatex,您可能需要确保对其和/或其他辅助程序(例如makeindex)的调用次数最少。这可能是一个负担,特别是当您事先不知道步骤时。

latexmk和等工具rubber的创建是为了简化编译过程,即尝试猜测您的文档是否需要再次运行,或调用其他程序。如果您的文档永远不会达到稳定状态,它们甚至会放弃编译。因此,它们可能会让您的生活更轻松。但当然,我们也有人类因素。:)这些工具可能无法按预期工作(例如,rubber无法识别biber),因此您可能需要使用配置文件或指令来调整这些工具。

现在,我有了arara,这是我的一个不起眼的项目(抱歉这么无耻地自我推销)。这个工具与latexmk和不同rubber,因为它只会执行您告诉它的内容。没有猜测,没有辅助文件解析或其他很酷的东西。通过向源代码添加指令(例如,% arara: pdflatex: { shell: yes }arara将根据明确定义的规则将这些指令扩展为命令,然后执行它们。如果您真的知道引擎内部发生了什么,它可能会很强大,否则可能很难理解这个概念。由于 Marco Daniel(项目合作者)和 Joseph Wright 的巨大贡献,该项目仍在大力开发中。

现在回到你的问题。:)

如果您能告诉我们您的 C# 应用程序,那就太好了。有时,根据您的需求,可以使用更可靠的解决方案。例如,宽恕者的回答是一种有趣的调用方式pdflatex。如果您需要更通用的配置,可以使用以下几行中的某些内容:

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.");
            }
        }
    }
}

这个想法是有一个基于 XML 的配置文件,它将决定如何调用命令。示例配置文件:

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

考虑以下tex文件:

\documentclass{article}

\begin{document}

Hello world!

\end{document}

现在,我可以使用以下命令运行我的代码MyProgram.exe hello config.xml

程序输出

我用单核细胞增多症:)

请注意,我使用了--interaction=nonstopmode作为一个参数。根据您的应用程序的行为方式,使用交互式命令可能会有问题。例如,如果我们tex上面的代码有语法错误,并且没有--interaction=nonstopmodepdflatex则会提示您一条消息并等待您的输入。对于arara我将输出流(stdoutstderr)重定向到单独的线程,但我仍然在努力处理输入流(stdin)。例如,如果程序需要交互,TeXworks 可以提示输入框:

特克斯沃克斯

值得庆幸的是,TeX 程序提供了退出状态,因此您可以检查运行是否成功。如果您有一个非常复杂的文档,也许创建一个工作线程并让它处理调用是明智之举,避免阻塞您的应用程序(David 在聊天室中提到 Frank 的书大约需要 20 分钟才能编译完成)。:)

那么,哪种工具最好呢?我完全不知道。:)我的想法是:如果你想更好地控制你的工作流程,请坚持使用原始的命令行程序。现在,灵活性和易用性是这里的关键词,不妨latexmkrubber试。:)

答案2

步骤 1:创建批处理文件

为了避免重新编译 C# 源代码以与要使用的 (La)TeX 编译器同步,创建批处理文件会很方便。批处理文件在后台执行 (La)TeX 编译器。将批处理文件保存在任何位置,并将 PATH 系统变量设置为批处理位置。

最简单的批次可能如下

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

步骤 2:创建 C# 代码并编译

最简单的例子如下:

   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();
         }
     }

使用 C# 编译器进行编译以获取MyApp.exe示例。

第 3 步:完成,您就可以尝试了

MyApp.exe需要一个参数,即没有扩展名的 TeX 输入文件名。

从命令提示符下,执行MyApp.exe TeXInputFileNameWithNoExtension

答案3

我想提供一个使用该工具的答案arara

该工具arara有详尽的文档

您还可以查看 Joseph 撰写的一篇小博客文章: arara:按照自己的方式制作 LaTeX 文件

根据给定的链接,您可以.tex使用 编译文件arara filename。编译过程可以通过 中的规则指定.tex。例如,一个简单的 |pdflatex| 如下所示:

% arara: pdflatex

arara提供了直接在 |.tex| 文件内设置选项的可能性。

试一下arara! ;-)

答案4

我编写了一个后台工作程序来捕获写入 pdflatex 日志文件的内容。然后我检查每条写入的消息是否有“重新运行”一词。如果出现这种情况,则必须重新运行。我想我自己并没有完全发现这一点,但在某个地方找到了原理。

我快速地剪切并粘贴了一些内容,但您肯定可以看到它的作用。

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.
}

相关内容