Printing StyledTextCtrl

I have been playing with this for a couple of days and I cannot get
printing and print previewing to work consistently. I adapted my
print setup from the wxPython In Action book and from the
StyledTextCtrl information. I can preview and print just fine, but I
cannot get the preview to match the print out exactly. For example,
when I preview the document I compute that the number of lines per
page are 45 but when I physically print it out I calculate 53 lines
per page. Also when I change certain zoom percentages, the line
numbers per page are sometimes not consistent with other zoom
percentages either. So the main problem is I cannot achieve
consistency between different zoom percentages of the print preview
what the actual print out will look like.

My page height calculations, margins etc all come out consistent. My
problems seem to be in calculating my scale and for some reason when I
try to retrieve the character height it differs significantly from the
preview to the actual printout.

I have attached my sample code below, I appreciate any assistance or
advice.

"""
    Assembles a document which can be sent to a printer. This will
also create a document for previewing as
    well.
"""
# Import outside dependancies
import wx
import wx.stc as stc

···

#
----------------------------------------------------------------------------------------------------------
class GeneratePrintableDoc(wx.Printout):

    def __init__(self, doc, margins=((wx.Point(15,15)),wx.Point
(15,15))):
        """
        Print the document
        """

        wx.Printout.__init__(self)

        # Set a reference to the STC document
        self.doc = doc

        # Set the margin
        self.margins = margins

    def HasPage(self, page):
        """
        Return whether this page exists
        """
        return page <= 1

    def GetPageInfo(self):
        return (1, 1, 1, 1)

    def CalculateScale(self, dc):
        """
        Calcualte the scale for the device context
        """

        # Get the device context parameters
        dcX_du, dcY_du = dc.GetSize()

        # Get the pixels per logical inch of the printer and screen
device context
        pPrintX_ppi, pPrintY_ppi = self.GetPPIPrinter()
        pScrnX_ppi, pScrnY_ppi = self.GetPPIScreen()

        # Get the physical dimensions of the page in millimeters and
pixels
        pPageX_px, pPageY_px = self.GetPageSizePixels()

        # Calculate the scale for the PPI between the device context
for the printer and screen
        self.ppiScale = float(pPrintX_ppi)/float(pScrnX_ppi)

        # Calculate the scale for the screen device context
        self.screenScaleX = self.ppiScale * (float(dcX_du)/float
(pPageX_px))
        self.screenScaleY = self.ppiScale * (float(dcY_du)/float
(pPageY_px))

        # Set the device context scale
        dc.SetUserScale(self.screenScaleX, self.screenScaleY)

        # Calcuate the number of pixels per millimeter
        self.PPMM = float(pPrintX_ppi)/(self.ppiScale * 25.4)

    def CalcualteLayout(self, dc):
        """
        Calcualate the layout of the document
        """
        # Get the size of the screen device context in millimeters
        dcX_px, dcY_px = dc.GetSize()

        # Get the top and bottom coordinates
        topLeft, bottomRight = self.margins

        # Calculate the top rectangle point in pixels
        self.x1 = topLeft.x * self.PPMM
        self.y1 = topLeft.y * self.PPMM
        self.x2 = (dc.DeviceToLogicalXRel(dcX_px) - bottomRight.x *
self.PPMM)
        self.y2 = (dc.DeviceToLogicalYRel(dcY_px) - bottomRight.y *
self.PPMM)

        # Calculate the printable page width and height
        self.pageHeight = self.y2 - self.y1
        self.pageWidth = self.x2 - self.x1

        # Set the font for the device
        font = wx.Font(12, wx.TELETYPE, wx.NORMAL, wx.NORMAL)
        dc.SetFont(font)

        self.charHeight = dc.GetCharHeight()
        self.charWidth = dc.GetCharWidth()

        # Calculate the allowable lines and columns per page
        self.linesPerPage = int(self.pageHeight/self.charHeight)
        self.linesPerColumn = int(self.pageWidth/self.charWidth)

    def OnPreparePrinting(self):
        """
        Calculate the scale and layout. Compute the number of pages
for this document
        """
        dc = self.GetDC()
        self.CalculateScale(dc)
        self.CalcualteLayout(dc)

        # Here I would calculate the number of pages in the document

    def OnPrintPage(self, page):
        """
        Create the device context and then use the STC FormatRange to
generate the final document
        """
        dc = self.GetDC()
        self.CalculateScale(dc)
        self.CalcualteLayout(dc)

        rect = wx.RectPP((self.x1, self.y1), (self.x2, self.y2))

        dc.SetPen(wx.Pen("black",1))
        dc.DrawRectangleRect(rect)

        # Format the text
        ep = self.doc.FormatRange(True, 0, self.doc.GetLength(), dc,
dc, rect, rect)

        return True

class MyApp(wx.App):

    def OnInit(self):
        frame = SimpleDoc(None)
        frame.Show(True)
        self.SetTopWindow(frame)
        return True

class SimpleDoc(wx.Frame):

    def __init__(self, parent):
        wx.Frame.__init__(self, parent, wx.ID_ANY)

        self.doc = stc.StyledTextCtrl(self, wx.ID_ANY)

        for i in range(0,128):
            self.doc.AddText("line: %d\n" % i)

        # Create the menu bar
        self.mb = wx.MenuBar()

        self.mFile = wx.Menu()
        PRINTPREVIEW = wx.NewId()
        self.mFile.Append(PRINTPREVIEW, "Print Preview")
        self.mb.Append(self.mFile, "File")

        self.SetMenuBar(self.mb)

        szr = wx.BoxSizer(wx.VERTICAL)
        szr.Add(self.doc,1,wx.EXPAND)

        self.Bind(wx.EVT_MENU, self.Print, id=PRINTPREVIEW)

    def Print(self, evt):
        printout = GeneratePrintableDoc(self.doc)
        printout2 = GeneratePrintableDoc(self.doc)

        preview = wx.PrintPreview(printout, printout2, wx.PrintData())
        pre_frame = wx.PreviewFrame(preview, None, "Print Preview")

        pre_frame.Initialize()
        pre_frame.Show()

# Main
if __name__=='__main__':

    app = MyApp(0)
    app.MainLoop()

Scott Hall wrote:

I have been playing with this for a couple of days and I cannot get
printing and print previewing to work consistently. I adapted my
print setup from the wxPython In Action book and from the
StyledTextCtrl information. I can preview and print just fine, but I
cannot get the preview to match the print out exactly. For example,
when I preview the document I compute that the number of lines per
page are 45 but when I physically print it out I calculate 53 lines
per page. Also when I change certain zoom percentages, the line
numbers per page are sometimes not consistent with other zoom
percentages either. So the main problem is I cannot achieve
consistency between different zoom percentages of the print preview
what the actual print out will look like.

My page height calculations, margins etc all come out consistent. My
problems seem to be in calculating my scale and for some reason when I
try to retrieve the character height it differs significantly from the
preview to the actual printout.

Have you looked at how Editra does it? (I haven't but it seems to work correctly so I assume that Cody is doing something right that is worth copying.)

···

--
Robin Dunn
Software Craftsman

Hi,

Scott Hall wrote:

I have been playing with this for a couple of days and I cannot get
printing and print previewing to work consistently. I adapted my
print setup from the wxPython In Action book and from the
StyledTextCtrl information. I can preview and print just fine, but I
cannot get the preview to match the print out exactly. For example,
when I preview the document I compute that the number of lines per
page are 45 but when I physically print it out I calculate 53 lines
per page. Also when I change certain zoom percentages, the line
numbers per page are sometimes not consistent with other zoom
percentages either. So the main problem is I cannot achieve
consistency between different zoom percentages of the print preview
what the actual print out will look like.

My page height calculations, margins etc all come out consistent. My
problems seem to be in calculating my scale and for some reason when I
try to retrieve the character height it differs significantly from the
preview to the actual printout.

Have you looked at how Editra does it? (I haven't but it seems to work
correctly so I assume that Cody is doing something right that is worth
copying.)

Didn't have time to look over your code too thoroughly but I see that you have a hard coded font that you set for the dc. You should make sure to use the same font in your DC that you do in the stc instance that you are printing for.

See ed_print.py for more examples (note though that the stc class used in that file is an EdStc and not a base StyledTextCtrl.). The code there worked correctly as far as I could tell last time I used it (I cant clearly remember the last time I used an actual printer so some bit rot may be present :wink: )

Cody

···

On Aug 26, 2009, at 6:33 PM, Robin Dunn wrote:

Hi Scott,
did you get it work?

···

On Aug 26, 4:43 pm, Scott Hall <smhall...@gmail.com> wrote:

I have been playing with this for a couple of days and I cannot get
printing and print previewing to work consistently. I adapted my
print setup from the wxPython In Action book and from the
StyledTextCtrl information. I can preview and print just fine, but I
cannot get the preview to match the print out exactly. For example,
when I preview the document I compute that the number of lines per
page are 45 but when I physically print it out I calculate 53 lines
per page. Also when I change certain zoom percentages, the line
numbers per page are sometimes not consistent with other zoom
percentages either. So the main problem is I cannot achieve
consistency between different zoom percentages of the print preview
what the actual print out will look like.

My page height calculations, margins etc all come out consistent. My
problems seem to be in calculating my scale and for some reason when I
try to retrieve the character height it differs significantly from the
preview to the actual printout.

I have attached my sample code below, I appreciate any assistance or
advice.

"""
Assembles a document which can be sent to a printer. This will
also create a document for previewing as
well.
"""
# Import outside dependancies
import wx
import wx.stc as stc

#
----------------------------------------------------------------------------------------------------------
class GeneratePrintableDoc(wx.Printout):

def \_\_init\_\_\(self, doc, margins=\(\(wx\.Point\(15,15\)\),wx\.Point

(15,15))):
"""
Print the document
"""

    wx\.Printout\.\_\_init\_\_\(self\)

    \# Set a reference to the STC document
    self\.doc = doc

    \# Set the margin
    self\.margins = margins

def HasPage\(self, page\):
    &quot;&quot;&quot;
    Return whether this page exists
    &quot;&quot;&quot;
    return page &lt;= 1

def GetPageInfo\(self\):
    return \(1, 1, 1, 1\)

def CalculateScale\(self, dc\):
    &quot;&quot;&quot;
    Calcualte the scale for the device context
    &quot;&quot;&quot;

    \# Get the device context parameters
    dcX\_du,  dcY\_du  = dc\.GetSize\(\)

    \# Get the pixels per logical inch of the printer and screen

device context
pPrintX_ppi, pPrintY_ppi = self.GetPPIPrinter()
pScrnX_ppi, pScrnY_ppi = self.GetPPIScreen()

    \# Get the physical dimensions of the page in millimeters and

pixels
pPageX_px, pPageY_px = self.GetPageSizePixels()

    \# Calculate the scale for the PPI between the device context

for the printer and screen
self.ppiScale = float(pPrintX_ppi)/float(pScrnX_ppi)

    \# Calculate the scale for the screen device context
    self\.screenScaleX = self\.ppiScale \* \(float\(dcX\_du\)/float

(pPageX_px))
self.screenScaleY = self.ppiScale * (float(dcY_du)/float
(pPageY_px))

    \# Set the device context scale
    dc\.SetUserScale\(self\.screenScaleX, self\.screenScaleY\)

    \# Calcuate the number of pixels per millimeter
    self\.PPMM = float\(pPrintX\_ppi\)/\(self\.ppiScale \* 25\.4\)

def CalcualteLayout\(self, dc\):
    &quot;&quot;&quot;
    Calcualate the layout of the document
    &quot;&quot;&quot;
    \# Get the size of the screen device context in millimeters
    dcX\_px,  dcY\_px  = dc\.GetSize\(\)

    \# Get the top and bottom coordinates
    topLeft, bottomRight = self\.margins

    \# Calculate the top rectangle point in pixels
    self\.x1 = topLeft\.x \* self\.PPMM
    self\.y1 = topLeft\.y \* self\.PPMM
    self\.x2 = \(dc\.DeviceToLogicalXRel\(dcX\_px\) \- bottomRight\.x \*

self.PPMM)
self.y2 = (dc.DeviceToLogicalYRel(dcY_px) - bottomRight.y *
self.PPMM)

    \# Calculate the printable page width and height
    self\.pageHeight = self\.y2 \- self\.y1
    self\.pageWidth  = self\.x2 \- self\.x1

    \# Set the font for the device
    font = wx\.Font\(12, wx\.TELETYPE, wx\.NORMAL, wx\.NORMAL\)
    dc\.SetFont\(font\)

    self\.charHeight = dc\.GetCharHeight\(\)
    self\.charWidth  = dc\.GetCharWidth\(\)

    \# Calculate the allowable lines and columns per page
    self\.linesPerPage = int\(self\.pageHeight/self\.charHeight\)
    self\.linesPerColumn = int\(self\.pageWidth/self\.charWidth\)

def OnPreparePrinting\(self\):
    &quot;&quot;&quot;
    Calculate the scale and layout\.  Compute the number of pages

for this document
"""
dc = self.GetDC()
self.CalculateScale(dc)
self.CalcualteLayout(dc)

    \# Here I would calculate the number of pages in the document

def OnPrintPage\(self, page\):
    &quot;&quot;&quot;
    Create the device context and then use the STC FormatRange to

generate the final document
"""
dc = self.GetDC()
self.CalculateScale(dc)
self.CalcualteLayout(dc)

    rect = wx\.RectPP\(\(self\.x1, self\.y1\), \(self\.x2, self\.y2\)\)

    dc\.SetPen\(wx\.Pen\(&quot;black&quot;,1\)\)
    dc\.DrawRectangleRect\(rect\)

    \# Format the text
    ep = self\.doc\.FormatRange\(True, 0, self\.doc\.GetLength\(\), dc,

dc, rect, rect)

    return True

class MyApp(wx.App):

def OnInit\(self\):
    frame = SimpleDoc\(None\)
    frame\.Show\(True\)
    self\.SetTopWindow\(frame\)
    return True

class SimpleDoc(wx.Frame):

def \_\_init\_\_\(self, parent\):
    wx\.Frame\.\_\_init\_\_\(self, parent, wx\.ID\_ANY\)

    self\.doc = stc\.StyledTextCtrl\(self, wx\.ID\_ANY\)

    for i in range\(0,128\):
        self\.doc\.AddText\(&quot;line: %d\\n&quot; % i\)

    \# Create the menu bar
    self\.mb = wx\.MenuBar\(\)

    self\.mFile = wx\.Menu\(\)
    PRINTPREVIEW = wx\.NewId\(\)
    self\.mFile\.Append\(PRINTPREVIEW, &quot;Print Preview&quot;\)
    self\.mb\.Append\(self\.mFile, &quot;File&quot;\)

    self\.SetMenuBar\(self\.mb\)

    szr = wx\.BoxSizer\(wx\.VERTICAL\)
    szr\.Add\(self\.doc,1,wx\.EXPAND\)

    self\.Bind\(wx\.EVT\_MENU, self\.Print, id=PRINTPREVIEW\)

def Print\(self, evt\):
    printout  = GeneratePrintableDoc\(self\.doc\)
    printout2 = GeneratePrintableDoc\(self\.doc\)

    preview = wx\.PrintPreview\(printout, printout2, wx\.PrintData\(\)\)
    pre\_frame = wx\.PreviewFrame\(preview, None, &quot;Print Preview&quot;\)

    pre\_frame\.Initialize\(\)
    pre\_frame\.Show\(\)

# Main
if __name__=='__main__':

app = MyApp\(0\)
app\.MainLoop\(\)