REBOL [ Title: "PDF Maker alpha version" Date: 23-Jun-2001/18:08:54+2:00 Version: 0.0.0 File: %pdf-maker.r Author: "Gabriele Santilli" Purpose: "Dialect to create Adobe Acrobat (PDF) files." Email: g.santilli@tiscalinet.it Web: http://web.tiscalinet.it/rebol/pdf-maker.r Category: [text util 4 lib] ] 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)] c2d: func [val] [divide any [val 0] 255] set-color: [set val1 tuple! (txtb-emit [c2d val1/1 c2d val1/2 c2d val1/3 'rg])] 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 | set-color ] ] 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 ] ]