1 {
2 Reads an ODG Document
3
4 License: The same modified LGPL as the Free Pascal RTL
5 See the file COPYING.modifiedLGPL for more details
6
7 An OpenDocument document is a compressed ZIP file with the following files inside:
8
9 content.xml - Actual contents
10 meta.xml - Authoring data
11 settings.xml - User persistent viewing information, such as zoom, cursor position, etc.
12 styles.xml - Styles, which are the only way to do formatting
13 mimetype - application/vnd.oasis.opendocument.spreadsheet
14 META-INF\manifest.xml - Describes the other files in the archive
15
16 Specifications obtained from:
17
18 http://docs.oasis-open.org/office/v1.1/OS/OpenDocument-v1.1.pdf
19
20 Example of content.xml structure:
21
22 <?xml version="1.0" encoding="UTF-8"?>
23 <office:document-content xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" .....>
24 <office:scripts/>
25 <office:automatic-styles>
26 <style:style style:name="dp1" style:family="drawing-page"/>
27 <style:style style:name="gr1" style:family="graphic" style:parent-style-name="standard">
28 ....
29 </office:automatic-styles>
30 <office:body>
31 <office:drawing>
32 <draw:page draw:name="page1" draw:style-name="dp1" draw:master-page-name="Oletus">
33 <draw:ellipse draw:style-name="gr2" draw:text-style-name="P1" draw:layer="layout" svg:width="11cm" svg:height="3cm" svg:x="5.5cm" svg:y="6.5cm">
34 <text:p/>
35 </draw:ellipse>
36 ... other elements in the page...
37 </draw:page>
38 </office:drawing>
39 </office:body>
40 </office:document-content>
41
42 AUTHORS: Felipe Monteiro de Carvalho
43 }
44 unit odgvectorialreader;
45
46 {$mode objfpc}{$H+}
47
48 interface
49
50 uses
51 Classes, SysUtils, math,
52 zipper, {NOTE: might require zipper from FPC 2.6.2+ }
53 xmlread, DOM, AVL_Tree,
54 fpimage, fpcanvas, fgl,
55 fpvectorial, fpvutils, lazutf8;
56
57 type
58 TDoubleArray = array of Double;
59
60 TSVGTokenType = (
61 // moves
62 sttMoveTo, sttRelativeMoveTo,
63 // Close Path
64 sttClosePath,
65 // lines
66 sttLineTo, sttRelativeLineTo,
67 sttHorzLineTo, sttRelativeHorzLineTo, sttVertLineTo, sttRelativeVertLineTo,
68 // cubic beziers
69 sttBezierTo, sttRelativeBezierTo,
70 // quadratic beziers
71 sttQuadraticBezierTo, sttRelativeQuadraticBezierTo,
72 // Elliptic curves
73 sttEllipticArcTo, sttRelativeEllipticArcTo,
74 sttEllipticArcToWithAngle,
75 // ToDo: Find out what these are
76 sttUnknown,
77 // numbers
78 sttFloatValue,
79 // text
80 sttConstantValue, sttNamedEquation);
81
82 TSVGToken = class
83 TokenType: TSVGTokenType;
84 Value: Float;
85 StrValue: string;
86 end;
87
88 TSVGTokenList = specialize TFPGList<TSVGToken>;
89
90 { TSVGPathTokenizer }
91
92 TSVGPathTokenizer = class
93 public
94 FPointSeparator, FCommaSeparator: TFormatSettings;
95 Tokens: TSVGTokenList;
96 constructor Create;
97 Destructor Destroy; override;
98 procedure AddToken(AStr: string);
99 procedure TokenizePathString(AStr: string);
100 procedure TokenizeFunctions(AStr: string);
101 end;
102
103 TODGStyle = class(TvStyle)
104 //
105 end;
106
107 TODGMasterPage = class
108 public
109 Name: string;
110 PageLayoutName: string;
111 StyleName: string;
112 end;
113
114 TODGPageLayout = class
115 public
116 Name: string;
117 MarginTop, MarginBottom, MarginLeft, MarginRight: Double;
118 PageWidth, PageHeight: Double;
119 end;
120
121 TCustomShapeInfo = class
122 public
123 Left, Top, Width, Height: Double; // in milimiters
124 ViewBox_Left, ViewBox_Top, ViewBox_Width, ViewBox_Height: Double; // unitless
125 Formulas: array of TvFormula;
126 end;
127
128 { TvODGVectorialReader }
129
130 TvODGVectorialReader = class(TvCustomVectorialReader)
131 private
132 FPointSeparator, FCommaSeparator: TFormatSettings;
133 FStyles: TFPList; // of TODGStyle;
134 FAutomaticStyles: TFPList; // of TODGStyle;
135 FPageLayouts: TFPList; // of TODGPageLayout;
136 FMasterPages: TFPList; // of TODGMasterPage;
137 //FSVGPathTokenizer: TSVGPathTokenizer;
138 //
ReadSpaceSeparatedFloatsnull139 function ReadSpaceSeparatedFloats(AInput: string; AOtherSeparators: string): TDoubleArray;
ReadSpaceSeparatedStringsnull140 function ReadSpaceSeparatedStrings(AInput: string; AOtherSeparators: string): TStringList;
141 //
142 procedure DeleteStyle(data,arg:pointer);
143 procedure ApplyGraphicAttributeToPenAndBrush(ANodeName, ANodeValue: string; var APen: TvPen; var ABrush: TvBrush);
144 procedure ApplyGraphicAttributeToEntity(ANodeName, ANodeValue: string; ADest: TvEntityWithPen);
145 procedure ApplyStyleByNameToEntity(AStyleName: string; ADest: TvEntityWithPen);
146 procedure ApplyTextStyleByNameToEntity(AStyleName: string; ADest: TvEntityWithPen);
147 procedure ApplyMasterPageToPage(AMasterPageName: string; ADest: TvVectorialPage);
148 //
149 procedure ReadStyleStyleNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument);
150 procedure ReadEnhancedGeometryNodeToTPath(ANode: TDOMNode; AData: TvVectorialPage; ADest: TPath; ADeltaX, ADeltaY: Double; var AInfo: TCustomShapeInfo);
151 procedure ConvertPathStringToTPath(AStr: string; AData: TvVectorialPage; ADest: TPath; ADeltaX, ADeltaY: Double; AInfo: TCustomShapeInfo);
ResolveEnhancedGeometryFormulanull152 function ResolveEnhancedGeometryFormula(AInput: TSVGToken; AInfo: TCustomShapeInfo): Double;
153 //
154 procedure ReadElement(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument);
155 procedure ReadCustomShapeNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument);
156 procedure ReadEllipseNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument);
157 procedure ReadFrameNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument);
158 procedure ReadLineNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument);
159 procedure ReadPathNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument);
ReadTextPSequenceNodenull160 function ReadTextPSequenceNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument): TvRichText;
ReadTextPNodenull161 function ReadTextPNode(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument): TvParagraph;
162 //
163 procedure ReadStylesMasterPage(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument);
164 procedure ReadStylesPageLayout(ANode: TDOMNode; AData: TvVectorialPage; ADoc: TvVectorialDocument);
165 //
166 procedure ParsePathString(AInputStr: string; ADest: TPath);
167 procedure GetDrawTransforms(AInputStr: string; out ASkewX, ASkewY, ARotate, ATranslateX, ATranslateY: Double);
ReadSVGColornull168 function ReadSVGColor(AValue: string): TFPColor;
GetAttrValuenull169 function GetAttrValue(ANode : TDOMNode; AAttrName : string) : string;
StringWithUnitToFloatnull170 function StringWithUnitToFloat(AStr: string): Double;
171 procedure ConvertODGCoordinatesToFPVCoordinates(
172 const AData: TvVectorialPage;
173 const ASrcX, ASrcY: Double; var ADestX, ADestY: Double);
174 procedure ConvertODGDeltaToFPVDelta(
175 const AData: TvVectorialPage;
176 const ASrcX, ASrcY: Double; var ADestX, ADestY: Double);
177 procedure ConvertViewBoxCoordinatesToFPVCoordinates(
178 const AData: TvVectorialPage; const AInfo: TCustomShapeInfo;
179 const ASrcX, ASrcY: Double; var ADestX, ADestY: Double);
180 procedure ConvertViewBoxDeltaToFPVDelta(
181 const AInfo: TCustomShapeInfo;
182 const ASrcX, ASrcY: Double; var ADestX, ADestY: Double);
183 procedure ConvertMilimiterCoordinatesToFPVCoordinates(
184 const AData: TvVectorialPage;
185 const ASrcX, ASrcY: Double; var ADestX, ADestY: Double);
186 public
187 { General reading methods }
188 constructor Create; override;
189 Destructor Destroy; override;
190 procedure ReadFromStream(AStream: TStream; AData: TvVectorialDocument); override;
191 procedure ReadFromFile(AFileName: string; AData: TvVectorialDocument); override;
192 procedure ReadFromContentXMLDocument(AXMLDocument: TXMLDocument; AData: TvVectorialDocument);
193 procedure ReadFromStylesXMLDocument(AXMLDocument: TXMLDocument; AData: TvVectorialDocument);
194 end;
195
196 implementation
197
198 const
199 { OpenDocument general XML constants }
200 XML_HEADER = '<?xml version="1.0" encoding="utf-8" ?>';
201
202 { OpenDocument Directory structure constants }
203 OPENDOC_PATH_CONTENT = 'content.xml';
204 OPENDOC_PATH_META = 'meta.xml';
205 OPENDOC_PATH_SETTINGS = 'settings.xml';
206 OPENDOC_PATH_STYLES = 'styles.xml';
207 OPENDOC_PATH_MIMETYPE = 'mimetype';
208 OPENDOC_PATH_METAINF = 'META-INF' + '/';
209 OPENDOC_PATH_METAINF_MANIFEST = 'META-INF' + '/' + 'manifest.xml';
210
211 { OpenDocument schemas constants }
212 SCHEMAS_XMLNS_OFFICE = 'urn:oasis:names:tc:opendocument:xmlns:office:1.0';
213 SCHEMAS_XMLNS_DCTERMS = 'http://purl.org/dc/terms/';
214 SCHEMAS_XMLNS_META = 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0';
215 SCHEMAS_XMLNS = 'http://schemas.openxmlformats.org/officeDocument/2006/extended-properties';
216 SCHEMAS_XMLNS_CONFIG = 'urn:oasis:names:tc:opendocument:xmlns:config:1.0';
217 SCHEMAS_XMLNS_OOO = 'http://openoffice.org/2004/office';
218 SCHEMAS_XMLNS_MANIFEST = 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0';
219 SCHEMAS_XMLNS_FO = 'urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0';
220 SCHEMAS_XMLNS_STYLE = 'urn:oasis:names:tc:opendocument:xmlns:style:1.0';
221 SCHEMAS_XMLNS_SVG = 'urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0';
222 SCHEMAS_XMLNS_TABLE = 'urn:oasis:names:tc:opendocument:xmlns:table:1.0';
223 SCHEMAS_XMLNS_TEXT = 'urn:oasis:names:tc:opendocument:xmlns:text:1.0';
224 SCHEMAS_XMLNS_V = 'urn:schemas-microsoft-com:vml';
225 SCHEMAS_XMLNS_NUMBER = 'urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0';
226 SCHEMAS_XMLNS_CHART = 'urn:oasis:names:tc:opendocument:xmlns:chart:1.0';
227 SCHEMAS_XMLNS_DR3D = 'urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0';
228 SCHEMAS_XMLNS_MATH = 'http://www.w3.org/1998/Math/MathML';
229 SCHEMAS_XMLNS_FORM = 'urn:oasis:names:tc:opendocument:xmlns:form:1.0';
230 SCHEMAS_XMLNS_SCRIPT = 'urn:oasis:names:tc:opendocument:xmlns:script:1.0';
231 SCHEMAS_XMLNS_OOOW = 'http://openoffice.org/2004/writer';
232 SCHEMAS_XMLNS_OOOC = 'http://openoffice.org/2004/calc';
233 SCHEMAS_XMLNS_DOM = 'http://www.w3.org/2001/xml-events';
234 SCHEMAS_XMLNS_XFORMS = 'http://www.w3.org/2002/xforms';
235 SCHEMAS_XMLNS_XSD = 'http://www.w3.org/2001/XMLSchema';
236 SCHEMAS_XMLNS_XSI = 'http://www.w3.org/2001/XMLSchema-instance';
237
238 // SVG requires hardcoding a DPI value
239
240 // The Opera Browser and Inkscape use 90 DPI, so we follow that
241
242 // 1 Inch = 25.4 milimiters
243 // 90 inches per pixel = (1 / 90) * 25.4 = 0.2822
244 // FLOAT_MILIMETERS_PER_PIXEL = 0.3528; // DPI 72 = 1 / 72 inches per pixel
245
246 FLOAT_MILIMETERS_PER_PIXEL = 0.2822; // DPI 90 = 1 / 90 inches per pixel
247 FLOAT_PIXELS_PER_MILIMETER = 3.5433; // DPI 90 = 1 / 90 inches per pixel
248
249 { TSVGPathTokenizer }
250
251 constructor TSVGPathTokenizer.Create;
252 begin
253 inherited Create;
254
255 FPointSeparator := DefaultFormatSettings;
256 FPointSeparator.DecimalSeparator := '.';
257 FPointSeparator.ThousandSeparator := '#';// disable the thousand separator
258
259 Tokens := TSVGTokenList.Create;
260 end;
261
262 destructor TSVGPathTokenizer.Destroy;
263 begin
264 Tokens.Free;
265
266 inherited Destroy;
267 end;
268
269 {
270 <draw:enhanced-geometry
271 svg:viewBox="0 0 1000 1000"
272 draw:modifiers="0 0"
273 draw:enhanced-path="M 0 1000 L $0 $1 1000 1000 Z N
274 M 0 1000 L ?f0 ?f1 F N">
275 <draw:equation draw:name="f0" draw:formula="($0 + right) / 2"/>
276 <draw:equation draw:name="f1" draw:formula="($1 + bottom) / 2"/>
277 <draw:equation draw:name="f2" draw:formula="2*(bottom - top) / 3"/>
278 <draw:handle draw:handle-position="$0 $1"
279 draw:handle-range-x-minimum="0"
280 draw:handle-range-x-maximum="right"
281 draw:handle-range-y-minimum="0"
282 draw:handle-range-y-maximum="?f2"/>
283 </draw:enhanced-geometry>
284 }
285 procedure TSVGPathTokenizer.AddToken(AStr: string);
286 var
287 lToken: TSVGToken;
288 lStr: string;
289 begin
290 lToken := TSVGToken.Create;
291
292 lStr := Trim(AStr);
293 if lStr = '' then Exit;
294
295 // Moves
296 case lStr[1] of
297 'M': lToken.TokenType := sttMoveTo;
298 'm': lToken.TokenType := sttRelativeMoveTo;
299 // Close Path
300 'Z': lToken.TokenType := sttClosePath;
301 'z': lToken.TokenType := sttClosePath;
302 // Lines
303 'L': lToken.TokenType := sttLineTo;
304 'l': lToken.TokenType := sttRelativeLineTo;
305 'H': lToken.TokenType := sttHorzLineTo;
306 'h': lToken.TokenType := sttRelativeHorzLineTo;
307 'V': lToken.TokenType := sttVertLineTo;
308 'v': lToken.TokenType := sttRelativeVertLineTo;
309 // cubic Bézier curve commands
310 'C': lToken.TokenType := sttBezierTo;
311 'c': lToken.TokenType := sttRelativeBezierTo;
312 // quadratic beziers
313 'Q': lToken.TokenType := sttQuadraticBezierTo;
314 'q': lToken.TokenType := sttRelativeQuadraticBezierTo;
315 // Elliptic curves
316 'A': lToken.TokenType := sttEllipticArcTo;
317 'a': lToken.TokenType := sttRelativeEllipticArcTo;
318 // U -> angle- ellipse
319 // (x y w h t0 t1) +
320 // The same as the “T” command, except that a implied moveto
321 // to the starting point is done.
322 // T -> angle- ellipseto
323 // (x y w h t0 t1) +
324 // Draws a segment of an ellipse. The ellipse is specified by
325 // the center(x, y), the size(w, h) and the start-angle t0 and end-angle t1.
326 'U', 'T': lToken.TokenType := sttEllipticArcToWithAngle;
327 // End path
328 // Ends the current set of sub-paths. The sub- paths will be filled
329 // by using the “even-odd” filling rule. Other following subpaths will
330 // be filled independently.
331 'N': lToken.TokenType := sttUnknown;
332 // Types that I don't know what they do
333 'e': lToken.TokenType := sttUnknown;
334 // Constants
335 '$':
336 begin
337 lToken.TokenType := sttConstantValue;
338 lToken.Value := 0;
339 lToken.StrValue := lStr;
340 end;
341 // Named Equations
342 '?':
343 begin
344 lToken.TokenType := sttNamedEquation;
345 lToken.Value := 0;
346 lToken.StrValue := lStr;
347 end;
348 else
349 lToken.TokenType := sttFloatValue;
350 lToken.Value := StrToFloat(AStr, FPointSeparator);
351 end;
352
353 // Sometimes we get a command glued to a value, for example M150
354 if (not (lToken.TokenType in [sttFloatValue, sttConstantValue, sttNamedEquation]))
355 and (Length(lStr) > 1) then
356 begin
357 Tokens.Add(lToken);
358 lToken.TokenType := sttFloatValue;
359 lStr := Copy(AStr, 2, Length(AStr));
360 lToken.Value := StrToFloat(lStr, FPointSeparator);
361 end;
362
363 Tokens.Add(lToken);
364 end;
365
366 procedure TSVGPathTokenizer.TokenizePathString(AStr: string);
367 const
368 Str_Space: Char = ' ';
369 Str_Comma: Char = ',';
370 var
371 i: Integer;
372 lTmpStr: string = '';
373 lState: Integer;
374 lFirstTmpStrChar, lCurChar: Char;
375 begin
376 lState := 0;
377
378 i := 1;
379 while i <= Length(AStr) do
380 begin
381 case lState of
382 0: // Adding to the tmp string
383 begin
384 lCurChar := AStr[i];
385 if lCurChar = Str_Space then
386 begin
387 lState := 1;
388 AddToken(lTmpStr);
389 lTmpStr := '';
390 end
391 else if lCurChar = Str_Comma then
392 begin
393 AddToken(lTmpStr);
394 lTmpStr := '';
395 end
396 else if lCurChar in ['?', '$'] then
397 begin
398 if lTmpStr <> '' then AddToken(lTmpStr);
399 lState := 2;
400 lTmpStr := lCurChar;
401 end
402 else
403 begin
404 // Check for a break, from letter to number
405 if (Length(lTmpStr) >= 1) then
406 begin
407 lFirstTmpStrChar := lTmpStr[1];
408 if ((lFirstTmpStrChar in ['a'..'z', 'A'..'Z']) and not (lCurChar in ['a'..'z', 'A'..'Z'])) or
409 (not (lFirstTmpStrChar in ['a'..'z', 'A'..'Z']) and (lCurChar in ['a'..'z', 'A'..'Z'])) then
410 begin
411 AddToken(lTmpStr);
412 lTmpStr := '';
413 Continue;
414 end;
415 end;
416
417 lTmpStr := lTmpStr + lCurChar;
418 end;
419
420 Inc(i);
421 end;
422 1: // Removing spaces
423 begin
424 if AStr[i] <> Str_Space then lState := 0
425 else Inc(i);
426 end;
427 2: // Adding a special group, such as "$2" or "?f3", which stop only at a space
428 begin
429 lCurChar := AStr[i];
430 if lCurChar = Str_Space then
431 begin
432 lState := 1;
433 AddToken(lTmpStr);
434 lTmpStr := '';
435 end
436 else
437 lTmpStr := lTmpStr + lCurChar;
438
439 Inc(i);
440 end;
441 end;
442 end;
443
444 // If there is a token still to be added, add it now
445 if (lState = 0) and (lTmpStr <> '') then AddToken(lTmpStr);
446 end;
447
448 procedure TSVGPathTokenizer.TokenizeFunctions(AStr: string);
449 const
450 Str_Space: Char = ' ';
451 Str_Start_Params: Char = '(';
452 Str_End_Params: Char = ')';
453 ListOfCommandLetters: set of Char = ['a'..'d', 'f'..'z', 'A'..'D', 'F'..'Z'];
454 var
455 i: Integer;
456 lTmpStr: string = '';
457 lState: Integer;
458 lFirstTmpStrChar, lCurChar: Char;
459 lToken: TSVGToken;
460 begin
461 lState := 0;
462
463 i := 1;
464 while i <= Length(AStr) do
465 begin
466 case lState of
467 0: // Adding to the tmp string
468 begin
469 lCurChar := AStr[i];
470 if lCurChar in [Str_Start_Params, Str_End_Params] then
471 begin
472 lState := 1;
473 // Add the token
474 lToken := TSVGToken.Create;
475 lToken.StrValue := lTmpStr;
476 Tokens.Add(lToken);
477 //
478 lTmpStr := '';
479 end
480 else
481 begin
482 lTmpStr := lTmpStr + lCurChar;
483 end;
484
485 Inc(i);
486 end;
487 1: // Removing spaces
488 begin
489 if AStr[i] <> Str_Space then lState := 0
490 else Inc(i);
491 end;
492 end;
493 end;
494
495 // If there is a token still to be added, add it now
496 if (lState = 0) and (lTmpStr <> '') then AddToken(lTmpStr);
497 end;
498
499
ReadSpaceSeparatedFloatsnull500 function TvODGVectorialReader.ReadSpaceSeparatedFloats(AInput: string;
501 AOtherSeparators: string): TDoubleArray;
502 var
503 lStrings: TStringList;
504 lInputStr: string;
505 lMatrixElements: array of Double;
506 i: Integer;
507 begin
508 lStrings := TStringList.Create;
509 try
510 lStrings.Delimiter := ' ';
511 // now other separator too
512 lInputStr := AInput;
513 for i := 1 to Length(AOtherSeparators) do
514 begin
515 lInputStr := StringReplace(lInputStr, AOtherSeparators[i], ' ', [rfReplaceAll]);
516 end;
517 //
518 lStrings.DelimitedText := lInputStr;
519 SetLength(lMatrixElements, lStrings.Count);
520 for i := 0 to lStrings.Count-1 do
521 begin
522 lMatrixElements[i] := StringWithUnitToFloat(lStrings.Strings[i]);
523 end;
524
525 Result := lMatrixElements;
526 finally
527 lStrings.Free;
528 end;
529 end;
530
ReadSpaceSeparatedStringsnull531 function TvODGVectorialReader.ReadSpaceSeparatedStrings(AInput: string;
532 AOtherSeparators: string): TStringList;
533 var
534 i: Integer;
535 lInputStr: String;
536 begin
537 Result := TStringList.Create;
538 Result.Delimiter := ' ';
539 // now other separator too
540 lInputStr := AInput;
541 for i := 1 to Length(AOtherSeparators) do
542 begin
543 lInputStr := StringReplace(lInputStr, AOtherSeparators[i], ' ', [rfReplaceAll]);
544 end;
545 //
546 Result.DelimitedText := lInputStr;
547 end;
548
549 procedure TvODGVectorialReader.DeleteStyle(data, arg: pointer);
550 begin
551 TObject(data).Free;
552 end;
553
554 procedure TvODGVectorialReader.ApplyGraphicAttributeToPenAndBrush(ANodeName,
555 ANodeValue: string; var APen: TvPen; var ABrush: TvBrush);
556 var
557 lColor: TFPColor;
558 begin
559 case ANodeName of
560 // "none", "solid"
561 'draw:fill':
562 begin
563 case ANodeValue of
564 'none': ABrush.Style := bsClear;
565 'solid': ABrush.Style := bsSolid;
566 end;
567 end;
568 // "#ffffff"
569 'draw:fill-color':
570 begin
571 lColor := ReadSVGColor(ANodeValue);
572 ABrush.Color := lColor;
573 end;
574 // Values: "justify", "center", "left"
575 'draw:textarea-horizontal-align':
576 begin
577 end;
578 // Values: "middle"
579 'draw:textarea-vertical-align':
580 begin
581 end;
582 // true-false
583 'draw:auto-grow-height':
584 begin
585 end;
586 // true-false
587 'draw:auto-grow-width':
588 begin
589 end;
590 // "none", "dash"
591 'draw:stroke':
592 begin
593 case ANodeValue of
594 'none': APen.Style := psClear;
595 'dash': APen.Style := psDash;
596 end;
597 end;
598 // "Fine_20_Dashed_20__28_var_29_"
599 'draw:stroke-dash':
600 begin
601 end;
602 // "Arrow"
603 'draw:marker-start':
604 begin
605 end;
606 // "0.45cm"
607 'draw:marker-start-width':
608 begin
609 end;
610 // "Circle"
611 'draw:marker-end':
612 begin
613 end;
614 // "0.45cm"
615 'draw:marker-end-width':
616 begin
617 end;
618 // "Transparency_20_1"
619 'draw:opacity-name':
620 begin
621 end;
622 // "0.1cm"
623 'svg:stroke-width': APen.Width := Round(StringWithUnitToFloat(ANodeValue));
624 // "#000000"
625 'svg:stroke-color': APen.Color := ReadSVGColor(ANodeValue);
626 // "0cm"
627 'fo:min-height':
628 begin
629 end;
630 // "0cm"
631 'fo:min-width':
632 begin
633 end;
634 // "wrap"
635 'fo:wrap-option':
636 begin
637 end;
638 // "0.175cm"
639 'fo:padding-top':
640 begin
641 end;
642 // "0.175cm"
643 'fo:padding-bottom':
644 begin
645 end;
646 // "0.3cm"
647 'fo:padding-left':
648 begin
649 end;
650 // "0.3cm"
651 'fo:padding-right':
652 begin
653 end;
654 end;
655 end;
656
657 procedure TvODGVectorialReader.ApplyGraphicAttributeToEntity(ANodeName,
658 ANodeValue: string; ADest: TvEntityWithPen);
659 var
660 DummyBrush: TvBrush;
661 begin
662 if (ADest is TvEntityWithPenAndBrush) then
663 begin
664 ApplyGraphicAttributeToPenAndBrush(ANodeName, ANodeValue, ADest.Pen,
665 (ADest as TvEntityWithPenAndBrush).Brush);
666 end
667 else
668 ApplyGraphicAttributeToPenAndBrush(ANodeName, ANodeValue, ADest.Pen, DummyBrush);
669 end;
670
671 // Don't apply font properties here, because there is a separate method for this
672 procedure TvODGVectorialReader.ApplyStyleByNameToEntity(AStyleName: string;
673 ADest: TvEntityWithPen);
674 var
675 i: Integer;
676 lCurStyle: TODGStyle;
677 begin
678 for i := 0 to FStyles.Count-1 do
679 begin
680 lCurStyle := TODGStyle(FStyles.Items[i]);
681 if lCurStyle.Name = AStyleName then
682 begin
683 ADest.AssignPen(lCurStyle.Pen);
684 if ADest is TvEntityWithPenAndBrush then
685 TvEntityWithPenAndBrush(ADest).AssignBrush(@lCurStyle.Brush);
686
687 Exit;
688 end;
689 end;
690 end;
691
692 procedure TvODGVectorialReader.ApplyTextStyleByNameToEntity(AStyleName: string;
693 ADest: TvEntityWithPen);
694 var
695 i: Integer;
696 lCurStyle: TODGStyle;
697 begin
698 for i := 0 to FStyles.Count-1 do
699 begin
700 lCurStyle := TODGStyle(FStyles.Items[i]);
701 if lCurStyle.Name = AStyleName then
702 begin
703 if ADest is TvEntityWithPenBrushAndFont then
704 TvEntityWithPenBrushAndFont(ADest).AssignFont(lCurStyle.Font);
705
706 Exit;
707 end;
708 end;
709 end;
710
711 procedure TvODGVectorialReader.ApplyMasterPageToPage(AMasterPageName: string;
712 ADest: TvVectorialPage);
713 var
714 i: Integer;
715 lMasterPage: TODGMasterPage;
716 lMasterPageLayout: TODGPageLayout;
717 begin
718 // Find the Master Page
719 for i := 0 to FMasterPages.Count-1 do
720 begin
721 lMasterPage := TODGMasterPage(FMasterPages.Items[i]);
722 if lMasterPage.Name = AMasterPageName then Break
723 else lMasterPage := nil;
724 end;
725
726 if lMasterPage = nil then
727 raise Exception.Create(Format('[TvODGVectorialReader.ApplyMasterPageToPage] Master page not found: %s', [AMasterPageName]));
728
729 // Find the Master Page Properties
730 for i := 0 to FPageLayouts.Count-1 do
731 begin
732 lMasterPageLayout := TODGPageLayout(FPageLayouts.Items[i]);
733 if lMasterPageLayout.Name = lMasterPage.PageLayoutName then Break
734 else lMasterPageLayout := nil;
735 end;
736
737 if lMasterPageLayout = nil then
738 raise Exception.Create(Format('[TvODGVectorialReader.ApplyMasterPageToPage] Master page layout not found: %s', [lMasterPage.PageLayoutName]));
739
740 ADest.Width := lMasterPageLayout.PageWidth;
741 ADest.Height := lMasterPageLayout.PageHeight;
742 end;
743
744 {
745 <style:style style:name="gr2" style:family="graphic" style:parent-style-name="standard">
746 <style:graphic-properties draw:fill="solid" draw:fill-color="#ffff99"
747 draw:textarea-horizontal-align="center" draw:textarea-vertical-align="middle"/>
748 </style:style>
749 }
750 procedure TvODGVectorialReader.ReadStyleStyleNode(ANode: TDOMNode;
751 AData: TvVectorialPage; ADoc: TvVectorialDocument);
752 var
753 lStyle: TvStyle;
754 i: Integer;
755 lGraphicPropertiesNode: TDOMNode;
756 lNodeName, lNodeValue: DOMString;
757 begin
758 lStyle := TODGStyle.Create();
759
760 // Read attributes of the main style tag
761 // <style:style style:name="gr4" style:family="graphic" style:parent-style-name="standard">;
762 for i := 0 to ANode.Attributes.Length - 1 do
763 begin
764 lNodeName := LowerCase(ANode.Attributes.Item[i].NodeName);
765 lNodeValue := ANode.Attributes.Item[i].NodeValue;
766 case lNodeName of
767 'style:name': lStyle.Name := lNodeValue;
768 //'style:family'
769 'style:parent-style-name':
770 begin
771 lNodeValue := LowerCase(lNodeValue);
772 case lNodeValue of
773 // "standard"
774 'standard': Continue;
775 // "objectwithoutfill"
776 'objectwithoutfill':
777 begin
778 lStyle.Brush.Style := bsClear;
779 end;
780 end;
781 end;
782 end;
783 end;
784
785 // Read graphic properties
786 lGraphicPropertiesNode := ANode.FindNode('style:graphic-properties');
787 if lGraphicPropertiesNode <> nil then
788 begin
789 for i := 0 to lGraphicPropertiesNode.Attributes.Length - 1 do
790 begin
791 lNodeName := LowerCase(lGraphicPropertiesNode.Attributes.Item[i].NodeName);
792 lNodeValue := LowerCase(lGraphicPropertiesNode.Attributes.Item[i].NodeValue);
793 ApplyGraphicAttributeToPenAndBrush(lNodeName, lNodeValue, lStyle.Pen, lStyle.Brush);
794 end;
795 end;
796 FStyles.Add(lStyle);
797 end;
798
799 {
800 <draw:custom-shape draw:style-name="gr1" draw:text-style-name="P1" draw:layer="layout"
801 svg:width="5.2cm" svg:height="1.3cm" svg:x="2.6cm" svg:y="3cm">
802 <text:p text:style-name="P1">Rectangle</text:p>
803 <draw:enhanced-geometry svg:viewBox="0 0 21600 21600" draw:type="rectangle"
804 draw:enhanced-path="M 0 0 L 21600 0 21600 21600 0 21600 0 0 Z N"/>
805 </draw:custom-shape>
806
807 For the drawing units see http://stackoverflow.com/questions/15335926/svg-viewbox-attribute
808 }
809 procedure TvODGVectorialReader.ReadEnhancedGeometryNodeToTPath(ANode: TDOMNode;
810 AData: TvVectorialPage; ADest: TPath; ADeltaX, ADeltaY: Double; var AInfo: TCustomShapeInfo);
811 var
812 i, Len: Integer;
813 lNodeName, lNodeValue, lAttrName, lAttrValue: string;
814 l10Strings: T10Strings;
815 lCurNode: TDOMNode;
816 begin
817 // Initial values to avoid invalid initial ones
818 if AInfo = nil then AInfo := TCustomShapeInfo.Create;
819 AInfo.ViewBox_Left := 0;
820 AInfo.ViewBox_Top := 0;
821 AInfo.ViewBox_Width := 10000;
822 AInfo.ViewBox_Height := 10000;
823
824 // First of all we need the viewBox, or else we can't map the coordinates
825 for i := 0 to ANode.Attributes.Length - 1 do
826 begin
827 lNodeName := ANode.Attributes.Item[i].NodeName;
828 lNodeValue := ANode.Attributes.Item[i].NodeValue;
829 if (lNodeName = 'draw:viewBox') or (lNodeName = 'svg:viewBox') then
830 begin
831 // From OpenDocument 1.1 specs page 300
832 // The syntax for using this attribute is the same as the [SVG] syntax.
833 // The value of the attribute are four numbers separated by white spaces,
834 // which define the left, top, right, and bottom dimensions of the user
835 // coordinate system.
836 l10Strings := SeparateString(lNodeValue, ' ');
837 AInfo.ViewBox_Left := StringWithUnitToFloat(l10Strings[0]);
838 AInfo.ViewBox_Top := StringWithUnitToFloat(l10Strings[1]);
839 AInfo.ViewBox_Width := StringWithUnitToFloat(l10Strings[2]) - AInfo.ViewBox_Left;
840 AInfo.ViewBox_Height := StringWithUnitToFloat(l10Strings[3]) - AInfo.ViewBox_Top;
841 end;
842 end;
843
844 // Read child elements
845 lCurNode := ANode.FirstChild;
846 while lCurNode <> nil do
847 begin
848 lNodeName := lCurNode.NodeName;
849
850 case lNodeName of
851 // Read variables
852 // <draw:equation draw:name="f0" draw:formula="$0 " />
853 // <draw:equation draw:name="f1" draw:formula="10800-$0 " />
854 'draw:equation':
855 begin
856 for i := 0 to lCurNode.Attributes.Length - 1 do
857 begin
858 lAttrName := lCurNode.Attributes.Item[i].NodeName;
859 lAttrValue := lCurNode.Attributes.Item[i].NodeValue;
860
861 if (lAttrName = 'draw:name') then
862 begin
863 Len := Length(AInfo.Formulas);
864 SetLength(AInfo.Formulas, Len+1);
865 AInfo.Formulas[Len] := TvFormula.Create(nil);
866 AInfo.Formulas[Len].Name := lAttrValue;
867 end
868 else if (lAttrName = 'draw:formula') then
869 begin
870 Len := Length(AInfo.Formulas);
871
872 // Replace the formulas and variables
873 lAttrValue := StringReplace(lAttrValue, '$0', '0', [rfReplaceAll, rfIgnoreCase]);
874 lAttrValue := StringReplace(lAttrValue, '$1', '0', [rfReplaceAll, rfIgnoreCase]);
875 lAttrValue := StringReplace(lAttrValue, '$2', '0', [rfReplaceAll, rfIgnoreCase]);
876 lAttrValue := StringReplace(lAttrValue, '$3', '0', [rfReplaceAll, rfIgnoreCase]);
877 lAttrValue := StringReplace(lAttrValue, '?f0', '0', [rfReplaceAll, rfIgnoreCase]);
878 lAttrValue := StringReplace(lAttrValue, '?f1', '0', [rfReplaceAll, rfIgnoreCase]);
879 lAttrValue := StringReplace(lAttrValue, '?f2', '0', [rfReplaceAll, rfIgnoreCase]);
880 lAttrValue := StringReplace(lAttrValue, '?f3', '0', [rfReplaceAll, rfIgnoreCase]);
881 lAttrValue := StringReplace(lAttrValue, '?f4', '0', [rfReplaceAll, rfIgnoreCase]);
882 lAttrValue := StringReplace(lAttrValue, '?f5', '0', [rfReplaceAll, rfIgnoreCase]);
883 lAttrValue := StringReplace(lAttrValue, '?f6', '0', [rfReplaceAll, rfIgnoreCase]);
884 lAttrValue := StringReplace(lAttrValue, '?f7', '0', [rfReplaceAll, rfIgnoreCase]);
885 lAttrValue := StringReplace(lAttrValue, '?f8', '0', [rfReplaceAll, rfIgnoreCase]);
886 lAttrValue := StringReplace(lAttrValue, '?f9', '0', [rfReplaceAll, rfIgnoreCase]);
887 lAttrValue := StringReplace(lAttrValue, '?f10', '0', [rfReplaceAll, rfIgnoreCase]);
888 lAttrValue := StringReplace(lAttrValue, 'right', FloatToStr(AInfo.ViewBox_Left + AInfo.ViewBox_Width, FPointSeparator), [rfReplaceAll, rfIgnoreCase]);
889 lAttrValue := StringReplace(lAttrValue, 'bottom', FloatToStr(AInfo.ViewBox_Top + AInfo.ViewBox_Height, FPointSeparator), [rfReplaceAll, rfIgnoreCase]);
890
891 AInfo.Formulas[Len-1].AddItemsByConvertingInfixStringToRPN(lAttrValue);
892 end;
893 end;
894 end;
895 // <draw:handle draw:handle-position="$0 10800" draw:handle-range-x-minimum="0"
896 // draw:handle-range-x-maximum="10800" />
897 'draw:handle':
898 begin
899
900 end;
901 end;
902
903 lCurNode := lCurNode.NextSibling;
904 end;
905
906
907 // read the attributes
908 for i := 0 to ANode.Attributes.Length - 1 do
909 begin
910 lNodeName := ANode.Attributes.Item[i].NodeName;
911 lNodeValue := ANode.Attributes.Item[i].NodeValue;
912 if lNodeName = 'draw:enhanced-path' then
913 begin
914 ConvertPathStringToTPath(lNodeValue, AData, ADest, ADeltaX, ADeltaY, AInfo);
915 end;
916 end;
917 end;
918
919 procedure TvODGVectorialReader.ConvertPathStringToTPath(AStr: string;
920 AData: TvVectorialPage; ADest: TPath; ADeltaX, ADeltaY: Double; AInfo: TCustomShapeInfo);
921 var
922 x1, y1, x2, y2: double;
923 t1, t2, lSrcX, lSrcY, lDestX, lDestY: Double;
924 j: Integer;
925 lTokenizer: TSVGPathTokenizer;
926 CurToken: TSVGToken;
927 begin
928 x1 := 0.0;
929 y1 := 0.0;
930 x2 := 0.0;
931 y2 := 0.0;
932
933 lTokenizer := TSVGPathTokenizer.Create;
934 try
935 lTokenizer.TokenizePathString(AStr);
936
937 j := 0;
938 while j < lTokenizer.Tokens.Count do
939 begin
940 CurToken := TSVGToken(lTokenizer.Tokens.Items[j]);
941
942 case CurToken.TokenType of
943 // moves
944 sttMoveTo, sttRelativeMoveTo:
945 begin
946 CurToken := TSVGToken(lTokenizer.Tokens.Items[j+1]);
947 x1 := CurToken.Value + ADeltaX;
948 CurToken := TSVGToken(lTokenizer.Tokens.Items[j+2]);
949 y1 := CurToken.Value + ADeltaY;
950 ConvertODGCoordinatesToFPVCoordinates(
951 AData, x1, y1, x1, y1);
952 ADest.AppendMoveToSegment(x1, y1);
953 Inc(j, 3);
954 end;
955 // Close Path
956 sttClosePath: Inc(j);
957 // lines
958 sttLineTo, sttRelativeLineTo,
959 sttHorzLineTo, sttRelativeHorzLineTo, sttVertLineTo, sttRelativeVertLineTo:
960 begin
961 Inc(j);
962 while TSVGToken(lTokenizer.Tokens.Items[j]).TokenType = sttFloatValue do
963 begin
964 CurToken := TSVGToken(lTokenizer.Tokens.Items[j+1]);
965 x1 := CurToken.Value + ADeltaX;
966 CurToken := TSVGToken(lTokenizer.Tokens.Items[j+2]);
967 y1 := CurToken.Value + ADeltaY;
968 ConvertODGCoordinatesToFPVCoordinates(
969 AData, x1, y1, x1, y1);
970 ADest.AppendLineToSegment(x1, y1);
971
972 Inc(j, 2);
973
974 if j >= lTokenizer.Tokens.Count then Break;
975 end;
976 end;
977 { // cubic beziers
978 sttBezierTo, sttRelativeBezierTo,
979 // quadratic beziers
980 sttQuadraticBezierTo, sttRelativeQuadraticBezierTo,}
981 // Elliptic curves
982 //
983 // A -> arcto
984 // (x1 y1 x2 y2 x3 y3 x y) +
985 // (x1, y1) and (x2, y2) is defining the bounding box of a ellipse.
986 // A line is then drawn from the current point to the start angle of
987 // the arc that is specified by the radial vector of point (x3, y3)
988 // and then counter clockwise to the end-angle that is specified by point (x4, y4).
989 //
990 // T -> angle- ellipseto
991 // (x y w h t0 t1) +
992 // Draws a segment of an ellipse. The ellipse is specified by
993 // the center(x, y), the size(w, h) and the start-angle t0 and end-angle t1.
994 sttEllipticArcTo, sttRelativeEllipticArcTo, sttEllipticArcToWithAngle:
995 begin
996 CurToken := TSVGToken(lTokenizer.Tokens.Items[j+1]);
997 x1 := CurToken.Value;
998 CurToken := TSVGToken(lTokenizer.Tokens.Items[j+2]);
999 y1 := CurToken.Value;
1000 CurToken := TSVGToken(lTokenizer.Tokens.Items[j+3]);
1001 x2 := ResolveEnhancedGeometryFormula(CurToken, AInfo) / 2;
1002 CurToken := TSVGToken(lTokenizer.Tokens.Items[j+4]);
1003 y2 := ResolveEnhancedGeometryFormula(CurToken, AInfo) / 2;
1004 CurToken := TSVGToken(lTokenizer.Tokens.Items[j+5]);
1005 t1 := CurToken.Value;
1006 t1 := DegToRad(t1);
1007 CurToken := TSVGToken(lTokenizer.Tokens.Items[j+6]);
1008 t2 := CurToken.Value;
1009 t2 := DegToRad(t2);
1010
1011 ConvertViewBoxCoordinatesToFPVCoordinates(AData, AInfo, x1, y1, x1, y1);
1012
1013 ConvertViewBoxDeltaToFPVDelta(AInfo, x2, y2, x2, y2);
1014
1015 // Parametrized Ellipse equation
1016 lSrcX := x2 * Cos(t1) + x1;
1017 lSrcY := y2 * Sin(t1) + y1;
1018 lDestX := x2 * Cos(t2) + x1;
1019 lDestY := y2 * Sin(t2) + y1;
1020
1021 // See http://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands
1022 ADest.AppendMoveToSegment(lSrcX, lSrcY);
1023 ADest.AppendEllipticalArcWithCenter(x2, y2, 0, lDestX, lDestY, x1, y1, t2 > t1);
1024
1025 Inc(j, 7);
1026 end;
1027 {// numbers
1028 sttFloatValue);}
1029 else
1030 Inc(j);
1031 //raise Exception.Create('[TvODGVectorialReader.ConvertPathStringToTPath] Unexpected token type!');
1032 end;
1033 end;
1034 finally
1035 lTokenizer.Free;
1036 end;
1037 end;
1038
ResolveEnhancedGeometryFormulanull1039 function TvODGVectorialReader.ResolveEnhancedGeometryFormula(AInput: TSVGToken;
1040 AInfo: TCustomShapeInfo): Double;
1041 var
1042 i: Integer;
1043 lStr: string;
1044 begin
1045 Result := 0;
1046
1047 case AInput.TokenType of
1048 sttFloatValue: Result := AInput.Value;
1049 sttNamedEquation:
1050 begin
1051 for i := 0 to Length(AInfo.Formulas)-1 do
1052 begin
1053 lStr := '?' + AInfo.Formulas[i].Name;
1054 if lStr = AInput.StrValue then
1055 Result := AInfo.Formulas[i].CalculateRPNFormulaValue();
1056 end;
1057 end;
1058 end;
1059 end;
1060
1061 procedure TvODGVectorialReader.ReadElement(ANode: TDOMNode;
1062 AData: TvVectorialPage; ADoc: TvVectorialDocument);
1063 var
1064 Str: String;
1065 begin
1066 Str := LowerCase(ANode.NodeName);
1067 case Str of
1068 'draw:custom-shape': ReadCustomShapeNode(ANode, AData, ADoc);
1069 'draw:ellipse': ReadEllipseNode(ANode, AData, ADoc);
1070 'draw:frame': ReadFrameNode(ANode, AData, ADoc);
1071 'draw:line': ReadLineNode(ANode, AData, ADoc);
1072 'draw:path': ReadPathNode(ANode, AData, ADoc);
1073 end;
1074 end;
1075
1076 {
1077 <draw:custom-shape draw:style-name="gr1" draw:text-style-name="P1" draw:layer="layout"
1078 svg:width="5.2cm" svg:height="1.3cm" svg:x="2.6cm" svg:y="3cm">
1079 <text:p text:style-name="P1">Rectangle</text:p>
1080 <draw:enhanced-geometry svg:viewBox="0 0 21600 21600" draw:type="rectangle"
1081 draw:enhanced-path="M 0 0 L 21600 0 21600 21600 0 21600 0 0 Z N"/>
1082 </draw:custom-shape>
1083 }
1084 procedure TvODGVectorialReader.ReadCustomShapeNode(ANode: TDOMNode;
1085 AData: TvVectorialPage; ADoc: TvVectorialDocument);
1086 var
1087 x1, y1, x2, y2, lWidth, lHeight: double;
1088 i: Integer;
1089 lNodeName, lNodeValue: string;
1090 lCurNode: TDOMNode;
1091 lSkewX, lSkewY, lRotate, lTranslateX, lTranslateY: Double;
1092 // various possible custom shape types
1093 lGroup: TvEntityWithSubEntities;
1094 lPath: TPath;
1095 lText: TvText;
1096 lInfo: TCustomShapeInfo;
1097 begin
1098 x1 := 0.0;
1099 y1 := 0.0;
1100 x2 := 0.0;
1101 y2 := 0.0;
1102 lWidth := 0.0;
1103 lHeight := 0.0;
1104
1105 lSkewX := 0.0;
1106 lSkewY := 0.0;
1107 lRotate := 0.0;
1108 lTranslateX := 0.0;
1109 lTranslateY := 0.0;
1110
1111 lGroup := TvEntityWithSubEntities.Create(AData);
1112 lPath := TPath.Create(Adata);
1113 lGroup.AddEntity(lPath);
1114
1115 // read the attributes
1116 for i := 0 to ANode.Attributes.Length - 1 do
1117 begin
1118 lNodeName := ANode.Attributes.Item[i].NodeName;
1119 lNodeValue := ANode.Attributes.Item[i].NodeValue;
1120 if lNodeName = 'svg:width' then
1121 lWidth := StringWithUnitToFloat(lNodeValue)
1122 else if lNodeName = 'svg:height' then
1123 lHeight := StringWithUnitToFloat(lNodeValue)
1124 else if lNodeName = 'draw:transform' then
1125 GetDrawTransforms(ANode.Attributes.Item[i].NodeValue, lSkewX, lSkewY, lRotate, lTranslateX, lTranslateY)
1126 else if lNodeName = 'svg:x' then
1127 x1 := StringWithUnitToFloat(lNodeValue)
1128 else if lNodeName = 'svg:y' then
1129 y1 := StringWithUnitToFloat(lNodeValue)
1130 else if lNodeName = 'draw:style-name' then
1131 ApplyStyleByNameToEntity(lNodeValue, lPath)
1132 else if lNodeName = 'draw:text-style-name' then
1133 ApplyTextStyleByNameToEntity(lNodeValue, lPath);
1134 // else if lNodeName = 'id' then
1135 // lEllipse.Name := UTF16ToUTF8(ANode.Attributes.Item[i].NodeValue)
1136 end;
1137
1138 ConvertMilimiterCoordinatesToFPVCoordinates(AData, x1, y1, x2, y2);
1139
1140 // Go through sub-nodes
1141 lCurNode := ANode.FirstChild;
1142 while lCurNode <> nil do
1143 begin
1144 lNodeName := lCurNode.NodeName;
1145
1146 case lNodeName of
1147 'text:p':
1148 begin
1149 if lCurNode.FirstChild <> nil then
1150 begin
1151 lText := TvText.Create(AData);
1152 lNodeValue := lCurNode.FirstChild.NodeValue;
1153 lText.Value.Add(lNodeValue);
1154 lGroup.AddEntity(lText);
1155 end;
1156 end;
1157 'draw:enhanced-geometry':
1158 begin
1159 lInfo := TCustomShapeInfo.Create;
1160 try
1161 lInfo.Left := x1 + lTranslateX;
1162 lInfo.Top := y1 + lTranslateY;
1163 lInfo.Width := lWidth;
1164 lInfo.Height := lHeight;
1165 ReadEnhancedGeometryNodeToTPath(lCurNode, AData, lPath, x1, y1, lInfo);
1166 finally
1167 lInfo.Free;
1168 end;
1169 end;
1170 end;
1171
1172 lCurNode := lCurNode.NextSibling;
1173 end;
1174
1175 AData.AddEntity(lGroup);
1176 end;
1177
1178 {
1179 <draw:ellipse
1180 draw:style-name="gr2" draw:text-style-name="P1" draw:layer="layout"
1181 svg:width="11cm" svg:height="3cm" svg:x="5.5cm" svg:y="6.5cm">
1182 <text:p/>
1183 </draw:ellipse>
1184 }
1185 procedure TvODGVectorialReader.ReadEllipseNode(ANode: TDOMNode;
1186 AData: TvVectorialPage; ADoc: TvVectorialDocument);
1187 var
1188 cx, cy, crx, cry: double;
1189 lEllipse: TvEllipse;
1190 i: Integer;
1191 lNodeName: DOMString;
1192 begin
1193 cx := 0.0;
1194 cy := 0.0;
1195 crx := 0.0;
1196 cry := 0.0;
1197
1198 lEllipse := TvEllipse.Create(AData);
1199 // SVG entities start without any pen drawing, but with a black brush
1200 lEllipse.Pen.Style := psClear;
1201 lEllipse.Brush.Style := bsSolid;
1202 lEllipse.Brush.Color := colBlack;
1203
1204 // read the attributes
1205 for i := 0 to ANode.Attributes.Length - 1 do
1206 begin
1207 lNodeName := ANode.Attributes.Item[i].NodeName;
1208 if lNodeName = 'svg:x' then
1209 cx := StringWithUnitToFloat(ANode.Attributes.Item[i].NodeValue)
1210 else if lNodeName = 'svg:y' then
1211 cy := StringWithUnitToFloat(ANode.Attributes.Item[i].NodeValue)
1212 else if lNodeName = 'svg:width' then
1213 crx := StringWithUnitToFloat(ANode.Attributes.Item[i].NodeValue) / 2
1214 else if lNodeName = 'svg:height' then
1215 cry := StringWithUnitToFloat(ANode.Attributes.Item[i].NodeValue) / 2
1216 else if lNodeName = 'draw:style-name' then
1217 ApplyStyleByNameToEntity(ANode.Attributes.Item[i].NodeValue, lEllipse)
1218 else if lNodeName = 'draw:text-style-name' then
1219 ApplyTextStyleByNameToEntity(ANode.Attributes.Item[i].NodeValue, lEllipse)
1220 // else if lNodeName = 'id' then
1221 // lEllipse.Name := UTF16ToUTF8(ANode.Attributes.Item[i].NodeValue)
1222 else
1223 ApplyGraphicAttributeToEntity(lNodeName, ANode.Attributes.Item[i].NodeValue, lEllipse);
1224 end;
1225
1226 // The svg:x and svg:y coordinates are relative to the top-left in ODG,
1227 // but in fpvectorial we use the center, so correct now
1228 cx := cx + crx;
1229 cy := cy + cry;
1230
1231 ConvertODGCoordinatesToFPVCoordinates(
1232 AData, cx, cy, lEllipse.X, lEllipse.Y);
1233 ConvertODGDeltaToFPVDelta(
1234 AData, crx, cry, lEllipse.HorzHalfAxis, lEllipse.VertHalfAxis);
1235
1236 AData.AddEntity(lEllipse);
1237 end;
1238
1239 {
1240 <draw:frame draw:style-name="gr12" draw:text-style-name="P2" draw:layer="layout" svg:width="4.5cm" svg:height="1.25cm" svg:x="4cm" svg:y="20cm">
1241 <draw:text-box>
1242 <text:p text:style-name="P2">
1243 <text:span text:style-name="T1">Kesäyö</text:span>
1244 </text:p>
1245 </draw:text-box>
1246 </draw:frame>
1247 }
1248 procedure TvODGVectorialReader.ReadFrameNode(ANode: TDOMNode;
1249 AData: TvVectorialPage; ADoc: TvVectorialDocument);
1250 var
1251 x1, y1, x2, y2, lWidth, lHeight: double;
1252 i: Integer;
1253 lNodeName, lNodeValue, lSubNodeName, lSubNodeValue: string;
1254 lCurNode, lCurSubNode: TDOMNode;
1255 lSkewX, lSkewY, lRotate, lTranslateX, lTranslateY: Double;
1256 // various possible frame types contents
1257 lGroup: TvEntityWithSubEntities;
1258 lBorder: TvRectangle;
1259 lRichText: TvRichText;
1260 begin
1261 x1 := 0.0;
1262 y1 := 0.0;
1263 x2 := 0.0;
1264 y2 := 0.0;
1265 lWidth := 0.0;
1266 lHeight := 0.0;
1267
1268 lSkewX := 0.0;
1269 lSkewY := 0.0;
1270 lRotate := 0.0;
1271 lTranslateX := 0.0;
1272 lTranslateY := 0.0;
1273
1274 lGroup := TvEntityWithSubEntities.Create(AData);
1275 lBorder := TvRectangle.Create(Adata);
1276 lGroup.AddEntity(lBorder);
1277
1278 // read the attributes
1279 for i := 0 to ANode.Attributes.Length - 1 do
1280 begin
1281 lNodeName := ANode.Attributes.Item[i].NodeName;
1282 lNodeValue := ANode.Attributes.Item[i].NodeValue;
1283 if lNodeName = 'svg:width' then
1284 lWidth := StringWithUnitToFloat(lNodeValue)
1285 else if lNodeName = 'svg:height' then
1286 lHeight := StringWithUnitToFloat(lNodeValue)
1287 //else if lNodeName = 'draw:transform' then
1288 //GetDrawTransforms(ANode.Attributes.Item[i].NodeValue, lSkewX, lSkewY, lRotate, lTranslateX, lTranslateY)
1289 else if lNodeName = 'svg:x' then
1290 x1 := StringWithUnitToFloat(lNodeValue)
1291 else if lNodeName = 'svg:y' then
1292 y1 := StringWithUnitToFloat(lNodeValue)
1293 //else if lNodeName = 'draw:style-name' then
1294 //ApplyStyleByNameToEntity(lNodeValue, lPath)
1295 //else if lNodeName = 'draw:text-style-name' then
1296 //ApplyTextStyleByNameToEntity(lNodeValue, lPath);
1297 // else if lNodeName = 'id' then
1298 // lEllipse.Name := UTF16ToUTF8(ANode.Attributes.Item[i].NodeValue)
1299 end;
1300
1301 ConvertMilimiterCoordinatesToFPVCoordinates(AData, x1, y1, x2, y2);
1302 lGroup.X := x2;
1303 lGroup.Y := y2;
1304
1305 // Go through sub-nodes
1306 lCurNode := ANode.FirstChild;
1307 while lCurNode <> nil do
1308 begin
1309 lNodeName := lCurNode.NodeName;
1310
1311 case lNodeName of
1312 'draw:text-box':
1313 begin
1314 lRichText := ReadTextPSequenceNode(lCurNode, AData, ADoc);
1315 lRichText.X := x2;
1316 lRichText.Y := y2;
1317 lGroup.AddEntity(lRichText);
1318 end;
1319 end;
1320
1321 lCurNode := lCurNode.NextSibling;
1322 end;
1323
1324 AData.AddEntity(lGroup);
1325 end;
1326
1327 {
1328 <draw:line draw:style-name="gr9" draw:text-style-name="P1" draw:layer="layout"
1329 svg:x1="14cm" svg:y1="22.5cm" svg:x2="14cm" svg:y2="23.5cm"><text:p/></draw:line>
1330 <draw:line draw:style-name="gr9" draw:text-style-name="P1" draw:layer="layout"
1331 svg:x1="14cm" svg:y1="23.5cm" svg:x2="13.5cm" svg:y2="25.5cm"><text:p/></draw:line>
1332 }
1333 procedure TvODGVectorialReader.ReadLineNode(ANode: TDOMNode;
1334 AData: TvVectorialPage; ADoc: TvVectorialDocument);
1335 var
1336 x1, y1, x2, y2: double;
1337 lPath: TPath;
1338 i: Integer;
1339 lNodeName, lNodeValue: DOMString;
1340 begin
1341 x1 := 0.0;
1342 y1 := 0.0;
1343 x2 := 0.0;
1344 y2 := 0.0;
1345
1346 lPath := TPath.Create(nil);
1347
1348 // read the attributes
1349 for i := 0 to ANode.Attributes.Length - 1 do
1350 begin
1351 lNodeName := ANode.Attributes.Item[i].NodeName;
1352 lNodeValue := ANode.Attributes.Item[i].NodeValue;
1353 if lNodeName = 'svg:x1' then
1354 x1 := StringWithUnitToFloat(lNodeValue)
1355 else if lNodeName = 'svg:y1' then
1356 y1 := StringWithUnitToFloat(lNodeValue)
1357 else if lNodeName = 'svg:x2' then
1358 x2 := StringWithUnitToFloat(lNodeValue)
1359 else if lNodeName = 'svg:y2' then
1360 y2 := StringWithUnitToFloat(lNodeValue)
1361 else if lNodeName = 'draw:style-name' then
1362 ApplyStyleByNameToEntity(lNodeValue, lPath)
1363 else if lNodeName = 'draw:text-style-name' then
1364 ApplyTextStyleByNameToEntity(lNodeValue, lPath)
1365 // else if lNodeName = 'id' then
1366 // lEllipse.Name := UTF16ToUTF8(lNodeValue)
1367 else
1368 ApplyGraphicAttributeToEntity(lNodeName, lNodeValue, lPath);
1369 end;
1370
1371 ConvertODGCoordinatesToFPVCoordinates(
1372 AData, x1, y1, x1, y1);
1373 ConvertODGCoordinatesToFPVCoordinates(
1374 AData, x2, y2, x2, y2);
1375
1376 lPath.AppendMoveToSegment(x1, y1);
1377 lPath.AppendLineToSegment(x2, y2);
1378 AData.AddEntity(lPath);
1379 end;
1380
1381 {
1382 <draw:path draw:style-name="gr11" draw:text-style-name="P1" draw:layer="layout"
1383 svg:width="9.429cm" svg:height="4.491cm"
1384 draw:transform="skewX (-4.3967693286338E-017) rotate (0.417482757076965) translate (6.73314019682066cm 26.0928070675985cm)"
1385 svg:viewBox="0 0 9430 4492" svg:d="m0 5c688-5 1345-23 2075 66 1374 167-412 989 814 1282 591 141 1129 504 1795 401 694-107 1142 607 1686 945 551 342 1077 719 1509 1195l501 355 549 243 501-238">
1386 <text:p/></draw:path>
1387 }
1388 procedure TvODGVectorialReader.ReadPathNode(ANode: TDOMNode;
1389 AData: TvVectorialPage; ADoc: TvVectorialDocument);
1390 var
1391 x1, y1, x2, y2, lWidth, lHeight: double;
1392 lPath: TPath;
1393 i: Integer;
1394 lNodeName: DOMString;
1395 lSkewX, lSkewY, lRotate, lTranslateX, lTranslateY: Double;
1396 begin
1397 x1 := 0.0;
1398 y1 := 0.0;
1399 x2 := 0.0;
1400 y2 := 0.0;
1401 lWidth := 0.0;
1402 lHeight := 0.0;
1403
1404 lPath := TPath.Create(nil);
1405
1406 // read the attributes
1407 for i := 0 to ANode.Attributes.Length - 1 do
1408 begin
1409 lNodeName := ANode.Attributes.Item[i].NodeName;
1410 if lNodeName = 'svg:width' then
1411 lWidth := StringWithUnitToFloat(ANode.Attributes.Item[i].NodeValue)
1412 else if lNodeName = 'svg:height' then
1413 lHeight := StringWithUnitToFloat(ANode.Attributes.Item[i].NodeValue)
1414 else if lNodeName = 'draw:transform' then
1415 GetDrawTransforms(ANode.Attributes.Item[i].NodeValue, lSkewX, lSkewY, lRotate, lTranslateX, lTranslateY)
1416 else if lNodeName = 'svg:d' then
1417 ParsePathString(ANode.Attributes.Item[i].NodeValue, lPath)
1418 else if lNodeName = 'draw:style-name' then
1419 ApplyStyleByNameToEntity(ANode.Attributes.Item[i].NodeValue, lPath)
1420 else if lNodeName = 'draw:text-style-name' then
1421 ApplyTextStyleByNameToEntity(ANode.Attributes.Item[i].NodeValue, lPath)
1422 // else if lNodeName = 'id' then
1423 // lEllipse.Name := UTF16ToUTF8(ANode.Attributes.Item[i].NodeValue)
1424 else
1425 ApplyGraphicAttributeToEntity(lNodeName, ANode.Attributes.Item[i].NodeValue, lPath);
1426 end;
1427
1428 ConvertODGCoordinatesToFPVCoordinates(
1429 AData, x1, y1, x1, y1);
1430 ConvertODGCoordinatesToFPVCoordinates(
1431 AData, x2, y2, x2, y2);
1432 ConvertODGDeltaToFPVDelta(
1433 AData, lWidth, lHeight, lWidth, lHeight);
1434
1435 AData.AddEntity(lPath);
1436 end;
1437
ReadTextPSequenceNodenull1438 function TvODGVectorialReader.ReadTextPSequenceNode(ANode: TDOMNode;
1439 AData: TvVectorialPage; ADoc: TvVectorialDocument): TvRichText;
1440 var
1441 lNodeName, lNodeValue: string;
1442 lCurNode: TDOMNode;
1443 lParagraph: TvParagraph;
1444 begin
1445 Result := TvRichText.Create(AData);
1446
1447 lCurNode := ANode.FirstChild;
1448 while lCurNode <> nil do
1449 begin
1450 lNodeName := lCurNode.NodeName;
1451 lNodeValue := lCurNode.NodeValue;
1452
1453 case lNodeName of
1454 'text:p':
1455 begin
1456 lParagraph := ReadTextPNode(lCurNode, AData, ADoc);
1457 Result.AddEntity(lParagraph);
1458 end;
1459 end;
1460
1461 lCurNode := lCurNode.NextSibling;
1462 end;
1463 end;
1464
1465 {
1466 Examples:
1467
1468 <text:p text:style-name="P2">
1469 <text:span text:style-name="T1">Kesäyö</text:span>
1470 </text:p>
1471
1472 <text:p>Jump opposite arm and leg up</text:p>
1473
1474 <text:p>
1475 <text:span text:style-name="T1">Back muscle movement</text:span>
1476 <text:span text:style-name="T2">opposite</text:span>
1477 <text:span text:style-name="T3">arm</text:span>
1478 and
1479 <text:span text:style-name="T3">leg</text:span>
1480 up
1481 </text:p>
1482 }
ReadTextPNodenull1483 function TvODGVectorialReader.ReadTextPNode(ANode: TDOMNode;
1484 AData: TvVectorialPage; ADoc: TvVectorialDocument): TvParagraph;
1485 var
1486 lNodeName, lNodeValue: string;
1487 lCurNode: TDOMNode;
1488 begin
1489 Result := TvParagraph.Create(AData);
1490
1491 lCurNode := ANode.FirstChild;
1492 while lCurNode <> nil do
1493 begin
1494 lNodeName := lCurNode.NodeName;
1495 lNodeValue := lCurNode.NodeValue;
1496
1497 // Check if this is a text:span
1498 if (lNodeValue = '') and (lCurNode.FirstChild <> nil) then
1499 begin
1500 lNodeValue := lCurNode.FirstChild.NodeValue;
1501 end;
1502
1503 Result.AddText(lNodeValue);
1504
1505 lCurNode := lCurNode.NextSibling;
1506 end;
1507 end;
1508
1509 {
1510 <office:master-styles>
1511 <style:master-page style:name="Oletus" style:page-layout-name="PM0" draw:style-name="Mdp1"/>
1512 </office:master-styles>
1513 }
1514 procedure TvODGVectorialReader.ReadStylesMasterPage(ANode: TDOMNode;
1515 AData: TvVectorialPage; ADoc: TvVectorialDocument);
1516 var
1517 lMasterPage: TODGMasterPage;
1518 i: Integer;
1519 lNodeName, lNodeValue: string;
1520 begin
1521 lMasterPage := TODGMasterPage.Create;
1522
1523 // Read properties
1524 for i := 0 to ANode.Attributes.Length - 1 do
1525 begin
1526 lNodeName := LowerCase(ANode.Attributes.Item[i].NodeName);
1527 lNodeValue := ANode.Attributes.Item[i].NodeValue;
1528
1529 case lNodeName of
1530 'style:name': lMasterPage.Name := lNodeValue;
1531 'style:page-layout-name': lMasterPage.PageLayoutName := lNodeValue;
1532 'draw:style-name': lMasterPage.StyleName := lNodeValue;
1533 end;
1534 end;
1535 FMasterPages.Add(lMasterPage);
1536 end;
1537
1538 {
1539 <style:page-layout style:name="PM0">
1540 <style:page-layout-properties fo:margin-top="1cm" fo:margin-bottom="1cm"
1541 fo:margin-left="1cm" fo:margin-right="1cm"
1542 fo:page-width="21cm" fo:page-height="29.7cm"
1543 style:print-orientation="portrait"/>
1544 </style:page-layout>
1545 }
1546 procedure TvODGVectorialReader.ReadStylesPageLayout(ANode: TDOMNode;
1547 AData: TvVectorialPage; ADoc: TvVectorialDocument);
1548 var
1549 lPageLayout: TODGPageLayout;
1550 lPageLayoutPropertiesNode: TDOMNode;
1551 i: Integer;
1552 lNodeName, lNodeValue: string;
1553 begin
1554 lPageLayout := TODGPageLayout.Create;
1555
1556 // Read properties
1557 for i := 0 to ANode.Attributes.Length - 1 do
1558 begin
1559 lNodeName := LowerCase(ANode.Attributes.Item[i].NodeName);
1560 lNodeValue := ANode.Attributes.Item[i].NodeValue;
1561
1562 case lNodeName of
1563 'style:name': lPageLayout.Name := lNodeValue;
1564 end;
1565 end;
1566
1567 // Read properties in the internal item
1568 lPageLayoutPropertiesNode := ANode.FindNode('style:page-layout-properties');
1569 if lPageLayoutPropertiesNode <> nil then
1570 begin
1571 for i := 0 to lPageLayoutPropertiesNode.Attributes.Length - 1 do
1572 begin
1573 lNodeName := LowerCase(lPageLayoutPropertiesNode.Attributes.Item[i].NodeName);
1574 lNodeValue := lPageLayoutPropertiesNode.Attributes.Item[i].NodeValue;
1575
1576 case lNodeName of
1577 'fo:margin-top': lPageLayout.MarginTop := StringWithUnitToFloat(lNodeValue);
1578 'fo:margin-bottom':lPageLayout.MarginBottom := StringWithUnitToFloat(lNodeValue);
1579 'fo:margin-left': lPageLayout.MarginLeft := StringWithUnitToFloat(lNodeValue);
1580 'fo:margin-right':lPageLayout.MarginRight := StringWithUnitToFloat(lNodeValue);
1581 'fo:page-width': lPageLayout.PageWidth := StringWithUnitToFloat(lNodeValue);
1582 'fo:page-height': lPageLayout.PageHeight := StringWithUnitToFloat(lNodeValue);
1583 end;
1584 end;
1585 end;
1586 FPageLayouts.Add(lPageLayout);
1587 end;
1588
1589 procedure TvODGVectorialReader.ParsePathString(AInputStr: string; ADest: TPath);
1590 begin
1591
1592 end;
1593
1594 procedure TvODGVectorialReader.GetDrawTransforms(AInputStr: string; out ASkewX,
1595 ASkewY, ARotate, ATranslateX, ATranslateY: Double);
1596 var
1597 // transform
1598 MA, MB, MC, MD, ME, MF: Double;
1599 lMTranslateX, lMTranslateY, lMScaleX, lMScaleY, lMSkewX, lMSkewY, lMRotate: Double;
1600 lTokenizer: TSVGPathTokenizer;
1601 i: Integer;
1602 lFunctionName, lParamStr: string;
1603 lMatrixElements: array of Double;
1604 lMatrixStrElements: TStringList;
1605 begin
1606 ASkewX := 0.0;
1607 ASkewY := 0.0;
1608 ARotate := 0.0;
1609 ATranslateX := 0.0;
1610 ATranslateY := 0.0;
1611
1612 // Examples:
1613 // transform="matrix(0.860815 0 -0 1.07602 339.302 489.171)"
1614 // transform="scale(0.24) translate(0, 35)"
1615 // transform="rotate(90)"
1616 lTokenizer := TSVGPathTokenizer.Create;
1617 try
1618 lTokenizer.TokenizeFunctions(AInputStr);
1619
1620 i := 0;
1621 while i < lTokenizer.Tokens.Count-1 do
1622 begin
1623 lFunctionName := Trim(lTokenizer.Tokens.Items[i].StrValue);
1624 lParamStr := lTokenizer.Tokens.Items[i+1].StrValue;
1625 //lMatrixElements := ReadSpaceSeparatedFloats(lParamStr, ',');
1626
1627 if lFunctionName = 'matrix' then
1628 begin
1629 {ReadSVGTransformationMatrix(lParamStr, MA, MB, MC, MD, ME, MF);
1630
1631 ConvertTransformationMatrixToOperations(MA, MB, MC, MD, ME, MF,
1632 lMTranslateX, lMTranslateY, lMScaleX, lMScaleY, lMSkewX, lMSkewY, lMRotate);
1633
1634 ConvertSVGDeltaToFPVDelta(nil,
1635 lMTranslateX, lMTranslateY,
1636 lMTranslateX, lMTranslateY);
1637
1638 ADestEntity.Move(lMTranslateX, lMTranslateY);
1639 ADestEntity.Scale(lMScaleX, lMScaleY); }
1640 end
1641 else if lFunctionName = 'scale' then
1642 begin
1643 ;
1644 end
1645 else if lFunctionName = 'translate' then
1646 begin
1647 lMatrixStrElements := ReadSpaceSeparatedStrings(lParamStr, ',');
1648 try
1649 ATranslateX := StringWithUnitToFloat(lMatrixStrElements.Strings[0]);
1650 ATranslateY := StringWithUnitToFloat(lMatrixStrElements.Strings[1]);
1651 finally
1652 lMatrixStrElements.Free;
1653 end;
1654 //ADestEntity.Move(lMatrixElements[0], lMatrixElements[1]);
1655 end
1656 else if lFunctionName = 'rotate' then
1657 begin
1658 //ADestEntity.Rotate(lMatrixElements[0], Make3DPoint(0, 0, 0));
1659 end;
1660
1661 Inc(i, 2);
1662 end;
1663 finally
1664 lTokenizer.Free;
1665 end;
1666 end;
1667
ReadSVGColornull1668 function TvODGVectorialReader.ReadSVGColor(AValue: string): TFPColor;
1669 var
1670 lValue, lStr: string;
1671 lStrings: TStringList;
1672 begin
1673 Result := colBlack;
1674 lValue := Trim(LowerCase(AValue));
1675
1676 // Support for rgb(255,255,0)
1677 if (Length(lValue) > 3) and (Copy(lValue, 0, 3) = 'rgb') then
1678 begin
1679 lStrings := TStringList.Create;
1680 try
1681 lStr := Copy(lValue, 5, Length(lValue)-5);
1682 lStrings.Delimiter := ',';
1683 lStrings.DelimitedText := lStr;
1684 if lStrings.Count = 3 then
1685 begin
1686 Result.Red := StrToInt(lStrings.Strings[0]) * $101;
1687 Result.Blue := StrToInt(lStrings.Strings[1]) * $101;
1688 Result.Green := StrToInt(lStrings.Strings[2]) * $101;
1689 end
1690 else
1691 raise Exception.Create(Format('[TvSVGVectorialReader.ReadSVGColor] An unexpected number of channels was found: %d', [lStrings.Count]));
1692 finally
1693 lStrings.Free;
1694 end;
1695 Exit;
1696 end;
1697
1698 // Support for RGB hex
1699 // ex: #0000ff
1700 // Another wierd valid variant: #000
1701 if (Length(lValue) > 1) and (lValue[1] = '#') then
1702 begin
1703 lStr := Copy(lValue, 2, 2);
1704 Result.Red := StrToInt('$'+lStr)*$101;
1705 lStr := Copy(lValue, 4, 2);
1706 if lStr = '' then Result.Green := 0
1707 else Result.Green := StrToInt('$'+lStr)*$101;
1708 lStr := Copy(lValue, 6, 2);
1709 if lStr = '' then Result.Blue := 0
1710 else Result.Blue := StrToInt('$'+lStr)*$101;
1711 Exit;
1712 end;
1713
1714 // Support for named colors
1715 // List here: http://www.december.com/html/spec/colorsvghex.html
1716 case lValue of
1717 'black': Result := colBlack;
1718 'navy': Result.Blue := $8080;
1719 'darkblue':Result.Blue := $8B8B;
1720 'mediumblue':Result.Blue := $CDCD;
1721 'blue': Result := colBlue;
1722 'darkgreen':Result.Green := $6464;
1723 'green': Result.Green := $8080;
1724 'teal':
1725 begin
1726 Result.Green := $8080;
1727 Result.Blue := $8080;
1728 end;
1729 'darkcyan':
1730 begin
1731 Result.Green := $8B8B;
1732 Result.Blue := $8B8B;
1733 end;
1734 'deepskyblue':
1735 begin
1736 Result.Green := $BFBF;
1737 Result.Blue := $FFFF;
1738 end;
1739 'darkturquoise':
1740 begin
1741 Result.Green := $CECE;
1742 Result.Blue := $D1D1;
1743 end;
1744 'mediumspringgreen':
1745 begin
1746 Result.Green := $FAFA;
1747 Result.Blue := $9A9A;
1748 end;
1749 'lime': Result := colGreen;
1750 'springgreen':
1751 begin
1752 Result.Green := $FFFF;
1753 Result.Blue := $7F7F;
1754 end;
1755 'cyan': Result := colCyan;
1756 'aqua': Result := colCyan;
1757 'midnightblue':
1758 begin
1759 Result.Red := $1919;
1760 Result.Green := $1919;
1761 Result.Blue := $7070;
1762 end;
1763 'dodgerblue':
1764 begin
1765 Result.Red := $1E1E;
1766 Result.Green := $9090;
1767 Result.Blue := $FFFF;
1768 end;
1769 'lightseagreen':
1770 begin
1771 Result.Red := $2020;
1772 Result.Green := $B2B2;
1773 Result.Blue := $AAAA;
1774 end;
1775 'forestgreen':
1776 begin
1777 Result.Red := $2222;
1778 Result.Green := $8B8B;
1779 Result.Blue := $2222;
1780 end;
1781 'seagreen':
1782 begin
1783 Result.Red := $2E2E;
1784 Result.Green := $8B8B;
1785 Result.Blue := $5757;
1786 end;
1787 'darkslategray', 'darkslategrey':
1788 begin
1789 Result.Red := $2F2F;
1790 Result.Green := $4F4F;
1791 Result.Blue := $4F4F;
1792 end;
1793 'limegreen':
1794 begin
1795 Result.Red := $3232;
1796 Result.Green := $CDCD;
1797 Result.Blue := $3232;
1798 end;
1799 'mediumseagreen':
1800 begin
1801 Result.Red := $3C3C;
1802 Result.Green := $CBCB;
1803 Result.Blue := $7171;
1804 end;
1805 'turquoise':
1806 begin
1807 Result.Red := $4040;
1808 Result.Green := $E0E0;
1809 Result.Blue := $D0D0;
1810 end;
1811 'royalblue':
1812 begin
1813 Result.Red := $4141;
1814 Result.Green := $6969;
1815 Result.Blue := $E1E1;
1816 end;
1817 'steelblue':
1818 begin
1819 Result.Red := $4646;
1820 Result.Green := $8282;
1821 Result.Blue := $B4B4;
1822 end;
1823 'darkslateblue':
1824 begin
1825 Result.Red := $4848;
1826 Result.Green := $3D3D;
1827 Result.Blue := $8B8B;
1828 end;
1829 'mediumturquoise':
1830 begin
1831 Result.Red := $4848;
1832 Result.Green := $D1D1;
1833 Result.Blue := $CCCC;
1834 end;
1835 {
1836 indigo #4B0082
1837 darkolivegreen #556B2F cadetblue #5F9EA0
1838 cornflowerblue #6495ED
1839 mediumaquamarine #66CDAA dimgrey #696969
1840 dimgray #696969
1841 slateblue #6A5ACD olivedrab #6B8E23
1842 slategrey #708090
1843 slategray #708090 lightslategray(Hex3) #778899
1844 lightslategrey(Hex3) #778899
1845 mediumslateblue #7B68EE lawngreen #7CFC00
1846 chartreuse #7FFF00
1847 }
1848 'aquamarine':
1849 begin
1850 Result.Red := $7F7F;
1851 Result.Green := $FFFF;
1852 Result.Blue := $D4D4;
1853 end;
1854 'maroon': Result.Red := $8080;
1855 'purple': Result := colPurple;
1856 'olive': Result := colOlive;
1857 'gray', 'grey': Result := colGray;
1858 'skyblue':
1859 begin
1860 Result.Red := $8787;
1861 Result.Green := $CECE;
1862 Result.Blue := $EBEB;
1863 end;
1864 'lightskyblue':
1865 begin
1866 Result.Red := $8787;
1867 Result.Green := $CECE;
1868 Result.Blue := $FAFA;
1869 end;
1870 'blueviolet':
1871 begin
1872 Result.Red := $8A8A;
1873 Result.Green := $2B2B;
1874 Result.Blue := $E2E2;
1875 end;
1876 'darkred': Result.Red := $8B8B;
1877 'darkmagenta':
1878 begin
1879 Result.Red := $8B8B;
1880 Result.Blue := $8B8B;
1881 end;
1882 {
1883 saddlebrown #8B4513
1884 darkseagreen #8FBC8F lightgreen #90EE90
1885 mediumpurple #9370DB
1886 darkviolet #9400D3 palegreen #98FB98
1887 darkorchid #9932CC
1888 yellowgreen #9ACD32 sienna #A0522D
1889 brown #A52A2A
1890 darkgray #A9A9A9 darkgrey #A9A9A9
1891 lightblue #ADD8E6
1892 greenyellow #ADFF2F paleturquoise #AFEEEE
1893 lightsteelblue #B0C4DE
1894 powderblue #B0E0E6 firebrick #B22222
1895 darkgoldenrod #B8860B
1896 mediumorchid #BA55D3 rosybrown #BC8F8F
1897 darkkhaki #BDB76B
1898 }
1899 'silver': Result := colSilver;
1900 'mediumvioletred':
1901 begin
1902 Result.Red := $C7C7;
1903 Result.Green := $1515;
1904 Result.Blue := $8585;
1905 end;
1906 'indianred':
1907 begin
1908 Result.Red := $CDCD;
1909 Result.Green := $5C5C;
1910 Result.Blue := $5C5C;
1911 end;
1912 'peru':
1913 begin
1914 Result.Red := $CDCD;
1915 Result.Green := $8585;
1916 Result.Blue := $3F3F;
1917 end;
1918 'chocolate':
1919 begin
1920 Result.Red := $D2D2;
1921 Result.Green := $6969;
1922 Result.Blue := $1E1E;
1923 end;
1924 {
1925 tan #D2B48C
1926 lightgray #D3D3D3 lightgrey #D3D3D3
1927 thistle #D8BFD8
1928 orchid #DA70D6 goldenrod #DAA520
1929 palevioletred #DB7093
1930 crimson #DC143C gainsboro #DCDCDC
1931 plum #DDA0DD
1932 burlywood #DEB887 lightcyan #E0FFFF
1933 lavender #E6E6FA
1934 }
1935 'darksalmon':
1936 begin
1937 Result.Red := $E9E9;
1938 Result.Green := $9696;
1939 Result.Blue := $7A7A;
1940 end;
1941 'violet':
1942 begin
1943 Result.Red := $EEEE;
1944 Result.Green := $8282;
1945 Result.Blue := $EEEE;
1946 end;
1947 'palegoldenrod':
1948 begin
1949 Result.Red := $EEEE;
1950 Result.Green := $E8E8;
1951 Result.Blue := $AAAA;
1952 end;
1953 'lightcoral':
1954 begin
1955 Result.Red := $F0F0;
1956 Result.Green := $8080;
1957 Result.Blue := $8080;
1958 end;
1959 'khaki':
1960 begin
1961 Result.Red := $F0F0;
1962 Result.Green := $E6E6;
1963 Result.Blue := $8C8C;
1964 end;
1965 'aliceblue':
1966 begin
1967 Result.Red := $F0F0;
1968 Result.Green := $F8F8;
1969 Result.Blue := $FFFF;
1970 end;
1971 'honeydew':
1972 begin
1973 Result.Red := $F0F0;
1974 Result.Green := $FFFF;
1975 Result.Blue := $F0F0;
1976 end;
1977 'azure':
1978 begin
1979 Result.Red := $F0F0;
1980 Result.Green := $FFFF;
1981 Result.Blue := $FFFF;
1982 end;
1983 'sandybrown':
1984 begin
1985 Result.Red := $F4F4;
1986 Result.Green := $A4A4;
1987 Result.Blue := $6060;
1988 end;
1989 {
1990 wheat #F5DEB3 beige #F5F5DC
1991 whitesmoke #F5F5F5
1992 mintcream #F5FFFA ghostwhite #F8F8FF
1993 salmon #FA8072
1994 antiquewhite #FAEBD7 linen #FAF0E6
1995 lightgoldenrodyellow #FAFAD2
1996 oldlace #FDF5E6
1997 }
1998 'red': Result := colRed;
1999 'fuchsia': Result := colFuchsia;
2000 'magenta': Result := colMagenta;
2001 { deeppink #FF1493
2002 orangered #FF4500
2003 tomato #FF6347 hotpink #FF69B4
2004 coral #FF7F50
2005 darkorange #FF8C00 lightsalmon #FFA07A
2006 orange #FFA500
2007 lightpink #FFB6C1 pink #FFC0CB
2008 gold #FFD700
2009 peachpuff #FFDAB9 navajowhite #FFDEAD
2010 moccasin #FFE4B5
2011 bisque #FFE4C4 mistyrose #FFE4E1
2012 blanchedalmond #FFEBCD
2013 papayawhip #FFEFD5 lavenderblush #FFF0F5
2014 seashell #FFF5EE
2015 cornsilk #FFF8DC lemonchiffon #FFFACD
2016 floralwhite #FFFAF0
2017 }
2018 'snow':
2019 begin
2020 Result.Red := $FFFF;
2021 Result.Green := $FAFA;
2022 Result.Blue := $FAFA;
2023 end;
2024 'yellow': Result := colYellow;
2025 'lightyellow':
2026 begin
2027 Result.Red := $FFFF;
2028 Result.Green := $FEFE;
2029 end;
2030 'ivory':
2031 begin
2032 Result.Red := $FFFF;
2033 Result.Green := $FFFF;
2034 Result.Blue := $F0F0;
2035 end;
2036 'white': Result := colWhite;
2037 end;
2038 end;
2039
GetAttrValuenull2040 function TvODGVectorialReader.GetAttrValue(ANode : TDOMNode; AAttrName : string) : string;
2041 var
2042 i : integer;
2043 Found : Boolean;
2044 begin
2045 Found:=false;
2046 i:=0;
2047 Result:='';
2048 while not Found and (i<ANode.Attributes.Length) do begin
2049 if ANode.Attributes.Item[i].NodeName=AAttrName then begin
2050 Found:=true;
2051 Result:=ANode.Attributes.Item[i].NodeValue;
2052 end;
2053 inc(i);
2054 end;
2055 end;
2056
StringWithUnitToFloatnull2057 function TvODGVectorialReader.StringWithUnitToFloat(AStr: string): Double;
2058 var
2059 UnitStr, ValueStr: string;
2060 Len: Integer;
2061 begin
2062 if AStr = '' then Exit(0.0);
2063
2064 // Check the unit
2065 Len := Length(AStr);
2066 UnitStr := Copy(AStr, Len-1, 2);
2067 if UnitStr = 'mm' then
2068 begin
2069 ValueStr := Copy(AStr, 1, Len-2);
2070 Result := StrToFloat(ValueStr, FPointSeparator);
2071 end
2072 else if UnitStr = 'cm' then
2073 begin
2074 ValueStr := Copy(AStr, 1, Len-2);
2075 Result := StrToFloat(ValueStr, FPointSeparator) * 10;
2076 end
2077 else if UnitStr = 'px' then
2078 begin
2079 ValueStr := Copy(AStr, 1, Len-2);
2080 Result := StrToInt(ValueStr);
2081 end
2082 else // If there is no unit, just use StrToFloat
2083 begin
2084 Result := StrToFloat(AStr, FPointSeparator);
2085 end;
2086 end;
2087
2088 procedure TvODGVectorialReader.ConvertODGCoordinatesToFPVCoordinates(
2089 const AData: TvVectorialPage; const ASrcX, ASrcY: Double;
2090 var ADestX,ADestY: Double);
2091 begin
2092 ADestX := ASrcX * FLOAT_MILIMETERS_PER_PIXEL;
2093 ADestY := AData.Height - ASrcY * FLOAT_MILIMETERS_PER_PIXEL;
2094 end;
2095
2096 procedure TvODGVectorialReader.ConvertODGDeltaToFPVDelta(
2097 const AData: TvVectorialPage; const ASrcX, ASrcY: Double; var ADestX,
2098 ADestY: Double);
2099 begin
2100 ADestX := ASrcX * FLOAT_MILIMETERS_PER_PIXEL;
2101 ADestY := - ASrcY * FLOAT_MILIMETERS_PER_PIXEL;
2102 end;
2103
2104 {
2105 For the drawing units see http://stackoverflow.com/questions/15335926/svg-viewbox-attribute
2106
2107 We should use these formulas to obtain the X, Y position from something in the drawing:
2108
2109 Xreal = (Xenhanced-path - ViewBox_X0) * (draw:custom-shape_svg:width / svg:viewBox_Width) + svg:viewBox_Xo;
2110 And the same for Y
2111
2112 For sizes just use without Xo
2113
2114 <draw:custom-shape draw:style-name="gr6" draw:text-style-name="P1" draw:layer="layout" svg:width="3.783cm" svg:height="3.602cm" draw:transform="skewX (-0.000872664625997166) rotate (-1.62385433605552) translate (19.087cm 20.266cm)">
2115 <text:p />
2116 <draw:enhanced-geometry svg:viewBox="0 0 21600 21600" draw:glue-points="10800 0 3163 3163 0 10800 3163 18437 10800 21600 18437 18437 21600 10800 18437 3163" draw:text-areas="3163 3163 18437 18437" draw:type="ring" draw:modifiers="647.870425914817"
2117 draw:enhanced-path="U 10800 10800 10800 10800 0 360 Z U 10800 10800 ?f1 ?f1 0 360 N">
2118 <draw:equation draw:name="f0" draw:formula="$0 " />
2119 <draw:equation draw:name="f1" draw:formula="10800-$0 " />
2120 <draw:handle draw:handle-position="$0 10800" draw:handle-range-x-minimum="0" draw:handle-range-x-maximum="10800" />
2121 </draw:enhanced-geometry>
2122 </draw:custom-shape>
2123 }
2124 procedure TvODGVectorialReader.ConvertViewBoxCoordinatesToFPVCoordinates(
2125 const AData: TvVectorialPage; const AInfo: TCustomShapeInfo;
2126 const ASrcX, ASrcY: Double; var ADestX, ADestY: Double);
2127 begin
2128 ADestX := (ASrcX - AInfo.ViewBox_Left) * (AInfo.Width / AInfo.ViewBox_Width) + AInfo.Left;
2129 ADestY := (ASrcY - AInfo.ViewBox_Top) * (AInfo.Height / AInfo.ViewBox_Height) + AInfo.Top;
2130 ADestY := AData.Height - ADestY;
2131 end;
2132
2133 procedure TvODGVectorialReader.ConvertViewBoxDeltaToFPVDelta(
2134 const AInfo: TCustomShapeInfo; const ASrcX, ASrcY: Double; var ADestX,
2135 ADestY: Double);
2136 begin
2137 ADestX := (ASrcX - AInfo.ViewBox_Left) * (AInfo.Width / AInfo.ViewBox_Width);
2138 ADestY := (ASrcY - AInfo.ViewBox_Top) * (AInfo.Height / AInfo.ViewBox_Height);
2139 end;
2140
2141 procedure TvODGVectorialReader.ConvertMilimiterCoordinatesToFPVCoordinates(
2142 const AData: TvVectorialPage; const ASrcX, ASrcY: Double; var ADestX,
2143 ADestY: Double);
2144 begin
2145 ADestX := ASrcX;
2146 ADestY := AData.Height - ASrcY;
2147 end;
2148
2149 constructor TvODGVectorialReader.Create;
2150 begin
2151 inherited Create;
2152
2153 FPointSeparator := DefaultFormatSettings;
2154 FPointSeparator.DecimalSeparator := '.';
2155 FPointSeparator.ThousandSeparator := '#';// disable the thousand separator
2156
2157 // FSVGPathTokenizer := TSVGPathTokenizer.Create;
2158 FStyles := TFPList.Create;
2159 FAutomaticStyles := TFPList.Create;
2160 FPageLayouts := TFPList.Create;
2161 FMasterPages := TFPList.Create;
2162 end;
2163
2164 destructor TvODGVectorialReader.Destroy;
2165 begin
2166 FStyles.ForEachCall(@DeleteStyle, nil);
2167 FStyles.Free;
2168 FAutomaticStyles.ForEachCall(@DeleteStyle, nil);
2169 FAutomaticStyles.Free;
2170 FPageLayouts.ForEachCall(@DeleteStyle, nil);
2171 FPageLayouts.Free;
2172 FMasterPages.ForEachCall(@DeleteStyle, nil);
2173 FMasterPages.Free;
2174 // FSVGPathTokenizer.Free;
2175
2176 inherited Destroy;
2177 end;
2178
2179 procedure TvODGVectorialReader.ReadFromStream(AStream: TStream;
2180 AData: TvVectorialDocument);
2181 var
2182 Doc: TXMLDocument;
2183 lCurNode: TDOMNode;
2184 lPage: TvVectorialPage;
2185 begin
2186 { try
2187 // Read in xml file from the stream
2188 ReadXMLFile(Doc, AStream);
2189
2190 // Read the properties of the <svg> tag
2191 AData.Width := StringWithUnitToFloat(Doc.DocumentElement.GetAttribute('width'));
2192 AData.Height := StringWithUnitToFloat(Doc.DocumentElement.GetAttribute('height'));
2193
2194 // Now process the elements
2195 lCurNode := Doc.DocumentElement.FirstChild;
2196 lPage := AData.AddPage();
2197 lPage.Width := AData.Width;
2198 lPage.Height := AData.Height;
2199 while Assigned(lCurNode) do
2200 begin
2201 ReadEntityFromNode(lCurNode, lPage, AData);
2202 lCurNode := lCurNode.NextSibling;
2203 end;
2204 finally
2205 // finally, free the document
2206 Doc.Free;
2207 end;}
2208 end;
2209
2210 procedure TvODGVectorialReader.ReadFromFile(AFileName: string; AData: TvVectorialDocument);
2211 var
2212 FilePath : string;
2213 UnZip : TUnZipper;
2214 FileList : TStringList;
2215 Doc : TXMLDocument;
2216 begin
2217 //unzip content.xml into AFileName path
2218 FilePath:=GetTempDir(false);
2219 UnZip:=TUnZipper.Create;
2220 UnZip.OutputPath:=FilePath;
2221 FileList:=TStringList.Create;
2222 FileList.Add('content.xml');
2223 FileList.Add('styles.xml');
2224 try
2225 Unzip.UnZipFiles(AFileName,FileList);
2226 finally
2227 FreeAndNil(FileList);
2228 FreeAndNil(UnZip);
2229 end; //try
2230
2231 Doc:=nil;
2232 try
2233 // First read the master styles
2234 ReadXMLFile(Doc,FilePath+'styles.xml');
2235 DeleteFile(FilePath+'styles.xml');
2236 ReadFromStylesXMLDocument(Doc, AData);
2237
2238 // Now process the contents
2239 ReadXMLFile(Doc,FilePath+'content.xml');
2240 DeleteFile(FilePath+'content.xml');
2241 ReadFromStylesXMLDocument(Doc, AData); // The content.xml may also contain styles
2242 ReadFromContentXMLDocument(Doc, AData);
2243 finally
2244 Doc.Free;
2245 end;
2246 end;
2247
2248 {
2249 <draw:page draw:name="page1" draw:style-name="dp1" draw:master-page-name="Oletus">
2250 }
2251 procedure TvODGVectorialReader.ReadFromContentXMLDocument(
2252 AXMLDocument: TXMLDocument; AData: TvVectorialDocument);
2253 var
2254 BodyNode, DrawingNode, PageNode, ElementNode: TDOMNode;
2255 CurPage: TvVectorialPage;
2256 i: Integer;
2257 lNodeName, lNodeValue: String;
2258 begin
2259 BodyNode := AXMLDocument.DocumentElement.FindNode('office:body');
2260 if not Assigned(BodyNode) then raise Exception.Create('[TvODGVectorialReader.ReadFromContentXMLDocument] node office:body not found');
2261
2262 DrawingNode := BodyNode.FindNode('office:drawing');
2263 if not Assigned(DrawingNode) then raise Exception.Create('[TvODGVectorialReader.ReadFromContentXMLDocument] node office:drawing not found');
2264
2265 //process each page
2266 PageNode := DrawingNode.FindNode('draw:page');
2267 while Assigned(PageNode) do
2268 begin
2269 CurPage := aData.AddPage();
2270 //CurPage..AddWorksheet(GetAttrValue(TableNode,'table:name'));
2271
2272 //process attributes of the page
2273 for i := 0 to PageNode.Attributes.Length - 1 do
2274 begin
2275 lNodeName := LowerCase(PageNode.Attributes.Item[i].NodeName);
2276 lNodeValue := PageNode.Attributes.Item[i].NodeValue;
2277 case lNodeName of
2278 'draw:master-page-name': ApplyMasterPageToPage(lNodeValue, CurPage);
2279 end;
2280 end;
2281
2282 //process each element inside the page
2283 ElementNode := PageNode.FirstChild;
2284 while Assigned(ElementNode) do
2285 begin
2286 ReadElement(ElementNode, CurPage, AData);
2287
2288 ElementNode:=ElementNode.NextSibling;
2289 end; // while Assigned(ElementNode)
2290
2291 PageNode:=PageNode.NextSibling;
2292 end; //while Assigned(PageNode)
2293 end;
2294
2295 procedure TvODGVectorialReader.ReadFromStylesXMLDocument(
2296 AXMLDocument: TXMLDocument; AData: TvVectorialDocument);
2297 var
2298 DocStylesNode, AutomaticStylesNode, MasterStylesNode, ElementNode: TDOMNode;
2299 CurPage: TvVectorialPage;
2300 lNodeName: String;
2301 begin
2302 DocStylesNode := AXMLDocument.DocumentElement;//.FindNode('office:document-styles');
2303 if not Assigned(DocStylesNode) then raise Exception.Create('[TvODGVectorialReader.ReadFromStylesXMLDocument] node document-styles not found');
2304
2305 AutomaticStylesNode := DocStylesNode.FindNode('office:automatic-styles');
2306 if Assigned(AutomaticStylesNode) then
2307 begin
2308 //process each master style
2309 ElementNode := AutomaticStylesNode.FirstChild;
2310 while Assigned(ElementNode) do
2311 begin
2312 lNodeName := LowerCase(ElementNode.NodeName);
2313 case lNodeName of
2314 'style:page-layout': ReadStylesPageLayout(ElementNode, CurPage, AData);
2315 'style:style': ReadStyleStyleNode(ElementNode, CurPage, AData);
2316 end;
2317
2318 ElementNode := ElementNode.NextSibling;
2319 end; //while Assigned(MasterStyleNode)
2320 end;
2321
2322 MasterStylesNode := DocStylesNode.FindNode('office:master-styles');
2323 if Assigned(MasterStylesNode) then
2324 begin
2325 //process each master style
2326 ElementNode := MasterStylesNode.FirstChild;
2327 while Assigned(ElementNode) do
2328 begin
2329 lNodeName := LowerCase(ElementNode.NodeName);
2330 case lNodeName of
2331 'style:master-page': ReadStylesMasterPage(ElementNode, CurPage, AData);
2332 end;
2333
2334 ElementNode := ElementNode.NextSibling;
2335 end; //while Assigned(MasterStyleNode)
2336 end;
2337 end;
2338
2339 initialization
2340
2341 RegisterVectorialReader(TvODGVectorialReader, vfODG);
2342
2343 end.
2344
2345