Skip to content

digineo/texd-ruby

Repository files navigation

texd

texd is a Ruby client for the texd web service.

It leverages ActionView's template rendering mechanism to compile .tex templates to PDF documents. The primary use case is to render documents in background jobs.

Installation

You need to meet the following requirements for this gem to work:

  • Ruby 2.7 or later1
  • Rails 6.0 or later

Older versions of Ruby and Rails may work, but this is not guaranteed.

(Please note that Rails 7.2 requires Ruby 3.1+2, and the current main branch, i.e. Rails 8.0-to-be, requires Ruby 3.2+3.)

Install the gem and add to the application's Gemfile by executing:

$ bundle add texd

Configuration

Before you can use texd, you need to tell it where your instance is located.

By default, this gem reads the TEXD_ENDPOINT environment variable and falls back to http://localhost:2201/render, should it be empty.

If this does not match your environment, you need can reconfigure texd:

Texd.configure do |config|
  config.endpoint = ENV.fetch("TEXD_ENDPOINT", "http://localhost:2201/")
end
Full default config (click to open)
Texd.configure do |config|
  config.endpoint       = ENV.fetch("TEXD_ENDPOINT", "http://localhost:2201/")
  config.open_timeout   = ENV.fetch("TEXD_OPEN_TIMEOUT", 60)
  config.read_timeout   = ENV.fetch("TEXD_READ_TIMEOUT", 180)
  config.write_timeout  = ENV.fetch("TEXD_WRITE_TIMEOUT", 60)
  config.error_format   = ENV.fetch("TEXD_ERRORS", "full")
  config.error_handler  = ENV.fetch("TEXD_ERROR_HANDLER", "raise")
  config.tex_engine     = ENV["TEXD_ENGINE"]
  config.tex_image      = ENV["TEXD_IMAGE"]
  config.helpers        = []
  config.lookup_paths   = []
  config.lookup_paths   = [] # Rails.root.join("app/tex") is always prepended
  config.ref_cache_size = 128
end

For development environments, you can start the texd server like so (requires Docker and about 4GB of disk space for the included TeX live installation):

$ docker run --rm -d -p localhost:2201:2201 --name texd-dev digineogmbh/texd

Head to the texd project page to learn about other installation methods.

Usage

First, create a few files:

app/views/layouts/application.tex.erb

This is the default layout. Here, you should define a \documentclass and use yield. In this example, we're using ERB (Erubi) to include dynamic content into a .tex file.

\documentclass{article}
\usepackage{graphicx}
<%= content_for :preamble %>

\begin{document}
<%= yield %>
\end{document}
app/views/document/doc.tex.erb

In document/doc.tex, we're specifying some stuff for the preamble, render a partial, and add content for the document:

<% content_for :preamble do %>
\usepackage{blindtext}

\title{Demo document}
\date{\today}
\author{<%= user.full_name %>}
<% end %>

<%= render partial: "document/title_page" %>

\Blinddocument

OK, that wasn't true. We're leveraging the blindtext package to add content for us :)

The user variable is passed as local method to Texd.render (see below).

app/views/document/_title_page.tex.erb

This partial embeds an image and creates the title page.

\begin{center}
  \includegraphics[width=0.5\linewidth]{<%= texd_attach "logo.png" %>}
\end{center}

\titlepage

With texd_attach, we're referencing a file outside ActionView's lookup paths, but in Texd's lookup paths (RAILS_ROOT/app/tex by default).

You can use this directory to store and deploy static assets.

Please be aware, that attachments will be renamed (att00123.png) in the POST body, and att00123.png will be returned from texd_attach. You can skip the renaming, if you want/need to:

% attaches RAILS_ROOT/app/tex/logo.png, and inserts "logo.png":
<%= texd_attach "logo.png", rename: false %>

% attaches RAILS_ROOT/app/tex/logo.png, and inserts "assets/logo.png":
<%= texd_attach "logo.png", rename: "assets/logo.png" %>

% attaches RAILS_ROOT/app/tex/common.tex, and inserts "att00042" (or similar):
<%= texd_attach "common.tex", without_extension: true %>
app/tex/logo.png

(Imagine your logo here.)

With those files in place, you can create a PDF document:

begin
  blob = Texd.render(template: "documents/doc", locals: {
    user: User.find(1)
  })
  Rails.root.join("tmp/doc.pdf").open("wb") { |f|
    f.write blob
  }
rescue Texd::Client::QueueError => err
  # texd server is busy, please retry in a moment
rescue Texd::Client::InputError => err
  # file input processing failed, maybe some file names were invalid
rescue Texd::Client::CompilationError => err
  # compilation failed
  if err.logs
    # TeX compiler logs, only available if Texd.config.error_format
    # is "full" or "condensed"
  end
end

All errors inherit from Texd::Client::RenderError and should have a details attribute (a Hash) containing the actual error returned from the server.

Global error reporting

texd can be configured with external error reporting, like Sentry.

This example sends the LaTeX compilation log and compiled main input .tex file to Sentry:

Texd.configure do |config|
  config.error_handler = ->(err, doc) {
    Sentry.set_context "texd", {
      details: err.details, # if config.error_format == "json"
      logs:    err.logs,    # otherwise
    }.compact
    Sentry.capture_exception(err)

    raise err # re-raise, so that your code can decide further actions
  }
end

config.error_handler must respond to call, and receives the error (an instance of Texd::Client::CompilationError) and the document context (an instance of Texd::Document::Compilation).

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in lib/texd/version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and the created tag, and push the .gem file to rubygems.org.

You may want to run a texd server instance locally. This is easiest done by calling make texd-docker (which pulls and runs the ghcr.io/digineo/texd Docker image). If you need to develop/test against the bleeding edge, you can clone and run texd from source:

$ cd ~/code/github.com/digineo
$ git clone [email protected]:digineo/texd
$ cd texd
$ mkdir -p tmp/refs
$ make run-container EXTRA_RUN_ARGS='--reference-store dir://./tmp/refs'

Note: In order to run the tests against the latest rails/main commit, you need to have Ruby 3.1+ installed. To run the tests against all released Rails versions, Ruby 2.7 currently suffices.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/digineo/texd.

License

This gem is open source under the terms of the MIT license. It is based heavily on the rails-latex gem.

  • © 2022, Dominik Menke
  • © 2010-2015, Geoff Jacobsen, Jan Baier and contributors

Footnotes

  1. We're currently tracking the Ruby version shipped with Ubuntu Focal (20.04 LTS). This may jump to Ubuntu Noble (24.04 LTS) and Ruby 3.1 in the near future.

  2. See commit 6ba2fdb and PR #50491 in the Rails repository.

  3. See commit c7b9bb1 in the Rails repository.