In the last post we generated a view on all "contemporaries" of a specified person. The data were fetched from the database with help of a Pilog query.
Now we will see how we can write this output to a .txt or .PDF file.
We continue with our family-contemporaries.l
file from the last post.
Requirements
In order to be able to print the PDF output, we need some additional packages.
First of all, make sure that the @lib/svg.l
library and svg
namespace are declared at the top of the program:
(load "@lib/http.l" "@lib/xhtml.l" "@lib/form.l" "@lib/svg.l")
(symbols 'family 'svg 'pico)
Also, we need an external package to convert SVG files to PDF called librsvg2.bin ("librsvg" on termux for Android). On Debian systems you can install it with
sudo apt-get install librsvg2.bin
Creating a Textfile for Download with tmp
First of all, let's create a new file. For this purpose we can use the tmp
function:
(tmp ['any ..]) -> sym
Returns the path name to the
pack
edany
arguments in a process-local temporary directory. The directory name consists of the path to ".pil/tmp/" in the user's home directory, followed by the current process ID*Pid
. This directory is automatically created if necessary, and removed upon termination of the process (bye
).
Let's try in the REPL:
: (tmp "Testfile")
-> "/home/mia/.pil/tmp/7768/Testfile"
Let's name our file Contemporaries.txt
and bind its path to the local variable Txt
:
'(let Txt (tmp "Contemporaries.txt")
(out Txt (txt> (chart)))
Then we need to get all properties from our chart
as text. This can be done using the txt>
method of the PicoLisp GUI class. Then we pass txt>
to our curent chart using the function (chart)
which returns the current chart and write the output to Txt
.
'(let Txt (tmp "Contemporaries.txt")
(out Txt (txt> (chart)))
Creating the download button
Now let's wrap the above code in a button which triggers the download. When the button is pressed, the above code is executed and the user is redirected to the URL of the file. This automatically triggers the download dialog in the browser.
The final code for the button looks like this:
(gui '(+Rid +Button) "Textfile"
'(let Txt (tmp "Contemporaries.txt")
(out Txt (txt> (chart)))
(url Txt) ) )
Quite short, isn't it? If we add this to our family-contemporaries.l
file (still inside of the form
function), the result looks like this:
We get a textfile where each line corresponds to a row in the table. The columns are separated by tabs
.
Creating the PDF download
Now let's do the same with our PDF. However this time we the output formatted as table, like this:
The first step for the PDF are the same, except that we don't use a simple text file, but the pdf
function to create a PDF in landscape format:
(pdf *A4-DY *A4-DX (tmp "Contemporaries.pdf")
(out (tmp "Contemporaries.txt")
(txt> (chart)) )
For portrait format, we can write *A4-DX *A4-DY
.
Formatting the output
In the next step, let's read the file in again and add some formatting. First, we define the variables
Page
: 1,Fmt
: list of width attributes in "dots" (1/72 inch), andTtl
: the first line that is read in.
(in (tmp "Contemporaries.txt")
(let (Page 1 Fmt (200 120 50 50 120 120 120) Ttl (line T))
Then we style the format with help of the following functions:
font
: takes a number and a fontwidth
: stroke width in dotsdown
: margin to the top in dotsindent
: margin to the left in dots
First we set the general font style:
(font (7 . "Helvetica"))
(width "0.5")
Every new page has a offset to top by 12 and is indented by 40. If there is more than one page, every subsequent page carries the title and page number. After that comes the table head, followed by another space.
(while
(page
(down 12)
(indent 40
(ifn (=1 *Page)
(ps (pack Ttl ", Page " (inc 'Page)))
(font 9 (ps Ttl)) )
(down 12)
(table Fmt
"Name" "Occupation" "born" "died" "Father" "Mother" "Partner" )
(down 6)
Generating the table content
After the table title is created, we loop over the each line of the file and split the line at the tabs ("^I"
) and put them to a new list L
.
Also we need to set the exit condition for loop
: either when the file ends (eof
) or when there are less than 80 dots distance to the lower bottom of the PDF.
(loop
(let L (mapcar pack (split (line) "^I"))
...
(T (eof))
(T (>= *Pos (- *DY 80)) T) ) )
Within the loop, we can write each table cell using the ps
function:
(down 8)
(table Fmt
(font "Helvetica-Bold" (ps (car L)))
(ps (cadr L))
(ps (caddr L))
(ps (cadddr L))
(ps (get L 5))
(ps (get L 6))
(ps (get L 7)) )
(down 4) )
Defining the button
Now the last thing we need to do is defining the button and call the url
function, which we can simply wrap around the whole PDF generating code:
(gui '(+Rid +Button) "PDF"
'(url
(pdf *A4-DY *A4-DX (tmp "Contemporaries.pdf")
(out (tmp "Contemporaries.txt")
...
(in (tmp "Contemporaries.txt")
...
If we now restart our script, we get another button labelled "PDF" and can download the displayed content.
You can find the finished program here.