XMCD file import

Jon McLoone

Intoduction

The following code is a framework for reading MathCAD .XMCD files into Mathematica.
It is intended for content conversion only and does not attempt to translate the MathCAD language into the Mathematica equivelent, although it will do trivial function name substitution to the closest Mathematica equivelent e.g acos->ArcCos etc.

The code is incomplete and does not attempt to preserve formatting such as font choices etc though it could be extended. It may not cover all tags used in the XMCD format but additional tags and their translations can be easily added.

Binary content in the XMCD format is not supported. This appears to mostly contain rendered bitmap images, but also contains plot commands.

The code is provided "as seen" and without support, and is not currently under active development.

Code

Package start

BeginPackage["XMCDConvert`"];

ImportXMCD::usage="ImportXMCD[path] reads the .xmcd file at path and converts its contents to a Notebook object.";

OpenXMCD::usage="OpenXMCD[path] reads the .xmcd file at path and opens a new window with the contents of the file converted to Mathematica format.";

ImportXMCD::unknowntag="XMLTag `1` not recognized. Discarding contents: `2`";

Begin["`private`"];

The basic idea is to map XMLElements to Notebook elements by repeated application of pattern matching rules.

Remove XMLElements that either do not contain useful information, or which are not yet supported but may be in future. All data with these tags is discarded.

discardparts={"metadata","settings","binaryContent"};
discardrules=Map[(XMLElement[#,___]->Sequence[])&,discardparts];

Text formatting

ProcessText is intended to turn handle formatted text input.

ProcessText[txt_]:=txt/.XMLElement["p",{opts___Rule},para_List]:>Apply[Sequence,Map[StyleBox[#,ProcessOptions[opts]]&,para]];

Convert attribute info into Mathematica options.

ProcessOptions[___]:=Sequence[];(*Currently no formatting options are supported so this function discards any attributes. Future improvement would add conversion rules to this function*)

Document structure rules

transformrules={
XMLElement[{schema_,tag_},props_List,contents_List]->XMLElement[tag,props,contents],
(*We can discard the schema name where explicitly given*)

XMLElement["worksheet",_,c_]->Notebook[c],
(*Worksheet is the outer wrapper. Convert it to a Notebook object*)
XMLElement["regions",{},{n___}]->n,
(*Flatten regions. Could be used to make CellGroup?*)
XMLElement["region",{opts___Rule},{XMLElement["text",{opts2___Rule},txt_]}]:>Cell[TextData[ProcessText[txt]],"Text",ProcessOptions[opts,opts2]],
(*region is equivelent to a text cell*)

XMLElement["b",{},{txt_String}]->StyleBox[txt,FontWeight->"Bold"],
(*b=bold*)

XMLElement["c",{col___},{txt_}]:>txt,
(*Probably font color, not yet supported*)

XMLElement["i",{},{txt_}]:>StyleBox[txt,FontSlant->"Italic"],
(*i=italic*)
XMLElement["sp",{},{}]->" ",
XMLElement["sp",{"count"->n_},{}]:>Apply[StringJoin,Table[" ",{ToExpression[n]}]],
(*sp= one ore more spaces according to the count attribute*)

XMLElement["region",{opts___Rule},{XMLElement["math",{otps2___Rule},mth_],XMLElement["rendering",{"item-idref"->n_},{}]}]:>Cell[BoxData[ProcessMath[mth]],"Input"]
(*math is typest math*)
(*rendering appears to be a bitmap of the content which we can discard*)

(*Apply[Sequence,{Cell[BoxData[ProcessMath[mth]],"Input",FontColor->RGBColor[1,0,0]],
Cell[ToString[ProcessRender[n]],"Output"]}]*),

XMLElement["f",{opts___Rule},{txt_}]:>StyleBox[txt,ProcessOptions[opts]]
(*Font family? Discard for now.*),


XMLElement["inlineAttr",{opts___Rule},{txt_}]:>StyleBox[txt,ProcessOptions[opts]],
(*Discardable attributes*)

StyleBox[StyleBox[face_,opts___],opts2___]:>StyleBox[face,opts,opts2],
StyleBox[face_]:>face,
(*Clean up nested StyleBoxes in the output*)

{a___,b_,XMLElement["sup",{},{c_}],d___}->{a,Cell[BoxData[SuperscriptBox[b,c]]],d},
{a___,b_,XMLElement["sub",{},{c_}],d___}->{a,Cell[BoxData[SubscriptBox[b,c]]],d},
(*Superscript and subscript tags*)
XMLElement["sup",{},{c_}]->c,(*Could be wrong, occurs in repeated sup tags, coule be nested?*)
XMLElement["sub",{},{c_}]->c,(*Could be wrong, occurs in repeated sup tags, coule be nested?*)


XMLObject["Document"][_,x_,{}]->x};

Math contents

Basic function name mappings, where similar function exists.

MapFunctionName[expr_]:=expr/.{"acos"->"ArcCos","acosh"->"ArcCosh","arg"->"Arg","asin"->"ArcSin","asinh"->"ArcSinh","tr"->"Tr","atan"->"ArcTan","atanh"->"ArcTanh","augment"->"RowJoin","C"->"ℭ","ceil"->"Ceiling","cfft"->"Fourier","CFFT"->"Fourier","cvar"->"Covariance","cos"->"Cos","cosh"->"Cosh","cot"->"Cot","coth"->"Coth","csc"->"Csc","csch"->"Csch","D"->"𝔇","diag"->"DiagonalMatrix","eigenvals"->"Eigenvalues","eigenvecs"->"Eigenvectors","erf"->"Erf","exp"->"Exp","fft"->"Fourier","FFT"->"Fourier","floor"->"Floor","FRAME"->"MCFrame","identity"->"IdentityMatrix","if"->"If","In"->"BesselI","icfft"->"InverseFourier","ICFFT"->"InverseFourier","Jn"->"BesselJ","Kn"->"BesselK","length"->"Length","linfit"->"Fit","ln"->"Log","lsolve"->"LinearSolve","min"->"Min","max"->"Max","mean"->"Mean","median"->"Median","mod"->"Mod","N"->"𝔑","re"->"Re","reverse"->"Reverse","rows"->"Length","rref"->"RowReduce","sech"->"Sech","sin"->"Sin","sinh"->"Sinh","sort"->"Sort","stack"->"ColumnJoin","stdev"->"StandardDeviation","svd"->"SingularValues","svds"->"SingularValues","tan"->"Tan","tanh"->"Tanh","until"->"Until","var"->"Variance","Yn"->"BesselY",XMLElement["absval",{},{}]->"Abs"};

Convert math typesetting rules. Need to handle MathML too.

ProcessMath[mth_]:=RowBox[mth//.{


XMLElement["id",{___,"subscript"->sub_,___},{lab_}]:>SubscriptBox[lab,sub],
(*Subscripted variable name*)
XMLElement["id",_,{lab_}]:>lab,
(*Variable name*)
XMLElement["real",_,{n_}]:>n,
(*Number*)
XMLElement["define",_,{a_,b_}]:>RowBox[{a,":=",b}],
(*Function definition*)

XMLElement["apply",_,{XMLElement["derivative",{},{}],XMLElement["lambda",{},{XMLElement["boundVars",{},{wrt_}],arg_}],XMLElement["degree",{},{n_}]}]:>RowBox[{SubscriptBox["∂",RowBox[{"{",RowBox[{wrt,",",n}],"}"}]],arg}],
(*Derivative operator*)

XMLElement["apply",_,{XMLElement["derivative",{},{}],XMLElement["lambda",{},{XMLElement["boundVars",{},{wrt_}],arg_}]}]:> RowBox[{  SubscriptBox["∂", wrt],    arg}],
(*Derivative operator*)

XMLElement["apply",_,{XMLElement["mult",_,{}],a_,b_}]:>RowBox[{a,"×",b}],
(*Times operator*)
XMLElement["apply",_,{XMLElement["plus",_,{}],a_,b_}]:>RowBox[{a,"+",b}],
(*Plus operator*)

XMLElement["apply",_,{XMLElement["div",_,{}],a_,b_}]:>FractionBox[a,b],
(*Fraction operator*)

XMLElement["apply",_,{XMLElement["pow",_,{}],a_,b_}]:>SuperscriptBox[a,b],
(*Power operator*)

XMLElement["parens",_,{args___}]:>RowBox[{"(",args,")"}],
(*Paranthesis operator*)

XMLElement["apply",_,{XMLElement["minus",_,{}],a_,b_}]:>RowBox[{a,"-",b}],
(*Minus operator*)

XMLElement["apply",_,{XMLElement["sqrt",_,{}],arg_}]:>SqrtBox[arg],
(*Square root operator*)
XMLElement["sqrt",_,{}]->"√",
(*Square root operator*)

XMLElement["apply",_,{XMLElement["equal",{},{}],lhs_,rhs_}]:>RowBox[{lhs,"==",rhs}],
(*Equation operator*)

XMLElement["function",_,{fn_,XMLElement["boundVars",_,{vars___}]}]:>RowBox[{fn,"[",vars,"]"}],
(*Function operator*)

XMLElement["neg",_,{}]->"-",
(*Negative *)

XMLElement["eval",_,{arg__}]->arg,
(*Evaluation operator*)

XMLElement["sequence",_,{a_,b_}]:>RowBox[{a,",",b}],
(*Sequence of argumants*)

XMLElement["sequence",_,{a_,b_,c__}]:>RowBox[Rest[Flatten[Map[{",",#}&,{a,b,c}]]]],
XMLElement["range",_,{a_,b_}]:>RowBox[{a,"…",b}],
(*Range operator*)

XMLElement["apply",_,{XMLElement["lessThan",{},{}],a_,b_}]:>RowBox[{a,"<",b}],
(*Less than operator*)
XMLElement["apply",_,{XMLElement["lessOrEqual",{},{}],a_,b_}]:>RowBox[{a,"≤",b}],
(*LessEqual than operator*)
XMLElement["apply",_,{XMLElement["greaterThan",{},{}],a_,b_}]:>RowBox[{a,">",b}],
(*Greater  operator*)
XMLElement["apply",_,{XMLElement["greaterOrEqual",{},{}],a_,b_}]:>RowBox[{a,"≥",b}],
(*GreaterEqual  operator*)




XMLElement["provenance",_,{metadata___,face_}]->face,
(*Meta data - discard*)
XMLElement["resultFormat",_,_]:>Apply[Sequence,{}]
(*This one is probably wrong. Could be formatting control for output?*),

XMLElement["matrix",{"rows"->r_,"cols"->c_},{args___}]:>RowBox[{"(",GridBox[Partition[{args},ToExpression[r]]],")"}],
(*Matrix *)

XMLElement["apply",_,{XMLElement["summation",{},{}],XMLElement["lambda",_,{XMLElement["boundVars",{},{n_}],arg_}],XMLElement["bounds",_,{lo_,hi_}]}]:>RowBox[{UnderoverscriptBox["∑",RowBox[{n,"=",lo}],hi],arg}],
(*Sum operator*)

XMLElement["apply",_,{XMLElement["indexer",{},{}],x_,i_}]:>SubscriptBox[x,i],
(*Subscript operator*)

XMLElement["program",_,prog_]:>RowBox[{"Block","[",RowBox[{"{",RowBox[{GetLocalVariables[prog]}],"}"}],",",RowBox[Rest[Flatten[Map[{";",#}&,prog]]]],"]"}],
XMLElement["localDefine",_,{var_,val_}]:>RowBox[{var,"=",val,";","\n"}],
(*Program block with local variables*)

XMLElement["apply",_,{XMLElement["integral",_,{}],XMLElement["lambda",_,{XMLElement["boundVars",{},{wrt_}],args_}],XMLElement["bounds",_,{lo_,hi_}]}]:>RowBox[{SubsuperscriptBox["∫",lo,hi],RowBox[{args}],"\[DifferentialD]",wrt}],
(*Integral operator*)


XMLElement["imag",_,{"1"}]:>"I",
XMLElement["imag",_,{n_}]:>RowBox[{n," ","I"}],
(*Imaginary I*)

XMLElement["ifThen",_,{test_,then_}]:>RowBox[{"If","[",test,",",then,"]"}],
(* If statement*)

XMLElement["apply",_,{fn_,arg_}]:>RowBox[{MapFunctionName[fn],"[",arg,"]"}],

XMLElement["result",_,{val_}]->RowBox[{"=",val}]
(*Tag for the result of calculations*)
}

];

GetLocalVariables[prog_]:=RowBox[Rest[Flatten[Map[{",",#}&,Union@Cases[prog,XMLElement["localDefine",{},{var_,_}]->var,∞]]]]];

Binary content (not yet solved but believed to contain mostly rendered versions of typesetting and pasted images)

renderingrules={XMLElement["region",{opts___Rule},{XMLElement["metafile",{opts2___Rule},{}],XMLElement["rendering",{"item-idref"->n_},{}]}]:>Cell[ToString[ProcessRender[n]],"Text"],
    
    XMLElement["region",{opts___Rule},{XMLElement["plot",{opts2___Rule},{}],XMLElement["rendering",{"item-idref"->n_},{}]}]:>Cell["Plot "<>ToString[ProcessRender[n]],"Text"],
    
    XMLElement["region",{opts___Rule},{XMLElement["ole",{oleopts___Rule},{}],XMLElement["rendering",{"item-idref"->n_},{}]}]:>Cell["OLE Object "<>ToString[ProcessRender[n]],"Text"],}

ProcessBinaryParts[data_]:=Block[{dat=Cases[data,XMLElement["binaryContent",{},n_]->n,{0,∞}]},dat]

The above rules are correct but since the ProcessRendering problem has not been solved, here are rules to discard the binary content references instead.

renderingrules={XMLElement["region",{opts___Rule},{XMLElement["metafile",{opts2___Rule},{}],XMLElement["rendering",{"item-idref"->n_},{}]}]->Sequence[],
XMLElement["region",{opts___Rule},{XMLElement["plot",{opts2___Rule},{}],XMLElement["rendering",{"item-idref"->n_},{}]}]->Sequence[],
XMLElement["region",{opts___Rule},{XMLElement["ole",{oleopts___Rule},{}],XMLElement["rendering",{"item-idref"->n_},{}]}]->Sequence[]};

End

ImportXMCD[path_]:=(Import[path,"XML"]//.
XMLElement[{url_,tag_},rest___]->XMLElement[tag,rest]/.
discardrules//.
transformrules//.
renderingrules//.
XMLElement[tag_,rest___]:>(Message[ImportXMCD::unknowntag,tag,{rest}];Cell["Unknown XML tag ("<>tag<>"). Contents discarded.","Message"]));

OpenXMCD[path_]:=NotebookPut[ImportXMCD[path]];

End[];

EndPackage[];

Usage

The ImportXMCD and OpenXMCD files can both operate on local files ...

?ImportXMCD

SetDirectory["C:\\Documents and Settings\\jonm\\My Documents\\ObsoleteSoftware\\MathCAD\\"];

ImportXMCD["Weave_Shield.xmcd"]

...or on remote URLs...

?OpenXMCD

OpenXMCD["http://www.coe.montana.edu/ee/andyo/EE445/filterdistortion.xmcd","XML"]