Guest User

Untitled

a guest
Jun 5th, 2012
299
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. Unit BODTUnit;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. Interface
  6.  
  7. Uses
  8.   {$ifdef unix}
  9.   unix,
  10.   {$else}
  11.   windows,
  12.   {$endif}
  13.   Classes, SysUtils, XMLRead, XMLWrite, DOM, zipper, strutils;
  14.  
  15. Type
  16.   TFontStyle = (ftBold, ftItalic);
  17.   TFontStyles = set of TFontStyle;
  18.   TTextPosition = (
  19.     tpCenter = 1,
  20.     tpLeft = 2,
  21.     tpRight = 3,
  22.     tpJustify = 4);
  23.   TVertAlign = (vaTop, vaMiddle, vaBottom, vaAutomatic);
  24.  
  25. Type BODTClass = Class;
  26.  
  27. Type
  28.  
  29. { BODTTableClass }
  30.  
  31.  BODTTableClass = Class
  32.   Private
  33.     bODT: BODTClass;
  34.     bRootNode: TDOMNode;
  35.     bTableName: String;
  36.     Function GetCellNode(Const aCol, aRow: Integer): TDOMNode;
  37.     Function GetRowCount: Integer;
  38.     Function GetRowNode(Const aRow: Integer): TDomNode;
  39.   Public
  40.     //** Ссылка к документу, содержащему эту таблицу
  41.     Property ODT: BODTClass Read bODT;
  42.     //** Имя таблицы
  43.     Property TableName: String Read bTableName;
  44.     //** Количество строк в таблице
  45.     Property RowCount: Integer Read GetRowCount;
  46.  
  47.     //** Копирует строку
  48.     Procedure MultiplyRow(Const aRow, aCount: Integer);
  49.     //** Удаляет последнюю строку
  50.     Procedure RemoveRow;
  51.     //** Объединяет ячейки в строке
  52.     Procedure SetCellColSpan(Const aCol, aRow: Integer;
  53.       Const aColSpan: Integer);
  54.     //** Устанавливает значение в ячейке. Также позволяет указать стиль текста
  55.     Procedure SetCellValue(Const aCol, aRow: Integer; Const aValue: String;
  56.       Const aStyleName: String = '');
  57.     //** Сокращение для присвоения ячейке значения по маске (вызывает format)
  58.     Procedure SetCellValue(Const aCol, aRow: Integer; Const aValueMask: String;
  59.       Const aParams: Array Of Const; Const aStyleName: String = '');
  60.     //** Сокращение для присвоения ячейке числового значения
  61.     Procedure SetCellValue(Const aCol, aRow: Integer; Const aValue: Integer;
  62.       Const aStyleName: String = '');
  63.     //** Сокращение для присвоения ячейке логического значения
  64.     Procedure SetCellValue(Const aCol, aRow: Integer; Const aValue: Boolean;
  65.       Const aTrueString, aFalseString: String; Const aStyleName: String = '');
  66.  
  67.     //** Удаляет таблицу из документа
  68.     Procedure DeleteTable;
  69.  
  70.     Constructor Build(Const aODT: BODTClass; Const aTableName: String);
  71.     Destructor Burn;
  72. End;
  73.  
  74. Type
  75.  
  76. { BODTClass }
  77.  
  78.  BODTClass = Class
  79.   Private
  80.     bContent: TXMLDocument;
  81.     bManifest: TXMLDocument;
  82.     bMeta: TXMLDocument;
  83.     bSettings: TXMLDocument;
  84.     bStyles: TXMLDocument;
  85.     bStyleSheetFile: String;
  86.     bTempDir: String;
  87.     bXSLTProcPath: String;
  88.   Public
  89.     //** content.xml - содержимое документа
  90.     Property Content: TXMLDocument Read bContent;
  91.     //** META-INF/manifest.xml - содержимое всего архива
  92.     Property Manifest: TXMLDocument Read bManifest;
  93.     //** meta.xml - мета-данные (автор, дата генерации документа и т.д.)
  94.     Property Meta: TXMLDocument read bMeta;
  95.     //** Еще один файл в котором лежат общие настройки документа OpenOffice
  96.     Property Settings: TXMLDocument Read bSettings;
  97.     //** styles.xml - стили текста, форматирования таблиц и т.д.
  98.     Property Styles: TXMLDocument Read bStyles;
  99.     //** Путь к файлу стилей для XSLT преобразования
  100.     Property StyleSheetFile: String Read bStyleSheetFile Write bStyleSheetFile;
  101.     //** Путь к исполняемому файлу xsltproc для XSLT преобразования
  102.     Property XSLTProcPath: String Read bXSLTProcPath Write bXSLTProcPath;
  103.  
  104.  
  105.     //** Создаёт документ
  106.     Procedure GenerateDocument(Const aFileName: String);
  107.     //** Создаёт HTML через xslt преобразование
  108.     Procedure GenerateHTML(Const aFileName: String);
  109.  
  110.     //** Загружает шаблон
  111.     Function LoadTemplate(Const aFileName: String): Boolean;
  112.  
  113.     //** Регистрирует стиль текста
  114.     Procedure RegisterStyle(Const aName, aFontName: String;
  115.       Const aFontSize: Integer; Const aFontStyle: TFontStyles;
  116.       Const aTextPosition: TTextPosition);
  117.  
  118.     Constructor Build;
  119.     Destructor Burn;
  120. End;
  121.  
  122. Implementation
  123.  
  124. ResourceString
  125.   //Files names
  126.   FILEcontentxml = 'content.xml';
  127.   FILEstylesxml = 'styles.xml';
  128.   {$IFDEF unix}
  129.   FILEmanifestxml = 'META-INF/manifest.xml';
  130.   {$ELSE}
  131.   FILEmanifestxml = 'META-INF\manifest.xml';
  132.   {$ENDIF}
  133.   FILEmetaxml = 'meta.xml';
  134.   FILEsettingsxml = 'settings.xml';
  135.  
  136.   //ODT Nodes names
  137.   XMLTableTableRow = 'table:table-row';
  138.   XMLTextStyleName = 'text:style-name';
  139.   XMLTableNumberColumnsSpanned = 'table:number-columns-spanned';
  140.   XMLTableTable = 'table:table';
  141.   XMLTableName = 'table:name';
  142.   XMLStyleStyle = 'style:style';
  143.   XMLStyleName = 'style:name';
  144.   XMLStyleFamily = 'style:family';
  145.   XMLStyleParentStyleName = 'style:parent-style-name';
  146.   XMLStyleTextProperties = 'style:text-properties';
  147.   XMLFoFontSize = 'fo:font-size';
  148.   XMLStyleFontName = 'style:font-name';
  149.   XMLFoFontStyle = 'fo:font-style';
  150.   XMLStyleFontStyleAsian = 'style:font-style-asian';
  151.   XMLStyleFontStyleComplex = 'style:font-style-complex';
  152.   XMLFoFontWeight = 'fo:font-weight';
  153.   XMLStyleFontWeightAsian = 'style:font-weight-asian';
  154.   XMLStyleFontWeightComplex = 'style:font-weight-complex';
  155.   XMLStyleParagraphProperies = 'style:paragraph-properies';
  156.   XMLFoTextAlign = 'fo:text-align';
  157.   XMLStyleJustifySingleWord = 'style:justify-single-word';
  158.   XMLOfficeAutomaticStyles = 'office:automatic-styles';
  159.   XMLXmlVersion10EncodingUTF8 = '<?xml version=''1.0'' encoding=''UTF-8''?>';
  160.   XMLOfficeDocumentXmlns = '<office:document '
  161.     +'xmlns:office=''urn:oasis:names:tc:opendocument:xmlns:office:1.0''>';
  162.  
  163.   // Font names
  164.   FontItalic = 'italic';
  165.   FontBold = 'bold';
  166.  
  167.   //Errors messages
  168.   ERRUnableFindCell = 'Unable find cell';
  169.   ERRIllegalDOMStructure = 'Illegal DOM structure';
  170.   ERRUnableFindAutomaticStylesNode = 'Unable find automatic-styles node';
  171.   XMLOfficeDocument = '</office:document>';
  172.   FILESinglefileXml = 'singlefile.xml';
  173.  
  174. Const
  175.   TTextPositionName: Array [1..4] Of String =
  176.     ('center', 'start', 'end', 'justify');
  177.  
  178. { BODTTableClass }
  179.  
  180. Function BODTTableClass.GetRowNode(Const aRow: Integer): TDomNode;
  181. var
  182.   i:integer;
  183.   aNode: TDOMNode;
  184.   aList: TDOMNodeList;
  185.   aRowIndex: Integer = 0;
  186. begin
  187.   Result := nil;
  188.   aList := bRootNode.ChildNodes;
  189.   For i := 0 To aList.Count - 1 Do
  190.     If aList[i].NodeName = XMLTableTableRow Then
  191.       Begin
  192.         If aRowIndex = ARow Then
  193.           Exit(aList[i]);
  194.         aRowIndex += 1;
  195.       End;
  196. End;
  197.  
  198. Function BODTTableClass.GetRowCount: Integer;
  199. Var
  200.   i: Integer;
  201.   aNodes: TDOMNodeList;
  202. Begin
  203.   Result := 0;
  204.   aNodes := bRootNode.ChildNodes;
  205.   For i := 0 To aNodes.Count - 1 Do
  206.     If aNodes[i].NodeName = XMLTableTableRow Then
  207.       Begin
  208.         Inc(Result);
  209.       End;
  210. end;
  211.  
  212. Function BODTTableClass.GetCellNode(Const aCol, aRow: Integer): TDOMNode;
  213. Var
  214.   aRowNode: TDomNode;
  215. Begin
  216.   Result := nil;
  217.   aRowNode := GetRowNode(aRow);
  218.   If aRowNode = nil Then Exit;
  219.   Result := aRowNode.ChildNodes[aCol].FirstChild;
  220. End;
  221.  
  222. Procedure BODTTableClass.MultiplyRow(Const aRow, aCount: Integer);
  223. Var
  224.   i: Integer;
  225.   aNode, aClone: TDomNode;
  226. Begin
  227.   For i := 0 to aCount - 1 Do
  228.     Begin
  229.       aNode := GetRowNode(aRow);
  230.       aClone := aNode.CloneNode(TRUE);
  231.       bRootNode.InsertBefore(aClone, aNode);
  232.     End;
  233. End;
  234.  
  235. Procedure BODTTableClass.RemoveRow;
  236. Begin
  237.   bRootNode.RemoveChild(bRootNode.LastChild);
  238. End;
  239.  
  240. Procedure BODTTableClass.SetCellValue(Const aCol, aRow: Integer;
  241.   Const aValue: String; Const aStyleName: String);
  242. Var
  243.   aCellNode: TDOMNode;
  244.   aStyleNode: TDOMNode;
  245. Begin
  246.   aCellNode := GetCellNode(aCol, aRow);
  247.   If aCellNode = nil Then
  248.     Raise Exception.Create(ERRUnableFindCell);
  249.   aCellNode.TextContent := UTF8Decode(aValue);
  250.   If Not(aStyleName = '') Then
  251.     Begin
  252.       aStyleNode := aCellNode.Attributes.GetNamedItem(XMLTextStyleName);
  253.       If Not(aStyleNode = nil) Then
  254.         aStyleNode.NodeValue := aStyleName;
  255.     End;
  256. End;
  257.  
  258. Procedure BODTTableClass.SetCellColSpan(Const aCol, aRow: Integer;
  259.   Const aColSpan: Integer);
  260. Var
  261.   i: Integer;
  262.   aNode: TDOMNode;
  263.   aSpanNode: TDOMNode;
  264. Begin
  265.   aNode := GetCellNode(aCol, aRow).ParentNode;
  266.   If aNode = nil Then
  267.     Raise Exception.Create(ERRIllegalDOMStructure);
  268.  
  269.   aSpanNode := aNode.Attributes.GetNamedItem(XMLTableNumberColumnsSpanned);
  270.   If aSpanNode = nil Then
  271.     Begin
  272.       aSpanNode := bODT.Content.CreateAttribute(XMLTableNumberColumnsSpanned);
  273.       aNode.Attributes.SetNamedItem(aSpanNode);
  274.     End;
  275.  
  276.   aSpanNode.NodeValue := IntToStr(aColSpan);
  277.   For i := 1 To aColSpan - 1 Do
  278.     aNode.ParentNode.RemoveChild(aNode.NextSibling);
  279. End;
  280.  
  281. Procedure BODTTableClass.SetCellValue(Const aCol, aRow: Integer;
  282.   Const aValueMask: String; Const aParams: Array Of Const;
  283.   Const aStyleName: String);
  284. Begin
  285.   SetCellValue(aCol, aRow, Format(aValueMask, aParams), aStyleName);
  286. End;
  287.  
  288. Procedure BODTTableClass.SetCellValue(Const aCol, aRow: Integer;
  289.   Const aValue: Integer; Const aStyleName: String);
  290. Begin
  291.   SetCellValue(aCol, aRow, IntToStr(aValue), aStyleName);
  292. End;
  293.  
  294. Procedure BODTTableClass.SetCellValue(Const aCol, aRow: Integer;
  295.   Const aValue: Boolean; Const aTrueString, aFalseString: String;
  296.   Const aStyleName: String);
  297. Begin
  298.   SetCellValue(aCol, aRow, BoolToStr(aValue, aTrueString, aFalseString),
  299.     aStyleName);
  300. End;
  301.  
  302. Procedure BODTTableClass.DeleteTable;
  303. Begin
  304.   bRootNode.Free;
  305. End;
  306.  
  307. Constructor BODTTableClass.Build(Const aODT: BODTClass;
  308.   Const aTableName: String);
  309. Var
  310.   aNodes: TDOMNodeList;
  311.   i: Integer;
  312. Begin
  313.   bODT := aODT;
  314.   bTableName := aTableName;
  315.   aNodes := ODT.Content.DocumentElement.GetElementsByTagName(XMLTableTable);
  316.   For i := 0 To aNodes.Count - 1 Do
  317.     If aNodes[i].Attributes.GetNamedItem(XMLTableName).NodeValue=aTableName Then
  318.       Begin
  319.         bRootNode := aNodes[i];
  320.         Break;
  321.       End;
  322.   aNodes.Free;
  323. End;
  324.  
  325. Destructor BODTTableClass.Burn;
  326. Begin
  327.   bTableName := '';
  328. End;
  329.  
  330. { BODTClass }
  331.  
  332. Procedure BODTClass.RegisterStyle(Const aName, aFontName: String;
  333.   Const aFontSize: Integer; Const aFontStyle: TFontStyles;
  334.   Const aTextPosition: TTextPosition);
  335. Const
  336.   MASK_POINTER = '%dpt';
  337.   STR_PARAGRAPH = 'paragraph';
  338.   STR_STANDART = 'Standart';
  339. Var
  340.   i: Integer;
  341.   aNode, aFontNode, aParagraphNode: TDOMElement;
  342.   aFontStyles: TStringList;
  343.   aStyles: TDOMNodeList;
  344. Begin
  345.   aNode := bContent.CreateElement(XMLStyleStyle);
  346.   aNode.SetAttribute(XMLStyleName, aName);
  347.   aNode.SetAttribute(XMLStyleFamily, STR_PARAGRAPH);
  348.   aNode.SetAttribute(XMLStyleParentStyleName, STR_STANDART);
  349.  
  350.   aFontNode := bContent.CreateElement(XMLStyleTextProperties);
  351.   aFontNode.SetAttribute(XMLFoFontSize, Format(MASK_POINTER, [aFontSize]));
  352.   aFontNode.SetAttribute(XMLStyleFontName, aFontName);
  353.   If ftItalic In aFontStyle Then
  354.     Begin
  355.       aFontNode.SetAttribute(XMLFoFontStyle, FontItalic);
  356.       aFontNode.SetAttribute(XMLStyleFontStyleAsian, FontItalic);
  357.       aFontNode.SetAttribute(XMLStyleFontStyleComplex, FontItalic);
  358.     End;
  359.   If ftBold In aFontStyle Then
  360.     Begin
  361.       aFontNode.SetAttribute(XMLFoFontWeight, FontBold);
  362.       aFontNode.SetAttribute(XMLStyleFontWeightAsian, FontBold);
  363.       aFontNode.SetAttribute(XMLStyleFontWeightComplex, FontBold);
  364.     End;
  365.   aParagraphNode := bContent.CreateElement(XMLStyleParagraphProperies);
  366.   aParagraphNode.SetAttribute(XMLFoTextAlign,
  367.     TTextPositionName[Integer(aTextPosition)]);
  368.   aParagraphNode.SetAttribute(XMLStyleJustifySingleWord, BoolToStr(FALSE));
  369.  
  370.   aFontNode.AppendChild(aParagraphNode);
  371.   aNode.AppendChild(aFontNode);
  372.  
  373.   aStyles := bContent.GetElementsByTagName(XMLOfficeAutomaticStyles);
  374.   Try
  375.     If aStyles.Count = 1 Then
  376.       aStyles[0].AppendChild(aNode)
  377.     Else
  378.       Raise Exception.Create(ERRUnableFindAutomaticStylesNode);
  379.   Finally
  380.     aStyles.Free;
  381.   End;
  382. End;
  383.  
  384. Function BODTClass.LoadTemplate(Const aFileName: String): Boolean;
  385. Var
  386.   aUnZipper: TUnZipper;
  387. Begin
  388.   Result := FALSE;
  389.   aUnZipper := TUnZipper.Create;
  390.   Try
  391.     aUnZipper.OutputPath := bTempDir;
  392.     aUnZipper.UnZipAllFiles(aFileName);
  393.  
  394.     ReadXMLFile(bStyles, bTempDir + FILEstylesxml);
  395.     ReadXMLFile(bContent, bTempDir + FILEcontentxml);
  396.     ReadXMLFile(bManifest, bTempDir + FILEmanifestxml);
  397.     ReadXMLFile(bMeta, bTempDir + FILEmetaxml);
  398.     ReadXMLFile(bSettings, bTempDir + FILEsettingsxml);
  399.  
  400.   Finally
  401.     aUnZipper.Free;
  402.   End;
  403.   Result := TRUE;
  404. End;
  405.  
  406. Procedure BODTClass.GenerateDocument(Const aFileName: String);
  407. Var
  408.   aZipper: TZipper;
  409.   aStringList: TStringList;
  410.   i: Integer;
  411.   aBuffer: String;
  412.   Procedure AddEntries(Const aDir:string; Const aFilesList: TStringList);
  413.   Const
  414.     STR_DOT = '.';
  415.     STR_DOUBLE_DOT = '..';
  416.   Var
  417.     aSearchRec: TSearchRec;
  418.     aResult: integer;
  419.     aBuffer: String;
  420.   Begin
  421.     aResult := FindFirst(aDir+'*', faAnyFile, aSearchRec);
  422.     While aResult = 0 Do
  423.       Begin
  424.         If Not((aSearchRec.Name = STR_DOT) Or
  425.           (aSearchRec.Name = STR_DOUBLE_DOT)) Then
  426.           Begin
  427.             If (aSearchRec.Attr And faDirectory > 0) Then
  428.               Begin
  429.                 aFilesList.Add(aDir +  aSearchRec.Name + '/');
  430.                 AddEntries(aDir + aSearchRec.Name + PathDelim, aFilesList)
  431.               End
  432.             Else
  433.               aFilesList.Add(aDir +  aSearchRec.Name);
  434.           End;
  435.         aResult := FindNext(aSearchRec);
  436.       End;
  437.     FindClose(aSearchRec);
  438.   End;
  439. Begin
  440.   aZipper := TZipper.Create;
  441.   aStringList := TStringList.Create;
  442.   Try
  443.     WriteXMLFile(bContent, bTempDir + FILEcontentxml);
  444.     WriteXMLFile(bStyles, bTempDir + FILEstylesxml);
  445.     WriteXMLFile(bMeta, bTempDir + FILEmetaxml);
  446.     WriteXMLFile(bSettings, bTempDir + FILEsettingsxml);
  447.     WriteXMLFile(bManifest, bTempDir + FILEmanifestxml);
  448.  
  449.     aZipper.FileName := aFileName;
  450.     AddEntries(bTempDir, aStringList);
  451.     For i := 0 To aStringList.Count - 1 Do
  452.       Begin
  453.         aBuffer := aStringList[i];
  454.         Delete(aBuffer, 1, Length(bTempDir));
  455.         aZipper.Entries.AddFileEntry(aStringList[i], aBuffer);
  456.       End;
  457.     aZipper.ZipAllFiles;
  458.   Finally
  459.     aStringList.Free;
  460.     aZipper.Free;
  461.   End;
  462. End;
  463.  
  464. Procedure BODTClass.GenerateHTML(Const aFileName: String);
  465. Const
  466.   XSLT_BIN_MASK = '%sxsltproc -o "%s" "%s" "%s"';
  467. Var
  468.   i: Integer;
  469.   aBuffer: TStringList;
  470.   aSingleFile: TFileStream;
  471.   Procedure WriteString(Const aString: String; Const aForce: Boolean = FALSE);
  472.   Const
  473.     STR_XML = '<?xml';
  474.     STR_005pt = '0.05pt';
  475.     STR_1px = '1px';
  476.     STR_BORDER = 'border';
  477.   Var
  478.     aBuffer: String;
  479.   Begin
  480.     If Not((Pos(STR_XML, aString) = 0) Or aForce) Then Exit;
  481.     // dirty workarounds for clean HTML view
  482.     If Pos(STR_BORDER, aString) > 0 Then
  483.       Begin
  484.         aBuffer := StringReplace(aString, STR_005pt, STR_1px, [rfReplaceAll]);
  485.         aSingleFile.Write(PChar(aBuffer)[0],Length(aBuffer));
  486.         Exit;
  487.       End;
  488.     aSingleFile.Write(PChar(aString)[0],Length(aString));
  489.   End;
  490. Begin
  491.   WriteXMLFile(bMeta, bTempDir + FILEmetaxml);
  492.   WriteXMLFile(bContent, bTempDir + FILEcontentxml);
  493.   WriteXMLFile(bStyles, bTempDir + FILEstylesxml);
  494.  
  495.   aSingleFile := TFileStream.Create(bTempDir + FILESinglefileXml,
  496.     fmCreate Or fmOpenReadWrite);
  497.   WriteString(XMLXmlVersion10EncodingUTF8, TRUE);
  498.   WriteString(XMLOfficeDocumentXmlns);
  499.  
  500.   aBuffer := TStringList.Create;
  501.   aBuffer.LoadFromFile(bTempDir + FILEmetaxml);
  502.   For i := 0 To aBuffer.Count - 1 Do
  503.     WriteString(aBuffer[i]);
  504.   aBuffer.LoadFromFile(bTempDir + FILEcontentxml);
  505.   For i := 0 To aBuffer.Count - 1 Do
  506.     WriteString(aBuffer[i]);
  507.   aBuffer.LoadFromFile(bTempDir + FILEstylesxml);
  508.   For i := 0 To aBuffer.Count - 1 Do
  509.     WriteString(aBuffer[i]);
  510.   aBuffer.Free;
  511.   WriteString(XMLOfficeDocument);
  512.   aSingleFile.Free;
  513.  
  514.   {$ifdef unix}
  515.   fpSystem(Format(XSLT_BIN_MASK, [XSLTProcPath, aFileName, StyleSheetFile,
  516.     bTempDir + FILESinglefileXml]));
  517.   {$else}
  518.   WinExec(Format(XSLT_BIN_MASK, [XSLTProcPath, aFileName, StyleSheetFile,
  519.     bTempDir + File]), SW_HIDE);
  520.   {$endif}
  521. End;
  522.  
  523. Constructor BODTClass.Build;
  524. Begin
  525.   bTempDir := GetTempDir(FALSE) + IntToStr(Random(MaxInt)) + PathDelim;
  526. End;
  527.  
  528. Destructor BODTClass.Burn;
  529. Const
  530.   {$ifdef unix}
  531.     PROG_REMOVER_MASK = 'rm -r "%s"';
  532.   {$else}
  533.     PROG_REMOVER_MASK = 'rmdir /S /Q "%s"';
  534.   {$endif}
  535. Begin
  536.   //dirty workaround
  537.   {$ifdef unix}
  538.   fpSystem(Format(PROG_REMOVER_MASK, [bTempDir]));
  539.   {$else}
  540.   WinExec(Format(PROG_REMOVER_MASK, [bTempDir]), SW_HIDE);
  541.   {$endif}
  542.  
  543.   bContent.Free;
  544.   bStyles.Free;
  545.   bMeta.Free;
  546.   bSettings.Free;
  547.   bManifest.Free;
  548. End;
  549.  
  550. End.
Advertisement
Add Comment
Please, Sign In to add comment