Funciones de la app

PDF → imágenes PNG

Pantalla Expo en un solo archivo: PDF.js en WebView oculta, páginas PNG en un ZIP, hoja de compartir. Solo iOS y Android nativos: las secciones siguientes explican qué hace el archivo y cómo fluyen los datos.

Vídeo de demostración

Grabación de pantalla del flujo: elegir PDF, progreso mientras se renderizan páginas, vista previa y compartir o guardar el ZIP (app nativa).

Referencia en un solo archivo (descarga)

Coloca el archivo en tu app Expo Router (`src/app/pdf-to-images-simple.tsx` o similar), incluye los UMD de PDF.js, registra la ruta e instala los paquetes de abajo.

Paquetes: expo-asset, expo-document-picker, expo-file-system, expo-keep-awake, expo-router, expo-sharing, jszip, react-native-webview, react-native-safe-area-context.

Qué hace este archivo

Una pantalla de React Native orquesta todo: elegir PDF, preparar una carpeta en caché junto a los UMD de PDF.js, cargar HTML mínimo en una WebView oculta, ejecutar Mozilla PDF.js para dibujar cada página en un canvas, codificar PNG, enviar datos por el bridge en trozos, montar un ZIP con JSZip, guardarlo en el directorio de documentos de la app y opcionalmente abrir la hoja de compartir. Todo en el dispositivo; no se sube nada.

La versión web está desactivada a propósito (`Platform.OS === 'web'`) porque el flujo depende de rutas `file://` locales y del acceso a archivos de WebView nativa, no de un despliegue web típico.

Flujo de punta a punta

  1. La persona pulsa «Elegir PDF». `DocumentPicker` devuelve el recurso. En Android, las URIs `content://` se copian a una ruta legible en caché con `resolveReadableUri`.
  2. Se crea una carpeta de sesión bajo `cacheDirectory/pdf_to_images_session/` con un id único en la ruta. El PDF se copia como `input.pdf`. `copyBundledPdfJsToWorkDir` copia `pdf.min.js` y `pdf.worker.min.js` desde assets de Expo junto a él para un único origen `file://`.
  3. Se establece `webSession` con HTML generado, `baseUrl` apuntando a esa carpeta y, en iOS, `allowingReadAccessToURL` en la ruta de sesión sin barra final—necesario para que WKWebView lea `input.pdf` y los scripts.
  4. `START_PDF_PIPELINE_JS` se ejecuta al cargar (y se reinyecta tras `onLoadEnd`, con retrasos extra en Android) hasta que exista `window.pdfjsLib`, luego llama a `window.startPdfToPngPipeline` definido en la cadena HTML.
  5. Dentro de la WebView, PDF.js abre el PDF desde un `ArrayBuffer`, limita páginas con `MAX_PAGES`, escala con `MAX_PAGE_WIDTH_PX`, renderiza en canvas 2D, obtiene PNG en base64, lo parte en trozos de ~450k caracteres y envía mensajes `pageStart`, `pagePart` y `pageDone` a React Native.
  6. `onMessage` analiza JSON: acumula trozos por página, escribe `page-001.png`, etc. en un `JSZip`, actualiza el progreso y, en `done`, llama a `finishZip`. Los errores limpian la sesión y muestran alerta.
  7. `finishZip` genera el archivo en base64, escribe un ZIP bajo `documentDirectory` en `PdfImageExports/` con nombre tipo `pdf_pages_*.zip` (en medio va una marca de tiempo), borra la carpeta de sesión, muestra vista previa de la primera página y el usuario puede usar `shareAsync` con MIME `application/zip` (y UTI `public.zip-archive` en iOS).

Bloques principales del código

Constantes y script de arranque

`MAX_PAGES`, `MAX_PAGE_WIDTH_PX`, `KEEP_AWAKE_TAG`, `SESSION_DIR` y `START_PDF_PIPELINE_JS`: un script que espera a `pdfjsLib` y `startPdfToPngPipeline`, inicia la conversión o envía error de tiempo tras muchos reintentos.

Ayudas de sistema de archivos

`ensureFileUrl` añade `file://` donde hace falta para compartir. `copyBundledPdfJsToWorkDir` usa `Asset.fromModule`, `downloadAsync` y `copyAsync` para que la WebView vea archivos reales. `resolveReadableUri` copia selecciones `content://` de Android al caché si hace falta.

HTML generado (`buildPdfToPngHtml`)

Un template construye una página mínima que carga `pdf.min.js`, define `startPdfToPngPipeline` para la ruta del worker, obtiene `input.pdf` (XHR con respaldo fetch), llama a `getDocument`, itera páginas, `render` al canvas con tiempo máximo, y `postMessage` con JSON—base64 en trozos por límites del bridge.

UI de React Native y WebView

El estado lleva `busy`, texto de progreso, `webSession` (HTML + baseUrl + readAccessUrl), ruta del ZIP, URI de vista previa y recuento. Una WebView casi invisible se monta cuando hay sesión; `activateKeepAwakeAsync` evita que el dispositivo duerma durante renders largos. Limpieza al desmontar o error borra el directorio temporal y el keep-awake.

Manejador `onMessage`

Gestiona `stage` (carga vs apertura), `meta` (recuentos de páginas), `pageStart` / `pagePart` / `pageDone` para montar el ZIP, `done` para finalizar y `error` para resetear UI y alertar. Un ref indica qué página se está acumulando.

Creación del ZIP y limpieza

`finishZip` lee entradas PNG del zip, arma un data URI de vista previa desde la primera imagen, escribe el archivo ZIP en base64, luego `teardown` elimina la carpeta de sesión y el estado de la WebView.

Compartir

`shareZip` comprueba `Sharing.isAvailableAsync` y luego `shareAsync` sobre la ruta del ZIP con el MIME correcto para guardar en Archivos u otra app.

En web el botón principal está desactivado y un mensaje indica que la demo es para iOS y Android nativos.

Patrocinado

Promoción breve