(* Embed a font *) open Pdfutil type t = Pdftext.font list * (int, int * int) Hashtbl.t (* Table returns font number and charcode for given unicode codepoint *) type cpdffont = PreMadeFontPack of t | EmbedInfo of {fontfile : Pdfio.bytes; fontname : string; encoding : Pdftext.encoding} | ExistingNamedFont let fontpack_of_standardfont sf = let te = Pdftext.text_extractor_of_font_real sf in let table = null_hash () in for x = 0 to 255 do let u = hd (Pdftext.codepoints_of_text te (string_of_char (char_of_int x))) in Hashtbl.add table u (0, x) done; ([sf], table) let get_char (fonts, table) u = match Hashtbl.find table u with | (n, charcode) -> Some (charcode, n, List.nth fonts n) | exception Not_found -> None let fontnum = ref 0 let basename () = incr fontnum; "AAAAA" ^ string_of_char (char_of_int (!fontnum + 65)) let make_single_font ~fontname ~encoding pdf f = let name_1 = basename () in let module TT = Cpdftruetype in let fontfile = let len = Pdfio.bytes_size f.TT.subset_fontfile in Pdf.Stream {contents = (Pdf.Dictionary [("/Length", Pdf.Integer len); ("/Length1", Pdf.Integer len)], Pdf.Got f.TT.subset_fontfile)} in let fontfile_num = Pdf.addobj pdf fontfile in let open Pdftext in let fontmetrics = let a = Array.make 256 0. in for x = f.TT.firstchar to f.TT.lastchar do a.(x) <- float_of_int (f.TT.widths.(x - f.TT.firstchar)) done; a in (f.TT.subset, SimpleFont {fonttype = Truetype; basefont = Printf.sprintf "/%s+%s" name_1 fontname; fontmetrics = Some fontmetrics; firstchar = f.TT.firstchar; lastchar = f.TT.lastchar; widths = f.TT.widths; fontdescriptor = Some {ascent = float_of_int f.TT.ascent; descent = float_of_int f.TT.descent; avgwidth = float_of_int f.TT.avgwidth; maxwidth = float_of_int f.TT.maxwidth; flags = f.TT.flags; italicangle = float_of_int f.TT.italicangle; capheight = float_of_int f.TT.capheight; xheight = float_of_int f.TT.xheight; stemv = float_of_int f.TT.stemv; fontbbox = (float_of_int f.TT.minx, float_of_int f.TT.miny, float_of_int f.TT.maxx, float_of_int f.TT.maxy); fontfile = Some (FontFile2 fontfile_num); charset = None; tounicode = f.TT.tounicode}; encoding}) let make_fontpack_hashtable fs = let indexes = indx0 fs in let table = null_hash () in iter2 (fun i (subset, f) -> let charcode_extractor = Pdftext.charcode_extractor_of_font_real f in iter (fun u -> match charcode_extractor u with | Some x -> Hashtbl.add table u (i, x) | None -> Printf.printf "charcode_extractor could not find char in make_fontpack_hashtable\n") subset) indexes fs; table let embed_truetype pdf ~fontfile ~fontname ~codepoints ~encoding = if codepoints = [] then ([], null_hash ()) else (* Can't call Cpdftruetype.parse with empty codepoint set. *) let fs = Cpdftruetype.parse ~subset:codepoints fontfile encoding in let subsets_and_their_fonts = map (make_single_font ~fontname ~encoding pdf) fs in (map snd subsets_and_their_fonts, make_fontpack_hashtable subsets_and_their_fonts) let rec collate_runs cfn a = function | [] -> rev (map rev a) | (charcode, fontnum, font) as h::t -> match a with | [] -> collate_runs fontnum [[h]] t | this::rest -> if fontnum = cfn then collate_runs cfn ((h::this)::rest) t else collate_runs fontnum ([h]::this::rest) t let collate_runs = function | [] -> [] | (_, fontnum, _)::_ as l -> collate_runs fontnum [] l let fontnames = [(Pdftext.TimesRoman, ["NimbusRoman-Regular.ttf"]); (Pdftext.TimesBold, ["NimbusRoman-Bold.ttf"]); (Pdftext.TimesItalic, ["NimbusRoman-Italic.ttf"]); (Pdftext.TimesBoldItalic, ["NimbusRoman-BoldItalic.ttf"]); (Pdftext.Helvetica, ["NimbusSans-Regular.ttf"]); (Pdftext.HelveticaBold, ["NimbusSans-Bold.ttf"]); (Pdftext.HelveticaOblique, ["NimbusSans-Italic.ttf"]); (Pdftext.HelveticaBoldOblique, ["NimbusSans-BoldItalic.ttf"]); (Pdftext.Courier, ["NimbusMonoPS-Regular.ttf"]); (Pdftext.CourierBold, ["NimbusMonoPS-Bold.ttf"]); (Pdftext.CourierOblique, ["NimbusMonoPS-Italic.ttf"]); (Pdftext.CourierBoldOblique, ["NimbusMonoPS-BoldItalic.ttf"]); (Pdftext.Symbol, ["StandardSymbolsPS.ttf"]); (Pdftext.ZapfDingbats, ["D050000L.ttf"])] let load_substitute dirname f = let filename = hd (List.assoc f fontnames) in (Pdfio.bytes_of_string (contents_of_file (Filename.concat dirname filename)), Filename.remove_extension filename)