MODULE SVGLoader;
IMPORT SVG, SVGColors, SVGGradients, SVGFilters, SVGRenderer, XML, XMLObjects, XMLLoader, Strings, Math, WMGraphics;
CONST OnOmittedParseDefault=0;
OnOmittedDontChange=1;
OnOmittedError=2;
TYPE
SVGLoader=OBJECT
VAR
ppi: LONGREAL;
state: SVG.State;
mainDocument: SVG.Document;
renderer: SVGRenderer.Renderer;
fePrev: SVGFilters.FilterElement;
sizeForced: BOOLEAN;
widthForced, heightForced: LONGINT;
PROCEDURE &New*;
BEGIN
sizeForced := FALSE;
END New;
PROCEDURE ForceSize(width, height: LONGINT);
BEGIN
sizeForced := TRUE;
widthForced := width;
heightForced := height;
END ForceSize;
PROCEDURE LoadRoot(e: XML.Element);
BEGIN
ppi := 90;
NEW(state);
SVG.InitDefaultStyle(state.style);
NEW(renderer);
LoadElement(e)
END LoadRoot;
PROCEDURE GetDocument():SVG.Document;
BEGIN
RETURN mainDocument;
END GetDocument;
PROCEDURE GetActualWidth(): SVG.Length;
BEGIN
RETURN state.viewport.width;
END GetActualWidth;
PROCEDURE GetActualHeight(): SVG.Length;
BEGIN
RETURN state.viewport.height;
END GetActualHeight;
PROCEDURE GetActualDiagonal(): SVG.Length;
BEGIN
RETURN Math.sqrt(SHORT( GetActualWidth()*GetActualWidth()+GetActualHeight()*GetActualHeight() )) / Math.sqrt(2.0);
END GetActualDiagonal;
PROCEDURE GetName(e: XML.Element):XML.String;
VAR name: XML.String;
BEGIN
name := e.GetName();
Strings.LowerCase(name^);
IF Strings.StartsWith2("svg:",name^) THEN name := Strings.Substring2(4,name^) END;
RETURN name;
END GetName;
PROCEDURE LoadElement(e: XML.Element);
VAR name: XML.String;
BEGIN
name := GetName(e);
IF name^ = "svg" THEN LoadSVG(e)
ELSIF name^ = "g" THEN LoadGroup(e)
ELSIF name^ = "desc" THEN SVG.Log("<desc />")
ELSIF name^ = "defs" THEN LoadDefs(e)
ELSIF name^ = "image" THEN LoadImage(e)
ELSIF name^ = "rect" THEN LoadRect(e)
ELSIF name^ = "circle" THEN LoadCircle(e)
ELSIF name^ = "ellipse" THEN LoadEllipse(e)
ELSIF name^ = "line" THEN LoadLine(e)
ELSIF name^ = "polyline" THEN LoadPoly(e, FALSE)
ELSIF name^ = "polygon" THEN LoadPoly(e, TRUE)
ELSIF name^ = "path" THEN LoadPath(e)
ELSE SVG.Log("Unknown element: "); SVG.Log(name^)
END
END LoadElement;
PROCEDURE LoadSubElements(e: XML.Element);
VAR contents: XMLObjects.Enumerator; content: ANY;
BEGIN
contents := e.GetContents();
WHILE contents.HasMoreElements() DO
content := contents.GetNext();
IF content IS XML.Element THEN LoadElement(content(XML.Element))
ELSE
END
END
END LoadSubElements;
PROCEDURE LoadSVG(e: XML.Element);
BEGIN
SVG.Log("<svg>");
state.Push();
LoadAttrLength(e, "x", state.viewport.x, 640.0, OnOmittedParseDefault, "0");
LoadAttrLength(e, "y", state.viewport.y, 480.0, OnOmittedParseDefault, "0");
LoadAttrLength(e, "width", state.viewport.width, 640.0, OnOmittedParseDefault, "100%");
LoadAttrLength(e, "height", state.viewport.height, 480.0, OnOmittedParseDefault, "100%");
IF mainDocument = NIL THEN
NEW(state.userToWorldSpace);
state.userToWorldSpace.SetIdentity();
IF sizeForced THEN
state.userToWorldSpace := state.userToWorldSpace.Scale(widthForced / state.viewport.width, heightForced / state.viewport.height);
state.viewport.width := widthForced;
state.viewport.height := heightForced;
END;
LoadViewBoxAttributes(e,state.userToWorldSpace, state.viewport.width, state.viewport.height);
state.target := SVG.NewDocument(state.viewport.width,state.viewport.height);
renderer.FillWhite(state);
mainDocument := state.target;
state.transparencyUsed := FALSE;
LoadSubElements(e);
ELSE
state.userToWorldSpace:= state.userToWorldSpace.Translate(state.viewport.x, state.viewport.y);
LoadViewBoxAttributes(e,state.userToWorldSpace, state.viewport.width, state.viewport.height);
LoadSubElements(e);
END;
state.Pop();
SVG.Log("</svg>")
END LoadSVG;
PROCEDURE LoadGroup(e: XML.Element);
BEGIN
SVG.Log("<g>");
state.Push();
LoadPaintAttributes(e);
LoadAttrTransform(e, "transform", state.userToWorldSpace);
LoadFilterAttributeBegin(e);
LoadSubElements(e);
LoadFilterAttributeEnd();
state.Pop();
SVG.Log("</g>")
END LoadGroup;
PROCEDURE LoadDefs(e: XML.Element);
VAR contents: XMLObjects.Enumerator; content: ANY;
defEl: XML.Element;
id, name: XML.String;
BEGIN
SVG.Log("<defs>");
contents := e.GetContents();
WHILE contents.HasMoreElements() DO
content := contents.GetNext();
IF content IS XML.Element THEN
defEl := content(XML.Element);
id := defEl.GetAttributeValue("id");
IF id # NIL THEN
name := GetName(defEl);
IF name^ = "lineargradient" THEN LoadLinearGradient(defEl, id)
ELSIF name^ = "radialgradient" THEN LoadRadialGradient(defEl, id)
ELSIF name^ = "filter" THEN LoadFilter(defEl, id)
END
END
ELSE
END
END;
SVG.Log("</defs>")
END LoadDefs;
PROCEDURE LoadLinearGradient(e: XML.Element; id: XML.String);
VAR gradient: SVGGradients.LinearGradient;
onOmitted: SHORTINT;
BEGIN
SVG.Log("<linearGradient>");
NEW(gradient);
IF LoadGradientParentAttribute(e, gradient) THEN onOmitted := OnOmittedDontChange
ELSE onOmitted := OnOmittedParseDefault END;
LoadAttrParsed(e, "gradientUnits", gradient.gradientUnits, onOmitted, "objectBoundingBox", SVG.ParseUnits);
LoadAttrParsed(e, "spreadMethod", gradient.spreadMethod, onOmitted, "pad", SVGGradients.ParseSpreadMethod);
LoadAttrTransform(e, "gradientTransform", gradient.transform);
LoadAttrLength(e, "x1", gradient.p1.x, GetActualWidth(), onOmitted, "0%");
LoadAttrLength(e, "y1", gradient.p1.y, GetActualHeight(), onOmitted, "0%");
LoadAttrLength(e, "x2", gradient.p2.x, GetActualWidth(), onOmitted, "100%");
LoadAttrLength(e, "y2", gradient.p2.y, GetActualHeight(), onOmitted, "0%");
LoadGradientStops(e,gradient);
renderer.gradients.AddGradient(gradient,id);
SVG.Log("</ linearGradient>");
END LoadLinearGradient;
PROCEDURE LoadRadialGradient(e: XML.Element; id: XML.String);
VAR gradient: SVGGradients.RadialGradient;
onOmitted: SHORTINT;
BEGIN
SVG.Log("<radialGradient>");
NEW(gradient);
IF LoadGradientParentAttribute(e, gradient) THEN onOmitted := OnOmittedDontChange
ELSE onOmitted := OnOmittedParseDefault END;
LoadAttrParsed(e, "gradientUnits", gradient.gradientUnits, onOmitted, "objectBoundingBox", SVG.ParseUnits);
LoadAttrParsed(e, "spreadMethod", gradient.spreadMethod, onOmitted, "pad", SVGGradients.ParseSpreadMethod);
LoadAttrTransform(e, "gradientTransform", gradient.transform);
LoadAttrLength(e, "cx", gradient.center.x, GetActualWidth(), onOmitted, "50%");
LoadAttrLength(e, "cy", gradient.center.y, GetActualHeight(), onOmitted, "50%");
LoadAttrLength(e, "r", gradient.radius, GetActualDiagonal(), onOmitted, "50%");
IF onOmitted=OnOmittedParseDefault THEN
gradient.focal.x := gradient.center.x;
gradient.focal.y := gradient.center.y;
END;
LoadAttrLength(e, "fx", gradient.focal.x, GetActualWidth(), OnOmittedDontChange, "0");
LoadAttrLength(e, "fy", gradient.focal.y, GetActualHeight(), OnOmittedDontChange, "0");
LoadGradientStops(e,gradient);
renderer.gradients.AddGradient(gradient,id);
SVG.Log("</radialGradient>");
END LoadRadialGradient;
PROCEDURE LoadGradientParentAttribute(e: XML.Element; gradient:SVGGradients.Gradient):BOOLEAN;
VAR parent: SVGGradients.Gradient;
linearGradient, linearParent: SVGGradients.LinearGradient;
radialGradient, radialParent: SVGGradients.RadialGradient;
href: XML.String;
BEGIN
href := e.GetAttributeValue("xlink:href");
IF href # NIL THEN
IF SVG.ParseURI(href,href) THEN
parent := renderer.gradients.GetGradient(href);
END
END;
IF parent#NIL THEN
IF (parent IS SVGGradients.LinearGradient) & (gradient IS SVGGradients.LinearGradient) THEN
linearParent := parent(SVGGradients.LinearGradient);
linearGradient := gradient(SVGGradients.LinearGradient);
linearGradient.CopyLinear(linearParent);
ELSIF (parent IS SVGGradients.RadialGradient) & (gradient IS SVGGradients.RadialGradient) THEN
radialParent := parent(SVGGradients.RadialGradient);
radialGradient := gradient(SVGGradients.RadialGradient);
radialGradient.CopyRadial(radialParent);
ELSE
gradient.Copy(parent);
END;
RETURN TRUE
ELSE
RETURN FALSE
END
END LoadGradientParentAttribute;
PROCEDURE LoadGradientStops(e: XML.Element; gradient: SVGGradients.Gradient);
VAR contents: XMLObjects.Enumerator; content: ANY;
foundStops: BOOLEAN;
name: XML.String;
BEGIN
foundStops := FALSE;
contents := e.GetContents();
WHILE contents.HasMoreElements() DO
content := contents.GetNext();
IF content IS XML.Element THEN
WITH content:XML.Element DO
name := GetName(content);
IF name^ = "stop" THEN
IF ~foundStops THEN
foundStops := TRUE;
gradient.ClearStops();
END;
LoadGradientStop(content(XML.Element), gradient)
ELSE
SVG.Warning("stop element expected, found element: ");
SVG.Warning(name^);
END
END
ELSE
END
END
END LoadGradientStops;
PROCEDURE LoadGradientStop(e: XML.Element; gradient: SVGGradients.Gradient);
VAR offset: SVG.Length;
color: SVG.Color;
colorStr: XML.String;
BEGIN
SVG.Log("<stop />");
LoadAttrNumber(e, "offset", offset, SVG.AllowPercentages, 1.0, OnOmittedError, "stop element omitted offset attribute");
colorStr := LoadAttribute(e,"stop-color");
IF colorStr = NIL THEN
color := SVGColors.Black;
END;
IF ~SVGColors.Parse(colorStr, color) THEN
color := SVGColors.Black;
SVG.Warning("stop element specifies invalid stop-color element");
SVG.Warning(colorStr^)
END;
LoadOpacity(e, "stop-opacity", color, OnOmittedParseDefault, "1");
gradient.AddStop(offset, color);
END LoadGradientStop;
PROCEDURE LoadFilter(e: XML.Element; id: XML.String);
VAR filter: SVGFilters.Filter;
BEGIN
SVG.Log("<filter>");
NEW(filter);
LoadAttrLength(e, "x", filter.window.x, GetActualWidth(), OnOmittedParseDefault, "-10%");
LoadAttrLength(e, "y", filter.window.y, GetActualHeight(), OnOmittedParseDefault, "-10%");
LoadAttrLength(e, "width", filter.window.width, GetActualWidth(), OnOmittedParseDefault, "120%");
LoadAttrLength(e, "height", filter.window.height, GetActualHeight(), OnOmittedParseDefault, "120%");
LoadFilterElements(e,filter);
renderer.filters.AddFilter(filter,id);
SVG.Log("</ filter>");
END LoadFilter;
PROCEDURE LoadFilterElements(e: XML.Element; filter: SVGFilters.Filter);
VAR contents: XMLObjects.Enumerator; content: ANY;
name: XML.String;
BEGIN
fePrev := NIL;
contents := e.GetContents();
WHILE contents.HasMoreElements() DO
content := contents.GetNext();
IF content IS XML.Element THEN
WITH content:XML.Element DO
name := GetName(content);
IF name^ = "feblend" THEN LoadFEBlend(content(XML.Element), filter)
ELSIF name^ = "feoffset" THEN LoadFEOffset(content(XML.Element), filter)
ELSIF name^ = "fecolormatrix" THEN LoadFEColorMatrix(content(XML.Element), filter)
ELSIF name^ = "fegaussianblur" THEN LoadFEGaussianBlur(content(XML.Element), filter)
ELSIF name^ = "femerge" THEN LoadFEMerge(content(XML.Element), filter)
ELSIF name^ = "feflood" THEN LoadFEFlood(content(XML.Element), filter)
ELSIF name^ = "feimage" THEN LoadFEImage(content(XML.Element), filter)
ELSIF name^ = "fetile" THEN LoadFETile(content(XML.Element), filter)
ELSE
SVG.Warning("fe* element expected, found element: ");
SVG.Warning(name^);
END
END
ELSE
END
END
END LoadFilterElements;
PROCEDURE LoadFECommonAttributes(e: XML.Element; fe: SVGFilters.FilterElement; filter: SVGFilters.Filter; loadIn: BOOLEAN);
VAR
result: SVG.String;
BEGIN
filter.rootElement := fe;
result := LoadAttribute(e, "result");
IF result#NIL THEN
filter.AddFilterElement(fe, result);
END;
IF loadIn THEN
LoadFilterInAttribute(e, filter, "in", fe.in)
END;
fe.x := filter.window.x;
fe.y := filter.window.y;
fe.width := filter.window.width;
fe.height := filter.window.height;
LoadAttrLength(e, "x", fe.x, GetActualWidth(), OnOmittedDontChange, "");
LoadAttrLength(e, "y", fe.y, GetActualHeight(), OnOmittedDontChange, "");
LoadAttrLength(e, "width", fe.width, GetActualWidth(), OnOmittedDontChange, "");
LoadAttrLength(e, "height", fe.height, GetActualHeight(), OnOmittedDontChange, "");
fePrev := fe
END LoadFECommonAttributes;
PROCEDURE LoadFEBlend(e: XML.Element; filter: SVGFilters.Filter);
VAR
fe: SVGFilters.feBlend;
BEGIN
SVG.Log("<feBlend />");
NEW(fe);
LoadFilterInAttribute(e, filter, "in2", fe.in2);
LoadAttrParsed(e,"mode",fe.mode,OnOmittedParseDefault,"normal",SVGFilters.ParseBlendMode);
LoadFECommonAttributes(e,fe,filter,TRUE)
END LoadFEBlend;
PROCEDURE LoadFEOffset(e: XML.Element; filter: SVGFilters.Filter);
VAR
fe: SVGFilters.feOffset;
BEGIN
SVG.Log("<feOffset />");
NEW(fe);
LoadAttrLength(e, "dx", fe.dx, filter.window.width, OnOmittedParseDefault, "0");
LoadAttrLength(e, "dy", fe.dy, filter.window.height, OnOmittedParseDefault, "0");
LoadFECommonAttributes(e,fe,filter,TRUE)
END LoadFEOffset;
PROCEDURE LoadFEColorMatrix(e: XML.Element; filter: SVGFilters.Filter);
VAR
fe: SVGFilters.feColorMatrix;
values: SVG.String;
BEGIN
SVG.Log("<feColorMatrix />");
NEW(fe);
LoadAttrParsed(e, "type", fe.type, OnOmittedError, "feColorMatrix omitted type attribute", SVGFilters.ParseColorMatrixType);
values := LoadAttribute(e, "values");
IF ~SVGFilters.ParseColorMatrixValues(values, fe.type, fe.matrix) THEN
SVG.Error("feColorMatrix has invalid values attribute:");
SVG.Error(values^)
END;
LoadFECommonAttributes(e,fe,filter,TRUE)
END LoadFEColorMatrix;
PROCEDURE LoadFEGaussianBlur(e: XML.Element; filter: SVGFilters.Filter);
VAR
fe: SVGFilters.feGaussianBlur;
value: SVG.String;
BEGIN
SVG.Log("<feGaussianBlur />");
NEW(fe);
value := LoadAttribute(e,"stdDeviation");
IF value = NIL THEN
fe.stdDeviationX := 0;
fe.stdDeviationY := 0;
ELSE
SVG.ParseLengthOptional2(value, ppi, filter.window.width, fe.stdDeviationX, fe.stdDeviationY)
END;
LoadFECommonAttributes(e,fe,filter,TRUE)
END LoadFEGaussianBlur;
PROCEDURE LoadFEMerge(e: XML.Element; filter: SVGFilters.Filter);
VAR
fe: SVGFilters.feMerge;
BEGIN
SVG.Log("<feMerge>");
NEW(fe);
LoadFEMergeNodes(e,fe,filter);
LoadFECommonAttributes(e,fe,filter,FALSE);
SVG.Log("</feMerge>")
END LoadFEMerge;
PROCEDURE LoadFEMergeNodes(e: XML.Element; fe: SVGFilters.feMerge; filter: SVGFilters.Filter);
VAR contents: XMLObjects.Enumerator; content: ANY;
name: XML.String;
in: SVGFilters.In;
first: BOOLEAN;
BEGIN
fePrev := NIL;
first := TRUE;
contents := e.GetContents();
WHILE contents.HasMoreElements() DO
content := contents.GetNext();
IF content IS XML.Element THEN
WITH content:XML.Element DO
name := GetName(content);
IF name^ = "femergenode" THEN
SVG.Log("<feMergeNode />");
LoadFilterInAttribute(content, filter, "in", in);
IF first THEN
fe.in := in;
first := FALSE
ELSE
fe.AddNode(in);
END
ELSE
SVG.Warning("feMergeNode element expected, found element: ");
SVG.Warning(name^);
END
END
ELSE
END
END;
IF first THEN
SVG.Warning("empty feMerge element. feMergeNode elements expected.");
END
END LoadFEMergeNodes;
PROCEDURE LoadFEFlood(e: XML.Element; filter: SVGFilters.Filter);
VAR
fe: SVGFilters.feFlood;
color: SVG.Color;
value: SVG.String;
BEGIN
SVG.Log("<feFlood />");
NEW(fe);
value := LoadAttribute(e, "flood-color");
IF value=NIL THEN value :=Strings.NewString("black") END;
IF ~SVGColors.Parse(value, color) THEN
SVG.Error("expected color, found:");
SVG.Log(value^);
END;
LoadOpacity(e,"flood-opacity",color, OnOmittedParseDefault, "1");
SVGColors.ColorToPixel(color,fe.pix);
LoadFECommonAttributes(e,fe,filter,FALSE)
END LoadFEFlood;
PROCEDURE LoadFEImage(e: XML.Element; filter: SVGFilters.Filter);
VAR
fe: SVGFilters.feImage;
href: SVG.String;
BEGIN
SVG.Log("<feImage />");
NEW(fe);
href := e.GetAttributeValue("xlink:href");
IF href = NIL THEN
SVG.Error("feImage element omitted xlink:href attribute");
RETURN
END;
fe.image :=WMGraphics.LoadImage(href^, TRUE);
IF fe.image = NIL THEN
SVG.Error("feImage element specifies invalid xlink:href attribute");
RETURN
END;
LoadFECommonAttributes(e,fe,filter,FALSE)
END LoadFEImage;
PROCEDURE LoadFETile(e: XML.Element; filter: SVGFilters.Filter);
VAR
fe: SVGFilters.feTile;
BEGIN
SVG.Log("<feTile />");
NEW(fe);
LoadFECommonAttributes(e,fe,filter,TRUE)
END LoadFETile;
PROCEDURE LoadFilterInAttribute(e: XML.Element; filter: SVGFilters.Filter; name: ARRAY OF CHAR; VAR in: SVGFilters.In);
VAR
value: XML.String;
feIn: SVGFilters.FilterElement;
BEGIN
value := LoadAttribute(e, name);
NEW(in);
IF value=NIL THEN
in.type := SVGFilters.InFilterElement;
in.fe := fePrev;
IF fePrev=NIL THEN
SVG.Error("filter element defaults to previous element, but no previous element available")
END
ELSE
SVGFilters.ParseIn(value, in.type);
IF in.type=SVGFilters.InFilterElement THEN
feIn := filter.GetFilterElement(value);
IF feIn#NIL THEN
in.fe := feIn
ELSE
SVG.Error("Couldn't find filter element with result= ");
SVG.Error(value^);
END
END
END
END LoadFilterInAttribute;
PROCEDURE LoadFilterAttribute(e: XML.Element):SVGFilters.Filter;
VAR filterAttr, name: XML.String;
filter: SVGFilters.Filter;
BEGIN
filterAttr := e.GetAttributeValue("filter");
IF filterAttr # NIL THEN
IF SVG.ParseURI(filterAttr, name) THEN
filter := renderer.filters.GetFilter(name);
state.transparencyUsed := TRUE;
END;
IF filter # NIL THEN
RETURN filter;
ELSE
SVG.Error("Couldn't find filter with specified id");
SVG.Error(name^);
RETURN NIL
END
ELSE
RETURN NIL
END
END LoadFilterAttribute;
PROCEDURE LoadFilterAttributeBegin(e: XML.Element);
VAR filter: SVGFilters.Filter;
BEGIN
filter := LoadFilterAttribute(e);
renderer.BeginFilter(filter, state);
END LoadFilterAttributeBegin;
PROCEDURE LoadFilterAttributeEnd;
BEGIN
renderer.EndFilter(state)
END LoadFilterAttributeEnd;
PROCEDURE LoadImage(e: XML.Element);
VAR x, y, width, height: SVG.Length;
image: SVG.Document;
href: SVG.String;
BEGIN
SVG.Log("<image />");
state.Push();
LoadAttrTransform(e, "transform", state.userToWorldSpace);
LoadAttrLength(e, "x", x, GetActualWidth(), OnOmittedParseDefault, "0");
LoadAttrLength(e, "y", y, GetActualHeight(), OnOmittedParseDefault, "0");
LoadAttrLength(e, "width", width, GetActualWidth(), OnOmittedError, "image element omitted width attribute");
LoadAttrLength(e, "height", height, GetActualHeight(), OnOmittedError, "image element omitted height attribute");
href := e.GetAttributeValue("xlink:href");
IF href = NIL THEN
SVG.Error("image element omitted xlink:href attribute");
RETURN
END;
image :=WMGraphics.LoadImage(href^, TRUE);
IF image = NIL THEN
SVG.Error("image element specifies invalid xlink:href attribute");
RETURN
END;
LoadFilterAttributeBegin(e);
renderer.RenderImage(x, y, width, height, image, state);
LoadFilterAttributeEnd();
state.Pop();
END LoadImage;
PROCEDURE LoadRect(e: XML.Element);
VAR x, y, width, height, rx, ry: SVG.Length;
rxe, rye: XML.Attribute;
BEGIN
SVG.Log("<rect />");
state.Push();
LoadPaintAttributes(e);
LoadAttrTransform(e, "transform", state.userToWorldSpace);
LoadAttrLength(e, "x", x, GetActualWidth(), OnOmittedParseDefault, "0");
LoadAttrLength(e, "y", y, GetActualHeight(), OnOmittedParseDefault, "0");
LoadAttrLength(e, "width", width, GetActualWidth(), OnOmittedError, "rect element omitted width attribute");
LoadAttrLength(e, "height", height, GetActualHeight(), OnOmittedError, "rect element omitted width attribute");
rxe := e.GetAttribute("rx");
rye := e.GetAttribute("ry");
IF (rxe=NIL) & (rxe=NIL) THEN
LoadFilterAttributeBegin(e);
renderer.RenderRect(x, y, width, height, state);
LoadFilterAttributeEnd();
ELSE
IF rxe=NIL THEN
LoadAttrLength(e, "ry", ry, GetActualDiagonal(), OnOmittedParseDefault, "0");
rx := ry;
ELSIF rye=NIL THEN
LoadAttrLength(e, "rx", rx, GetActualDiagonal(), OnOmittedParseDefault, "0");
ry := rx;
ELSE
LoadAttrLength(e, "rx", rx, GetActualWidth(), OnOmittedParseDefault, "0");
LoadAttrLength(e, "ry", ry, GetActualHeight(), OnOmittedParseDefault, "0");
END;
LoadFilterAttributeBegin(e);
renderer.RenderRoundedRect(x, y, width, height, rx, ry, state);
LoadFilterAttributeEnd();
END;
state.Pop();
END LoadRect;
PROCEDURE LoadCircle(e: XML.Element);
VAR cx, cy, r: SVG.Length;
BEGIN
SVG.Log("<circle />");
state.Push();
LoadPaintAttributes(e);
LoadAttrTransform(e, "transform", state.userToWorldSpace);
LoadAttrLength(e, "cx", cx, GetActualWidth(), OnOmittedParseDefault, "0");
LoadAttrLength(e, "cy", cy, GetActualHeight(), OnOmittedParseDefault, "0");
LoadAttrLength(e, "r", r, GetActualDiagonal(), OnOmittedError, "circle element omitted r attribute");
LoadFilterAttributeBegin(e);
renderer.RenderCircle(cx, cy, r, state);
LoadFilterAttributeEnd();
state.Pop();
END LoadCircle;
PROCEDURE LoadEllipse(e: XML.Element);
VAR cx, cy, rx, ry: SVG.Length;
BEGIN
SVG.Log("<ellipse />");
state.Push();
LoadPaintAttributes(e);
LoadAttrTransform(e, "transform", state.userToWorldSpace);
LoadAttrLength(e, "cx", cx, GetActualWidth(), OnOmittedParseDefault, "0");
LoadAttrLength(e, "cy", cy, GetActualHeight(), OnOmittedParseDefault, "0");
LoadAttrLength(e, "rx", rx, GetActualWidth(), OnOmittedError, "ellipse element omitted rx attribute");
LoadAttrLength(e, "ry", ry, GetActualHeight(), OnOmittedError, "ellipse element omitted ry attribute");
LoadFilterAttributeBegin(e);
renderer.RenderEllipse(cx, cy, rx, ry, state);
LoadFilterAttributeEnd();
state.Pop();
END LoadEllipse;
PROCEDURE LoadLine(e: XML.Element);
VAR x1, y1, x2, y2: SVG.Length;
BEGIN
SVG.Log("<line />");
state.Push();
LoadPaintAttributes(e);
LoadAttrTransform(e, "transform", state.userToWorldSpace);
LoadAttrLength(e, "x1", x1, GetActualWidth(), OnOmittedParseDefault, "0");
LoadAttrLength(e, "y1", y1, GetActualHeight(), OnOmittedParseDefault, "0");
LoadAttrLength(e, "x2", x2, GetActualWidth(), OnOmittedParseDefault, "0");
LoadAttrLength(e, "y2", y2, GetActualHeight(), OnOmittedParseDefault, "0");
LoadFilterAttributeBegin(e);
renderer.RenderLine(x1, y1, x2, y2, state);
LoadFilterAttributeEnd();
state.Pop();
END LoadLine;
PROCEDURE LoadPoly(e: XML.Element; closed: BOOLEAN);
VAR points: SVG.String;
BEGIN
IF closed THEN SVG.Log("<polygon />")
ELSE SVG.Log("<polyline />") END;
state.Push();
LoadPaintAttributes(e);
LoadAttrTransform(e, "transform", state.userToWorldSpace);
points := e.GetAttributeValue("points");
IF points=NIL THEN
IF closed THEN SVG.Error("polygon element omitted points attribute")
ELSE SVG.Error("polyline element omitted points attribute") END;
RETURN
ELSE
LoadFilterAttributeBegin(e);
renderer.RenderPoly(points, closed, state);
LoadFilterAttributeEnd();
END;
state.Pop();
END LoadPoly;
PROCEDURE LoadPath(e: XML.Element);
VAR d: SVG.String;
BEGIN
SVG.Log("<path />");
state.Push();
LoadPaintAttributes(e);
LoadAttrTransform(e, "transform", state.userToWorldSpace);
d := e.GetAttributeValue("d");
IF d=NIL THEN
SVG.Error("path element omitted d attribute");
ELSE
LoadFilterAttributeBegin(e);
renderer.RenderPath(d, state);
LoadFilterAttributeEnd();
END;
state.Pop();
END LoadPath;
PROCEDURE LoadPaintAttributes(e: XML.Element);
VAR
value: XML.String;
BEGIN
value := LoadAttribute(e, "fill");
IF value#NIL THEN SVG.ParsePaint(value,state.style.fill) END;
value := LoadAttribute(e, "stroke");
IF value#NIL THEN SVG.ParsePaint(value, state.style.stroke) END;
value := LoadAttribute(e, "stroke-width");
IF value#NIL THEN SVG.ParseLength(value, ppi, GetActualDiagonal(), state.style.strokeWidth) END;
LoadOpacity(e, "fill-opacity", state.style.fill.color, OnOmittedParseDefault, "1");
LoadOpacity(e, "stroke-opacity", state.style.stroke.color, OnOmittedParseDefault, "1");
END LoadPaintAttributes;
PROCEDURE LoadViewBoxAttributes(e: XML.Element; VAR transform: SVG.Transform; width0, height0: SVG.Length);
VAR viewBox, preserveAR: XML.String;
minx, miny, width, height, ratioX, ratioY: SVG.Length;
xAlign, yAlign: LONGINT;
meet: BOOLEAN;
BEGIN
viewBox := e.GetAttributeValue("viewBox");
IF viewBox # NIL THEN
SVG.ParseViewBox(viewBox, minx, miny, width, height);
ratioX := width0/width;
ratioY := height0/height;
preserveAR := e.GetAttributeValue("preserveAspectRatio");
IF (preserveAR = NIL) OR (Strings.StartsWith2("none",preserveAR^)) THEN
xAlign := -1;
yAlign := -1;
ELSE
SVG.ParsePreserveAspect(preserveAR, xAlign, yAlign, meet);
IF meet = (width0/height0 < width/height) THEN
ratioY := ratioX
ELSE ratioX := ratioY END
END;
transform := transform.TransformBy(ratioX, 0.0, 0.0, ratioY,
(1+xAlign)*width0/2-(minx+(1+xAlign)*width/2)*ratioX,
(1+yAlign)*height0/2 -(miny+(1+yAlign)*height/2)*ratioY)
END
END LoadViewBoxAttributes;
PROCEDURE LoadAttrNumber(e: XML.Element; name: ARRAY OF CHAR; VAR number: SVG.Number;
percentageAllowed: BOOLEAN; percent100: SVG.Number; onOmitted: SHORTINT; default: ARRAY OF CHAR);
VAR value: XML.String;
BEGIN
value := LoadAttribute(e,name);
IF value = NIL THEN
CASE onOmitted OF
OnOmittedParseDefault:
value := Strings.NewString(default);
SVG.ParseNumber(value, number, percentageAllowed, percent100)
| OnOmittedDontChange:
| OnOmittedError:
SVG.Error(default);
END
ELSE
SVG.ParseNumber(value, number, percentageAllowed, percent100)
END
END LoadAttrNumber;
PROCEDURE LoadAttrLength(e: XML.Element; name: ARRAY OF CHAR; VAR length: SVG.Length;
percent100: SVG.Number; onOmitted: SHORTINT; default: ARRAY OF CHAR);
VAR value: XML.String;
BEGIN
value := LoadAttribute(e,name);
IF value = NIL THEN
CASE onOmitted OF
OnOmittedParseDefault:
value := Strings.NewString(default);
SVG.ParseLength(value, ppi, percent100, length)
| OnOmittedDontChange:
| OnOmittedError:
SVG.Error(default);
END
ELSE
SVG.ParseLength(value, ppi, percent100, length)
END
END LoadAttrLength;
PROCEDURE LoadAttrTransform(e: XML.Element; name: ARRAY OF CHAR; VAR transform: SVG.Transform);
VAR value: XML.String;
BEGIN
value := LoadAttribute(e,name);
IF value # NIL THEN
SVG.ParseTransformList(value, transform)
END
END LoadAttrTransform;
PROCEDURE LoadAttrParsed(e: XML.Element; name: ARRAY OF CHAR; VAR parsed: SHORTINT;
onOmitted: SHORTINT; default: ARRAY OF CHAR;
parser: PROCEDURE(value: XML.String; VAR parsed: SHORTINT));
VAR value: XML.String;
BEGIN
value := LoadAttribute(e,name);
IF value = NIL THEN
CASE onOmitted OF
OnOmittedParseDefault:
value := Strings.NewString(default);
parser(value,parsed)
| OnOmittedDontChange:
| OnOmittedError:
SVG.Error(default);
END
ELSE
parser(value,parsed)
END
END LoadAttrParsed;
PROCEDURE LoadAttribute(e: XML.Element; name: ARRAY OF CHAR): XML.String;
VAR value: XML.String;
BEGIN
value := e.GetAttributeValue(name);
IF value=NIL THEN
value := e.GetAttributeValue("style");
IF value=NIL THEN RETURN NIL END;
value := SVG.ParseStyle(value,name);
END;
RETURN value
END LoadAttribute;
PROCEDURE LoadOpacity(e: XML.Element; name: ARRAY OF CHAR; VAR color: SVG.Color; onOmitted: SHORTINT; default: ARRAY OF CHAR);
VAR opacity: SVG.Length;
r,g,b,a: INTEGER;
BEGIN
LoadAttrNumber(e,name, opacity,SVG.DisallowPercentages, 0.0, onOmitted, default);
IF opacity#1 THEN
SVGColors.Split(color, r,g,b,a);
a:=SHORT(ENTIER(255*opacity));
SVGColors.Unsplit(color, r,g,b,a);
state.transparencyUsed := TRUE;
END;
END LoadOpacity;
END SVGLoader;
PROCEDURE LoadSVG*(svgName: ARRAY OF CHAR): SVG.Document;
VAR
xml: XML.Document;
root: XML.Element;
BEGIN
xml := XMLLoader.LoadXML(svgName);
IF xml = NIL THEN RETURN NIL END;
root := xml.GetRoot();
IF root = NIL THEN RETURN NIL END;
RETURN LoadSVGEmbedded(root)
END LoadSVG;
PROCEDURE LoadSVGEmbedded*(root: XML.Element): SVG.Document;
VAR loader: SVGLoader;
BEGIN
NEW(loader);
loader.LoadRoot(root);
RETURN loader.GetDocument()
END LoadSVGEmbedded;
PROCEDURE LoadSizedSVG*(svgName: ARRAY OF CHAR; width, height: LONGINT): SVG.Document;
VAR
xml: XML.Document;
root: XML.Element;
loader: SVGLoader;
BEGIN
xml := XMLLoader.LoadXML(svgName);
IF xml = NIL THEN RETURN NIL END;
root := xml.GetRoot();
IF root = NIL THEN RETURN NIL END;
NEW(loader);
loader.ForceSize(width, height);
loader.LoadRoot(root);
RETURN loader.GetDocument()
END LoadSizedSVG;
END SVGLoader.