Как программно перебирать подписи, верхние индексы и уравнения, найденные в документе Word

12

У меня есть несколько документов Word, каждый из которых содержит несколько сотен страниц научных данных, которые включают в себя:

  • Химические формулы (H2SO4 со всеми соответствующими индексами и индексами)
  • Научные числа (показатели, отформатированные с использованием верхних индексов)
  • Много математических уравнений. Написано с использованием редактора математических уравнений в Word.

Проблема в том, что хранение этих данных в Word неэффективно для нас. Поэтому мы хотим хранить всю эту информацию в базе данных (MySQL). Мы хотим преобразовать форматирование в LaTex.

Есть ли способ перебрать все индексы, нижние индексы и уравнения в документе Word, используя VBA?

когти
источник
Задумывались ли вы о извлечении данных XML из самого документа? Все Microsoft Documents 2007+ (.docx) в основном представляют собой сжатые XML-файлы. Вы можете получить их, используя анализатор xml.
Джеймс Мерц
это было слишком долго, чтобы оставлять комментарии, поэтому я добавил их в качестве ответа.
Джеймс Мерц

Ответы:

12

Да, есть. Я бы рекомендовал использовать Powershell, так как он довольно хорошо обрабатывает файлы Word. Я думаю, что я буду самым простым способом.

Больше о Powershell против автоматизации Word здесь: http://www.simple-talk.com/dotnet/.net-tools/com-automation-of-office-applications-via-powershell/

Я копнул немного глубже, и я нашел этот скрипт 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();

Сохраните его как .ps1 и начните с:

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

Он сохранит все файлы .doc из указанного каталога в виде файлов html. Итак, у меня есть файл документации, в котором у меня есть H2SO4 с подписками, и после преобразования в PowerShell получается следующее:

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

Как вы можете видеть, у подписчиков есть свои собственные теги в HTML, поэтому остается только проанализировать файл в bash или c ++, чтобы вырезать из тела в / body, изменить на LATEX и впоследствии удалить остальные HTML-теги.

Код от http://blogs.technet.com/b/bshukla/archive/2011/09/27/3347395.aspx


Поэтому я разработал синтаксический анализатор в C ++ для поиска HTML-индекса и замены его на LATEX-индекс.

Код:

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

Для 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>

Выход:

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

Конечно, это не идеал, но угощение - это доказательство концепции.

mnmnc
источник
3

Вы можете извлечь XML непосредственно из любого офисного документа 2007 года. Это делается следующим образом:

  1. переименуйте файл из .docx в .zip
  2. распакуйте файл с помощью 7zip (или другой программы извлечения)
  3. Фактическое содержание документа ищите в извлеченной папке в wordподпапке и в document.xmlфайле. Это должно содержать все содержимое документа.

введите описание изображения здесь

Я создал образец документа, и в тегах body я нашел это (обратите внимание, я быстро собрал это вместе, так что форматирование может быть немного неправильным):

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

Похоже, что <w:t>тег для текста <w:rPr>- это определение шрифта, а <w:p>новый абзац.

Слово эквивалент выглядит так:

введите описание изображения здесь

Джеймс Мерц
источник
2

Я искал подход, отличный от подхода mnmnc.

Мои попытки сохранить тестовый документ Word в формате HTML не увенчались успехом. В прошлом я обнаружил, что HTML, сгенерированный Office, настолько переполнен, что выделять нужные биты практически невозможно. Я обнаружил, что это имеет место здесь. У меня также была проблема с уравнениями. Слово сохраняет уравнения в виде изображений. Для каждого уравнения будет два изображения, одно с расширением WMZ и одно с расширением GIF. Если вы отображаете html-файл с помощью Google Chrome, уравнения выглядят хорошо, но не замечательно; внешний вид соответствует файлу GIF при отображении с помощью инструмента отображения / редактирования изображений, который может обрабатывать прозрачные изображения. Если вы отображаете HTML-файл с помощью Internet Explorer, уравнения выглядят идеально.

Дополнительная информация

Я должен был включить эту информацию в первоначальный ответ.

Я создал небольшой документ Word, который я сохранил как HTML. Три панели на изображении ниже показывают исходный документ Word, HTML-документ, отображаемый в Microsoft Internet Explorer, и HTML-документ, отображаемый в Google Chrome.

Исходное слово, HTML отображается в IE и HTML отображается в Chrome

Как объяснялось ранее, разница между изображениями IE и Chrome является результатом того, что уравнения сохраняются дважды, один раз в формате WMZ и один раз в формате GIF. HTML слишком велик, чтобы показать здесь.

HTML, созданный макросом:

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

Который отображается как:

HTML, созданный макросом, как показано в IE

Я не пытался преобразовать уравнения, так как бесплатный MathType Software Development Kit, очевидно, включает в себя процедуры, которые конвертируют в LaTex

Код довольно простой, поэтому не так много комментариев. Спросите, если что-то неясно. Примечание: это улучшенная версия исходного кода.

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
Тони Даллимор
источник
Отличная работа. Будет ли это работать для нескольких файлов или вам нужно поместить его в файл, который вы хотите конвертировать?
Mnmnc
@mnmnc. Спасибо. Я думаю, что ваше решение впечатляет, хотя, вероятно, ясно, что я не верю, что решение, которое начинается с Microsoft Html, будет работать. В результате вопроса переполнения стека я работаю над преобразованием Excel в Html, потому что Microsoft PublishObjects создает Html, неприемлемый для большинства (всех?) Смартфонов. У меня мало опыта с Word VBA; Я лучше всех работаю с Excel и Outlook VBA, и раньше у меня было хорошо с Acess VBA. Все они позволяют макросу в одном файле обращаться к другим файлам, поэтому я уверен, что то же самое верно для Word.
Тони Даллимор
0

Самый простой способ сделать это - просто следующие строки в VBA:

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

End Sub

Это найдет весь надстрочный текст. Если вы хотите что-то с этим сделать, просто вставьте это в метод. Например, чтобы найти слово «super» в верхнем индексе и превратить его в «super found», используйте:

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
источник