1package com.peachpasswords.codex23import android.graphics.Color4import android.graphics.Paint5import android.graphics.RectF6import android.graphics.pdf.PdfDocument7import com.google.zxing.common.BitMatrix8import java.io.ByteArrayOutputStream91011121314151617181920212223242526object CodexPdf {27 private const val PAGE_W = 59528 private const val PAGE_H = 84229 private const val MARGIN = 36f30 private const val HEADER_H = 54f31 private const val FOOTER_H = 28f3233 fun render(aztecs: List<BitMatrix>, manifest: CodexManifest): ByteArray {34 val doc = PdfDocument()35 val gridTop = MARGIN + HEADER_H36 val gridW = PAGE_W - 2 * MARGIN37 val gridH = PAGE_H - gridTop - MARGIN - FOOTER_H38 val cellW = gridW / CODES_PER_ROW39 val cellH = gridH / CODES_PER_COL40 val cell = minOf(cellW, cellH)41 42 43 val pad = cell * 0.05f4445 val title = Paint().apply { color = Color.BLACK; textSize = 16f; isAntiAlias = true; isFakeBoldText = true }46 val mono = Paint().apply { color = Color.rgb(0x55, 0x55, 0x55); textSize = 9f; isAntiAlias = true }47 48 49 50 val modulePaint = Paint().apply { color = Color.BLACK; isAntiAlias = false }5152 val pageCount = Math.ceil(aztecs.size.toDouble() / CODES_PER_PAGE).toInt().coerceAtLeast(1)53 for (page in 0 until pageCount) {54 val info = PdfDocument.PageInfo.Builder(PAGE_W, PAGE_H, page + 1).create()55 val pg = doc.startPage(info)56 val canvas = pg.canvas5758 canvas.drawText("Peach Codex", MARGIN, MARGIN + 18f, title)59 canvas.drawText(manifest.verifyCode, MARGIN, MARGIN + 36f, mono)60 canvas.drawText(61 "${manifest.entryCount} entries · ${manifest.totalChunks} codes · keep with your recovery words",62 MARGIN + 120f, MARGIN + 36f, mono,63 )6465 val first = page * CODES_PER_PAGE66 val last = minOf(first + CODES_PER_PAGE, aztecs.size)67 for (idx in first until last) {68 val slot = idx - first69 val row = slot / CODES_PER_ROW70 val col = slot % CODES_PER_ROW71 val left = MARGIN + col * cell + pad72 val top = gridTop + row * cell + pad73 val sizePt = cell - 2 * pad74 drawAztecVector(canvas, aztecs[idx], left, top, sizePt, modulePaint)75 }7677 canvas.drawText(78 "page ${page + 1} of $pageCount · ${manifest.verifyCode}",79 MARGIN, PAGE_H - MARGIN + 4f, mono,80 )81 doc.finishPage(pg)82 }8384 val out = ByteArrayOutputStream()85 doc.writeTo(out)86 doc.close()87 return out.toByteArray()88 }8990 91929394959697 private fun drawAztecVector(98 canvas: android.graphics.Canvas,99 matrix: BitMatrix,100 left: Float,101 top: Float,102 sizePt: Float,103 paint: Paint,104 ) {105 val n = matrix.width 106 val moduleSize = sizePt / n107 for (y in 0 until n) {108 var x = 0109 while (x < n) {110 if (!matrix.get(x, y)) {111 x++112 continue113 }114 val spanStart = x115 while (x < n && matrix.get(x, y)) x++116 val rectLeft = left + spanStart * moduleSize117 val rectRight = left + x * moduleSize118 val rectTop = top + y * moduleSize119 canvas.drawRect(rectLeft, rectTop, rectRight, rectTop + moduleSize, paint)120 }121 }122 }123}