Latex rendering via KLFBackend

Render LaTeX code via KLFBackend library

23/5/2014 Update: TinyTex’s Qt 5 port of KLFBackend has been updated to 3.2.8 [bitbucket.org] (fixes a bug with ghostscript) thanks to retux

What is KLFBackend?

KLFBackend is a C++ library originally developed by Philippe Faist to render LaTeX code in KLatexFormula:
http://klatexformula.sourceforge.net/doc/apidoc/klfbackend/html/

Getting it

TinyTex

The source code of KLFBackend is inside KLatexFormula from here http://klatexformula.sourceforge.net/download/ (as of this writing the current stable source version is 3.2.6). However, the library only works with Qt 4. I have a modified version of the library that allows it to support both Qt 4 and Qt 5 plus certain features which I’ll get to. For this tutorial to work you’ll need this modified version. Download it from here [bitbucket.org] . It comes with an simple example(TinyTex [bitbucket.org] ). Note that TinyTex only works with Qt 5. To make it work with Qt 4 you would need to rebuild it and change some includes.

Requirements

Library is tested to work on Windows 7 and Linux Mint 14 32bit, in Qt 4 and Qt 5. However, for windows users you can only use MinGW version of Qt. MSCV2010 compiler will produce an error should you try to build the library or run programs with it.

To get latex working: In linux you’ll nees to install texlive and ghostscript from synaptic package manager. In windows install Miktex [miktex.org] – just the basic Miktex would work.

Building the library

Open the KLFBackend pro file located in the latextest folder that you’ve downloaded and build the project. Now go to the “build-KLFBackend” directory where you have build your library and you should see KLFBackend.dll(windows) or libKLFBackend.so, libKLFBackend.so.1, libKLFBackend.so.1.0, libKLFBackend.so.1.0.0(linux). You can freely choose to discard the rest of the files cause they’re taking up space.

Using KLFBackend in your Qt app.

Open TinyTex.pro to access the example(alternatively you can refer to KLatexFormula’s source code as most of the code in this mini example originated from there).

in your pro file be sure you have

  1. DEFINES += KLF_SRC_BUILD

or it will output a bunch of warnings(though the app still runs happily). You will also need the following:

  1. # note that in unix(linux) systems library names are case sensitive
  2. win32:CONFIG(release, debug|release): LIBS += -L$$PWD/build-KLFBackend/ -lklfbackend
  3. else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/build-KLFBackend/ -lklfbackend
  4. else:unix: LIBS += -L$$PWD/build-KLFBackend/ -lKLFBackend
  5.  
  6. INCLUDEPATH += $$PWD/klfbackend

this will add KLFBackend library(LIB) when running app, and import all the headers(INCLUDEPATH) in KLFBackend into your project. Note that $$PWD refers to the project working directory.

mainwindow.h

  1. #ifndef MAINWINDOW_H
  2. #define MAINWINDOW_H
  3.  
  4. #include <QMainWindow>
  5. #include <QClipboard>
  6. #include "klfbackend.h"
  7. #include "klfpreviewbuilderthread.h"
  8.  
  9. namespace Ui {
  10. class MainWindow;
  11. }
  12.  
  13. class MainWindow : public QMainWindow
  14. {
  15.     Q_OBJECT
  16.    
  17. public:
  18.     explicit MainWindow(QWidget *parent = 0);
  19.     ~MainWindow();
  20.  
  21. private slots:
  22.     void updatePreviewBuilderThreadInput();
  23.     void showRealTimePreview(const QImage& preview, bool latexerror);
  24.     void copyToClipboard();
  25.  
  26. private:
  27.     Ui::MainWindow *ui;
  28.     KLFPreviewBuilderThread *mPreviewBuilderThread;
  29.     KLFBackend::klfInput input;
  30.     KLFBackend::klfSettings settings;
  31.     KLFBackend::klfOutput output;
  32.     QClipboard *clipboard;
  33.     QPixmap pixmap;
  34.  
  35. };
  36.  
  37. #endif // MAINWINDOW_H

KLFBackend::klfInput object stores input like preamble, mathmode, latex code and size. KLFBackend::klfSettings stores settings like the directories of your latex executable. the KLFPreviewBuilderThread object is used to render your equations by feeding it with KLFBackend::klfInput and KLFBackend::klfSettings. We will save the image as a QPixmap to the clipboard although it’s output a QImage, because for some reason copying the QImage of the equation gives really choppy results.

Below is the implementation file:

mainwindow.cpp

  1. #include "mainwindow.h"
  2. #include "ui_mainwindow.h"
  3. #include "klfbackend.h"
  4. #include <QDebug>
  5.  
  6.  
  7. MainWindow::MainWindow(QWidget *parent) :
  8.  
  9.     QMainWindow(parent),
  10.     ui(new Ui::MainWindow)
  11. {
  12.  
  13.     ui->setupUi(this);
  14.     input.mathmode = "\\[ ... \\]";
  15.     input.dpi = 200;
  16.     ui->label->setMinimumHeight(input.dpi/2);
  17.  
  18. //    input.bg_color = qRgba(225, 225, 225, 225);
  19.     input.preamble = QString("\\usepackage{amssymb,amsmath,mathrsfs}");
  20.     if(!KLFBackend::detectSettings(&settings)) {
  21.         qDebug() << "unable to find LaTeX in default directories.";
  22.     } else {
  23.         qDebug() << "default settings working!";
  24.     }
  25.  
  26.     // setup variables:
  27.     mPreviewBuilderThread = new KLFPreviewBuilderThread(this, input, settings);
  28.  
  29.     // make connections
  30.     connect(ui->plainTextEdit, SIGNAL(textChanged()), this,
  31.         SLOT(updatePreviewBuilderThreadInput()), Qt::QueuedConnection);
  32.     connect(mPreviewBuilderThread, SIGNAL(previewAvailable(const QImage&, bool)),
  33.         this, SLOT(showRealTimePreview(const QImage&, bool)), Qt::QueuedConnection);
  34.     connect(ui->clipBtn, SIGNAL(clicked()), this, SLOT(copyToClipboard()));
  35.  
  36.     ui->plainTextEdit->setFocus();
  37.     ui->statusBar->showMessage("Waiting for input...");
  38. }
  39.  
  40. void MainWindow::copyToClipboard()
  41. {
  42.     clipboard->setPixmap(pixmap);
  43.     ui->statusBar->showMessage("your image has been copied to clipboard");
  44.  
  45. }
  46.  
  47. MainWindow::~MainWindow()
  48. {
  49.     delete mPreviewBuilderThread;
  50.     delete ui;
  51. }
  52.  
  53. void MainWindow::updatePreviewBuilderThreadInput()
  54. {
  55.     // in linux, I need to reinstate the preamble when rendering. No idea why.
  56.     input.preamble = QString("\\usepackage{amssymb,amsmath}");
  57.     input.latex = ui->plainTextEdit->toPlainText();
  58.     if(mPreviewBuilderThread->inputChanged(input)) {
  59.         qDebug() << "input changed. Render...";
  60.         ui->statusBar->showMessage("Input changed. Render...");
  61.         mPreviewBuilderThread->start();
  62.   }
  63. }
  64.  
  65. void MainWindow::showRealTimePreview(const QImage& preview, bool latexerror)
  66. {
  67.     if (latexerror) {
  68.       ui->statusBar->showMessage("Unable to render your equation. Please double check.");
  69.     } else {
  70.       ui->statusBar->showMessage("render is succesful!! :D");
  71.       pixmap = QPixmap::fromImage(preview);
  72.       ui->label->setPixmap(pixmap);
  73.       ui->label->adjustSize();
  74.     }
  75. }

Although the LaTeX equation itself is

  1. \usepackage{amssymb,amsmath,mathrsfs}

I have to write it as
  1. \\usepackage{amssymb,amsmath,mathrsfs}

because in C++ strings, the backslash is used to indicate some commands(e.g \n is newline). So \\ is read as \ by the program.

KLFBackend::detectSettings is a useful function to automatically search your the required executables. However should you need to set it manually you’ll do is as so:

  1. #if defined(Q_OS_WIN)
  2.     settings.tempdir = QDir::tempPath();
  3.     settings.dvipsexec = "D:/Program Files/MiKTeX 2.9/miktex/bin/dvips.exe";
  4.     settings.gsexec = "D:/Program Files/MiKTeX 2.9/miktex/bin/mgs.exe";
  5.     settings.latexexec = "D:/Program Files/MiKTeX 2.9/miktex/bin/latex.exe";
  6. #elif defined(Q_OS_LINUX)
  7.     settings.tempdir = QDir::tempPath();
  8.     settings.dvipsexec = "/usr/bin/dvips";
  9.     settings.gsexec = "/usr/bin/gs";
  10.     settings.latexexec = "/usr/bin/latex";
  11. #endif

note that to use QDir you need to include QDir in your implementation file.

Where to go from here:

the KLFBackend API documentation [klatexformula.sourceforge.net]

The development version of KLatexFormula adds extra features to KLFBackend. According to the author…

Performance wise the older version might be slightly better, if only because it’s more lightweight. The new version no longer depends on “epstopdf” for generating pdf, and uses directly ghostscript directly. It has much better detection of bounding boxes, can also generate SVG if the ghostscript driver is installed, and has a better interface for different fonts sizes and vector scaling. It stores also meta-information in PDF files too. You can also invoke user scripts that change the way the formula is rendered (for example for feynman diagrams with feynmf). Maybe more and I’m forgetting. So basically, it has more features, but if you were fine with 3.2, you can stick to that.
KLFBackend now needs some tools that are defined in klftools (e.g. data serializing, and utilities for the user scripts), and so depends on that library. And it wasn’t logical to have klftools depend on klfbackend.

Note that you will need to install cmake to configure it, and that it currently only works with Qt 4, and that I can’t get to build in windows.

  1.   > svn checkout https://klatexformula.svn.sourceforge.net/svnroot/klatexformula/trunk/klatexformula
  2.   > cd klatexformula/
  3.   > mkdir build && cd build/
  4.   > cmake .. -DKLF_BUILD_GUI=off -DKLF_LIBKLFTOOLS_STATIC=on -DKLF_LIBKLFBACKEND_STATIC=on \
  5.              -DKLF_BUILD_KTEXTEDITORPLUGIN=off -DCMAKE_INSTALL_PREFIX=/usr/local

…and there you have it! Qt and Latex! If you encounter any problems or have any questions/feedback, please add a comment at my forum post: http://qt-project.org/forums/viewthread/26657/

Categories: