Remove (comment out) the first three buttons in the Preview form’s toolbar

Unless your application will use these functions, it is probably best to render them invisible so your users will not be distracted or confused. Use modi comm preview.wfm at the command line to edit the file, and just comment out the code for these buttons in Preview.wfm as shown below. The commented-out code is in BOLD.
CLASS myTBar(lMapi) of ToolBar
this.imagewidth = 24
this.flat = true
this.floating = false

* if lMapi = .t.
* Send Mail Button
* this.mail = new toolbutton(this)
* this.mail.bitmap = "RESOURCE #121 PREVIEW32.DLL"
* this.mail.OnClick = {;this.parent.form.ToolMail_OnClick()}
* this.mail.speedtip = "Send as Attachment"
* this.mail.statusmessage = "Send as Attachment"

** separator
* this.bs1 = new toolbutton(this)
* this.bs1.separator = true
* this.bs1.visible = true
* endif

** Open Preview File Button
* = new toolbutton(this)
* = {;this.parent.form.file_open()}
* = "Open Preview File"
* = "Open Preview File"

** Save Preview File Button
* = new toolbutton(this)
* = {;this.parent.form.file_save_onclick()}
* = "Save Preview File"
* = "Save Preview File"

** separator
* this.bs2 = new toolbutton(this)
* this.bs2.separator = true
* this.bs2.visible = true

// First Page Button
this.FirstPage = new toolbutton(this)
this.FirstPage.bitmap = "RESOURCE #102 PREVIEW32.DLL"
this.FirstPage.OnClick = {;this.parent.form.toolfirstpage_onclick()}
this.FirstPage.speedtip = "Go to First Page"
// this.FirstPage.statusmessage = "Go to First Page"


The p.nLine property, its cohorts, and other related positional properties

Thep.nLineproperty contains a value (defaulting to inches) that is used by the Printer class to specify the vertical position in the various AtSay()functions. In conjunction with the NextRow()andSetFont()functions, it is your connection to a very nice behind-the-scenes system which keeps track of where, vertically, on the page the current data is being written. Although you can always just use a decimal value (in inches) instead ofp.nLineto specify the vertical position of anything you print on the page, p.nLine is the best way to go for printing multiple lines of text. This is because the NextRow()function increments p.nLineby an amount that is appropriate for the current font height. That font height is stored in p.nLineInc(line increment) when you set the font via p.SetFont().

Whenp.StartPage()is called, p.nLineis automatically set to the height of the current font (assuming a font has been set) plus the offset to the start of the printable part of the page (see p.nTopOffseta few paragraphs below). This is done so that the vertical position does not default to zero (physical edge of the paper) where the printer cannot print.

For example, 11pt Arial has a height of 0.169", and a top offset for a laser printer might be 0.11". So, p.StartPage()setsp.nLineto 0.169 + 0.11 = 0.279". Test this by inserting the following lines just after p.StartPage(), but be sure you have called p.SetFont()prior to starting the page:
msgbox("p.nTopOffSet is " + ltrim(str(p.nTopOffSet,5,3)))
msgbox("p.nLineInc is " + ltrim(str(p.nLineInc,5,3)))
msgbox("p.nLine is " + ltrim(str(p.nLine,5,3)))

Then, each time p.NextRow() is called, it increments p.nLine by 0.169" or a multiple of it as specified in the NextRow() parameter. For example, p.NextRow(5) increments p.nLine by 0.169*5 = 0.845".

The p.EndPage() method also resets p.nLine to the font height (p.LineInc) plus p.nTopOffSet.

Important note about p.nLine: Although p.nLine is automatically incremented by p.NextRow(), you also have access to it programatically. For example, if you are printing labels in two or three columns on the page and wish to shift to the top of the next column of labels, just re-assign p.nLine to the appropriate value for the top label and printing will move there on the next AtSay(). And, with the next p.NextRow(), the value for p.nLine will be incremented.

Vic has also provided us with info from the printer driver — the offsets from the physical edges of the page to the beginning of the printable areas: p.nTopOffset is the distance from the physical top of the page to the beginning of the printable area. Moreover p.nLeftOffset is the analogous value for the left edge of the page. If you want to start printing as high on the page as possible, and to print as far to the left as possible, you could use these properties. For example,
p.AtSay(p.TopOffset + p.nLineInc, p.LeftOffset, "As high and as far left as printably possible...")

Of course, as long as a font has been previously set, p.StartPage already sets p.nLine to p.TopOffset + p.nLineInc so you don’t actually have to do this yourself.

One other related property is p.nPageWidth. This value represents the physical width of the page minus the widths of the unprintable areas on both sides of the page. So, for an HP4000 LaserJet printing in portrait orientation, the value of p.nPageWidth is 7.953". This makes sense, since laser printers can’t print closer to either edge than about 0.25"; so accounting for both sides of 8.5"-wide paper, the printable page width should be about 8". To specify the far right edge of the printable area as you would to maximally right-justify, use
p.AtSayRight(p.nLine, p.nPageWidth + p.nLeftOffset,"This is as far to the right as can be printed.")

The above specifies that the horizontal position be, from the physical left edge of the page, 7.953 + 0.263 = 8.216" to the right. That should be right about at the limit of printability on the right side. Since the right-alignment AtSayRight() is used, the text will be printed to the left of that point.

Fix Invalid subscripterence bug in Printdlg.wfm that chokes on incomplete printer entries in config.sys

Your version of the Printer class may have this one fixed, but if not, this bug will surface only on Win installations that have a missing port parameter for one or more of the printers in the list of installed printers in config.sys. Use modi comm printdlg.wfm at the command line to edit the file. Search for “Procedure Setup” (about line #604), then scroll down to the section of code shown below and add the lines shown in BOLD:
nLen = aDevices.size
aPrinters = new array(nLen, 3)
form.aPrinters = new array(nLen)
for i = 1 to nLen
  cDevice = aDevices[i]
  if empty(cDevice)
  cBuffer = space(1000)
  nResults = WinAPIGetProfileString('Devices', cDevice, "", cBuffer, 1000)
  aTmp = this.tokenize(substr(cBuffer, 1, nResults), ',')
  aPrinters[i, 1] = cDevice
  aPrinters[i, 2] = aTmp[1]
    aPrinters[i, 3] = aTmp[2]
  catch (exception e)
    form.aPrinters[i] = " "
  form.aPrinters[i] = cDevice

Enable strikeout font

Vic didn’t include strikeout “appearance” for fonts in Printer.prg, but he has provided code to enable it if you need it. You’ll need to use modi comm printer.prg at the command line to make changes in the two sections of code shown below. Use Ctrl-f to find the locations in the code. The new code that you’ll add is shown in BOLD:
* create a logfont structure to hold font
sLogFont = LogFontStruct()
sLogFont.setMem('lfStrikeOut', iif( this.lStrikeOut, 1, 0))
sLogFont.setMem('lfHeight', nInches)
sLogFont.setMem('lfWidth', nWidth*this.nMetricFactor)
sLogFont.setMem('lfEscapement', nDegrees)
sLogFont.setMem('lfWeight', nWeight)
sLogFont.setMem('lfItalic', nItalic)
sLogFont.setMem('lfUnderline', nUnderline)
sLogFont.setMem('lfFaceName', padr(alltrim(cName), 32, chr(0)))

* font properties
this.aFonts = new assocarray()  && array to hold the fonts
this.cFontTag = 0  && current font tag
this.hFont = 0  && used to hold handle to the font in use
this.nFontBold = 700  && set higher to increase bold weight
this.hOldFont = 0  && save hDC's original font for ResetDC()
this.lStrikeOut = .f.  && set to true for strikeout fonts

Since the appearance (1 or more of bold, underline, italics, strikeout) of a font is part of the structure of the font (unlike color, which doesn’t affect the shape of the characters), the strikeout appearance must be specified when the font is defined. This is analogous to defining a bold, italics, or underline font, but cannot (yet) be accomplished via a parameter in the DefineFont() function. Here is an example of defining a 10-point, not bold, not underline, not italics “Courier New” font with strikeout appearance, and whose tag is “4”:
p.lStrikeOut = .t.
p.DefineFont(4, "Courier New", -10, .f., .f., .f.)
p.lStrikeOut = .f.

Then use p.SetFont(4) to turn on the font (assuming Printer object is called “p”).


Visual dBASE 7.x users

Open your Project, then drag/drop from the appropriate windows in Navigator all the files listed above. The one exception, if your application won’t use the AtSayImage() function, is NVIEWLIB.DLL. It’s easy to tell if there are any files missing from the Project — just Build your application, load the exe and try to run a preview or report — you will get an error if you left any of the files out.

For InstallShield Express, you will also need to include any DLL files that you use from the Printer class. Load InstallShield and open the file for your application, then look for “Specify Components and Files”:

Click the Groups and Files line. Under Groups, click the “+” for “Program Files”.

This is where you specify all of the non-BDE files to be deployed with your application — your exe file, tables, image files, help files, etc. Load Explorer (you can use their Launch Explorer button), and drag into the File Groups window the DLL files you’ll need: Preview32.dll, and, if you’ll be using AtSayImage(), Nviewlib.dll.

If you accidentally drag a file in that you don’t need, remove it from the list by highlighting it and pressing the Del key.

Then click OK to accept the additions, and save this new configuration.

Visual dBASE 5.x users

When you compile, include in the Files to Compile all the files listed above. The one exception, if your application won’t use the AtSayImage() function, is NVIEW16.DLL. It’s easy to tell if there are any files missing from the Project — just compile your application, load the exe, and try to run a preview or report — you will get an error if you left any of the files out.