Quick/Dirty Print

Printer Selection

Printing Text

Print Preview

Printing Graphics




WPF Tutorial Contents

APL  Home Page




Quick/Dirty Print


No selection; just throwing a text vector (with embedded newlines) to the default printer...

 ∇ KwikPrint w;⎕IO;⎕ML;pd;doc;dps;⎕USING;items
    ⍝ Quick/dirty print of character matrix to default printer
      ⎕IO ⎕ML←0 3
      ⎕USING←#.WPF.Using
      w←∊w,⎕UCS 13
      pd←⎕NEW PrintDialog
      doc←⎕NEW FlowDocument(⎕NEW Paragraph(⎕NEW Run(⊂w)))
      dps←IDocumentPaginatorSource ⎕CLASS doc
      pd.PrintDocument(dps.DocumentPaginator'Printing Something')
 ∇


Printer Selection


As usual, Microsoft impresses by adding confusion.  There are two classes called <PrintDialog> in .NET, the one we want for WPF-based applications is in System.Windows.Controls.  For the nonce, lets show a printer selection dialogue and return the selected printer name...

∇ z←PrinterSelect w;⎕IO;⎕ML;⎕USING;pdg
  ⍝ Printer selection
    ⎕IO ⎕ML ⎕USING←0 3 #.WPF.Using
    pdg←⎕NEW PrintDialog
    z←pdg.ShowDialog
    z,←⊂z/pdg.PrintQueue.Name


Notice here that there's always a Name property, even if the user decides to cancel.

It is, of course, not a good idea to actually use this stand-alone function - put the code inline (as shown below).

Printing Text


Becoming a little more ambitious, we now want to allow our application to specify the font and our user to be able to select the printer...

  a DogPrint w;⎕IO;⎕ML;pd;doc;dps;⎕USING;font;jobname;select
⍝ Tester for text/figure printing
⍝ Example ←→ 'percy' 1 DogPrint PrintSample
 ⎕IO ⎕ML←0 3
 ⎕USING←#.UTIL.PRINT.PrintUsing ⍬
 jobname select←a
 pd←⎕NEW PrintDialog
 :If select
     rc←pd.ShowDialog
     :If ~rc
         :Return
     :EndIf
 :EndIf
 doc←pd DogPrintFlowDoc w
 dps←IDocumentPaginatorSource ⎕CLASS doc
 pd.PrintDocument(dps.DocumentPaginator jobname)


In use...

  'percy' 1 DogPrint   ('Text' ('Constantia' 10) (10⍴'a')) ('Text' ('Felix Titling' 20) (10⍴'b')) ('Text' ('Impact' 8)(10⍴'c'))

where...

∇ flowDoc←pd PrintFlowDoc docs;⎕IO;⎕ML;ff;pd;font;doc;run;para;ic;source;image;bi
    ⍝ Create FlowDocument for printing
      ⎕IO ⎕ML←0 3
      flowDoc←⎕NEW FlowDocument ⍬
      flowDoc.PageHeight←pd.PrintableAreaHeight
      flowDoc.PageWidth←pd.PrintableAreaWidth
      flowDoc.PagePadding←⎕NEW Thickness 50
      flowDoc.ColumnWidth←pd.PrintableAreaWidth
      flowDoc.ColumnGap←0
      :For doc :In docs
          :Select ↑doc
          :Case 'Text'
              run←⎕NEW Run(2⌷doc)
              para←⎕NEW Paragraph run
              ff←⎕NEW FontFamily(⊂1 0⊃doc)
              para.FontFamily←ff
              para.FontSize←1 1⊃doc
          :Case 'Image'
              para←⎕NEW Paragraph ⍬
              para.fig←para.⎕NEW Figure ⍬
              para.fig.Background←Brushes.Red
              para.fig.blockcontainer←para.fig.⎕NEW BlockUIContainer ⍬
              para.fig.blockcontainer.Background←Brushes.Green
              para.fig.blockcontainer.image←para.fig.blockcontainer.⎕NEW Image ⍬
              para.fig.blockcontainer.image.Height←2000
              para.fig.blockcontainer.image.Width←50
              ic←⎕NEW ImageSourceConverter''
              source←ic.ConvertFrom⊂1⊃doc
              para.fig.blockcontainer.image.Source←source
          :EndSelect
          flowDoc.Blocks.Add para
      :EndFor
    ∇


Watch this space for further enlightenment/enhancement...

Print Preview


Here's an adaptation of a function from Pierre Gilbert which illustrates a slightly different (more complex) approach to building the printed document.  It may be that this is what's really necessary.

What we're aiming for here is a series of paragraphs with font specified for each.

    ∇ PrintPreview docs;flowDoc;docViewer;flowDocPaginator;package;packageUri;packageUriString;printDLG;sink;wDV;xpsDoc;xpsSm;xpsStream;⎕USING;⎕IO;⎕ML;ff;pd
⍝ Adaptation of Pierre Gilbert's <PrintPreview> to handle multiple documents and maybe graphic images
      ⎕IO ⎕ML←0 3
      ⎕USING←PrintUsing ⍬
      pd←⎕NEW PrintDialog
      flowDoc←pd PrintFlowDoc docs
      flowDocPaginator←(IDocumentPaginatorSource ⎕CLASS flowDoc).DocumentPaginator    
      xpsStream←⎕NEW MemoryStream
      package←Package.Open(xpsStream,FileMode.Create,FileAccess.ReadWrite)
      packageUriString←'memorystream://printstream'
      packageUri←⎕NEW Uri(⊂packageUriString)
      PackageStore.AddPackage(packageUri,package)
      xpsDoc←⎕NEW XpsDocument(package,(CompressionOption.SuperFast),⊂packageUriString) 
      xpsSm←⎕NEW XpsSerializationManager((⎕NEW XpsPackagingPolicy(xpsDoc)),0)
      xpsSm.SaveAsXaml flowDocPaginator
      docViewer←⎕NEW DocumentViewer  
      docViewer.Document←xpsDoc.GetFixedDocumentSequence
      wDV←⎕NEW Window 
      wDV.Title←'Print Preview'
      wDV.Content←docViewer
      {}wDV.ShowDialog
      PackageStore.RemovePackage(packageUri)
      xpsDoc.Close
      package.Close
      xpsStream.Close
      xpsStream.Dispose
    ∇

And, in use...

  #.UTIL.PRINT.PrintPreview ('Text' ('Tahoma' 12) 'aaaa') ('Text' ('Verdana' 16) 'bbb')

Printing Graphics

PrintVisual is the first (easy, but limited) thing you need to know about; there's a usefully simple page here showing some nerd-language examples.

What makes PrintVisual both easy and limited is that it prints precisely what you send it; there's no pagination, no scrolling - just what you see is what you print.  More ambitious requirements - you'll need to start exploring PrintDocument options.

For a simple example of using this from APL here are some snippets from an APLet which superimposes some lines onto a map image, and has some further text annotation.

I keep separate (updated in parallel) windows for the screen (which the user interacts with) and printing (which stays invisible, just getting populated with what's for printing.

The XAML for the print window is...

<?xml version="1.0" encoding="utf-8"?>
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Name="wdwDogPrint">
  <ScrollViewer Name="svMap" VerticalScrollBarVisibility="Hidden" Height="800" Width="700">
    <StackPanel>
      <Grid>
        <Image Name="imgMap" />
        <Canvas Name="cvsTrack" />
      </Grid>
      <TextBox Name="txtFileName" />
      <TextBox Name="txtCatalogue" />
      <TextBox Name="txtDate" />
      <TextBox Name="txtMap" />
    </StackPanel>
  </ScrollViewer>
</Window>

Notice how both the Image and the Canvas are contained in the Grid with no row or column definitions - done like this anything drawn onto the Canvas is visually overlaid onto the Image.  Notice the jiggery-pokery in the ScrollViewer definition (making sure, in my pig-ignorant way, that sizes are appropriate for an A4 page and there's no chance of an unwanted ScrollBar showing up).

Populate in the usual way, here's a snippet...

:If #.UTIL.FILE.FileExists mapfile
     ic←⎕NEW ImageSourceConverter''
     source←ic.ConvertFrom⊂mapfile
     wdw.imgMap.Source←source
     wdw.imgMap.Height←700
 :EndIf
 wdw.txtFileName.Text←'File: ',file

What's important here is to force the size of the Image source.

Add this line where it suits you...

pd.PrintVisual(#.wdwDogPrint.svMap printname)

And, once you've done that - bingowingo, out pops the printing.