Android Question [SOLVED] How to create a pdf table using the Printer library?

noeleon

Active Member
Licensed User
Hello.
How can I create a table in a pdf using the Printer library?
A simple drawText won't do because the "cells" may contain long text and must not overlap each other.
I had a project years ago where I drew each cell to a label and used its snapshot, which is a bitmap, to assemble a table.
I really need the texts to be selectable.
Thanks.
 

Attachments

  • pdftable.zip
    11.1 KB · Views: 77
Last edited:
Solution
I think I finally got it. Just need to adjust the text sizes to fit long text
Screenshot_20240128-090717.jpg

noeleon

Active Member
Licensed User
I attached a sample project.
The label is drawn to the pdf and is selectable but I don't know how to set its coordinates, it is at the top left corner of the page
Screenshot_20240127-080931.jpg
 
Last edited:
Upvote 0

noeleon

Active Member
Licensed User
Did/would you consider using PDF Generator - B4X Cross Platform - Class 100% B4X Code ?

The documentation indicates it has methods that:
  • draw text : single line of text at position x, y
  • Measure text (width and height)
  • draw line
  • draw rectangle
which sound like all the essentials needed to create your PDF table.
I tried that but I find it very confusing. The y coordinate starts at the bottom. For example, the coordinates 20,30 is 20 from left and 30 from the bottom. So when I write text like a number per line in a loop it would appear on the page as
3
2
1
But when I copy the text and paste it somewhere it is
1
2
3
 
Upvote 0

emexes

Expert
Licensed User
The y coordinate starts at the bottom.

Yeah, I agree that doesn't help. I wrote a function to flip Y coordinates so that they are from the top:

B4X:
Sub FlipY(Y As Float) As Float
    Return 297 - Y
End Sub

and then this routine to create a PDF from a supplied 2D String Array:

B4X:
Private Sub PrintTable(NumRows As Int, NumCols As Int, Tbl(,) As String, X As Int, Y As Int, W As Int, H As Int)
    Dim pdf As cPDF
    pdf.Initialize("mm")
    
    pdf.pageAdd(pdf.pageSizeA4PortraitWidth, pdf.pageSizeA4PortraitHeight)

    pdf.sFont( _
        pdf.fontHelvetica, _
        pdf.fontBold, _
        12, _
        pdf.colorBlack _
    )
    
    Dim RowY(NumRows + 1) As Float
    For R = 0 To NumRows
        RowY(R) = Y + R / NumRows * H
        Log(R & TAB & RowY(R))
    Next
    
    Dim ColX(NumCols + 1) As Float
    For C = 0 To NumCols
        ColX(C) = X + C / NumCols * W
        Log(C & TAB & ColX(C))
    Next
    
    ColX(1) = ColX(1) - 10    'demo individual column widths

    'draw table grid
    For R = 0 To NumRows
        pdf.outLine(ColX(0), FlipY(RowY(R)), ColX(NumCols), FlipY(RowY(R)))
    Next
    For C = 0 To NumCols
        pdf.outLine(ColX(C), FlipY(RowY(0)), ColX(C), FlipY(RowY(NumRows)))
    Next

    'draw table contents
    For R = 0 To NumRows - 1
        For C = 0 To NumCols - 1
            pdf.outtext(ColX(C) + 2.0, FlipY(RowY(R) + 5.0), Tbl(R, C))
        Next
    Next
    
    'save to file with compression if data compressed are smaller
    savePDF(pdf,"test.pdf",pdf.CompressAlways)
    
    'open with default viewer
    openPDF("test.pdf")
End Sub

which, when tested with:

B4X:
Dim NumRows As Int = 38
Dim NumCols As Int = 3

Dim Tbl(NumRows, NumCols) As String

For R = 0 To NumRows - 1
    For C = 0 To NumCols - 1
        Tbl(R, C) = "row " & (R + 1) & " col " & (C + 1)
        If C = 1 Then
            Tbl(R, C) = Tbl(R, C) & " " & "RandomWordString".SubString(Rnd(0, 17))
        End If
    Next
Next

PrintTable(NumRows, NumCols, Tbl, 20, 20, 180, 260)

produced the attached PDF file:

1706366445165.png
 

Attachments

  • test.pdf
    2 KB · Views: 95
Upvote 0

noeleon

Active Member
Licensed User
I wrote a function to flip Y coordinates so that they are from the top:
Thank you @emexes for trying to help but for now I will have to stick to the Printing library because I also want to use a custom font.

I wonder if that's possible with the outRAW function. The documentation says it can do a lot more including creating a proper table. I hope spsp can bring more features to his library.
 
Upvote 0

emexes

Expert
Licensed User
How can I create a table in a pdf using the Printer library?

Just to clarify:

the Printing library has two ways of generating output:

PdfDocument
Printer

Which do you wish to use?

PdfDocument's line and text drawing methods look like they'll do the job:

B4X:
dim pdf as PdfDocument
pdf.Initialize
pdf.StartPage(595, 842) 'A4 size
pdf.Canvas.DrawLine(2, 2, 593 , 840, Colors.Blue, 4)
pdf.Canvas.DrawText("Hello", 100, 100, Typeface.DEFAULT_BOLD, 30, Colors.Yellow, "CENTER")
pdf.FinishPage
 
Upvote 0

noeleon

Active Member
Licensed User
I think I finally got it. Just need to adjust the text sizes to fit long text
Screenshot_20240128-090717.jpg
 

Attachments

  • pdftable v2.zip
    11 KB · Views: 71
Upvote 0
Solution

emexes

Expert
Licensed User
Just need to adjust the text sizes to fit long text

My only suggestion of consequence so far is to close the rectangle:

B4X:
Sub drawRectangle(left As Int, top As Int, width As Int, height As Int)
    Path.Initialize(left, top)
    Path.LineTo(left, top+height)
    Path.LineTo(left+width, top+height)
    Path.LineTo(left+width, top)
    Path.LineTo(left, top)
    CV.DrawPath(Path, Colors.Black, False, 0.2)
End Sub

unless... is there a reason for having no border at the top of the table?
 
Upvote 0

emexes

Expert
Licensed User
Maybe do the cell drawing inside a loop rather than repeat it three times:

B4X:
Sub createCellsRow(Texts() As String, Left As Double, Top As Double, Widths() As Double, Height As Double)
    Dim FirstCol As Int = 0    'should really be parameters
    Dim LastCol As Int = 2
    
    For Col = FirstCol To LastCol
        drawRectangle(Left, Top, Widths(Col), Height)
        CV.ClipPath(Path)
        CV.DrawText(Texts(Col), Left+1, Top + Height - 4, Typeface.DEFAULT, 5, Colors.Black, "LEFT")
        CV.RemoveClip
        Left = Left + Widths(Col)    'final column easier to do than to skip (might even be useful in future)
    Next
End Sub
 
Upvote 0

emexes

Expert
Licensed User
Is your table wider than one page, as in split across pages horizontally?

Split across pages vertically isn't so bad, but split both vertically and horizontally, that's a more interesting challenge. ?
 
Upvote 0

noeleon

Active Member
Licensed User
Is your table wider than one page, as in split across pages horizontally?

Split across pages vertically isn't so bad, but split both vertically and horizontally, that's a more interesting challenge. ?
No. My table has about 20k rows. The cells are a little wider than my example but it would continue to the middle of the page then to the right side, and then to the next page. It would take 100 pages more or less.
 
Upvote 0

emexes

Expert
Licensed User
My table has about 20k rows.

That's a lot of rows. :oops:

It would take 100 pages more or less.

20000 rows / (11 inches x 8 lines/inch) = 228 pages

without taking into account margins and headings

good thing it's a pdf and not on paper ?

although... if it's not on paper, perhaps HTML might be more useable, eg can import directly into a spreadsheet no problem

or is there some auditability or archivability requirement?



ps a local computer supplier here used to print pricelists like this:


which were bordering on unusable even before my eyesight dropped to 6/6.
 
Upvote 0
Top