File: cpdfembed.ml

package info (click to toggle)
cpdf 2.8.1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 6,828 kB
  • sloc: ml: 34,724; makefile: 65; sh: 45
file content (130 lines) | stat: -rw-r--r-- 4,754 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
(* 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 U+%04x in make_fontpack_hashtable\n" u)
          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)