| | | | | linguist.page@gmail.com

From APK Teardown to A7 Booklet

Most “compact Quran” PDFs online are screenshots of screenshots - soft edges, compression artifacts, uneven margins. I wanted the actual source calligraphy, so I went straight to where a Quran app stores its rendering data, pulled it out, and built a layout and print pipeline around it: a pocket-sized A7 booklet that prints double-sided on ordinary A4 paper. This walks through the pipeline, including the wrong turns.

Getting the Source Assets

My assumption going in was that the app rendered verses as text with a custom Arabic font - pull the font, pull the layout rules, re-render. That didn’t survive contact with the file system. Every line of the Mushaf turned out to be stored as an individual SVG file, one per line:

mv QuranApp.xapk QuranApp.zip
unzip QuranApp.zip -d QuranApp_bundle

An XAPK is just a ZIP bundling the base APK with config APKs and asset packs. The 474 MB asset_pack_0.apk was the obvious target - base APKs are usually a few MB of code; anything in the hundreds of MB is media. APKs are ZIPs too, so:

mkdir asset_pack_0
unzip asset_pack_0.apk -d asset_pack_0
find asset_pack_0 -type f | head
asset_pack_0/assets/lines/a03966.svg
asset_pack_0/assets/lines/a08833.svg

One SVG per Qur’anic line, not per page. That’s a real advantage over text rendering or screenshots: infinite scaling with no upsampling artifacts, no JPEG degradation, and the original mushaf’s exact word-spacing and line breaks survive untouched - which matters since Quranic line breaks are traditionally fixed.

Before writing any layout logic, I built the simplest possible viewer to sanity-check ordering:

<img class="line" src="asset_pack_0/assets/lines/a03966.svg">

Then split the full line list into fixed-size page chunks:

grep '<img class="line"' mushaf.html > lines_only.txt
split -l 22 -a 4 lines_only.txt page_

Worth knowing: split’s default suffix length is 2 characters, capping you at 676 files. With thousands of lines, that ceiling gets hit and split just fails - -a 4 widens it and avoids the problem entirely.

Two Targets, One Source

With clean SVG lines in hand, I split the project into two tracks: a Kindle version at 6 lines per page, and a print version at 4 lines per page - because the print target wasn’t a full page at all, it was A7 (74mm × 105mm, postcard-sized), where 4 lines was the ceiling before the script got too small to read.

The A7 Booklet Problem

Nobody’s home printer prints A7 - it prints A4. So the real task was: tile many A7 pages onto one A4 sheet, print both sides, and end up with something that cuts and folds into a mini book in the correct reading order. This is an imposition problem, the same category print shops solve for saddle-stitch binding.

Getting there took a few wrong turns:

The hardest part was double-sided pairing: each page’s reverse side needs to show the next page, like a real book. The naive approach - pages 1-8 on the front, 9-16 on the back, same grid order - fails because flipping a sheet horizontally mirrors its columns. The top-left cell on the front doesn’t end up behind the top-left cell on the back; it ends up behind whatever’s in the top-right, since that’s what lands over it after the flip.

The layout that actually works: one continuous PDF where page 1 holds the odd-numbered mini-pages in forward order, and page 2 holds the even-numbered mini-pages in fully reversed order:

PDF Page 1 (odd)PDF Page 2 (even, reversed)
18
36
54
72
916
1114
1312
1510

Printed double-sided with “flip on long edge,” page 1 lands exactly over page 2, page 3 over page 4, and so on - the printer’s duplex mechanism handles the physical flip, and the reversed order on the even page compensates for it in software.

One more subtlety: rotation direction. Clockwise and counter-clockwise both technically rotate 90°, but only one produces correctly-oriented Arabic once the booklet is assembled. This was resolved empirically - print a test sheet, hold it the way it’ll actually be read, and rotate until it’s right.

Margins: a late visual issue was uneven whitespace around the images in their cells. Shifting the image in the cell fixes it cosmetically; cropping the source image asymmetrically actually removes the excess pixels. Cropping was the correct fix, but the crop amount had to be scaled from the preview resolution to the true print resolution - a margin measured on a downscaled preview doesn’t transfer 1:1 to the source file.

Two output variants came out of this: a guided-cut version (preserves aspect ratio, adds cutting-guide grid lines, configurable margins) for precision, and an edge-to-edge version (stretches each page to fill its cell, no margins or gridlines) for maximum use of the page. Neither is strictly better - different trade-offs, kept as separate outputs rather than one parameterized script.

Takeaways