A print sample code for text listings

Share your advanced PureBasic knowledge/code with the community.
fweil
Enthusiast
Enthusiast
Posts: 725
Joined: Thu Apr 22, 2004 5:56 pm
Location: France
Contact:

A print sample code for text listings

Post by fweil »

Here you will find a simple text print code.

It is not exactly a must, but works good on my PC.

I added some lines to start to design a preview, a bit slow right now.

Have fun ... any comment / suggestion appreciated.

Code: Select all

;
; Print text
; F.Weil : 20040726
;
; Here is a sample code to touch what is text printing using full bitmap drawing.
; It is Windows API dependant. I guess a Linux coder would easily add the required lines, maybe coding conditional compiler directives.
; This code does not apply to Rich Edit text. Only to text printing with no embedding.
; Easy to customize however.
; ====================================================
; Voici un programme permettant de mettre les doigts dans l'impression de texte passant par des bitmaps.
; Ce code est dépendant de Windows. Je pense qu'un codeur Linux devrait pouvoir ajouter le pendant des commandes appropriées, éventuellement en plaçant des directives de compilation conditionnelle
; Ce code ne s'applique pas à du texte de type Rich Edit. Seulement au texte non décoré.
; Toutefois l'amélioration en est relativement accessible.
;
Enumeration
  #Window_Preview
  #Image_Preview
  #Image_FullPrint
  #Gadget_Image_Preview
  #DrawingFont
  #Requester_OK = 6
  #Requester_NO
EndEnumeration

#MAX_STRING_LENGTH = $400000

Structure StringBytes
  Bytes.b[#MAX_STRING_LENGTH]
EndStructure

NewList Print_text_PageName.s()

;
; Set strings length at a # max value than PureBasic's default
; ========================================
; Modifie la longueur max des chaines de caractères.
;
Procedure ManipulationBufferSize(Bytes)
  PBStringBase.l = 0
  PBMemoryBase.l = 0
  !MOV eax, dword [PB_StringBase]
  !MOV [esp+4], eax
  !MOV eax, dword [PB_MemoryBase]
  !MOV [esp+8], eax
  HeapReAlloc_(PBMemoryBase, #GMEM_ZEROINIT, PBStringBase, Bytes)
  !MOV dword [PB_StringBase], eax
  Debug "ManipulationBufferSize : max length of string set to : " + Str(Bytes)
  ProcedureReturn Bytes
EndProcedure

;
; Split a full text placed in StringIn, in lines corresponding to a given drawing width. The text is returned to the caller
; =========================================================================
; Découpe un texte entier placé dans StringIn pour faire correspondre les lignes à une largeur de dessin donné. Le texte transformé est retourné à l'appelant.
;
Procedure.s FoxLinedText2D(StringIn.s, PixelWidth.l)
  LenStringIn = Len(StringIn)
  LenStringOut = LenStringIn + 3 * (Int(LenStringIn / PixelWidth) + 1)
  StringOut.s = Space(LenStringOut)
  *MyStringIn.StringBytes = @StringIn
  *MyStringOut.StringBytes = @StringOut
  j = 0
  For i = 0 To LenStringIn - 1
    Cod.b = *MyStringIn\Bytes[i]
    If Cod = 13 Or Cod = ' ' Or Cod = '.' Or Cod = ','
        LastWordBreakInPosition = i
        LastWordBreakOutPosition = j
        If Cod = 13
            LastLineBreak = j + 3
        EndIf
    EndIf
    If k > PixelWidth And i > 0 ; means the counter reached the right margin
        io = i
        jo = j
        i = LastWordBreakInPosition + 1
        j = LastWordBreakOutPosition
        k = 0
        *MyStringOut\Bytes[j] = 13
        *MyStringOut\Bytes[j + 1] = 10
        *MyStringOut\Bytes[j + 2] = *MyStringIn\Bytes[i] ; Asc(Mid(StringIn, i, 1))
        LastLineBreak = j + 2
        If i = LastBreakDone
            LastWordBreakInPosition = io - 1
            LastWordBreakOutPosition = jo
        EndIf
        LastBreakDone = i
        j + 2
      Else
        *MyStringOut\Bytes[j] = *MyStringIn\Bytes[i] ; Asc(Mid(StringIn, i, 1))
    EndIf
    k = TextLength(Mid(StringOut, LastLineBreak, j - LastLineBreak))
    j + 1
  Next
  *MyStringOut\Bytes[j] = 0
  ProcedureReturn StringOut
EndProcedure

;
; Build a preview image from the image stored in FileName, using desktop metrics
; ====================================================
; Construit une image preview à partir de l'original situé dans le fichier cité, en utilisant la métrique de l'écran
;
Procedure MakePreview(FileName.s)
  ImageID_FullSize = LoadImage(#Image_FullPrint, FileName)
  PreviewWidth = ImageWidth()
  PreviewHeight = ImageHeight()
  HRatio.f = 1.2 * PreviewWidth / GetSystemMetrics_(#SM_CXSCREEN)
  VRatio.f = 1.2 * PreviewHeight / GetSystemMetrics_(#SM_CYSCREEN)
  If HRatio => VRatio
      ImageWidth = PreviewWidth / HRatio
      ImageHeight = PreviewHeight / HRatio
      ImageID_Preview = ResizeImage(#Image_FullPrint, ImageWidth, ImageHeight)
    Else
      ImageWidth = PreviewWidth / VRatio
      ImageHeight = PreviewHeight / VRatio
      ImageID_Preview = ResizeImage(#Image_FullPrint, ImageWidth, ImageHeight)
  EndIf
  UseImage(#Image_FullPrint)
  StartDrawing(ImageOutput())
    DrawingMode(4)
    Box(0, 0, ImageWidth, ImageHeight, #Black)
  StopDrawing()
  ProcedureReturn ImageID_Preview
EndProcedure

;
; Draw the text in images build to the printer choosen inside this procedure.
; If the given printer allows both Portrait / Landscape, just select the desired parameters in the printer requester.
; This procedure does support multiple pages. Required pages are drawn in bitmaps queued in files for print / preview.
; The queued files names are stored in Print_text_PageName() linked list.
;
; CPI (characters per inch) and font name are received from the caller to make this procedure more flexible.
; All required metrics are turned to available printer parameters. When selecting a printer the metrics are updated for this printer.
;
; The preview feature is actually not fast. I will go back on this further more in some days / weeks.
; =========================================================================
; Dessine le texte dans les images nécessaires à l'impression éventuellement multi-pages.
; L'imprimante est choisie à l'intérieur de la présente procédure. Si l'imprimante choisie autorise par exemple le portrait / paysage
; les paramètres sont automatiquement recalculés en fonction du choix opéré.
; L'impression est générée dans autant de bitmaps que de pages sont nécessaires. Les bitmaps sont stockés en attente sur le disque.
; Les noms des bitmaps sont stockés dans la liste Print_text_PageName().
;
; Les paramètres CPI (nombre de caractères au pouce) et FontName (nom de la police), sont donnés pour rendre la procédure plus flexible.
; Toute la métrique nécessaire et suffisante est calculée pour correspondre à l'imprimante choisie au moment de l'impression.
;
; La fonction de prévisualisation est actuellement peu performante. Je reprendrai ce point dans quelques jours / semaines.
;
Procedure PrintText(Text.s, CPI.l, FontName.s)
  ClearList(Print_text_PageName())
  If PrintRequester()
     printer_DC.l = StartDrawing(PrinterOutput())
      If  printer_DC
          HorizontalDPI = GetDeviceCaps_(printer_DC,#LOGPIXELSX)
          VerticalDPI = GetDeviceCaps_(printer_DC,#LOGPIXELSY)
          FontSize = HorizontalDPI / CPI
          FontHeight = FontSize * 1.6
          PrinterLeftMargin = GetDeviceCaps_(printer_DC,#PHYSICALOFFSETY)
          PrinterTopMargin = GetDeviceCaps_(printer_DC,#PHYSICALOFFSETX)
          UserMargin = HorizontalDPI
          PixelWidth = GetDeviceCaps_(printer_DC,#PHYSICALWIDTH)
          PixelHeight = GetDeviceCaps_(printer_DC,#PHYSICALHEIGHT)
          PrinterRowHeight = FontHeight
          StopDrawing()
          ImageID_FullPrint = CreateImage(#Image_FullPrint, PixelWidth, PixelHeight)
          If ImageID_FullPrint
              StartDrawing(ImageOutput())
                Box(0, 0, PixelWidth, PixelHeight, #White)
                DrawingFont(LoadFont(#DrawingFont, FontName, FontSize, #PB_Font_HighQuality))
                TextOut.s = FoxLinedText2D(Text, PixelWidth - UserMargin * 2)
                BackColor(255, 255, 255)
                FrontColor(0, 0, 0)
                DrawingMode(1)
                LineNumber = 0
                While TextOut <> ""
                  LineOut.s = Mid(TextOut, 1, FindString(TextOut, Chr(13) + Chr(10), 1) - 1)
                  TextOut = Mid(TextOut, FindString(TextOut, Chr(13) + Chr(10), 1) + 2, Len(TextOut) - FindString(TextOut, Chr(13) + Chr(10), 1) - 2 + 1)
                  Locate(UserMargin, UserMargin + PrinterRowHeight * LineNumber)
                  DrawText(LineOut)
                  LineNumber + 1
                  If UserMargin + PrinterRowHeight * LineNumber => PixelHeight - UserMargin
                      AddElement(Print_text_PageName())
                      Print_text_PageName() = "Print text." + Str(ElapsedMilliseconds()) + ".jpg"
                      Debug "Adding page : " + Print_text_PageName()
                      SaveImage(#Image_FullPrint, Print_text_PageName(), #PB_ImagePlugin_JPEG)
                      StopDrawing()
                      FreeImage(#Image_FullPrint)
                      ImageID_FullPrint = CreateImage(#Image_FullPrint, PixelWidth, PixelHeight)
                      StartDrawing(ImageOutput())
                      Box(0, 0, PixelWidth, PixelHeight, #White)
                      DrawingFont(LoadFont(#DrawingFont, FontName, FontSize, #PB_Font_HighQuality))
                      BackColor(255, 255, 255)
                      FrontColor(0, 0, 0)
                      DrawingMode(1)
                      LineNumber = 0
                  EndIf
                Wend
              StopDrawing()
              AddElement(Print_text_PageName())
              Print_text_PageName() = "Print text." + Str(ElapsedMilliseconds()) + ".jpg"
              Debug "Adding page : " + Print_text_PageName()
              SaveImage(#Image_FullPrint, Print_text_PageName(), #PB_ImagePlugin_JPEG)
              FreeImage(#Image_FullPrint)
          EndIf
          Select MessageRequester("Print request", "Do you want to print (OK) / Preview (No) / Give up (Cancel)", #PB_MessageRequester_YesNoCancel)
            Case #Requester_OK
              If StartPrinting("PureBasic Test")
                  If StartDrawing(PrinterOutput())
                      ResetList(Print_text_PageName())
                      While NextElement(Print_text_PageName())
                        Debug "Printing page : " + Print_text_PageName()
                        LoadImage(#Image_FullPrint, Print_text_PageName())
                        DrawImage(ImageID(), 0, 0)
                        DeleteFile(Print_text_PageName())
                        DeleteElement(Print_text_PageName())
                        NewPrinterPage()
                      Wend
                      StopPrinting()
                      StopDrawing()
                  EndIf
              EndIf
            Case #Requester_NO
              ResetList(Print_text_PageName())
              NextElement(Print_text_PageName())
              PreviewWidth = ImageWidth()
              PreviewHeight = ImageHeight()
              HRatio.f = 1.2 * PreviewWidth / GetSystemMetrics_(#SM_CXSCREEN)
              VRatio.f = 1.2 * PreviewHeight / GetSystemMetrics_(#SM_CYSCREEN)
              If HRatio => VRatio
                  ImageWidth = PreviewWidth / HRatio
                  ImageHeight = PreviewHeight / HRatio
                Else
                  ImageWidth = PreviewWidth / VRatio
                  ImageHeight = PreviewHeight / VRatio
              EndIf
              If OpenWindow(#Window_Preview, 0, 0, ImageWidth, ImageHeight, #PB_Window_SystemMenu | #PB_Window_ScreenCentered, "Preview window")
                  AddKeyboardShortcut(#Window_Preview, #PB_Shortcut_Escape, #PB_Shortcut_Escape)
                  AddKeyboardShortcut(#Window_Preview, #PB_Shortcut_Up, #PB_Shortcut_Up)
                  AddKeyboardShortcut(#Window_Preview, #PB_Shortcut_Down, #PB_Shortcut_Down)
                  If CreateGadgetList(WindowID(#Window_Preview))
                      ImageGadget(#Gadget_Image_Preview, 0, 0, ImageWidth, ImageHeight, MakePreview(Print_text_PageName()))
                  EndIf
                  Repeat
                    Select WaitWindowEvent()
                      Case #PB_Event_CloseWindow
                        Quit = #TRUE
                      Case #PB_Event_Menu
                        Select EventMenuID()
                          Case #PB_Shortcut_Escape
                            Quit = #TRUE
                          Case #PB_Shortcut_Up
                            PreviousElement(Print_text_PageName())
                            SetGadgetState(#Gadget_Image_Preview, MakePreview(Print_text_PageName()))
                          Case #PB_Shortcut_Down
                            NextElement(Print_text_PageName())
                            SetGadgetState(#Gadget_Image_Preview, MakePreview(Print_text_PageName()))
                        EndSelect
                    EndSelect
                  Until Quit
              EndIf
          EndSelect
      EndIf
  EndIf
  While CountList(Print_text_PageName())
    LastElement(Print_text_PageName())
    DeleteFile(Print_text_PageName())
    DeleteElement(Print_text_PageName())
  Wend
EndProcedure

;
; The Main program cnotains initalization for using JPEG files and enhancing the string max length.
; Then you can just place any text in the parameter to call PrintText() with the desired font size and name.
; ===================================================================
; Le programme principal contient simplement les initialisations permettant d'utiliser les images JPEG et les chaines de longueur non standard.
; Il suffit d'appeler PrintText() avec nu texte quelconque et les paramètres de police souhaités.
;
Text.s
  ManipulationBufferSize(#MAX_STRING_LENGTH)
  UseJPEGImageDecoder()
  UseJPEGImageEncoder()

  If ReadFile(0, "Print text.pb")
      DataLength = Lof()
      *Buffer = AllocateMemory(DataLength)
      ReadData(*Buffer, DataLength)
      CloseFile(0)
      Text = PeekS(*Buffer)
      PrintText(Text, 12, "Verdana")
  EndIf
End
My avatar is a small copy of the 4x1.8m image I created and exposed at 'Le salon international du meuble à Paris' january 2004 in Matt Sindall's 'Shades' designers exhibition. The original laminated print was designed using a 150 dpi printout.
User avatar
Flype
Addict
Addict
Posts: 1542
Joined: Tue Jul 22, 2003 5:02 pm
Location: In a long distant galaxy

Post by Flype »

@fweil: sorry i will write the next words in french

connais-tu un pilote d'impression virtuel pour win32 ?
ton truc m'intéresse mais n'ayant pas d'imprimante
chez moi pour tester, j'apprécierais bien de pouvoir
visualiser sans imprimer réellement,
il me semble bien que çà existe :roll:
No programming language is perfect. There is not even a single best language.
There are only languages well suited or perhaps poorly suited for particular purposes. Herbert Mayer
User avatar
Flype
Addict
Addict
Posts: 1542
Joined: Tue Jul 22, 2003 5:02 pm
Location: In a long distant galaxy

Post by Flype »

i was watching for a virtual printer in order to test printer outputs without printing for real. i found one but not really free :

http://www.download.com/redir?pid=43838 ... 83898.html

thanx for sharing fweil, it's like always from you very interessant...
No programming language is perfect. There is not even a single best language.
There are only languages well suited or perhaps poorly suited for particular purposes. Herbert Mayer
Post Reply