Jump to content

Trying to get columns in a row for a Word table [Solved]


Recommended Posts

I'm trying to get the number of columns in a specific row in a Word table and am stuck.   I need a push.  Program below and Word file attached.

Thanks.

#AutoIt3Wrapper_run_debug_mode=Y    ;use this to debug in console window
#include <Word.au3>

$oWord = _Word_Create(True, True)   ;Create Word application object, make it visible, and force a new instance of Word
$oDoc = _Word_DocOpen($oWord, @ScriptDir&"\ColumnTest.docx", Default, Default, True)    ;Open the Word document

$iTablesCount = $oDoc.Tables.Count      ;get Tables count in $oDoc
Pause("$iTablesCount = '" & $iTablesCount & "'")

$iRowCount = $oDoc.Tables.Item(1).Rows.Count    ;Table hard coded
$iColCount = $oDoc.Tables.Item(1).Columns.Count
Pause("Table#1 $iRowCount = '" & $iRowCount & "  $iColCount = '" & $iColCount & "'")

;trying to get the number of columns in each row
;$ColCountInRow = $oDoc.Tables.Item(1).Rows(1).Columns.Count    ;this fails and read somewhere to use Cells.Count
$ColCountInRow = $oDoc.Tables.Item(1).Rows(1).Cells.Count   ;hard code Row 1    <<<<< ERROR here
;this is the error I get
;: ==> The requested action with this object has failed.:
;$ColCountInRow = $oDoc.Tables.Item(1).Rows(1).Cells.Count
;$ColCountInRow = $oDoc.Tables.Item(1)^ ERROR

Pause("Row 1 has " & $ColCountInRow & " Columns")

Exit


Func    Pause($text="")
    MsgBox(262144, "DEBUG", "Paused: " & $text)
EndFunc

 

ColumnTest.docx

Edited by ahha
solved
Link to comment
Share on other sites

This works.

#AutoIt3Wrapper_run_debug_mode=Y    ;use this to debug in console window
#include <Word.au3>

$oWord = _Word_Create(True, True)   ;Create Word application object, make it visible, and force a new instance of Word
$oDoc = _Word_DocOpen($oWord, @ScriptDir&"\ColumnTest.docx", Default, Default, True)    ;Open the Word document

$iTablesCount = $oDoc.Tables.Count      ;get Tables count in $oDoc
Pause("$iTablesCount = '" & $iTablesCount & "'")

$iRowCount = $oDoc.Tables.Item(1).Rows.Count    ;Table hard coded
$iColCount = $oDoc.Tables.Item(1).Columns.Count
Pause("Table#1 $iRowCount = '" & $iRowCount & "  $iColCount = '" & $iColCount & "'")

$vTable = $oDoc.Tables.Item(1)

; Temporarly replace tabs and paragraphs in the table
Local $asSeparators[2][2] = [[@TAB, "   "], [@CR, "|"]], $iTableRows, $iTableColumns, $iUndo = 1, $bFound = False
$vTable.Range.Find.ClearFormatting
If @error Then Exit
$bFound = $vTable.Range.Find.Execute($asSeparators[0][0], False, False, False, False, False, True, $WdFindStop, False, $asSeparators[0][1], $WdReplaceAll)
If $bFound Then $iUndo = $iUndo + 1
$bFound = $vTable.Range.Find.Execute($asSeparators[1][0], False, False, False, False, False, True, $WdFindStop, False, $asSeparators[1][1], $WdReplaceAll)
If $bFound Then $iUndo = $iUndo + 1
$iTableRows = $vTable.Rows.Count()
$iTableColumns = $vTable.Columns.Count()
Local $asLines, $asColumns
Local $oRange = $vTable.ConvertToText(@TAB, False)
If @error Then Exit
Local $sData = $oRange.Text
$oDoc.Undo($iUndo)
$asLines = StringSplit($sData, @CR, $STR_NOCOUNT)
For $i = 0 To $iTableRows - 1
    $asColumns = StringSplit($asLines[$i], @TAB)
    Pause("Row " & ($i + 1) & " has " & (UBound($asColumns) - 1) & " Columns")
Next

;trying to get the number of columns in each row
;$ColCountInRow = $oDoc.Tables.Item(1).Rows(1).Columns.Count    ;this fails and read somewhere to use Cells.Count
;$ColCountInRow = $oDoc.Tables.Item(1).Rows(1).Cells.Count   ;hard code Row 1    <<<<< ERROR here
;this is the error I get
;: ==> The requested action with this object has failed.:
;$ColCountInRow = $oDoc.Tables.Item(1).Rows(1).Cells.Count
;$ColCountInRow = $oDoc.Tables.Item(1)^ ERROR

;Pause("Row 1 has " & $ColCountInRow & " Columns")

Exit


Func    Pause($text="")
    MsgBox(262144, "DEBUG", "Paused: " & $text)
EndFunc

Most of the code was extracted from _Word_DocTableRead.

Link to comment
Share on other sites

JockoDundee,

Thanks.  Based on your explanation it makes sense.  Apparently the object can't do a row by row count of the columns.  I assume the same would happen with horizontally merged cells or both vertically and horizontally merged cells.  (Duh - any merged cells.)  I just wish it would come back with an error so I could process it.

I opened a Debug Window and this appears when I request info on the 2nd column in the first row (that column does not exist).

2021/05/28 05:29:23 COM Error encountered in ColumnTest v1b_DebugIt.au3 (71) :
    Number            = 0x80020009 (-2147352567)
    WinDescription    = Exception occurred.
    Description       = The requested member of the collection does not exist.
    Source            = Microsoft Word
    HelpFile          = C:\Program Files (x86)\Microsoft Office\Office12\1033\WDMAIN11.CHM
    HelpContext       = 25421
    LastDllError      = 0
    Retcode           = 0x800A1735

So it's throwing an exception and I'm not comfortable writing a routine that intercepts an exception to do processing (plus it seems in bad form).

Water's _Word_DocTableRead is a very clever way of getting the information that bypasses a lot of issues.  I'll use it and extract what I need as I can't have a program throwing exceptions.

Thanks everyone, mystery solved.

Link to comment
Share on other sites

  • ahha changed the title to Trying to get columns in a row for a Word table [Solved]
On 5/28/2021 at 7:47 AM, ahha said:

Apparently the object can't do a row by row count of the columns.

Of course you can get a count of columns cells by row.  :) 

The example script below, using the attached sample doc with a 4 col 3 row table (with some merged column cells), creates the following output.  As you can see, it dynamically processes the rows & cols of the table as well as displays the number of columns in each row.  It also displays the values in each cell with the width of each column.  I added the width of each column in case you wanted a quick way to see if the column was merged.

#AutoIt3Wrapper_AU3Check_Parameters=-w 3 -w 4 -w- 5 -w 6 -d

#include <Constants.au3>
#include <Word.au3>


example()

Func example()
    Local $oComErr, $oWord, $oRange
    Local $iCellCount

    ;Define local COM error handler
    $oComErr = ObjEvent("AutoIt.Error", "com_error_handler")
    If @error Then Exit MsgBox($MB_ICONERROR + $MB_TOPMOST, "ERROR", "Unable to create COM error handler - @error = " & @error)

    ;Create a Word app object
    $oWord = _Word_Create(True, True)
    If @error Then Exit ConsoleWrite("Unable to create Word object.  @error = " & @error & @CRLF)

    ;Open doc
    _Word_DocOpen($oWord, "word-merged-cell-table-doc.docx", Default, Default, True)
    If @error Then Exit ConsoleWrite("Unable to open Word doc.  @error = " & @error & @CRLF)

    ;Display table information
    With $oWord.ActiveDocument.Tables(1)
        ConsoleWrite("Table columns    = " & .Columns.Count & @CRLF)
        ConsoleWrite("Table rows       = " & .Rows.Count & @CRLF)

        ;Display number of cells in each row
        ConsoleWrite(@CRLF)
        For $iRow = 1 To .Rows.Count
            $iCellCount = .Rows($iRow).Cells.Count
            If @error Then
                ConsoleWrite("DISPLAY CELL COUNT ERROR: " & $oComErr.Description & @CRLF)
                MsgBox($MB_ICONERROR + $MB_TOPMOST, "DISPLAY CELL COUNT ERROR", $oComErr.Description)
                _Word_Quit($oWord)
                Return
            EndIf

            ConsoleWrite(StringFormat("Row %i cell count = %i", $iRow, .Rows($iRow).Cells.Count) & @CRLF)
        Next

        ;Display row values
        ConsoleWrite(@CRLF & "Table values with (col width)" & @CRLF)
        For $iRow = 1 To .Rows.Count
            For $iCol= 1 To .Rows($iRow).Cells.Count
                ;Get cell range and strip "end of cell" marker
                $oRange     = .Cell($iRow, $iCol).Range
                $oRange.End = $oRange.End - 1

                ;Ouput cell value with col widths so you know if it is a merged column
                ConsoleWrite(StringFormat("%s (%s)", $oRange.Text, Round(.Cell($iRow, $iCol).Width)))
                If $iCol < .Rows($iRow).Cells.Count Then ConsoleWrite(@TAB & @TAB)
            Next
            ConsoleWrite(@CRLF)
        Next
    EndWith

    ;Close doc and app
    _Word_Quit($oWord)
EndFunc

Func com_error_handler($oError)
    Return ;Return so @error can be trapped by the calling function
EndFunc

Console

Table columns    = 4
Table rows       = 3

Row 1 cell count = 4
Row 2 cell count = 3
Row 3 cell count = 3

Table values with (col width)
1 (117)     2 (117)     3 (117)     4 (117)
1 (234)     3 (117)     4 (117)
1 (117)     2 (234)     4 (117)

 

Edit:

I just noticed that merged rows may be a factor too.  Of course the script above does not take into account merged rows...yet.  ;)

Edit:

Without looking at _Word_DocTableRead() first, I came up with a solution that was very similar to that function that also used .ConvertToText.
So it's really not worth posting it.  :doh: 

word-merged-cell-table-doc.docx

Edited by TheXman
Added edit about merged rows
Link to comment
Share on other sites

TheXman,

Thanks.

Was not aware of StringFormat, nice.
I'm definitely going to have to start using the With...EndWith construct.

Okay now I'm confused and here's why.
In my original post I used this, and it's where the COM exception/error occurred:

$ColCountInRow = $oDoc.Tables.Item(1).Rows(1).Cells.Count   ;hard code Row 1    <<<<< ERROR here

You use With:

For $iCol= 1 To .Rows($iRow).Cells.Count


and no error occurred. 

What's the difference as we're both using the .Rows($x).Cells.Count feature?

Link to comment
Share on other sites

11 minutes ago, ahha said:

What's the difference as we're both using the .Rows($x).Cells.Count feature?

The difference is most likely the documents.  My document only has some merged columns.  The document in your first post has merged columns and merged rows.  So you most likely got an uncaught exception error because the statement couldn't handle a collection with mixed column widths.  If you had a COM error handler, it could've showed you the exact reason.  Not knowing for sure, I'm only speculating.

I updated my post saying that my example script does not handle tables with merged rows, like the attached document in your first post.

Edited by TheXman
Link to comment
Share on other sites

Hmm - that may be it.  I'll continue testing.

The error thrown as noted above was "Description       = The requested member of the collection does not exist."

Also JockoDundee noted above the error "Cannot access individual rows in this collection because the table has vertically merged cells."

This is what started me down the catch the COM Exception path for trying to get the number of columns in a row for a Word table.

Link to comment
Share on other sites

Actually, you may have received the following error:

Return code:  0x800A1767 (-2146822297)

"Cannot access individual rows in this collection because the table has vertically merged cells."

I'm not sure,  A COM error handler will let you know for sure.

Edited by TheXman
Link to comment
Share on other sites

The difference may be that I used

_DebugSetup("Word Debug Window", True, 1, "", True)
_DebugCOMError(1) ; Register a default COM error handler to grab Word COM errors and write the messages to the Debug window

Before I understood to use the MyErrFunc

Msgbox(0,"AutoItCOM Test","We intercepted a COM Error !"    & @CRLF  & @CRLF & _
             "err.description is: " & @TAB & $oMyError.description  & @CRLF & _
             "err.windescription:"   & @TAB & $oMyError.windescription & @CRLF & _
             "err.number is: "       & @TAB & hex($oMyError.number,8)  & @CRLF & _
             "err.lastdllerror is: "   & @TAB & $oMyError.lastdllerror   & @CRLF & _
             "err.scriptline is: "   & @TAB & $oMyError.scriptline   & @CRLF & _
             "err.source is: "       & @TAB & $oMyError.source       & @CRLF & _
             "err.helpfile is: "       & @TAB & $oMyError.helpfile     & @CRLF & _
             "err.helpcontext is: " & @TAB & $oMyError.helpcontext _
            )

 

Link to comment
Share on other sites

TheXman,

Suspicion on vertically merged rows confirmed.

I modified your program very slightly to show when we hit a COM error and attached Word file show it hitting the COM error.

;from https://www.autoitscript.com/forum/topic/205953-trying-to-get-columns-in-a-row-for-a-word-table-solved/
;#AutoIt3Wrapper_run_debug_mode=Y   ;use this to debug in console window <--- LOOK
#AutoIt3Wrapper_AU3Check_Parameters=-w 3 -w 4 -w- 5 -w 6 -d

#include <Constants.au3>
#include <Word.au3>


example()

Func example()
    Local $oComErr, $oWord, $oRange

    ;Define local COM error handler
    $oComErr = ObjEvent("AutoIt.Error", "com_error_handler")
    If @error Then Exit MsgBox($MB_ICONERROR + $MB_TOPMOST, "ERROR", "Unable to create COM error handler - @error = " & @error)

    ;Create a Word app object
    $oWord = _Word_Create()
    If @error Then Exit ConsoleWrite("Unable to create Word object.  @error = " & @error & @CRLF)

    ;Open doc
    _Word_DocOpen($oWord, "word-merged-cell-table-docv3Z.docx", Default, Default, True)
    If @error Then Exit ConsoleWrite("Unable to open Word doc.  @error = " & @error & @CRLF)

    ;Display table information
    With $oWord.ActiveDocument.Tables(1)
        ConsoleWrite("Table columns    = " & .Columns.Count & @CRLF)
        ConsoleWrite("Table rows       = " & .Rows.Count & @CRLF)

        ;Display number of cells in each row
        ConsoleWrite(@CRLF)
        For $i = 1 To .Rows.Count
            ConsoleWrite(StringFormat("Row %i cell count = %i", $i, .Rows($i).Cells.Count) & @CRLF)
        Next

        ;Display row values
        ConsoleWrite(@CRLF & "Table values with (col width)" & @CRLF)
        For $iRow = 1 To .Rows.Count
            For $iCol= 1 To .Rows($iRow).Cells.Count
                ;Get cell range and strip "end of cell" marker
                $oRange     = .Cell($iRow, $iCol).Range
                $oRange.End = $oRange.End - 1

                ;Ouput cell value with col widths so you know if it is a merged column
                ConsoleWrite(StringFormat("%s (%s)", $oRange.Text, Round(.Cell($iRow, $iCol).Width)))
                If $iCol < .Rows($iRow).Cells.Count Then ConsoleWrite(@TAB & @TAB)
            Next
            ConsoleWrite(@CRLF)
        Next
    EndWith

    ;Close doc and app
    _Word_Quit($oWord)
EndFunc

Func com_error_handler($oError)
    ConsoleWrite("-Hit a COM error"&@CRLF)  ;show in Orange
    Return ;Return so @error can be trapped by the calling function
EndFunc

So it appears that there is no clean way but to use COM Exceptions to figure out the columns in a Word Table with merged cells 😒

word-merged-cell-table-docv3Z.docx

Link to comment
Share on other sites

I made a slight modification to my example script above.  I added a check for errors on the first statement that queries row info.  If I execute the modified script using the doc in your first post, I get the following console messages and an error:

Table columns    = 3
Table rows       = 4

DISPLAY CELL COUNT ERROR: Cannot access individual rows in this collection because the table has vertically merged cells.

That error is generated by the line:

.Rows($iRow).Cells.Count

This is the same error alluded to by @JockoDundee in his post above.  The difference between the error message I received and the error message you received is probably due to where the error is caught.  Since I'm using my own local COM error handler as opposed to the one in the UDF, I am catching it immediately.  I haven't looked in the UDF to see how or when it is catching the error.

Link to comment
Share on other sites

 

30 minutes ago, ahha said:

Suspicion on vertically merged rows confirmed.

I modified your program very slightly to show when we hit a COM error and attached Word file show it hitting the COM error.

:) Yes, we came to the same conclusion in regards to the error message. 

I must have been replying when you posted your last message.

I guess I don't fully understand what your goal is because the _Word_DocTableRead() function appears to work well with tables that have merged columns and/or merged rows.

Edited by TheXman
Link to comment
Share on other sites

1 hour ago, ahha said:

So it appears that there is no clean way but to use COM Exceptions to figure out the columns in a Word Table with merged cells 😒

There's almost always a way.  However, doing it by catching specific exceptions is not so bad. 

I haven't given it much though but I would probably start by copying the table from Word and temporarily importing the table into Excel, then it would be much easier to work with because Excel has a property that will tell you if a range contains a merged cell.  It also has other methods and properties that may be useful in this case.

Edited by TheXman
Link to comment
Share on other sites

TheXman,

re: However, doing it by catching specific exceptions is not so bad.

Thanks for the comment because not being that familiar with COM handling and its implications makes me very wary.

Excel is certainly a possibility although I'd really like to avoid using it as it requires that any user of my code also have Excel.

As for my goal from my other post (205972-using-seterror-with-a-com-handler) I want to be able to get the cell contents, merged or not and I'm hoping to be able to read the CRLF in a cell as most merged cells have several lines of text.

So the general idea is to have two functions: (1) ReadWordTableToArray and (2) WriteArrayToWordTable with the ASSUMPTION the WordTable is the same (i.e Read Modify Write) with the functions handling anything in the Word table. 

I'm working on it now and hope to have it within a day.  My challenge at the moment is putting multiple lines with CRs LFs into an $aArray cell in AutoIt.  It looks like I'm going to have to encode/decode the strings as $aArray in AutoIt likes to strip out the CR LF.  (Water's excellent UDF strips CRLF.  In the Remarks "The array can not contain @CR, @CRLF or @LF in any cell.")

Back to programming quantum sufficit

 

 

Link to comment
Share on other sites

Okay here's my program for reading in a Word Table with or without merged cells, placing it in an AutoIt Array, and writing the AutoIt Array back to the Word Table with or without merged cells.  It takes a read/modify/write approach.
It also handles correctly tables having CR LF so multi-lines in Word Table cells are handled.
The first part of the program runs on a Word test file "Test.docx" that should be placed in the @ScriptDir - The program shows the tables and then for Table 4 rewrites the lower right cell with "End of Record: 2313-032" with modified entries to show it works.

There are way too many commented out lines, but they're for showing the evolution of the program, and to remind me what the heck I did :).
I discuss Word Table Cell structure and the AutoIt Array structure format.  Note that both are 1-based so that Rows and Columns both start at 1.  RowCount and ColCount are in [0][0] and [0][1] respectively.

In order to handle Word Tables with merged cells you'll see a lot of exception handling as that was the only way I (and others - thanks) could figure out how to do it.
In order to handle the CR LF issue in the AutoIt Arrays they are escaped with a YenYen notation so for example a CR is escaped as ¥¥CR¥¥ so no need to panic when you view the _DebugArrayDisplay.

Please ignore some of the comments to "IDIOT PROGRAMMER" that's me not anyone else (generally spending 4+ hours trying to track down a bone-headed error :)
The only item not completed is "Func CellStripCount()" as I need to determine automatically how many characters to strip off the end of a Word Table cell.  Apparently it varies as 1 or 2.  In my version of Word it's 2.  If you spot the table entries missing the last character please set "$iCellStripCount = 1" in Func CellStripCount().  When I can find a version of Word that has a single table cell marker I'll test it.  If you come across it, please let me know.

There probably (almost always) is a better way to do it, so I welcome any and all comments, suggestions, and even - what the heck did you do?

 

;
#AutoIt3Wrapper_run_debug_mode=Y    ;use this to debug in console window <--- LOOK

#include <Array.au3>
#include <MsgBoxConstants.au3>
#include <Word.au3>
#include <Debug.au3>
#include <String.au3>

;v1d - just check @error rather than trying to use SetError
;       see https://www.autoitscript.com/forum/topic/205972-using-seterror-with-a-com-handler/
;       One cannot set/change the @error and @extended values when returning from a COM error handler.
;v1e,f,g - tightened up some code
;v1g - got rid of extraneous code, pushed some Global variables into functions (making them Local)
;v1h - chased down stupid error in scope
;       ;~ Global $oTableError  ;NOTE KEY - conflicted with Local definition below ************************** LOOK IDIOT PROGRAMMER !!!!!
;v1i - added array $aTableIndex for array-in-array structure.  Remember MUST unpact to access individual 2D Tables
;v1j - trying to clean up before submission

;;NOTE KEY - conflicted with Local definition below ************************** LOOK IDIOT PROGRAMMER !!!!!
;Global $oTableError = ObjEvent("AutoIt.Error","LocalErrFunc")    ;initialize a Global COM error handler

;Global $aTemp[1][1] = [[""]]   ;1x1 zero based and nulled initially
;Global $aArray
;NOTE KEY - conflicted with Local definition below ************************** LOOK IDIOT PROGRAMMER !!!!!
; -->>> ERROR Global $oTableError
Global $sCellText   ;so exception handling can set it

Global $iCellStripCount = CellStripCount()  ;$iCellStripCount = 2   ;to strip end of cell characters.  Some systems are 1, some are 2.
Pause("$iCellStripCount = '" & $iCellStripCount & "'")

;Global $SUB = Chr(26)  ;0x1A - substitute character was not a good choice as it's a rectangle outline
Global $YEN = Chr(0xA5) ;substitute/ESCAPE character
;use 2 for CRLF as unlikely that text will have 2 Yens in a row ¥¥ ¥¥ (Alt+157 or Alt+0165)
Global $YENYEN = $YEN&$YEN

Global $QUOTE = Chr(34)     ;0x22
Global $SQUOTE = "'"        ;single quote
Global $CR = Chr(0x0d)
Global $LF = Chr(0x0a)
Global $CRLF = $CR & $LF
Global $LFCR = $LF & $CR
Global $COMMA = Chr(0x2c)
Global $TAB = Chr(0x09)

Global $iExceptionCount = 0     ;debugging

;from https://www.autoitscript.com/forum/topic/134615-the-requested-action-with-this-object-has-failed/
;~ _DebugSetup("Word Debug Window", True, 1, "", True)
;~ _DebugCOMError(1) ; Register a default COM error handler to grab Word COM errors and write the messages to the Debug window

$sTestDoc = @ScriptDir&"\Test.docx"
;$sTestDoc = @ScriptDir&"\Test 2021-05-29 tues.doc"     ;this doc has some huge cell entries - seems to work okay

;Create application object
Local $oWord = _Word_Create()
If @error Then Exit MsgBox($MB_SYSTEMMODAL, "Word UDF: _Word_DocTableRead Example", "Error creating a new Word application object." & @CRLF & "@error = " & @error & ", @extended = " & @extended)

;Open the test document
Local $oDoc = _Word_DocOpen($oWord, $sTestDoc, Default, Default, True)
If @error Then Exit MsgBox($MB_SYSTEMMODAL, "Word UDF: _Word_DocTableRead Example", "Error opening '" & $sTestDoc & "'" & @CRLF & "@error = " & @error & ", @extended = " & @extended)

;cycle through the Word Tables in $oDoc storing then showing the resulting Arrays
$iTablesCount = $oDoc.Tables.Count      ;get Tables count in $oDoc
Global $aTablesIndex[$iTablesCount + 1] ;keep Tables 1 based
$aTablesIndex[0] = $iTablesCount        ;[0] = Table Count
;Pause("$iTablesCount = '"&$iTablesCount&"'")

For $i = 1 to $iTablesCount
    $aArray = 0     ;clear it out
    $aArray = ahha_Word_Table_to_Array($i)
    ;_DebugArrayDisplay($aArray, "ahha_Table_to_Array  $iTableNumber = " & $i)
    $aTablesIndex[$i] = $aArray     ;store it
Next
Pause("Tables stored = " & $iTablesCount)

For $i = 1 to $iTablesCount
    _DebugArrayDisplay($aTablesIndex[$i], "$iTableNumber = " & $i)
Next

;for Test.docx Table 4 is interesting because "End of Record: 2313-032" shows up in col 2 not col 3
;so let's test the ahha_Array_to_Word_Table on that entry

;direct write
;$oDoc.Tables.Item($iTableNumber).Cell($iRow, $iCol).Range.Text = $sNewCellText ;Write into a specific Table Cell
Pause("$oDoc.Tables.Item(4).Cell(6, 2).Range.Text = '" & $oDoc.Tables.Item(4).Cell(6, 2).Range.Text & "'")
$oDoc.Tables.Item(4).Cell(6, 2).Range.Text = "END OF RECORD WAS HERE" ;Write into a specific Table Cell
;sure enough it's there

;now test ahha_Array_to_Word_Table
$aTemp = $aTablesIndex[4]
$aTemp[6][2] = "EOR is being replaced"&$YENYEN&"CR"&$YENYEN&"with this MARKER"  ;explicit escapping
$aTablesIndex[4] = $aTemp   ;need to assign back to $aTablesIndex otherwise it's not accessible
ahha_Array_to_Word_Table(4)     ;write Table 4

;rather than explicit escaping - use an Encode function
$aTemp = $aTablesIndex[4]
;Pause("Encoded = '" & Encode("EOR is being re-replaced" &@CR& "with this MARKER and" &@CRLF &"a third line.") & "'")
$aTemp[6][2] = Encode("EOR is being re-replaced" &@CR& "with this MARKER and" &@CRLF &"a third line.")  ;normal entry
$aTablesIndex[4] = $aTemp   ;need to assign back to $aTablesIndex otherwise it's not accessible
ahha_Array_to_Word_Table(4)     ;write Table 4

;~ ;check that all text is in a cell
;~ $aTemp = $aTablesIndex[2]    ;get Table2
;~ $sT2R4C4 = $aTemp[4][4]
;~ Pause("$sT2R4C4 = '" & $sT2R4C4 & "'")

Pause("$iExceptionCount = '" & $iExceptionCount & "'")

Exit


;---------------- Functions ;----------------

Func CellStripCount()   ;NOT FINISHED

    ;not finished need to create a Word doc with a table write to the table something like "123" then read it and figure out
    ;how many characters to strip to just have "123" left
    ;holdup is that I don't have a version of Word that has only 1 character needing to be removed.  Need to test it on Word that has 1 char.

    ;see if we can determine if cell mark is 1 or 2 characters, Unicode character 00A4 (¤)
    ;http://wordfaqs.ssbarnhill.com/NonprintChars.htm#CellMarkers
    ;http://infobitt.blogspot.com/2010/11/working-with-tables.html
    ;https://social.msdn.microsoft.com/Forums/office/en-US/3a681fe8-6f72-418e-ab90-2ee714e17e9b/vba-in-word-table-showing-mystery-character

    ;manually being set for my version of Word 2007, supposedly some versions only have a single char
    ;see https://social.msdn.microsoft.com/Forums/office/en-US/3a681fe8-6f72-418e-ab90-2ee714e17e9b/vba-in-word-table-showing-mystery-character
    ;ANSI 7 and  ANSI 13 ? 7=Bel and 13=CR
    $iCellStripCount = 2    ;trim 2 char cell marker

    Return($iCellStripCount)    ;and set it as a Global so other Functions can see it.

EndFunc     ;CellStripCount()   ;NOT FINISHED


Func Encode($sCellText = "")

    ;encode all possible combinations
    $sNewCellText = StringReplace($sCellText, $CRLF, $YENYEN&"CRLF"&$YENYEN)    ;encode any CRLF
    $sNewCellText = StringReplace($sNewCellText, $LFCR, $YENYEN&"LFCR"&$YENYEN) ;encode any LFCR
    $sNewCellText = StringReplace($sNewCellText, $CR, $YENYEN&"CR"&$YENYEN) ;encode any CR
    $sNewCellText = StringReplace($sNewCellText, $LF, $YENYEN&"LF"&$YENYEN) ;encode any LF
    ;Pause("$sNewCellText = '" & $sNewCellText & "'")
    Return($sNewCellText)

EndFunc     ;Func Encode($sCellText = "")


Func ahha_Word_Table_to_Array($iTableNumber = 1)    ;input = TableNumber    ;output = $aArray
    Local $oTableError = ObjEvent("AutoIt.Error","LocalErrFunc")    ;initialize a Local COM error handler
    ;NOTE needs to be Global so that LocalErrFunc can access $oTableError
    ;HOWEVER if we just Return in LocalErrFunc then it can be Local

    $ldebug = 0     ;local debug flag, 0 = off, 1 = on, 2+... deeper

    Local $aTemp[1][1] = [[""]]     ;1x1 zero based and nulled initially

    ;Row and Col count are outermost values (i.e. assuming there are no merged cells)
    $iRowCount = $oDoc.Tables.Item($iTableNumber).Rows.Count
    $iColCount = $oDoc.Tables.Item($iTableNumber).Columns.Count
    If $ldebug > 0 Then Pause("Table#"&$iTableNumber& " $iRowCount = '" & $iRowCount & "  $iColCount = '" & $iColCount & "'")

    ;Now march through grabbing Table cell contents (CR & LF allowed) and place in array
    ;(Note we need to be able to read/write multiline cells i.e. with CRLF so we are using the $YENYEN substitute character for encoding/decoding this)
    ;construct is as if there were no merged cells but array for merged cells will have identifying entries for merged cells that should NOT be written as it will cause an error
    ;for example this is the 3x4 Table with cells 1,2 and 2,2 merged
    ; +------+------+------+------+
    ; |  1,1 |  1,2 |  1,3 |  1,4 |
    ; +------+      +------+------+
    ; |  2,1 |      |  2,3 |  2,4 |
    ; +------+------+------+------+
    ; |  3,1 |  3,2 |  3,3 |  3,4 |
    ; +------+------+------+------+
    ;
    ;this is the Array construct where, cells 1,2 and 2,2 are merged
    ; +------+------+------+------+
    ; |  1,1 |  1,2 |  1,3 |  1,4 |
    ; +------+------+------+------+
    ; |  2,1 |  *   |  2,3 |  2,4 |  * = note "¥¥[MERGED INTO ABOVE OR LEFT]¥¥"
    ; +------+------+------+------+
    ; |  3,1 |  3,2 |  3,3 |  3,4 |
    ; +------+------+------+------+
    ;
    ;this is the Array construct where, Table cells 1,2 and 2,2 are merged, and Teble cells 3,3 and 3,4 are merged
    ; +------+------+------+------+
    ; |  1,1 |  1,2 |  1,3 |  1,4 |
    ; +------+------+------+------+
    ; |  2,1 |  *   |  2,3 |  2,4 |  * = note "¥¥[MERGED INTO ABOVE OR LEFT]¥¥"     ;in this case merged with above
    ; +------+------+------+------+
    ; |  3,1 |  3,2 |  3,3 |  *   |  * = note "¥¥[MERGED INTO ABOVE OR LEFT]¥¥";    in this case merged with left
    ; +------+------+------+------+

    ;If Table cell 3,1 has this multiline comment (minus the semicolon):
    ;Well@CR
    ;how about@CR
    ;that.

    ;Then this is what the Array at Row 3 and Col 1 will have:
    ;Well¥¥CR¥¥how about¥¥CR¥¥that.

    ;(1) The  substitute (or escape) character $YENYEN is an interesting choice/compromise because we use _DebugArrayDisplay to display the array
    ;and we'd like see the delimiter and yet many characters map to a same little black rectangle or black retangular box as does
    ;the end-of-a-cell marker in the Word table so we could not distinguish it from the escape character.
    ;(2) Additionally we have the constraint that not all characters can be store in an array ($aArray) such as CR LF, etc.
    ;Initially I tried the Substitute character (0x1a) but it did not display distinguishingly.
    ;(3) And finally as an escape character or sequence we'd like it not to occur in normal text.
    ;In any event if you need to use a different character just re-equate $YENYEN

    ;code to look at char one at a time
    ;For $i = 0 to 255
    ;   Pause("Character " & $i & " = '" & Chr($i) & "'")
    ;Next

    ;actual Array structure is like this: 1 based

    ;now set up array to hold it and reserve Row 0 AND Col 0, at [0][0] = $iRowCount, and at [0][1] = $iColCount    ;ASSUMPTION at least 2 columns
    ;ASSUMPTION not needed now since we have Col 0 reserved.  Now can address array starting from Row 1 and Col 1
    ;Example array
    ;       Col0            Col1            Col2
    ;Row0   $iRowCount      $iColCount      empty ... for rest of Row0
    ;Row1   empty           R1C1 data                                   ;<<<< can handle a single cell table easily
    ;Row2   empty
    ;       .
    ;       .
    ;       .
    ;       empty
    ;       for
    ;       rest
    ;       of
    ;       Col0

    ;set up array - REMEMBER we can copy arrays (e.g. $aArray1 = $aArray2) and erase them easily (e.g. $aArray = 0)

    $aTemp = 0  ;erase array
    Local $aTemp[$iRowCount + 1][$iColCount + 1]    ;+1 because going from 0 based to 1 based array, declared Global outside function
    $aTemp[0][0] = $iRowCount   ;put in $iRowCount
    $aTemp[0][1] = $iColCount   ;put in $iColCount

    For $iRow = 1 to $iRowCount     ;do row at a time
        For $iCol = 1 to $iColCount ;by column
            ;Read a specific Table Cell (can include CR LF)
            $sCellText = $oDoc.Tables.Item($iTableNumber).Cell($iRow, $iCol).Range.Text ;<<<< when this generates an EXCEPTION, it will just continue below
            $iError = @error ;grab @error as we are getting @error = -2147221005 rather than 23.  It makes no difference still get -2147221005
            If $ldebug > 1 Then Pause("$iRow = "&$iRow& " $iCol = "&$iCol&@CRLF& "$sCellText = '" & $sCellText & "'")
            If $iError <> 0 Then    ;we threw an exception
                ;If $ldebug > 0 Then    ;debugging
;Pause("$iExceptionCount = '" & $iExceptionCount & "'")

                    $sMsg = "AutoIt COM Exception/Error" & @CRLF  & @CRLF & _
                     "err.description is: "     & @TAB & "'" &  $oTableError.description    & "'"  & @CRLF & _
                     "err.windescription:"      & @TAB & "'" &  $oTableError.windescription & "'"  & @CRLF & _
                     "err.number is: "          & @TAB & "'" &  hex($oTableError.number,8)  & "'"  & @CRLF & _
                     "err.lastdllerror is: "    & @TAB & "'" &  $oTableError.lastdllerror   & "'"  & @CRLF & _
                     "err.scriptline is: "      & @TAB & "'" &  $oTableError.scriptline     & "'"  & @CRLF & _
                     "err.source is: "          & @TAB & "'" &  $oTableError.source         & "'"  & @CRLF & _
                     "err.helpfile is: "        & @TAB & "'" &  $oTableError.helpfile       & "'"  & @CRLF & _
                     "err.helpcontext is: "     & @TAB & "'" &  $oTableError.helpcontext    & "'"
;                   Pause($sMsg)
                ;EndIf

                ;check it
                If $oTableError.description <> "The requested member of the collection does not exist." Then
                    Pause("Unknown error occurred.  Error description = '" & $oTableError.description & "'")
                    ;continue anyway
                EndIf

                $sPadding = _StringRepeat("X", $iCellStripCount)
                $sCellText = $YENYEN &"[MERGED INTO ABOVE OR LEFT]"& $YENYEN & $sPadding    ;set up a marker pre-add $iCellStripCount chars for stripping below, "[EMPTY]"
            EndIf
            ;removing the end-of-cell mark see http://infobitt.blogspot.com/2010/11/working-with-tables.html
            ;https://social.msdn.microsoft.com/Forums/office/en-US/3a681fe8-6f72-418e-ab90-2ee714e17e9b/vba-in-word-table-showing-mystery-character
            $sCellText = StringTrimRight($sCellText, $iCellStripCount)  ;ANSI 7 and ANSI 13 (CR) in most versions of Word <<<< may need to chack for this and set $iCellStripCount to 1 or 2
            If $ldebug > 1 Then Pause("$sCellText = '" & $sCellText & "'")
            If $ldebug > 1 Then Pause("After possible exception: $iRow = " & $iRow &" $iCol = " & $iCol)

            If $ldebug > 1 Then
                For $i = 1 to StringLen($sCellText)     ;for char by char debugging
                    $OneChar = StringMid($sCellText, $i, 1)
                    Pause("Character " & $i & " = '" & $OneChar & "' = Asc " & Asc($OneChar))
                Next
            EndIf

            ;encode all possible combinations
            $sNewCellText = StringReplace($sCellText, $CRLF, $YENYEN&"CRLF"&$YENYEN)    ;encode any CRLF
            $sNewCellText = StringReplace($sNewCellText, $LFCR, $YENYEN&"LFCR"&$YENYEN) ;encode any LFCR
            $sNewCellText = StringReplace($sNewCellText, $CR, $YENYEN&"CR"&$YENYEN) ;encode any CR
            $sNewCellText = StringReplace($sNewCellText, $LF, $YENYEN&"LF"&$YENYEN) ;encode any LF
            ;Pause("$sNewCellText = '" & $sNewCellText & "'")
            $aTemp[$iRow][$iCol] = $sNewCellText
        Next
    Next

    If $ldebug >= 1 Then _DebugArrayDisplay($aTemp, "ahha_Table_to_Array($iTableNumber = " & $iTableNumber &")")

    ;debug force an enty into the array
    ;$aTemp[3][1] = "This is a" & $SUB &"2nd line"  ;put $SUB in place of @CRLF
;~  $aTemp[3][1] = "This is a" & $YENYEN &"2nd line"    ;put $SUB in place of @CRLF
;~  Pause("$aTemp[3][1] = '" & $aTemp[3][1] & "'")
;~  _DebugArrayDisplay($aTemp, "h_Table_to_Array($iTableNumber = " & $iTableNumber)


;~      ;Write into a specific Table Cell   ;Tables.Item($i) is $i th Table, .Cell(Row, Col)
;~      $oDoc.Tables.Item(1).Cell(3, 1).Range.Text = "Well" &@CRLF& "how about" &@CRLF& "that."
;~      ;$oDoc.Tables.Item($i).Cell(3, 1).Range.ParagraphFormat.Alignment = $wdAlignParagraphLeft   ;alignment


    Return $aTemp   ;this will destroy the local COM exception handler

EndFunc     ;Func ahha_Word_Table_to_Array($iTableNumber = 1)

    ;now read the cell contents and place in the array
    ;THIS CRAPS OUT on this 2x3 Table because the first cell only has 1 row and when we try the second row
    ;like this $oDoc.Tables.Item(1).Cell(2, 1).Range.Text   it CRAPS out
    ;: ==> The requested action with this object has failed.:
    ;see https://stackoverflow.com/questions/48711070/visual-basic-table-object-get-column-count-of-specific-row
    ;https://www.tek-tips.com/viewthread.cfm?qid=1575358
    ;THIS APPROACH WON'T WORK AS THE OBJECT MODEL CANNOT HANDLE ANY MERGED CELLS
    ;SEE https://www.autoitscript.com/forum/topic/205953-trying-to-get-columns-in-a-row-for-a-word-table-solved/
    ;USE WATER'S _Word_DocTableRead AND EXTRACT WHAT WE NEED as an alternative approach if our's does not work


Func LocalErrFunc()     ;should only be invoked when "err.windescription:" = "The requested member of the collection does not exist."   ;i.e. a merged cell
    ;just check @error rather than trying to use SetError
    ;see https://www.autoitscript.com/forum/topic/205972-using-seterror-with-a-com-handler/
    ;One cannot set/change the @error and @extended values when returning from a COM error handler. <<-- Key
    ;Pause("err.windescription = '" & $oTableError.windescription & "'")
    $iExceptionCount = $iExceptionCount + 1
    Return  ;simply return and continue, check for @error to see if hit exception
Endfunc


Func ahha_Array_to_Word_Table($iTableNumber = 1)    ;, $aArray = 0) ;using Read/Modify/Write (RMW) so $iTableNumber already has an array associated with it
    ;ASSUMPTIONS - (1) ahha_Word_Table_to_Array($iTableNumber) has been called FIRST and the Array exists; AND (2) the Word file is still open
    ;user can modify the Array except where the marker "¥¥[MERGED INTO ABOVE OR LEFT]¥¥" is located
    ;if a CR LF is needed use encoding like this: where $YENYEN = ¥¥
    ;CRLF = $YENYEN&"CRLF"&$YENYEN
    ;LFCR = $YENYEN&"LFCR"&$YENYEN
    ;CR = $YENYEN&"CR"&$YENYEN
    ;LF = $YENYEN&"LF"&$YENYEN
    ;in this way ahha_Array_to_Word_Table can write the Array to the Word Table

    ;reach into $aTablesIndex[] to get array
    $aArray = $aTablesIndex[$iTableNumber]

    _DebugArrayDisplay($aArray, "$aArray to Write to TableNumber = '" & $iTableNumber & "'")    ;debug look at $aArray
    $iRowCount = $aArray[0][0]
    $iColCount = $aArray[0][1]

    For $iRow = 1 to $iRowCount     ;do row at a time
        For $iCol = 1 to $iColCount ;by column
            If $aArray[$iRow][$iCol] = "¥¥[MERGED INTO ABOVE OR LEFT]¥¥" Then
                ContinueLoop    ;skip it as a merged cell
            EndIf

            ;decode if needed before writing to cell as it can handle CR LF
            $sCellText = $aArray[$iRow][$iCol]

            $sNewCellText = StringReplace($sCellText, $YENYEN&"CRLF"&$YENYEN, $CRLF)    ;decode any CRLF
            $sNewCellText = StringReplace($sNewCellText, $YENYEN&"LFCR"&$YENYEN, $LFCR) ;decode any LFCR
            $sNewCellText = StringReplace($sNewCellText, $YENYEN&"CR"&$YENYEN, $CR) ;decode any CR
            $sNewCellText = StringReplace($sNewCellText, $YENYEN&"LF"&$YENYEN, $LF) ;decode any LF

            ;Write into a specific Table Cell
            $oDoc.Tables.Item($iTableNumber).Cell($iRow, $iCol).Range.Text = $sNewCellText
            ;user can do further formatting outside this function, so DO NOT do it here or pass arguments to do such, KISS
            ;$oDoc.Tables.Item($i).Cell($iRow, $iCol).Range.ParagraphFormat.Alignment = $wdAlignParagraphLeft   ;alignment
        Next
    Next

    Return(1) ;successful

EndFunc     ;Func ahha_Array_to_Word_Table($iTableNumber = 1, $aArray = 0)




;---------------------- functions ;----------------------

Func    Pause($text="")
    ;__scrolltext("Debug: Paused " & $text & @CRLF)
    MsgBox(262144, "DEBUG", "Paused: " & $text)
EndFunc

 

Test.docx

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...