Open source · Worked example

Repair the Arabic text of a PDF: a worked example

A sample certificate whose Arabic looks right on screen but copies as boxes and wrong letters, repaired with arabic-pdf-repair in one command. Download both files, compare what they paste as, and run the same code on your own PDFs.

1. Download the sample files

Both files are a one-page certificate made for this example: the names and details in it are fictional. The original's text layer is broken the way old Windows tools left it; the repaired file is the original after the command below. The pages look exactly the same.

2. Copy the text of the original

These are its first lines as pdf.js, the PDF reader in Firefox and many web apps, extracts them. Each □ is a control character: invisible or a box, depending on where you paste it.

□دة إ □□م دورة
□ □□ ا□□ر□□□□دة □□ر □□□□
□ □ إدارة ا□□□□□ □□ن
□ري
□□□"! ا□□رة □"
'□ أ □% □□$□ح دورة

3. Repair it with one command

With Node.js 20 or newer, and nothing to install. The command names each font it fixed and writes the repaired copy next to the original, which it never changes:

$ npx arabic-pdf-repair certificate.pdf
fixed CertArabic: 65 codes
wrote certificate.repaired.pdf

4. Copy the text again

The same lines, copied from certificate.repaired.pdf:

شهادة إتمام دورة
مركز ريادة للتدريب المهني
تشهد إدارة المركز بأن
سارة يوسف المنصوري
قد أتمت بنجاح دورة
أساسيات تحرير المستندات الرقمية

Search works too: the word التعلم has no match in the original and one in the repaired file.

5. The same repair in your code

In Node.js, install the package (npm install arabic-pdf-repair) and pass the file's bytes to repairTextLayer. It returns the repaired file and the fonts it fixed, or null when there is nothing to repair:

import { readFile, writeFile } from "node:fs/promises";
import { repairTextLayer } from "arabic-pdf-repair";

const input = new Uint8Array(await readFile("certificate.pdf"));
const result = await repairTextLayer(input);
if (result) {
  await writeFile("certificate.repaired.pdf", result.bytes);
  console.log(result.fonts); // [{ name: "CertArabic", codes: 65 }]
}

The library uses no Node.js APIs, so it also runs in the browser or a web worker, on a file the user picks, without uploading it:

import { repairTextLayer } from "arabic-pdf-repair";

const picker = document.querySelector("input[type=file]");
picker.addEventListener("change", async () => {
  const file = picker.files[0];
  const result = await repairTextLayer(new Uint8Array(await file.arrayBuffer()));
  if (!result) return; // nothing to repair
  const url = URL.createObjectURL(new Blob([result.bytes], { type: "application/pdf" }));
  window.open(url); // the repaired copy; the file never left the browser
});

Why the original copies as gibberish

  • A PDF draws each letter as a shape (a glyph) from a font. To copy it, a reader needs a table in the file, called ToUnicode, that says which letter each shape stands for.
  • The original's font has no such table. Its glyphs carry only Microsoft's Arabic glyph names (afii62753 and onward), which readers do not take for letters, so copying gives control characters and stray letters.
  • arabic-pdf-repair reads those names, writes the missing table and changes nothing else: the same glyphs in the same places, so the page looks the same.

Next steps