Como iterar programaticamente através de subscritos, sobrescritos e equações encontrados em um documento do Word

12

Eu tenho alguns documentos do Word, cada um contendo algumas centenas de páginas de dados científicos que incluem:

  • Fórmulas químicas (H2SO4 com todos os subscritos adequados e sobrescritos)
  • Números científicos (expoentes formatados usando sobrescritos)
  • Muitas equações matemáticas. Escrito usando editor de equações matemáticas no Word.

O problema é que armazenar esses dados no Word não é eficiente para nós. Então, queremos armazenar todas essas informações em um banco de dados (MySQL). Queremos converter a formatação para o LaTex.

Existe alguma maneira de iterar todos os sub-scripts, sobrescritos e equações dentro de um documento do Word usando o VBA?

    
por claws 19.07.2012 / 18:46

4 respostas

12

Sim, existe. Eu sugiro usar o Powershell como ele lida com arquivos do Word muito bem. Eu acho que serei a maneira mais fácil.

Mais sobre Powershell vs automação Word aqui: "> link

Eu investiguei um pouco mais e encontrei este script de 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();

Salve como .ps1 e inicie com:

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

Ele salvará todo o arquivo .doc do diretório especificado, como os arquivos html. Então eu tenho um arquivo doc no qual eu tenho o seu H2SO4 com subscritos e depois da conversão do PowerShell a saída é a seguinte:

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

Como você pode ver, os subscritos têm suas próprias tags em HTML, então a única coisa que resta é analisar o arquivo em bash ou c ++ para cortar do corpo para / body, alterar o para LATEX e remover o restante das tags HTML posteriormente.

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

Então eu desenvolvi um analisador em C ++ para procurar o código HTML e substituí-lo pelo subscrito do LATEX.

O código:

#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()] = '
<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>
'; } 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; }

Para o arquivo html:

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

A saída é:

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

Não é ideal, é claro, mas tratar é uma prova de conceito.

    
por 25.07.2012 / 19:17
3

Você pode extrair o xml diretamente de qualquer documento do Office 2007+. Isso é feito da seguinte maneira:

  1. renomeie o arquivo de .docx para .zip
  2. extrai o arquivo usando o 7zip (ou algum outro programa de extração)
  3. Para o conteúdo real da aparência do documento, na pasta extraída na subpasta word e no arquivo document.xml . Isso deve conter todo o conteúdo do documento.

Eucrieiumdocumentodeamostra,enastagsbodyeuencontreiisso(notequeeurapidamentecoloqueiissojunto,entãoaformataçãopodeestarumpoucoerrada):

<?xmlversion="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>

Parece que a tag <w:t> é para texto, a <w:rPr> é a definição da fonte e a <w:p> é um novo parágrafo.

A palavra equivalente é assim:

    
por 01.08.2012 / 20:34
2

Eu tenho procurado uma abordagem diferente da que é perseguida pelo mnmnc.

Minhas tentativas de salvar um documento do Word de teste como HTML não foram um sucesso. Eu encontrei no passado que o Office gerou HTML é tão cheio de palha que escolher os bits que você quer é quase impossível. Eu descobri que esse é o caso aqui. Eu também tive um problema com equações. O Word salva equações como imagens. Para cada equação, haverá duas imagens, uma com uma extensão de WMZ e outra com uma extensão de GIF. Se você exibir o arquivo html com o Google Chrome, as equações terão boa aparência, mas não serão maravilhosas; a aparência corresponde ao arquivo GIF quando exibida com uma ferramenta de exibição / edição de imagens que pode manipular imagens transparentes. Se você exibir o arquivo HTML com o Internet Explorer, as equações parecerão perfeitas. O HTML faz referência aos arquivos WMZ, portanto, suponho que o Internet Explorer contenha uma extensão para exibir arquivos WMZ que aparentemente são skins do Windows Media Player, embora o WMP afirme estar corrompido.

Informações adicionais

Eu deveria ter incluído essa informação na resposta original.

Eu criei um pequeno documento do Word que salvei como Html. Os três painéis na imagem abaixo mostram o documento original do Word, o documento HTML exibido pelo Microsoft Internet Explorer e o documento HTML exibido pelo Google Chrome.

Comoexplicadoanteriormente,adiferençaentreasimagensdoIEedoChromeéoresultadodasequaçõesseremsalvasduasvezes,umaveznoformatoWMZeumaveznoformatoGIF.OHtmlémuitograndeparamostraraqui.

OHTMLcriadopelamacroé:

<!DOCTYPEhtmlPUBLIC"-//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>

Qual é exibido como:

Eunãotenteiconverterasequações,poiso MathType Software Development Kit gratuito inclui rotinas que convertem para LaTex

O código é bem básico, portanto, não há muitos comentários. Pergunte se alguma coisa não está clara. Nota: esta é uma versão melhorada do código original.

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
    
por 27.07.2012 / 19:17
0

A maneira mais simples de fazer isso é apenas as seguintes linhas no VBA:

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

End Sub

Isso encontrará todo o texto sobrescrito. Se você quiser fazer algo com isso, basta inseri-lo no método. Por exemplo, para encontrar a palavra "super" em um sobrescrito e transformá-la em uso "super encontrado":

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
    
por 02.08.2012 / 04:18