2010-02-26 15 views
10

Me gustaría generar (y luego imprimir o guardar) grandes documentos XPS (> 400 páginas) desde mi aplicación WPF. Tenemos una gran cantidad de datos en memoria que deben escribirse en XPS.¿Cómo generar e imprimir documentos XPS grandes en WPF?

¿Cómo se puede hacer esto sin obtener un OutOfMemoryException? ¿Hay alguna manera de que pueda escribir el documento en pedazos? ¿Cómo se hace esto usualmente? ¿No debería estar usando XPS para archivos grandes en primer lugar?

La causa raíz del OutOfMemoryException parece ser la creación del enorme FlowDocument. Estoy creando el FlowDocument completo y luego lo envío al escritor de documentos XPS. ¿Es este el enfoque equivocado?

+0

> OutOfMemeoryExcpetion es probablemente causado por ... En otras palabras, no ha verificado realmente. Necesitamos más información para decirle cuál es el problema. –

+0

@ bitbonk Me encuentro con el mismo problema ahora. ¿Encontraste alguna solución? –

Respuesta

3

Puedo confirmar que XPS no no tira memoria insuficiente en documentos largos. Tanto en teoría (porque las operaciones en XPS se basan en páginas, no intenta cargar todo el documento en la memoria), y en la práctica (uso de informes basados ​​en XPS, y los mensajes de error de ejecución se suman a muchos miles de páginas).

¿Podría ser que el problema esté en una única página particularmente grande? Una gran imagen, por ejemplo? Página grande con alta resolución DPI? Si un solo objeto en el documento es demasiado grande para ser asignado a la vez, dará lugar a una excepción de falta de memoria.

+0

¿Cómo se supone que debo configurar las páginas? Pensé que el paginador hace eso por mí. – bitbonk

+0

No especificamos un tamaño de página tal vez esa es la raíz de todo mal ... – bitbonk

+0

No tomé ninguna medida especial, solo seguí ejemplos. Como han notado otros, proporcionó muy poca información. Debe haber algo inusual en su código o sistema, pero ¿cómo podemos adivinar de qué se trata sin pila de llamadas o fragmentos de código? – ima

5

¿Cómo lo haces? No mostró ningún código.

utilizo un XpsDocumentWriter a escribir en trozos, así:

FlowDocument flowDocument = . .. ..; 

// write the XPS document 
using (XpsDocument doc = new XpsDocument(fileName, FileAccess.ReadWrite)) 
{ 
    XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(doc); 
    DocumentPaginator paginator = ((IDocumentPaginatorSource)flowDocument).DocumentPaginator; 

    // Change the PageSize and PagePadding for the document 
    // to match the CanvasSize for the printer device. 
    paginator.PageSize = new Size(816, 1056); 
    copy.PagePadding = new Thickness(72); 
    copy.ColumnWidth = double.PositiveInfinity; 
    writer.Write(paginator); 
} 

qué no funciona para usted?

+0

¿Dónde están los trozos? ¿Quiere decir que este ejemplo podría potencialmente escribir varios documentos XPS en el mismo flowDocument? Wehn reutilizo el mismo paginador todos los documentos XPS van a terminar en el mismo archivo XPS o en el mismo documento impreso? – bitbonk

+0

The chunks: podría usar writer.Write() en un bucle. Incluso sin eso, supongo que ese escritor.Write() utiliza un enfoque de transmisión en el paginador, y no es necesario que se acumule nada en la memoria. No sé a qué te refieres con "usa el mismo paginador". necesitas mostrar tu código – Cheeso

+0

Esto me obligaría a necesitar generar múltiples instancias de FlowDocument, ¿correcto? Dado que el paginador proviene de la instancia de FlowDocument. – bitbonk

0

¿Has utilizado sos para saber qué está gastando toda la memoria?

Podría ser que se hayan creado objetos administrados o no administrados durante la producción de su documento, y no se lanzarán hasta que el documento haya finalizado (o no se haya terminado).

Tracking down managed memory leaks de Rico Mariani podría ser de ayuda.

+0

Es probable que OutOfMemeoryExcpetion se deba a la creación del enorme FlowDocument en la memoria. ¿Cómo se supone que debo crear el XPS cuando no puedo crear el FixedDocument para él? – bitbonk

+0

Si su FlowDocument no se aferra a más objetos de los que necesita, entonces ¿tal vez podría crear un DocumentPaginator personalizado que gerena las páginas bajo demanda? Sin embargo, no parece haber mucha información sobre el tema. – marklam

0

como dices: probablemente el FixedDocument en memoria consume demasiada memoria.

Quizás un enfoque en el que escriba las páginas XPS cada una de manera individual (y asegúrese de que el FixedDocument se publique cada vez), y luego use una fusión posteriormente podría ser fructífero.

¿Puede escribir cada página por separado?

Nick.

ps. No dude en comunicarse conmigo directamente ([email protected]); hacemos muchas cosas de XPS en NiXPS, y estoy muy interesado en ayudarlo a resolver este problema.

4

Hablando de la ignorancia perfecta del sistema específico implicado, ¿podría sugerir utilizar la técnica de depuración de Wolf Fence en Alaska para identificar el origen del problema? Estoy sugiriendo esto porque otros respondedores no están reportando el mismo problema que estás experimentando. Al trabajar con errores fáciles de reproducir, Wolf Fence es muy fácil de hacer (no funciona tan bien con condiciones de carrera y similares).

  1. Elija un punto medio en sus datos de entrada y trate de generar su documento de salida solo a partir de esos datos, nada más.
  2. Si tiene éxito, elija un punto cerca del 75% en la entrada y vuelva a intentarlo; de lo contrario, elija un punto en aproximadamente el 25% en la entrada y vuelva a intentarlo.
  3. Enjabona, enjuaga, repite, estrechando cada vez la ventana hasta donde está la línea de trabajos/falla.
  4. Puede encontrar que identifica bastante rápidamente una página específica, o quizás un objeto específico en esa página, que es "divertida". Nota: solo tiene que hacer esto log2 (N) veces, o en este caso 9 veces dado las 400 páginas que menciona.

Ahora es probable que tenga algo que pueda atacar directamente. Buena suerte.

4

No se puede usar un solo FlowDocument para generar documentos grandes porque se quedará sin memoria. Sin embargo, si es posible generar su salida como una secuencia de FlowDocument o como una extremadamente alta ItemsControl, es posible.

He encontrado la manera más fácil de hacer esto es a subclase DocumentPaginator y pasar una instancia de mi subclase de XpsDocumentWriter.Write:

var document = new XpsDocument(...); 
var writer = XpsDocument.CreateXpsDocumentWriter(xpsDocument); 
writer.Write(new WidgetPaginator { Widget = widgetBeingPrinted, PageSize = ... }); 

La clase WidgetPaginator sí es bastante simple:

class WidgetPaginator : DocumentPaginator, IDocumentPaginatorSource 
{ 
    Size _pageSize; 

    public Widget Widget { get; set; } 

    public override Size PageSize { get { return _pageSize; } set { _pageSize = value; } } 
    public override bool IsPageCountValid { return true; } 
    public override IDocumentPaginatorSource Source { return this; } 
    public override DocumentPaginator DocumentPaginator { return this; } 
    public override int PageCount 
    { 
    get 
    { 
     return ...; // Compute page count 
    } 
    } 
    public override DocumentPage GetPaget(int pageNumber) 
    { 
    var visual = ...; // Compute page visual 

    Rect box = new Rect(0,0,_pageSize.With, _pageSize.Height); 
    return new DocumentPage(visual, _pageSize, box, box); 
    } 

Por supuesto, todavía tiene que escribir el código que realmente crea las páginas.

Si desea utilizar una serie de FlowDocuments para crear el documento

Si estás usando una secuencia de FlowDocuments para diseñar una sección de su documento a la vez en lugar de todos a la vez, su encargo paginator puede funcionar en dos pasos:

  • El primer paso ocurre cuando se construye el paginador. Crea un FlowDocument para cada sección, luego obtiene un DocumentPaginator para recuperar el número de páginas. Cada sección FlowDocument se descarta una vez que se cuentan las páginas.
  • El segundo pase ocurre durante la salida del documento real: si el número pasó a GetPage() está en el más reciente FlowDocument creado, GetPage() simplemente llama al paginador de ese documento para obtener la página adecuada. De lo contrario, descarta ese FlowDocument y crea un FlowDocument para la nueva sección, obtiene su paginador, luego llama al GetPage() en el paginador.

Esta estrategia le permite continuar a utilizar FlowDocuments como lo has sido, siempre y cuando se puede separar los datos en "secciones" cada uno con su propio documento. Su paginador personalizado trata de manera efectiva todos los FlowDocuments individuales como un documento grande. Esto es similar a la función "Documento maestro" de Word.

Si usted puede hacer que sus datos como una secuencia de imágenes apiladas verticalmente

En este caso, la misma técnica se puede utilizar.Durante el primer pase, todos los efectos visuales se generan en orden y se miden para ver cuántos encajarán en una página. Se construye una estructura de datos para indicar qué rango de imágenes (por índice o lo que sea) se encuentran en una página determinada. Durante este proceso, cada vez que una página se llena, la siguiente vista se coloca en una página nueva. Los encabezados y pies de página serían manejados de la manera obvia.

Durante la generación real de documentos, se implementa el método GetPage() para regenerar los elementos visuales previamente decididos para estar en una página dada y combinarlos utilizando un DockPanel vertical u otro panel de su elección.

He encontrado esta técnica más flexible a largo plazo porque no tiene que lidiar con las limitaciones de FlowDocument.

Cuestiones relacionadas