Impresión en Xojo

La impresión de documentos continúa suponiendo una parte realmente importante en las aplicaciones de caracter empresarial. En el siguiente artículo encontrarás las principales pautas a tener en cuenta a la hora de implementar dicha capacidad en tus desarrollos multiplataforma con Xojo. Esta es una traducción al castellano de la entrada disponible en la web de desarrolladores de Xojo.

En el caso de las aplicaciones de escritorio, la impresión es muy similar al dibujado en la pantalla. En vez de dibujar en el objeto Graphics de un control Canvas el dibujado se produce sobre un objeto especialmente creado para la impresión. En dicho objeto se proporcionan los métodos necesarios para crear nuevas páginas y obtener también los ajustes de impresión.

Cuando se trata de aplicaciones Web, la impresión es más limitada. Por lo general lo que mejor funciona es crear sencillamente el HTML, mostrarlo en un control HTML Viewer e imprimirlo.

Impresión de escritorio

Ajustes de impresión

Antes de que podamos imprimir algo hemos suele presentarse al usuario la opción de seleccionar la impresora a usar. Esto es lo que se puede hacer utilizando la clase PrinterSetup, encargada de mostrar el cuadro de diálogo correspondiente a Ajustar Página para el sistema operativo en cuestión. Además, esta clase devuelve los ajustes establecidos, de modo que puedas utilizarlos posteriormente de nuevo sin que debas de volver a consultar al usuario.

' mPrinterSettings es una propiedad String de la ventana
Dim ps As New PrinterSetup
ps.SetupString = mPrinterSettings
 If ps.PageSetupDialog Then
  mPrinterSettings = ps.SetupString
End If

Dado que mPrinterSettings es una String, puedes guardarla fuera de la app (como por ejemplo en una base de datos o en un archivo), de modo que puedas emplearla la próxima vez que imprima el usuario.

La clase PrinterSetup tiene, entre otras, propiedades para los ajustes de impresión como la orientación, tamaño de página y resolución. Lo que has de tener en cuenta es que PrinterSetup no está soportado en las aplicaciones de Linux.

Imprimir Texto y Gráficos

Para imprimir texto y gráficos sólo has de dibujar sobre el objeto Graphics de la impresora. Para obtener dicho objeto puedes llamar a los métodos globales OpenPrinterDialog u OpenPrinter. La única diferencia entre estos dos métodos es que uno muestra el diálogo de Impresión y el otro no. Dado que el sistema de impresión en macOS tiene la capacidad de imprimir a PDF, puedes emplear este método para generar archivos PDF con facilidad. En Windows puedes instalar un controlador de impresión “Imprimir a PDF” que permita generar archivos PDF a partir de la impresión.

Puesto que estás dibujando en un objeto Graphics, puedes utilizar todos los comandos que ya estás habituado a utilizar en un objeto gráfico normal. Además, también puedes utilizar el método NextPage específico de impresión para crear múltiples páginas. El siguiente ejemplo imprime “Hola” en la página 1 y “Mundo” en la página 2:

Dim ps As New PrinterSetup
If ps.PageSetupDialog Then
  Dim page As Graphics
  page = OpenPrinterDialog(ps)
  If page <> Nil Then
    ' Dibuja el texto en la página 1 con 2,54 cm de márgen superio/izquierdo
    page.DrawString("Hola", ps.HorizontalResolution, ps.VerticalResolution)
    page.NextPage
    ' Dibuja texto en la página 2 con 2,54 cm sobre el marge superior/izquierdo
    page.DrawString("Mundo", ps.HorizontalResolution, ps.VerticalResolution)
  End If
End If

También puedes utilizar cualquier ajuste de impresora que se haya almacenado previamente utilizando la clase PrinterSetup.

Dim ps As New PrinterSetup
ps.SetupString = mPrinterSettings ' Propiedad que contiene ajustes guardados previamente
If ps.PageSetupDialog Then
  mPrinterSettings = ps.SetupString ' Obtener nuevos ajustes
  Dim page As Graphics
  ' Usar ajustes de Impresión
  page = OpenPrinterDialog(ps)
  If page <> Nil Then
    ' Dibuja texto en la página 1 con 2,54 cm sobre el margen superior/izquierdo
    page.DrawString("Hola", ps.HorizontalResolution, ps.VerticalResolution)
    page.NextPage
    ' Dibuja texto en la página 2 con 2,54 cm sobre el margen superior/izquierdo
    page.DrawString("Mundo", ps.HorizontalResolution, ps.VerticalResolution)
  End If
End If

Cada vez que llamas al método NextPage se envía la página actual a la impresora y se borra el objeto gráfico (en este caso la página) de modo que puedas comenzar a dibujar el nuevo contenido. La página final se envía a la impresora cuando la página sale del ámbito.

Para imprimir sin mostrar el diálogo de Impresión, sólo has de invocar el método OpenPrinter:

Dim ps As New PrinterSetup
Dim page As Graphics
page = OpenPrinter(ps)
If page <> Nil Then
  ' Dibuja texto en la página 1 con 2,54 cm sobre el margen superior/izquierdo
  page.DrawString("Hola", ps.HorizontalResolution, ps.VerticalResolution)
  page.NextPage
  ' Dibuja texto en la página 2 con 2,54 cm sobre el margen superior/izquierdo
  page.DrawString("Mundo", ps.HorizontalResolution, ps.VerticalResolution)
  page.NextPage
End If

Imprimir múltiples páginas de texto

Para imprimir múltiples páginas, has de parsear cada línea e imprimirla si cabe en la página. Si no cabe, entonces llamas a NextPage de modo que el texto comenzará a imprimirse en la parte superior de la siguiente página.

Una aproximación habitual consiste en dividir el texto en párrafos, llevando la cuenta de la altura de la página y la altura del siguiente párrafo de texto a imprimir para estar seguro de que cabrá en la página. Si no es así, entonces se envía la página actual a la impresora y se crea una nueva página para comenzar la impresión del siguiente párrafo.

Este código muestra como imprimir texto de una TextArea en múltiples páginas:

Dim ps As New PrinterSetup
If ps.PageSetupDialog Then
  Dim page As Graphics
  page = OpenPrinterDialog(ps)
  If page <> Nil Then
    ' Dibuja texto en la página 1 con 2,54 cm sobre el margen superior/izquierdo
    Dim leftMargin As Double = ps.HorizontalResolution
    Dim topMargin As Double = ps.VerticalResolution
    page.TextSize = Val(FontPopup.Text)
    ' Para imprimir múltiples páginas, has de parsear cada línea e imprimirla
    ' sólo si cabe en la página. Si no es así, entonces
    ' llamas a NextPage de modo que comiences a imprimir en la parte superior
    ' de la siguiente página.
    ' En primer lugar, divide el texto en múltiples párrafos
    Dim paragraphs() As String = ReplaceLineEndings(TextArea1.Text, EndOfLine).Split(EndOfLine)
    Dim pageTextHeight As Double = topMargin
    Dim newpageTextHeight As Double
    Dim lineWidth As Double
    For i As Integer = 0 To paragraphs.Ubound
      newpageTextHeight = pageTextHeight + page.StringHeight(paragraphs(i), ps.Width - leftMargin 2)
      If newPageTextHeight > ps.Height - topMargin 2 Then
        ' La cadena no cabe en la página, así que nos movemos a la página siguiente
        page.NextPage
        pageTextHeight = topMargin
        newpageTextHeight = pageTextHeight + page.StringHeight(paragraphs(i), ps.Width - leftMargin * 2)
      End If
      ' Si la cadena cabe en la página, dibújala
      page.DrawString(paragraphs(i), leftMargin, pageTextHeight, ps.Width - leftMargin * 2)
      ' Mantén un contador acumulado con la altura de la página a medida que se añaden nuevas cadenas
      pageTextHeight = newpageTextHeight ' ajusta la altura de texto de la página
      pageTextHeight = pageTextHeight + topMargin / 8 ' 1/8 de pulgada entre párrafos
    Next
  End If
End If

El código anterior te proporciona una buena cantidad de control, pero aun queda trabajo por hacer para controlarlo todo por ti mismo. En la siguiente sección verás como puedes sacar provecho de StyledTextPrinter para hacerlo más sencillo. Además, utilizando un informe (Report).

Dado que las TextArea pueden mostrar texto con estilo y múltiples tamaños de fuente, probablemente quieras retener el texto con estilo a la hora de imprimir. la clase StyledTextPrinter se utiliza con este propósito, usando para ello el método DrawBlock.

La clase StuledTextPrinter sólo funciona con apps macOS.

Para imprimir texto con estilo has de crear en primer lugar un objeto StyledTextPrinter y llamar a continuación el método StyledTextPrinter del objeto TextArea (indicando el objeto gráfico a usar y el ancho del texto), para obtener una instancia de una StyledTextPrinter que pueda utilizarse en la impresión.

Con esta instancia puedes llamar al método DrawBlock para dibujar el texto con estilo en la página (indicando las coordenadas de inicio y la altura).

Este ejemplo imprime texto con estilo en una TextArea:

Dim stp As StyledTextPrinter
Dim g As Graphics
Dim p As New PrinterSetup
If p.PageSetupDialog Then
  g = OpenPrinterDialog(p)
  If g <> Nil Then
    ' Ajusta el ancho a 7,5 pulgadas
    stp = PrintTextArea.StyledTextPrinter(g, 7.5 * p.HorizontalResolution)
    Do Until stp.EOF
      ' Rellena la página con texto
      ' y una altura de 10 pulgadas
      stp.DrawBlock(0, 0, 10 * p.VerticalResolution)
      If Not stp.EOF Then
        ' Hay más texto, de modo que añadimos una página
        g.NextPage
      End If
    Loop
  End If
End If

Para soportar la impresión con estilo, la TextArea debe de tener activadas sus propiedades MultiLine y Styled.

Si el texto a imprimir es mayor que lo que cabe en el bloque indicado, entonces puedes iterar por el texto hasta que se imprima todo. Puedes hacer esto comprobando la propiedad EOF de la clase StyledTextPrinter tras cada llamada a DrawBlock.

Este ejemplo imprime los contenidos de una TextArea en dos columnas con un cuarto de pulgada de espacio entre las columnas:

Dim g As Graphics
Dim p As New PrinterSetup
If p.PageSetupDialog Then
  g = OpenPrinterDialog(p)
  ' 1 pulgada de margen
  Dim leftRightMargin As Double = 1 p.HorizontalResolution
  Dim topBottomMargin As Double = 1 p.VerticalResolution
  ' Ancho de página tras tener en cuenta los márgenes
  Dim pageWidth As Double = p.Width - (leftRightMargin * 2)
  ' El espacio entre columnas es de 1/4 de pulgada
  Dim columnGap As Double = leftRightMargin / 4
  ' Calcula el ancho de columna
  ' Resta el espacio entre columnas para la página
  ' Y divide el resultado entre dos
  Dim columnWidth As Double = (pageWidth - columnGap) / 2
  ' Tamaño de la página tras contemplar los márgenes
  Dim pageHeight As Double = p.Height - (topBottomMargin 2)
  Dim stp As StyledTextPrinter
  stp = PrintTextArea.StyledTextPrinter(g, pageWidth)
  stp.Width = columnWidth
  Dim columnToPrint As Integer = 1
  Do Until stp.EOF
    stp.DrawBlock(leftRightMargin + (columnWidth + columnGap) (columnToPrint - 1), _
    topBottomMargin, pageHeight)
    If columnToPrint = 2 Then ' printing last column
      If Not stp.EOF Then ' more text to print
        g.NextPage
        columnToPrint = 1
      End If
    Else ' more columns to print on this page
      columnToPrint = columnToPrint + 1
    End If
  Loop
End If

Impresión HTML

Otra técnica que puedes usar es la de crear lo que desees imprimir en HTML, mostrarlo en un HTMLViewer y llamar luego a su método Print. Esto puede resultar de utilidad en algunos casos para la impresión de informes sencillos. Por ejemplo, este código carga un HTMLViewe con algo de HTML:

Dim html As String = "Hello, World!"
HTMLViewer1.LoadPage(html, GetTemporaryFolderItem)

Este código mostrará a continuación el cuadro de diálogo de impresión para imprimir el HTML:

HTMLViewer1.Print(True)

Como puedes observar resulta realmente sencillo, y en combinación con la aplicación de CSS puede resultar incluso más beneficioso. Por ejemplo, podrías utilizar la clase MarkdownParser para Xojo para generar el HTML con los estilos CSS deseados a partir de un conjunto de texto escrito en Markdown e imprimir el resultado tal y como se ha indicado.

2 comentarios en “Impresión en Xojo

  1. gabriel camargo gonzalez

    todo eso esta bien pero si cambio xojo2018r1 la calidad se pìerde
    porque el GDI vs directd2 no se entienden lo compilo para 32 bits
    y mis impresiones salen mal sin embargo cuando compilo con xojo2013 no tengo problemas
    espero tu comentario
    gracias

    1. Javier Rodriguez

      Hola,

      Sin ver el código fuente… es imposible saber por qué sale mal en 2018r1. ¿Igual tiene que ver con la relación entre HDPI y la impresión? Lo dicho, necesito más pistas 😉

      Javier

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *