Android Question [Solved] B4X Create PDF, (HTML?, RTF?) - Print - best/simplest solution?

MrKim

Well-Known Member
Licensed User
Longtime User
Edit 1/24/21: OK, so what I wound up doing is using a hidden CustomListView and then using the snapshot feature to make a Bitmap, which I then sent to a PDF file. In my real business my partner does all of the reports. I really don't like all of the formatting stuff and my partner loves it.
In any case by carefully formatting the CLV with the the appropriate borders on the boxes, etc. It gave me a nice columnar layout. I only had format the header and and footer. I also had to keep track of the number of rows, and the column and when I filled the page (about 35 rows) "Print" the appropriate column, clear the CLV, and start again.

Here is the relevant code - I have left out the part that fills in the CLV:

B4X:
'Code to open the file - Dim pdf As PdfDocument, DestRect As Rect
    #If B4A
        D = File.DirInternal
    #ELSE IF B4I
        D = File.DirDocuments
    #END IF
    If File.Exists(D, "YourFileName.pdf") Then
        File.Delete(D, "YourFileName.pdf")
    End If
    
    #If B4A
        ProgressDialogShow2("Formatting Page 1", False)
        pdf.Initialize
        pdf.StartPage(612, 792) ' (8.5 in × 11 in).
    #ELSE IF B4I
        HUD.ProgressDialogShow("Formatting Page 1")
        Dim PDFCanvas As Canvas
        pdf.Initialize(D, "ALAPhone.pdf")
        PDFCanvas.InitializePDF(D, "ALAPhone.pdf", 612, 792)
    #END IF
    Sleep(1)

'Code to snapshot the scrollview (PrintCLD)
            bmp = PrintCLV.sv.ScrollViewInnerPanel.Snapshot
'This determines whether it is column 1 or two
            If Y Mod 2 = 0 Then
                DestRect.Initialize(40, FPColTop, 305, (LineHeight * PrintCLV.Size) + FPColTop - 10)  '(612W, 792H) 297
            Else
                DestRect.Initialize(305, FPColTop, 572, (LineHeight * PrintCLV.Size) + FPColTop - 10)  '(612W, 792H) 297
            End If
            RowsLeft = TotalRows - X
'Now draw it on the pdf
            #If B4A
                pdf.Canvas.DrawBitmap(bmp, Null, DestRect)
                If (Floor(Y / 2) + 1) > 1 Then pdf.Canvas.DrawText($"(${NumberFormat((Floor(Y / 2) + 1), 1, 0)})"$, (612 / 2), DestRect.Bottom + 30, Typeface.DEFAULT, 5, xui.Color_Black, "CENTER")
                If OfficersCB.Checked = True Then
                    pdf.Canvas.DrawText($"(S)-Secretary, (T)-Treasurer, (L)-Literature, (G)-Group Rep, (A)-Phone List Admin"$, (612 / 2), DestRect.Bottom + 12, Typeface.DEFAULT, 3, xui.Color_Black,  "CENTER")
                End If
                pdf.Canvas.DrawLine(DestRect.Left, DestRect.Bottom, DestRect.Right - 1, DestRect.Bottom - 1, xui.Color_Black, .5)
            #ELSE IF B4I
                PDFCanvas.DrawBitmap(bmp, DestRect)
                If (Floor(Y / 2) + 1) > 1 Then PDFCanvas.DrawText($"(${(Floor(Y / 2) + 1)})"$, (612 / 2), DestRect.Bottom + 40, Font.CreateNew(20), xui.Color_Black, "CENTER")
                If OfficersCB.Checked = True Then
                    PDFCanvas.DrawText($"(S)-Secretary, (T)-Treasurer, (L)-Literature, (G)-Group Rep, (A)-Phone List Admin"$, (612 / 2), DestRect.Bottom + 15, Font.CreateNew(12), xui.Color_Black, "CENTER")
                End If
                PDFCanvas.DrawLine(DestRect.Left, DestRect.Bottom, DestRect.Right - 1, DestRect.Bottom - 1, xui.Color_Black, 1)
                PDFCanvas.Refresh
            #END IF
'code to finish a page and start the next page
                #If B4A
                    pdf.FinishPage
'                #ELSE IF B4I
'                    PDFCanvas.NextPDFPage     'B4i only needs NextPdfPage, Not finishpage AND startPAge
                #END IF
                    #If B4A
                        pdf.StartPage(612, 792)
                    #ELSE IF B4I
                        PDFCanvas.NextPDFPage
                    #END IF
'Code to 'close' the pdf file
                #If B4A
                    pdf.FinishPage
                #ELSE IF B4I
                    PDFCanvas.Release
                #END IF

The following is some code I used to print the footer - might be useful to someone:

B4X:
            #If B4A
                pdf.Canvas.DrawBitmap(bmp, Null, DestRect)
                If (Floor(Y / 2) + 1) > 1 Then pdf.Canvas.DrawText($"(${NumberFormat((Floor(Y / 2) + 1), 1, 0)})"$, (612 / 2), DestRect.Bottom + 30, Typeface.DEFAULT, 5, xui.Color_Black, "CENTER")
                If OfficersCB.Checked = True Then
                    pdf.Canvas.DrawText($"(S)-Secretary, (T)-Treasurer, (L)-Literature, (G)-Group Rep, (A)-Phone List Admin"$, (612 / 2), DestRect.Bottom + 12, Typeface.DEFAULT, 3, xui.Color_Black,  "CENTER")
                End If
                pdf.Canvas.DrawLine(DestRect.Left, DestRect.Bottom, DestRect.Right - 1, DestRect.Bottom - 1, xui.Color_Black, .5)
            #ELSE IF B4I
                PDFCanvas.DrawBitmap(bmp, DestRect)
                If (Floor(Y / 2) + 1) > 1 Then PDFCanvas.DrawText($"(${(Floor(Y / 2) + 1)})"$, (612 / 2), DestRect.Bottom + 40, Font.CreateNew(20), xui.Color_Black, "CENTER")
                If OfficersCB.Checked = True Then
                    PDFCanvas.DrawText($"(S)-Secretary, (T)-Treasurer, (L)-Literature, (G)-Group Rep, (A)-Phone List Admin"$, (612 / 2), DestRect.Bottom + 15, Font.CreateNew(12), xui.Color_Black, "CENTER")
                End If
                PDFCanvas.DrawLine(DestRect.Left, DestRect.Bottom, DestRect.Right - 1, DestRect.Bottom - 1, xui.Color_Black, 1)
                PDFCanvas.Refresh
            #END IF

And here is some ugly but effective code I used to fit the text to the views on the CLV - If you have a better solution let me know.

B4X:
        #If B4J OR B4A
'B4XCod is a BXCode
            If MP.BXCod.MeasureTextWidth(NameLbl.Text, NameLbl.Font) > NameLbl.Width Then
                Do While MP.BXCod.MeasureTextWidth(NameLbl.Text, NameLbl.Font) > NameLbl.Width
                    NameLbl.TextSize = (NameLbl.TextSize - 1)
                Loop
            End If
            If MP.BXCod.MeasureTextWidth(Phone1Lbl.Text, Phone1Lbl.Font) > Phone1Lbl.Width Then
                Do While MP.BXCod.MeasureTextWidth(Phone1Lbl.Text, Phone1Lbl.Font) > Phone1Lbl.Width
                    Phone1Lbl.TextSize = (Phone1Lbl.TextSize - 1)
                Loop
            End If
            If MP.BXCod.MeasureTextWidth(EmailLbl.Text, EmailLbl.Font) > EmailLbl.Width Then
                Do While MP.BXCod.MeasureTextWidth(EmailLbl.Text, EmailLbl.Font) > EmailLbl.Width
                    EmailLbl.TextSize = (EmailLbl.TextSize - 1)
                Loop
            End If
        #End If

Note - I did not try to print directly. Just create the pdf, I have a seperate button to print and one to share the file externally if the user want to do it some other way - like email it to themselves to print form their home computer.

End of Edit


I need to create a simple phone list for printing. Name, Number, Email on 8 1/2 x 11 paper.
Ideally if I can provide a print button easily as well that would be ideal, but if I can get a pdf/html/rtf file then the user can print it.
I am not familiar with the commands, formatting for any of these things so I will have to figure it out as I go.
I need it to work on B4A and B4i, and ideally eventually B4J as well. I might want to add thumbnail pictures in the future but for now I am looking for simple.
As usual, I find any number of solutions for each platform here, but not for all.
I'm OK writing separate code for each platform if I have to but I thought I would post here first and see if anyone has any suggestions for the best/simplest way to handle this before I head down the rabbit hole of trying everything that;s available.:p.

Thanks in advance for your ideas.
 
Last edited:
Top