A solution to printing in REBOL
[1/13] from: g::santilli::tiscalinet::it at: 16-Jun-2001 19:59
Hi all!
I see there has been some discussion on the list lately about the
need of a (possibly multiplatform) solution to the problem of
printing. One of the possible solution, useful when one has a
relatively simple text document and does not need precise
positioning of text in the page, is using HTML files.
But if I need to print a report on a given template with
precise text positioning? A better control on the page layout and
size? In this case, a useful solution would be to create a PDF
file. I see this solution has been proposed on the list, but I
didn't see (maybe I missed them?) any taker wrt writing a dialect
to generate PDF files.
So I decided to write it myself. I downloaded the specification
from www.adobe.com yesterday afternoon, and I was able to write a
PDF generator in REBOL in about a day of work.
WARNING! This is ALPHA unfinished buggy code! WARNING!
I thought that this script might be useful. Creating a PDF file is
as easy as writing:
write/binary %pdf-file.pdf layout-pdf [
[ textbox [
"Hello world! This is a simple PDF file."
]
]
]
Please run the provided pdf-maker-doc.r script to create the
documentation, in PDF format of course. :-)
I'm looking forward to get comments, and possibly help.
Have fun!
Gabriele.
--
Gabriele Santilli <[giesse--writeme--com]> - Amigan - REBOL programmer
Amiga Group Italia sez. L'Aquila -- http://www.amyresource.it/AGI/
-- Attached file included as plaintext by Listar --
-- File: pdf-maker-doc.r
REBOL []
do %pdf-maker.r
write/binary %pdf-maker-doc.pdf layout-pdf [
[ ; page 1
textbox [
font Helvetica-Bold 36 offset 50 0 "PDF-Maker!"
font Helvetica 12 offset 78 0 "(first alpha release)"
offset -123 -20
{Welcome to pdf-maker.r preliminary documentation. This is my first attempt at a PDF
generator as}
newline
offset -5 0
{a multiplatform solution to printing in }
font Helvetica-Bold 12 {REBOL} font Helvetica 12 {. As you can see, it is pretty limited
currently. Anyway, it
should work, and can surely be improved in the future. Current limitations are:} newline
offset 20 -5
"^(97) Text only, no graphics at the moment." newline
"^(97) No colors, etc. You have to cope with just black text." newline
"^(97) Only the 14 PostScript standard fonts are supported." newline
"^(97) No justification, right alignement, etc." newline
offset -20 -5
{Graphics would not be very difficult to add, so would colors. Custom fonts would definitely
require
work, and justification and right alignement would require A LOT of work, because you'd
have to
process font metrics and so on. These two are not likely to be added by me soon.} newline
offset 0 -5
{Anyway, let's go on explaining how to create a PDF file. You'll need just one function,
}
font Courier 12 "layout-pdf" font Helvetica 12 {,
which in turn just needs an argument of type } font Courier 12 "block!" font Helvetica
12 {:} newline
offset 20 -5
font Courier 12
{layout-pdf [
; page definitions here
]} newline
offset -20 -5
font Helvetica 12
{This block contains a block for each page in the document, so that a document with two
pages will
look like:} newline
offset 20 -5
font Courier 12
{layout-pdf [
[ ; first page
; ...
] [ ; second page
; ...
]
]} newline
offset -20 -5
font Helvetica 12
{Each page definition might include and optional page size and rotation specification;
the page size
is expressed in millimeters (as any other position or size in the dialect). For example,
to create a page
150 millimeters wide and 140 high, you can write:} newline
offset 20 -5
font Courier 12
{[ page size 150 140
; ...
]} newline
offset -20 -5
font Helvetica 12
{while to create a page of default size (ISO A4, 211x297 millimeters) but rotated 270
degrees
clockwise:} newline
offset 20 -5
font Courier 12
{[ page rotation 270 ; has to be multiple of 90
; ...
]}
]
] [ ; page 2
textbox [
{Of course the two can be specified together, in any order:} newline
offset 20 -5
font Courier 12
{[ page rotation 90 size 180.5 280
; ...
]} newline
offset -20 -5
font Helvetica 12
{A page can contain any number of } font Times-Italic 12 "text boxes"
font Helvetica 12 {; a text box is used to render text inside a rectangular
area of the page with proper clipping. A text box can be created with:} newline
offset 20 -5
font Courier 12
{[ textbox 10 17 191 263 [
; text...
]
]} newline
offset -20 -5
font Helvetica 12
{The numbers after } font Courier 12 "textbox" font Helvetica 12 { are x position, y
position, width and height (the numbers shown above
are a reasonable default for A4 pages used if you don't specify any). The position is
referred to
the LOWER LEFT corner of the box; also notice that the y axis is swapped if you compare
it to
REBOL/View's faces y axis.} newline
offset 0 -5
{A simple text box can just contain a string of text. The lines of text will simply be
rendered left
justified inside the text box with the default font (Helvetica 12pt). However, it can
also contain some
commands to specify the font to use, where to position the text, and so on. The currently
available
commands are:} newline
offset 20 -5
font Courier 10
{font <font-name> <font-size> ; font-size is in points
offset <x-offset> <y-offset> ; in millimeters, relative to start of line
newline ; just begins a new line
line height <line-height> ; sets the line height} newline
offset -20 -5
font Helvetica 12
{To see an example of these commands, look at the REBOL script that created this PDF
file. Notice
that the font name should be one of:} newline
offset 20 -5
font Courier 10
{Helvetica Courier Times-Roman Symbol
Helvetica-Bold Courier-Bold Times-Bold ZapfDingbats
Helvetica-Oblique Courier-Oblique Times-Italic
Helvetica-BoldOblique Courier-BoldOblique Times-BoldItalic} newline
offset -20 -5
font Helvetica 12
"Enjoy!" newline
offset 10 0 font Times-Italic 12 "Gabriele."
]
]
]
-- Attached file included as plaintext by Listar --
-- File: pdf-maker.r
REBOL []
context [
pdf-start: "%PDF-1.3^/"
pdf-end: "%%EOF"
pdf-string-valid: complement charset "()\"
pdf-form: func ["REBOL to PDF" value /only /local result mrk1 mrk2] [
result: make string! 256
if block? :value [
if only [insert result "["]
foreach element value [
insert insert tail result pdf-form/only element #" "
]
either only [change back tail result "]"] [remove back tail result]
return result
]
if char? :value [
return head insert result reduce [
#"("
either find pdf-string-valid value [""] [#"\"] value
#")"
]
]
if string? :value [
insert result "("
parse/all value [
some [
mrk1: some pdf-string-valid mrk2: (
insert/part tail result mrk1 mrk2
)
| mrk1: skip (
insert insert tail result #"\" mrk1/1
)
]
]
insert tail result ")"
return result
]
if issue? :value [return form value]
mold :value
]
xref: []
contents: #{}
pdf-words: context [
obj: func [id data] [
insert tail xref reduce [id index? tail contents]
insert tail contents reduce [
id " 0 obj^/" pdf-form data "^/endobj^/"
]
]
stream: func [id data] [
insert tail xref reduce [id index? tail contents]
if block? data [data: pdf-form data]
insert tail contents reduce [
id " 0 obj^/"
pdf-form compose [
#<< /Length (length? data) #>>
]
"^/stream^/"
data
"^/endstream^/endobj^/"
]
]
]
zero-padded: func [val n] [
val: form val
head insert insert/dup make string! n #"0" n - length? val val
]
make-xref: has [pos] [
pos: tail contents
insert pos "xref^/0 1^/0000000000 65535 f ^/"
foreach [id ofs] xref [
insert tail pos reduce [
id " 1^/"
zero-padded ofs 10 " 00000 n ^/"
]
]
insert tail pos reduce [
newline
"trailer^/"
pdf-form compose [
#<< /Size (1 + divide length? xref 2)
/Root 1 0 R ; this assumes root will always be 1
#>>
]
"^/startxref^/"
index? pos newline
]
]
set 'make-pdf func [spec [block!]] [
clear xref
clear contents
insert contents pdf-start
do bind spec in pdf-words 'self
make-xref
copy head insert tail contents pdf-end
]
pages: []
used-fonts: []
font-resources: []
pdf-spec: []
default-page: context [
size: [211 297] ; mm. (ISO A4)
rotation: 0
contents: []
]
default-textbox: context [
bbox: [10 17 191 263]
text: []
]
make-docroot: does [
insert tail pdf-spec [
obj 1 [
#<< /Type /Catalog
/Outlines 2 0 R
/Pages 100 0 R
#>>
]
obj 2 [
#<< /Type /Outlines
/Count 0
#>>
]
obj 3 [ ; ProcSet to use in pages
[/PDF /Text]
]
]
]
new: val1: val2: bb: txtb: none
txtb-emit: func [data] [
insert tail txtb/text reduce data
]
use-font: func [name size] [
used-fonts: union used-fonts reduce [name]
txtb-emit [to-refinement name size 'Tf size / 10 + size 'TL]
]
font-def: ['font set val1 word! set val2 number! (use-font val1 val2)]
offset-text: ['offset set val1 number! set val2 number! (txtb-emit [mm2pt val1 mm2pt
val2 'Td])]
set-lead: ['line 'height set val1 number! (txtb-emit [val1 'TL])]
process-text: func [text] [
text: parse/all text "^/"
if pick text 1 [txtb-emit [pick text 1 'Tj]]
foreach line next text [
txtb-emit ['T* line 'Tj]
]
]
draw-text: [set val1 string! (process-text val1)]
textbox-rule: [
[font-def | none (use-font 'Helvetica 12)]
(txtb-emit [mm2pt txtb/bbox/1 mm2pt txtb/bbox/2 + txtb/bbox/4 'Td 'T*])
some [
font-def
| offset-text
| 'newline (insert tail txtb/text 'T*)
| set-lead
| draw-text
]
]
page-rule: [
(insert tail pages new: make default-page [])
opt ['page any [
'size set val1 number! set val2 number! (new/size: reduce [val1 val2])
| 'rotation set val1 integer! (new/rotation: val1)
]]
any [
'textbox (insert tail new/contents txtb: make default-textbox [])
opt [(bb: clear []) 4 [set val1 number! (append bb val1)] (change txtb/bbox bb)]
into textbox-rule
]
]
parse-spec: func [spec] [
parse spec [some [into page-rule]]
]
make-fonts: has [i] [
i: 4
clear font-resources
foreach font used-fonts [
insert tail font-resources reduce [to-refinement font i 0 'R]
insert tail pdf-spec compose/deep [
obj (i) [
#<< /Type /Font
/Subtype /Type1
/BaseFont (to-refinement font)
/Encoding /WinAnsiEncoding
#>>
]
]
i: i + 1
]
]
mm2pt: func [mm] [mm * 72 / 25.4]
make-pages: has [i kids mediabox stream] [
i: 101
kids: clear []
foreach page pages [
insert tail kids reduce [i 0 'R]
mediabox: reduce [0 0 mm2pt page/size/1 mm2pt page/size/2]
stream: clear []
foreach textbox page/contents [
insert tail stream compose [
q
(mm2pt textbox/bbox/1) (mm2pt textbox/bbox/2)
(mm2pt textbox/bbox/3) (mm2pt textbox/bbox/4) re W n
BT (textbox/text) ET
Q
]
]
insert tail pdf-spec compose/deep [
obj (i) [
#<< /Type /Page
/Parent 100 0 R
/MediaBox [(mediabox)]
/Rotate (page/rotation)
/Contents (i + 1) 0 R
/Resources #<<
/ProcSet 3 0 R
/Font #<< (font-resources) #>>
#>>
#>>
]
stream (i + 1) [
(stream)
]
]
i: i + 2
]
insert tail pdf-spec compose/deep [
obj 100 [
#<< /Type /Pages
/Kids [(kids)]
/Count (length? pages)
#>>
]
]
]
set 'layout-pdf func [spec [block!]] [
clear pages
clear used-fonts
make-docroot
parse-spec spec
make-fonts
make-pages
make-pdf pdf-spec
]
]
[2/13] from: robert:muench:robertmuench at: 21-Jun-2001 14:48
> -----Original Message-----
> From: [rebol-bounce--rebol--com] [mailto:[rebol-bounce--rebol--com]]On Behalf Of
<<quoted lines omitted: 6>>
> PDF generator in REBOL in about a day of work.
> WARNING! This is ALPHA unfinished buggy code! WARNING!
Hi, I tried it but it crashes on my system with a "should not happen" message.
Is this only my problem? Anyway, I think this is a nice start and maybe we can
expand "makedoc" with a PDF emitter. Robert
[3/13] from: al:bri:xtra at: 23-Jun-2001 10:14
I, too, would be highly interested.
Andrew Martin
ICQ: 26227169 http://zen.scripterz.org
[4/13] from: g:santilli:tiscalinet:it at: 22-Jun-2001 23:56
Hello Ladislav!
On 22-Giu-01, you wrote:
LM> Hi Gabriele,
LM> I, for one, would like to have one.
Maybe you missed the message from the list? I'm resending
pdf-maker.r to you directly.
Regards,
Gabriele.
--
Gabriele Santilli <[giesse--writeme--com]> - Amigan - REBOL programmer
Amiga Group Italia sez. L'Aquila -- http://www.amyresource.it/AGI/
[5/13] from: louisaturk:eudoramail at: 22-Jun-2001 18:17
Hi Gabriele,
Would you please send it to me also please?
Thanks,
Louis
At 11:56 PM 6/22/2001 +0200, you wrote:
[6/13] from: depotcity:telus at: 22-Jun-2001 15:46
here's a link to the old message
http://www.escribe.com/internet/rebol/m10731.html
[7/13] from: larry:ecotope at: 22-Jun-2001 15:45
Hi Gabriele,
Sorry for my belated reply. I downloaded your pdf-maker scripts the day you
posted them. With Core 2.5 on Win98, they worked flawlessly to produce the
documentation in PDF form. It displayed without problems in Acrobat reader
v4 both directly and through the browser. I think you might have gotten a
larger response on the mail-list if PDF had appeared in the subject line.
The pdf-maker script is a very nice piece of work that well illustrates the
power of REBOL. Hats off to you.
Would it be possible for you to post the scripts to a website, the View lib,
or someones' rebsite (I volunteer mine if you are interested). It would be
better to have them as two separate scripts. That might have thrown a few
folks trying to DO the email.
Please don't abandon work on the pdf-maker, at least until you have added
images and colors ;-)
-Larry
[8/13] from: lmecir:mbox:vol:cz at: 22-Jun-2001 21:44
Hi Gabriele,
I, for one, would like to have one.
L
...snip...
[9/13] from: giesse:writeme at: 22-Jun-2001 14:22
Robert M. Muench
wrote:
> Hi, I tried it but it crashes on my system with a "should not happen" message.
Maybe it's an email formatting problem?
> Is this only my problem? Anyway, I think this is a nice start and maybe we can
> expand "makedoc" with a PDF emitter. Robert
I'll surely do that as soon as I have some free time. Anyway, it
seems like there is not much interest in a PDF generator...
Regards,
Gabriele.
--
Gabriele Santilli <[giesse--writeme--com]> - Amigan - REBOL programmer
Amiga Group Italia sez. L'Aquila -- http://www.amyresource.it/AGI/
[10/13] from: m:converso:bitinia at: 23-Jun-2001 8:07
Hi Gabriele
me too: as you know very well
best
Maurizio
Ladislav Mecir wrote:
[11/13] from: ljurado:bariloche:ar at: 23-Jun-2001 13:04
Gabriele :
do %pdf-maker.r
write/binary %example.pdf layout-pdf [ [textbox ["Hello World !"] ] ]
This is working OK, but, how can get the message in an 100 x 100 page ?
Regards,
Luis
[12/13] from: g:santilli:tiscalinet:it at: 24-Jun-2001 12:26
Hello Luis!
On 23-Giu-01, you wrote:
LD> do %pdf-maker.r
LD> write/binary %example.pdf layout-pdf [ [textbox ["Hello World
LD> !"] ] ]
LD> This is working OK, but, how can get the message in an 100 x
LD> 100 page ?
For a 100x100 millimeters page:
layout-pdf [
[ page size 100 100
textbox 10 10 80 80 ["Hello World!"]
]
]
You can read the documentation for the details.
Regards,
Gabriele.
--
Gabriele Santilli <[giesse--writeme--com]> - Amigan - REBOL programmer
Amiga Group Italia sez. L'Aquila -- http://www.amyresource.it/AGI/
[13/13] from: robert:muench:robertmuench at: 24-Jun-2001 17:42
> -----Original Message-----
> From: [giesse--sunti3--ing--univaq--it] [mailto:[giesse--sunti3--ing--univaq--it]]On
<<quoted lines omitted: 3>>
> Subject: [REBOL] Re: A solution to printing in REBOL
> Maybe it's an email formatting problem?
Hi, hmm... you sent it in plain text and I just copied it into a text-editor.
Anyway, I check it out.
> I'll surely do that as soon as I have some free time. Anyway, it
> seems like there is not much interest in a PDF generator...
Well, there now has been generated some interest :-)). A simple makedoc emitter
would be a nice place to start, there are a lot of input files available to test
it. Robert
Notes
- Quoted lines have been omitted from some messages.
View the message alone to see the lines that have been omitted