First typesetting result

This commit is contained in:
John Whitington 2021-11-18 14:48:25 -08:00
parent 3b54c94538
commit 87fb7e0dff
3 changed files with 118 additions and 15 deletions

View File

@ -1,5 +1,6 @@
2.5 (Upcoming 2022) 2.5 (Upcoming 2022)
o New operation -table-of-contents makes table of contents from bookmarks
o When adding text or graphics, may choose CYMK or Grey instead of RGB o When adding text or graphics, may choose CYMK or Grey instead of RGB
o Append e.g DUP2 to a page range to make 1,2,3 --> 1,1,2,2,3,3 etc. o Append e.g DUP2 to a page range to make 1,2,3 --> 1,1,2,2,3,3 etc.
o The -list-fonts operation now obeys the range o The -list-fonts operation now obeys the range

View File

@ -1,6 +1,7 @@
(* A typesetter for cpdf. A list of elements is manipulated zero or more times (* A typesetter for cpdf. A list of elements is manipulated zero or more times
to lay it out, paginate it, and so on. It is then typeset to produce a list to lay it out, paginate it, and so on. It is then typeset to produce a list
of pages *) of pages *)
open Pdfutil
(* Text is represented as a list of unicode code points *) (* Text is represented as a list of unicode code points *)
type text = int list type text = int list
@ -11,47 +12,148 @@ type glue =
stretch : float} stretch : float}
(* Main type *) (* Main type *)
type code = type element =
Text of text Text of text
| HGlue of glue | HGlue of glue
| VGlue of glue | VGlue of glue
| NewLine | NewLine
| NewPage | NewPage
| Font of Pdftext.font | Font of Pdftext.font * float
let string_of_element = function
| Text t -> Pdftext.utf8_of_codepoints t
| HGlue _ -> "HGLUE"
| VGlue _ -> "VGLUE"
| NewLine -> "NewLine"
| NewPage -> "NewPage"
| Font _ -> "Font"
let indent x = HGlue {glen = x; stretch = 0.} let indent x = HGlue {glen = x; stretch = 0.}
let newpara x = HGlue {glen = x; stretch = 0.} let newpara x = VGlue {glen = x; stretch = 0.}
type t = code list type t = element list
let of_utf8 = Pdftext.codepoints_of_utf8 let of_utf8 = Pdftext.codepoints_of_utf8
let example = let example =
[Text (of_utf8 "Jackdaws love my Sphinx of Quartz. And this, this is the second sentence to provoke a line-break."); [Font (Pdftext.StandardFont (Pdftext.TimesRoman, Pdftext.WinAnsiEncoding), 12.);
Text (of_utf8 "Jackdaws love my Sphinx of Quartz. And this, this is the second sentence to provoke a line-break.");
NewLine;
newpara 10.; newpara 10.;
indent 72.; indent 72.;
Text (of_utf8 "The second paragraph"); Text (of_utf8 "The second paragraph");
NewPage] NewPage]
(* Typesetter state. Origin at top left of page *)
type state = type state =
{mutable font : Pdftext.font; {mutable font : Pdftext.font option;
mutable xpos : float; mutable xpos : float;
mutable ypos : float} mutable ypos : float}
let initial_state () = let initial_state () =
{font = Pdftext.StandardFont (Pdftext.TimesRoman, Pdftext.WinAnsiEncoding); {font = None;
xpos = 0.; xpos = 0.;
ypos = 0.} ypos = 0.}
(* Split text into lines, resolve all hglue stretches to 0, remove Newlines. *) (* Split text into lines, resolve all hglue stretches to 0, remove Newlines. *)
let layout_element s xpos_max fo = function
| _ -> ()
let layout lmargin rmargin papersize i = let layout lmargin rmargin papersize i =
i i
(*let o = ref [] in
let s = initial_state () in
let xpos_max = Pdfpaper.width papersize -. lmargin in
s.xpos <- lmargin;
iter (layout_element s xpos_max (fun e -> o := e::!o)) i;
rev !o*)
(* Resolve all hglue stretches, insert NewPage as needed. *) (* Resolve all hglue stretches, insert NewPage as needed. *)
let paginate tmargin bmargin papersize i = i let paginate tmargin bmargin papersize i = i
(* Split on NewPages, typeset each page, add font dictionaries *) let make_resources fontobjnums =
let typeset papersize i = Pdf.Dictionary
[Pdfpage.blankpage papersize] [("/Font", Pdf.Dictionary (map (fun fo -> ("/F" ^ string_of_int fo, Pdf.Indirect fo)) fontobjnums))]
(* At this stage, just Font and Text and HGlue 0. and VGlue 0. and Newline and
NewPage elements. Split on NewPages, typeset each page, add font
dictionaries. New page only
creates a page when that page has content. *)
let typeset lmargin rmargin tmargin bmargin papersize pdf i =
let i = layout lmargin rmargin papersize i in
let i = paginate tmargin bmargin papersize i in
let height = Pdfpaper.height papersize -. tmargin in
let s = initial_state () in
s.xpos <- lmargin;
let ops = ref [] in
let fonts = ref [] in
let thispagefontnums = ref [] in
let pages = ref [] in
let write_page () =
if !ops <> [] then
let page =
{Pdfpage.content = [Pdfops.stream_of_ops (rev !ops)];
Pdfpage.mediabox = Pdfpage.rectangle_of_paper papersize;
Pdfpage.resources = make_resources !thispagefontnums;
Pdfpage.rotate = Pdfpage.Rotate0;
Pdfpage.rest = Pdf.Dictionary []}
in
Printf.printf "Writing a page\n";
pages := page :: !pages
in
let typeset_element = function
| Text cps ->
let charcodestring =
match s.font with
| None -> failwith "font not set up"
| Some f ->
match List.assoc_opt f !fonts with
| Some objnum ->
let extractor =
Pdftext.charcode_extractor_of_font pdf (Pdf.lookup_obj pdf objnum)
in
implode (map char_of_int (option_map extractor cps))
| None -> failwith "font not found"
in
ops :=
Pdfops.Op_ET
::Pdfops.Op_Tj charcodestring
::Pdfops.Op_BT
::Pdfops.Op_cm (Pdftransform.mktranslate s.xpos (height -. s.ypos))
::!ops
| Font (f, fontsize) ->
let name, objnum =
match List.assoc_opt f !fonts with
| Some objnum -> ("/F" ^ string_of_int objnum, objnum)
| None ->
let num = Pdftext.write_font pdf f in
let n = "/F" ^ string_of_int num in
fonts := (f, num) :: !fonts;
(n, num)
in
s.font <- Some f;
thispagefontnums := objnum :: !thispagefontnums;
ops := Pdfops.Op_Tf (name, fontsize)::!ops
| HGlue {glen} ->
s.xpos <- s.xpos +. glen
| VGlue {glen} ->
s.ypos <- s.ypos -. glen
| NewLine ->
s.xpos <- 0.
| NewPage ->
write_page ();
ops := [];
s.xpos <- 0.;
s.ypos <- 0.
in
iter typeset_element i;
write_page ();
rev !pages
let example_pdf () =
let pdf = Pdf.empty () in
let pages = typeset 50. 50. 50. 50. Pdfpaper.a4 pdf example in
let pdf, pageroot = Pdfpage.add_pagetree pages pdf in
Pdfpage.add_root pageroot [] pdf
let _ =
Pdfwrite.pdf_to_file (example_pdf ()) "out.pdf"

View File

@ -4,14 +4,14 @@ type glue =
{glen : float; {glen : float;
stretch : float} stretch : float}
type code = type element =
Text of text Text of text
| HGlue of glue | HGlue of glue
| VGlue of glue | VGlue of glue
| NewLine | NewLine
| NewPage | NewPage
| Font of Pdftext.font | Font of Pdftext.font * float
type t = code list type t = element list
val typeset : Pdfpaper.t -> t -> Pdfpage.t list val typeset : float -> float -> float -> float -> Pdfpaper.t -> Pdf.t -> t -> Pdfpage.t list