# poppler-render-check — receta-TESTIGO del camino de PDF, en el espíritu de `gtk4-hello`: # no aporta nada a ninguna imagen, prueba que la cadena SIRVE y no sólo que sella. # # ══ QUÉ PRUEBA, Y POR QUÉ HACÍA FALTA ══════════════════════════════════════════════════════════ # `zathura` es GTK4: sin compositor no abre ventana, así que «arranca» no se puede comprobar de la # forma barata. Pero lo que de verdad importaba comprobar no es la ventana: es que # **`poppler-glib` decodifique y RASTERICE**, que es exactamente el camino que recorre # `zathura-pdf-poppler/render.c` — `poppler_document_new_from_file` → `poppler_page_render` sobre # un surface de cairo. Eso sí se puede correr sin display, y es lo que hace este programa. # # Escribe un PDF mínimo A MANO (catálogo, páginas, una página con un rectángulo relleno Y texto # Helvetica, más su xref), lo renderiza y comprueba tres cosas MEDIDAS, no un exit code: # 1. que el documento abra y reporte 1 página de 200x100, # 2. que la imagen tenga PÍXELES NEGROS — o sea que algo se DIBUJÓ. Sin esto, un render que # devuelve un lienzo blanco pasa la prueba, que es el modo de fallar que este repo persigue # (el mismo criterio que contar colores del PPM en el arranque de sway), # 3. que el PNG salga y tenga cabecera PNG de verdad. # # ══ POR QUÉ EL ASSERT ES SOBRE UN RECTÁNGULO Y NO SOBRE EL TEXTO ═══════════════════════════════ # La primera versión de esta receta dibujaba SÓLO texto y **falló**, con `pixeles_oscuros=0` y un # `some font thing failed` de poppler. No es un fallo de la cadena: es que el sandbox de build no # tiene NI UNA FUENTE, y las «base-14» de PDF (Helvetica, Times…) no vienen dentro del documento — # el visor tiene que sustituirlas por fontconfig. Un rectángulo no depende de eso, así que aísla lo # que esta receta quiere probar (¿decodifica y rasteriza?) de lo que no le toca (¿hay fuentes?). # El texto se dibuja igual y se INFORMA aparte, sin condicionar el veredicto — así queda a la vista # si algún día el sandbox sí tiene fuentes. # # ⚠ **Y deja una consecuencia escrita para las imágenes:** un PDF con fuentes base-14 se ve VACÍO # si la imagen no trae fuentes. Los tres perfiles que llevan zathura tienen que traerlas; no es # problema de esta receta pero es donde se descubre. # # Reutiliza el tarball de poppler sólo como acarreo de `[source]`; ignora su árbol. name = "poppler-render-check" version = "25.02.0" license = "GPL-2.0-or-later" [source] tarball = "https://poppler.freedesktop.org/poppler-25.02.0.tar.xz" sha256 = "21234cb2a9647d73c752ce4031e65a79d11a511a835f2798284c2497b8701dee" [build] compiler = "zig-cc" target = "x86_64-linux-musl" link = "dynamic" [build.phases] configure = "true" compile = ''' set -e export PKG_CONFIG_PATH=/usr/lib/pkgconfig cat > check.c <<'CEOF' #include #include #include #include int main(void) { GError *e = NULL; char *uri = g_filename_to_uri("/src/prueba.pdf", NULL, &e); PopplerDocument *doc = poppler_document_new_from_file(uri, NULL, &e); if (!doc) { fprintf(stderr, "FALLO: no abre el PDF: %s\n", e ? e->message : "?"); return 1; } int n = poppler_document_get_n_pages(doc); PopplerPage *pg = poppler_document_get_page(doc, 0); double w, h; poppler_page_get_size(pg, &w, &h); printf("paginas=%d tamano=%.0fx%.0f\n", n, w, h); if (n != 1 || (int)w != 200 || (int)h != 100) { fprintf(stderr, "FALLO: geometria inesperada\n"); return 1; } cairo_surface_t *s = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, (int)w, (int)h); cairo_t *cr = cairo_create(s); cairo_set_source_rgb(cr, 1, 1, 1); cairo_paint(cr); poppler_page_render(pg, cr); cairo_destroy(cr); cairo_surface_flush(s); /* Tercera comprobacion: contar pixeles oscuros. Un lienzo blanco NO puede pasar. */ unsigned char *d = cairo_image_surface_get_data(s); int stride = cairo_image_surface_get_stride(s), oscuros = 0; for (int y = 0; y < (int)h; y++) for (int x = 0; x < (int)w; x++) { unsigned char *px = d + y * stride + x * 4; if (px[0] < 100 && px[1] < 100 && px[2] < 100) oscuros++; } printf("pixeles_oscuros=%d\n", oscuros); /* El rectangulo son 160x60 = 9600 px. Con la mitad ya esta probado que rasterizo. */ if (oscuros < 4000) { fprintf(stderr, "FALLO: se renderizo un lienzo vacio\n"); return 1; } printf("texto_base14=%s (informativo: depende de que haya fuentes, no de esta cadena)\n", oscuros > 9600 + 50 ? "si" : "no"); if (cairo_surface_write_to_png(s, "salida.png") != CAIRO_STATUS_SUCCESS) { fprintf(stderr, "FALLO: no escribe PNG\n"); return 1; } printf("OK: poppler-glib abre, rasteriza y dibuja\n"); return 0; } CEOF zig cc -mcpu=baseline check.c -o check $(pkg-config --cflags --libs poppler-glib cairo) ''' install = ''' set -e # El PDF de prueba se arma acá y no se versiona: son 583 bytes de estructura PDF a mano. python3 - <<'PYEOF' objs = [ b"<< /Type /Catalog /Pages 2 0 R >>", b"<< /Type /Pages /Kids [3 0 R] /Count 1 >>", b"<< /Type /Page /Parent 2 0 R /MediaBox [0 0 200 100] /Resources << /Font << /F1 5 0 R >> >> /Contents 4 0 R >>", None, b"<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>", ] # Rectangulo relleno (no depende de fuentes) + texto (si que depende). Ver la cabecera. stream = b"0 0 0 rg 20 20 160 60 re f\nBT /F1 12 Tf 30 45 Td 1 1 1 rg (HAMMER OK) Tj ET" objs[3] = b"<< /Length %d >>\nstream\n" % len(stream) + stream + b"\nendstream" out = b"%PDF-1.4\n"; offs = [] for i, o in enumerate(objs, 1): offs.append(len(out)); out += b"%d 0 obj\n" % i + o + b"\nendobj\n" xref = len(out) out += b"xref\n0 %d\n" % (len(objs) + 1) + b"0000000000 65535 f \n" for off in offs: out += b"%010d 00000 n \n" % off out += b"trailer\n<< /Size %d /Root 1 0 R >>\nstartxref\n%d\n%%%%EOF\n" % (len(objs) + 1, xref) open("/src/prueba.pdf", "wb").write(out) PYEOF ./check head -c 8 salida.png | od -An -tx1 | grep -q "89 50 4e 47" || { echo "FALLO: el PNG no tiene cabecera PNG" >&2; exit 1; } mkdir -p /out/usr/share/poppler-render-check cp salida.png /out/usr/share/poppler-render-check/ ''' [deps] build = ["pkgconf", "python3", "poppler-glib", "glib-shared", "pcre2-shared", "cairo", "pixman", "libffi", "zlib", "zlib-shared", "bzip2-shared", "freetype-shared", "fontconfig-shared", "expat", "lcms2", "libjpeg-turbo-shared", "libpng-shared"]