File: cpdfpng.ml

package info (click to toggle)
cpdf 2.9-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 7,140 kB
  • sloc: ml: 35,825; makefile: 66; sh: 49
file content (148 lines) | stat: -rw-r--r-- 4,924 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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
(* Read non-interlaced, non-transparent PNGs. Such a PNG may
   be loaded into a PDF simply by extracting its width and height from the
   IHDR, and concatenating all its IDAT data sections together, and specifying
   the appropriate Filter and Predictor.*)
open Pdfutil
open Pdfio

type t =
  {width : int;
   height : int;
   bitdepth : int;
   colortype : int;
   idat : bytes}

(* Writing *)
(*let tbl =
  ref ([||] : int32 array)

let mktbl () =
  let f n =
    let a = ref (i32ofi n) in
      for _ = 0 to 7 do
        a := lxor32 (lsr32 !a 1) (land32 0xEDB88320l (i32succ (lnot32 (land32 !a 1l))))
      done;
      !a
  in
    tbl := Array.init 256 f

let update crc buf len =
  let a = ref crc in
    for n = 0 to len - 1 do
      let e = i32ofi (int_of_char buf.[n]) in
        a := lxor32 !tbl.(i32toi (land32 (lxor32 !a e) 0xFFl)) (lsr32 !a 8)
    done;
    !a

let bytes_of_word x =
  i32toi (sr32 x 24),
  i32toi (land32 0x000000FFl (sr32 x 16)),
  i32toi (land32 0x000000FFl (sr32 x 8)),
  i32toi (land32 0x000000FFl x)

let output_bytes_of_word o w =
  let a, b, c, d = bytes_of_word w in
    o.output_byte a;
    o.output_byte b;
    o.output_byte c;
    o.output_byte d

let write_crc o ctype cdata =
  let crc = update 0xFFFFFFFFl ctype 4 in
  let crc = update crc cdata (String.length cdata) in
  let crc = lnot32 crc in
    output_bytes_of_word o crc

let write_chunk o ctype data =
  output_bytes_of_word o (i32ofi (Bytes.length data));
  for x = 0 to 3 do o.output_byte (int_of_char ctype.[x]) done;
  o.output_string (Bytes.unsafe_to_string data);
  write_crc o ctype (Bytes.unsafe_to_string data)

let write_word b x n =
  let p, q, r, s = bytes_of_word n in
    Bytes.set b x (char_of_int p);
    Bytes.set b (x + 1) (char_of_int q);
    Bytes.set b (x + 2) (char_of_int r);
    Bytes.set b (x + 3) (char_of_int s)

let write_png png o =
  if bytes_size png.idat > 1073741823 then raise (Invalid_argument "write_png: too large") else
  if Array.length !tbl = 0 then mktbl ();
  o.output_string "\137\080\078\071\013\010\026\010";
  let ihdr = Bytes.make 13 '\000' in
  write_word ihdr 0 (i32ofi png.width);
  write_word ihdr 4 (i32ofi png.height);
  Bytes.set ihdr 8 (char_of_int 8); (* bit depth *)
  Bytes.set ihdr 9 (char_of_int 2); (* colour type *)
  Bytes.set ihdr 10 (char_of_int 0); (* compression method *)
  Bytes.set ihdr 11 (char_of_int 0); (* filter method *)
  Bytes.set ihdr 12 (char_of_int 0); (* interlace method *)
  write_chunk o "IHDR" ihdr;
  write_chunk o "IDAT" (Bytes.unsafe_of_string (string_of_bytes png.idat));
  write_chunk o "IEND" (Bytes.create 0)*)

(* Reading *)
let string_of_tag t =
  Printf.sprintf "%c%c%c%c"
    (char_of_int (i32toi (sr32 t 24)))
    (char_of_int (i32toi (land32 0x000000FFl (sr32 t 16))))
    (char_of_int (i32toi (land32 0x000000FFl (sr32 t 8))))
    (char_of_int (i32toi (land32 0x000000FFl t)))

let read_unsigned_4byte i =
  let a = i32ofi (i.input_byte ()) in
  let b = i32ofi (i.input_byte ()) in
  let c = i32ofi (i.input_byte ()) in
  let d = i32ofi (i.input_byte ()) in
    lor32 (lor32 (lsl32 a 24) (lsl32 b 16)) (lor32 (lsl32 c 8) d)

let read_chunk i =
  let chunklen = i32toi (read_unsigned_4byte i) in
  let chunktype = read_unsigned_4byte i in
  let chunkdata = mkbytes chunklen in 
  setinit i chunkdata 0 chunklen;
  let _ (* crc *) = read_unsigned_4byte i in
    (string_of_tag chunktype, chunkdata) 

let concat_bytes ss =
  let total_length = sum (map bytes_size ss) in
    let s' = mkbytes total_length in
      let p = ref 0 in
        iter
          (fun s ->
             for x = 0 to bytes_size s - 1 do bset_unsafe s' !p (bget_unsafe s x); incr p done)
          ss;
        s'

let read_png i =
  try
    i.seek_in 8;
    let ihdr, ihdrdata = read_chunk i in
    if ihdr <> "IHDR" then raise (Pdf.PDFError "read_png: first table not IHDR") else
    let hdr = input_of_bytes ihdrdata in
    let width = read_unsigned_4byte hdr in
    let height = read_unsigned_4byte hdr in
    let bitdepth = hdr.input_byte () in
    let colortype = hdr.input_byte () in
    if colortype <> 2 && colortype <> 0 && colortype <> 6 && colortype <> 4 then raise (Pdf.PDFError "read_png: only non-palette PNGs") else
    let _ (*compressionmethod*) = hdr.input_byte () in
    let _ (*filtermethod*) = hdr.input_byte () in
    let interlacemethod = hdr.input_byte () in
    if interlacemethod <> 0 then raise (Pdf.PDFError "read_png: interlaced PDFs not supported") else
    let idat = ref [] in
      begin try
        while true do
          let chunkname, chunkdata = read_chunk i in
            if chunkname = "IDAT" then idat := chunkdata::!idat
        done
      with
        _ -> ()
      end;
      {width = i32toi width;
       height = i32toi height;
       colortype;
       bitdepth;
       idat = concat_bytes (rev !idat)}
  with
    e -> raise (Pdf.PDFError (Printf.sprintf "read_png: failed on %s" (Printexc.to_string e)))