123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390(**************************************************************************)(* *)(* This file is part of OcamlGraph. *)(* *)(* Copyright (C) 2009-2010 *)(* CEA (Commissariat � l'�nergie Atomique) *)(* *)(* you can redistribute it and/or modify it under the terms of the GNU *)(* Lesser General Public License as published by the Free Software *)(* Foundation, version 2.1, with a linking exception. *)(* *)(* It is distributed in the hope that it will be useful, *)(* but WITHOUT ANY WARRANTY; without even the implied warranty of *)(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *)(* GNU Lesser General Public License for more details. *)(* *)(* See the file ../LICENSE for more details. *)(* *)(* Authors: *)(* - Julien Signoles (Julien.Signoles@cea.fr) *)(* - Jean-Denis Koeck (jdkoeck@gmail.com) *)(* - Benoit Bataille (benoit.bataille@gmail.com) *)(* *)(**************************************************************************)(* This module parses _draw_ attributes in XDot *)typepos=float*floattypewidth=floattypeheight=floattypesize=inttypealign=Left|Center|Righttypestyle_attr=|Filled|Invisible|Diagonals|Rounded|Dashed|Dotted|Solid|Bold|StyleStringofstring(* Drawing operations *)typeoperation=|Filled_ellipseofpos*width*height|Unfilled_ellipseofpos*width*height|Filled_polygonofposarray|Unfilled_polygonofposarray|Polylineofposarray|Bsplineofposarray|Filled_bsplineofposarray|Textofpos*align*width*string|Fill_colorofstring|Pen_colorofstring|Fontoffloat*string|Styleofstyle_attrlist(* Drawing state *)typedraw_state={mutablefill_color:string;mutablepen_color:string;mutablefont:float*string;mutablestyle:style_attrlist}letdefault_draw_state()={fill_color="#FFFFFF";pen_color="#000000";font=0.,"";style=[]}letset_fill_colorstc=st.fill_color<-cletset_pen_colorstc=st.pen_color<-cletset_fontstc=st.font<-cletset_stylests=st.style<-s(* STRING OPERATIONS *)letsuffixsi=tryString.subsi(String.lengths-i)withInvalid_argument_->""(** Splits a string with a separator
returns a list of strings *)letsplitcs=letrecsplit_fromn=tryletp=String.index_fromsncin(String.subsn(p-n))::(split_from(p+1))withNot_found->[suffixsn]inifs=""then[]elsesplit_from0;;letstring_scale_size~(fontMeasure:fontName:string->fontSize:int->string->(int*int))fontsizes=letwidth,height=fontMeasure~fontName:font~fontSize:(int_of_floatsize)sinletwidth=floatwidthinletlinear_width=size*.(float(String.lengths))insize*.width/.linear_width,floatheight(* HSV TO RGB CONVERSION *)(* If color string in hsv format, convert to hex *)letnormalize_colors=tryleth,s,v=Scanf.sscanfs"%f %f %f"(funabc->(a,b,c))inleth'=360.*.h/.60.inlethi=truncateh'mod6inletf=h'-.floorh'inletp=v*.(1.-.s)inletq=v*.(1.-.f*.s)inlett=v*.(1.-.(1.-.f)*.s)inletr,g,b=matchhiwith|0->v,t,p|1->q,v,p|2->p,v,t|3->p,q,v|4->t,p,v|5->v,p,q|_->1.,1.,1.inletto_hexx=Printf.sprintf"%02X"(truncate(x*.255.))in"#"^to_hexr^to_hexg^to_hexbwithScanf.Scan_failure_->s(* PARSE STATE *)typestate={mutableoperations:operationlist;mutablecur:int;str:string;}exceptionParseErrorofstringexceptionNoOperationIdletmk_states={operations=[];cur=0;str=s}letcharstate=state.str.[state.cur]letincrstate=state.cur<-state.cur+1(* No more characters *)letoverstate=state.cur>=String.lengthstate.strletadd_operationistate=state.operations<-i::state.operations(* GET TOKENS *)letget_nnst=lets=String.subst.strst.curninst.cur<-st.cur+n;sletis_space=function|' '|'\t'|'\n'->true|_->falseletis_token=function|"E"|"e"|"P"|"p"|"L"|"B"|"b"|"T"|"C"|"c"|"F"|"S"->true|_->falseletskip_spacesstate=letrecloop()=ifnot(overstate)thenifis_space(charstate)thenbeginincrstate;loop()endinloop()(* Gets a word *)letget_wordstate=skip_spacesstate;letstart=state.curinletrecget'()=ifoverstatethenifstart=String.lengthstate.strthenNoneelseSome(String.substate.strstart(state.cur-start))elseifnot(is_space(charstate))thenbeginincrstate;get'()endelseSome(String.substate.strstart(state.cur-start))inget'()(* Gets a rendering or attribute operation *)letget_op_idstate=lettok=get_wordstateinmatchtokwith|None->raiseNoOperationId|Sometok'->ifis_tokentok'thentok'elseraiseNoOperationIdletget_intstate=matchget_wordstatewith|Somew->begin(*let w' = filter_int w in*)tryint_of_stringwwithFailure_->raise(ParseError"Cannot parse int")end|None->raise(ParseError"Cannot parse int")letget_floatstate=matchget_wordstatewith|Somew->begintryfloat_of_stringwwithFailure_->raise(ParseError"Cannot parse float")end|None->raise(ParseError"Cannot parse float")letget_posstate=tryletx0=get_floatstateinlety0=get_floatstatein(x0,y0)withParseError_->raise(ParseError"Cannot parse point in position")(* PARSING *)letget_anchorstate=leti=get_intstateinmatchiwith|-1->Left|0->Center|1->Right|_->raise(ParseError"Cannot parse anchor")letparse_bytesst=skip_spacesst;letn=get_intstinskip_spacesst;ifcharst<>'-'thenraise(ParseError"Cannot parse bytes")elsebeginincrst;get_nnstendletparse_ellipseconstrstate=(* pos width height *)letpos=get_posstateinletw=get_floatstateinleth=get_floatstateinconstr(pos,w,h)letinvert_y_pos(x,y)=(x,-.y)letparse_filled_ellipse=parse_ellipse(fun(p,w,h)->Filled_ellipse(invert_y_posp,w,h))letparse_unfilled_ellipse=parse_ellipse(fun(p,w,h)->Unfilled_ellipse(invert_y_posp,w,h))letparse_pointsstate=letn=get_intstateinArray.initn(fun_->invert_y_pos(get_posstate))letparse_filled_polygonstate=Filled_polygon(parse_pointsstate)letparse_unfilled_polygonstate=Unfilled_polygon(parse_pointsstate)letparse_polylinestate=Polyline(parse_pointsstate)letparse_bsplinestate=Bspline(parse_pointsstate)letparse_filled_bsplinestate=Filled_bspline(parse_pointsstate)letparse_textstate=letpos=invert_y_pos(get_posstate)inletanchor=get_anchorstateinletwidth=get_floatstateinletstr=parse_bytesstateinText(pos,anchor,width,str)letparse_fill_colorstate=Fill_color(normalize_color(parse_bytesstate))letparse_pen_colorstate=Pen_color(normalize_color(parse_bytesstate))letparse_fontstate=letsize=get_floatstateinletfont=parse_bytesstateinFont(size,font)letparse_stylestate=letread=function|"filled"->Filled|"invisible"->Invisible|"diagonals"->Diagonals|"rounded"->Rounded|"dashed"->Dashed|"dotted"->Dotted|"solid"->Solid|"bold"->Bold|s->StyleStringsinletstr=parse_bytesstateinStyle(List.mapread(split','str))letparse_operationstate=letoperation()=matchget_op_idstatewith|"E"->parse_filled_ellipsestate|"e"->parse_unfilled_ellipsestate|"P"->parse_filled_polygonstate|"p"->parse_unfilled_polygonstate|"L"->parse_polylinestate|"B"->parse_bsplinestate|"b"->parse_filled_bsplinestate|"T"->parse_textstate|"C"->parse_fill_colorstate|"c"->parse_pen_colorstate|"F"->parse_fontstate|"S"->parse_stylestate|_->raise(ParseError"Cannot parse operation")intryadd_operation(operation())statewithNoOperationId->()letparse_with_statestate=letrecloop()=parse_operationstate;ifoverstatethenstate.operationselseloop()intryList.rev(loop())withNoOperationId->List.revstate.operations(* Long drawing operation strings sometimes contain useless backslashes
We get rid of them to avoid problems with the parser *)letremove_backslashess=letbuf=Buffer.create30inletrecloopi=ifi=String.lengthsthen()elseifs.[i]='\\'&&i<String.lengths-1&&s.[i+1]='\n'thenloop(i+2)elsebeginBuffer.add_charbufs.[i];loop(i+1)endinloop0;Buffer.contentsbufletparses=parse_with_state(mk_state(remove_backslashess))letdraw_with(f:draw_state->operation->unit)operations=letst=default_draw_state()inletdraw_op=function(* The 4 following instructions modify the drawing state *)|Fill_colorcasop->set_fill_colorstc;fstop|Pen_colorcasop->set_pen_colorstc;fstop|Font(sty,font)asop->set_fontst(sty,font);fstop|Stylestysasop->set_styleststys;fstop(* No state effects on the other operations *)|op->fstopinList.iterdraw_opoperations(* let d1 = parse "c 5 -white C 5 -white P 4 0 0 0 409 228 409 228 0 " *)(* let d2 = parse "S 6 -filled c 9 -lightgrey C 9 -lightgrey P 4 8 72 8 365 101 365 101 72 " *)(* let d3 = parse "S 6 -filled c 5 -white C 5 -white E 65 314 27 18 " *)(* let d4 = parse "F 14.000000 11 -Times-Roman c 5 -black T 39 109 0 35 4 -LR_0 " *)(* let d5 = parse "S 6 -filled c 5 -white C 5 -white E 64 98 27 18 " *)(* let d6 = parse "S 5 -solid S 15 -setlinewidth(1) c 5 -black C 5 -black P 3 69 270 65 260 62 270 " *)(* let d7 = parse "S 6 -filled c 7 -salmon2 C 7 -salmon2 P 9 865 1177 877 1193 841 1200 760 1192 695 1178 700 1167 756 1161 810 1160 841 1165 " *)(* let d8 = parse "F 14.000000 17 -Helvetica-Outline c 5 -black T 529 1005 0 65 9 -Mini Unix " *)(* let d9 = parse "S 6 -filled c 11 -greenyellow C 11 -greenyellow P 10 1254 819 1263 834 1247 843 1197 841 1137 830 1110 817 1131 808 1177 805 121\ *)
(* 6 804 1238 809 " *)(* let d10 = parse "S 6 -filled c 11 -greenyellow C 11 -greenyellow P 10 255 282 264 297 248 306 198 304 138 293 111 280 132 271 178 268 217 267 239\\\n 272 " *)