gioser

sergio/takana

> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…

TOML 1373Shell 116Rust 74Markdown 641915 Sutichaykuna

⎇ 64005efcbd7f4aa6d6f2ab9d3a6fec8ec1bc30d4 recipes/poppler-render-check.toml TOML · 127 siq'ikuna · 6.4 KB Ñawpa willay ch'uya

apps: zathura + backend de PDF, en gnome/cosmic/sway — y tres defectos de imagen que salieron da0186d25 · Sergio ·

1# poppler-render-check — receta-TESTIGO del camino de PDF, en el espíritu de `gtk4-hello`:
2# no aporta nada a ninguna imagen, prueba que la cadena SIRVE y no sólo que sella.
3#
4# ══ QUÉ PRUEBA, Y POR QUÉ HACÍA FALTA ══════════════════════════════════════════════════════════
5# `zathura` es GTK4: sin compositor no abre ventana, así que «arranca» no se puede comprobar de la
6# forma barata. Pero lo que de verdad importaba comprobar no es la ventana: es que
7# **`poppler-glib` decodifique y RASTERICE**, que es exactamente el camino que recorre
8# `zathura-pdf-poppler/render.c` — `poppler_document_new_from_file` → `poppler_page_render` sobre
9# un surface de cairo. Eso sí se puede correr sin display, y es lo que hace este programa.
10#
11# Escribe un PDF mínimo A MANO (catálogo, páginas, una página con un rectángulo relleno Y texto
12# Helvetica, más su xref), lo renderiza y comprueba tres cosas MEDIDAS, no un exit code:
13#   1. que el documento abra y reporte 1 página de 200x100,
14#   2. que la imagen tenga PÍXELES NEGROS — o sea que algo se DIBUJÓ. Sin esto, un render que
15#      devuelve un lienzo blanco pasa la prueba, que es el modo de fallar que este repo persigue
16#      (el mismo criterio que contar colores del PPM en el arranque de sway),
17#   3. que el PNG salga y tenga cabecera PNG de verdad.
18#
19# ══ POR QUÉ EL ASSERT ES SOBRE UN RECTÁNGULO Y NO SOBRE EL TEXTO ═══════════════════════════════
20# La primera versión de esta receta dibujaba SÓLO texto y **falló**, con `pixeles_oscuros=0` y un
21# `some font thing failed` de poppler. No es un fallo de la cadena: es que el sandbox de build no
22# tiene NI UNA FUENTE, y las «base-14» de PDF (Helvetica, Times…) no vienen dentro del documento —
23# el visor tiene que sustituirlas por fontconfig. Un rectángulo no depende de eso, así que aísla lo
24# que esta receta quiere probar (¿decodifica y rasteriza?) de lo que no le toca (¿hay fuentes?).
25# El texto se dibuja igual y se INFORMA aparte, sin condicionar el veredicto — así queda a la vista
26# si algún día el sandbox sí tiene fuentes.
27#
28# ⚠ **Y deja una consecuencia escrita para las imágenes:** un PDF con fuentes base-14 se ve VACÍO
29# si la imagen no trae fuentes. Los tres perfiles que llevan zathura tienen que traerlas; no es
30# problema de esta receta pero es donde se descubre.
31#
32# Reutiliza el tarball de poppler sólo como acarreo de `[source]`; ignora su árbol.
33name    = "poppler-render-check"
34version = "25.02.0"
35license = "GPL-2.0-or-later"
36
37[source]
38tarball = "https://poppler.freedesktop.org/poppler-25.02.0.tar.xz"
39sha256  = "21234cb2a9647d73c752ce4031e65a79d11a511a835f2798284c2497b8701dee"
40
41[build]
42compiler = "zig-cc"
43target   = "x86_64-linux-musl"
44link     = "dynamic"
45
46[build.phases]
47configure = "true"
48compile = '''
49set -e
50export PKG_CONFIG_PATH=/usr/lib/pkgconfig
51cat > check.c <<'CEOF'
52#include <poppler.h>
53#include <cairo.h>
54#include <stdio.h>
55#include <string.h>
56int main(void) {
57  GError *e = NULL;
58  char *uri = g_filename_to_uri("/src/prueba.pdf", NULL, &e);
59  PopplerDocument *doc = poppler_document_new_from_file(uri, NULL, &e);
60  if (!doc) { fprintf(stderr, "FALLO: no abre el PDF: %s\n", e ? e->message : "?"); return 1; }
61  int n = poppler_document_get_n_pages(doc);
62  PopplerPage *pg = poppler_document_get_page(doc, 0);
63  double w, h; poppler_page_get_size(pg, &w, &h);
64  printf("paginas=%d tamano=%.0fx%.0f\n", n, w, h);
65  if (n != 1 || (int)w != 200 || (int)h != 100) { fprintf(stderr, "FALLO: geometria inesperada\n"); return 1; }
66  cairo_surface_t *s = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, (int)w, (int)h);
67  cairo_t *cr = cairo_create(s);
68  cairo_set_source_rgb(cr, 1, 1, 1); cairo_paint(cr);
69  poppler_page_render(pg, cr);
70  cairo_destroy(cr);
71  cairo_surface_flush(s);
72  /* Tercera comprobacion: contar pixeles oscuros. Un lienzo blanco NO puede pasar. */
73  unsigned char *d = cairo_image_surface_get_data(s);
74  int stride = cairo_image_surface_get_stride(s), oscuros = 0;
75  for (int y = 0; y < (int)h; y++)
76    for (int x = 0; x < (int)w; x++) {
77      unsigned char *px = d + y * stride + x * 4;
78      if (px[0] < 100 && px[1] < 100 && px[2] < 100) oscuros++;
79    }
80  printf("pixeles_oscuros=%d\n", oscuros);
81  /* El rectangulo son 160x60 = 9600 px. Con la mitad ya esta probado que rasterizo. */
82  if (oscuros < 4000) { fprintf(stderr, "FALLO: se renderizo un lienzo vacio\n"); return 1; }
83  printf("texto_base14=%s (informativo: depende de que haya fuentes, no de esta cadena)\n",
84         oscuros > 9600 + 50 ? "si" : "no");
85  if (cairo_surface_write_to_png(s, "salida.png") != CAIRO_STATUS_SUCCESS) {
86    fprintf(stderr, "FALLO: no escribe PNG\n"); return 1;
87  }
88  printf("OK: poppler-glib abre, rasteriza y dibuja\n");
89  return 0;
90}
91CEOF
92zig cc -mcpu=baseline check.c -o check $(pkg-config --cflags --libs poppler-glib cairo)
93'''
94install = '''
95set -e
96# El PDF de prueba se arma acá y no se versiona: son 583 bytes de estructura PDF a mano.
97python3 - <<'PYEOF'
98objs = [
99 b"<< /Type /Catalog /Pages 2 0 R >>",
100 b"<< /Type /Pages /Kids [3 0 R] /Count 1 >>",
101 b"<< /Type /Page /Parent 2 0 R /MediaBox [0 0 200 100] /Resources << /Font << /F1 5 0 R >> >> /Contents 4 0 R >>",
102 None,
103 b"<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>",
104]
105# Rectangulo relleno (no depende de fuentes) + texto (si que depende). Ver la cabecera.
106stream = 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"
107objs[3] = b"<< /Length %d >>\nstream\n" % len(stream) + stream + b"\nendstream"
108out = b"%PDF-1.4\n"; offs = []
109for i, o in enumerate(objs, 1):
110    offs.append(len(out)); out += b"%d 0 obj\n" % i + o + b"\nendobj\n"
111xref = len(out)
112out += b"xref\n0 %d\n" % (len(objs) + 1) + b"0000000000 65535 f \n"
113for off in offs: out += b"%010d 00000 n \n" % off
114out += b"trailer\n<< /Size %d /Root 1 0 R >>\nstartxref\n%d\n%%%%EOF\n" % (len(objs) + 1, xref)
115open("/src/prueba.pdf", "wb").write(out)
116PYEOF
117./check
118head -c 8 salida.png | od -An -tx1 | grep -q "89 50 4e 47" || { echo "FALLO: el PNG no tiene cabecera PNG" >&2; exit 1; }
119mkdir -p /out/usr/share/poppler-render-check
120cp salida.png /out/usr/share/poppler-render-check/
121'''
122
123[deps]
124build = ["pkgconf", "python3", "poppler-glib", "glib-shared", "pcre2-shared", "cairo", "pixman",
125  "libffi", "zlib", "zlib-shared", "bzip2-shared",
126  "freetype-shared", "fontconfig-shared", "expat", "lcms2",
127  "libjpeg-turbo-shared", "libpng-shared"]