SVG.Mod 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. MODULE SVG;
  2. IMPORT SVGUtilities, SVGColors, SVGMatrix, XML, CSS2, Strings, Raster, GfxFonts;
  3. CONST
  4. (* Constants that determine the type of some paint attribute *)
  5. PaintNone* = 0;
  6. PaintCurrentColor* = 1;
  7. PaintColor* = 2;
  8. PaintURI* = 3;
  9. (* Constants that determine the type of some units *)
  10. UnitsUserSpaceOnUse* = 0;
  11. UnitsObjectBoundingBox* = 1;
  12. (* Constants that determine if percentage values are allowed *)
  13. AllowPercentages*=TRUE;
  14. DisallowPercentages*=FALSE;
  15. TYPE
  16. Document*=Raster.Image;
  17. String*=XML.String;
  18. Number*=SVGMatrix.Number;
  19. Length*=Number;
  20. Color*=SVGColors.Color;
  21. Box*=RECORD
  22. x*, y*, width*, height*: Length;
  23. END;
  24. Coordinate*=SVGMatrix.Point;
  25. Transform*=SVGMatrix.Matrix;
  26. Paint*=RECORD
  27. type*: SHORTINT;
  28. color*: Color;
  29. uri*: String;
  30. END;
  31. Style*=RECORD
  32. fill*: Paint;
  33. stroke*: Paint;
  34. strokeWidth*: Length;
  35. END;
  36. State*=OBJECT
  37. VAR
  38. style*: Style;
  39. target*: Document;
  40. transparencyUsed*: BOOLEAN;
  41. viewport*: Box;
  42. userToWorldSpace*: Transform;
  43. next: State;
  44. (* Push a new copy of the states on a stack *)
  45. PROCEDURE Push*;
  46. VAR pushed: State;
  47. BEGIN
  48. NEW(pushed);
  49. pushed^ := SELF^;
  50. next := pushed;
  51. END Push;
  52. (* Pop the top state from the stack *)
  53. PROCEDURE Pop*;
  54. BEGIN
  55. SELF^ := next^;
  56. END Pop;
  57. END State;
  58. (* Load the default style attributes *)
  59. PROCEDURE InitDefaultStyle*(VAR style: Style);
  60. BEGIN
  61. style.fill.type := PaintColor;
  62. style.fill.color := SVGColors.Black;
  63. style.stroke.type := PaintNone;
  64. style.strokeWidth := 1;
  65. END InitDefaultStyle;
  66. (* Parse a number at the specified position in the string *)
  67. PROCEDURE ParseNumber2(value: String; VAR number: Number; percentageAllowed: BOOLEAN; percent100: Length; VAR i: SIZE; VAR unitStr: String);
  68. BEGIN
  69. SVGUtilities.StrToFloatPos(value^, number, i);
  70. unitStr := Strings.Substring2(i, value^);
  71. IF unitStr^ = '%' THEN
  72. IF percentageAllowed THEN
  73. number := number*percent100/100.0;
  74. ELSE
  75. Error("expected number, found percentage: ");
  76. Error(value^);
  77. END
  78. END
  79. END ParseNumber2;
  80. (* Parse a number *)
  81. PROCEDURE ParseNumber*(value: String; VAR number: Number; percentageAllowed: BOOLEAN; percent100: Length);
  82. VAR i: SIZE;
  83. unitStr: String;
  84. BEGIN
  85. i := 0;
  86. ParseNumber2(value, number, percentageAllowed, percent100, i, unitStr);
  87. END ParseNumber;
  88. (* Parse an attribute of type length at the specified position in the string *)
  89. PROCEDURE ParseLength2(value:String; ppi: LONGREAL; percent100: Length; VAR length: Length; VAR i: SIZE);
  90. VAR
  91. term: CSS2.Term;
  92. unit: SHORTINT;
  93. unitStr: String;
  94. BEGIN
  95. ParseNumber2(value, length, AllowPercentages, percent100, i, unitStr);
  96. IF unitStr^ # '%' THEN
  97. unit := GetTermUnit(unitStr^);
  98. IF unit # CSS2.Undefined THEN
  99. term := ChangeToPixel(length);
  100. term.SetUnit(unit);
  101. length := GetPixels(term, ppi, GfxFonts.Default) (* Use DefaultFont for now... *)
  102. END
  103. END
  104. END ParseLength2;
  105. (* Parse an attribute of type length *)
  106. PROCEDURE ParseLength*(value:String; ppi: LONGREAL; percent100: Length; VAR length: Length);
  107. VAR
  108. i: SIZE;
  109. BEGIN
  110. i := 0;
  111. ParseLength2(value,ppi,percent100,length,i)
  112. END ParseLength;
  113. (* Parse one or optionally two attributes of type length *)
  114. PROCEDURE ParseLengthOptional2*(value:String; ppi: LONGREAL; percent100: Length; VAR length, length2: Length);
  115. VAR
  116. i: SIZE;
  117. BEGIN
  118. i := 0;
  119. ParseLength2(value,ppi,percent100,length,i);
  120. SVGUtilities.SkipCommaWhiteSpace(i, value);
  121. IF value[i]=0X THEN
  122. length2 := length
  123. ELSE
  124. ParseLength2(value,ppi,percent100,length2,i)
  125. END
  126. END ParseLengthOptional2;
  127. (* Parse a coordinate pair *)
  128. PROCEDURE ParseCoordinate*(value: String; VAR i: SIZE; VAR current: Coordinate; relative: BOOLEAN);
  129. VAR x, y: Length;
  130. BEGIN
  131. SVGUtilities.SkipCommaWhiteSpace(i, value);
  132. SVGUtilities.StrToFloatPos(value^, x, i);
  133. SVGUtilities.SkipCommaWhiteSpace(i, value);
  134. SVGUtilities.StrToFloatPos(value^, y, i);
  135. IF relative THEN
  136. current.x := current.x + x;
  137. current.y := current.y + y;
  138. ELSE
  139. current.x := x;
  140. current.y := y;
  141. END
  142. END ParseCoordinate;
  143. (* Parse a single coordinate value *)
  144. PROCEDURE ParseCoordinate1*(value: String; VAR i: SIZE; VAR current: Length; relative: BOOLEAN);
  145. VAR l: Length;
  146. BEGIN
  147. SVGUtilities.SkipCommaWhiteSpace(i, value);
  148. SVGUtilities.StrToFloatPos(value^, l, i);
  149. IF relative THEN
  150. current := current + l;
  151. ELSE
  152. current := l;
  153. END
  154. END ParseCoordinate1;
  155. (* Parse a paint style attribute *)
  156. PROCEDURE ParsePaint*(value: String; VAR paint: Paint);
  157. BEGIN
  158. IF value^ = "none" THEN paint.type := PaintNone
  159. ELSIF value^ = "currentColor" THEN paint.type := PaintCurrentColor
  160. ELSIF SVGColors.Parse(value, paint.color) THEN paint.type := PaintColor
  161. ELSIF ParseURI(value, paint.uri) THEN paint.type := PaintURI
  162. ELSE
  163. Error("expected paint, found :");
  164. Error(value^);
  165. paint.type := PaintNone
  166. END
  167. END ParsePaint;
  168. (* Parse the contents of the xlink:href attribute *)
  169. PROCEDURE ParseURI*(value: String; VAR uri: String):BOOLEAN;
  170. BEGIN
  171. IF Strings.StartsWith2("url(#", value^) & Strings.EndsWith(")", value^) THEN
  172. uri := Strings.Substring(5, Strings.Length(value^)-1, value^);
  173. RETURN TRUE
  174. ELSIF Strings.StartsWith2("#", value^) THEN
  175. uri := Strings.Substring2(1, value^);
  176. RETURN TRUE
  177. ELSE RETURN FALSE
  178. END
  179. END ParseURI;
  180. (* Parse the contents of the gradientUnits attribute *)
  181. PROCEDURE ParseUnits*(value: String; VAR units: SHORTINT);
  182. BEGIN
  183. IF value^ = "userSpaceOnUse" THEN units := UnitsUserSpaceOnUse
  184. ELSIF value^ = "objectBoundingBox" THEN units := UnitsObjectBoundingBox
  185. ELSE
  186. Error("expected userSpaceOnUse or objectBoundingBox, found: ");
  187. Error(value^)
  188. END
  189. END ParseUnits;
  190. (* Parse the contents of the fill or stroke attributes *)
  191. PROCEDURE ParseStyle*(style: String; CONST name: ARRAY OF CHAR): String;
  192. VAR i, end: SIZE;
  193. id: String;
  194. BEGIN
  195. i := 0;
  196. SVGUtilities.SkipWhiteSpace(i, style);
  197. WHILE style[i] # 0X DO
  198. end:=Strings.IndexOfByte(':', i, style^);
  199. IF end=-1 THEN RETURN NIL END;
  200. id := Strings.Substring(i, end, style^);
  201. i := end+1;
  202. SVGUtilities.SkipWhiteSpace(i, style);
  203. end:=Strings.IndexOfByte(';', i, style^);
  204. IF end=-1 THEN
  205. IF id^ = name THEN
  206. RETURN Strings.Substring2(i, style^);
  207. END;
  208. RETURN NIL
  209. END;
  210. IF id^ = name THEN
  211. RETURN Strings.Substring(i, end, style^);
  212. END;
  213. i := end+1;
  214. SVGUtilities.SkipWhiteSpace(i, style);
  215. END;
  216. RETURN NIL;
  217. END ParseStyle;
  218. (* Parse the contents of the transform and gradientTransform attributes *)
  219. PROCEDURE ParseTransformList*(value: String; VAR transform: Transform);
  220. VAR
  221. i, len: SIZE;
  222. a, b, c, d, e, f: Length;
  223. BEGIN
  224. i := 0;
  225. len := Strings.Length(value^);
  226. SVGUtilities.SkipWhiteSpace(i, value);
  227. WHILE i#len DO
  228. IF Strings.StartsWith("matrix(", i, value^) THEN
  229. i := i + 7;
  230. SVGUtilities.StrToFloatPos(value^, a, i);
  231. SVGUtilities.SkipCommaWhiteSpace(i, value);
  232. SVGUtilities.StrToFloatPos(value^, b, i);
  233. SVGUtilities.SkipCommaWhiteSpace(i, value);
  234. SVGUtilities.StrToFloatPos(value^, c, i);
  235. SVGUtilities.SkipCommaWhiteSpace(i, value);
  236. SVGUtilities.StrToFloatPos(value^, d, i);
  237. SVGUtilities.SkipCommaWhiteSpace(i, value);
  238. SVGUtilities.StrToFloatPos(value^, e, i);
  239. SVGUtilities.SkipCommaWhiteSpace(i, value);
  240. SVGUtilities.StrToFloatPos(value^, f, i);
  241. transform := transform.TransformBy(a, b, c, d, e, f)
  242. ELSIF Strings.StartsWith("translate(", i, value^) THEN
  243. i := i + 10;
  244. SVGUtilities.StrToFloatPos(value^, a, i);
  245. SVGUtilities.SkipCommaWhiteSpace(i, value);
  246. IF value[i] # ")" THEN SVGUtilities.StrToFloatPos(value^, b, i)
  247. ELSE b := 0.0 END;
  248. transform := transform.Translate(a, b)
  249. ELSIF Strings.StartsWith("scale(", i, value^) THEN
  250. i := i + 6;
  251. SVGUtilities.StrToFloatPos(value^, a, i);
  252. SVGUtilities.SkipCommaWhiteSpace(i, value);
  253. IF value[i] # ")" THEN SVGUtilities.StrToFloatPos(value^, b, i)
  254. ELSE b := a END;
  255. transform := transform.Scale(a, b)
  256. ELSIF Strings.StartsWith("rotate(", i, value^) THEN
  257. i := i + 7;
  258. SVGUtilities.StrToFloatPos(value^, a, i);
  259. SVGUtilities.SkipCommaWhiteSpace(i, value);
  260. IF value[i] # ")" THEN
  261. SVGUtilities.StrToFloatPos(value^, b, i);
  262. SVGUtilities.SkipCommaWhiteSpace(i, value);
  263. SVGUtilities.StrToFloatPos(value^, c, i)
  264. ELSE b := 0.0; c := 0.0 END;
  265. transform := transform.Rotate(a, b, c)
  266. ELSIF Strings.StartsWith("skewX(", i, value^) THEN
  267. i := i + 6;
  268. SVGUtilities.StrToFloatPos(value^, a, i);
  269. transform := transform.SkewX(a)
  270. ELSIF Strings.StartsWith("skewY(", i, value^) THEN
  271. i := i + 6;
  272. SVGUtilities.StrToFloatPos(value^, a, i);
  273. transform := transform.SkewY(a)
  274. ELSE
  275. Error("unknown transform command");
  276. Error(value^);
  277. RETURN
  278. END;
  279. SVGUtilities.SkipWhiteSpace(i, value);
  280. SVGUtilities.SkipChar(i, value, ')');
  281. SVGUtilities.SkipCommaWhiteSpace(i,value)
  282. END
  283. END ParseTransformList;
  284. (* Parse the contents of the viewBoxattribute *)
  285. PROCEDURE ParseViewBox*(value: String; VAR minx, miny, width, height: Length);
  286. VAR i: SIZE;
  287. BEGIN
  288. i := 0;
  289. SVGUtilities.SkipWhiteSpace(i, value);
  290. SVGUtilities.StrToFloatPos(value^, minx, i);
  291. SVGUtilities.SkipCommaWhiteSpace(i, value);
  292. SVGUtilities.StrToFloatPos(value^, miny, i);
  293. SVGUtilities.SkipCommaWhiteSpace(i, value);
  294. SVGUtilities.StrToFloatPos(value^, width, i);
  295. SVGUtilities.SkipCommaWhiteSpace(i, value);
  296. SVGUtilities.StrToFloatPos(value^, height, i);
  297. END ParseViewBox;
  298. (* Parse the contents of the preserveAspectRatio attribute *)
  299. PROCEDURE ParsePreserveAspect*(value: String; VAR xAlign, yAlign: LONGINT; VAR meet: BOOLEAN);
  300. VAR i: SIZE;
  301. BEGIN
  302. i := 0;
  303. IF Strings.StartsWith("xMin", i, value^) THEN i := i + 4; xAlign := -1
  304. ELSIF Strings.StartsWith("xMid", i, value^) THEN i := i + 4; xAlign := 0
  305. ELSIF Strings.StartsWith("xMax", i, value^) THEN i := i + 4; xAlign := +1
  306. ELSE
  307. Error("expected xMin, xMid or xMax, found: ");
  308. Error(value^);
  309. END;
  310. IF Strings.StartsWith("YMin", i, value^) THEN i := i + 4; yAlign := -1
  311. ELSIF Strings.StartsWith("YMid", i, value^) THEN i := i + 4; yAlign := 0
  312. ELSIF Strings.StartsWith("YMax", i, value^) THEN i := i + 4; yAlign := +1
  313. ELSE
  314. Error("expected yMin, yMid or yMax, found: ");
  315. Error(value^);
  316. END;
  317. SVGUtilities.SkipWhiteSpace(i, value);
  318. IF Strings.StartsWith("slice", i, value^) THEN
  319. meet := FALSE
  320. ELSIF Strings.StartsWith("meet", i, value^) THEN
  321. meet := TRUE
  322. ELSIF i=Strings.Length(value^) THEN
  323. meet := TRUE
  324. ELSE
  325. Error("expected meet or slive, found: ");
  326. END
  327. END ParsePreserveAspect;
  328. (* Create a new, empty SVG.Document *)
  329. PROCEDURE NewDocument*(width, height: Length):Document;
  330. VAR
  331. doc: Document;
  332. BEGIN
  333. NEW(doc);
  334. Raster.Create(doc,ENTIER(width),ENTIER(height),Raster.BGRA8888);
  335. Raster.Clear(doc);
  336. RETURN doc;
  337. END NewDocument;
  338. (* The following procedures are forwarded to SVGUtilities for convenience *)
  339. PROCEDURE Log*(CONST msg: ARRAY OF CHAR);
  340. BEGIN
  341. SVGUtilities.Log(msg)
  342. END Log;
  343. PROCEDURE Warning*(CONST msg: ARRAY OF CHAR);
  344. BEGIN
  345. SVGUtilities.Warning(msg)
  346. END Warning;
  347. PROCEDURE Error*(CONST msg: ARRAY OF CHAR);
  348. BEGIN
  349. SVGUtilities.Error(msg)
  350. END Error;
  351. (* The following procedures are defined in, but not exported from the Modules CSS2Properties and CSS2Parser *)
  352. PROCEDURE GetPixels(term: CSS2.Term; ppi: LONGREAL; font: GfxFonts.Font): LONGREAL;
  353. VAR fact, pixels: LONGREAL; x, y, dx, dy: REAL; map: Raster.Image;
  354. BEGIN
  355. IF (term # NIL) & term.IsLength() THEN
  356. CASE term.GetUnit() OF
  357. | CSS2.em: fact := font.ptsize / ppi
  358. | CSS2.ex: GfxFonts.GetMap(font, 'x', x, y, dx, dy, map); fact := -y / ppi
  359. | CSS2.px: fact := 1.0 / ppi
  360. | CSS2.in: fact := 1.0
  361. | CSS2.cm: fact := 1.0 / 2.54
  362. | CSS2.mm: fact := 1.0 / 25.4
  363. | CSS2.pt: fact := 1.0 / 72.0
  364. | CSS2.pc: fact := 1.0 / 6.0
  365. END;
  366. IF term.GetType() = CSS2.IntDimension THEN pixels := term.GetIntVal() * ppi * fact
  367. ELSIF term.GetType() = CSS2.RealDimension THEN pixels := term.GetRealVal() * ppi * fact
  368. END
  369. ELSIF (term # NIL) & (((term.GetType() = CSS2.IntNumber) & (term.GetIntVal() = 0))
  370. OR ((term.GetType() = CSS2.RealNumber) & (term.GetRealVal() = 0.0))) THEN
  371. pixels := 0.0
  372. ELSE
  373. pixels := 0.0
  374. END;
  375. RETURN pixels
  376. END GetPixels;
  377. PROCEDURE ChangeToPixel(pixelVal: LONGREAL): CSS2.Term;
  378. VAR term: CSS2.Term;
  379. BEGIN
  380. NEW(term); term.SetType(CSS2.RealDimension); term.SetRealVal(pixelVal); term.SetUnit(CSS2.px); RETURN term
  381. END ChangeToPixel;
  382. PROCEDURE GetTermUnit(CONST unitStr: ARRAY OF CHAR): SHORTINT;
  383. BEGIN
  384. IF unitStr = 'em' THEN RETURN CSS2.em
  385. ELSIF unitStr = 'ex' THEN RETURN CSS2.ex
  386. ELSIF unitStr = 'px' THEN RETURN CSS2.px
  387. ELSIF unitStr = 'in' THEN RETURN CSS2.in
  388. ELSIF unitStr = 'cm' THEN RETURN CSS2.cm
  389. ELSIF unitStr = 'mm' THEN RETURN CSS2.mm
  390. ELSIF unitStr = 'pt' THEN RETURN CSS2.pt
  391. ELSIF unitStr = 'pc' THEN RETURN CSS2.pc
  392. ELSIF unitStr = 'deg' THEN RETURN CSS2.deg
  393. ELSIF unitStr = 'grad' THEN RETURN CSS2.grad
  394. ELSIF unitStr = 'rad' THEN RETURN CSS2.rad
  395. ELSIF unitStr = 'ms' THEN RETURN CSS2.ms
  396. ELSIF unitStr = 's' THEN RETURN CSS2.s
  397. ELSIF unitStr = 'Hz' THEN RETURN CSS2.Hz
  398. ELSIF unitStr = 'kHz' THEN RETURN CSS2.kHz
  399. ELSE RETURN CSS2.Undefined
  400. END
  401. END GetTermUnit;
  402. END SVG.