Як програмно ітератувати за допомогою підписок, надписів та рівнянь, знайдених у документі Word


12

У мене є декілька документів Word, кожен з яких містить кілька сотень сторінок наукових даних, що включає:

  • Хімічні формули (H2SO4 з усіма належними підписками та надписками)
  • Наукові номери (експоненти відформатовані за допомогою надписів)
  • Багато математичних рівнянь. Написано за допомогою редактора математичних рівнянь у Word.

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

Чи є якийсь спосіб перебрати всі підписи, надписи та рівняння в документі Word за допомогою VBA?


Чи задумувались ви витягти дані XML з документа самостійно? Усі документи Microsoft Office 2007+ (.docx) - це в основному стислі файли xml. Ви можете отримати тих, хто використовує аналізатор xml.
Джеймс Мерц

це було занадто довго, щоб розмістити коментар, тому я додав як відповідь.
Джеймс Мерц

Відповіді:


12

Так, є. Я б сказав, що використовує Powershell, оскільки він обробляє файли Word досить добре. Я думаю, що мені буде найпростіше.

Більше про Powershell проти автоматизації Word тут: http://www.simple-talk.com/dotnet/.net-tools/com-automation-of-office-applications-via-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-файли. Таким чином, у мене є документ doc, в якому у мене є ваш H2SO4 з підписками і після перетворення powerhell вихідний результат наступний:

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

Звичайно, це не ідеально, але трактування є доказом концепції.


3

Ви можете витягнути xml безпосередньо з будь-якого офісного документа, який становить 2007+. Це робиться наступним чином:

  1. перейменуйте файл із .docx у .zip
  2. витягніть файл за допомогою 7zip (або іншої програми вилучення)
  3. Дійсний вміст документа знайдіть у витягнутій папці під папкою wordта document.xmlфайлом. Він повинен містити весь вміст документа.

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

Я створив зразок документа, і в тегах тіла я виявив це (зверніть увагу, я швидко склав це, щоб форматування могло бути трохи відключеним):

<?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 не мали успіху. У минулому я виявив, що створений Office Office HTML настільки насичений, що вибирати потрібні біти майже неможливо. Я виявив, що це так і тут. У мене також були проблеми з рівняннями. Слово зберігає рівняння як зображення. Для кожного рівняння буде два зображення, одне із розширенням 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, очевидно, включає підпрограми, які перетворюються на 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, оскільки PublishObjects Microsoft створює 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

Тут знайдеться весь текст, написаний на надписі. Якщо ви хочете щось з цим зробити, просто вставте його в метод. Наприклад, щоб знайти слово "супер" у суперскрипті та перетворити його на "супер знайдене" використання:

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.