(***********************************************************************) (* *) (* Active-DVI *) (* *) (* Projet Cristal, INRIA Rocquencourt *) (* *) (* Copyright 2002 Institut National de Recherche en Informatique et *) (* en Automatique. All rights reserved. This file is distributed *) (* under the terms of the GNU Lesser General Public License. *) (* *) (* Jun Furuse, Didier Rémy and Pierre Weis. *) (* Contributions by Roberto Di Cosmo, Didier Le Botlan, *) (* Xavier Leroy, and Alan Schmitt. *) (* *) (* Based on Mldvi by Alexandre Miquel. *) (***********************************************************************) (* $Id: driver.ml,v 1.6 2011-08-04 13:25:59 remy Exp $ *) open Misc;; let active = Options.flag true "-passive" " cancel all Active-DVI effects,\ \n\t (the default is to play all effects).";; let toggle_active () = active := not !active;; let with_active b f x = let restore_delays () = if !active then Transimpl.sleep := (fun _ -> false) in let a = !active in try let v = active := b; f x in active := a; restore_delays (); v with | exc -> active := a; restore_delays (); raise exc;; (* Number of steps before checking for user interruptions *) let checkpoint_frequency = 10;; (*** Some utilities for specials ***) let split_string s start = Misc.split_string s (function ' ' -> true | _ -> false) start;; (* "hello world" is one word *) let rec split_string_quoted s start = let len = String.length s and i = ref start in (* find a space *) while !i < len && s.[!i] = ' ' do incr i done; if !i >= len then [] else begin let i0 = !i in while !i < len && s.[!i] <> ' ' do if s.[!i] = '"' (* '"' *) then begin incr i; while !i < len && s.[!i] <> '"' do incr i done; if !i >= len || s.[!i] <> '"' then failwith ("parse error (split_string_quoted): " ^ s); incr i end else incr i done; let i1 = !i in String.sub s i0 (i1 - i0) :: split_string_quoted s i1 end;; (* "\"hello world\"" -> "hello world" *) let unquote s = let len = String.length s in if len = 0 then s else let b = if s.[0] = '"' then 1 else 0 in if len - b = 0 then "" else let e = if s.[len - 1] = '"' then 1 else 0 in let len = len - b - e in if len = 0 then "" else String.sub s b len;; let split_record s = let tokens = split_string_quoted s 0 in List.map (fun token -> try let i = String.index token '=' in String.sub token 0 i, String.sub token (i + 1) (String.length token - i - 1) with | Not_found -> token, "") tokens;; module Dev = Grdev;; module Symbol = Dev.Symbol;; module DFont = Devfont.Make(Dev);; let base_dpi = 600;; (*** Cooked fonts ***) exception Pause;; exception Wait of float;; type cooked_font = { name : string; ratio : float; mtable : (int * int) Table.t; mutable gtables : (int * Dev.glyph Table.t) list };; let dummy_mtable = Table.make (fun _ -> raise Not_found);; let dummy_gtable = Table.make (fun _ -> raise Not_found);; let dummy_font = { name = "--nofont--"; ratio = 1.0; mtable = dummy_mtable; gtables = [] };; let cook_font fdef dvi_res = let name = fdef.Dvicommands.name and sf = fdef.Dvicommands.scale_factor and ds = fdef.Dvicommands.design_size in let ratio = float sf /. float ds in let mtable = try DFont.find_metrics name (dvi_res *. ratio) with Not_found -> dummy_mtable in { name = name; ratio = ratio; mtable = mtable; gtables = [] };; let get_gtable cfont sdpi = try List.assoc sdpi cfont.gtables with Not_found -> let dpi = ldexp (float sdpi) (-16) in let table = try DFont.find_glyphs cfont.name (dpi *. cfont.ratio) with Not_found -> dummy_gtable in cfont.gtables <- (sdpi, table) :: cfont.gtables; table;; (*** Cooked DVI's ***) type cooked_dvi = { base_dvi : Cdvi.t; dvi_res : float; font_table : cooked_font Table.t };; let base_dpi = 600 let prefetch_fonts dvi = let font_map = dvi.Cdvi.font_map in let cfont n = let f = List.assoc n font_map in f.Dvicommands.name in let fontnames = List.map (fun (n, _) -> (cfont n)) font_map in Search.prefetch fontnames base_dpi ;; let cook_dvi dvi = let dvi_res = 72.27 in let build n = cook_font (List.assoc n dvi.Cdvi.font_map) dvi_res in let () = prefetch_fonts dvi in { base_dvi = dvi; dvi_res = dvi_res; font_table = Table.make build } ;; (*** The rendering state ***) type reg_set = { reg_h : int; reg_v : int; reg_w : int; reg_x : int; reg_y : int; reg_z : int; };; type state = { cdvi : cooked_dvi; sdpi : int; conv : float; x_origin : int; y_origin : int; (* Current font attributes *) mutable cur_font : cooked_font; mutable cur_mtable : (int * int) Table.t; mutable cur_gtable : Dev.glyph Table.t; (* Registers *) mutable h : int; mutable v : int; mutable w : int; mutable x : int; mutable y : int; mutable z : int; mutable put : (int * int) list; (* Register stack *) mutable stack : reg_set list; (* Color & Color stack *) mutable color : Dvicolor.color; mutable color_stack : Dvicolor.color list; (* Other attributes *) mutable alpha : Drawimage.alpha; mutable alpha_stack : Drawimage.alpha list; mutable blend : Drawimage.blend; mutable blend_stack : Drawimage.blend list; mutable epstransparent : bool; mutable epstransparent_stack : bool list; mutable epsbygs : bool; mutable epsbygs_stack : bool list; mutable epswithantialiasing : bool; mutable epswithantialiasing_stack : bool list; mutable direction : Transitions.direction option; mutable transition : Transitions.t; mutable transition_stack : Transitions.t list; (* TPIC specials state *) mutable tpic_pensize : float; mutable tpic_path : (float * float) list; mutable tpic_shading : float; (* PS specials page state *) mutable status : Cdvi.known_status; mutable headers : (bool * string) list; mutable html : (Dev.H.tag * (int * int * Dev.glyph) list ref) list; mutable checkpoint : int; };; type proc_unit = { escaped_register : reg_set; escaped_stack : reg_set list; escaped_cur_font : cooked_font; escaped_cur_mtable : (int * int) Table.t; escaped_cur_gtable : Dev.glyph Table.t; mutable escaped_commands : Dvicommands.command list };; let procs = Hashtbl.create 107;; type recording = { tag : string; unit : proc_unit} let current_recording_proc = ref [];; let visible = ref true;; let is_recording () = !current_recording_proc <> [];; (*** Rendering primitives ***) let last_height = ref 0;; let clear_symbols () = last_height := 2;; let add_char st x y code glyph = let g : Symbol.g = { Symbol.fontname = st.cur_font.name; Symbol.fontratio = st.cur_font.ratio; Symbol.glyph = glyph } in last_height := (Dev.get_glyph glyph).Glyph.voffset; let s : Symbol.symbol = Symbol.Glyph g in Symbol.add_to_global_display_set st.color x y code s;; let add_line st (line, file) = let x = st.x_origin + Misc.round (st.conv *. float st.h) and y = st.y_origin + Misc.round (st.conv *. float st.v) in Symbol.add_to_global_display_set st.color x y 0 (Symbol.Line (line, file));; let add_blank nn st width = let x = st.x_origin + Misc.round (st.conv *. float st.h) and y = st.y_origin + Misc.round (st.conv *. float st.v) and w = Misc.round (st.conv *. float width) in Symbol.add_to_global_display_set st.color x y nn (Symbol.Space (w, !last_height));; let add_rule st x y w h = Symbol.add_to_global_display_set st.color x y 0 (Symbol.Rule (w, h));; let get_register_set st = { reg_h = st.h; reg_v = st.v; reg_w = st.w; reg_x = st.x; reg_y = st.y; reg_z = st.z };; let set_register_set st rset = st.h <- rset.reg_h; st.v <- rset.reg_v; st.w <- rset.reg_w; st.x <- rset.reg_x; st.y <- rset.reg_y; st.z <- rset.reg_z;; let push st = st.stack <- (get_register_set st) :: st.stack;; let pop st = match st.stack with | [] -> () | rset :: rest -> set_register_set st rset; st.stack <- rest;; let color_push st col = st.color_stack <- st.color :: st.color_stack; st.color <- col; if !visible then Dev.set_color col;; let color_pop st = match st.color_stack with | [] -> () | col :: rest -> st.color <- col; if !visible then Dev.set_color col; st.color_stack <- rest;; let alpha_push st v = st.alpha_stack <- st.alpha :: st.alpha_stack; st.alpha <- v; if !visible then Dev.set_alpha v;; let alpha_pop st = match st.alpha_stack with | [] -> () | v :: rest -> st.alpha <- v; if !visible then Dev.set_alpha v; st.alpha_stack <- rest;; let blend_push st v = st.blend_stack <- st.blend :: st.blend_stack; st.blend <- v; if !visible then Dev.set_blend v;; let blend_pop st = match st.blend_stack with | [] -> () | v :: rest -> st.blend <- v; if !visible then Dev.set_blend v; st.blend_stack <- rest;; let epstransparent_push st v = st.epstransparent_stack <- st.epstransparent :: st.epstransparent_stack; st.epstransparent <- v; if !visible then Dev.set_epstransparent v;; let epstransparent_pop st = match st.epstransparent_stack with | [] -> () | v :: rest -> st.epstransparent <- v; if !visible then Dev.set_epstransparent v; st.epstransparent_stack <- rest;; let epsbygs_push st v = st.epsbygs_stack <- st.epsbygs :: st.epsbygs_stack; st.epsbygs <- v; if !visible then Dev.set_epsbygs v;; let epsbygs_pop st = match st.epsbygs_stack with | [] -> () | v :: rest -> st.epsbygs <- v; if !visible then Dev.set_epsbygs v; st.epsbygs_stack <- rest;; let epswithantialiasing_push st v = st.epswithantialiasing_stack <- st.epswithantialiasing :: st.epswithantialiasing_stack; st.epswithantialiasing <- v; if !visible then Dev.set_epswithantialiasing v;; let epswithantialiasing_pop st = match st.epswithantialiasing_stack with | [] -> () | v :: rest -> st.epswithantialiasing <- v; if !visible then Dev.set_epswithantialiasing v; st.epswithantialiasing_stack <- rest;; let transition_push st v = st.transition <- v; if !visible then Dev.set_transition v;; let fnt st n = let (mtable, gtable, cfont) = try let cfont = Table.get st.cdvi.font_table n in (cfont.mtable, get_gtable cfont st.sdpi, cfont) with Not_found -> (dummy_mtable, dummy_gtable, dummy_font) in st.cur_mtable <- mtable; st.cur_gtable <- gtable; st.cur_font <- cfont;; let put st code = try let x = st.x_origin + Misc.round (st.conv *. float st.h) and y = st.y_origin + Misc.round (st.conv *. float st.v) and glyph = Table.get st.cur_gtable code in if !visible then begin begin match st.html with | (tag, draw) :: _ -> draw := (x, y, glyph) :: !draw | [] -> () end; Dev.draw_glyph (glyph : Dev.glyph) x y; add_char st x y code glyph end with _ -> ();; let set st code = put st code; try let (dx, dy) = Table.get st.cur_mtable code in st.h <- st.h + dx; st.v <- st.v + dy with _ -> ();; let put_rule st a b = let x = st.x_origin + Misc.round (st.conv *. float st.h) and y = st.y_origin + Misc.round (st.conv *. float st.v) and w = int_of_float (ceil (st.conv *. float b)) and h = int_of_float (ceil (st.conv *. float a)) in add_rule st x (y - h) w h; if !visible then Dev.fill_rect x (y - h) w h;; let set_rule st a b = put_rule st a b; st.h <- st.h + b;; (*** Specials ***) let ill_formed_special s = Misc.warning (Printf.sprintf "Ill formed special <<%s>>" s);; exception Ill_formed_special of string;; let line_of_special s k = match split_string s k with | line :: rest -> (* Printf.eprintf "%s @ %s\n%!" line (match rest with h :: _ -> h | _ -> ""); *) begin try let l = int_of_string line in let f = match rest with | file :: _ -> Some file | _ -> None in (l, f) with | Failure _ -> raise (Ill_formed_special s) end | _ -> raise (Ill_formed_special s);; let line_special st s k = try add_line st (line_of_special s k) with Ill_formed_special s -> ill_formed_special s;; let color_special st s = match split_string s 0 with | "color" :: "push" :: args -> color_push st (Dvicolor.parse_color_args args) | "color" :: "pop" :: [] -> color_pop st | "color" :: args -> let _c = Dvicolor.parse_color_args args in Misc.warning "global color special is not supported" | _ -> ill_formed_special s;; let parse_float s = try float_of_string s with | _ -> failwith (Printf.sprintf "advi: cannot read a floating number in %S" s);; let parse_quoted_float s = parse_float (unquote s);; let parse_float_option s r = try Some (parse_float (List.assoc s r)) with _ -> None;; let alpha_special st s = match split_string s 0 with | ["advi:"; "alpha"; "push"; arg] -> alpha_push st (parse_float arg) | ["advi:"; "alpha"; "pop"] -> alpha_pop st | _ -> ill_formed_special s;; let parse_blend s = match String.lowercase s with | "none" -> Drawimage.Normal | "normal" -> Drawimage.Normal | "multiply" -> Drawimage.Multiply | "screen" -> Drawimage.Screen | "overlay" -> Drawimage.Overlay | "dodge" -> Drawimage.ColorDodge | "burn" -> Drawimage.ColorBurn | "darken" -> Drawimage.Darken | "lighten" -> Drawimage.Lighten | "difference" -> Drawimage.Difference | "exclusion" -> Drawimage.Exclusion | _ -> Misc.warning (Printf.sprintf "blend: invalid blend mode %s" s); Drawimage.Normal;; let blend_special st s = match split_string s 0 with | ["advi:"; "blend"; "push"; arg] -> blend_push st (parse_blend arg) | "advi:" :: "blend" :: "pop" :: [] -> blend_pop st | _ -> ill_formed_special s;; let parse_bool s = match String.lowercase s with | "true" -> true | "false" -> false | _ -> failwith "invalid boolean";; let epstransparent_special st s = match split_string s 0 with | ["advi:"; "epstransparent"; "push"; arg] -> epstransparent_push st (parse_bool arg) | "advi:" :: "epstransparent" :: "pop" :: [] -> epstransparent_pop st | _ -> ill_formed_special s;; let epsbygs_special st s = match split_string s 0 with | ["advi:"; "epsbygs"; "push"; arg] -> epsbygs_push st (parse_bool arg) | "advi:" :: "epsbygs" :: "pop" :: [] -> epsbygs_pop st | _ -> ill_formed_special s;; let epswithantialiasing_special st s = match split_string s 0 with | ["advi:"; "epswithantialiasing"; "push"; arg] -> epswithantialiasing_push st (parse_bool arg) | "advi:" :: "epswithantialiasing" :: "pop" :: [] -> epswithantialiasing_pop st | _ -> ill_formed_special s;; let get_records s = List.map (fun (k, v) -> String.lowercase k, v) (split_record s);; let psfile_special st s = try Misc.debug_endline (Printf.sprintf "psfile_special %S" s); let records = get_records s in let file = try unquote (List.assoc "psfile" records) with Not_found -> failwith "psfile: invalid special" in Misc.debug_endline ("PSFILE=" ^ file); (* bbox *) let llx, lly, urx, ury as bbox = try let llx = int_of_float_of_string (List.assoc "llx" records) and lly = int_of_float_of_string (List.assoc "lly" records) and urx = int_of_float_of_string (List.assoc "urx" records) and ury = int_of_float_of_string (List.assoc "ury" records) in Misc.debug_endline (Printf.sprintf "BBOX=%d %d %d %d" llx lly urx ury); llx, lly, urx, ury with | _ -> failwith "psfile: no bbox" in let rwi = try int_of_string (List.assoc "rwi" records) with _ -> 0 in let rhi = try int_of_string (List.assoc "rhi" records) with _ -> 0 in let file, drawbygs = let get_second_token s = Scanf.sscanf s "`%_s %s" (fun token -> token) in try if file.[0] = '`' then (* ex. `jpeg2ps world.jpg *) get_second_token file, false (* it must not be eps *) else file, st.epsbygs with | _ -> (* file must be an eps *) file, st.epsbygs in let x = st.x_origin + Misc.round (st.conv *. float st.h) in let y = st.y_origin + Misc.round (st.conv *. float st.v) in if !visible then if drawbygs then Dev.draw_ps_by_gs file bbox (rwi, rhi) (x - st.x_origin) (y - st.y_origin) else let width, height = match rwi, rhi with | 0, 0 -> float (urx - llx), float (ury - lly) | 0, _ -> let h = float rhi *. 0.1 in let w = float (urx - llx) *. (h /. float (ury - lly)) in w, h | _, 0 -> let w = float rwi *. 0.1 in let h = float (ury - lly) *. (w /. float (urx - llx)) in w, h | _, _ -> float rwi *. 0.1, float rhi *. 0.1 in let dpi = ldexp (float st.sdpi) (-16) in let width_pixel = truncate (width /. 72.0 *. dpi) in let height_pixel = truncate (height /. 72.0 *. dpi) in Dev.draw_img file st.epstransparent st.alpha st.blend (Some bbox) Drawimage.ScaleAuto st.epswithantialiasing (width_pixel, height_pixel) x y with | exc -> Misc.warning (Printf.sprintf "Failed to load psfile: %s" (Printexc.to_string exc));; (* Killing embedded applications: (1) we parse Unix signals in specials. *) let int_of_signal = function | "SIGABRT" | "sigabrt" -> Sys.sigabrt (* -1 *) | "SIGALRM" | "sigalrm" -> Sys.sigalrm (* -2 *) | "SIGFPE" | "sigfpe" -> Sys.sigfpe (* -3 *) | "SIGHUP" | "sighup" -> Sys.sighup (* -4 *) | "SIGILL" | "sigill" -> Sys.sigill (* -5 *) | "SIGINT" | "sigint" -> Sys.sigint (* -6 *) | "SIGKILL" | "sigkill" -> Sys.sigkill (* -7 *) | "SIGPIPE" | "sigpipe" -> Sys.sigpipe (* -8 *) | "SIGQUIT" | "sigquit" -> Sys.sigquit (* -9 *) | "SIGSEGV" | "sigsegv" -> Sys.sigsegv (* -10 *) | "SIGTERM" | "sigterm" -> Sys.sigterm (* -11 *) | "SIGUSR1" | "sigusr1" -> Sys.sigusr1 (* -12 *) | "SIGUSR2" | "sigusr2" -> Sys.sigusr2 (* -13 *) | "SIGCHLD" | "sigchld" -> Sys.sigchld (* -14 *) | "SIGCONT" | "sigcont" -> Sys.sigcont (* -15 *) | "SIGSTOP" | "sigstop" -> Sys.sigstop (* -16 *) | "SIGTSTP" | "sigtstp" -> Sys.sigtstp (* -17 *) | "SIGTTIN" | "sigttin" -> Sys.sigttin (* -18 *) | "SIGTTOU" | "sigttou" -> Sys.sigttou (* -19 *) | "SIGVTALRM" | "sigvtalrm" -> Sys.sigvtalrm (* -20 *) | "SIGPROF" | "sigprof" -> Sys.sigprof (* -21 *) | "" -> Sys.sigquit | s -> int_of_string s;; (* Killing embedded applications: (2) finding the application and calling Embed.kill_* to kill it. *) let kill_embed_special kill_fun st s = (* advi: kill[all]embed name=? signal=? *) let records = get_records s in let app_name = try unquote (List.assoc "name" records) with Not_found -> failwith ("No command to kill in " ^ s) in let signal = List.assoc "signal" records in (* prerr_endline (Printf.sprintf "Signal is ``%s''" signal); *) let sig_val = try int_of_signal (unquote signal) with | Not_found -> failwith ("No signal to kill command in " ^ s) | Failure _ -> failwith ("Cannot understand signal in " ^ s) in Misc.debug_endline (Printf.sprintf "Killing %s, in special %s." app_name s); kill_fun sig_val app_name;; (* Killing embedded applications: (3) partial applications to define the required primitives. *) let kill_one_embed_special = kill_embed_special Embed.kill_embedded_app;; let kill_all_embed_special = kill_embed_special Embed.kill_all_embedded_app;; (* Hide or show the window containing the output of an embedded application. *) let show_hide_embed_special show_fun st s = (* advi: [un]map[all]embed name=? *) let records = get_records s in let app_name = try unquote (List.assoc "name" records) with | Not_found -> failwith ("No application, hence no window to operate in " ^ s) in Misc.debug_endline (Printf.sprintf "Showing or hiding %s, in special %s." app_name s); show_fun app_name;; (* Mapping or unmapping windows of embedded applications. *) let unmap_one_embed_special st s = show_hide_embed_special Embed.unmap_embedded_app st s;; let unmap_all_embed_special st s = show_hide_embed_special Embed.unmap_all_embedded_app st s;; let map_one_embed_special st s = show_hide_embed_special Embed.map_embedded_app st s;; let map_all_embed_special st s = show_hide_embed_special Embed.map_all_embedded_app st s;; (* Parsing embedding modes in specials. *) let app_mode_of_string = function | "fake" -> Embed.Fake | "raw" -> Embed.Raw | "sticky" -> Embed.Sticky | "persistent" -> Embed.Persistent | "ephemeral" -> Embed.Ephemeral | s -> failwith ("Unknown embedding mode " ^ s);; (* Parsing embedding applications commands and calling embed_app. *) let embed_special st s = (* advi: embed mode=? width=? height=? command="command string" *) let records = get_records s in let app_mode = try app_mode_of_string (List.assoc "mode" records) with | Not_found -> failwith ("embed: no embedding mode in special " ^ s) in let app_name = try unquote (List.assoc "name" records) with | Not_found -> "" in let command = try unquote (List.assoc "command" records) with | Not_found -> if app_mode = Embed.Fake then "" else failwith ("embed: no command to embed in special " ^ s) in (* prerr_endline ("embed command=" ^ command); *) let get_dim dim records = match Dimension.normalize (Dimension.dimen_of_string (List.assoc dim records)) with | Dimension.In d -> d | _ -> assert false in let width_pixel, height_pixel = let w, h = try let width = get_dim "width" records in let height = get_dim "height" records in width, height with | _ -> failwith ("embed: no size in special " ^ s) in let dpi = ldexp (float st.sdpi) (-16) in let width_pixel = truncate (w *. dpi) in let height_pixel = truncate (h *. dpi) in (* prerr_endline (Printf.sprintf "%d x %d pixel" width_pixel height_pixel);*) width_pixel, height_pixel in let x = st.x_origin + Misc.round (st.conv *. float st.h) and y = st.y_origin + Misc.round (st.conv *. float st.v) in Misc.debug_endline (Printf.sprintf "Embedding %s with command %S, in special %s." app_name command s); if !visible then Dev.embed_app command app_mode app_name width_pixel height_pixel x y;; (* When scanning the page, we gather information on the embedded commands *) let scan_embed_special st s = let records = get_records s in let command = try unquote (List.assoc "command" records) with | Not_found -> failwith ("advi embed: no command to embed in special " ^ s) in Launch.add_white_run_command command;; let parse_transition dir mode record = let default_dir = match dir with Some d -> d | None -> Transitions.DirNone in let parse_genpath record = try List.assoc "genpath" record with | Not_found -> Misc.warning (Printf.sprintf "advi trans push: genpath function not found"); "spiral" in let parse_pathelem s = (parse_float_option (s ^ "x") record, parse_float_option (s ^ "y") record, None, None) (* to complete with parsed scale and rotation *) in let parse_steps = try let stepsstr = List.assoc "steps" record in try Some (int_of_string stepsstr) with | _ -> Misc.warning (Printf.sprintf "advi trans push: steps parsing failed %S" stepsstr); None with | Not_found -> None in let parse_direction key default = try match String.lowercase (List.assoc key record) with | "left" -> Transitions.DirLeft | "right" -> Transitions.DirRight | "top" | "up" -> Transitions.DirTop | "bottom" | "down" -> Transitions.DirBottom | "topleft" | "upleft" -> Transitions.DirTopLeft | "topright" | "upright" -> Transitions.DirTopRight | "bottomleft" | "downleft" -> Transitions.DirBottomLeft | "bottomright" | "downright" -> Transitions.DirBottomRight | "center" -> Transitions.DirCenter | s -> Misc.warning (Printf.sprintf "advi trans push: direction parsing failed %S" s); raise Exit with _ -> default (* Transitions.DirNone *) in match String.lowercase mode with | "slide" -> Transitions.TransSlide (parse_steps, parse_direction "from" default_dir) | "wipe" -> Transitions.TransWipe (parse_steps, parse_direction "from" default_dir) | "block" -> Transitions.TransBlock (parse_steps, parse_direction "from" Transitions.DirNone) | "path" -> Transitions.TransPath (parse_steps, parse_genpath record, parse_pathelem "start", parse_pathelem "stop") | "none" -> Transitions.TransNone | _ -> Misc.warning (Printf.sprintf "advi trans push: mode parsing failed %S" mode); Transitions.TransNone;; let transition_special st s = match split_string s 0 with | "advi:" :: "trans" :: mode :: args -> let record = split_record (String.concat " " args) in let trans = parse_transition st.direction mode record in transition_push st trans | _ -> ill_formed_special s;; let transbox_save_special st s = match split_string s 0 with | "advi:" :: "transbox" :: "save" :: args -> let dpi = ldexp (float st.sdpi) (-16) in let record = split_record (String.concat " " args) in let width = Dimension.dimen_of_string (List.assoc "width" record) in let height = Dimension.dimen_of_string (List.assoc "height" record) in let depth = Dimension.dimen_of_string (List.assoc "depth" record) in let pixels_of_dimen dim = match Dimension.normalize dim with | Dimension.Px x -> x | Dimension.In x -> truncate (x *. dpi) | _ -> assert false in let width_pixel = pixels_of_dimen width and height_pixel = pixels_of_dimen height and depth_pixel = pixels_of_dimen depth in let x = st.x_origin + Misc.round (st.conv *. float st.h) and y = st.y_origin + Misc.round (st.conv *. float st.v) + depth_pixel in Dev.transbox_save x y width_pixel (height_pixel + depth_pixel) | _ -> failwith "advi: transbox save special failed";; let transbox_go_special st s = match split_string s 0 with | "advi:" :: "transbox" :: "go" :: mode :: args -> let record = split_record (String.concat " " args) in let trans = parse_transition None mode record in Dev.transbox_go trans | _ -> failwith "advi: transbox go special failed";; exception Ignore;; let edit_special st s = try match split_string s 0 with | "advi:" :: "edit" :: args -> let record = split_record (String.concat " " args) in let first = try let assignable (x, v) = List.mem x [ "x"; "y"; "w"; "h"; "d"; ] in Some (List.find assignable record) with Not_found -> None in let field x = try List.assoc x record with Not_found -> Misc.warning (Printf.sprintf "Field %s missing in special %s" x s); raise Ignore in let dpi = ldexp (float st.sdpi) (-16) in let pixels dim = match Dimension.normalize (Dimension.dimen_of_string dim) with | Dimension.Px x -> float x | Dimension.In x -> x *. dpi | _ -> assert false in let xunit,yunit = try pixels (field "xunit"), pixels (field "yunit") with Not_found -> let u = pixels (field "unit") in u, u in let float_field x x' = let b, fx = try true, List.assoc x record with Not_found -> false, field x' in try b, float_of_string fx with _ -> Misc.warning (Printf.sprintf "Field %s=%s not a float in special %s" x fx s); raise Ignore in let float_to_pixel f unit = truncate (f *. unit) in let raw_fields = { Dev.rx = float_field "x" "X"; Dev.ry = float_field "y" "Y"; Dev.rw = float_field "w" "W"; Dev.rh = float_field "h" "H"; Dev.rd = float_field "d" "D"; } in let rmap f r = { Dev.rx = f r.Dev.rx; Dev.ry = f r.Dev.ry; Dev.rw = f r.Dev.rw; Dev.rh = f r.Dev.rh; Dev.rd = f r.Dev.rd; } in let r = rmap snd raw_fields in let m = rmap fst raw_fields in let rect = { Dev.rx = st.x_origin + Misc.round (st.conv *. float st.h) + float_to_pixel r.Dev.rx xunit; Dev.ry = st.y_origin + Misc.round (st.conv *. float st.v) - float_to_pixel r.Dev.ry yunit; Dev.rw = float_to_pixel r.Dev.rw xunit; Dev.rh = float_to_pixel r.Dev.rh yunit; Dev.rd = float_to_pixel r.Dev.rd yunit; } in let info = { Dev.E.comm = field "comm"; Dev.E.name = field "name"; Dev.E.line = field "line"; Dev.E.file = field "file"; Dev.E.first = first; Dev.E.xunit = xunit; Dev.E.yunit = yunit; Dev.E.origin = r; Dev.E.action = m; } in Dev.E.add rect info | _ -> ill_formed_special s with Ignore -> () ;; (* Defining the forward function eval_command. *) let forward_eval_command = ref (fun (st : state) (c : Dvicommands.command) -> (failwith "Undefined forward eval_command" : unit));; let set_forward_eval_command f = forward_eval_command := f;; let eval_command st c = !forward_eval_command st c;; let playing = ref 0;; let get_playing () = !playing;; (* Setting the forward function Dev.get_playing. *) Dev.set_forward_get_playing get_playing;; let visible_stack = ref [];; let proc_clean () = current_recording_proc := []; playing := 0; visible_stack := []; visible := true; Hashtbl.clear procs;; let proc_special st s = let records = get_records s in try let v = List.assoc "record" records in match v with | "start" -> let procname = try unquote (List.assoc "proc" records) with | Not_found -> failwith "proc: invalid special" in visible_stack := !visible :: !visible_stack; visible := List.mem_assoc "play" records; if !playing = 0 then let recording = { tag = procname; unit = { escaped_register = get_register_set st; escaped_stack = st.stack; escaped_cur_mtable = st.cur_mtable; escaped_cur_gtable = st.cur_gtable; escaped_cur_font = st.cur_font; escaped_commands = [] } } in current_recording_proc := recording :: !current_recording_proc | "end" -> if !playing = 0 then begin match !current_recording_proc with | [] -> Misc.warning (Printf.sprintf "'xxx %s' not recording" s) | recording :: rest -> let procname = recording.tag in current_recording_proc := rest; let u = recording.unit in Hashtbl.add procs procname u; match u.escaped_commands with | h :: rest -> u.escaped_commands <- List.rev rest | [] -> assert false end; begin match !visible_stack with | h :: rest -> visible := h; visible_stack := rest; | [] -> (* Ill-formed DVI not recording error should have ben reported right above *) (); end; | _ -> ill_formed_special s with | Not_found -> let procname = try unquote (List.assoc "proc" records) with | Not_found -> failwith "proc: invalid special" in try ignore (List.assoc "play" records); if not (is_recording ()) then begin let us = Hashtbl.find_all procs procname in let escaped_cur_font = st.cur_font and escaped_cur_mtable = st.cur_mtable and escaped_cur_gtable = st.cur_gtable in let escaped_stack = push st; st.stack in incr playing; List.iter (fun u -> set_register_set st u.escaped_register; st.stack <- u.escaped_stack; st.cur_mtable <- u.escaped_cur_mtable; st.cur_gtable <- u.escaped_cur_gtable; st.cur_font <- u.escaped_cur_font; List.iter (fun com -> eval_command st com) u.escaped_commands ) us; decr playing; st.stack <- escaped_stack; pop st; st.cur_mtable <- escaped_cur_mtable; st.cur_gtable <- escaped_cur_gtable; st.cur_font <- escaped_cur_font; end with | Not_found -> Misc.warning (Printf.sprintf "xxx '%s': %s not recorded" s procname);; let wait_special st s = let records = get_records s in let second = try parse_float (List.assoc "sec" records) with | Not_found | Failure _ -> failwith (Printf.sprintf "wait: invalid special: [ %s ]" s) in (* Wait is treated like Pause, as an exception *) if !visible then raise (Wait second); st.checkpoint <- 0;; (* Background object configuration. *) let inherit_background_info = Options.flag false "-inherit-background" " the background options are inherited\ \n\t from the previous page,\ \n\t (the default is not to inherit background settings).";; let setup_bkgd st = (* Propagate bkgd preferences to graphics device storing the default/inherited prefs into Dev. *) Dev.blit_bkgd_data st.Cdvi.bkgd_prefs Dev.bkgd_data; (* Apply local modifications. *) Dev.set_bg_options st.Cdvi.bkgd_local_prefs; (* Recover modified preferences. *) Dev.blit_bkgd_data Dev.bkgd_data st.Cdvi.bkgd_prefs;; let ratios_alist = [ ("auto", Drawimage.ScaleAuto); ("center", Drawimage.ScaleCenter); ("top", Drawimage.ScaleTop); ("bottom", Drawimage.ScaleBottom); ("left", Drawimage.ScaleLeft); ("right", Drawimage.ScaleRight); ("topleft", Drawimage.ScaleTopLeft); ("bottomright", Drawimage.ScaleBottomRight); ("topright", Drawimage.ScaleTopRight); ("bottomleft", Drawimage.ScaleBottomLeft); ];; (* The find_bgfun function should eventually handle dynamically loaded plugins *) let bggradients_alist = [ ("hgradient", Addons.hgradient); ("vgradient", Addons.vgradient); ("dgradient", Addons.dgradient); ("d1gradient", Addons.d1gradient); ("d2gradient", Addons.d2gradient); ("cgradient", Addons.cgradient); ("circgradient", Addons.circgradient); ];; let find_bggradient s = try Some (List.assoc (unquote s) bggradients_alist) with _ -> None;; let bkgd_alist = [ ("color", fun s st -> let c = Dvicolor.parse_color_args (split_string (unquote s) 0) in [Dev.BgColor c]); ("image", fun s st -> [Dev.BgImg s]); ("reset", fun s st -> Dev.blit_bkgd_data (Dev.default_bkgd_data ()) st.Cdvi.bkgd_prefs; []); ("inherit", fun s st -> inherit_background_info := true; []); ("alpha", fun s st -> let a = parse_quoted_float s in [Dev.BgAlpha a]); ("blend", fun s st -> let b = parse_blend (unquote s) in [Dev.BgBlend b]); ("fit", fun s st -> let f = try List.assoc (unquote s) ratios_alist with _ -> Drawimage.ScaleAuto in [Dev.BgRatio f]); ("colorstart", fun s st -> let c = Dvicolor.parse_color_args (split_string (unquote s) 0) in [Dev.BgColorStart c]); ("colorstop", fun s st -> let c = Dvicolor.parse_color_args (split_string (unquote s) 0) in [Dev.BgColorStop c]); ("xstart", fun s st -> let x = parse_quoted_float s in [Dev.BgXStart x]); ("ystart", fun s st -> let y = parse_quoted_float s in [Dev.BgYStart y]); ("width", fun s st -> let w = parse_quoted_float s in [Dev.BgWidth w]); ("height", fun s st -> let h = parse_quoted_float s in [Dev.BgHeight h]); ("xcenter", fun s st -> let x = parse_quoted_float s in [Dev.BgXCenter x]); ("ycenter", fun s st -> let y = parse_quoted_float s in [Dev.BgYCenter y]); ("gradient", fun s st -> [Dev.BgGradient (find_bggradient (unquote s))]); ];; let filter_alist alist falist = let aux k alist okalist = try (k, List.assoc k alist) :: okalist with | Not_found -> okalist in List.fold_left (fun l -> fun k -> aux k alist l) [] (List.map (fun (k, v) -> k) falist);; (* When scanning the page, we just fill the info structure for backgrounds *) let scan_bkgd_special st s = let records = get_records s in st.Cdvi.bkgd_local_prefs <- List.flatten (List.map (fun (k, v) -> (List.assoc k bkgd_alist) v st) (filter_alist records bkgd_alist)) @ st.Cdvi.bkgd_local_prefs;; (* When not scanning, we ignore the background information *) let bkgd_special st s = ();; (* Support for TPIC specials. *) let milli_inch_to_sp = AdviUnits.from_to AdviUnits.IN AdviUnits.SP 1e-3;; let tpic_milli_inches s = parse_float s *. milli_inch_to_sp;; let no_shading = -1.0 let tpic_pen st = Misc.round (st.conv *. st.tpic_pensize);; let tpic_x st x = st.x_origin + Misc.round (st.conv *. (float st.h +. x));; let tpic_y st y = st.y_origin + Misc.round (st.conv *. (float st.v +. y));; let tpic_flush_path st cntr = let path = Array.of_list (List.rev st.tpic_path) in (* Convert points in path to pixel coordinates *) let pixpath = Array.map (fun (x, y) -> (tpic_x st x, tpic_y st y)) path in (* If shading requested and path is closed, fill *) if st.tpic_shading >= 0.0 && Array.length path >= 2 && path.(0) = path.(Array.length path - 1) && !visible then Dev.fill_path pixpath ~shade:st.tpic_shading; (* If requested, draw outline of path *) if cntr && !visible then Dev.draw_path pixpath ~pensize:(tpic_pen st); (* Reset path *) st.tpic_path <- []; st.tpic_shading <- no_shading;; let dist (x0, y0) (x1, y1) = abs (x0 - x1) + abs (y0 - y1);; let tpic_spline_path st = (* Code shamelessly stolen from xdvi *) let path = Array.of_list (List.map (fun (x, y) -> (tpic_x st x, tpic_y st y)) (List.rev st.tpic_path)) in let p = Array.concat [[|path.(0)|]; path; [|path.(Array.length path - 1)|]] in let r = ref [] in for i = 0 to Array.length p - 3 do let steps = (dist p.(i) p.(i + 1) + dist p.(i + 1) p.(i + 2)) / 4 in let (x2, y2) = p.(i + 2) and (x1, y1) = p.(i + 1) and (x0, y0) = p.(i) in for j = 0 to steps - 1 do let w = (j * 1000 + 500) / steps in let t1 = w * w / 20 in let w = w - 500 in let t2 = (750000 - w * w) / 10 in let w = w - 500 in let t3 = w * w / 20 in let xp = (t1 * x2 + t2 * x1 + t3 * x0 + 50000) / 100000 and yp = (t1 * y2 + t2 * y1 + t3 * y0 + 50000) / 100000 in r := (xp, yp) :: !r done done; if !visible then Dev.draw_path (Array.of_list (List.rev !r)) ~pensize:(tpic_pen st); st.tpic_path <- []; st.tpic_shading <- no_shading;; let rad_to_deg = 45.0 /. atan 1.0;; let tpic_arc st x y rx ry s e cntr = let x = tpic_x st x and y = tpic_y st y and rx = Misc.round (st.conv *. rx) and ry = Misc.round (st.conv *. ry) and s = Misc.round (s *. rad_to_deg) and e = Misc.round (e *. rad_to_deg) in (* If shading requested, fill the arc *) if st.tpic_shading >= 0.0 && !visible then Dev.fill_arc ~x ~y ~rx ~ry ~start:s ~stop:e ~shade:st.tpic_shading; (* If requested, draw outline of arc *) if cntr && !visible then Dev.draw_arc ~x ~y ~rx ~ry ~start:s ~stop:e ~pensize:(tpic_pen st); (* Reset shading *) st.tpic_shading <- no_shading;; let tpic_specials st s = match split_string s 0 with | "pn" :: size :: _ -> st.tpic_pensize <- tpic_milli_inches size | "pa" :: x :: y :: _ -> st.tpic_path <- (tpic_milli_inches x, tpic_milli_inches y) :: st.tpic_path | "fp" :: _ -> tpic_flush_path st true | "ip" :: _ -> tpic_flush_path st false | "da" :: _ -> (* TODO: dashed lines *) tpic_flush_path st true | "dt" :: _ -> (* TODO: dotted lines *) tpic_flush_path st true | "sp" :: _ -> (* TODO: dashed/dotted splines *) tpic_spline_path st | "ar" :: x :: y :: rx :: ry :: s :: e :: _ -> tpic_arc st (tpic_milli_inches x) (tpic_milli_inches y) (tpic_milli_inches rx) (tpic_milli_inches ry) (parse_float s) (parse_float e) true | "ia" :: x :: y :: rx :: ry :: s :: e :: _ -> tpic_arc st (tpic_milli_inches x) (tpic_milli_inches y) (tpic_milli_inches rx) (tpic_milli_inches ry) (parse_float s) (parse_float e) true | "sh" :: s :: _ -> st.tpic_shading <- parse_float s | "wh" :: _ -> st.tpic_shading <- 0.0 | "bk" :: _ -> st.tpic_shading <- 1.0 | s :: _ -> Misc.warning (Printf.sprintf "Unknown pic command: %s" s) | _ -> ();; (* End of TPIC hacks *) let put_special st s = if Gs.get_do_ps () && st.status.Cdvi.hasps then if s = "begin" then let x, y = Dev.current_pos () in (* we get absolute coordinates and convert them *) let h' = Misc.round (float (x - st.x_origin) /. st.conv) in let v' = Misc.round (float (y - st.y_origin) /. st.conv) in st.put <- (st.h - h', st.v - v') :: st.put; st.h <- h'; st.v <- v' else if s = "end" then match st.put with | _ :: tail -> st.put <- tail | _ -> ();; let rec put_coor x y = function | (dx, dy) :: tail -> put_coor (x + dx) (y + dy) tail | [] -> x, y;; let ps_special st s = if Gs.get_do_ps () && st.status.Cdvi.hasps then let h', v' = put_coor st.h st.v st.put in let x = Misc.round (st.conv *. float h') in let y = Misc.round (st.conv *. float v') in if !visible then begin try Dev.exec_ps s x y with Dev.GS -> st.status.Cdvi.hasps <- false end;; (* header are not "rendered", only stored during scan *) let header_special st s = ();; (* For html specials *) (* Should check that a pause is not in the middle of some html code *) let open_html st link tag = st.html <- (tag (unquote link), ref []) :: st.html (* match st.html with *) (* | Some (t, k) -> st.html <- Some (t, succ k) *) (* | None -> st.html <- Some (tag (unquote link), 0);; *) let close_html st = match st.html with | [] -> Misc.warning ("Closing html tag that was not open") | (tag, draw) :: rest -> Dev.H.add {Dev.H.tag = tag; Dev.H.draw = List.rev !draw}; st.html <- rest; match rest with | (_, draw') :: _ -> draw' := !draw @ !draw' | [] -> () ;; (* match st.html with *) (* | Some (tag, k) when k > 0 -> *) (* (\* Just added that line *\) *) (* Dev.H.add {Dev.H.tag = tag; Dev.H.draw = List.rev st.draw_html}; *) (* st.html <- Some (tag, k - 1) *) (* | Some (tag, 0) -> *) (* Dev.H.add {Dev.H.tag = tag; Dev.H.draw = List.rev st.draw_html}; *) (* st.html <- None; *) (* st.draw_html <- [] *) (* | Some (_, k) -> assert false *) (* | None -> Misc.warning ("Closing html tag that was not open");; *) let html_special st html = if has_prefix " open_html st link (fun x -> Dev.H.Name x) | ("href", link) :: _ -> open_html st link (fun x -> Dev.H.Href x) | (("advi" | "hdvi" | "pdvi" as kind), link) :: rest -> let mode = if kind = "advi" then Dev.H.Over else if kind = "hdvi" then Dev.H.Click_down else Dev.H.Stick in let style = try match List.assoc "style" rest with | "invisible" -> Dev.H.Invisible | "underline" -> Dev.H.Underline | "box" -> Dev.H.Box | _ -> Misc.warning (Printf.sprintf "Incorrect style in html suffix %s" html); Dev.H.Box with Not_found -> Dev.H.Box in let color = try Some (Dvicolor.parse_color_args [List.assoc "color" rest]) with Not_found -> None in let advi x = let play () = proc_special st ("advi: proc=" ^ x ^ " play") in Dev.H.Advi {Dev.H.link = x; Dev.H.action = play; Dev.H.mode = mode; Dev.H.style = style; Dev.H.color = color; Dev.H.area = None} in open_html st link advi | _ -> Misc.warning (Printf.sprintf "Unknown html suffix %s" html) end else if has_prefix "" html || has_prefix "" html then close_html st else Misc.warning (Printf.sprintf "Unknown html suffix %s" html);; let scan_special_html (_, xrefs, _) page s = let name = String.sub s 14 (String.length s - 16) in Hashtbl.add xrefs name page;; let scan_special_line (_, _, lastline) s k = try lastline := Some (line_of_special s k) with Ill_formed_special s -> ill_formed_special s;; let save_page_image_special st = Shot.save_page_image ();; let get_file_name records = try unquote (List.assoc "file" records) with | Not_found -> failwith "advi_save_page: invalid special (no file name)";; let save_page_image_file_special st s = let records = get_records s in Shot.save_page_image_file (get_file_name records);; let save_page_area_image_special st s = let records = get_records s in try let x = int_of_string (List.assoc "x" records) and y = int_of_string (List.assoc "y" records) and w = int_of_string (List.assoc "w" records) and h = int_of_string (List.assoc "h" records) in Shot.save_page_area_image x y w h with | Not_found | Failure _ -> failwith (Printf.sprintf "advi_save_page: invalid special %s" s);; let save_page_area_image_file_special st s = let records = get_records s in try let fname = get_file_name records in let x = int_of_string (List.assoc "x" records) and y = int_of_string (List.assoc "y" records) and w = int_of_string (List.assoc "w" records) and h = int_of_string (List.assoc "h" records) in Shot.save_page_area_image_file fname x y w h with | Not_found | Failure _ -> failwith (Printf.sprintf "advi_save_page_area: invalid special %s" s);; let push_keys_special st s = Misc.debug_endline (Printf.sprintf "push_keys_special %S" s); match split_string_quoted s 0 with | ["advi:"; "pushkeys"; keys] -> let push_keys keys = let push pc = if pc >= 0 then Misc.push_key_event (char_of_int pc) GraphicsY11.nomod in let push_control pc = if pc >= 0 then Misc.push_key_event (char_of_int pc) GraphicsY11.control in let rec loop pc i = if i >= 0 then let k = keys.[i] in match k with | '^' when pc >= 0 -> push_control pc; loop (-1) (i - 1) | '^' -> loop (int_of_char k) (i - 1) | k when pc >= 0 -> push pc; loop (int_of_char k) (i - 1) | k -> loop (int_of_char k) (i - 1) else push pc in loop (-1) (String.length keys - 1) in (* We scan the string as a Caml token to handle properly \ddd chars if any. In case of error (i.e. when keys is not properly enclosed between double quotes), we simply push back the characters verbatim. *) Misc.debug_endline (Printf.sprintf "advi_push_keys %S" keys); Scanf.kscanf (Scanf.Scanning.from_string keys) (fun _ _ -> let keys = unquote keys in push_keys keys) "%S" push_keys | _ -> failwith (Printf.sprintf "advi_push_keys: invalid special ``%s''" s);; (* This function is iterated on the current DVI page BEFORE rendering it, to gather the information contained in some "asynchronous" specials (typically, PS headers, background commands, html references) *) let scan_special status (headers, xrefs, lastline as args) pagenum s = try if Launch.white_run () && has_prefix "advi: embed " s then scan_embed_special status s else (* Embedded Postscript, better be first for speed when scanning *) let do_ps = Gs.get_do_ps () in if has_prefix "\" " s || has_prefix "ps:" s || has_prefix "psfile=" s || has_prefix "PSfile=" s then status.Cdvi.hasps <- do_ps else if has_prefix "!" s then (if do_ps then headers := (true, get_suffix "!" s) :: !headers) else if has_prefix "header=" s then (if do_ps then headers := (false, get_suffix "header=" s) :: !headers) else if has_prefix "advi: setbg " s then scan_bkgd_special status s else if has_prefix "line: " s then scan_special_line args s 6 else (* if has_prefix "src:" s then scan_special_line args s 4 else *) (* We must test both case ill_formed_special s | Ill_formed_special s -> ill_formed_special s;; (* Scan a page calling function scan_special when seeing a special and the function otherwise for other DVI stuff. *) let scan_special_page otherwise cdvi globals pagenum = Misc.debug_stop "Scanning specials"; let page = cdvi.base_dvi.Cdvi.pages.(pagenum) in match page.Cdvi.page_status with | Cdvi.Unknown -> let status = {Cdvi.hasps = false; Cdvi.bkgd_local_prefs = []; Cdvi.bkgd_prefs = (if !inherit_background_info then Dev.copy_of_bkgd_data () else Dev.default_bkgd_data ())} in let lastline = ref None in let eval = function | Dvicommands.C_xxx s -> let globals = (fst globals, snd globals, lastline) in scan_special status globals pagenum s | x -> otherwise x in Cdvi.page_iter eval cdvi.base_dvi.Cdvi.pages.(pagenum); page.Cdvi.line <- !lastline; page.Cdvi.page_status <- Cdvi.Known status; status | Cdvi.Known stored_status -> stored_status;; let scan_find_location cdvi page (line, filename) = let intervals = ref [] in let last = ref 0 in let eval = function | Dvicommands.C_xxx s when has_prefix "line: " s -> let (l, f as _location) = line_of_special s 6 in if f = filename then begin if !last <= line && line < l then intervals := !last :: !intervals; last := l end; | _ -> () in Cdvi.page_iter eval cdvi.base_dvi.Cdvi.pages.(page); match List.sort (fun l1 l2 -> compare (line - l1) (line - l2)) !intervals with | l :: _ -> l | [] -> 0;; exception Found of (int * string option) option;; exception Position of int * int * int * int * int;; let scan_find_anchor_position cdvi dpi page anchor = let dvi = cdvi.base_dvi in let mag = float dvi.Cdvi.preamble.Dvicommands.pre_mag /. 1000.0 in let conv = mag *. dpi /. cdvi.dvi_res /. 65536.0 in let st = { cdvi = cdvi; sdpi = 0; conv = conv; x_origin = 0; y_origin = 0; cur_mtable = dummy_mtable; cur_gtable = dummy_gtable; cur_font = dummy_font; h = 0; v = 0; w = 0; x = 0; y = 0; z = 0; put = []; stack = []; color = Dev.get_fgcolor (); color_stack = []; alpha = 1.0; alpha_stack = []; blend = Drawimage.Normal; blend_stack = []; epstransparent = true; epstransparent_stack = []; epsbygs = false; epsbygs_stack = []; epswithantialiasing = true; epswithantialiasing_stack = []; direction = None; transition = Transitions.TransNone; transition_stack = []; tpic_pensize = 0.0; tpic_path = []; tpic_shading = no_shading; status = Obj.magic (); headers = []; html = []; checkpoint = 0; } in let eval = function | Dvicommands.C_xxx s when has_prefix "html: raise (Position (st.h, st.v, 0, 0, 0)) | Dvicommands.C_set code -> begin try let (dx, dy) = Table.get st.cur_mtable code in st.h <- st.h + dx; st.v <- st.v + dy with _ -> () end; | Dvicommands.C_set_rule (a, b) -> st.h <- st.h + b | Dvicommands.C_push -> push st | Dvicommands.C_pop -> pop st | Dvicommands.C_right k -> st.h <- st.h + k | Dvicommands.C_w0 -> st.h <- st.h + st.w | Dvicommands.C_w k -> st.w <- k; st.h <- st.h + st.w | Dvicommands.C_x0 -> st.h <- st.h + st.x | Dvicommands.C_x k -> st.x <- k; st.h <- st.h + st.x | Dvicommands.C_down k -> st.v <- st.v + k | Dvicommands.C_y0 -> st.v <- st.v + st.y | Dvicommands.C_y k -> st.y <- k; st.v <- st.v + st.y | Dvicommands.C_z0 -> st.v <- st.v + st.z | Dvicommands.C_z k -> st.z <- k; st.v <- st.v + st.z | Dvicommands.C_fnt n -> fnt st n | Dvicommands.C_xxx s -> () (* Should catch displacements *) | _ -> () in let page_dvi = cdvi.base_dvi.Cdvi.pages.(page) in try Cdvi.page_iter eval page_dvi; raise Not_found with Position (h, v, _, _, _) -> let x = Misc.round (st.conv *. float h) in let y = Misc.round (st.conv *. float v) in (x, y, 0, 0, 0);; let scan_find_anchor_location cdvi page anchor = let last = ref None in let eval = function | Dvicommands.C_xxx s when has_prefix "line: " s -> last := Some (line_of_special s 6) | Dvicommands.C_xxx s when has_prefix "html: raise (Found !last) | _ -> () in try Cdvi.page_iter eval cdvi.base_dvi.Cdvi.pages.(page); raise Not_found with Found l -> l;; let special st s = try if has_prefix "\" " s || has_prefix "ps:" s || has_prefix "! " s then ps_special st s else if has_prefix "advi: put" s then put_special st (get_suffix "advi: put" s) else (* Other specials *) if has_prefix "color " s then color_special st s else if has_prefix "html:" s then html_special st (get_suffix "html:" s) else if has_prefix "PSfile=" s || has_prefix "psfile=" s then psfile_special st s else if has_prefix "advi: " s then begin if has_prefix "advi: edit" s then edit_special st s else if has_prefix "advi: alpha" s then alpha_special st s else if has_prefix "advi: blend" s then blend_special st s else if has_prefix "advi: epstransparent" s then epstransparent_special st s else if has_prefix "advi: epsbygs" s then epsbygs_special st s else if has_prefix "advi: epswithantialiasing" s then epswithantialiasing_special st s else if has_prefix "advi: pause" s then raise Pause else if has_prefix "advi: proc" s then proc_special st s else if has_prefix "advi: setbg " s then bkgd_special st s else (* all the following have effects, and should be ignored if active is false *) if !active then begin if has_prefix "advi: wait " s then wait_special st s else if has_prefix "advi: embed " s then (if !visible then embed_special st s) else if has_prefix "advi: trans " s then transition_special st s else if has_prefix "advi: transbox save " s then transbox_save_special st s else if has_prefix "advi: transbox go " s then transbox_go_special st s else if has_prefix "advi: killembed " s then (if !visible then kill_one_embed_special st s) else if has_prefix "advi: killallembed " s then (if !visible then kill_all_embed_special st s) else if has_prefix "advi: mapembed " s then (if !visible then map_one_embed_special st s) else if has_prefix "advi: mapallembed " s then (if !visible then map_all_embed_special st s) else if has_prefix "advi: unmapembed " s then (if !visible then unmap_one_embed_special st s) else if has_prefix "advi: unmapallembed " s then (if !visible then unmap_all_embed_special st s) else if has_prefix "advi: savepageimage" s then (if !visible then save_page_image_special st) else if has_prefix "advi: savepageimagefile " s then (if !visible then save_page_image_file_special st s) else if has_prefix "advi: savepageareaimage " s then (if !visible then save_page_area_image_special st s) else if has_prefix "advi: savepageareaimagefile " s then (if !visible then save_page_area_image_file_special st s) else if has_prefix "advi: pushkeys " s then (if !visible then push_keys_special st s) else Misc.warning (Printf.sprintf "unknown special: %s" s) end (* else we ignore it, whether well-formed or ill-formed *) end else if has_prefix "line: " s then line_special st s 6 else (* if has_prefix "src:" s then line_special st s 4 else *) if has_prefix "pn " s || has_prefix "pa " s || s = "fp" || s = "ip" || has_prefix "da " s || has_prefix "dt " s || s = "sp" || has_prefix "sp " s || has_prefix "ar " s || has_prefix "ia " s || has_prefix "sh " s || s = "wh" || s = "bk" then tpic_specials st s with Pause | Wait _ as exn -> raise exn | _ -> Misc.warning (Printf.sprintf "Unknown or ill formed special <<%s>>" s);; (*** Page rendering ***) let eval_dvi_command st = function | Dvicommands.C_set code -> set st code | Dvicommands.C_set_rule(a, b) -> set_rule st a b | Dvicommands.C_put code -> put st code | Dvicommands.C_put_rule(a, b) -> put_rule st a b | Dvicommands.C_nop | Dvicommands.C_bop _ | Dvicommands.C_eop -> () | Dvicommands.C_push -> push st | Dvicommands.C_pop -> pop st | Dvicommands.C_right k -> add_blank 1 st k; st.h <- st.h + k | Dvicommands.C_w0 -> add_blank 2 st st.w; st.h <- st.h + st.w | Dvicommands.C_w k -> st.w <- k; add_blank 3 st st.w; st.h <- st.h + st.w | Dvicommands.C_x0 -> add_blank 4 st st.x; st.h <- st.h + st.x | Dvicommands.C_x k -> st.x <- k; add_blank 5 st st.x; st.h <- st.h + st.x | Dvicommands.C_down k -> st.v <- st.v + k | Dvicommands.C_y0 -> st.v <- st.v + st.y | Dvicommands.C_y k -> st.y <- k; st.v <- st.v + st.y | Dvicommands.C_z0 -> st.v <- st.v + st.z | Dvicommands.C_z k -> st.z <- k; st.v <- st.v + st.z | Dvicommands.C_fnt n -> fnt st n | Dvicommands.C_xxx s -> special st s | Dvicommands.C_fnt_def (_, _) | Dvicommands.C_pre _ | Dvicommands.C_post (_, _) | Dvicommands.C_post_post _ -> ();; let eval_command st c = let record r = let u = r.unit in match c with (* The advi: proc specials are not recorded *) (* | Dvicommands.C_xxx s when has_prefix "advi: proc" s -> () *) | _ -> u.escaped_commands <- c :: u.escaped_commands in List.iter record !current_recording_proc; eval_dvi_command st c;; (* Setting the forward function eval_command. *) set_forward_eval_command eval_command;; let newpage h st magdpi x y = if st.status.Cdvi.hasps then try Dev.newpage h st.sdpi magdpi x y with Dev.GS -> st.status.Cdvi.hasps <- false else Dev.clearps ();; let find_prologues l = let l = List.rev l in let h = List.map snd (List.filter (function b, _ -> not b) l) in try let h' = Search.true_file_names [] h in let table = List.combine h h' in try List.map (function b, s as p -> if b then p else b, List.assoc s table) l with | Not_found -> assert false with | Invalid_argument _ | Not_found -> Misc.warning "Cannot find postscript prologue. Continuing without Postscript"; Gs.set_do_ps false; [];; let render_step cdvi num ?trans ?chst dpi xorig yorig = proc_clean (); if num < 0 || num >= Array.length cdvi.base_dvi.Cdvi.pages then invalid_arg "Driver.render_step"; let dvi = cdvi.base_dvi in let mag = float dvi.Cdvi.preamble.Dvicommands.pre_mag /. 1000.0 and page = dvi.Cdvi.pages.(num) in let otherwise = ignore in let status = let headers = ref [] and xrefs = dvi.Cdvi.xrefs in let s = scan_special_page otherwise cdvi (headers, xrefs) num in if !headers <> [] then Dev.add_headers (find_prologues !headers); s in (* Didier: should it be ``Gs.get_do_ps ()'' instead of ``false''? and why has Dvi been changed to Cdvi ? --Why should we forget about the status? status.Cdvi.hasps <- Gs.get_do_ps (); *) let orid = function Some f -> f | None -> fun x->x in let st = { cdvi = cdvi; sdpi = Misc.round (mag *. ldexp dpi 16); conv = mag *. dpi /. cdvi.dvi_res /. 65536.0; x_origin = xorig; y_origin = yorig; cur_mtable = dummy_mtable; cur_gtable = dummy_gtable; cur_font = dummy_font; h = 0; v = 0; w = 0; x = 0; y = 0; z = 0; put = []; stack = []; color = Dev.get_fgcolor (); color_stack = []; alpha = 1.0; alpha_stack = []; blend = Drawimage.Normal; blend_stack = []; epstransparent = true; epstransparent_stack = []; epsbygs = Gs.get_do_ps(); epsbygs_stack = []; epswithantialiasing = true; epswithantialiasing_stack = []; direction = trans; transition = Transitions.TransNone; transition_stack = []; tpic_pensize = 0.0; tpic_path = []; tpic_shading = no_shading; status = (orid chst) status; headers = []; html = []; checkpoint = 0; } in newpage [] st (mag *. dpi) xorig yorig; setup_bkgd st.status; (* Apply the background preferences in Dev, *) Dev.clear_dev (); (* and redraw the background. *) Dev.set_color st.color; Dev.set_transition st.transition; st.checkpoint <- 0; let check () = begin try Dev.continue () with | Dev.Stop as exn -> raise exn end; st.checkpoint <- checkpoint_frequency in let eval st x = st.checkpoint <- st.checkpoint - 1; let b = eval_command st x in if st.checkpoint < 0 then check (); b in Cdvi.page_step (eval st) page;; let unfreeze_font cdvi n = try let cfont = Table.get cdvi.font_table n in ignore (Table.get cfont.mtable (Char.code 'A')) with _ -> ();; let unfreeze_fonts cdvi = let font_map = cdvi.base_dvi.Cdvi.font_map in List.iter (fun (n, _) -> unfreeze_font cdvi n) font_map let scan_special_pages cdvi lastpage = let headers = ref [] and xrefs = cdvi.base_dvi.Cdvi.xrefs in let otherwise = ignore in for n = 0 to min lastpage (Array.length cdvi.base_dvi.Cdvi.pages) - 1 do ignore (scan_special_page otherwise cdvi (headers, xrefs) n); done; if !headers <> [] then Dev.add_headers (find_prologues !headers);; let unfreeze_glyphs cdvi dpi = let mag = float cdvi.base_dvi.Cdvi.preamble.Dvicommands.pre_mag /. 1000.0 in let sdpi = Misc.round (mag *. ldexp dpi 16) and mtable = ref dummy_mtable and gtable = ref dummy_gtable in let otherwise = function | Dvicommands.C_fnt n -> let (mt, gt) = try let cfont = Table.get cdvi.font_table n in (cfont.mtable, get_gtable cfont sdpi) with Not_found -> (dummy_mtable, dummy_gtable) in mtable := mt; gtable := gt | Dvicommands.C_set code -> begin try ignore (Table.get !mtable code) with _ -> () end; begin try ignore (Table.get !gtable code) with _ -> () end | _ -> () in let headers = ref [] and xrefs = cdvi.base_dvi.Cdvi.xrefs in let globals = headers, xrefs in for n = 0 to Array.length cdvi.base_dvi.Cdvi.pages - 1 do mtable := dummy_mtable; gtable := dummy_gtable; ignore (scan_special_page otherwise cdvi globals n); done; Dev.add_headers (find_prologues !headers);;