Comment itérer par programme à travers les indices, les exposants et les équations trouvés dans un document Word

12

J'ai quelques documents Word, contenant chacun quelques centaines de pages de données scientifiques qui comprennent:

  • Formules chimiques (H2SO4 avec tous les indices et exposants appropriés)
  • Numéros scientifiques (exposants formatés à l'aide d'exposants)
  • Beaucoup d'équations mathématiques. Écrit à l'aide de l'éditeur d'équations mathématiques dans Word.

Le problème est que le stockage de ces données dans Word n'est pas efficace pour nous. Nous voulons donc stocker toutes ces informations dans une base de données (MySQL). Nous voulons convertir le formatage en LaTex.

Existe-t-il un moyen de parcourir tous les sous-indices, exposants et équations d'un document Word à l'aide de VBA?

les griffes
la source
Avez-vous pensé à extraire les données xml du document lui-même? Tous les documents Microsoft 2007+ (.docx) sont essentiellement des fichiers xml compressés. Vous pouvez les récupérer à l'aide d'un analyseur xml.
James Mertz
il était trop long pour poster un commentaire, j'ai donc ajouté comme réponse.
James Mertz

Réponses:

12

Oui il y a. Je suggérerais d'utiliser Powershell car il gère assez bien les fichiers Word. Je pense que je serai le moyen le plus simple.

Plus d'informations sur Powershell vs Word Automation ici: http://www.simple-talk.com/dotnet/.net-tools/com-automation-of-office-applications-via-powershell/

J'ai creusé un peu plus et j'ai trouvé ce script PowerShell:

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();

Enregistrez-le sous .ps1 et démarrez-le avec:

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

Il enregistrera tous les fichiers .doc du répertoire spécifié, en tant que fichiers html. J'ai donc un fichier doc dans lequel j'ai votre H2SO4 avec des indices et après la conversion powershell, la sortie est la suivante:

<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>

Comme vous pouvez le voir, les indices ont leurs propres balises en HTML, donc il ne reste plus qu'à analyser le fichier en bash ou c ++ pour couper de corps en / corps, changer en LATEX et supprimer le reste des balises HTML par la suite.

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


J'ai donc développé un analyseur en C ++ pour rechercher un indice HTML et le remplacer par un indice LATEX.

Le 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;
}

Pour le fichier html:

<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>

La sortie est:

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

Ce n'est pas idéal, bien sûr, mais le traitement est une preuve de concept.

mnmnc
la source
3

Vous pouvez extraire le xml directement à partir de n'importe quel document Office 2007+. Cela se fait de la manière suivante:

  1. renommer le fichier de .docx en .zip
  2. extraire le fichier en utilisant 7zip (ou un autre programme d'extraction)
  3. Pour le contenu réel du document, regardez dans le dossier extrait sous le sous- worddossier et le document.xmlfichier. Cela devrait contenir tout le contenu du document.

entrez la description de l'image ici

J'ai créé un exemple de document, et dans les balises body, j'ai trouvé ceci (notez que je l'ai rapidement mis ensemble, donc la mise en forme peut être un peu décalée):

<?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>

Il apparaît que la <w:t>balise est pour le texte <w:rPr>est la définition de la police et <w:p>est un nouveau paragraphe.

Le mot équivalent ressemble à ceci:

entrez la description de l'image ici

James Mertz
la source
2

J'ai cherché une approche différente de celle suivie par mnmnc.

Mes tentatives pour enregistrer un document Word de test au format HTML n'ont pas été un succès. J'ai découvert dans le passé que le code HTML généré par Office est si plein de paillettes que sélectionner les bits que vous voulez est presque impossible. J'ai trouvé que c'était le cas ici. J'ai également eu un problème avec les équations. Word enregistre les équations sous forme d'images. Pour chaque équation, il y aura deux images une avec une extension de WMZ et une avec une extension de GIF. Si vous affichez le fichier html avec Google Chrome, les équations semblent OK mais pas merveilleuses; l'apparence correspond au fichier GIF lorsqu'il est affiché avec un outil d'affichage / d'édition d'image qui peut gérer des images transparentes. Si vous affichez le fichier HTML avec Internet Explorer, les équations semblent parfaites.

Information additionnelle

J'aurais dû inclure ces informations dans la réponse d'origine.

J'ai créé un petit document Word que j'ai enregistré en Html. Les trois panneaux de l'image ci-dessous montrent le document Word d'origine, le document Html affiché par Microsoft Internet Explorer et le document Html affiché par Google Chrome.

Mot d'origine, Html affiché par IE et HTML affiché par Chrome

Comme expliqué précédemment, la différence entre les images IE et Chrome résulte du fait que les équations sont enregistrées deux fois, une fois au format WMZ et une fois au format GIF. Le HTML est trop grand pour être affiché ici.

Le code HTML créé par la macro est:

<!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>

Qui s'affiche comme:

HTML créé par macro tel qu'affiché par IE

Je n'ai pas tenté de convertir les équations car le kit de développement logiciel gratuit MathType comprend apparemment des routines qui se convertissent en LaTex

Le code est assez basique donc pas beaucoup de commentaires. Demandez si quelque chose n'est pas clair. Remarque: il s'agit d'une version améliorée du code d'origine.

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
la source
Bon travail. Cela fonctionnera-t-il pour plusieurs fichiers ou devez-vous le placer dans le fichier que vous souhaitez convertir?
mnmnc
@mnmnc. Je vous remercie. Je pense que votre solution est impressionnante bien qu'il soit probablement clair que je ne crois pas qu'une solution qui commence avec Microsoft Html fonctionnera. À la suite d'une question de débordement de pile, je travaille sur la conversion d'Excel en HTML parce que les PublishObjects de Microsoft créent du HTML inacceptable pour la plupart (tous?) Des smartphones. J'ai peu d'expérience avec Word VBA; Je suis meilleur avec Excel et Outlook VBA et j'étais bon avec Acess VBA. Tout cela permet à une macro dans un fichier d'accéder à d'autres fichiers, donc je suis sûr que c'est la même chose pour Word.
Tony Dallimore
0

La façon la plus simple de le faire est simplement les lignes suivantes dans VBA:

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

End Sub

Cela trouvera tout le texte en exposant. Si vous voulez en faire quelque chose, insérez-le simplement dans la méthode. Par exemple, pour trouver le mot "super" dans un exposant et le transformer en "super trouvé", utilisez:

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
la source