The first command you will need to give ddoc is dpStartDoc. This tells ddoc that you want
to print or preview a document. It returns a handle to the new document. Some folks get a
little intimidated when they hear the word handle. Don't be. It's just an identifier for
the document. It must be passed to the engine so that ddoc knows which document you're
working on. It's necessary in the 16-bit world because all of your .exe programs that call
ddoc16.dll share the same copy of the dll. It's needed in the 32-bit world because all
threads of a program share the dll.
dpStartDoc has quite a few parameters. Don't let this intimidate you either! Usually, you'll
want to create a wrapper function so you don't have to remember all of the parameters.
Below I describe the parameters and then give an example of how a simple wrapper can make calling it easier.
Parameter |
Description |
hParent |
Reserved for future use. Please put a Zero here |
zTitle |
Title of the document, displayed in the title bar of the viewer and identifies the print job |
zFile |
Name of the document to create. This parameter may be passed as "" if you want the viewer to determine a unique temp file and use that. Note: If you pass "" and want to find out what the file name is, you can to call dpGetFileName.to fill a buffer with the name. Note that "" is a null string. You can pass ByVal %NULL in PowerBasic, or '' in Delphi. |
iUOM |
Either DDOC_INCH or DDOC_CM indicating whether you are referencing positions in inches or cm |
iPaper |
Paper size for the first page - see the DDOC_PAPER_xxx constants |
iOrient |
Page orientation for the first page |
iBin |
Paper Bin for the first page - see the DDOC_BIN_xxx constants |
iOptions |
Other document options: this sets the initial zoom setting (DDOC_ZOOMFIT, etc - see constants) and turns on and off some optional buttons (DDOC_ALLOWEMAPI, etc.) |
'- Here is a wrapper function for dpStartDoc
' If you have to dpStartDoc a bunch of times in your
' program, odds are the function will look similar each
' time it's called. Anyway, here's a wrapper.
'
Function myStartDoc ( zTitle$ ) as Integer
myStartDoc = dpStartDoc ( 0, zTitle$, '', DDOC_INCH, DDOC_PAPER_LETTER, _
DDOC_PORTRAIT, DDOC_SYSTEM_DEFAULT, DDOC_ZOOMWIDTH )
End Function
Now that we know how to initialize the document, here's a basic outline of how to work ddoc.
'- Here's a skeleton call
'
Sub MainSub ()
Dim hDoc%
hDoc% = myStartDoc ( "My Document" )
if hDoc% < 1 then
MsgBox "Unable to init document"
else
'- This will pop up an empty preview window
' . . . empty because I didn't execute
' any text or drawing commands.
'
dpEndDoc hDoc%, DDOC_END_VIEW + DDOC_END_DELETE
end if
End Sub
Working With Text
This section is based on the skeleton described in the first section of this text.
Working with text is the most basic feature of ddoc. ddoc provides an interface that
allows the programmer to use any True Type font installed on the system, print a line
of text, print text at any angle, and wrap text in a given area. Here is a list of the
text manipulation functions in ddoc:
- dpAngleText
- dpClipText
- dpFont
- dpFontSpacing
- dpSetTabs
- dpTabText
- dpText
- dpTextWidth
- dpWrapText
- dpWrapContinue
- dpWrapCount
Changing fonts and printing basic lines of text
After a document is initialized with dpStartDoc, the next thing a programmer must do
is specify which font will be used by calling dpFont. Once specified, the new font will
be selected every time a text function is called until:
- dpFont is called again
- The next page is started by calling dpNewPage
It is important to remember that dpFont needs to always be called after dpStartDoc and
after dpNewPage. If you forget to call dpFont, the default font will be set to Arial 10point.
Another tip … stick with the basic 3 fonts … Arial, Times New Roman, and Courier New (or other
fonts that come standard with the system) unless you are absolutely positive that your users have
other fonts installed. Trying to print a document with a font that isn't installed on the system
can lead to unpredictable results. Note that, as of v1.8, ddoc supports the dpFontSpacing call. This
allows the programmer to add extra space (expressed in points) to be added between each letter
of all text calls (except dpWrapText and dpWrapContinue).
After a font is selected, you can print some text. There are 5 things you need to tell ddoc
in order to have it print:
- Which viewer handle are you using? This makes it thread-safe as a program can be building
more than one document simultaneously.
- How far over on the page should it print (x coordinate)?
- How far down (y coordinate)?
- How do you want the text aligned … left, right, centered, or decimal?
- Finally, what words do you want printed?
Use dpText to print out a standard line of text. There are a few things to know about dpText:
- It doesn't wrap text.
- It doesn't care how long the text is, it will print as much as it can given the width of the page..
- It always prints with the font specified by the last call to dpFont. If this font is invalid, the computer will select a font that's "close". Close, however, might not be close enough, so, as mentioned above, make sure that you are selecting a font that is on the system. To be safe, stick with Arial, Times, and Courier.
e.g.
Dim hPreview as Integer
hPreview = dpStartDoc( 0, "MyPreview", "", DDOC_INCH, DDOC_PAPER_LETTER, _
DDOC_PORTRAIT, DDOC_BIN_UPPER, DDOC_ZOOMFIT )
if hPreview < 1 then
MsgBox "Unable to initilaize the print engine. Status =" + str$(hPreview)
else
dpFont hPreview, DDOC_FONTBOLD + DDOC_FONTITALIC, 14, vbBlack, "Arial"
dpText hPreview, 4.25, 1, DDOC_CENTER, "Centered, Arial 14 pt, Bold-Italic"
dpFont hPreview, DDOC_FONTNORMAL, 9, vbBlack, "Courier New"
dpText hPreview, 1, 2, DDOC_LEFT, "Courier New, 9 point"
dpFont hPreview, DDOC_FONTUNDERLINE, 10, vbBlack, "Times New Roman"
dpText hPreview, 1, 3, DDOC_LEFT, "Section"
dpText hPreview, 2, 3, DDOC_RIGHT, "Value"
dpFont hPreview, DDOC_FONTNORMAL, 10, vbBlack, "Times New Roman"
dpText hPreview, 1, 3.2, DDOC_LEFT, "Sec. A"
dpText hPreview, 2, 3.2, DDOC_RIGHT, "100,000"
dpEndDoc hPreview, DDOC_END_VIEW + DDOC_END_DELETE
end if
Width of text and baseline alignment
ddoc (as of version 1.6) has a new function to determine the width of any text before printing it.
This call, dpTextWidth, takes the text you want to print and returns how wide the text would be
(in current units of measure - either cm or inches) if printed to the default printer. This is
useful if you want to print more text (perhaps in a different font) where the last text left off.
There is an important note, if you want to do this: by default, ddoc prints text with respect to
the TOP of the text being printed. That is, if you tell dpText to print at 1" down and 1" over,
it prints the line so that the top of the first character lines up with this coordinate. If you
want to print multiple strings of text, one after the other on the same line using a different
font, this isn't good - most people expect that text printed in different size fonts, on the same
line will have their baselines aligned, not the tops of the letters! In order for text to be printed
and have it use coordinates with respect to the baseline of the text instead of the top of the text,
you need to add a flag, DDOC_FONTBASELINE, to the dpFont command. All text printed after this line
will have it's text printed with respect to the baseline instead of to the top of the text. For
example, the following code should output aligned to the baseline:
currentX! = 3
dpFont hPrev%, DDOC_FONTBASELINE, 10, vbBlack, "Times New Roman"
dpText hPrev%, currentX!, 1, DDOC_LEFT, "This"
currentX! = currentX! + dpTextWidth(hPrev%, "This ")
dpFont hPrev%, DDOC_FONTBASELINE or DDOC_FONTBOLD, _
14, vbBlack, "Times New Roman"
dpText hPrev%, currentX!, 1, DDOC_LEFT, "Text "
currentX! = currentX! + dpTextWidth(hPrev%, "This ")
dpFont hPrev%, DDOC_FONTBASELINE, 10, vbBlack, "Times New Roman"
dpText hPrev%, currentX!, 1, DDOC_LEFT, "is Baseline "
currentX! = currentX! + dpTextWidth(hPrev%, "is Baseline ")
dpFont hPrev%, DDOC_FONTBASELINE, 18, vbBlack, "Times New Roman"
dpText hPrev%, currentX!, 1, DDOC_LEFT, "Aligned"
The tabular report
A typical use for the print/preview engine is to print a report from a database. Suppose,
for example, we wish to print a list of phone numbers and e-mail addresses for customers.
ddoc will help you do this job with ease. For example, the following routine will loop through a
database and print the names and phone numbers in it.
If I were to use dpText for all columns of a report, there is a chance that if the text in one
column were too long, it would run over into the next column. Remember that dpText doesn't care
how long the text is; it prints all the way over to the edge of the page if there is enough text.
This is where you would use dpClipText. I use it below for the Name field. The name field is
unable to run over into the phone field on the page because is clipped off before it gets that far.
dpClipText can be very handy when you don't want to bother wrapping text and you think it might
not fit in the column width that you have available.
Note: This is for example purposes only, I don't provide the actual data access routines.
Unfortunately, you can't copy this code into vb and run it without providing the GetDBField$(iRecord&, sField$)
function.
Sub PrintMailingList()
Dim hPreview%
Dim cy! ' This variable keeps track of the Y position on the page.
Dim lh! ' This is the line-height or space between lines
Dim c!(1 to 5) ' This array will contain column positions
lh! = .1667 ' 6 lines per inch
'- Define our column positions
c!(1) = .5
c!(2) = 1
c!(3) = 4
c!(4) = 5
c!(5) = 6
'- Start creating the Preview document
hPreview% = dpStartDoc( . . . )
if hPreview% < 1 then
MsgBox "Can't initialize the print engine. Status =" + str$(hPreview)
else
'- Print the page header
gosub print_header
'- Loop through all records in the database.
For iLoop& = 1 to NumberOfRecords&
'- Check for the end of a page (if we are printing
' more than 10 inches down on the page, we need
' a new one).
'
if cy! > 10 then
dpNewPage hPreview%, DDOC_PAPER_LETTER, DDOC_PORTRAIT, _
DDOC_BIN_UPPER
gosub print_header
end if
'- Print the columns of text
dpText c!(1), cy!, DDOC_LEFT, GetDBField$(iLoop&, "ID")
dpClipText c!(2), cy!, c!(3) - c!(2) - .1, DDOC_LEFT, _
GetDBField$(iLoop&, "Name")
dpText c!(3), cy!, DDOC_LEFT, GetDBField$(iLoop&, "Phone")
dpText c!(4), cy!, DDOC_LEFT, GetDBField$(iLoop&, "Fax")
dpText c!(5), cy!, DDOC_LEFT, GetDBField$(iLoop&, "E-Mail")
cy! = cy! + lh!
Next iLoop&
end if
exit sub
print_header:
'- Print a title on the top of the page
cy! = .5
dpFont hPreview, DDOC_FONTBOLD, 12, vbBlack, "Times New Roman"
dpText hPreview, 4.25, cy!, DDOC_CENTER, "Customer Listing"
'- Print the column headings
dpFont hPreview, DDOC_FONTUNDERLINE, 10, vbBlack, "Times New Roman"
cy! = cy! + 2 * lh!
dpText hPreview, c!(1), cy!, DDOC_LEFT, "ID"
dpText hPreview, c!(2), cy!, DDOC_LEFT, "Customer Name"
dpText hPreview, c!(3), cy!, DDOC_LEFT, "Phone"
dpText hPreview, c!(4), cy!, DDOC_LEFT, "Fax"
dpText hPreview, c!(5), cy!, DDOC_LEFT, "E-Mail"
'- Set the font and Y position on the page
' so we're ready to print the columns of text.
'
cy! = cy! + 1.5 * lh!
dpFont hPreview, DDOC_FONTNORMAL, 10, vbBlack, "Times New Roman"
return
End Sub
I use ddoc almost daily to write routines like the one above. This type of report is what
ddoc is designed specifically to do.
Another way to print columns
The new (as of 5-1-1999), dpTabText and dpSetTabs calls allow the programmer to easily print
rows of tab-delimited text. dpSetTabs specifies the places you wish to have tabs. Call it and
pass it the ddoc handle and a string describing the tabs. The string is a set of space delimited
tab stops. Once the tab-stops are established, subsequent calls to dpTabText will print the text
according to the tab stops specified. For example:
'- This tells ddoc to set 3 tab stops …
' The first one is Left aligned at 1" over with a max width of 2 inches
' The next is right aligned at 4" over with a maximum width of 1 inch
' The final tab-stop is left aligned 4.1 inches over and will print
' until it runs out of space on the page (no maximum width)
'
sTabStops = "L1W2 R4W1 L4.1"
dpSetTabs hPrev, sTabStops
'- Printing the text at the tab-stops is a matter of calling
' the dpTabText call telling it how far down on the page to print,
' and passing it the columns separated by a tab character.
'
CurrentY = 3.2 'The Y position on the page
dpTabText hPrev, CurrentY, "Col 1" + chr$(9) + "Col 2" + chr$(9) + "Col 3"
dpTabText, like all other text commands, prints all text columns with the font specified
by the last call to dpFont.
Printing text at angles
ddoc provides a single call to allow you to draw text at an angle: dpAngleText
This function respects the current selected font and prints the text at the angle requested.
It operates very similarly to the dpText function, except that instead of a parameter indicating
the text alignment, it takes the angle of the text (in degrees from 0 though 360). The angle is
given as a 2-byte integer (SmallInt in Delphi, Short Int in C, Integer in PB/DLL and VB).
E.G.
'- The code below will print angled text that prints
' at 270 degrees (straight down) along the right
' side of letter sized paper. Note that is will
' print in a Blue, 10-point Arial font.
'
dpFont hPreview, DDOC_FONTNORMAL, 10, vbBlue, "Arial"
dpAngleText hPreview, 8, .75, 270, "This is some text"
Wrapping text
Wrapping text in a rectangle is tricky if you just use the API. ddoc makes it
a little easier. There are three calls to master if you need to print paragraphs of text.
The first is dpWrapText. This call takes some text and draws it within a given rectangle
until either the rectangle is full of text or all of the text has been printed. dpWrapText
returns the actual number of characters printed. The other that is needed is a call to tell
you how many lines were actually printed. ddoc has the dpWrapCount call to tell you how
many lines were printed in the last call to dpWrapText. As of version 1.8, there is a
dpWrapContinue function. This allows you to avoid having to use dpWrapCount in most
cases. It continues printing the text from the previous dpWrapText or dpWrapContinue
call. It returns non-zero as long is there is still text left to print.
Below I give an example of how to use the paragraph printing features in ddoc:
Sub PrintTextFile (fileName$)
Dim hFile%, hPreview%
Dim iLeft&, iPrinted&
Dim Start!
Dim sInput$
'- The code below opens a file for
' sequential access. If there's an
' error, we bail.
'
On Error Resume Next
hFile% = FreeFile
Open fileName$ For Input As #hFile%
If Err Then
MsgBox "Unable to open the text file: " + fileName$
Exit Sub
End If
On Error GoTo 0
'- Init the ddoc print engine, bail if failed
hPreview% = dpStartDoc(0, "Test file printing", "", DDOC_INCH, _
DDOC_PAPER_LETTER, DDOC_PORTRAIT, _
DDOC_BIN_UPPER, DDOC_ZOOMFIT)
If hPreview% < 1 Then
MsgBox "Can't initialize the print engine. Status =" + Str$(hPreview%)
Exit Sub
End If
'- Loop through the text file
Start! = 1 ' we start printing at 1" down on the page
Do Until EOF(hFile%)
'- Get the first paragraph
Line Input #hFile%, sInput$
iLeft& = Len(sInput$)
Debug.Print sInput$
Do
dpFont hPreview%, DDOC_FONTNORMAL, 10, vbBlack, "Times New Roman"
'- Tell the engine to print the current paragraph within
' the boundaries of the page (1 inch margins on 8.5x11 paper).
' Also, the .1667 parameter tells the engine to print each line
' .1667 inches apart (6 lines per inch). Remember, it returns
' the actual number of characters printed. By subtracting the
' number printed from the number in sInput$, we know when all
' text has been printed. The variable Start! Keeps track of
' where we are on the page.
'
iPrinted& = dpWrapText(hPreview%, 1, Start!, 7.5, 10, .1667, sInput$)
sInput$ = Mid$(sInput$, iPrinted& + 1)
iLeft& = iLeft& - iPrinted&
'- If there are characters left to print, it means that we need
' to go on to the next page. Otherwise, we need to keep track
' of where we left off so we can continue printing the next
' paragraph.
'
If iLeft& > 1 Then
dpNewPage hPreview%, DDOC_PAPER_LETTER, DDOC_PORTRAIT, _
DDOC_BIN_UPPER
Start! = 1
Else
'- The new starting position is the last one
' plus the total height of the lines that
' we just printed.
'
Start! = Start! + dpWrapCount(hPreview%) * .1667
Exit Do
End If
Loop
'- Get the next paragraph if we're not at the end of the file.
Loop
Close #hFile%
dpEndDoc hPreview%, DDOC_END_VIEW + DDOC_END_DELETE
End Sub
Lines, Rectangles, Ellipses, Arcs, Pies, Bitmaps, and Metafiles
Lines
The ddoc API provides a single call to print a line. It takes as parameters the coordinates
of the starting and ending points that define the line. Along with the defining points of
the line, the programmer must provide the width of the line and the color of the line.
'- This prints a 1 point horizontal line in the color red.
' The line is one inch down on the page and runs from
' one inch over to 7.5 inches over on the page.
'
dpLine hPreview%, 1, 1, 7.5, 1, 1, vbRed
Rectangles and Ellipses
Rectangles and ellipses work in a similar manner to lines.
Define the upper-left and lower-right corners
of the rectangle, tell it what color to use for fill, and what type of line (if any) to
surround the box with, and ddoc responds by printing the rectangle or ellipse.
There is one trick involved … to get it to print without a surrounding border, pass -1 for the
line width parameter.
'- The following prints a rectangle with a blue
' border filled with the color red. The border is
' three points wide. The rectangle fills a rectangle
' defined by upper-left = (1,1) and lower-right = (3,2)
'
dpRect hPreview%, 1, 1, 3, 2, 3, vbRed, vbBlue
'- The following prints an ellipse a shaded background with no border.
dpEllipse hPreview%, 1, 1, 3, 2, -1, &hE0E0E0, 0
Arcs and Pie slices
To draw an arc or a pie slice, you use the dpArc and dpPie functions.
Define the upper-left and lower-right corners
of the rectangle that bounds the ellipse/circle from which the arc or pie slice will
be taken. Tell ddoc the starting angle and how many degrees to draw (counter clock-wise).
Define how wide the bounding line is, what color it is, and in the case of
a pie, the fill color. To get a pie slice to print without a surrounding border, pass -1 for the
line width parameter.
'- The following prints a pie with a blue
' border filled with the color red. The border is
' three points wide. The bounding rectangle is (1,1)(3, 2)
' It starts at 45 degrees and ends at 180 degrees
'
dpPie hPreview%, 1, 1, 3, 2, 45, 135, 3, vbRed, vbBlue
'- This prints an arc shaped like a U.
' the line will be as thin as possible (w = 0)
' and will be black.
'
dpArc hPreview%, 1, 1, 3, 2, 180, 180, 0, vbBlack
Bitmaps and MetaFiles
ddoc has an interface to print bitmap graphics and enhanced metafiles directly from a .bmp/.emf file.
Additionally (as of ddoc v1.9d released 9/2003) .jpg files are also supported in the dpAddGraphic
command only. If you need to print .gif or .png graphics, or need jpg support in a command
other than dpAddGraphic I can
only suggest that you use an external library to translate these graphics into .bmps for
printing with ddoc if you need to use a format other than .bmp.
There are three ways to print a .bmp or .emf file. In all cases you need to provide the engine with
an area to fit the graphic in and the name of the file. Note to ddrv users: unlike ddoc's
little brother, ddrv, dpGraphic , dpDrawGraphic and dpEmbedGraphic by default will shrink the graphic to
fit within the box, not stretch the graphic to the size of the bounding box. You can enable
stretching (non-proportional) with a call to dpStretchMode. This call must be made
on every page as it is reset to proportional shrinking at the beginning of a page. Additionally,
the dpAddGraphic/dpDrawGraphic command supports jpg/jpeg files.
'- The following will print logo.bmp in the upper-left
' corner of the viewer window in a box defined by the
' points (1,1) and (3,2).
'
dpGraphic hPreview, 1, 1, 3, 2, "c:\mygraph.bmp"
'- or using embedding the graphic
dpEmbedGraphic hPreview, 1, 1, 3, 2, "c:\mygraph.bmp"
'- or using a referenced bitmap.
hGraphic = dpAddGraphic(hPreview, "c:\mygraph.bmp")
if hGraphic then
dpDrawGraphic(hPreview, hGraphic, 1,1,3,2)
end if
Note the three different functions to print graphics. dpGraphic tells the print engine the
location of the bmp/emf/jpg file so it can be loaded directly from the file when needed. This is
the most efficient way to print graphics. dpEmbedGraphic copies the bmp into the temp file.
The viewer then extracts the bmp to a temporary file when needed. dpAddGraphic stores the bitmap
in the ddoc file, just like dpEmbedGraphic, but it doesn't do any drawing - once stored,
dpAddGraphic returns a handle to that graphic. To print the stored graphic to the screen call
dpDrawGraphic and pass it the handle. dpAddGraphic/dpDrawGraphic only store one copy of the
graphic in the file, even if it's used multiple times. This is a huge space saver over
dpEmbedGraphic. It is my intent to phase out dpEmbedGraphic and recommend my users use
dpAddGraphic/dpDrawGraphic instead. Here are pros and cons of each graphic method:
dpGraphic
Pros: Faster, takes less storage space
Cons: Can't use this if you are going to transfer the temp file to another user via built-in email or other means of storage and recall, unless the recipient has the same path to the graphic file.
dpEmbedGraphic
Pros: Any user (email recipient or otherwise) can call up the document without needing access to the original graphic file.
Cons: Slower, takes up additional storage space as each time it's called, a graphic is added to the document.
Tip: If you're going to use the dpEmbedGraphic function, try to use it with bitmaps that are RLE compressed.
The support for this type of compression is built-in and can save a lot of disk space. To convert a
regular bitmap to RLE, use a good paint program (I use Paint Shop Pro from JASC) to call up the image
and save it out RLE. If you need to use the same graphic more than once, use instead as that method
only puts one copy of the bmp in the file - dpEmbedGraphic puts a new copy of the graphic in the
file each time.
dpAddGraphic/dpDrawGraphic
Pros: Any user (email recipient or otherwise) can call up the document without needing access to the original graphic file. Use this function if you're allowing email and you need to display the same graphic more than once.
This is the only function that supports .jpg/.jpeg graphics.
Cons: Slower than dpGraphic and takes up more space that dpGraphic. It takes up less space than dpEmbedGraphic, however, because a bitmap referenced only once by dpAddGraphic can be drawn as many times as you wish with dpDrawGraphic.
Note: All of the above graphics commands will shrink a bitmap to fit within the defined rectangle.
If you desire the bitmap to be stretched to fit the rectangle instead, call the dpStretchMode command and
put a non-zero number in the second parameter. All subsequent graphics calls will then stretch the bitmap.