Skip to content

Exportación

Pablo Martínez López edited this page Nov 10, 2026 · 11 revisions

Exportación

English · Español

Exportar convierte el mazo en pantalla en uno o varios PDF listos para imprimir, y también exporta el mazo como lista de texto (Moxfield, Arena, MTGO, texto plano) o como paquete .pbdeck. La exportación a PDF es un trabajo en segundo plano largo, paralelo y cancelable, con eventos de progreso, que nunca deja un fichero a medio escribir con el nombre definitivo.

Código fuente: src-tauri/src/commands/export.rs (trabajo), src-tauri/src/jobs.rs (ids y banderas de cancelación), src/features/export/ (panel derecho, alineación de filas, presets, menú de «más formas de exportar»), src/lib/trabajos.ts y src/stores/trabajo-store.ts (la fontanería del trabajo en la pantalla), src-tauri/crates/proxy-core/src/decklist/exportar.rs (texto de la lista), src-tauri/src/fichero.rs y src-tauri/src/commands/fichero.rs (donde la ruta elegida puede ser un content:// de Android, y compartir; véase «Exportar en el móvil» más abajo). Los ajustes viven en PrintSettings (src-tauri/crates/proxy-core/src/model.rs).

Exportando

Superficie pública

Comandos de Tauri (registrados en src-tauri/src/lib.rs):

Comando Dónde Qué hace
export_pdf(deck, path, zip?, lang?) -> u32 commands/export.rs Inicia el trabajo en una tarea bloqueante y devuelve el job_id de inmediato. Progreso en export://progress, resultado en export://done. path es una ruta de fichero o, en Android, una URI content://; zip: true fuerza que los trozos vayan en un ZIP (véase «Exportar en el móvil»). lang es el idioma de la interfaz (exportarA manda leerIdioma()), para la línea del margen de cada hoja; si falta o no se conoce, inglés (sello::idioma).
export_cancel(job_id) -> bool igual Activa la bandera de cancelación del trabajo (Jobs::cancelar); false si el trabajo ya no existe.
export_estimate(deck) -> ExportEstimate { bytes, images, pages, files, memory, lowMemory } igual Tamaño aproximado del PDF, cuántos PDF saldrán y la memoria que hará falta, antes de exportar (véase más abajo).
card_export_image(card, back, settings, deck_back, path) -> String commands/imagen.rs Guarda una cara de una carta como imagen (clic derecho → «Exportar imagen»): la misma imagen que llevaría el PDF (retoques, oscurecido, cartas diseñadas pintadas por el motor) a los PPP de la calidad del PDF y sin sangrado, siempre con el sello de Tapete pintado en el idioma de la carta (El sello de las cartas). El formato sale de la extensión (png, jpg, webp, tif, bmp; ninguna → PNG; otra → InvalidInput); en Android sale del nombre del content:// elegido, que da Android. El reverso es el propio de la carta, la otra cara de una DFC o el del deck.
export_calibration(settings, path, texts) -> String igual Escribe la hoja de calibración duplex (Imagen y PDF); path puede ser un content://.
export_open(path), export_reveal(path) igual Abren un fichero exportado con el visor del sistema / lo muestran en su carpeta. Solo se aceptan ficheros .pdf y .pbdeck que existan (exportado()); si no, InvalidInput. En Android export_open abre el fichero compartiéndolo (el sistema deja elegir el visor; ACTION_VIEW con una ruta no funciona allí) y export_reveal contesta Unsupported (no hay carpeta que enseñar).
deck_list_text(deck, format) -> String commands/decklist.rs El mazo como texto de lista para copiar.
deck_list_save(deck, format, path) igual Escribe la lista en un .txt (la ruta debe ser absoluta y acabar en .txt; un content:// se escribe tal cual, porque su nombre se puso en el diálogo).
fichero_temporal(nombre) -> String, compartir_fichero(ruta) commands/fichero.rs El camino de «Compartir» en el móvil: una ruta nueva dentro de la caché donde se escribe lo exportado, y la hoja de compartir del sistema para un fichero (una ruta o un content://). Véase «Exportar en el móvil».
deck_export_package(id, path) -> u64 commands/library.rs Guarda el .pbdeck a partir del mazo almacenado (Formato del paquete de mazo); path puede ser un content://.
preset_list / preset_save / preset_delete commands/library.rs, store/presets.rs Presets de impresión con nombre (véase «Presets» más abajo).

Eventos (tipos en src/bindings/, declarados en src/lib/ipc.ts):

  • export://progress: ExportProgressEvent { jobId, phase: "prepare" | "compose" | "write", done, total }.
  • export://done: ExportDoneEvent { jobId, result: ExportResult | null, error: AppError | null }. ExportResult { paths, pages, sheets, bytes }.

Flujo de principio a fin

sequenceDiagram
    participant UI as export-panel.tsx
    participant A as acciones.ts (exportarA)
    participant T as lanzarTrabajo
    participant R as export_pdf (Rust)
    participant J as exportar() worker
    UI->>A: pedirDestino (diálogo de guardar)
    UI->>R: export_estimate(deck)
    alt estimación > 500 MB
        UI->>UI: diálogo de aviso (bajar a JPEG 90 / 300 dpi / exportar igualmente)
    end
    A->>T: lanzarTrabajo("export://progress","export://done", call export_pdf)
    T->>T: primero escucha, luego lanza
    T->>R: export_pdf(instantánea del mazo, ruta)
    R-->>T: job_id
    R->>J: spawn_blocking
    J-->>UI: export://progress (prepare n/total)
    J-->>UI: export://progress (compose página/total)
    J-->>UI: export://progress (write)
    J-->>T: export://done
    T-->>A: resultado o error
    A-->>UI: aviso con Abrir / Mostrar en carpeta
Loading

Lado de la pantalla (src/features/export/acciones.ts)

  1. pedirDestino pregunta dónde guardar mediante guardarParaExportar de src/lib/dialogos.ts (que recuerda en Rust la última carpeta de exportación). Nombre por defecto: nombreSeguro(deck.name, "pdf"), que sustituye los caracteres que rechazan los sistemas de ficheros y, si el deck aún tiene el nombre de fábrica («Deck sin título» en cualquiera de los 15 idiomas, nombreVisible de src/lib/nombre-deck.ts), lo pone en el idioma de ahora, como en pantalla.
  2. export-panel.tsx llama a export_estimate; por encima de AVISO_BYTES (500 MiB) o cuando la estimación dice lowMemory (pideAviso en acciones.ts) muestra un diálogo con tres opciones: «Bajar a JPEG 90», «Bajar a 300 PPP» (ambas cambian los ajustes del mazo y luego exportan) o «Exportar igualmente».
  3. exportarA(destino, t) lee la instantánea del mazo del store del mazo y lanza { code: "Busy" } si useTrabajo ya tiene un trabajo. Marca el trabajo como iniciado y luego llama a lanzarTrabajo.
  4. Al terminar: un error Cancelled muestra «Exportación cancelada»; cualquier otro error se lanza (quien llama muestra mensaje(e) en un aviso); si va bien, se muestra un aviso (toast) con el tamaño, la ruta, «Abrir» (export_open) y «Mostrar en carpeta» (export_reveal).

Decisión: el mazo que se envía es la instantánea en memoria, no lo que hay en la base de datos. Motivo: el autoguardado puede ir con un retraso de hasta unos 800 ms, y quien cambia un ajuste y exporta enseguida debe obtener ese ajuste.

lanzarTrabajo (src/lib/trabajos.ts): se suscribe a los eventos de progreso y final ANTES de invocar el comando, almacena en un búfer lo que llegue antes de conocerse el job_id y luego lo reproduce filtrado por jobId. Motivo: un trabajo corto puede acabar antes de que se resuelva la promesa de invoke; suscribirse después perdería el evento final. useTrabajo (trabajo-store.ts) guarda el único trabajo en marcha (tipo: "resolver" | "exportar", jobId, fase, hechas, total); el panel muestra un spinner, «n/total», una barra Progress y un botón de cancelar (cancelarTrabajo en src/features/decklist/acciones.ts llama a export_cancel en las exportaciones). Etiquetas de fase: el mapa FASES (prepare, compose, write a «Preparando imágenes…», «Componiendo páginas…», «Escribiendo el PDF…»).

Lado de Rust (exportar)

export_pdf registra un trabajo (state.jobs.alta() -> (job_id, Arc<AtomicBool>)), marca la app como ocupada con state.trabajo() (así, por ejemplo, el actualizador automático no reiniciará a mitad de una exportación, véase Versiones y actualizaciones automáticas), ejecuta exportar(...) en tauri::async_runtime::spawn_blocking dentro de catch_unwind, siempre elimina el trabajo (jobs.baja) y emite export://done con el resultado o con el AppError (un pánico pasa a Internal { detalle: "pánico" }). Motivo: sin export://done la pantalla se queda para siempre con el último progreso y «Cancelar» no tiene nada que parar.

exportar(state, deck, destino, lang_app, cancelado, progreso) hace:

  1. Plan. plan(deck) (la misma llamada que usa la vista previa; Motor de maquetación).
  2. Diseños. DisenosDelDeck::de(state, deck) (src-tauri/src/diseno/pintar.rs) calcula una sola vez por trabajo el diseño efectivo de cada carta diseñada (carta > colección > fábrica).
  3. Hoja de pedido (encargo). Para cada casilla ocupada de cada página, construye un RenderSpec con render::spec_de_casilla. Si la carta tiene diseño, DisenosDelDeck::referencia da un DisenoRef { huella, cara, lang } (qué cara del diseño muestra esta casilla: el anverso, o la cara 1 de una carta diseñada de doble cara como reverso o como carta aparte; véase cara_de_casilla), se vacían los edits y edge de la especificación (las cartas diseñadas no reciben retoques ni oscurecido de bordes) y se rellena diseno. Las especificaciones se indexan por RenderSpec::key(), así que las casillas idénticas (p. ej. 4 copias de la misma carta, o el mismo reverso en cada hoja) se renderizan una sola vez. por_casilla asocia (page, slot) con la clave, e idiomas junta, por clave, los idiomas del sello de las casillas que la usan (Slot.sello). La especificación de impresión nunca pinta el sello (sello: None).
  4. Preparar. Un pool de rayon de min(available_parallelism, 8) hilos renderiza cada especificación distinta: render_para_pdf para las imágenes normales, o render_diseno_para_pdf para las diseñadas (pintar(...) con Salida::Impresion al tamaño exacto de corte en píxeles para los dpi elegidos, y luego render_desde_imagen para el sangrado y la codificación). Cada tarea comprueba primero la bandera de cancelación (devuelve ErrorCode::Cancelled) y emite progreso Prepare (done, total de imágenes distintas). Cada tarea decide además dónde va el sello en esa imagen para cada uno de sus idiomas: sello::colocaciones quita el sangrado y pasa sello::colocar (El sello de las cartas).
  5. Imágenes para krilla. Los bytes renderizados pasan a ser krilla::image::Image (from_jpeg o from_png).
  6. Componer y escribir, por cada trozo de salida (véase la división): compose(&sub_plan, &cutGuides, PdfMeta{title: deck.name, document_id: "<hash>-<chunk>", hoja: Some(lang_app)}, casilla_for_slot, page_done). La retrollamada devuelve una Casilla con la imagen y, siempre, un SelloCasilla { lang: slot.sello, colocacion } (el sello no se puede apagar; si no hay colocación calculada, se usa SITIOS[0] con pastilla). page_done emite progreso Compose y devuelve !cancelled. Tras componer un trozo se emite progreso Write y escribir_atomico lo escribe.

document_id sale de huella_documento: SHA-256 del plan serializado más las claves casilla-especificación ordenadas, primeros 16 bytes en hex. El mismo mazo con los mismos ajustes da PDF idénticos byte a byte.

Cancelación y escrituras atómicas

  • Cancelar activa el AtomicBool compartido; las tareas de preparación y la retrollamada por página de compose lo observan, y el trabajo devuelve PdfError::Cancelled / ErrorCode::Cancelled, que la pantalla trata como un resultado normal de «cancelado», no como un fallo.
  • escribir_atomico (src-tauri/src/store/blobs.rs) escribe en <name>.<ext>.part, hace sync_all y luego renombra sobre el destino. Una exportación cancelada o fallida nunca deja, por tanto, un fichero a medio escribir con el nombre real (test cancelar_no_toca_el_destino).
  • Trampa: los contadores de progreso no son perfectamente uniformes. Prepare cuenta imágenes distintas, Compose cuenta páginas de todos los trozos, Write es el índice del trozo (t) sobre el número de trozos, así que nunca llega al 100 % antes de done.

División del PDF

PrintSettings.splitEvery (Option<u32>, en la interfaz «Partir cada» N «hojas»; vacío significa un solo fichero). trozos(plan, n) agrupa las páginas por page.sheet / n, de modo que el anverso y el reverso de una hoja siempre quedan en el mismo fichero. Nombres de salida: ruta_trozo conserva la ruta elegida cuando hay un solo fichero; si no, <stem> (1).pdf, <stem> (2).pdf... junto a ella. ExportResult.paths lista todos los ficheros escritos. Test partir_cada_dos_hojas. En Android los trozos van en cambio en un solo ZIP (más abajo). Una cancelación tras escribir algunos trozos deja esos trozos anteriores (cada uno se escribió de forma atómica); Deducido de la estructura del bucle en exportar.

Estimación

export_estimate vuelve a ejecutar plan y encargo(.., None) (sin referencias de diseño, de modo que las cartas diseñadas se cuentan por su blob de miniatura guardado) y multiplica pdf::estimate::bytes_por_imagen(...) por el número de especificaciones distintas. Es una aproximación, usada solo para avisar. Test la_estimacion_cuenta_imagenes_distintas.

También contesta dos cosas pensando en el móvil. files es el número de PDF que saldrán (trozos(plan, splitEvery).len()), para que la pantalla pida un ZIP en vez de un PDF antes de lanzar el trabajo. memory y lowMemory son el aviso de memoria: memoria_necesaria suma todas las imágenes ya codificadas (krilla las guarda enteras hasta escribir) más, por hilo de trabajo, la carta decodificada y sus copias de trabajo (ancho x alto x 4 x 3 bytes), y cabe_en_memoria dice que no cabe cuando eso pasa del 60 % de la memoria libre que da sysinfo (memoria_libre; 0 significa que no se sabe y nunca avisa). Por qué: en un móvil con poca memoria Android mata la app a mitad de la exportación, sin error que enseñar, así que el aviso va antes. Reutiliza el mismo diálogo que el aviso de tamaño, con una frase más. Tests la_memoria_necesaria_suma_las_imagenes_y_el_trabajo_de_cada_hilo y un_deck_de_100_cartas_a_600_ppp_en_png_no_cabe_en_2_gb_y_a_300_si.

Exportar en el móvil (Android)

En Android el diálogo de guardar no devuelve una ruta sino una URI content://: un permiso que el sistema concede a ese único fichero, sin carpeta a su lado y sin extensión. Todo lo que exporta pasa por src-tauri/src/fichero.rs (IPC y manejo de errores) y en escritorio nada cambia (una ruta es una ruta).

  • Escribir un PDF. exportar_en (commands/export.rs): cuando path es una URI (o zip está activo) el PDF se compone en una carpeta temporal de la caché con fichero::temporal y solo después se copia al destino con fichero::copiar_a. Motivo: un content:// no se puede renombrar, así que la escritura atómica de escribir_atomico (.part y renombrar) no existe allí. El resultado da la URI como única ruta.
  • PDF partido y PNG van en un ZIP. Con splitEvery la exportación escribiría varios ficheros, y junto a un content:// no se pueden crear ficheros. Por eso la pantalla pide un .zip (pedirDestino en acciones.ts llama a saldraPartido, que lee files de export_estimate) y zip_de_trozos empaqueta los trozos con fichero::zip_de_lista. La exportación de PNG del diseñador hace lo mismo: Android no tiene selector de carpetas, así que elegirDestinoDeImagenes (src/lib/dialogos.ts) pide un ZIP y png_en_zip (commands/diseno.rs) comprime las carpetas es/, en/... con fichero::zip_de_carpeta. Los PDF y las imágenes ya van comprimidos, así que se guardan tal cual (Stored), sin deflate.
  • Compartir. Junto a «Guardar» el móvil ofrece «Compartir» (en el menú de «más formas» de menu-exportar.tsx: compartir el PDF, la lista como texto, el .pbdeck; y como acción del aviso de «guardado», en lugar de «Abrir» y «Mostrar en carpeta»). La pantalla pide a fichero_temporal una ruta en la caché, exporta allí (exportarA(..., { compartir: true, zip }), que se salta el aviso de «guardado») y llama a compartir_fichero. Rust copia a la caché un fichero que no esté en ella (para_compartir: el FileProvider del proyecto de Android solo ve la caché), saca el tipo MIME de la extensión (mime_de) y pide al plugin de Android un ACTION_SEND (Capa de aplicación Rust). La copia se queda en la caché hasta el siguiente barrido: la otra app la lee cuando quiere.
  • Lo que no hay en el móvil. «Mostrar en carpeta» (export_reveal) y «Abrir» con el visor del sistema no existen; «Exportar imagen» escribe en el content:// elegido como cualquier otro fichero.

La calidad de impresión de fábrica sigue siendo 600 PPP también en el móvil (el aviso de memoria es la única protección). Deducido: si se quiere un valor menor de fábrica para móviles sigue siendo una decisión de producto.

Panel de ajustes (src/features/export/export-panel.tsx)

Cada control escribe en deck.settings mediante useDeck.setAjustes, de modo que cada cambio se guarda con el mazo, se puede deshacer con Ctrl+Z y dispara un recálculo del plan (100 ms de espera, Motor de maquetación). Secciones:

Sección Controles Campos de ajustes
PDF Calidad 300/600/900, formato de imagen JPEG/PNG, calidad JPEG (60-100), partir cada N hojas quality, encoding {format, jpegQuality}, splitEvery
Doble cara Apagado / dúplex, intercalar pares de DFC, reverso de las cartas grandes (solo cuando el plan tiene bigGrid), hoja de calibración duplex, interleaveDfc, bigCardsBack
Página Papel, apaisado, tamaño de carta (63 x 88, 63,5 x 88,9 o 65 x 90 mm, con un texto de ayuda sobre el último), margen mínimo paper, landscape, cardSize, minMarginMm
Separación y sangrado Separación, sangrado (0-5 mm), relleno del sangrado (repetir el borde / espejo), rellenar las esquinas, sangrado en los reversos gapMm, bleedMm, bleedMode, fillCorners, bleedOnBacks
Guías de corte Dibujar guías, color, grosor (px), separación del borde, longitud cutGuides {enabled, color, widthPx, offsetMm, lengthMm}
Oscurecer bordes (plegada) Modo, cantidad, ancho de la banda, detección automática, aplicar a Scryfall / imágenes propias / reversos edgeDarkening
Alinear filas (plegada) Desplazamientos X/Y por fila de anverso/reverso, medir y corregir rowOffsets
Presets de impresión (plegada) Guardar/cargar conjuntos con nombre todo PrintSettings

Una nota al pie recuerda al usuario que imprima al 100 % (sin «ajustar a la página») para que las medidas salgan exactas.

Valores de fábrica y su porqué (impl Default for PrintSettings)

Ajuste Valor por defecto Justificación
paper / landscape A4 / vertical El caso común.
cardSize 63 x 88 mm Tamaño oficial; la opción de 63,5 x 88,9 es el tamaño poker de 2,5 x 3,5 pulgadas, y 65 x 90 llena una funda (abajo).
minMarginMm 5 Borde no imprimible típico; el margen efectivo es max(min, bleed).
gapMm 2 Una separación en blanco, como en la herramienta de referencia: cada carta conserva su tamaño exacto (cabe en fundas) y el sangrado no cubre la separación.
bleedMm 0 Separación en lugar de sangrado por defecto; el sangrado sigue disponible.
bleedOnBacks encendido Los reversos coinciden con los anversos cuando se usa sangrado.
fillCorners apagado Las esquinas transparentes se imprimen del color del papel; corners::fill solo cuando el usuario lo activa.
cutGuides.enabled apagado Hojas limpias por defecto; al activarlas, color #00c853, 0,5 px, separación 0, longitud 5 mm.
edgeDarkening desactivado Solo para escaneos viejos; cantidad 80, banda 8 %, detección automática activada, se aplica solo a imágenes de Scryfall.
duplex apagado La mayoría imprime a una cara.
quality / encoding 600 dpi / JPEG 95 Valor por defecto de calidad de impresión con un tamaño razonable.
designLang En (mazos guardados sin él: Es) Idioma en que se pintan las cartas diseñadas (DesignLang). Cualquiera de los 15 idiomas; se cambia con el selector «Idioma de las cartas diseñadas» (SelectorIdioma, bandera y nombre propio) de la sección PDF del panel de exportar (y con el de la vista previa de Diseñar); un preset de impresión no lo lleva. El cambio pasa por ponerIdiomaDiseno (src/features/disenar/renumerar.ts): en un idioma que no sea el español ni el inglés lanza antes completarIdioma (trabajo design_complete_lang: baja la letra de japonés, coreano o chino si hace falta y la impresión de cada carta diseñada a la que le falte ese idioma) y luego rehace las miniaturas de las cartas diseñadas (design_rerender), porque la hoja enseña esas. Véase Capa de aplicación del diseñador.

El test los_ajustes_por_defecto_son_los_de_fabricard de model.rs fija varios de ellos.

Tamaños de carta

El selector ofrece tres constantes CardSize de src-tauri/crates/proxy-core/src/model.rs (el mapa TAMANOS del panel, que se reconoce de vuelta por widthMm):

Opción Constante Uso
63 x 88 mm CardSize::MTG (por defecto) El tamaño que publica Wizards.
63,5 x 88,9 mm CardSize::POKER 2,5 x 3,5 pulgadas.
65 x 90 mm CardSize::FUNDA Llena justa una funda estándar.

Por qué 65 x 90: ocupa lo mismo que 63 x 88 impreso con 1 mm de sangrado, pero con la imagen entera a ese tamaño en vez de con su borde repetido o en espejo, que es donde el sangrado enseña sus defectos (el borde repetido de un escaneo, una franja en espejo). Lo dice el texto de ayuda del selector («65 × 90 mm llena justa una funda: es como 1 mm de sangrado, pero con la imagen entera a ese tamaño en vez del borde repetido.»). La imagen se estira a la casilla como cualquier otra (imaging::frame, véase Imagen y PDF): la proporción de 65 x 90 se aparta de la de 63 x 88 menos de un 1 %. Las cartas diseñadas se pintan a ese tamaño desde su diseño (la geometría del marco se estira lo mismo; véase Motor del diseñador).

Cuántas caben con la separación de 2 mm y el margen mínimo de 5 mm de fábrica (Motor de maquetación):

  • A4 vertical: siguen siendo 3 x 3 (test a4_vertical_a_la_medida_de_la_funda_siguen_siendo_3x3 de layout/grid.rs).
  • US Letter vertical: solo 3 x 2, porque tres filas piden 3 x 90 + 2 x separación + 2 x margen <= 279,4, es decir, margen + separación <= 4,7 mm. Bajar el margen mínimo a 2,7 mm (con la separación de 2 mm), o la separación a 0 con un margen de hasta 4,7 mm, vuelve a dar 3 x 3, si la impresora llega a imprimir tan cerca del borde.

El test del PDF la_carta_sale_en_el_pdf_con_su_tamano_en_milimetros (pdf/compose.rs) comprueba los tres tamaños, a una cara y a doble cara.

Alineación de filas y hoja de calibración (alinear-filas.tsx)

AlinearFilas muestra una tabla X/Y por fila para «Frente» y «Reverso» (rango -20..20 mm, paso 0,1). Los desplazamientos del reverso se introducen en el marco del ANVERSO, indexados por la fila del anverso; Rust los aplica antes del espejo (Motor de maquetación). «Corregir con lo medido» abre un diálogo donde el usuario teclea, por fila, la desviación leída en la hoja de calibración (derecha y abajo en positivo); aplicar la resta de los desplazamientos de reverso existentes (redondeado a 2 decimales). «Restablecer» vacía ambas listas. exportarCalibracion guarda la hoja de export_calibration, pasando el título y cuatro líneas de instrucciones traducidos (Rust no escribe prosa; véase Internacionalización).

Presets (presets.tsx)

Presets es un control genérico de «cargar / guardar / borrar» sobre preset_list, preset_save, preset_delete, con PresetKind (borrar pregunta antes, «¿Borrar el preset «{n}»?»): frontOffsets, backOffsets (JSON Offset[]) y print (JSON de todo PrintSettings). Viven en la base de datos de la biblioteca local (tabla presets, store/presets.rs), no en el mazo, así que valen para cualquier mazo. Cargar un preset de impresión lo mezcla sobre los ajustes actuales ({ ...actual, ...s }) tras comprobar que tiene un campo paper; un JSON corrupto muestra «Ese preset está dañado». Trampa: como se hace con spread, un preset guardado por una versión anterior carece de los campos más nuevos y conserva para ellos los valores actuales.

Otros formatos de exportación (menu-exportar.tsx)

Sin ninguna carta de verdad en el deck (los huecos no cuentan) el botón «Exportar PDF» está desactivado y su tooltip dice «El deck está vacío.»: no hay nada que imprimir. El botón va partido: los tres puntos (MenuExportar) ofrecen:

Elemento Qué hace
Copiar la lista: Para Moxfield / Para Arena / Para MTGO / En texto plano deck_list_text(deck, format) y luego portapapeles; si el resultado está vacío, muestra un aviso informativo en su lugar.
Descargar para MTGO (.txt) Diálogo de guardar y luego deck_list_save(deck, "mtgo", path).
Deck con sus imágenes (.pbdeck) guardarAhora() (vacía el autoguardado), diálogo de guardar y luego deck_export_package(id, path).

El texto de la lista lo escribe proxy_core::decklist::exportar (FormatoLista::{Moxfield, Arena, Mtgo, Texto}):

  • El origen son las cartas del mazo (lo que se imprime), fusionando las copias de la misma impresión (nombre + set + número de coleccionista) por orden de aparición, no el texto pegado.
  • Las secciones (comandante, compañero, banquillo) no se guardan por carta, así que se recuperan volviendo a analizar deck.decklistText y emparejando por nombre (también por el nombre de la cara frontal en las cartas de doble cara).
  • Se omiten las fichas (por origen o por línea de tipo), las casillas en blanco y las cartas propias vinculadas a una fuente externa de fichas.
  • Marcas: cuentan(&deck.cards) descarta las cartas opcionales y las variantes de diseño extra; las cartas marcadas como banquillo van al banquillo (sideboard). Véase Estadísticas para la regla de cuentan.
  • Formatos: Moxfield 1 Sol Ring (CMM) 410 con SIDEBOARD:; Arena con cabeceras Commander / Companion / Deck / Sideboard y nombres de la cara frontal; MTGO 1 Name con el comandante y el banquillo tras una línea en blanco; texto plano 1 Name.

Modos de fallo

Situación Comportamiento
La carta no cabe en el papel / ajustes no válidos plan falla: AppError InvalidInput con motivo.
Falta un blob ErrorCode::NotFound de leer_blob.
Imagen incorrecta ErrorCode::Image (motivo=too_large o detalle).
Error al escribir el PDF ErrorCode::Pdf con detalle.
Cancelar ErrorCode::Cancelled, que la interfaz trata como «Exportación cancelada».
Hay otro trabajo en marcha La interfaz lanza Busy antes de llamar a Rust.
Móvil con poca memoria libre No es un fallo: el diálogo de tamaño añade una frase (lowMemory) y decide la persona.
No se puede abrir o escribir el content:// elegido ErrorCode::Io con detalle (io_uri lo apunta en el registro).
«Mostrar en carpeta» en Android ErrorCode::Unsupported (detalle: content).
Diseño perdido entre la planificación y el renderizado ErrorCode::Internal («diseno perdido»).

Tests

  • Rust: tests de src-tauri/src/commands/export.rs (exportar_veinte_cartas_en_duplex, exportar_cartas_disenadas, una_dfc_disenada_sale_con_cada_cara_en_su_sitio, un_plano_disenado_sale_grande_en_su_hoja, cancelar_no_toca_el_destino, partir_cada_dos_hojas, la_estimacion_cuenta_imagenes_distintas, la_hoja_de_calibracion_se_escribe y un test de principio a fin), tests de src-tauri/crates/proxy-core/src/decklist/exportar.rs, src-tauri/src/jobs.rs; para el móvil, los_trozos_de_un_pdf_partido_van_en_un_zip y los tests de memoria de commands/export.rs, y los Acceso simulados de fichero.rs.
  • Frontend: src/features/export/__tests__/acciones.test.ts (bus de eventos simulado).

Por dónde empezar para ampliarlo

  • Nueva opción de impresión: añade el campo a PrintSettings (con #[serde(default)] si los mazos antiguos deben seguir abriéndose), regenera los bindings, añade un control en export-panel.tsx, úsalo en layout::plan o render::spec_de_casilla y añade la etiqueta a src/i18n/en/.
  • Nuevo formato de lista: añade una variante de FormatoLista y una rama en exportar(), y luego una entrada en COPIAR en menu-exportar.tsx.

Véase también

Documentation · Documentación

Language: English · Español

Use and build · Uso y desarrollo

Architecture · Arquitectura

Data · Datos

Getting cards · Conseguir cartas

Layout, images, PDF · Maquetación, imagen y PDF

Card designer · Diseñador de cartas

Game table · Mesa de juego

Mobile · Móvil

Frontend · Frontend

Reference · Referencia

Clone this wiki locally