123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559(**************************************************************************)(* *)(* Copyright (C) Johannes Kanig, Stephane Lescuyer *)(* Jean-Christophe Filliatre, Romain Bardou and Francois Bobot *)(* *)(* This software is free software; you can redistribute it and/or *)(* modify it under the terms of the GNU Library General Public *)(* License version 2.1, with the special exception on linking *)(* described in file LICENSE. *)(* *)(* This software 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. *)(* *)(**************************************************************************)openPoint_libopenMatrixmoduleP=Picture_libmoduleS=Spline_libopenDviinterpopenConcrete_typesletfprintf=Printf.fprintfletconversion=0.3937*.72.letpoint_of_cmcm=conversion*.cmletfloatfmtf=(* PDF does not understand e notation, so we protect the printf which
uses %g in the cases where this would use e notation; we do not need that
much precision anyway*)leta=abs_floatfinifclassify_floatf=FP_nanthen(* should be an error there is a bug somewhere to track *)fprintffmt"0"elseifa<0.0001thenfprintffmt"0"elseifa>=1.e04thenfprintffmt"%.4f"felsePrintf.fprintffmt"%.4g"fletreclistseppfmtl=matchlwith|[]->()|[x]->pfmtx|x::xs->pfmtx;sepfmt;listseppfmtxsletoptionseppfmto=matchowith|None->()|Somex->pfmtx;sepfmtletnothing_=()letspacefmt=fprintffmt" "typespecials_env={externalimages:(string*Matrix.t,int)Hashtbl.t;colors:(P.color,int)Hashtbl.t;count:intref;}letnew_specials_env()={externalimages=Hashtbl.create7;colors=Hashtbl.create17;count=ref0;}moduleMPS=structtypeline_cap=ButtCap|RoundCap|SquareCaplet_=ButtCaplet_=SquareCaptypeline_join=MiterJoin|RoundJoin|BevelJoinlet_=MiterJoinlet_=BevelJoinletmoveto_floatfmtxy=fprintffmt"%a %a moveto"floatxfloatyletlineto_floatfmtxy=fprintffmt"%a %a lineto"floatxfloatyletrlineto_floatfmtxy=fprintffmt"%a %a rlineto"floatxfloatyletlinetofmtp=lineto_floatfmtp.xp.yletmovetofmtp=moveto_floatfmtp.xp.yletrlinetofmtp=rlineto_floatfmtp.xp.yletlineto_floatpfmt(x,y)=lineto_floatfmtxyletcurvetofmtp1p2p3=fprintffmt"%a %a %a %a %a %a curveto"floatp1.xfloatp1.yfloatp2.xfloatp2.yfloatp3.xfloatp3.yletclose_pathfmt=fprintffmt"close_path"letnewpathfmt=fprintffmt"newpath"letstrokefmt=fprintffmt"stroke"letfillfmt=fprintffmt"fill"letshowpagefmt=fprintffmt"showpage"letclipfmt=fprintffmt"clip"letgsavefmt=fprintffmt"gsave"letgrestorefmt=fprintffmt"grestore"letsetlinewidthfmtf=(* handle strange treatment of linewidth of Metapost *)fprintffmt"0 %a dtransform truncate idtransform setlinewidth pop"floatfletsetlinecapfmtc=leti=matchcwithButtCap->0|RoundCap->1|SquareCap->2infprintffmt"%d setlinecap"iletsetlinejoinfmtj=leti=matchjwithMiterJoin->0|RoundJoin->1|BevelJoin->2infprintffmt"%d setlinejoin"iletmatrixfmtt=fprintffmt"[%a %a %a %a %a %a]"floatt.xxfloatt.yxfloatt.xyfloatt.yyfloatt.x0floatt.y0lettransformfmtt=ift=Matrix.identitythen()elsefprintffmt"%a concat"matrixtletscolor_rgbfmtrgb=fprintffmt"%a %a %a setrgbcolor"floatrfloatgfloatbletscolor_cmykfmtcmyk=fprintffmt"%a %a %a %a setcmykcolor"floatcfloatmfloatyfloatkletscolor_grayfmtc=fprintffmt"%a setgray"floatcletscolorfmtc=matchcwith|Concrete_types.RGB(r,g,b)->scolor_rgbfmtrgb|Concrete_types.CMYK(c,m,y,k)->scolor_cmykfmtcmyk|Concrete_types.Grayc->scolor_grayfmtcletcolorfmtc=matchcwith|Concrete_types.OPAQUEc->scolorfmtc|Concrete_types.TRANSPARENT_->(* harvest take care of that case *)assertfalseletdvi_colorfmtc=matchcwith|Dviinterp.RGB(r,g,b)->scolor_rgbfmtrgb|Dviinterp.CMYK(c,m,y,k)->scolor_cmykfmtcmyk|Dviinterp.HSB_->assertfalse|Dviinterp.Grayg->scolor_grayfmtgletdashfmt(offset,pattern)=fprintffmt"[%a ] %a setdash"(listspacefloat)patternfloatoffsetletchar_constfmtc=fprintffmt"\\%03lo"cletglyphfmtclfont=fprintffmt"(%a) %s %a fshow"(listnothingchar_const)cl(Fonts.tex_namefont)float(Fonts.scalefontconversion)letglyphpfmt(cl,font)=glyphfmtclfontletrectanglefmtpwh=fprintffmt"%t %a %a %a %a %t %t"newpathmovetoplineto_floatp(p.x+.w,p.y)lineto_floatp(p.x+.w,p.y+.h)lineto_floatp(p.x,p.y+.h)close_pathfillletrectanglepfmt(p,w,h)=rectanglefmtpwhendletin_contextfmtf=fprintffmt"%t %t %t"MPS.gsavefMPS.grestoreletfill_rectfmttransixywh=letx=point_of_cmxandy=point_of_cmyandw=point_of_cmwandh=point_of_cmhinletp={x;y}inin_contextfmt(fun_->fprintffmt"%a %a %a"MPS.transformtransMPS.dvi_colori.Dviinterp.colorMPS.rectanglep(p,w,h))letdraw_charfmttranstext=(* FIXME why do we need to negate y coordinates? *)letf1,f2=text.tex_posinletf1=point_of_cmf1andf2=point_of_cmf2inletp={x=f1;y=-.f2}inin_contextfmt(fun_->fprintffmt"%a %a %a %a"MPS.transformtransMPS.dvi_colortext.Dviinterp.tex_info.Dviinterp.colorMPS.movetopMPS.glyphp(text.tex_string,text.tex_font))(* FIXME why do we need to negate y coordinates? *)lettex_cmdfmttransc=matchcwith|Dviinterp.Fill_rect(i,x,y,w,h)->fill_rectfmttransix(-.y)wh|Dviinterp.Draw_texttext->draw_charfmttranstext|Dviinterp.Specials_->()|Dviinterp.Draw_text_type1_->assertfalseletdraw_texfmtt=listspace(funfmtx->tex_cmdfmtt.Gentex.transx)fmtt.Gentex.texletcurvetofmts=letsa,sb,sc,sd=Spline.explodesinifsa=sb&&sc=sdthenMPS.linetofmtsdelseMPS.curvetofmtsbscsdletpath=letpathfmt=function|S.Pathp->(matchp.S.plwith|[]->assertfalse|x::_asl->fprintffmt"%a %a"MPS.moveto(Spline.left_pointx)(listspacecurveto)l);ifp.S.cyclethenfprintffmt" %t"MPS.close_path|S.Pointp->fprintffmt"%a %a"MPS.movetopMPS.rlinetopinfunfmtp->fprintffmt"%t %a"MPS.newpathpathpletpenfmtt=(* FIXME do something better *)(* for now assume that the pen is simply a scaled circle, so just grab the xx
* value of the matrix and use that as linewidth *)MPS.setlinewidthfmtt.xxletspecials_signal=0.123letspecials_division=1000.(** map real color to encoded color :
- identity for four first case
- encoding for transparency
- encoding specials rgb opaque color
*)letadd_color_seclrse=matchclrwith|None->clr|Some(OPAQUE(Gray_))->clr|Some(OPAQUE(CMYK_))->clr|Some(OPAQUE(RGB(r,_,_)))whenr<>specials_signal->clr|Someclr->letnb=tryHashtbl.findse.colorsclrwithNot_found->incrse.count;letnb=!(se.count)inHashtbl.addse.colorsclrnb;nbinletnb=float_of_intnb/.specials_divisioninSome(OPAQUE(RGB(specials_signal,0.003,nb)))letadd_image_se=letdumb_path=Spline_lib.create_lines[Point_lib.zero;Point_lib.zero;Point_lib.zero;Point_lib.zero]inletdumb_path=Spline_lib.closedumb_pathinfunpse->letnb=tryHashtbl.findse.externalimagespwithNot_found->incrse.count;letnb=!(se.count)inHashtbl.addse.externalimagespnb;nbin(* 0.010? *)letnb=float_of_intnb/.specials_divisioninletc=Some(OPAQUE(RGB(specials_signal,0.019,nb)))inP.Fill_path(dumb_path,c)letrecharvestse=function|P.Emptyasp->p|P.OnTopl->letaddacce=letp=harvestseeinifp=P.Emptythenaccelsep::accinletl=List.fold_leftadd[]linifl=[]thenP.EmptyelseP.OnTop(List.revl)|P.Stroke_path(p,c,d,e)->P.Stroke_path(p,add_color_secse,d,e)|P.Fill_path(p,c)->P.Fill_path(p,add_color_secse)|P.Tex_asp->p|P.Transform(m,t)->harvestse(P.apply_transform_cmdsmt)|P.Clip(com,p)->letcom=harvestsecominifcom=P.EmptythencomelseP.Clip(com,p)|P.ExternalImage(f,_,m)->add_image_se(f,m)se(*
For specials in mps
The specials are described at the bottom of the preamble
The first line describe the version, the special signal and the special_div
%%MetaPostSpecials: 2.0 123 1000
The next describe specials : length data special_number special_type
%%MetaPostSpecial: 7 1 0.5 1 0 0 1 3
Color cmyk : 7 (cmyk_counter) c m y k special_number 1
Color spot : 2
Color rgba : 7 mode_transparency value_transparency r g b special_number 3
Color cmyka : 8 mode_transparency value_transparency c m y k special_number 4
Color spota : 8 mode_transparency value_transparency ? ? ? ? special_number 5
In the text they appear as color :
special_signal (1 cmyk 2 spot 3 rgb) special_number
0.123 0.003 0.001 setrgbcolor
*)letprint_specials_color=letpr_colorfmtc=matchcwith|RGB(r,b,g)->fprintffmt"%f %f %f"rgb|Grayg->fprintffmt"%f %f %f"ggg|CMYK(c,m,y,k)->fprintffmt"%f %f %f %f"cmykinfunfmtclid->lettrans,c=matchclwithOPAQUEc->(1.,c)|TRANSPARENT(a,c)->(a,c)inletmode,special_type=matchcwithRGB_|Gray_->(7,3)|CMYK_->(8,4)infprintffmt"%%%%MetaPostSpecial: ";fprintffmt"%i 1 %f %a %i %i\n"modetranspr_colorcidspecial_typeletprint_specials_extimgfmt(f,m)id=fprintffmt"%%%%MetaPostSpecial: 9 %f %f %f %f %f %f %s %i 10\n"m.xxm.yxm.xym.yym.x0m.y0fidletprint_specialsfmtcx=letse=new_specials_env()inletcx=harvestsecxinifHashtbl.lengthse.colors<>0||Hashtbl.lengthse.externalimages<>0then(fprintffmt"%%%%MetaPostSpecials: 2.0 %i %i\n"(int_of_float(specials_signal*.specials_division))(int_of_floatspecials_division);Hashtbl.iter(print_specials_colorfmt)se.colors;Hashtbl.iter(print_specials_extimgfmt)se.externalimages);cxletrecpicturefmtp=matchpwith|P.Empty->()|P.OnTopl->listspacepicturefmtl|P.Stroke_path(pa,clr,pe,da)->in_contextfmt(fun_->fprintffmt"%a%a%a %a %t\n"(optionspaceMPS.color)clr(optionspaceMPS.dash)dapenpepathpaMPS.stroke)|P.Fill_path(p,clr)->in_contextfmt(fun_->fprintffmt"%a %a %t\n"(optionspaceMPS.color)clrpathpMPS.fill)|P.Text->draw_texfmtt|P.Clip(com,p)->in_contextfmt(fun_->fprintffmt"%a %t %a"pathpMPS.clippicturecom)|P.Transform_|P.ExternalImage_->assertfalsemoduleBitMap=struct(* FIXME replace me by something more efficient *)(* encode our bitmap as a string (array of chars) *)typet=Bytes.t(* a char '0' corresponds to '0', a char '1' corresponds to '1' *)letmkn:t=Bytes.maken'0'letsettn=Bytes.settn'1'let_gettn=t.[n]letmint=tryBytes.indext'1'withNot_found->assertfalseletsafe_sub_foursi=(* if the string does not have 4 remaining chars, pad with zeros *)letmy_len=4inletl=Bytes.lengthsinifi+my_len<=lthenBytes.subsimy_lenelseletbuf=Bytes.makemy_len'0'inforj=itol-1doBytes.setbuf(j-i)(Bytes.getsj)done;bufletone_charti=matchBytes.to_string(safe_sub_fourti)with|"0000"->'0'|"0001"->'1'|"0010"->'2'|"0011"->'3'|"0100"->'4'|"0101"->'5'|"0110"->'6'|"0111"->'7'|"1000"->'8'|"1001"->'9'|"1010"->'a'|"1011"->'b'|"1100"->'c'|"1101"->'d'|"1110"->'e'|"1111"->'f'|_->assertfalseletcharst=letb=Buffer.create5inletrecauxk=letc=one_chartkinifc='0'thenBuffer.contentsbelse(Buffer.add_charbc;aux(k+4))inletm=mintinPrintf.sprintf"%x:%s"m(auxm)end(* FIXME do better than comparing font names *)moduleFontCmp=structtypet=Fonts.tletcompareab=String.compare(Fonts.tex_namea)(Fonts.tex_nameb)end(* module FS = Set.Make(FontCmp) *)moduleFM=Map.Make(FontCmp)letmax_charf=(Fonts.metricf).Tfm.file_hdr.Tfm.ecletfontsp=letx=refFM.emptyinPicture_lib.iter(funp->matchpwith|P.Texg->List.iter(func->matchcwith|Draw_texttext->letf=text.tex_fontinletmap=tryFM.findf!xwithNot_found->letmap=BitMap.mk(max_charf)inx:=FM.addfmap!x;mapinList.iter(funx->BitMap.setmap(Int32.to_intx))text.tex_string|_->())g.Gentex.tex|_->())p;!x(* Following the dvips manual, for example on
http://www.radicaleye.com/dvipsman/dvips.html#SEC34,
the Font line looks as follows:
%*Font: tfmname scaledbp designbp hex-start:hex-bitstring
Here is the meaning of each of these elements:
tfmname
The TeX TFM filename, e.g., `cmr10'. You can give the same tfmname on more
than one `%*Font' line; this is useful when the number of characters from
the font used needs a longer hex-bitstring (see item below) than
conveniently fits on one line.
scaledbp
The size at which you are using the font, in PostScript points (TeX big
points). 72bp = 72.27pt = 1in.
designbp
The designsize of the font, again in PostScript points. This should match
the value in the TFM file tfmname. Thus, for `cmr10', it should be
`9.96265'.
hex-start
The character code of the first character used from the font, specified as
two ASCII hexadecimal characters, e.g., `4b' or `4B' for `K'.
hex-bitstring
An arbitrary number of ASCII hexadecimal digits specifying which characters
following (and including) hex-start are used. This is treated as a bitmap.
For example, if your figure used the single letter `K', you would use
`4b:8' for hex-start and hex-bitstring. If it used `KLMNP', you would use
`4b:f4'.
*)letfontdeclfmtfmap=letn=Fonts.tex_namefinletd=Fonts.design_sizefinletr=point_of_cm(Fonts.ratio_cmf)inletmagic_string=BitMap.charsmapinfprintffmt"%%*Font: %s %f %f %s\n"ndrmagic_stringletdrawfmtx=let{x=minx;y=miny},{x=maxx;y=maxy}=Picture_lib.bounding_boxxinletminxt,minyt,maxxt,maxyt=(floorminx,floorminy,ceilmaxx,ceilmaxy)infprintffmt"%%!PS\n";fprintffmt"%%%%BoundingBox: %f %f %f %f\n"minxtminytmaxxtmaxyt;fprintffmt"%%%%HiResBoundingBox: %f %f %f %f\n"minxminymaxxmaxy;fprintffmt"%%%%Creator: Mlpost %s\n"Mlpost_version.version;(* metapost adds a creation date but this breaks determinism *)(* fprintf fmt "%%%%CreationDate: %s\n" (Misc.date_string ()); *)fprintffmt"%%%%Pages: 1\n";FM.iter(fontdeclfmt)(fontsx);fprintffmt"%%%%BeginProlog\n";fprintffmt"%%%%EndProlog\n";fprintffmt"%%%%Page: 1 1\n";letcx=print_specialsfmt(P.contentx)infprintffmt"%a %a %a %a %t"MPS.setlinewidth(P.default_line_size/.2.)MPS.setlinecapMPS.RoundCapMPS.setlinejoinMPS.RoundJoinpicturecxMPS.showpage;fprintffmt"\n%%%%EOF\n"letgenerate_onefnfig=File.write_tofn(funfmt->letfig=Compute.commandpicfigin(* Format.printf "picturelib code: \n %a@." P.Print.pic fig; *)drawfmtfig);fnletmpsfigl=List.map(fun(fn,fig)->letfn=File.mkfn"mps"in(* Format.printf "metapost code:\n %a@."Print.commandpic fig; *)generate_onefnfig)figlletdump()=ignore(mps(Defaults.emited()))letgeneratefigs=ignore(mpsfigs)