Programmgesteuertes Durchlaufen von Indizes, Hoch- und Gleichungen in einem Word-Dokument

12

Ich habe ein paar Word-Dokumente, die jeweils ein paar hundert Seiten mit wissenschaftlichen Daten enthalten, darunter:

  • Chemische Formeln (H2SO4 mit allen korrekten tiefgestellten und hochgestellten Zeichen)
  • Wissenschaftliche Zahlen (Exponenten, die mit hochgestellten Zeichen formatiert wurden)
  • Viele mathematische Gleichungen. Geschrieben mit dem mathematischen Gleichungseditor in Word.

Das Problem ist, dass das Speichern dieser Daten in Word für uns nicht effizient ist. Deshalb möchten wir all diese Informationen in einer Datenbank (MySQL) speichern. Wir wollen die Formatierung nach LaTex konvertieren.

Gibt es eine Möglichkeit, mit VBA alle Tief-, Hoch- und Gleichungen in einem Word-Dokument zu durchlaufen?

Krallen
quelle
Haben Sie darüber nachgedacht, die XML-Daten selbst aus dem Dokument zu extrahieren? Alle Microsoft Documents 2007+ (.docx) sind im Grunde genommen komprimierte XML-Dateien. Sie können diese mit einem XML-Parser abrufen.
James Mertz
Es war zu lang, um als Kommentar zu posten, deshalb habe ich als Antwort hinzugefügt.
James Mertz

Antworten:

12

Ja da ist. Ich würde empfehlen, PowerShell zu verwenden, da es Word-Dateien recht gut verarbeitet. Ich denke, ich werde der einfachste Weg sein.

Weitere Informationen zu PowerShell und Word finden Sie hier: http://www.simple-talk.com/dotnet/.net-tools/com-automation-of-office-applications-via-powershell/

Ich habe etwas tiefer gegraben und dieses Powershell-Skript gefunden:

param([string]$docpath,[string]$htmlpath = $docpath)

$srcfiles = Get-ChildItem $docPath -filter "*.doc"
$saveFormat = [Enum]::Parse([Microsoft.Office.Interop.Word.WdSaveFormat], "wdFormatFilteredHTML");
$word = new-object -comobject word.application
$word.Visible = $False

function saveas-filteredhtml
    {
        $opendoc = $word.documents.open($doc.FullName);
        $opendoc.saveas([ref]"$htmlpath\$doc.fullname.html", [ref]$saveFormat);
        $opendoc.close();
    }

ForEach ($doc in $srcfiles)
    {
        Write-Host "Processing :" $doc.FullName
        saveas-filteredhtml
        $doc = $null
    }

$word.quit();

Speichern Sie es als .ps1 und starten Sie es mit:

convertdoc-tohtml.ps1 -docpath "C:\Documents" -htmlpath "C:\Output"

Die gesamte DOC-Datei aus dem angegebenen Verzeichnis wird als HTML-Datei gespeichert. Also habe ich eine doc-Datei, in der ich Ihre H2SO4 mit Indizes und nach der Powershell-Konvertierung die Ausgabe folgt:

<html>

<head>
<meta http-equiv=Content-Type content="text/html; charset=windows-1252">
<meta name=Generator content="Microsoft Word 14 (filtered)">
<style>
<!--
 /* Font Definitions */
 @font-face
    {font-family:Calibri;
    panose-1:2 15 5 2 2 2 4 3 2 4;}
 /* Style Definitions */
 p.MsoNormal, li.MsoNormal, div.MsoNormal
    {margin-top:0in;
    margin-right:0in;
    margin-bottom:10.0pt;
    margin-left:0in;
    line-height:115%;
    font-size:11.0pt;
    font-family:"Calibri","sans-serif";}
.MsoChpDefault
    {font-family:"Calibri","sans-serif";}
.MsoPapDefault
    {margin-bottom:10.0pt;
    line-height:115%;}
@page WordSection1
    {size:8.5in 11.0in;
    margin:1.0in 1.0in 1.0in 1.0in;}
div.WordSection1
    {page:WordSection1;}
-->
</style>

</head>

<body lang=EN-US>

<div class=WordSection1>

<p class=MsoNormal><span lang=PL>H<sub>2</sub>SO<sub>4</sub></span></p>

</div>

</body>

</html>

Wie Sie sehen können, haben Subskripte ihre eigenen Tags in HTML. Sie müssen also nur die Datei in Bash oder C ++ analysieren, um sie von Body zu Body zu schneiden, die in LATEX zu ändern und den Rest der HTML-Tags anschließend zu entfernen.

Code von http://blogs.technet.com/b/bshukla/archive/2011/09/27/3347395.aspx


Daher habe ich in C ++ einen Parser entwickelt, der nach HTML-Indizes sucht und diese durch LATEX-Indizes ersetzt.

Der Code:

#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <vector>

using namespace std;

 vector < vector <string> > parse( vector < vector <string> > vec, string filename )
{
        /*
                PARSES SPECIFIED FILE. EACH WORD SEPARATED AND
                PLACED IN VECTOR FIELD.

                REQUIRED INCLUDES:
                                #include <iostream>
                                #include <fstream>
                                #include <string>
                                #include <sstream>
                                #include <vector>

            EXPECTS: TWO DIMENTIONAL VECTOR
                     STRING WITH FILENAME
            RETURNS: TWO DIMENTIONAL VECTOR
                     vec[lines][words]
        */
        string vword;
        ifstream vfile;
        string tmp;

         // FILENAME CONVERSION FROM STING
        //  TO CHAR TABLE

        char cfilename[filename.length()+1];
        if( filename.length() < 126 )
        {
                for(int i = 0; i < filename.length(); i++)
                                cfilename[i] = filename[i];
                cfilename[filename.length()] = '\0';
        }
        else return vec;

         // OPENING FILE
        //
        vfile.open( cfilename );
        if (vfile.is_open())
        {
                while ( vfile.good() )
                {
                        getline( vfile, vword );
                        vector < string > vline;
                        vline.clear();

                        for (int i = 0; i < vword.length(); i++)
                        {
                                tmp = "";
                                 // PARSING CONTENT. OMITTING SPACES AND TABS
                                //
                                while (vword[i] != ' ' && vword[i] != ((char)9) && i < vword.length() )
                                        tmp += vword[i++];
                                if( tmp.length() > 0 ) vline.push_back(tmp);
                        }
                        if (!vline.empty())
                                vec.push_back(vline);
                }
                vfile.close();
        }
        else cout << "Unable to open file " << filename << ".\n";
        return vec;
}

int main()
{
        vector < vector < string > > vec;
        vec = parse( vec, "parse.html" );

        bool body = false;
        for (int i = 0; i < vec.size(); i++)
        {
                for (int j = 0; j < vec[i].size(); j++)
                {
                        if ( vec[i][j] == "<body") body=true;
                        if ( vec[i][j] == "</body>" ) body=false;
                        if ( body == true )
                        {
                                for ( int k=0; k < vec[i][j].size(); k++ )
                                {
                                        if (k+4 < vec[i][j].size() )
                                        {
                                                if (    vec[i][j][k]   == '<' &&
                                                        vec[i][j][k+1] == 's' &&
                                                        vec[i][j][k+2] == 'u' &&
                                                        vec[i][j][k+3] == 'b' &&
                                                        vec[i][j][k+4] == '>' )
                                                {

                                                        string tmp = "";
                                                        while (vec[i][j][k+5] != '<')
                                                        {
                                                                tmp+=vec[i][j][k+5];
                                                                k++;
                                                        }
                                                        tmp = "_{" + tmp + "}";
                                                        k=k+5+5;
                                                        cout << tmp << endl;;
                                                }
                                                else cout << vec[i][j][k];
                                        }
                                        else cout << vec[i][j][k];
                                }
                                cout << endl;
                        }
                }
        }
        return 0;
}

Für die HTML-Datei:

<html>

<head>
<meta http-equiv=Content-Type content="text/html; charset=windows-1252">
<meta name=Generator content="Microsoft Word 14 (filtered)">
<style>
<!--
 /* Font Definitions */
 @font-face
        {font-family:Calibri;
        panose-1:2 15 5 2 2 2 4 3 2 4;}
 /* Style Definitions */
 p.MsoNormal, li.MsoNormal, div.MsoNormal
        {margin-top:0in;
        margin-right:0in;
        margin-bottom:10.0pt;
        margin-left:0in;
        line-height:115%;
        font-size:11.0pt;
        font-family:"Calibri","sans-serif";}
.MsoChpDefault
        {font-family:"Calibri","sans-serif";}
.MsoPapDefault
        {margin-bottom:10.0pt;
        line-height:115%;}
@page WordSection1
        {size:8.5in 11.0in;
        margin:1.0in 1.0in 1.0in 1.0in;}
div.WordSection1
        {page:WordSection1;}
-->
</style>

</head>

<body lang=EN-US>

<div class=WordSection1>

<p class=MsoNormal><span lang=PL>H<sub>2</sub>SO<sub>4</sub></span></p>

</div>

</body>

</html>

Die Ausgabe ist:

<body
lang=EN-US>
<div
class=WordSection1>
<p
class=MsoNormal><span
lang=PL>H_{2}
SO_{4}
</span></p>
</div>

Es ist natürlich nicht ideal, aber der Genuss ist ein Proof of Concept.

mnmnc
quelle
3

Sie können die XML-Datei direkt aus jedem Office-Dokument extrahieren, das 2007+ ist. Dies geschieht auf folgende Weise:

  1. Benennen Sie die Datei von .docx in .zip um
  2. extrahiere die Datei mit 7zip (oder einem anderen Extraktionsprogramm)
  3. Den tatsächlichen Inhalt des Dokuments finden Sie im extrahierten Ordner unter dem wordUnterordner und der document.xmlDatei. Das sollte den gesamten Inhalt des Dokuments enthalten.

Bildbeschreibung hier eingeben

Ich habe ein Beispieldokument erstellt und in den Body-Tags Folgendes gefunden (Anmerkung: Ich habe dies schnell zusammengestellt, sodass die Formatierung möglicherweise ein wenig verfälscht ist):

<?xml version="1.0" encoding="UTF-8" standalone="true"?>
<w:body>
    -<w:p w:rsidRDefault="000E0C3A" w:rsidR="008B5DAA">
        -<w:r>
            <w:t xml:space="preserve">This </w:t>
        </w:r>
-       <w:r w:rsidRPr="000E0C3A">
            -<w:rPr>
                <w:vertAlign w:val="superscript"/>
            </w:rPr>
            <w:t>is</w:t>
        </w:r>
-       <w:r>
            <w:t xml:space="preserve"> a </w:t>
        </w:r>
            -<w:r w:rsidRPr="000E0C3A">
                -<w:rPr>
                    <w:vertAlign w:val="subscript"/>
                </w:rPr>
                <w:t>test</w:t>
            </w:r>
        -<w:r>
            <w:t>.</w:t>
        </w:r>
    </w:p>
</w:body>

Es scheint, dass das <w:t>Tag für Text <w:rPr>ist, das die Definition der Schriftart ist und das <w:p>ein neuer Absatz ist.

Das Wortäquivalent sieht folgendermaßen aus:

Bildbeschreibung hier eingeben

James Mertz
quelle
2

Ich habe einen anderen Ansatz als den von mnmnc verfolgt.

Meine Versuche, ein Test-Word-Dokument als HTML zu speichern, waren nicht erfolgreich. Ich habe in der Vergangenheit festgestellt, dass von Office generiertes HTML so voller Spreu ist, dass es nahezu unmöglich ist, die gewünschten Elemente auszuwählen. Ich habe festgestellt, dass dies hier der Fall ist. Ich hatte auch ein Problem mit Gleichungen. Word speichert Gleichungen als Bilder. Für jede Gleichung gibt es zwei Bilder, eines mit einer Erweiterung von WMZ und eines mit einer Erweiterung von GIF. Wenn Sie die HTML-Datei mit Google Chrome anzeigen, sehen die Gleichungen OK aus, sind aber nicht besonders gut. Die Darstellung entspricht der GIF-Datei, wenn sie mit einem Bildanzeige- / Bearbeitungswerkzeug angezeigt wird, das transparente Bilder verarbeiten kann. Wenn Sie die HTML-Datei mit dem Internet Explorer anzeigen, sehen die Gleichungen perfekt aus.

Zusätzliche Information

Ich hätte diese Information in die ursprüngliche Antwort aufnehmen sollen.

Ich habe ein kleines Word-Dokument erstellt, das ich als HTML gespeichert habe. Die drei Bereiche im Bild unten zeigen das ursprüngliche Word-Dokument, das HTML-Dokument, wie es von Microsoft Internet Explorer angezeigt wird, und das HTML-Dokument, wie es von Google Chrome angezeigt wird.

Originalwort, von IE angezeigtes HTML und von Chrome angezeigtes HTML

Wie bereits erläutert, ist der Unterschied zwischen den IE- und Chrome-Bildern darauf zurückzuführen, dass die Gleichungen zweimal gespeichert wurden, einmal im WMZ-Format und einmal im GIF-Format. Das HTML ist zu groß, um es hier anzuzeigen.

Das vom Makro erzeugte HTML ist:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" 
                   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head><body>
<p>Some ordinary text.</p>
<p>H<sub>2</sub>SO<sub>4</sub>.</p>
<p>Abc &amp; def &gt; ghi &lt; jkl</p>
<p>x<sup>3</sup>+ x<sup>2</sup>+3x+4=0.</p><p></p>
<p><i>Equation</i>  </p>
<p>Mno</p>
<p><i>Equation</i></p>
</body></html>

Welche zeigt als:

HTML erstellt von Makro wie vom IE angezeigt

Ich habe nicht versucht, die Gleichungen zu konvertieren, da das kostenlose MathType Software Development Kit anscheinend Routinen enthält, die in LaTex konvertiert werden

Der Code ist ziemlich einfach, also nicht viele Kommentare. Fragen Sie, ob etwas unklar ist. Hinweis: Dies ist eine verbesserte Version des Originalcodes.

Sub ConvertToHtml()

  Dim FileNum As Long
  Dim NumPendingCR As Long
  Dim objChr As Object
  Dim PathCrnt As String
  Dim rng As Word.Range
  Dim WithinPara As Boolean
  Dim WithinSuper As Boolean
  Dim WithinSub As Boolean

  FileNum = FreeFile
  PathCrnt = ActiveDocument.Path
  Open PathCrnt & "\TestWord.html" For Output Access Write Lock Write As #FileNum

  Print #FileNum, "<!DOCTYPE html PUBLIC ""-//W3C//DTD XHTML 1.0 Frameset//EN""" & _
                  " ""http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"">" & _
                  vbCr & vbLf & "<html xmlns=""http://www.w3.org/1999/xhtml"" " & _
                  "xml:lang=""en"" lang=""en"">" & vbCr & vbLf & _
                  "<head><meta http-equiv=""Content-Type"" content=""text/html; " _
                  & "charset=utf-8"" />" & vbCr & vbLf & "</head><body>"

  For Each rng In ActiveDocument.StoryRanges

    NumPendingCR = 0
    WithinPara = False
    WithinSub = False
    WithinSuper = False

    Do While Not (rng Is Nothing)
      For Each objChr In rng.Characters
        If objChr.Font.Superscript Then
          If Not WithinSuper Then
            ' Start of superscript
            Print #FileNum, "<sup>";
            WithinSuper = True
          End If
        ElseIf WithinSuper Then
          ' End of superscript
          Print #FileNum, "</sup>";
          WithinSuper = False
        End If
        If objChr.Font.Subscript Then
          If Not WithinSub Then
            ' Start of subscript
            Print #FileNum, "<sub>";
            WithinSub = True
          End If
        ElseIf WithinSub Then
          ' End of subscript
          Print #FileNum, "</sub>";
          WithinSub = False
          End If
          Select Case objChr
            Case vbCr
              NumPendingCR = NumPendingCR + 1
            Case "&"
              Print #FileNum, CheckPara(NumPendingCR, WithinPara) & "&amp;";
            Case "<"
              Print #FileNum, CheckPara(NumPendingCR, WithinPara) & "&lt;";
            Case ">"
              Print #FileNum, CheckPara(NumPendingCR, WithinPara) & "&gt;";
            Case Chr(1)
              Print #FileNum, CheckPara(NumPendingCR, WithinPara) & "<i>Equation</i>";
            Case Else
              Print #FileNum, CheckPara(NumPendingCR, WithinPara) & objChr;
          End Select
      Next
      Set rng = rng.NextStoryRange
    Loop
  Next

  If WithinPara Then
    Print #FileNum, "</p>";
    withpara = False
  End If

  Print #FileNum, vbCr & vbLf & "</body></html>"

  Close FileNum

End Sub
Function CheckPara(ByRef NumPendingCR As Long, _
                   ByRef WithinPara As Boolean) As String

  ' Have a character to output.  Check paragraph status, return
  ' necessary commands and adjust NumPendingCR and WithinPara.

  Dim RtnValue As String

  RtnValue = ""

  If NumPendingCR = 0 Then
    If Not WithinPara Then
      CheckPara = "<p>"
      WithinPara = True
    Else
      CheckPara = ""
    End If
    Exit Function
  End If

  If WithinPara And (NumPendingCR > 0) Then
    ' Terminate paragraph
    RtnValue = "</p>"
    NumPendingCR = NumPendingCR - 1
    WithinPara = False
  End If
  Do While NumPendingCR > 1
    ' Replace each pair of CRs with an empty paragraph
    RtnValue = RtnValue & "<p></p>"
    NumPendingCR = NumPendingCR - 2
  Loop
  RtnValue = RtnValue & vbCr & vbLf & "<p>"
  WithinPara = True
  NumPendingCR = 0

  CheckPara = RtnValue

End Function
Tony Dallimore
quelle
Gute Arbeit. Funktioniert es für mehrere Dateien oder müssen Sie es in der Datei platzieren, die Sie konvertieren möchten?
Mnmnc
@mnmnc. Vielen Dank. Ich denke, Ihre Lösung ist Eindruck, obwohl es wahrscheinlich klar ist, dass ich nicht glaube, dass eine Lösung, die mit Microsoft Html beginnt, funktionieren wird. Infolge einer Stapelüberlauf-Frage arbeite ich an der Konvertierung von Excel in HTML, da mit PublishObjects von Microsoft HTML erstellt wird, das für die meisten (alle?) Smartphones nicht akzeptabel ist. Ich habe wenig Erfahrung mit Word VBA. Ich bin am besten mit Excel und Outlook VBA und ich war gut mit Acess VBA. Diese ermöglichen es einem Makro in einer Datei, auf andere Dateien zuzugreifen, sodass ich sicher bin, dass dies auch für Word gilt.
Tony Dallimore
0

Der einfachste Weg, dies zu tun, sind nur die folgenden Zeilen in VBA:

Sub testing()
With ActiveDocument.Content.Find
 .ClearFormatting
 .Format = True
 .Font.Superscript = True
 .Execute Forward:=True
End With

End Sub

Dies findet den gesamten hochgestellten Text. Wenn Sie etwas damit anfangen möchten, fügen Sie es einfach in die Methode ein. Um zum Beispiel das Wort "super" in einem hochgestellten Text zu finden und es in "super found" umzuwandeln, verwenden Sie:

Sub testing()

With ActiveDocument.Content.Find
 .ClearFormatting
 .Format = True
 .Font.Superscript = True
 .Execute Forward:=True, Replace:=wdReplaceAll, _
 FindText:="super", ReplaceWith:="super found"
End With

End Sub
soandos
quelle