This example prints RTF text to the default printer in a WYSIWYG format. AFAICT without spending more time on it than I have, it works correctly. I tested it with several Word documents that contained multiple different fonts, bullets, symbols, paragraphs, etc, copied from Word and pasted into the rich edit control.
Edit: The code below is a revision of the first code I posted. Even though the output looks correct, and the width as printed matches the width as determined by the ruler in Word, there are some unanswered questions about the print preview and the PrintRTF return value.
Edit2: I have now verified that the on-screen size in the print preview is a close match for the on-screen size in Word (once I realized that I had to set the zoom in Word to 100% instead of the 140% that normally use :)).
PrintRTF.bas:
Code: Select all
''=============================================================================
'' Build as a console app to see the print output.
''=============================================================================
#include "windows.bi"
#include "win\richedit.bi"
#include "win\winspool.bi"
''=============================================================================
#define IDD_DLG 100
#define IDM_ACTION 200
#define IDM_PRINT 201
#define IDC_RE 300
''=============================================================================
''-----------------------------------------------------------------------------
'' This is a translation of the Microsoft example code from here:
''
'' http://msdn.microsoft.com/en-us/library/windows/desktop/bb787875(v=vs.85).aspx
''
'' With error corrections and additional comments.
''
'' hwnd is the HWND of the rich edit control
'' hdc is the HDC of the printer
''-----------------------------------------------------------------------------
function PrintRTF(hwnd as HWND, hdc as HDC) as BOOL
dim as DOCINFO di
dim as FORMATRANGE fr
dim as BOOL fSuccess
dim as integer cxPhysOffset, cyPhysOffset, cxPhys, cyPhys, cpMin
di.cbSize = sizeof(DOCINFO)
if StartDoc(hdc, @di) = 0 then return FALSE
cxPhysOffset = GetDeviceCaps(hdc, PHYSICALOFFSETX)
cyPhysOffset = GetDeviceCaps(hdc, PHYSICALOFFSETY)
cxPhys = GetDeviceCaps(hdc, PHYSICALWIDTH)
cyPhys = GetDeviceCaps(hdc, PHYSICALHEIGHT)
''--------------------------------------------------------------
'' For printing devices GetDeviceCaps returns the width, height
'' and offsets in device units (based on the printer dpi).
'' The rich edit control expects the dimensions to be in twips,
'' so convert them here.
''--------------------------------------------------------------
cxPhysOffset = MulDiv(cxPhysOffset, 1440, GetDeviceCaps(hdc, LOGPIXELSX))
cyPhysOffset = MulDiv(cyPhysOffset, 1440, GetDeviceCaps(hdc, LOGPIXELSY))
cxPhys = MulDiv(cxPhys, 1440, GetDeviceCaps(hdc, LOGPIXELSX))
cyPhys = MulDiv(cyPhys, 1440, GetDeviceCaps(hdc, LOGPIXELSY))
''---------------------------------------------------------------------
'' Create "print preview", specifying the width of the printable area.
''---------------------------------------------------------------------
SendMessage(hwnd, EM_SETTARGETDEVICE, cast(WPARAM,hdc), cxPhys - cxPhysOffset)
fr.hdc = hdc
fr.hdcTarget = hdc
''-----------------------------------------------
'' Set page rect to physical page size in twips.
''-----------------------------------------------
fr.rcPage.top = 0
fr.rcPage.left = 0
fr.rcPage.right = cxPhys
fr.rcPage.bottom = cyPhys
''------------------------------------------------------------------------
'' Set the rendering rectangle to the pintable area of the page in twips.
''------------------------------------------------------------------------
fr.rc.left = cxPhysOffset
fr.rc.right = cxPhysOffset + cxPhys
fr.rc.top = cyPhysOffset
fr.rc.bottom = cyPhysOffset + cyPhys
''------------------------------
'' Select the entire contents.
''------------------------------
SendMessage(hwnd, EM_SETSEL, 0, cast(LPARAM,-1))
''-------------------------------------
'' Get the selection into a CHARRANGE.
''-------------------------------------
SendMessage(hwnd, EM_EXGETSEL, 0, cast(LPARAM,@fr.chrg))
fSuccess = TRUE
''------------------------------------
'' Use GDI to print successive pages.
''------------------------------------
while (fr.chrg.cpMin < fr.chrg.cpMax AND fSuccess)
fSuccess = StartPage(hdc) > 0
if fSuccess = 0 then exit while
cpMin = SendMessage(hwnd, EM_FORMATRANGE, TRUE, cast(LPARAM,@fr))
if (cpMin <= fr.chrg.cpMin) then
fSuccess = FALSE
exit while
end if
fr.chrg.cpMin = cpMin
fSuccess = EndPage(hdc) > 0
wend
''----------------------------------------------------------
'' Cached information must be freed by specifying a NULL in
'' lParam before using the message for a different device.
''----------------------------------------------------------
SendMessage(hwnd, EM_FORMATRANGE, FALSE, NULL)
if (fSuccess) then
EndDoc(hdc)
else
AbortDoc(hdc)
end if
return fSuccess
end function
''=============================================================================
function DialogProc( byval hwndDlg as HWND, _
byval uMsg as UINT, _
byval wParam as WPARAM, _
byval lParam as LPARAM ) as integer
static as HMENU hMenu
static as HWND hwndRE
static as HDC hdcPrinter
static as zstring * 100 printerName
static as integer i = 100
select case uMsg
case WM_INITDIALOG
hMenu = LoadMenu(GetModuleHandle(NULL),MAKEINTRESOURCE(IDM_ACTION))
SetMenu(hwndDlg, hMenu)
hwndRE = GetDlgItem( hwndDlg, IDC_RE )
''-----------------------------------------------------
'' I'm assuming here that there is a default printer.
''
'' This could easily be replaced with code that opens
'' a print dialog box and lets the user select the
'' printer. See the documentation for the PrintDlg and
'' PrintDlgEx functions.
''-----------------------------------------------------
GetDefaultPrinter( @printerName, @i )
print "printerName ";printerName
hdcPrinter = CreateDC( "WINSPOOL", @printerName, NULL, NULL )
print "hdcPrinter ";hex(hdcPrinter);"h"
case WM_SIZE
''------------------------------------------------
'' Size the rich edit control to the client area.
''------------------------------------------------
MoveWindow(hwndRE, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE)
case WM_COMMAND
select case wParam
''--------------------------------------------------------
'' There is no need here to isolate the low-order word of
'' wParam to test for a BN_CLICKED or menu notification
'' because the high-order word will be zero for both of
'' these notifications.
''--------------------------------------------------------
case IDM_PRINT
print "PrintRTF return ";
print PrintRTF(hwndRE, hdcPrinter)
case IDCANCEL
''------------------------------------------
'' This allows the user to close the dialog
'' window by pressing the Escape key.
''------------------------------------------
DeleteDC(hdcPrinter)
EndDialog(hwndDlg, 0)
end select
case WM_CLOSE
DeleteDC(hdcPrinter)
EndDialog(hwndDlg, 0)
end select
return 0
end function
''=============================================================================
''------------------------------------------------------------------
'' This is necessary to allow the rich edit control to register its
'' class name, and it must be done prior to creating the dialog.
''------------------------------------------------------------------
LoadLibrary("riched20.dll")
''---------------------------------------------------------------------
'' Create a modal dialog from the dialog box resource, specifying zero
'' in the hWndParent parameter. The DialogBoxParam function does not
'' return until the dialog box is destroyed.
''---------------------------------------------------------------------
DialogBoxParam( GetModuleHandle(null), _
cast(zstring ptr,IDD_DLG), _
0, _
@DialogProc, _
0 )
PrintRTF.rc
Code: Select all
#include "resource.inc"
IDM_ACTION MENUEX
BEGIN
POPUP "Action"
BEGIN
MENUITEM "Print", IDM_PRINT
END
END
IDD_DLG DIALOGEX 0,0,400,200
CAPTION "PrintRTF"
STYLE WS_SYSMENU | DS_CENTER
BEGIN
CONTROL "",IDC_RE,RICHEDIT_CLASS,RICHEDIT_STYLE,0,0,0,0
END
resource.inc
Code: Select all
#define IDD_DLG 100
#define IDM_ACTION 200
#define IDM_PRINT 201
#define IDC_RE 300
#define RICHEDIT_STYLE WS_VSCROLL|WS_HSCROLL|ES_MULTILINE|ES_AUTOVSCROLL|ES_AUTOHSCROLL|ES_WANTRETURN
//
// The defines below are from winuser.bi and richedit.bi,
// included here to support resource definitions that are
// coded manually.
//
#define WS_BORDER 0x800000
#define WS_CAPTION 0xc00000
#define WS_CHILD 0x40000000
#define WS_CHILDWINDOW 0x40000000
#define WS_CLIPCHILDREN 0x2000000
#define WS_CLIPSIBLINGS 0x4000000
#define WS_DISABLED 0x8000000
#define WS_DLGFRAME 0x400000
#define WS_GROUP 0x20000
#define WS_HSCROLL 0x100000
#define WS_ICONIC 0x20000000
#define WS_MAXIMIZE 0x1000000
#define WS_MAXIMIZEBOX 0x10000
#define WS_MINIMIZE 0x20000000
#define WS_MINIMIZEBOX 0x20000
#define WS_OVERLAPPED 0
#define WS_OVERLAPPEDWINDOW 0xcf0000
#define WS_POPUP 0x80000000
#define WS_POPUPWINDOW 0x80880000
#define WS_SIZEBOX 0x40000
#define WS_SYSMENU 0x80000
#define WS_TABSTOP 0x10000
#define WS_THICKFRAME 0x40000
#define WS_TILED 0
#define WS_TILEDWINDOW 0xcf0000
#define WS_VISIBLE 0x10000000
#define WS_VSCROLL 0x200000
#define MDIS_ALLCHILDSTYLES 1
#define BS_3STATE 5
#define BS_AUTO3STATE 6
#define BS_AUTOCHECKBOX 3
#define BS_AUTORADIOBUTTON 9
#define BS_BITMAP 128
#define BS_BOTTOM 0x800
#define BS_CENTER 0x300
#define BS_CHECKBOX 2
#define BS_DEFPUSHBUTTON 1
#define BS_GROUPBOX 7
#define BS_ICON 64
#define BS_LEFT 256
#define BS_LEFTTEXT 32
#define BS_MULTILINE 0x2000
#define BS_NOTIFY 0x4000
#define BS_OWNERDRAW 0xb
#define BS_PUSHBUTTON 0
#define BS_PUSHLIKE 4096
#define BS_RADIOBUTTON 4
#define BS_RIGHT 512
#define BS_RIGHTBUTTON 32
#define BS_TEXT 0
#define BS_TOP 0x400
#define BS_USERBUTTON 8
#define BS_VCENTER 0xc00
#define BS_FLAT 0x8000
#define CBS_AUTOHSCROLL 64
#define CBS_DISABLENOSCROLL 0x800
#define CBS_DROPDOWN 2
#define CBS_DROPDOWNLIST 3
#define CBS_HASSTRINGS 512
#define CBS_LOWERCASE 0x4000
#define CBS_NOINTEGRALHEIGHT 0x400
#define CBS_OEMCONVERT 128
#define CBS_OWNERDRAWFIXED 16
#define CBS_OWNERDRAWVARIABLE 32
#define CBS_SIMPLE 1
#define CBS_SORT 256
#define CBS_UPPERCASE 0x2000
#define ES_AUTOHSCROLL 128
#define ES_AUTOVSCROLL 64
#define ES_CENTER 1
#define ES_LEFT 0
#define ES_LOWERCASE 16
#define ES_MULTILINE 4
#define ES_NOHIDESEL 256
#define ES_NUMBER 0x2000
#define ES_OEMCONVERT 0x400
#define ES_PASSWORD 32
#define ES_READONLY 0x800
#define ES_RIGHT 2
#define ES_UPPERCASE 8
#define ES_WANTRETURN 4096
#define LBS_DISABLENOSCROLL 4096
#define LBS_EXTENDEDSEL 0x800
#define LBS_HASSTRINGS 64
#define LBS_MULTICOLUMN 512
#define LBS_MULTIPLESEL 8
#define LBS_NODATA 0x2000
#define LBS_NOINTEGRALHEIGHT 256
#define LBS_NOREDRAW 4
#define LBS_NOSEL 0x4000
#define LBS_NOTIFY 1
#define LBS_OWNERDRAWFIXED 16
#define LBS_OWNERDRAWVARIABLE 32
#define LBS_SORT 2
#define LBS_STANDARD 0xa00003
#define LBS_USETABSTOPS 128
#define LBS_WANTKEYBOARDINPUT 0x400
#define SBS_BOTTOMALIGN 4
#define SBS_HORZ 0
#define SBS_LEFTALIGN 2
#define SBS_RIGHTALIGN 4
#define SBS_SIZEBOX 8
#define SBS_SIZEBOXBOTTOMRIGHTALIGN 4
#define SBS_SIZEBOXTOPLEFTALIGN 2
#define SBS_SIZEGRIP 16
#define SBS_TOPALIGN 2
#define SBS_VERT 1
#define SS_BITMAP 14
#define SS_BLACKFRAME 7
#define SS_BLACKRECT 4
#define SS_CENTER 1
#define SS_CENTERIMAGE 512
#define SS_ENHMETAFILE 15
#define SS_ETCHEDFRAME 18
#define SS_ETCHEDHORZ 16
#define SS_ETCHEDVERT 17
#define SS_GRAYFRAME 8
#define SS_GRAYRECT 5
#define SS_ICON 3
#define SS_LEFT 0
#define SS_LEFTNOWORDWRAP 0xc
#define SS_NOPREFIX 128
#define SS_NOTIFY 256
#define SS_OWNERDRAW 0xd
#define SS_REALSIZEIMAGE 0x800
#define SS_RIGHT 2
#define SS_RIGHTJUST 0x400
#define SS_SIMPLE 11
#define SS_SUNKEN 4096
#define SS_WHITEFRAME 9
#define SS_WHITERECT 6
#define SS_USERITEM 10
#define SS_TYPEMASK 0x0000001FL
#define SS_ENDELLIPSIS 0x00004000L
#define SS_PATHELLIPSIS 0x00008000L
#define SS_WORDELLIPSIS 0x0000C000L
#define SS_ELLIPSISMASK 0x0000C000L
#define DS_3DLOOK 4
#define DS_ABSALIGN 1
#define DS_CENTER 0x800
#define DS_CENTERMOUSE 4096
#define DS_CONTEXTHELP 0x2000
#define DS_CONTROL 0x400
#define DS_FIXEDSYS 8
#define DS_LOCALEDIT 32
#define DS_MODALFRAME 128
#define DS_NOFAILCREATE 16
#define DS_NOIDLEMSG 256
#define DS_SETFONT 64
#define DS_SETFOREGROUND 512
#define DS_SYSMODAL 2
#define DS_SHELLFONT (64 or 8)
#define WS_EX_ACCEPTFILES 16
#define WS_EX_APPWINDOW 0x40000
#define WS_EX_CLIENTEDGE 512
#define WS_EX_COMPOSITED 0x2000000
#define WS_EX_CONTEXTHELP 0x400
#define WS_EX_CONTROLPARENT 0x10000
#define WS_EX_DLGMODALFRAME 1
#define WS_EX_LAYERED 0x80000
#define WS_EX_LAYOUTRTL 0x400000
#define WS_EX_LEFT 0
#define WS_EX_LEFTSCROLLBAR 0x4000
#define WS_EX_LTRREADING 0
#define WS_EX_MDICHILD 64
#define WS_EX_NOACTIVATE 0x8000000
#define WS_EX_NOINHERITLAYOUT 0x100000
#define WS_EX_NOPARENTNOTIFY 4
#define WS_EX_OVERLAPPEDWINDOW 0x300
#define WS_EX_PALETTEWINDOW 0x188
#define WS_EX_RIGHT 0x1000
#define WS_EX_RIGHTSCROLLBAR 0
#define WS_EX_RTLREADING 0x2000
#define WS_EX_STATICEDGE 0x20000
#define WS_EX_TOOLWINDOW 128
#define WS_EX_TOPMOST 8
#define WS_EX_TRANSPARENT 32
#define WS_EX_WINDOWEDGE 256
// This specifies rich edit version 2/3
// Requires LoadLibrary("riched20.dll")
#ifdef UNICODE
#define RICHEDIT_CLASS "RichEdit20W"
#else
#define RICHEDIT_CLASS "RichEdit20A"
#endif
And in case it’s not clear how this could be used, you could code your pages in RTF, stream them in to an invisible rich edit control, use the control to format the pages for the printer, then print them.