{"id":15203929,"url":"https://github.com/digineo/texd","last_synced_at":"2025-10-27T08:04:35.691Z","repository":{"id":38257379,"uuid":"469808898","full_name":"digineo/texd","owner":"digineo","description":"texd wraps TeX in a web API","archived":false,"fork":false,"pushed_at":"2024-09-23T10:30:08.000Z","size":1039,"stargazers_count":8,"open_issues_count":5,"forks_count":1,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-09-29T05:04:12.190Z","etag":null,"topics":["docker","go","latex","microservice","pdf-generation","tex","texd","text-processing"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/digineo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-03-14T16:09:56.000Z","updated_at":"2024-09-23T10:30:12.000Z","dependencies_parsed_at":"2023-11-14T09:29:40.647Z","dependency_job_id":"da6ab45d-abf4-4e79-a86b-a1a337c85a77","html_url":"https://github.com/digineo/texd","commit_stats":{"total_commits":276,"total_committers":5,"mean_commits":55.2,"dds":0.4528985507246377,"last_synced_commit":"68544a605984d4e70dd9eb08b0c7e8acac10b92c"},"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/digineo%2Ftexd","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/digineo%2Ftexd/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/digineo%2Ftexd/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/digineo%2Ftexd/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/digineo","download_url":"https://codeload.github.com/digineo/texd/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":219858396,"owners_count":16556045,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["docker","go","latex","microservice","pdf-generation","tex","texd","text-processing"],"created_at":"2024-09-28T05:04:15.276Z","updated_at":"2025-10-27T08:04:35.593Z","avatar_url":"https://github.com/digineo.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# texd\n\n\n[![Go Reference](https://pkg.go.dev/badge/github.com/digineo/texd.svg)](https://pkg.go.dev/github.com/digineo/texd)\n[![Test, Lint, Release](https://github.com/digineo/texd/actions/workflows/test.yml/badge.svg)](https://github.com/digineo/texd/actions/workflows/test.yml)\n[![Coverage](https://codecov.io/gh/digineo/texd/branch/master/graph/badge.svg)](https://codecov.io/gh/digineo/texd)\n[![Go Report Card](https://goreportcard.com/badge/github.com/digineo/texd)](https://goreportcard.com/report/github.com/digineo/texd)\n[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/digineo/texd/master/LICENSE)\n\n\ntexd is a TeXaS (TeX as (a) service) solution, designed for your internal document generation, i.e.\non your own servers.\n\nIt features:\n\n- ad-hoc compilation,\n- a simple HTTP API access,\n- pluggable TeX distributions,\n- ...\n\nThe idea behind texd is to provide a single, network-reachable compiler; sending a `.tex` file via\nHTTP POST should then simply generate a PDF document. You won't need a TeX distribution on the\nclient system, just an HTTP client.\n\nSeveral technologies make scaling in any dimension relatively easy:\n\n- the TeX distribution is provided through Docker containers (this also allows using multiple\n  distributions simultaneously)\n- using HTTP enables redundancy and/or load balancing without much effort\n\n## Operation Modes\n\ntexd is designed to be run/deployed in 2½ different ways:\n\n### Local Mode\n\nThis is primarily for (local) testing and development. You download and start texd locally, provide\na TeX distribution, and texd will compile documents on your host machine.\n\nTo start texd in this mode, execute:\n\n```console\n$ texd\n```\n\n### Ephemeral Containers\n\nHere, you still download and run texd locally, but document rendering will happen in an short-lived\nDocker container, using a specific Docker image (`registry.gitlab.com/islandoftex/images/texlive:latest`\nwill do just fine, but you could easily build a smaller one using e.g. a Debian base image).\n\nTo run in container mode, run:\n\n```console\n$ texd registry.gitlab.com/islandoftex/images/texlive:latest\n```\n\nThis will pull the specified image, if it doesn't exist yet. Note that you need to give texd\naccess to `/var/run/docker.sock`, in order to allow it to pull the image and create containers.\n\nYou may provide multiple image names and switch on a per-request basis (see HTTP API below). In\nthis case, the first image is used as default image:\n\n```console\n$ texd \\\n    registry.gitlab.com/islandoftex/images/texlive:latest \\\n    registry.gitlab.com/islandoftex/images/texlive:TL2014-historic \\\n    ghcr.io/yourcompany/texlive-prod\n```\n\n### CI Service\n\nThis runs texd within a Docker container, and is primarily targeted for CI pipelines, but can be a\nviable alternative to the local mode. In fact, this mode is functionally equivalent to the\n*local mode*, with the one exception (texd being packaged and started in a container).\n\nTo run texd as Docker service, use this command:\n\n```console\n$ docker run --rm -t -p localhost:2201:2201 ghcr.io/digineo/texd:latest\n```\n\nThe image `ghcr.io/digineo/texd:latest` is based on Debian Bookworm with\nsome texlive packages installed from the Debian repositories (see this\n[`Dockerfile`](./.github/Dockerfile.base) for the current list). This also\nmeans that the contained TeX distribution is [TeXlive 2022][].\n\n\u003e **Note:**\n\u003e\n\u003e If you want/need to run the container with non-root user ID (e.g. when\n\u003e started with sth. like `docker run --user=$(id -un):$(id -gn)`), make\n\u003e sure to also setup a `HOME` directory. Otherwise you'll likely encounter\n\u003e FontConfig caching errors.\n\u003e\n\u003e \u003cdetails\u003e\n\u003e   \u003csummary\u003eExample\u003c/summary\u003e\n\u003e\n\u003e   ```console\n\u003e   $ mkdir -p texd/{home,jobs}\n\u003e   $ docker run --rm -t  \\\n\u003e       -p localhost:2201:2201 \\\n\u003e       --user $(id -un):$(id -gn) \\\n\u003e       -e HOME=/texd/home \\\n\u003e       -v $(pwd)/texd/home:/texd/home \\\n\u003e       -v $(pwd)/texd/jobs:/texd/jobs \\\n\u003e       ghcr.io/digineo/texd:latest \\\n\u003e           --job-directory /texd/jobs\n\u003e   ```\n\u003e \u003c/details\u003e\n\nWhen using Gitlab CI, you can add this snippet to your `.gitlab-ci.yml`:\n\n```yml\nservices:\n  - name: ghcr.io/digineo/texd:latest\n    alias: texd\n\nvariables:\n  # reconfigure test application to use this endpoint\n  # (this is specific to your application!)\n  TEXD_ENDPOINT: http://texd:2201/render\n```\n\n[TeXlive 2022]: https://packages.debian.org/bullseye/texlive\n\n## CLI Options\n\nCalling texd with options works in any mode; these commands are equivalent:\n\n```console\n$ texd -h\n$ texd tregistry.gitlab.com/islandoftex/images/texlive:latestest -h\n$ docker run --rm -t ghcr.io/digineo/texd:latest -h\n```\n\n- `--help`, `-h`\n\n  Prints a short option listing and exits.\n\n- `--version`, `-v`\n\n  Prints version information and exits.\n\n- `--listen-address=ADDR`, `-b ADDR` (Default: `:2201`)\n\n  Specifies host address (optional) and port number for the HTTP API to bind to. Valid values are,\n  among others:\n\n  - `:2201` (bind to all addresses on port 2201)\n  - `localhost:2201` (bind only to localhost on port 2201)\n  - `[fe80::dead:c0ff:fe42:beef%eth0]:2201` (bind to a link-local IPv6 address on a specific\n    interface)\n\n- `--tex-engine=ENGINE`, `-X ENGINE` (Default: `xelatex`)\n\n  TeX engine used to compile documents. Can be overridden on a per-request basis (see HTTP API\n  below). Supported engines are `xelatex`, `lualatex`, and `pdflatex`.\n\n- `--compile-timeout=DURATION`, `-t DURATION` (Default: `1m`)\n\n  Maximum duration for a document rendering process before it is killed by texd. The value must be\n  acceptable by Go's `ParseDuruation` function.\n\n- `--parallel-jobs=NUM`, `-P NUM` (Default: number of cores)\n\n  Concurrency level. PDF rendering is inherently single threaded, so limiting the document\n  processing to the number of cores is a good start.\n\n- `--queue-wait=DURATION`, `-w DURATION` (Default: `10s`)\n\n  Time to wait in queue before aborting. When \u003c= 0, clients will immediately receive a \"full queue\"\n  response.\n\n- `--job-directory=PATH`, `-D PATH` (Default: OS temp directory)\n\n  Place to put job sub directories in. The path must exist and it must be writable.\n\n- `--pull` (Default: omitted)\n\n  Always pulls Docker images. By default, images are only pulled when they don't exist locally.\n\n  This has no effect when no image tags are given to the command line.\n\n\u003e Note: This option listing might be outdated. Run `texd --help` to get the up-to-date listing.\n\n## HTTP API\n\n### Render a document\n\nTo create a PDF document from an input `.tex` file, send a HTTP POST to the `/render` endpoint.\nYou may encode the payload as `multipart/form-data` or `application/x-www-form-encoded`, however\nthe latter is not recommended.\n\nAssuming, you have a `input.tex` in the current directory, you can issue the following command\nto send that file to your texd instance, and save the result in a file named `output.pdf`:\n\n```console\n$ curl -X POST \\\n    -F \"input.tex=\u003cinput.tex\" \\\n    -o \"output.pdf\" \\\n    \"http://localhost:2201/render\"\n```\n\nYou can send multiple files (even in sub directories) as well:\n\n```console\n$ curl -X POST \\\n    -F \"cv.tex=\u003ccv.tex\" \\\n    -F \"chapters/introduction.tex=\u003cchapters/introduction.tex\" \\\n    -F \"logo.pdf=\u003clogo.pdf\" \\\n    -o \"vita.pdf\" \\\n    \"http://localhost:2201/render?input=cv.tex\"\n```\n\nWhen sending multiple files, you should specify which one is the main input file (usually the one\ncontaining `\\documentclass`), using the `input=` query parameter. If you omit this parameter, texd\nwill try to guess the input file.\n\nPlease note that file names will be normalized, and files pointing outside the root directory\nwill be discarded entirely (i.e. `../input.tex` is NOT a valid file name). You can't do this:\n\n```console\n$ curl -X POST \\\n    -F \"../input.tex=\u003cinput.tex\" \\\n    -o \"output.pdf\" \\\n    \"http://localhost:2201/render\"\n```\n\nHowever, this is perfectly fine:\n\n```console\n$ curl -X POST \\\n    -F \"input.tex=\u003c../input.tex\" \\\n    -o \"output.pdf\" \\\n    \"http://localhost:2201/render\"\n```\n\n\u003cdetails\u003e\u003csummary\u003eGuessing the input file (click to show details)\u003c/summary\u003e\n\n- only filenames starting with alphanumeric character and ending in `.tex` are considered\n  (`foo.tex`, `00-intro.tex` will be considered, but not `_appendix.tex`, `figure.png`)\n- files in sub directories are ignored (e.g. `chapters/a.tex`)\n- if only one file in the root directory remains, it is taken as main input\n  - otherwise search for a file containing a line starting with:\n    - either `%!texd` at the beginning of the file\n    - or `\\documentclass` somewhere in the first KiB\n  - if no match, consider (in order):\n    - `input.tex`\n    - `main.tex`\n    - `document.tex`\n\n\u003c/details\u003e\n\nIf no main input file can be determined, texd will abort with an error.\n\n#### URL Parameters\n\n- `input=\u003cfilename\u003e` - instructs texd to skip guessing main input file and use the specified one.\n  The filename must be present in the body.\n\n- `engine=\u003cvalue\u003e` - specifies which TeX engine to run. Supported engines are:\n\n  - `xelatex` (default)\n  - `lualatex`\n  - `pdflatex`\n\n  Note that the default can be changed with a CLI option (e.g. `--tex-engine=lualatex`).\n\n- `image=\u003cimagename\u003e` - selects Docker image for document processing.\n\n  This is only available in *ephemeral container* mode. The image name must match the ones listed\n  in the texd command invocation, i.e. you can't select arbitrary images.\n\n  If you provide an unknown image name, you will receive a 404 Not Found response. In *local* and\n  *CI service* mode, this parameter only logged, but will otherwise be ignored.\n\n- `errors=\u003cdetail level\u003e` - tries to retrieve the compilation log, in case of compilation errors.\n  Acceptable detail levels are:\n\n  - *empty* (or `errors` completely absent), to return a JSON description (default)\n  - `condensed`, to return only the TeX error message from the log file\n  - `full`, to return the full log file as `text/plain` response\n\n  The \"condensed\" form extracts only the lines from the error log which start with a `!`. Due to\n  the way TeX works, these lines may not paint the full picture, as TeX's log lines generally don't\n  exceed a certain line length, and wrapped lines won't get another `!` prefix.\n\n  Note that this parameter changes the response content to a plain text file if you select `full`\n  or `condensed`, and not a JSON response as in all other cases.\n\n#### Successful response\n\nIf compilation succeeds, you'll receive a status 200 OK, with content type `application/pdf`, and\nthe PDF file as response body.\n\n```http\nHTTP/1.1 200 OK\nContent-Type: application/pdf\nContent-Length: 1234\n\n%PDF/1.5...\n```\n\n#### Failure responses\n\nIf the request was accepted, but could not complete due to errors, you will by default receive a 422\nUnprocessable Entity response with content type `application/json`, and an error description in\nJSON format:\n\n```http\nHTTP/1.1 422 Unprocessable Entity\nContent-Type: application/json\nContent-Length: 154\n\n{\n  \"error\": \"latexmk call failed with status 1\",\n  \"category\": \"compilation\",\n  \"output\": \"[truncated output log]\"\n}\n```\n\nThe fields `error` and `category` represent a short error description and an error category,\nrespectively.\n\nPossible, known error categories are currently:\n\n- *input* - one or more files are invalid (e.g. file was discarded after path normalization),\n  or the main input file could not be determined.\n\n- *compilation* - `latexmk` exited with an error (likely due to invalid or missing input files).\n\n- *queue* - texd won't accept new render jobs, if its internal queue is at capacity. In this case\n  wait for a few moments to give texd a chance to catch up and then try again.\n\n- *reference* - texd could not find the provided reference store entries. The missing references\n  are listed in the response; you need to repeat the request with those files included.\n\nAdditional fields, like `log` for compilation failures, might be present.\n\n\u003e Note: The JSON response is pretty-printed only for this README. Expect the actual response to\n\u003e be minified.\n\nIf you set `errors=full`, you may receive a plain text file with the compilation log:\n\n\u003cdetails\u003e\u003csummary\u003eShow response (click to open)\u003c/summary\u003e\n\n```http\nHTTP/1.1 422 Unprocessable Entity\nContent-Type: text/plain\nContent-Length: 3156\n\nThis is XeTeX, Version 3.141592653-2.6-0.999993 (TeX Live 2021) (preloaded format=xelatex 2022.3.6)  12 MAR 2022 13:57\nentering extended mode\n restricted \\write18 enabled.\n %\u0026-line parsing enabled.\n... omitting some lines ...\n! LaTeX Error: File `missing.tex' not found.\n\nType X to quit or \u003cRETURN\u003e to proceed,\nor enter new name. (Default extension: tex)\n\nEnter file name:\n! Emergency stop.\n\u003cread *\u003e\n\nl.3 \\input{missing.tex}\n                       ^^M\n*** (cannot \\read from terminal in nonstop modes)\n```\n\n\u003c/details\u003e\n\nFor `errors=condensed`, you'll only receive the lines starting with `!` (with this prefix removed):\n\n\u003cdetails\u003e\u003csummary\u003eShow response (click to open)\u003c/summary\u003e\n\n```http\nHTTP/1.1 422 Unprocessable Entity\nContent-Type: text/plain\nContent-Length: 59\n\nLaTeX Error: File `missing.tex' not found.\nEmergency stop.\n```\n\n\u003c/details\u003e\n\n### Status and Configuration\n\ntexd has a number of runtime configuration knobs and internal state variables, which may or may not\nof interest for API consumers. To receive a current snapshot, query `/status`:\n\n```console\n$ curl -i http://localhost:2201/status\nHTTP/1.1 200 OK\nContent-Type: application/json; charset=utf-8\nContent-Length: 287\n\n{\n  \"version\":        \"0.0.0\",\n  \"mode\":           \"container\",\n  \"images\":         [\"registry.gitlab.com/islandoftex/images/texlive:latest\"],\n  \"timeout\":        60,\n  \"engines\":        [\"xelatex\",\"pdflatex\",\"lualatex\"],\n  \"default_engine\": \"xelatex\",\n  \"queue\": {\n    \"length\":       0,\n    \"capacity\":     16\n  }\n}\n```\n\n### Metrics\n\nFor monitoring, texd provides a Prometheus endpoint at `/metrics`:\n\n```console\n$ curl -i http://localhost:2201/metrics\nContent-Type: text/plain; version=0.0.4; charset=utf-8\n\n...\n```\n\nThe metrics include Go runtime information, as well as texd specific metrics:\n\n| Metric name | Type | Description |\n|:------------|:-----|:------------|\n| `texd_processed_total{status=\"success\"}` | counter | Number of documents processed. |\n| `texd_processed_total{status=\"failure\"}` | counter | Number of rendering errors, including timeouts. |\n| `texd_processed_total{status=\"rejected\"}` | counter | Number of rejected requests, due to full job queue. |\n| `texd_processed_total{status=\"aborted\"}` | counter | Number of aborted requests, usually due to timeouts. |\n| `texd_processing_duration_seconds` | histogram | Overview of processing time per document. |\n| `texd_input_file_size_bytes{type=?}` | histogram | Overview of input file sizes. Type is either \"tex\" (for .tex, .cls, .sty, and similar files), \"asset\" (for images and fonts), \"data\" (for CSV files), or \"other\" (for unknown files) |\n| `texd_output_file_size_bytes` | histogram | Overview of output file sizes. |\n| `texd_job_queue_length` | gauge | Length of rendering queue, i.e. how many documents are waiting for processing. |\n| `texd_job_queue_usage_ratio` | gauge | Queue capacity indicator (0.0 = empty, 1.0 = full). |\n| `texd_info{version=\"0.0.0\", mode=\"local\", ...}` | constant | Various version and configuration information. |\n\n\nMetrics related to processing also have an `engine=?` label indicating the TeX engine (\"xelatex\",\n\"lualatex\", or \"pdflatex\"), and an `image=?` label indicating the Docker image.\n\n### Simple Web UI\n\nYou can try compiling TeX documents directly in your browser: Visit http://localhost:2201, and\nyou'll be greeted with a very basic, but functional UI.\n\nPlease note, that this UI is *not* built to work in every browser. It intentionally does not\nuse fancy build tools. It's just a simple HTML file, built by hand, using Bootstrap 5 for\naesthetics and Vue 3 for interaction. Both Bootstrap and Vue are bundled with texd, so you won't\nneed internet access for this to work.\n\nIf your browser does not support modern features like ES2022 proxies, `Object.entries`, `fetch`,\nand `\u003cobject type=\"application/pdf\" /\u003e` elements, you're out of luck. (Maybe upgrade your browser?)\nAnyway, consider the UI only as demonstrator for the API.\n\n## Reference store\n\ntexd has the ability to reuse previously sent material. This allows you to reduce the amount\nof data you need to transmit with each render request. Following a back-of-the-envelope calculation:\n\n- If you want to generate 1000 documents, each including a font with 400 kB in size, and a logo\n  file with 100 kB in size, you will need to transmit 500 MB of the same two files in total.\n- If you can reuse those two assets, you would only need to transmit them once, and use a reference\n  hash for each subsequent request. The total then reduces 1×500 kB (complete assets for the first\n  request) + 999×100 Byte (50 Byte per reference hash for subsequent requests) = 599.9 kB.\n\nThe feature in texd parlance is called \"reference store\", and you may think of it as a cache. It\nsaves files server-side (e.g. on disk) and retrieves them on-demand, if you request such a file\nreference.\n\nA reference hash is simply the Base64-encoded SHA256 checksum of the file contents, prefixed with\n\"sha256:\". (Canonically, we use the URL-safe alphabet without padding for the Base64 encoder, but\ntexd also accepts the standard alphabet, and padding characters are ignored in both cases.)\n\nTo *use* a file reference, you need to set a special content type in the request, and include the\nreference hash instead of the file contents. The content type must be `application/x.texd; ref=use`.\n\nThe resulting HTTP request should then look something like this:\n\n```http\nPOST /render HTTP/1.1\nContent-Type: multipart/form-data; boundary=boundary\n\n--boundary\nContent-Disposition: form-data; name=input.tex; filename=input.tex\nContent-Type: application/octet-stream\n\n[content of input.tex omitted]\n--boundary\nContent-Disposition: form-data; name=logo.pdf; filename=logo.pdf\nContent-Type: application/x.texd; ref=use\n\nsha256:p5w-x0VQUh2kXyYbbv1ubkc-oZ0z7aZYNjSKVVzaZuo=\n--boundary--\n```\n\nFor unknown reference hashes, texd will respond with an error, and list all unknown references:\n\n```http\nHTTP/1.1 422 Unprocessable Entity\nContent-Type: application/json\n\n{\n  \"category\": \"reference\",\n  \"error\": \"unknown file references\",\n  \"reference\": [\n    \"sha256:p5w-x0VQUh2kXyYbbv1ubkc-oZ0z7aZYNjSKVVzaZuo=\"\n  ]\n}\n```\n\nIn such a case, you can repeat you HTTP request, and change the `ref=use` to `ref=store` for\nmatching documents:\n\n```http\nPOST /render HTTP/1.1\nContent-Type: multipart/form-data; boundary=boundary\n\n--boundary\nContent-Disposition: form-data; name=input.tex; filename=input.tex\nContent-Type: application/octet-stream\n\n[content of input.tex omitted]\n--boundary\nContent-Disposition: form-data; name=logo.pdf; filename=logo.pdf\nContent-Type: application/x.texd; ref=store\n\n[content of logo.pdf omitted]\n--boundary--\n```\n\n### Server configuration\n\nBy default, the reference store is not enabled. You must enable it explicitly, by providing\na command line flag. Assuming you have a local directory `./refs`, you instruct texd to use\nthis directory for references:\n\n```console\n$ texd --reference-store=dir://./refs\n```\n\nThe actual syntax is `--reference-store=DSN`, where storage adapters are identified through and\nconfigured with a DSN (*data source name*, a URL). Currently there are only handful implementations:\n\n1. The `dir://` adapter ([docs][docs-dir]), which stores reference files on disk in a specified\n   directory. Coincidentally, this adapter also provides an in-memory adapter (`memory://`),\n   courtesy of the [spf13/afero][afero] package.\n\n2. The `memcached://` adapter ([docs][docs-memcached]), which stores, you may have guessed it,\n   reference files in a [Memcached][memcached] instance or cluster.\n\n3. The `nop://` adapter ([docs][docs-nop]), which―for the sake of completeness sake―implements a\n   no-op store (i.e. attempts to store reference file into is, or load files from it fail silently).\n   This adapter is used as fallback if you don't configure any other adapter.\n\n[docs-dir]: https://pkg.go.dev/github.com/digineo/texd/refstore/dir\n[afero]: https://github.com/spf13/afero\n[docs-memcached]: https://pkg.go.dev/github.com/digineo/texd/refstore/memcached\n[memcached]: https://memcached.org/\n[docs-nop]: https://pkg.go.dev/github.com/digineo/texd/refstore/nop\n\nIt is not unfeasible to imagine further adapters being available in the future, such as additional\nkey/value stores (`redis://`), object storages (`s3://`, `minio://`), or even RDBMS (`postgresql://`,\n`mariadb://`).\n\n### Data retention\n\ntexd supports three different retention policies:\n\n1. `keep` (or `none`) will keep all file references forever. This is the default setting.\n2. `purge-on-start` (or just `purge`) will delete file references once on startup.\n3. `access` will keep an access list with LRU semantics, and delete file references, either if\n   a max. number of items is reached, or if the total size of items exceeds a threshold, or both.\n\nTo select a specific retention policy, use the `--retention-policy` CLI flag:\n\n```console\n$ texd --reference-store=dir://./refs --retention-policy=purge\n```\n\nTo configure the access list (`--retention-policy=access`), you can adopt the quota to your needs:\n\n```\n$ texd --reference-store=dir://./refs \\\n    --retention-policy=access \\\n    --rp-access-items=1000 \\\n    --rp-access-size=100MB\n```\n\nNotes:\n\n- The default quota for the max. number of items (`--rp-access-items`) is 1000.\n- The default quota for the max. total file size (`--rp-access-size`) is 100MB.\n- Total file size is measured in bytes, common suffixes (100KB, 2MiB, 1.3GB) work as expected.\n- To disable either limit, set the value to 0 (e.g. `--rp-access-items=0`).\n- It is an error to disable both limits (in this case just use `--retention-policy=keep`).\n- Currently, only the `dir://` (and `memory://`) adapter support a retention policy; the\n `memcached://` adapter delegates this responsibility to the Memcached server.\n\n## History\n\ntexd came to life because I've build dozens of Rails applications, which all needed to build PDF\ndocuments in one form or another (from recipes, to invoices, order confirmations, reports and\ntechnical documentation). Each server basically needed a local TeX installation (weighing in at\nseveral 100 MB, up to several GB). Compiling many LaTeX documents also became a bottleneck for\napplications running on otherwise modest hardware (or cloud VMs), as this process is also\ncomputationally expensive.\n\nOver time I've considered using alternatives for PDF generation (Prawn, HexaPDF, gofpdf, SILE, iText\nPDF, to name but a few), and found that the quality of the rendered PDF is far inferior to the ones\ngenerated by LaTeX. Other times, the licensing costs are  astronomical, or the library doesn't\nsupport some layouting feature, or the library in an early alpha stage or already abandoned...\n\nI'll admit that writing TeX templates for commercial settings is a special kind of pain-inducing\nform of art. But looking back at using LaTeX for now over a decade, I still feel it's worth it.\n\n\n## Future\n\nOne wishlist's item is asynchronous rendering: Consider rendering monthly invoices on the first\nof each month; depending on the amount of customers/contracts/invoice positions, this can easily\nmean you need to render a few thousand PDF documents.\n\nUsually, the PDF generation is not time critical, i.e. they should finish in a reasonable amount of\ntime (say, within the next 6h to ensure timely delivery to the customer via email). For this to\nwork, the client could provide a callback URL to which texd sends the PDF via HTTP POST when\nthe rendering is finished.\n\nOf course, this will also increase complexity on both sides: The client must be network-reachable\nitself, an keep track of rendering request in order to associate the PDF to the correct invoice;\ntexd on the other hand would need a priority queue (processing async documents only if no sync\ndocuments are enqueued), and it would need to store the callback URL somewhere.\n\n\n## Related work\n\nOf course, this project was not created in a void, other solutions exist as well:\n\n- **latexcgi**, MIT license, [GitHub project][latexmk-gh], [Website][latexmk-web]\n\n  Project description:\n\n  \u003e The TeXLive.net server (formally known as (LaTeX CGI server) (currently running at texlive.net)\n  \u003e accepts LaTeX documents via an HTTP POST request and returns a PDF document or log file in the\n  \u003e case of error.\n  \u003e\n  \u003e It is written as a perl script accepting the post requests via cgi-bin access in an apache\n  \u003e HTTP server.\n\n- **Overleaf**, AGPL-3.0 license, [GitHub project][overleaf-gh], [Website][overleaf-web]\n\n  Project description:\n\n  \u003e Overleaf is an open-source online real-time collaborative LaTeX editor. We run a hosted version\n  \u003e at www.overleaf.com, but you can also run your own local version, and contribute to the\n  \u003e development of Overleaf.\n\n- **overleaf/clsi**, AGPL-3.0 license, [GitHub project][clsi-gh]\n\n  Project description:\n\n  \u003e A web api for compiling LaTeX documents in the cloud\n  \u003e\n  \u003e The Common LaTeX Service Interface (CLSI) provides a RESTful interface to traditional LaTeX\n  \u003e tools (or, more generally, any command line tool for composing marked-up documents into a\n  \u003e display format such as PDF or HTML).\n\n[latexmk-gh]: https://github.com/davidcarlisle/latexcgi\n[latexmk-web]: https://davidcarlisle.github.io/latexcgi/\n[overleaf-gh]: https://github.com/overleaf/overleaf\n[overleaf-web]: https://www.overleaf.com\n[clsi-gh]: https://github.com/overleaf/overleaf/tree/main/services/clsi\n\n## Contributing\n\nPlease report bugs and feature request to \u003chttps://github.com/digineo/texd/issues\u003e.\n\nPull requests are welcome, even minor ones for typo fixes. Before you start on a larger feature,\nplease create a proposal (in form of an issue) first.\n\n\n## License\n\nMIT, © 2022, Dominik Menke, see file [LICENSE](./LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdigineo%2Ftexd","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdigineo%2Ftexd","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdigineo%2Ftexd/lists"}