caj2pdf
| Language | Python 3 |
|---|---|
| License | GLWTPL ("Good Luck With That Public License", permissive but not OSI-approved) |
| Repository | github.com/caj2pdf/caj2pdf |
| Documentation | README and wiki (Chinese) |
| Depends on | PyPDF2, imagesize, the mutool program from MuPDF; for HN files also libjbigdec and libjbig2codec (built with poppler or jbig2dec) |
| Read / write | reads CAJ, KDH, PDF-in-CAJ, partially HN and C8 / writes PDF only |
caj2pdf is the project that first reverse-engineered the CAJ format. It reads the header and table of contents, extracts the embedded PDF from CAJ files, de-obfuscates KDH files, repairs the result with mutool clean and adds bookmarks. For HN files it decodes page images and builds an image PDF, which requires compiling two helper libraries (prebuilt DLLs are included for Windows).
The project describes itself as working on some files only: unknown variants exit with Unknown file type., and TEB files aren’t supported. It’s not on PyPI; you run it from a checkout. It pins the old PyPDF2 2.2.0. Development has slowed; for new projects, caj2pdf-core (Rust) converts more variants.
Installation#
git clone https://github.com/caj2pdf/caj2pdf.git
cd caj2pdf
pip install -r requirements.txt # PyPDF2==2.2.0, imagesize
# mutool (MuPDF) must be on PATH
sudo apt install mupdf-tools # Debian / Ubuntu
brew install mupdf-tools # macOS
Basic usage: convert CAJ to PDF#
From the command line:
python caj2pdf show paper.caj # type, page count, number of bookmarks
python caj2pdf convert paper.caj -o paper.pdf
From Python, import CAJParser from the checkout (run your script from the caj2pdf folder or add it to sys.path):
import sys
sys.path.insert(0, "/path/to/caj2pdf")
from cajparser import CAJParser
caj = CAJParser("paper.caj")
print("format:", caj.format) # "CAJ", "HN", "C8", "KDH", "PDF" or "TEB"
if caj.format in ("CAJ", "HN", "C8"):
print("pages:", caj.page_num)
caj.convert("paper.pdf")
CAJParser raises SystemExit("Unknown file type.") for files it doesn’t recognize; catch SystemExit when you process many files.
How to detect the CAJ variant#
The variant decides whether conversion will keep text. Check it before converting:
from cajparser import CAJParser
try:
caj = CAJParser("paper.caj")
except SystemExit as e:
print("not supported:", e)
else:
if caj.format in ("CAJ", "KDH", "PDF"):
print("contains a PDF: text and bookmarks are kept")
elif caj.format in ("HN", "C8"):
print("page images: needs the jbig libraries, output is image-only")
else:
print("cannot convert:", caj.format)
How to read the table of contents#
get_toc() returns a list of dicts with the title (UTF-8 bytes), target page and level:
from cajparser import CAJParser
caj = CAJParser("thesis.caj")
for entry in caj.get_toc():
indent = " " * (entry["level"] - 1)
print(f'{indent}{entry["title"].decode("utf-8")} p.{entry["page"]}')
How to add bookmarks to a PDF printed from CAJViewer#
When conversion fails, print the CAJ to PDF in CAJViewer, then copy the bookmarks from the CAJ file into that PDF:
python caj2pdf outlines thesis.caj -o printed.pdf
Tips#
- Conversion writes temporary files (
pdf.tmp,pdf_toc.pdf) into the current directory; run it in a scratch folder and don’t convert two files in parallel from the same folder. python caj2pdf text-extract paper.cajprints the text of HN pages, useful when the images can’t be decoded.