Announcement

Collapse
No announcement yet.

XPrinting Text with Embedded Tab Characters

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • XPrinting Text with Embedded Tab Characters

    Another thread is covering printing richedit content. This one discusses printing text strings using XPrint, where the text strings contain $TAB characters.

    I'll post the code in the Source Code forum as soon once this thread completes. I figure there are always improvements someone will suggest - something I welcome.

    There are two basic approaches to printing with embedded TABs - expanding the TABs to spaces (fixed-width fonts) or setting the print POS (variable-width fonts) every time a TAB character is encountered.

    This code works with variable width fonts.

    The method is to parse the line on $tab characters, then print each element one at a time. After each element is printed, the x position is moved to the next tab location.

    This took surprisingly little code. Here's the primary code involved:

    Code:
    Sub PrintSingleTextLineWithTabs (temp$)
       Local i As Long, x As Single, y As Single
       For i = 1 To ParseCount(temp$, $Tab)
          XPrint Parse$(temp$,$Tab, i) ;
          XPrint Get Pos To x,y
          XPrint Set Pos (TabLoc((Fix(x/0.5)+1)),y)
       Next i
       XPrint
    End Sub
    This example uses a richedit control, whose default tab positions are set every 0.5 inches.

    There are two critical parts to this approach.

    1. The printer and richedit control must be set to the same font.
    2. The Tab positions of the richedit must be used on the printer.

    Break either of these rules and the printed result may not look as it did in the richedit control.

    Code:
    'Compilable Example:
    #Compile Exe
    #Dim All
    #Include "Win32API.inc"
    #Include "RichEdit.inc"
    #Include "CommCtrl.inc"
    Global hDlg as Dword, hRichEdit as Dword, TabLoc() as Single, hFont as Dword
    %ID_RichEdit = 500
    Function PBMain() As Long
       Local style&, buf$
       buf$ = "This is" + $Tab + $Tab + "an example" + $Tab + "of a string with an embedded tab character."
       style& = %WS_Child Or %WS_Visible Or %ES_MultiLine Or %WS_VScroll Or %ES_AutoHScroll _
                 Or %WS_HScroll Or %ES_AutoVScroll Or %ES_WantReturn Or %ES_NoHideSel Or %WS_TabStop
       Dialog New Pixels, 0, "Test Code",300,300,550,150, %WS_OverlappedWindow To hDlg
       Control Add Button, hDlg, 100,"Print", 30,10,140,20
       LoadLibrary("riched32.dll") : InitCommonControls
       Control Add "RichEdit", hDlg, %ID_RichEdit, buf$,20,40,510,100, style&, %WS_EX_ClientEdge
       Control Handle hDlg, %ID_RichEdit To hRichEdit
       Font New "Comic Sans MS", 10, 0 To hFont
       Control Set Font hDlg, %ID_RichEdit, hFont
       Dialog Show Modal hDlg Call DlgProc
    End Function
    CallBack Function DlgProc() As Long
       If CB.Msg = %WM_Command AND CB.Ctl = 100 AND CB.Ctlmsg = %BN_Clicked Then
          Local temp$, w as Single, h as Single, i as Long
          ReDim TabLoc(50)  'tab locations
          For i = 0 To 50 : TAbLoc(i) = i * 0.5 : Next i   '0.5" tab locations
          XPrint Attach Default
          XPrint Set Font hFont
          SetPrinterScaleToInches w,h             'w,h are return values, not used in this example
          Control Get Text hDlg, %ID_RichEdit To temp$
          PrintSingleTextLineWithTabs (temp$)    'temp$ is text string with tabs
          XPrint Close
       End If
    End Function
    
    Sub SetPrinterScaleToInches (ncWidth!, ncHeight!)
          Local x&, y&
          XPrint Get Client TO ncWidth!, ncHeight!  'Retrieve the client size (printable area) of the printer page
          XPrint Get PPI TO x&, y&                  'Retrieve the resolution (points per inch) of the attached printer
          ncWidth! = ncWidth!/x&                    'Width in inches of the printable area
          ncHeight! = ncHeight!/y&                    'Height in inches of the printable area
          XPrint Scale (0,0)-(ncWidth!,ncHeight!)    'Set scale to inches
    End Sub
    
    Sub PrintSingleTextLineWithTabs (temp$)
       Local i As Long, x As Single, y As Single
       For i = 1 To ParseCount(temp$, $Tab)
          XPrint Parse$(temp$,$Tab, i) ;
          XPrint Get Pos To x,y
          XPrint Set Pos (TabLoc((Fix(x/0.5)+1)),y)
       Next i
       XPrint
    End Sub
    This approach does not print with the same formatting (variable text size, colors, ...) that printing with RTF provides. But it does offer an easy way to programmatically add text (such as headers/footers).

    This is another case where I expected to see several solutions in the forums, and was surprised not to find the code. It would seem like a very common event - printing text with embedded tabs. Perhaps I just missed it - that seems to happen a lot, where the search terms I use don't always locate the discussion in the forum.
    Last edited by Gary Beene; 19 Aug 2009, 12:20 AM.

  • #2
    Gary, IMHO you are better off using SET POS than any type of Tab for XPRINT. If you are referring to Variable Fonts vs Fixed Fonts, I know from working with PCL for years that using Column numbers with variable fonts is unreliable from printer to printer whereas it works pretty well for Fixed Fonts.
    I would think that perhaps the same applies to $TAB or even TAB(n).
    Client Writeup for the CPA

    buffs.proboards2.com

    Links Page

    Comment


    • #3
      Hi Fred,

      ?? Set POS is exactly what my code uses. So I'm not sure what you mean.

      I suspect that you're referring to when information is generated, that using SET POS is the best way to position it, without using TABs - which I certainly agree with.

      But when all I'm doing is printing pre-existing content that already has TAB characters in it, I have no way of knowing what POS the originator wanted, only what they created in the textbox. So that's the POS that my code uses.

      Also, I'm not sure what your comment meant, about :

      ... column numbers ...
      My code didn't use column numbers, so perhaps you were just speaking generically? In that case you're certainly right, with variable width fonts using column numbers is a non-aligning solution.

      In my example, I knew that a richedit control TABs are set to 0.5 inches so I used those to populate the array containing the TAB positions. In a more generic solution, I'd have read the current TAB settings and assigned those values to the array. Perhaps that's what caught your eye, thinking of the settings I used as arbitrary columns?
      Last edited by Gary Beene; 19 Aug 2009, 08:19 AM.

      Comment


      • #4
        Ooh, ooh, I think I see a new feature suggestion for XPRINT users....
        Code:
           XRINT  SET TABSTOPS   TabStops() | OFF  
           XPRINT stringvar$
        (I am not sending in since I would not use).
        Michael Mattias
        Tal Systems (retired)
        Port Washington WI USA
        [email protected]
        http://www.talsystems.com

        Comment


        • #5
          Yes Gary the Column reference was as an example rather that specific to your case though I think the tab character chr$(9) as I recall, will vary for display purposes and printers I would think, even on different printers. I think Michael has the idea, but then if you don't know where tha tab character should be putting the text then it seems that setting tabs for XPRINT will not solve the problem either.
          Client Writeup for the CPA

          buffs.proboards2.com

          Links Page

          Comment


          • #6
            Why not write a function to expand tabs into spaces and then just print the result string?

            e.g.,...
            Code:
            TYPE MyTAB
              pos      AS SINGLE     ' inches, millimeters, columns, whatever 
              justify   AS LONG      ' true = right justified
            END TYPE 
            REDIM   MT (nPos-1) AS MyTab
            ' set up tabs 
            Build text string w/tabs embedded = S 
            CALL   MakeOneString (S, MT()) TO  OutputString
            XPRINT OutputString
            Write once, use many.

            (Post in Source Code forum, too).

            MCM
            Michael Mattias
            Tal Systems (retired)
            Port Washington WI USA
            [email protected]
            http://www.talsystems.com

            Comment


            • #7
              Sorry, MCM, I missed your earlier post.

              Actually, I put that code in a separate post:

              "Getting Physical Length of String"
              http://www.powerbasic.com/support/pb...ad.php?t=41287

              And since then I made up two ways to do it:

              In the first, I calculate the needed physical width, then add replace each tab with a string of spaces.

              In the second, I brute forced it - adding a single space, calculating the new width (as compared to a tab stop), then add more spaces until I exceed the tab stop width.

              In both cases you have make sure the ending position, once spaces are added, is not less than the tabstop position, so that in the next iteration you've cleared the last tabstop and won't hit one twice.

              Code:
              Approach one: build print string, substituting tabs with calculated # of spaces to reach tabstops
              
                 For j = 0 To UBound(D)
                    temp$ = ""
                    For i = 1 To ParseCount(D(j), $Tab)
                       temp$ = temp$ + Parse$(D(j),$Tab, i)
                       XPrint Text Size temp$ To tempWidth!, ncHeight!
                       iTab& = Fix(tempWidth!/0.5)+1
                       iSpaces& = (TabLoc(iTab&)-tempWidth!) / iSpaceWidth!
                       temp$ = temp$ + Space$(iSpaces&)
                         'add extra single space as needed to ensure current tab position is crossed
                          XPrint Text Size temp$ To tempWidth!, ncHeight!
                          If tempWidth! < TabLoc(iTab&) Then temp$ = temp$ + " "
                    Next i
                    result$ = result$ + $CrLf + temp$
                 Next J
              
              Approach Two: build replacement string, add spaces until tabstop is passed
              
                 For j = 0 To UBound(D)
                    temp$ = ""
                    For i = 1 To ParseCount(D(j), $Tab)
                       temp$ = temp$ + Parse$(D(j),$Tab, i)
                       XPrint Text Size temp$ To tempWidth!, ncHeight!
                       iTab& = Fix(tempWidth!/0.5)+1        'next location tab after current endpoint
                       If iTab& < 50 Then
                          Do
                             temp$ = temp$ + " "
                             XPrint Text Size temp$ To tempWidth!, ncHeight!
                          Loop While tempWidth! < TabLoc(iTab&)
                       End If
                    Next i
                    result$ = result$ + $CrLf + temp$
                 Next j


              The point of the other post was to find a way to not use XPrint as part of the solution for getting the right number of spaces. As you saw in that post,
              I tried using GetTextExtentPoint32 in lieu of XPrint statements. It worked, but not perfectly - giving consistently short positions. I'd assumed that was because I failed to take into account borders/edges in some way, but I've not followed up on it to run it to ground.

              Last edited by Gary Beene; 25 Aug 2009, 04:26 PM.

              Comment

              Working...
              X