Help centering diagonal Watermark - is not consistent !!

I've created some basic code that creates information along the edges of a page.

It also creates a diagonal 'watermark' across the face of the page.

I'm having trouble keeping the watermark centered when I change the watermark text.
For example, if I use "watermark", the text is positioned differently than if I use "NOT FOR DISTRIBUTION" - and neither are centered as they should be.

My code calculates the font size based on the text length.

Can someone help me figure out why my centering code does not work properly???

I need to be able to use any font, any text and still have the watermark be
centered on the digonal.

Please test your code - I will!

I think that the Printer.TextHeight and Printer.TextWidth methods return inaccurate info...
I've also tried GetTextExtentPoint32 with the same results.

Click this link do download my sample code...

Or contact me by email:
<email address removed - Bingie EE PE>
Who is Participating?
Here is as far as I got.  It works better, but not perfect.  The textwidth seems to be shorter than I calculated it to be, not sure why.  Anyway give it a try.  I only modified the setBanners function, so here it is...

Private Sub setBanners(Optional page As Integer)

Dim pageWidth As Double, pageHeight As Double, pi As Double
Dim rotobj As New rotator, txHite As Integer
Dim banner As String
Dim twip As Integer, halftwip As Integer

twip = 1440
halftwip = 720
pi = 3.14159

'get the current page size
pageWidth = ScaleX(GetDeviceCaps(Printer.hdc, HORZSIZE), vbMillimeters, vbInches)
pageHeight = ScaleY(GetDeviceCaps(Printer.hdc, VERTSIZE), vbMillimeters, vbInches)

Debug.Print "Width, Height", pageWidth, pageHeight

txHite = Printer.TextHeight("Tj") 'sample text for calculating height only
Set rotobj.Device = Printer
rotobj.Font = tfont

'do the top/bottom banners
rotobj.Angle = 0 'same for all top & bottom

Printer.CurrentY = 0  'same for all Top

'top left
Printer.CurrentX = 0
banner = iso.tleft
GoSub handleHorz

'top center
Printer.CurrentX = (pageWidth * halftwip) - (Printer.TextWidth(iso.tcenter) * halftwip)
banner = iso.tcenter
GoSub handleHorz

'top right
Printer.CurrentX = (pageWidth * twip) - Printer.TextWidth(iso.tright)
banner = iso.tright
GoSub handleHorz

' Bottom labels
Printer.CurrentY = ((pageHeight - margs.bottom) * twip)

'bottom left
Printer.CurrentX = 0
banner = iso.bleft
GoSub handleHorz

'bottom center
Printer.CurrentX = (pageWidth * halftwip) - (Printer.TextWidth(iso.bcenter) * halftwip)
banner = iso.bcenter
GoSub handleHorz

'bottom right
Printer.CurrentX = (pageWidth * twip) - Printer.TextWidth(iso.bright)
banner = iso.bright
GoSub handleHorz

'Do the side banners
rotobj.Angle = 90 'same for all (Could make this a param in VueLogic)

Printer.CurrentX = 0

Printer.CurrentY = ((pageHeight - margs.bottom) * twip) - txHite
banner = iso.lbottom
GoSub handleVert

Printer.CurrentY = (pageHeight * halftwip) + (Printer.TextWidth(iso.lcenter) * halftwip)
banner = iso.lcenter
GoSub handleVert

Printer.CurrentY = ( * twip) + Printer.TextWidth(iso.ltop)
banner = iso.ltop
GoSub handleVert

Printer.CurrentX = (pageWidth - margs.right) * twip

Printer.CurrentY = ((pageHeight - margs.bottom) * twip) - txHite
banner = iso.rbottom
GoSub handleVert

Printer.CurrentY = (pageHeight * halftwip) + (Printer.TextWidth(iso.rcenter) * half)
banner = iso.rcenter
GoSub handleVert

Printer.CurrentY = ( * twip) + Printer.TextWidth(iso.rtop)
banner = iso.rtop
GoSub handleVert

Dim aRad As Double

'//complimentary angle
Dim aCompRad As Double

Dim twidth As Long '// changed to long!
Dim xHyp As Double, nHyp As Double

'// need a temp holder to calc hypotenuse, etc.
Dim lCurrY As Long
Dim lCurrX As Long

'rotate watermark diagonally
'work in twips

'// need to work on this...
'find the diagonal hypotenuse
xHyp = Sqr(((pageWidth * twip) ^ 2) + ((pageHeight * twip) ^ 2))

'find the diagonal angle
aRad = Atn(pageHeight / pageWidth)

aCompRad = Atn(pageWidth / pageHeight)

rotobj.Angle = aRad * (180 / pi)

'find the font size for the diagonal
Printer.FontSize = 2

    Printer.FontSize = Printer.FontSize + 2
    twidth = Printer.TextWidth(iso.wmark)
    '// current y changes as text height increases
    lCurrY = (pageHeight * twip) - (margs.bottom * twip) - (Cos(aRad) * Printer.TextHeight("Xyj"))
    '// Need to recalc the hyp based on text height
    '//xhyp = (currenty - top margin) / (cosine(complimentary angle))
    If Cos(aRad) < 0.01 Then 'Prevent overflow
        xHyp = (pageWidth - margs.left - margs.right) * twip
        xHyp = (lCurrY - ( * twip)) / Cos(aCompRad)
    End If
    '//Debug.Print "HYP = ", xHyp, "twidth = ", twidth, "lcurry = ", lCurrY, "Size = ", Printer.FontSize
Loop Until twidth > xHyp

Debug.Print "printer.ScaleMode", Printer.ScaleMode

Printer.FontSize = Printer.FontSize - 2

'//Printer.FontSize = 64

rotobj.Font = Printer.FontSize & ",Arial,0,0,400,0,0,0,0,3, 2, 1, 34"

Debug.Print rotobj.Font

''''find new hyp length by subtracting the width of the text / 2
''''this sould be the space left over along the diagonal after subtracting the text width
'''nHyp = (xHyp - Printer.TextWidth(iso.wmark)) * 0.5

''''width/height of available area
'''wid = (pageWidth - margs.left - margs.right) * twip
'''hite = (pageHeight - - margs.bottom) * twip

'''horizontal center should be
'''width of left margin + length of triangle base from hypotenuse
'''Printer.CurrentX = (margs.left * twip) + (nHyp * Cos(aRad))

'// Current x should be 0 + left margin
lCurrX = margs.left * twip

''''vertical center should be
''''page bottom - bottom margin - length of triangle side from hypotenuse - half the text height
''''Printer.CurrentY = (pageHeight * twip) - (margs.bottom * twip) - (nHyp * Sin(aRad)) - (Printer.TextHeight(iso.wmark) * 0.5)

'// Current y should be page height - bottom margin - (cosine(angle) * text height)
lCurrY = (pageHeight * twip) - (margs.bottom * twip) - (Cos(aRad) * Printer.TextHeight("Xyj"))

'// draw a line around what we think the text is
Dim tmpX As Long, tmpY As Long, lTxtH As Long, lTxtW As Long
Printer.CurrentX = lCurrX
Printer.CurrentY = lCurrY
lTxtH = Printer.TextHeight(iso.wmark)
lTxtW = Printer.TextWidth(iso.wmark)
tmpX = lCurrX + (Cos(aRad) * lTxtW)
tmpY = lCurrY - (Sin(aRad) * lTxtW)
Printer.Line -(tmpX, tmpY)
tmpY = Printer.CurrentY
tmpX = Printer.CurrentX
Printer.Line -(tmpX + Sin(aRad) * lTxtH, tmpY + Cos(aRad) * lTxtH)
tmpY = Printer.CurrentY
tmpX = Printer.CurrentX
Printer.Line -(tmpX - (Cos(aRad) * lTxtW), tmpY + (Sin(aRad) * lTxtW))
tmpY = Printer.CurrentY
tmpX = Printer.CurrentX
Printer.Line -(lCurrX, lCurrY)
'//end draw text outline

'// Draw the margins
tmpX = margs.left * twip
tmpY = * twip
Printer.CurrentX = tmpX
Printer.CurrentY = tmpY
Printer.Line -((pageWidth - margs.right) * twip, tmpY)
tmpX = Printer.CurrentX
tmpY = Printer.CurrentY
Printer.Line -(tmpX, (pageHeight - margs.bottom) * twip)
tmpX = Printer.CurrentX
tmpY = Printer.CurrentY
Printer.Line -(margs.left * twip, tmpY)
tmpX = Printer.CurrentX
tmpY = Printer.CurrentY
Printer.Line -(tmpX, * twip)
Debug.Print "margs",, margs.right, margs.bottom, margs.left
'// end drawing margins

Printer.CurrentX = lCurrX
Printer.CurrentY = lCurrY

rotobj.PrintText iso.wmark
Exit Sub

    chunks = Split(banner, vbCrLf)
    oldY = Printer.CurrentY
    For i = 0 To UBound(chunks)
        rotobj.PrintText CStr(chunks(i))
        Printer.CurrentY = Printer.CurrentY + txHite
    Next i
    Printer.CurrentY = oldY
    chunks = Split(banner, vbCrLf)
    oldX = Printer.CurrentX
    For i = 0 To UBound(chunks)
        rotobj.PrintText CStr(chunks(i))
        Printer.CurrentX = Printer.CurrentX + txHite
    Next i
    Printer.CurrentX = oldX
End Sub
I have not seen the algorithm yet, but a quick question or two:

If the text is not rotated, does it center correctly?

If the text is rotated, do you adjust it's position according to the sine of the angle?
wehoitAuthor Commented:
Erick -

1) Yes

2) Yes - and the Cosine

Check out the code - it's very short and clear, plus it's easy to run and test.
Ultimate Tool Kit for Technology Solution Provider

Broken down into practical pointers and step-by-step instructions, the IT Service Excellence Tool Kit delivers expert advice for technology solution providers. Get your free copy now.

Well I ran your code.

It output "NOT FOR PRODUCTION" on a diagonal across an 8.5x11 size paper.
Running a ruler diagonally across the paper, from corner to corner, lines up with the tops of the letters.
I am assuming you want the alignment through the center of the text?

My printer is an HP Laserjet 5P
wehoitAuthor Commented:
I'm looking for centering - left/right and top bottom, along the diagonal, with the alignment through the center.

It should work for any typeface (or certainly the most common) , upper/lower/mixed case, etc....

I'd like to continue to calculate the font size dynamically so that it fills the available diagonal space inside the margins.
It should not conflict with the border text, regardless of how many lines there are on each page edge.

I have Acrobat, so I typically print to a PDF file for testing - saves a lot of paper if you can do it.

Please feel free to ask any more questions....
wehoitAuthor Commented:
Is anyone working on this?

'Cause if not, I'll delete it and re-submit.

I'm raising the point value this time.
I could not get it working correctly in the time I had to look at it.  I believe the problem lies in calculating the hypotenuse according to the height of the text.  As the textheight increases the triangle defined by (topleft margin) - (currentx currenty) - (the intersection of hyp and the top of page) decreases.

As the height increases:
1) the hypotenuse should decrease
2) CurrentX should remain at the left border
3) CurrentY should decrease

That's as far as I got, no working algorithms.  Feel free to delete and resubmit to the top of the stack.

Good Luck!


Hold the question open a bit longer.  Let me take a crack at it. :p
wehoitAuthor Commented:
I've already asked Support to allow me to close and re-post it, but if they do, I'll let you know.

By all means, have at it!
Bleh, I know how to do it in my head, but I just don't have the math skill to code it.

Good luck to anyone who does get it. :)
wehoitAuthor Commented:
Why don't you try explaining it to me - perhaps I can write the math.

Just so you know, this is what I do now:

1) Calculate the angle from corner to corner
2) Calculate the length of the diagonal (hypotenuse) from corner to corner.
3) Calculate the font size based on using TextWidth until it equals or exceeds the hypotenuse.
4) Calculate the left position by getting the hypotenuse minus the TextWidth, then finding the length of the base of the triangle formed by the 'new' hypotenuse and a horizontal and vertical line. Add this to the left margin.
5) Calculate the vertical (y) position using the same method as step 4.

Unfortunately, I think that TextWidth/TextHeight are not returning correct values...
Arg. I've been working on this for about an hour now, but no success. I'm still a little unsure about what you are calculating at certain points, but I don't think the math is the problem. Here's what I've come up with, maybe it'll spark an idea in someone else's mind (these are referencing the setBanners Sub):

- Printer.FontSize is not being set to the proper values when you are calculating the length of the page's hypotenuse. It seems to always be off by a multiple of 0.08, and is also returning a non-integer when you set rotobj.Font.

- pageWidth and pageHeight are each approximately 0.5 less than the actual dimensions of the paper size (meaning, 0.25" margins all around are automatically being factored in). The actual values of the two variables are not exactly 8.0" and 10.5" for some reason -- they are both off by a few hundredths.

- An observation: The phrase in all capital letters (i.e. "NOT FOR PRODUCTION") is centered properly, but the phrases in lower-case or mixed letters (i.e. "watermark" or "Not For Production") are not centered properly.

It's a conundrum, it is... Lastly,

>> Unfortunately, I think that TextWidth/TextHeight are not returning correct values...

I don't understand what these are supposed to be returning. Is it the height and width of the text at 0 degrees, returned in pixels? Or is it the height and width of the rotated text (meaning, the lengths of the tall vertical leg and the short horizontal leg of the triangle, respectively)?
Sorry if that seems like an obvious question; I understand the actual mathematics involved (aka "triangle stuff"), but I don't understand very well how Visual Basic handles the Printer object in this regard... lol :)

Have you tried to set printer.scalemode, this field is important
wehoitAuthor Commented:
Burbble -

I've noticed the incorrect values that you  pointed out, but I think they are irrelevant. If they're always off by the same amount (which they are) then the math should still place the watermark at the same place every time - which, of course, it doesn't.

As for TextWidth/Height - it returns the width or height of the text based on 0 degrees. It doesn't take into account any rotation. Besides, I use it before the rotation.
Also, they return units based on the ScaleMode setting of the device. In this case, the printer is Twips by default.

Which also refers to the comment by EDDYKT - I don't change the ScaleMode - it's twips and I work with twips for all the other calculations.

Yes, it is incredibly frustrating!!! I've been tinkering with this for DAYS, which leads me to believe that the math is basically sound, but the TextWidth/Height is wrong.
I've also tried the API GetTextExtentPoint32 which returns different values but still doesn;t solve the problem...

I wanted to change the point value of this question to 750 but I'm not allowed to.

I really need a solution to this problem, and I know it can be solved...
Just a thought, instead of using arial can you use courier which is fixed size for all character

also use form to return textwidth instead of using printer object and see what is the different
As a side note.  Please use Option Explicit as the first line in all modules.  This forces you to Dim all your variables.  I ran across a couple undeclared variables in your code ("half" for example which I believe should be called "halftwip").  This kind of error is very difficult to trace because the compiler does not complain, and just sets the value to 0.
wehoitAuthor Commented:
Erick -

Your code is consistent, but I'm looking to get the watermark centered. Yours is always in the lower-left corner.
Also, it seems as though the biggest change you make is when you calculate the font size - it now comes out smaller than mine - why did you this?

The actual code for positioning the text (setting CurrentX and CurrentY) is nearly identical to my original code.

I've been tinkering with it still, and have found that TextHeight includes not only the height of the actual text, but also the imposed line spacing. My testing came up with a value of 0.155 which can account for this spacing. (Your example shows this line spacing when you draw the box around the text area.)
When I incorporate this into my earlier code I get this for the CurrentY:
Printer.CurrentY=(pageHeight * twip) - (margs.bottom * twip) - (Printer.TextHeight(iso.wmark) * 0.155) - (Printer.TextHeight(iso.wmark) * half)
This is more accurate for the vertical centering although it is still not completely accurate.

Regarding the TextWidth, it seems to calculate additional space based on the text kerning and the fact that it's a proportional font (I can't use a fixed-width font like EDDYKT suggested 'cause I need to give the user the option of selecting any font...) So, for example, if you don't use the Rotate method ans simply try to left-align the text at 0, you'll see what I mean....

As for "half" - that's definitely my fault - I removed the lines defining and setting the var - it's supposed to be "half=0.5" - sorry.
Halftwip is something else. If you notice, when you changed "half" to "halftwip", the centered text around the edges disappeared...
wehoitAuthor Commented:
All -

I've been working on this and have come very close to solving it.
I've updated the sample code at this link:

I'd still like to get it working 100%.

wehoit: I do not have a printer available, so I cannot test the code, and hence am not looking at it, but can you offset the text vertically by [height/2] before rotating it? This will align it at the center, and make it seem more of in-the-same-spot each time.  
Also, if you change the font to a fixed-pitch font, and try two different strings of the same character length, do they show up in the same place?
wehoitAuthor Commented:
tonsofpcs -

First - be aware that I wanted to reduce the point value - mainly to Erick 200 pts since no one else really came close.

Also, be aware that I have mainly solved it my self and have uploaded newer test code.

I have offset the text before rotation, by several methods. You'll have to look at the code for the current solution as it's fairly complex.

As for Fixed -width fonts, they have a tendency to look worse and still not be positioned any better than variable-width fonts.

If someone wants to take my uploaded code and make it work 100%, I'll split the points with you and Erick.
wehoitAuthor Commented:
While not a complete answer, it did help point me in the right direction to *mostly* solve this myself.

Thanks for the effort.
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.