Multi-page Scintilla printing with Header & Footer

Share your advanced PureBasic knowledge/code with the community.
User avatar
Tenaja
Addict
Addict
Posts: 1959
Joined: Tue Nov 09, 2010 10:15 pm

Multi-page Scintilla printing with Header & Footer

Post by Tenaja »

This routine will print one or more copies of a scintilla file with optional header and footer data. It was a pain to figure out the footer because once you make the first #SCI_FORMATRANGE call to draw the first page, you can no longer use DrawText(). You have to StopDrawing() with each page, then restart.

This "should be" cross platform, since I am not using any API calls.

Features not yet implemented:
* Margins are in Pixels, not inches or millimeters. Feel free to update this.
* No Landscape or Portrait orientation. (Currently, it is chosen by the printer setting in the PrintRequester)
* Number of copies are set beforehand; I do not know how to read them from the PrintRequester() without API calls.
* Print Range is not implemented, although that could easily be added using #SCI_POSITIONFROMLINE.
*

I have a macro file that I use for my Sci gadgets, so I do not need the ScintillaSendMessage commands. This allows copying commands straight from the Sci help page. I tried to convert them all, but it does compile with EnableExplicit... if I missed one, let me know and I will update. This is not totally refactored (there may be a few unnecessary temporary variables); feel free to clean it up or add features and share.

Have fun!

Code: Select all

;  Fill the global structure with the appropriate flags and data, and call filePrint().
Structure PrintSettingStruct
	HeaderDate.i	; flags to include info in the header & footers.
	HeaderFile.i
	HeaderPath.i
	HeaderPage.i
	FooterDate.i
	FooterFile.i
	FooterPath.i
	FooterPage.i
	
	Color.i
	WordWrap.i
	Landscape.i		; not implemented.
	Copies.i
	MarginT.d		; These were originally intended to be double precision floats.
	MarginL.d		; but there is no native PB command to get printer resolution.
	MarginR.d
	MarginB.d
EndStructure

Global PrintPref.PrintSettingStruct


Procedure filePrint(ActiveSci.i, openFName.s)
	Protected.i drawresult, prn, copynum = 1
	Protected FormatRange.FORMATRANGE
	Protected.i headerht, headerwd, footerht, footerwd
	Protected.i length, oversize, cut
	Protected.i PageCount
	Protected.s strDate, strFile, strPage	; temp string holders.
	Protected.s header, footer, Output$
	Protected.i PageWidth
	
		If ScintillaSendMessage(ActiveSci, #SCI_GETLENGTH)
			prn = PrintRequester()
			If prn
				With PrintPref
					; Start header:
					header = "" : strFile = ""
					headerht = 0 : headerwd = 0
					If \HeaderDate Or \HeaderFile Or \HeaderPath
						StartDrawing(PrinterOutput())
						headerht = 2 * TextHeight(openFName)	; use 2x so we get a space between.
						If \HeaderDate	: strDate = FormatDate("%yyyy/%mm/%dd", Date()) + ";  "	: EndIf
						If \HeaderFile	: strFile = GetFilePart(openFName)	: EndIf
						If \HeaderPath	: strFile = openFName	: EndIf
						If \HeaderPage	: strPage = " ; Page "	: EndIf
						
						If strFile <> ""
							header = strFile + strDate 
							If strPage <> ""
								header = header + strPage + "00000"
							EndIf
							headerwd = TextWidth(header)
							PageWidth = PrinterPageWidth() - \MarginL -\MarginR
							If headerwd > PageWidth
								oversize = headerwd - PageWidth
								cut = oversize / TextWidth("O")
								length = Len(strFile) - cut - 1
								strFile = Left(strFile, length)
							EndIf
							header = strDate + strFile + strPage
						Else
							header = strDate + strPage
						EndIf
						StopDrawing()
					EndIf	; done with Header
					; Start Footer:
					footer = "" : strFile = ""
					footerht = 0 : footerwd = 0
					If \FooterDate Or \FooterFile Or \FooterPath
						StartDrawing(PrinterOutput())
						footerht = 2 * TextHeight(openFName)
						If \FooterDate	: strDate = FormatDate("%yyyy/%mm/%dd", Date()) + ";  "	: EndIf
						If \FooterFile	: strFile = GetFilePart(openFName)	: EndIf
						If \FooterPath	: strFile = openFName : EndIf
						If \FooterPage	: strPage = " ; Page "	: EndIf
						
						If strFile <> ""
							footer = strFile + strDate + strPage + "00000"
							footerwd = TextWidth(footer)
							PageWidth = PrinterPageWidth() - \MarginL -\MarginR
							If footerwd > PageWidth
								oversize = footerwd - PageWidth
								cut = oversize / TextWidth("O")
								length = (Len(strFile) - cut) - 1		; one extra character, just in case
								strFile = Left(strFile, length)
							EndIf
							footer = strDate + strFile + strPage
						Else
							footer = strDate + strPage
						EndIf
						StopDrawing()
					EndIf	; done with Footer
					
					; Set up the printer:
					ScintillaSendMessage(ActiveSci, #SCI_SETPRINTWRAPMODE, \WordWrap)
					ScintillaSendMessage(ActiveSci, #SCI_SETPRINTCOLOURMODE, \Color)
					
					; rc is where to print
					FormatRange\rc\left=\MarginL
					FormatRange\rc\top =\MarginT + headerht
					FormatRange\rc\right=PrinterPageWidth() - \MarginL -\MarginR				; width of the drawing area, in pixels
					FormatRange\rc\bottom=PrinterPageHeight() - \MarginB - headerht - footerht	;  
					;rcPage is physically extents-- possible to print
					FormatRange\rcPage\left=0
					FormatRange\rcPage\top= 0
					FormatRange\rcPage\right=PrinterPageWidth()
					FormatRange\rcPage\bottom=PrinterPageHeight()
					
					FormatRange\chrg\cpMin=0
					FormatRange\chrg\cpMax=ScintillaSendMessage(ActiveSci, #SCI_GETLENGTH)
					If StartPrinting(GetFilePart(openFName))
						While copynum <= \Copies
							PageCount = 1
							FormatRange\chrg\cpMin=0	; reset to character 0 to start over.
							While FormatRange\chrg\cpMin < FormatRange\chrg\cpMax
								drawresult = StartDrawing(PrinterOutput())
								If drawresult = 0	:	Break 2	:	EndIf
								FormatRange\hDC = drawresult
								FormatRange\hdcTarget = drawresult
								If header <> ""	; Print header:
									Output$ = header
									If strPage : Output$ + Str(PageCount) : EndIf
									DrawText(10 + \MarginL, 10 + \MarginT , Output$, #Black, #White)
								EndIf
								If footer <> ""	; print footer:
									Output$ = footer
									If strPage : Output$ + Str(PageCount) : EndIf
									DrawText(10 + \MarginL, (PrinterPageHeight() - footerht) - \MarginB, Output$, #Black, #White)
								EndIf
								FormatRange\chrg\cpMin = ScintillaSendMessage(ActiveSci, #SCI_FORMATRANGE, #True, FormatRange)	; Print this scintilla page:
								PageCount + 1
								StopDrawing()			; stop drawing between #SCI_FORMATRANGE calls, or the header will not draw.
								ScintillaSendMessage(ActiveSci, #SCI_FORMATRANGE, #False, 0)
								If FormatRange\chrg\cpMin < FormatRange\chrg\cpMax
									NewPrinterPage()
								EndIf
							Wend
							copynum + 1
							If copynum <= \Copies 
								NewPrinterPage()
							EndIf
						Wend
						StopPrinting()
					EndIf
				EndWith
			EndIf
		EndIf
EndProcedure
User avatar
Frarth
Enthusiast
Enthusiast
Posts: 241
Joined: Tue Jul 21, 2009 11:11 am
Location: On the planet
Contact:

Re: Multi-page Scintilla printing with Header & Footer

Post by Frarth »

Great work! Thnks for sharing.
PureBasic 5.41 LTS | Xubuntu 16.04 (x32) | Windows 7 (x64)
User avatar
Frarth
Enthusiast
Enthusiast
Posts: 241
Joined: Tue Jul 21, 2009 11:11 am
Location: On the planet
Contact:

Re: Multi-page Scintilla printing with Header & Footer

Post by Frarth »

On Linux (Debian Squeeze) I get an error message "Structure not found: FORMATRANGE" Is it a Windows only structure?
PureBasic 5.41 LTS | Xubuntu 16.04 (x32) | Windows 7 (x64)
User avatar
Tenaja
Addict
Addict
Posts: 1959
Joined: Tue Nov 09, 2010 10:15 pm

Re: Multi-page Scintilla printing with Header & Footer

Post by Tenaja »

Frarth wrote:On Linux (Debian Squeeze) I get an error message "Structure not found: FORMATRANGE" Is it a Windows only structure?
No, it is a Scintilla structure; it should be defined in the Scintilla library, because I do not define it in my code anywhere. It works for me whether I use the DLL or ScintillaStatic.

Did you InitScintilla([LibraryName$])? This is just a function, not a full program.
User avatar
ts-soft
Always Here
Always Here
Posts: 5756
Joined: Thu Jun 24, 2004 2:44 pm
Location: Berlin - Germany

Re: Multi-page Scintilla printing with Header & Footer

Post by ts-soft »

It is a Windows-API structure but Scintilla use the same.
Add the following on the top of your code:

Code: Select all

CompilerIf Defined(CHARRANGE, #PB_Structure) = 0
Structure CHARRANGE
  cpMin.l
  cpMax.l
EndStructure
CompilerEndIf
CompilerIf Defined(RECT, #PB_Structure) = 0
Structure RECT
  left.l
  top.l
  right.l
  bottom.l
EndStructure
CompilerEndIf
CompilerIf Defined(FORMATRANGE, #PB_Structure) = 0
Structure FORMATRANGE
  hdc.i
  hdcTarget.i
  rc.RECT
  rcPage.RECT
  chrg.CHARRANGE
EndStructure
CompilerEndIf
Greetings - Thomas
PureBasic 5.73 | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Old bugs good, new bugs bad! Updates are evil: might fix old bugs and introduce no new ones.
Image
User avatar
Tenaja
Addict
Addict
Posts: 1959
Joined: Tue Nov 09, 2010 10:15 pm

Re: Multi-page Scintilla printing with Header & Footer

Post by Tenaja »

Thanks for that, Thomas. I am not trying to test anything on Linux today... that comes after it is all done on Windows.
User avatar
Frarth
Enthusiast
Enthusiast
Posts: 241
Joined: Tue Jul 21, 2009 11:11 am
Location: On the planet
Contact:

Re: Multi-page Scintilla printing with Header & Footer

Post by Frarth »

Thanks Trond, it now works on Linux, along with two added constant definitions:

Code: Select all

#Black = 0
#White = 16777215
PureBasic 5.41 LTS | Xubuntu 16.04 (x32) | Windows 7 (x64)
Post Reply