Hebrew not rendering correctly in a PDF
The Hebrew in your PDF is not in the order you wrote it. Every letter is there, none of them are boxes, and the line reads backwards.
in the file חשבונית 42
on the page 42 תינובשחThat output is correct and there is nothing to fix in it. Hebrew is stored in the order it is read and drawn in the opposite direction, so the file and the page always disagree, and comparing the two is not a test of anything. Before changing a font, find out which of these you actually have.
dir changes one of these lines and not the other
The advice you will find is to add dir="rtl". Here is that attribute doing nothing at all:
no dir attribute
on the page 42 תינובשח
dir="rtl"
on the page 42 תינובשחIdentical. Hebrew letters carry strong right-to-left direction of their own, so a run of them lays itself out correctly whatever the paragraph says, and the trailing number comes along. Most single fields on an invoice look like this, which is why a document can go a long way with no direction set and no sign of trouble.
Then a Latin word arrives at the end of the line:
no dir attribute
in the file סה"כ 1,250.00 ILS
on the page 1,250.00 כ"הס ILS
dir="rtl"
in the file סה"כ 1,250.00 ILS
on the page ILS 1,250.00 כ"הסNow it decides everything. Without the attribute the paragraph is left-to-right, so ILS keeps the right-hand end and the Hebrew is squeezed between the amount and the currency. With it, the line runs right to left the whole way and the currency lands where a reader expects it. Same characters, same font, same three tokens, and only one of the two is the document you meant.
So the answer to whether you need dir is that you cannot tell from a field that works. Set it on the document and stop thinking about it.
The font is a smaller problem here than in the other scripts
Hebrew is the script where the fallback is usually fine. Reading DejaVu's own character map, which is what a Debian image hands you when nothing else is configured:
DejaVu Sans
Hebrew letters 22/22
Final forms 5/5
Niqqud (vowel points) 12/12
Cantillation marks 0/6 missing: ֑ ֒ ֓ ֠ ֡ ֯
Punctuation 5/5Letters, final forms, vowel points and punctuation all complete. The only gap is cantillation, the marks used in biblical text, which no invoice has ever needed. If your Hebrew is legible and merely disordered, the font is not what you are looking for.
docker run --rm your-image:tag sh -c 'fc-list :lang=he family | sort -u'An empty answer there is the one case where the font really is the problem, and it means the image has no Hebrew at all. That happens when a browser package is installed with --no-install-recommends and leaves only Liberation behind. Missing fonts in headless Chrome covers that case for every script.
Naming the Hebrew face is how you lose your digits
The obvious move once you know the family exists is to put it in your CSS. Its character map is worth reading first:
Noto Sans Hebrew
Hebrew letters 22/22
Latin letters 0/6 missing: A B C a b c
Digits 0/10 missing: 0 1 2 3 4 5 6 7 8 9
Currency and punctuation 1/6 missing: € $ . , :Twenty-two letters and no digits. No Latin, no full stop, no comma, no dollar or euro sign. It is a script face and it carries the script, which means font-family: 'Noto Sans Hebrew' on its own hands every amount on the invoice to whatever the browser finds next.
You can watch that happen in the finished file. One line of text, three tokens, two embedded fonts:
$ pdffonts one-line.pdf
name type encoding emb sub uni object ID
------------------------------------ ----------------- ---------------- --- --- --- ---------
AAAAAA+LiberationSerif CID TrueType Identity-H yes yes yes 4 0
BAAAAA+NotoSansHebrew-Regular CID TrueType Identity-H yes yes yes 5 0The Hebrew came from Noto Sans Hebrew and the digits came from Liberation Serif, which is the default serif and not a choice anybody made. On a well-populated image that fallback is invisible. On a thin one your Hebrew is perfect and your totals are boxes, which is the reverse of every other guide here.
/* The Hebrew face cannot draw a digit or a full stop. Give it
somewhere to fall through to, or let fontconfig do it. */
body { font-family: 'Noto Sans Hebrew', 'Noto Sans', sans-serif; }How to check it without reading Hebrew
The instinct is to extract the text and compare it against the string you sent. That check cannot work, and here is why:
$ pdftotext one-line.pdf -
ILS 1,250.00 סה"כThe runs come back in the order they sit on the page rather than the order you wrote them, with bidi embedding marks around the Hebrew. So the extracted string legitimately differs from your input and always will, in both the correct and the broken document.
Two checks that do work. Look at which token is furthest left in the extracted output: for a right-to-left line it should be the last thing you wrote, and if it is the first, the paragraph is running the wrong way. And run pdffonts, because a Hebrew document that embeds no Hebrew face is a font problem, while one that embeds two faces is the fallback above and is fine.
How we handle this
PaperPony renders PDFs from HTML through an API. Noto Sans Hebrew is installed in the render container with a fontconfig rule that prefers it over DejaVu, and the build asks for it by language and then checks that the face it named is the one that drew the string:
ok fallback he -> Noto Sans Hebrew (want Noto Sans Hebrew)
ok render hebrew default=81.3 named=77.4 drift=5.0%Direction is yours to set, in the document, because it is a property of what you are writing rather than of the renderer. The quickstart is where to start.