Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Suchen & Ersetzen in einem OpenOffice-Dokument (https://www.delphipraxis.net/139422-suchen-ersetzen-einem-openoffice-dokument.html)

Killuah 28. Aug 2009 14:57


Suchen & Ersetzen in einem OpenOffice-Dokument
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo erstmal,

zur Zeit bin ich auf der Suche nach einer Möglichkeit in einem OpenOffice-Dokument(.odt / Writer) alle Variablen mit Inhalten zu ersetzen.
Syntax der Variable ist immer "[Bezeichnung]". Da ich schon einige verschiedene Möglichkeiten ausprobiert habe und nichts meinen Anforderungen ensprechen konnte bitte ich hier um etwas Hilfe.

Die wohl einfachste Methode wäre über den Search-/ ReplaceDescriptor von OpenOffice. Da ich hier jedoch den genauen Suchbegriff eingeben muss und ich in meinen Variablen auch If/Else-Anweisungen zulasse (eigener interner Parser der das Ganze verarbeitet) scheidet diese Möglichkeit leider aus.

Eine weitere Möglichkeit ist, über den SearchDescriptor eine Range zu erhalten, an dem die Variable beginnt und dort mit einem TextCursor zu arbeiten. Das funktioniert in reinem Text wirklich ausgezeichnet. Erst wenn man mit Tabellen oder Textfeldern arbeitet stößt man bei dieser Methode allein an seine Grenzen. So ist es leider nicht möglich in der Tabelle den TextCursor zu erstellen, da ich diesen nur auf einem XText-Objekt erstellen kann, und jede Tabelle ein eigenes XText behinhaltet, das ich hier nicht herausbekomme. (vielleicht weiß ja einer wie ich das rauskriege)

Eine weitere Möglichkeit ist, das ich alle XText-Objekte der Reihe nach durchgehe und schaue, ob die eckigen Klammern vorhanden sind. Jedoch tauchen auch hier Probleme bei den Tabellen auf. Wie man in der Datei im Anhang sieht, habe ich in einer Tabelle eine weitere Tabelle. Im Folgenden ersteinmal der bisherige QuellText:

Delphi-Quellcode:
type
  TOOTest = class(TForm)
    OpenDialog1: TOpenDialog;
    procedure FormCreate(Sender: TObject);
  public
    OOSrvMgr: Variant;  // ServiceManager
    OOfficeApp: Variant; // XDesktop
    OODispHlp: Variant; // DispatchHelper
    OODokument: Variant;
    procedure ReplaceTables;
    procedure ReplaceText(XText: Variant);
    function GetCountInString(sAllText, sSearch: String): Integer;
  end;

implementation

  procedure TOOTest.FormCreate(Sender: TObject);
  var
    sURLLocation: String;
  begin
  // Vorbereitung
    OOSrvMgr := CreateOleObject('com.sun.star.ServiceManager');
    OOfficeApp := OOSrvMgr.CreateInstance('com.sun.star.frame.Desktop');
    OODispHlp := OOSrvMgr.CreateInstance('com.sun.star.frame.DispatchHelper');
    if OpenDialog1.Execute then
      sURLLocation := ConvertToURL(OpenDialog1.FileName); // ConvertToURL ist aus den Examples von OOC
    OODokument := OOfficeApp.LoadComponentFromURL(sURLLocation, '_blank', 0, VarArrayCreate([0, -1], VarVariant));

    ReplaceTables;

    OODokument.Close(False);
    OOfficeApp := unassigned;
    OOSrvMgr := Null;
  end;

  procedure TOOTest.ReplaceTables;
  var
    XTextTables: Variant;
    XTable: Variant;
    XCell : Variant;
    iTable: Integer;
    Row, Column: Integer;
  begin
    XTextTables := OODokument.getTextTables;
 
    for iTable := XTextTables.getCount -1 downto 0 do
    begin
      XTable:= XTextTables.getByIndex(iTable);

      for Row := 0 to XTable.Rows.Count -1  do
      begin
        for Column := 0 to XTable.Columns.Count -1 do
        begin
          XCell := XTable.getCellByPosition(Column, Row);
          //ShowMessage('CellText = "'+XCell.getText.getString+'"');
          ReplaceText(XCell.getText);
        end;
      end;
    end;
  end;

  procedure TOOTest.ReplaceText(XText: Variant);
  var
    XTextCursor: Variant;
    // XTextCursor2: Variant;
    // XHlpCursor: Variant;
    bSelect: Boolean;
    sAllText: String;
    iAktPos: Integer;
  begin
    XTextCursor := XText.createTextCursor;
    XTextCursor.gotoStart(False);
 
    // XTextCursor2 := XText.createTextCursor;
    // XTextCursor2.gotoStart(False);
    // XHlpCursor := XText.createTextCursor;
    // XHlpCursor.gotoStart(False);

    sAllText := XText.getString;
 
    bSelect := False;
    while (Pos('[', sAllText) > 0) and (Pos(']', sAllText) > 0) do
    begin
      if sAllText[1] = '[' then
      begin
        bSelect := True;
        XTextCursor.goRight(1, bSelect);
      end else if sAllText[1] = ']' then
      begin
        XTextCursor.goRight(1, bSelect);
        if XTextCursor.getString <> '' then
          // überprüfen, ob die Anzahl an '[' und ']' identisch sind (für internen Parser (bzgl. If-Abfragen) wichtig)
          // GetCountInString(Text, SuchString: String): Integer;
          if GetCountInString(XTextCursor.getString, '[') = GetCountInString(XTextCursor.getString, ']') then
          begin
            XTextCursor.setString('');
            bSelect := False;
          end;
      end else
        XTextCursor.goRight(1, bSelect);

      // ToDo: geht so nicht, da der TextCursor ÜBER die Tabelle springt und
      //       der sAllText die Inhalte enthält
      sAllText := Copy(sAllText, 2, Length(sAllText)-1);

      // ToDo: "sAllText := XTextCursor2.getText.getString;" gibt immer den kompletten Inhalt der Zelle zurück
      // XTextCursor2.goRight(1, False);
      // XTextCursor2.gotoEnd(True);
      // XHlpCursor.goRight(1, False);
      // sAllText := XTextCursor2.getText.getString;

      // Expand = True, damit die Seletierung wieder aufgehoben wird (?)
      // XTextCursor2.gotoRange(XHlpCursor, True);
    end;
  end;

end.
Nach dem Auswählen der Datei suche ich alle Tabellen und gehe diese der Reihe nach, beginnend mit der Letzten, einzeln durch.

Delphi-Quellcode:
Info: Warum mit der Letzten beginnen?

In OpenOffice sind die Tabellen im Dokument von Oben nach Unten einfach durchnummeriert.

Bsp:
[Dokument]
  [Tabelle1]
  [/Tabelle1]

  [Tabelle2]
    [Tabelle3]
    [/Tabelle3]
  [/Tabelle2]
[/Dokument]

Wenn ich in Tabelle2 bin und mir "sAllText" hole dann enthält dieser den kompletten Textinhalt von Tabelle3 - getrennt über einen Zeilenumbruch #13#10. Wenn ich also von hinten beginne habe ich in Tabelle3 keine '[' oder ']' mehr.
Dann wähle ich jede Zelle einzeln aus und ersetze mit "ReplaceText(XText: Variant)" die Variablen. Wie man anhand der beiden ToDo's in der Prozedur sehen kann stoße ich jedoch auf Probleme, wenn ich in einer Tabelle eine weitere habe.


Im Anhang einmal die Textdatei mit der ich teste und der Quellcode zum Debuggen. Ich selber Suche zur Zeit entweder in verschiedenen Foren, der API-Seite von OpenOffice oder in dem ObjektInspektor den man als Plugin in OpenOffice starten kann. Jedoch konnte bisher nichts weiterhelfen.


freundliche Grüße
Killuah

worker 28. Aug 2009 15:06

Re: Suchen & Ersetzen in einem OpenOffice-Dokument
 
ODT-Dateien bestehen doch nur aus XML-FILES:
In diesen mittels RegEx Search&Replace.

Killuah 28. Aug 2009 15:47

Re: Suchen & Ersetzen in einem OpenOffice-Dokument
 
XML sieht auf den ersten Blick recht gut aus.

Um meine Problematik etwas genauer zu definieren komme ich wohl nicht drum rum mehr zu erzählen. ^^

Ich will die Variablen mit Personendaten aus einer Datenbank ersetzen. Diese Personendaten enthalten unter Anderen auch einen Lebenslauf. Da ich bei der Erstellung einer Vorlage dafür nie genau weiß, wieviele CV-Einträge ich habe, ersetzte ich Diese während der Laufzeit.

Beispiel:

Eine Person hat 5 Einträge im Lebenslauf. In einer Vorlage würde dies für mich so aussehen.

XML-Code:
<text>
  [CV_Start]
  <tabelle>
    [CV_Daten][CV_Next]
  </tabelle>
  [CV_Ende]
</text>
Programmiertechnisch wurde (bisher über MS Word) der Inhalt zwischen [CV_Start] und [CV_Ende] für jeden Eintrag kopiert damit das Ganze vor dem eigentlichen Ersetzen der Variablen so aussieht.

XML-Code:
<text>
  [CV_Start]
  <tabelle>
    [CV_Daten][CV_Next]
  </tabelle>
  <tabelle>
    [CV_Daten][CV_Next]
  </tabelle>
  <tabelle>
    [CV_Daten][CV_Next]
  </tabelle>
  <tabelle>
    [CV_Daten][CV_Next]
  </tabelle>
  <tabelle>
    [CV_Daten][CV_Next]
  </tabelle>
  [CV_Ende]
</text>
Da in der XML die Tabellen jedoch schon vor dem eigentlichen Text deklariert sind und die CV_Daten unterschiedliche Längen haben können, weiß ich nicht genau ob ich dann in der XML einfach die Tabelle erweitern kann, da hier feste Größen eingestellt sind.

Edit: Das mit den Größen einfach nicht beachten, habs falsch interpretiert und ist somit hinfällig!

XML-Code:
<style:style style:name="Tabelle2.A" style:family="table-column">
<style:table-column-properties style:column-width="16.999cm" style:rel-column-width="65535*"/>
</style:style>
<style:style style:name="Tabelle2.A1" style:family="table-cell">
<style:table-cell-properties fo:padding="0.097cm" fo:border="0.002cm solid #000000"/>
</style:style>
Ich werde das mit der XML mal austesten. Für weitere Lösungsansätze bin ich trotzdem dankbar.

freundliche Grüße
Killuah

Killuah 2. Sep 2009 10:17

Re: Suchen & Ersetzen in einem OpenOffice-Dokument
 
*push* :coder:

So, ich habe mir das Ganze mit XML jetzt mal etwas genauer angesehn.

Ich öffne die content.xml in Delphi mit der JvSimpleXML und gehe rekursiv durch die einzelnen
Nodes/ChildNodes und ersetze dann jeweils die im Value stehenden Variablen.
Klappt soweit echt super.

Bezüglich der Tabellen habe ich mir überlegt einfach weitere Nodes einzufügen. Da das an zwei
verschiedenen Stellen geschehen muss könnte ich ja den Namen der Tabelle/Zelle im Bereich
"office:automatic-styles" suchen und das Ganze dann dort noch erweitern.

Dazu habe ich testweise eine Writer-Datei erstellt, die eine Tabelle mit drei Spalten und
einer Zeile besitzt. Jetzt sieht meine XML folgendermaßen aus:

XML-Code:
<office:automatic-styles>
  <style:style style:name="Tabelle1" style:family="table">
    <style:table-properties style:width="16.999cm" table:align="margins"/>
  </style:style>
  <style:style style:name="Tabelle1.A" style:family="table-column">
    <style:table-column-properties style:column-width="5.666cm" style:rel-column-width="21845*"/>
  </style:style>
  <style:style style:name="Tabelle1.A1" style:family="table-cell">
    <style:table-cell-properties fo:padding="0.097cm" fo:border-left="0.002cm solid #000000" fo:border-right="none" fo:border-top="0.002cm solid #000000" fo:border-bottom="0.002cm solid #000000"/>
  </style:style>
  <style:style style:name="Tabelle1.C1" style:family="table-cell">
    <style:table-cell-properties fo:padding="0.097cm" fo:border="0.002cm solid #000000"/>
  </style:style>
</office:automatic-styles>
<office:body>
  <office:text>
    <table:table table:name="Tabelle1" table:style-name="Tabelle1">
    <table:table-column table:style-name="Tabelle1.A" table:number-columns-repeated="3"/>
      <table:table-row>
        <table:table-cell table:style-name="Tabelle1.A1" office:value-type="string">
          <text:p text:style-name="Table_20_Contents"/>
        </table:table-cell>
        <table:table-cell table:style-name="Tabelle1.A1" office:value-type="string">
          <text:p text:style-name="Table_20_Contents"/>
        </table:table-cell>
        <table:table-cell table:style-name="Tabelle1.C1" office:value-type="string">
          <text:p text:style-name="Table_20_Contents"/>
        </table:table-cell>
      </table:table-row>
    </table:table>
  </office:text>
</office:body>
Wie man sieht, sind im Bereich "office:automatic-styles" nur die Tabelle und 2 der 3 Spalten
deklariert. Im Bereich "office:body" wurde dann der Stylename 'Tabelle1.A1' doppelt vergeben,
obwohl es unterschiedliche Zellen sind.

In der API-Beschreibung von OpenOffice habe ich gelesen, das die Zellennamen eindeutig zugewiesen
sind und man auch über Funktionen direkt auf eine spezielle Zelle per Namen zugreifen kann. Wo
sind diese Namen denn hinterlegt, wenn nicht hier? (habe alle andern .xml durchgeschaut aber nix
gefunden)

Eine weitere Frage ist, was mir "table:number-columns-repeated" aussagen soll. Ich habe in der Tabelle
doch alle Spalten/Zellen deklariert, wozu dann also nochmal solch eine Eigenschaft?

Ich werds weiterversuchen, für jede Hilfe bin ich dennoch dankbar.

Mfg
Killuah


Edit: "Delphi-Code" durch "XML-Code" ersetzt

Schwedenbitter 6. Jun 2010 20:28

AW: Re: Suchen & Ersetzen in einem OpenOffice-Dokument
 
Hallo,

ich komme mit OOO nicht ganz klar.
Ich möchte odt-Dateien nach einem ganz bestimmten String durchsuchen. Dabei habe ich ein paar Probleme:
  1. Das Öffnen und Bearbeiten mit der in Deinem ersten Eintrag beschriebenen Vorgehensweise dauert mir bereits zu lange. Ich möchte später einige Dateien anhand des Inhalts sortieren. Das würde dann ewig brauchen.
  2. Ferner habe ich das Problem, dass ich zwar mittlerweile an den gesamten Text einer odt-Datei rankomme. Das aber auch nur, wenn keine separaten Textfelder vorhanden sind. Die "sehe" ich leider nicht. Und wenn dann dort meine wichtigen Infos enthalten sind, bin ich schon zum Scheitern verurteilt.
Ich wollte daher nach Plan B vorgehen. Dieser sah vor, die content.xml aus der odt-Datei zu entpacken und dann diese zu durchsuchen. Ich habe bereits probiert, die odt-Datei in eine tmp.zip zu kopieren und damit auszupacken. Das klappt nur leider nicht. Ich teste gerade, ob es an der odt-Datei oder am Code liegt.

Ich habe in Deinen Beiträgen zu diesem Thema mehrfach das Wort "content.xml" gefunden. Extrahierst Du die auch oder wie kommt man an deren Inhalt?

Gruß & Dank, Alex


Alle Zeitangaben in WEZ +1. Es ist jetzt 03:57 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz