Home > Back-end >  Modify matrix column spacing in equation through VBA (Word)
Modify matrix column spacing in equation through VBA (Word)

Time:07-18

I would like to automate vertically stacked math problems (sums, products, etc.). I found that by using matrices I can align the numbers to the right so the digits align properly. however, the column spacing default is too wide: wide column equation I can manually right click the matrix, select matrix spacing and set the minimum distance between columns to exactly 1, achieving my goal: narrow column equation But I cannot seem to get the syntax of the matrix manipulation in VBA. The documentation seems very sparse (no examples). I tried recording a macro, but the right-click menu does not appear for the matrix in the equation when recording. I am not sure how to "set" the OMathMat object, since it is not a property of OMath. I would settle for code that looped through all the equation objects in the document, all the matrix objects in those equations, and updated the OMathMat.ColSpacing property. I tried something like:

For Each equation In ActiveDocument.OMaths
                    For Each Func In equation.Functions
                            Func.Mat.ColSpacing = 1
                    Next
            Next

But the requested member (Mat) of the collection (Functions) did not exist. Also, there seems to be OMathFunction.Mat and OMathMat. I think I need the second option... yeah, I'm confused. Any guidance is appreciated, as I haven't found much online for this specific VBA topic.

CodePudding user response:

I solved my problem, looping through the equations (OMaths collection), and then, using the WdOMathFunctionType enumeration to find fraction type functions that contained a matrix in their numerator, I could properly set the matrix properties:

For Each eq In ActiveDocument.OMaths
    For Each Func In eq.Functions
            If Func.Type = 7 Then 'a fraction function
                    If Func.Args(1).Functions(1).Type = 12 Then 'a matrix function in the numerator
                            With Func.Args(1).Functions(1).Mat
                                     .ColGapRule = wdOMathSpacingExactly
                                     .ColGap = 1
                                     .PlcHoldHidden = True
                            End With
                    End If
            End If
    Next

Next

I knew the type of structured equations my document contained, so I didn't include many check conditions. (There probably a more elegant and robust way to search all 'child functions' of equations until the last node is reached.) Hopefully this can serve as a template for anyone trying to expose specific OMath Function properties.

CodePudding user response:

I agree that there isn't an obvious place to find documentation about accessing the OMath objects, so started trying to put something together.

But then you answered your own question - at this point it's probably more useful to publish what I already have, despite the fact that there are many unanswered questions.

So here are a few pieces of code that may help shed some light. They aren't well-tested.

I may well try to improve this question in future, but it will take time.

The first set of code should be able to deal with the situation where your Matrix objects could be anywhere in an OMath object.

The second set of code implements an "explorer" that reports the structure of the OMath objects in the main body of the document via Debug.Print statements.

Then there are a few more notes at the end.

Here's the code that should do what you needed, but slightly more generalised. You can copy the code into a single Module and run it.

' Keep some running totals

Dim OMathCount As Integer
Dim FunctionCount As Long
Dim MatCount As Long

Sub processOMaths()
Dim i As Long

FunctionCount = 0
MatCount = 0

' Just process the document body
With ActiveDocument
  For i = 1 To .OMaths.Count
    With .OMaths(i)
      Call processOMathFunctions(.Functions)
    End With
    OMathCount = i
  Next
End With
MsgBox "Processed " & CStr(OMathCount) & " Equation(s), " & _
       CStr(FunctionCount) & " Function(s), " & _
       CStr(MatCount) & " Matrix object(s)"
End Sub

Sub processOMathFunctions(oFunctions As OMathFunctions)

' There does not seem to be a way to return the entire collection of Functions
' in an OMath object. So it looks as if we have to recurse. But because the
' Object names for different Functions are different, we can't easily drill down
' to the next level using exactly the same code for multiple object types...

Dim i As Integer
For i = 1 To oFunctions.Count
  Call processSingleOMathFunction(oFunctions, i)
Next
End Sub

Sub processSingleOMathFunction(oFunctions As OMathFunctions, index As Integer)

' ...so unless someone has a better idea, we'll just use a Select Case
' statement and deal with all the possible Function types

FunctionCount = FunctionCount   1
With oFunctions(index)
  Select Case .Type
  
  Case WdOMathFunctionType.wdOMathFunctionAcc
    Call processOMathFunctions(.Acc.E.Functions)
  
  Case WdOMathFunctionType.wdOMathFunctionBar
    Call processOMathFunctions(.Bar.E.Functions)
  
  Case WdOMathFunctionType.wdOMathFunctionBorderBox
    Call processOMathFunctions(.BorderBox.E.Functions)
  
  Case WdOMathFunctionType.wdOMathFunctionBox
    Call processOMathFunctions(.Box.E.Functions)
  
  Case WdOMathFunctionType.wdOMathFunctionDelim
    Dim delimCount As Integer
    For delimCount = 1 To .Delim.E.Count
      Call processOMathFunctions(.Delim.E(1).Functions)
    Next
    
  Case WdOMathFunctionType.wdOMathFunctionEqArray
    Dim eqCount As Integer
    For eqCount = 1 To .EqArray.E.Count
      Call processOMathFunctions(.EqArray.E(eqCount).Functions)
    Next
      
  Case WdOMathFunctionType.wdOMathFunctionFrac
    Call processOMathFunctions(.Frac.Num.Functions)
    Call processOMathFunctions(.Frac.Den.Functions)
    
  Case WdOMathFunctionType.wdOMathFunctionFunc
    Call processOMathFunctions(.Func.E.Functions)
    Call processOMathFunctions(.Func.FName.Functions)
  
  Case WdOMathFunctionType.wdOMathFunctionGroupChar
    Call processOMathFunctions(.GroupChar.E.Functions)
    
  Case WdOMathFunctionType.wdOMathFunctionLimLow
    Call processOMathFunctions(.LimLow.E.Functions)
    Call processOMathFunctions(.LimLow.Lim.Functions)

  Case WdOMathFunctionType.wdOMathFunctionLimUpp
    Call processOMathFunctions(.LimUpp.E.Functions)
    Call processOMathFunctions(.LimUpp.Lim.Functions)
  
  Case WdOMathFunctionType.wdOMathFunctionLiteralText
    ' as far as I know, this cannot contain further Functions
    ' Do nothing.
  
  Case WdOMathFunctionType.wdOMathFunctionMat
    MatCount = MatCount   1
    Dim i As Integer
    .Mat.ColGapRule = wdOMathSpacingExactly
    ' Hardcode this bit
    .Mat.ColGap = 1 ' I think these are Twips, i.e. 1/20 pt
    ' We could iterate the columns and rows, but
    ' we'll iterate the Args instead.
    For i = 1 To .Args.Count
      Call processOMathFunctions(.Args(i).Functions)
    Next
    
  Case WdOMathFunctionType.wdOMathFunctionNary
    Call processOMathFunctions(.Nary.Sub.Functions)
    Call processOMathFunctions(.Nary.Sup.Functions)
    Call processOMathFunctions(.Nary.E.Functions)
  
  Case WdOMathFunctionType.wdOMathFunctionNormalText
    ' Used for 'Non-Math text'
    ' Do nothing
      
  Case WdOMathFunctionType.wdOMathFunctionPhantom
    Call processOMathFunctions(.Phantom.E.Functions)
    
  Case WdOMathFunctionType.wdOMathFunctionRad
    Call processOMathFunctions(.Rad.Deg.Functions)
    Call processOMathFunctions(.Rad.E.Functions)

  Case WdOMathFunctionType.wdOMathFunctionScrPre
    Call processOMathFunctions(.ScrPre.Sub.Functions)
    Call processOMathFunctions(.ScrPre.Sup.Functions)
    Call processOMathFunctions(.ScrPre.E.Functions)
  
  Case WdOMathFunctionType.wdOMathFunctionScrSub
    Call processOMathFunctions(.ScrSub.E.Functions)
    Call processOMathFunctions(.ScrSub.Sub.Functions)
  
  Case WdOMathFunctionType.wdOMathFunctionScrSubSup
    Call processOMathFunctions(.ScrSubSup.E.Functions)
    Call processOMathFunctions(.ScrSubSup.Sub.Functions)
    Call processOMathFunctions(.ScrSubSup.Sup.Functions)
        
  Case WdOMathFunctionType.wdOMathFunctionScrSup
    Call processOMathFunctions(.ScrSup.E.Functions)
    Call processOMathFunctions(.ScrSup.Sup.Functions)
  
  Case WdOMathFunctionType.wdOMathFunctionText
    ' Text - do nothing
    
  Case Else
    MsgBox "OMath Function type " & CStr(.Type) & " not recognized. Ignoring."
  End Select
End With
End Sub

The second lot of code is the Explorer. It's incomplete, in various ways. All the code could go in a single Module but as it stands it is divided into three Modules:

One module contains the main Explorer code, which is structured in a similar way to the code I posted above. I haven't completed the code for all the function types so you will see some TBD (To Be Done) comments.

' indentation increment for each level of oMath object nesting
Const incindent As String = "  "

Sub exploremath()
' This code explores the structure of 'modern' equations in Word
' i.e. the sort that have neen in Word since around Word 2007, not the older
' types inserted using an ActiveX object or an EQ field.

' Note to English speakers: some places use "Math" to refer to Mathematics
' e.g. the US. Others, e.g. the UK, use "Maths". This can cause a bit of confusion
' for UK English speakers but the trick is to realise that the oMaths object
' is just a collection of oMath objects. i.e. the naming convention is exactly the same as
' e.g. Paragraphs/Paragraph and so on.

' The overview is that
' - each Equation is represented by an OMath object
' - an oMath object contains an oMathFunctions collection
'   with 0 (?1) or more oMathFunction objects
' - an oMathFunction object can represent several different
'   types of structure, not just those with familiar function names
'   such as Sin, Cos etc. but structures such as Matrices,
'   Equation Arrays and so on.

Dim eqn As oMath
Dim fn As OMathFunction
Dim i As Long
Dim j As Long
Dim indent As String
With ActiveDocument
  For i = 1 To .OMaths.Count
    With .OMaths(i)
      Debug.Print "Equation " & CStr(i) & ":-"
      indent = ""
      Call documentOMathFunctions(.Functions, indent)
    End With
    Debug.Print
  Next
End With
End Sub

Sub documentOMathFunctions(fns As OMathFunctions, currentindent As String)
Dim i As Integer
Dim indent As String
indent = currentindent & incindent
Debug.Print indent & "Function count: " & CStr(fns.Count)
For i = 1 To fns.Count
  Call documentOMathFunction(fns, i, indent)
  Debug.Print
Next

End Sub

Sub documentOMathFunction(fns As OMathFunctions, index As Integer, currentindent As String)
Dim indent As String
indent = currentindent & incindent
With fns(index)
  Debug.Print indent & "Function " & CStr(index) & ", Type: " & OMathFunctionTypeName(.Type) & " :-"
  Select Case .Type
  
  Case WdOMathFunctionType.wdOMathFunctionAcc
    ' Accented object
    Debug.Print indent & "Accent: " & debugPrintString(ChrW(.Acc.Char))
    Call documentOMathFunctions(.Acc.E.Functions, indent)
  
  Case WdOMathFunctionType.wdOMathFunctionBar
    ' object with an overbar
    Debug.Print indent & "Bar " & AB(.Bar.BarTop) & ":-"
    Call documentOMathFunctions(.Bar.E.Functions, indent)
  
  Case WdOMathFunctionType.wdOMathFunctionBorderBox
    ' TBD

  Case WdOMathFunctionType.wdOMathFunctionBox        
    Debug.Print indent & "Box: IsDifferential? " & YN(.Box.Diff) & _
                         ", Breaks Allowed? " & YN(Not .Box.NoBreak) & _
                         ", TreatAsSingleOp? " & YN(.Box.OpEmu)
    Call documentOMathFunctions(.Box.E.Functions, indent)
  
  Case WdOMathFunctionType.wdOMathFunctionDelim
    ' Brackets etc.
    Debug.Print indent & "Delim: BeginningChar: " & _
                         debugPrintString(ChrW(.Delim.BegChar)) & _
                         ", EndChar: " & debugPrintString(ChrW(.Delim.EndChar)) & _
                         ", SeparatorChar: " & debugPrintString(ChrW(.Delim.SepChar))
    Debug.Print indent & incindent & "Grow? " & _
                         YN(.Delim.Grow) & ", LeftChar Hidden? " & _
                         YN(.Delim.NoLeftChar) & ", RightChar Hidden? " & _
                         YN(.Delim.NoRightChar) & ", Appearance: " & OMathShapeTypeName(.Delim.Shape)
    Dim delimCount As Integer
    For delimCount = 1 To .Delim.E.Count
      Debug.Print indent & "Part " & CStr(delimCount) & ":-"
      Call documentOMathFunctions(.Delim.E(1).Functions, indent)
    Next
    
  Case WdOMathFunctionType.wdOMathFunctionEqArray
    ' Array of aligned equations
    Debug.Print indent & "Equation Array: Vertical Alignment : " & OMathVertAlignTypeName(.EqArray.Align) & _
                         ", Expand to page column width? " & YN(.EqArray.MaxDist)
    Debug.Print "Expand to object width? " & YN(.EqArray.ObjDist) & _
                         ", Row Spacing Rule: " & oMathSpacingRuleName(.EqArray.RowSpacingRule);
    If .EqArray.RowSpacingRule = WdOMathSpacingRule.wdOMathSpacingExactly Then
      Debug.Print ", Row Spacing: " & CStr(.EqArray.RowSpacing) & " twips"
    ElseIf .EqArray.RowSpacingRule = WdOMathSpacingRule.wdOMathSpacingMultiple Then
      ' Don't know what the .rowspacing Unit is in this case
      Debug.Print ", Row Spacing: " & CStr(.EqArray.RowSpacing) & " half-lines";
    End If
    Debug.Print
    Dim eqCount As Integer
    For eqCount = 1 To .EqArray.E.Count
      Debug.Print indent & "Equation " & CStr(eqCount) & ":-"
      Call documentOMathFunctions(.EqArray.E(eqCount).Functions, indent)
    Next
    
  Case WdOMathFunctionType.wdOMathFunctionFrac
    ' Fraction
    Debug.Print indent & "Fraction numerator:-"
    Call documentOMathFunctions(.Frac.Num.Functions, indent)
    Debug.Print indent & "Fraction denominator:-"
    Call documentOMathFunctions(.Frac.Den.Functions, indent)
    
  Case WdOMathFunctionType.wdOMathFunctionFunc
    ' Function (not sure yet whether a 'Func' can
    ' only have a single argument (possibly blank))

    Debug.Print indent & "Func name: " & debugPrintString(.Func.FName.Range.Text)
    Call documentOMathFunctions(.Func.E.Functions, indent)
    Call documentOMathFunctions(.Func.FName.Functions, indent)
  
  Case WdOMathFunctionType.wdOMathFunctionGroupChar
    ' A character such as a brace over or under another Function.
    Debug.Print indent & "Group Char: " & UHex(.GroupChar.Char) & ", Position: " & AB(.GroupChar.CharTop); ""
    Call documentOMathFunctions(.GroupChar.E.Functions, indent)
    
  Case WdOMathFunctionType.wdOMathFunctionLimLow
    ' A Limit with the small text under the 'Lim word'
    Debug.Print indent & "'LimLow':-"
    Debug.Print indent & "Base:-"
    Call documentOMathFunctions(.LimLow.E.Functions, indent)
    Debug.Print indent & "Lim:-"
    Call documentOMathFunctions(.LimLow.Lim.Functions, indent)

  Case WdOMathFunctionType.wdOMathFunctionLimUpp
    ' A limit with the small text over the 'Lim word'
    Debug.Print indent & "'LimUpp':-"
    Debug.Print indent & "Base:-"
    Call documentOMathFunctions(.LimUpp.E.Functions, indent)
    Debug.Print indent & "Lim:-"
    Call documentOMathFunctions(.LimUpp.Lim.Functions, indent)
  
  Case WdOMathFunctionType.wdOMathFunctionLiteralText
    ' 'Literal Text' at first sight seems to be followed by
    ' a wdOMathFunctionText function with a Range
    ' containing the actual text.
    ' To be explored further
    ' But for now, do nothing.
  
  Case WdOMathFunctionType.wdOMathFunctionMat
    ' A Matrix. AFAIK they have to be rectangular
    Dim i As Integer
    Debug.Print indent & ", Column count: " & CStr(.Mat.Cols.Count) & _
                         ", Column gap rule: " & oMathSpacingRuleName(.Mat.ColGapRule);
    If .Mat.ColGapRule = WdOMathSpacingRule.wdOMathSpacingExactly Then
      Debug.Print ", Spacing: " & CStr(.Mat.ColGap) & " twips";
    ElseIf .Mat.ColGapRule = WdOMathSpacingRule.wdOMathSpacingMultiple Then
      Debug.Print ", Spacing: " & CStr(.Mat.ColGap);
    End If
    Debug.Print
    
    Debug.Print indent & "Row count: " & CStr(.Mat.Rows.Count) & _
                         ", Row gap rule: " & oMathSpacingRuleName(.Mat.RowSpacingRule);
    If .Mat.RowSpacingRule = WdOMathSpacingRule.wdOMathSpacingExactly Then
      Debug.Print ", Spacing: " & CStr(.Mat.RowSpacing) & " twips";
    End If
    Debug.Print
    
    Debug.Print indent & "Args count: " & CStr(.Args.Count)
    For i = 1 To .Args.Count
      Debug.Print indent & "  Arg " & CStr(i) & ":-"
      Call documentOMathFunctions(.Args(i).Functions, indent)
    Next
    
  Case WdOMathFunctionType.wdOMathFunctionNary
    ' An N-Ary function, such as a summation operator, product operator
    ' various types of integral operator and so on.
    ' AFAICS all current N-Ary operators are in effect 3-Ary, i.e.
    ' The lower limit is the Sub, the upper limit is the Sup, and the
    ' thing being summed/integrated etc. is the 'Base'
    
    ' ignore .SubSupLim for now
      .Nary.Char = &H2AFF
      
    Debug.Print indent & "N-ary function, Type character: " & _
                         oMathNaryOpName(.Nary.Char) & ", Grow? " & YN(.Nary.Grow) & ":-"
    Debug.Print indent & "N-ary Lower limit:- Hidden? " & YN(.Nary.HideSub)
    Call documentOMathFunctions(.Nary.Sub.Functions, indent)
    Debug.Print indent & "N-ary Upper limit:- Hidden? " & YN(.Nary.HideSup)
    Call documentOMathFunctions(.Nary.Sup.Functions, indent)
    Debug.Print indent & "N-ary body:-"
    Call documentOMathFunctions(.Nary.E.Functions, indent)
  
  Case WdOMathFunctionType.wdOMathFunctionNormalText
    'Used for 'Non-Math text'
    Debug.Print indent & "Literal Text: " & debugPrintString(.Range.Text)
  
  Case WdOMathFunctionType.wdOMathFunctionPhantom
  ' TBD

  Case WdOMathFunctionType.wdOMathFunctionRad
    Debug.Print indent & "Degree:- (Hidden? " & YN(.Rad.HideDeg)
    Call documentOMathFunctions(.Rad.Deg.Functions, indent)
    Debug.Print indent & "Radical:-"
    Call documentOMathFunctions(.Rad.E.Functions, indent)

  Case WdOMathFunctionType.wdOMathFunctionScrPre
    ' base object with a superscript/subscript *before* the base
    ' Think this means an obect that has *both* (although one or both
    ' could be left blank)
    ' (TBR: Can OMath be used right-to-left, and if so, how
    ' are properties named/documented as 'to the left of',
    ' 'to the right of', 'before', 'after' to be interpreted?
    ' Or are math formulas etc. always expressed as LTR worldwide
    ' these days (I would guess so!)
    
    Debug.Print indent & "ScrPre Subscript:-"
    Call documentOMathFunctions(.ScrPre.Sub.Functions, indent)
    Debug.Print indent & "ScrPre Superscript:-"
    Call documentOMathFunctions(.ScrPre.Sup.Functions, indent)
    Debug.Print indent & "ScrPre Base-"
    Call documentOMathFunctions(.ScrPre.E.Functions, indent)
  
  Case WdOMathFunctionType.wdOMathFunctionScrSub
    ' base object with a subscript after the base
    Debug.Print indent & "Base:-"
    Call documentOMathFunctions(.ScrSub.E.Functions, indent)
    Debug.Print indent & "Superscript:-"
    Call documentOMathFunctions(.ScrSub.Sub.Functions, indent)
  
  Case WdOMathFunctionType.wdOMathFunctionScrSubSup
    ' base object with subscript and supersript after the base
    Debug.Print indent & "ScrSubSup Base-"
    Call documentOMathFunctions(.ScrSubSup.E.Functions, indent)
    Debug.Print indent & "ScrSubSup Subscript:-"
    Call documentOMathFunctions(.ScrSubSup.Sub.Functions, indent)
    Debug.Print indent & "ScrSubSup Superscript:-"
    Call documentOMathFunctions(.ScrSubSup.Sup.Functions, indent)
            
  Case WdOMathFunctionType.wdOMathFunctionScrSup
    ' base object with supersript after the base
    Debug.Print indent & "Base:-"
    Call documentOMathFunctions(.ScrSup.E.Functions, indent)
    Debug.Print indent & "Superscript:-"
    Call documentOMathFunctions(.ScrSup.Sup.Functions, indent)
  
  Case WdOMathFunctionType.wdOMathFunctionText
    Debug.Print indent & "Text: " & debugPrintString(getRunTextFromXML(.Range))
  
  Case Else
    ' we already printed an unknown type message before the select statement.
  End Select
End With

End Sub

In Module "Common" there are some Helper routines

Function UHex(codepoint As Long) As String
' Form a 4-digit Unicode Hex string
UHex = "U " & Right("0000" & CStr(codepoint), 4)
End Function


Function debugPrintString(s As String) As String
' Form a string where 1-byte characters are output as is,
' others are output as Unicode Hex strings
' NB, at the moment we do not try to change stuff such as "&" to "&"
Dim i As Long
Dim t As String
t = ""
For i = 1 To Len(s)
  If AscW(Mid(s, i, 1)) < 256 Then
    t = t & Mid(s, i, 1)
  Else
    t = t & " " & UHex(AscW(Mid(s, i, 1))) & " "
  End If
Next
debugPrintString = t
End Function

Function YN(b As Boolean) As String
If b Then YN = "Y" Else YN = "N"
End Function

Function AB(b As Boolean) As String
If b Then AB = "Above" Else AB = "Below"
End Function

Function getRunTextFromXML(r As Word.Range) As String
' We need a function like this to retrieve text in the Math Font, (e.g. Cambria Math Font,
' which appears to be encoded as ASCII rather than Unicode.
' So if the equation contains a Cambria Math "A", the .Range.Text is returned as "??"
' For later: if the text is *not* in Cambria Math, we probably *don't* want to do this!
' (Could end up inspecting character by character).
' For the moment, use a kludge to get the first run of text in the range.
Dim x As String
Dim i1 As Long
Dim i2 As Long
x = r.WordOpenXML
' FOr an oMath text, we look for m:t rather than w:t
i1 = InStr(1, x, "<m:t>")
i2 = InStr(i1, x, "</m:t>")
getRunTextFromXML = Mid(x, i1   5, i2 - i1 - 5)
End Function

In module Enums, there are some more Helper routines to return things such as Enum names as text (if only VBA had better facilities for Reflection!)

Function oMathIsAllowedNaryOp(codepoint As Long) As Boolean
' Perhaps can look up the unicode database rather than hardcode this list
Select Case codepoint
Case &H2140, &H220F To &H2211, &H222B To &H2233, &H22C0 To &H22C3, &H2A00 To &H2A06, &H2A09, &H2AFF
  oMathIsAllowedNaryOp = True
Case Else
  oMathIsAllowedNaryOp = False
End Select
End Function

Function oMathNaryOpName(codepoint As Long) As String
' Perhaps can look up the unicode database rather than hardcode this list
' and the standard Unicode character names

Select Case codepoint
Case &H2104
  oMathNaryOpName = "Double-Struck N-Ary Summation"
Case &H220F
  oMathNaryOpName = "N-Ary Product"
Case &H2210
  oMathNaryOpName = "N-Ary Coproduct"
Case &H2211
  oMathNaryOpName = "N-Ary Summation"
Case &H22C0
  oMathNaryOpName = "N-Ary Logical And"
Case &H22C1
  oMathNaryOpName = "N-Ary Logical Or"
Case &H22C2
  oMathNaryOpName = "N-Ary Intersection"
Case &H22C3
  oMathNaryOpName = "N-Ary Union"
Case &H22A0
  oMathNaryOpName = "N-Ary Circled Dot Operator"
Case &H22A1
  oMathNaryOpName = "N-Ary Circled Plus Operator"
Case &H22A2
  oMathNaryOpName = "N-Ary Circled Times Operator"
Case &H22A3
  oMathNaryOpName = "N-Ary Union Operator With Dot"
Case &H22A4
  oMathNaryOpName = "N-Ary Union Operator With Plus"
Case &H22A5
  oMathNaryOpName = "N-Ary Square Intersection Operator"
Case &H22A6
  oMathNaryOpName = "N-Ary Square Union Operator"
Case &H22A9
  oMathNaryOpName = "N-Ary Times Operator"
Case &H2AFF
  oMathNaryOpName = "N-Ary White Vertical Bar"
Case Else
  oMathNaryOpName = "(Possibly invalid N-ary opcode: " & UHex(codepoint) & ")"
End Select
End Function

Function OMathShapeTypeName(OMathShapeType As Integer) As String
Select Case OMathShapeType
Case WdOMathShapeType.wdOMathShapeCentered
  OMathShapeTypeName = "wdOMathShapeCentered"
Case WdOMathShapeType.wdOMathShapeMatch
  OMathShapeTypeName = "wdOMathShapeMatch"
Case Else
  OMathShapeTypeName = "(Math Shape Type unknown: " & CStr(OMathShapeType) & ")"
End Select
End Function

Function oMathSpacingRuleName(oMathSpacingRule As Long) As String
Select Case oMathSpacingRule
Case WdOMathSpacingRule.wdOMathSpacing1pt5
  oMathSpacingRuleName = "wdOMathSpacing1pt5"
Case WdOMathSpacingRule.wdOMathSpacingDouble
  oMathSpacingRuleName = "wdOMathSpacingDouble"
Case WdOMathSpacingRule.wdOMathSpacingExactly
  oMathSpacingRuleName = "wdOMathSpacingExactly"
Case WdOMathSpacingRule.wdOMathSpacingMultiple
  oMathSpacingRuleName = "wdOMathSpacingMultiple"
Case WdOMathSpacingRule.wdOMathSpacingSingle
  oMathSpacingRuleName = "wdOMathSpacingSingle"
Case Else
  oMathSpacingRuleName = "(Math Spacing Rule unknown: " & CStr(oMathSpacingRule) & ")"
End Select
End Function

Function OMathVertAlignTypeName(OMathVertAlignType As Integer) As String
Select Case OMathVertAlignType
Case WdOMathVertAlignType.wdOMathVertAlignBottom
  OMathVertAlignTypeName = "wdOMathVertAlignBottom"
Case WdOMathVertAlignType.wdOMathVertAlignCenter
  OMathVertAlignTypeName = "wdOMathVertAlignCenter"
Case WdOMathVertAlignType.wdOMathVertAlignTop
  OMathVertAlignTypeName = "wdOMathVertAlignTop"
Case Else
  OMathVertAlignTypeName = "(Math Vertical Alignment Type unknown: " & CStr(OMathVertAlignType) & ")"
End Select
End Function

Notes.

  • AFAIK the author/designer of the OMath objects and User Interface (and indeed other aspect of layout in Word) is Murray Sargent III. His paper on UnicodeMath Describes how the system as a whole is intended to use Build-Up. But take care, because not everything mentioned in there is necessarily implemented in all versions of OMath (which is used across a number of MS Office products). His Math-in-Office blog can be quite enlightening too.
  • There are at least two versions of the OMath object documentation - one for "VBA" and one for .NET. There are some differences (e.g. some Properties and at least one Function Type enumeration name is missing from the VBA version. The .NET version is near here and the VBA version is near here.
  • At the moment, none of the code I've posted provides anything that would help you modify the Function structure of an Equation, e.g. insert a new function. That's mainly because I haven't got to grips with it yet. Even writing code to insert a piece of Text throws up a number of problems, not least the question of why Math Font text is not encoded as Unicode and what that means when it comes to modifying it. It may in fact be easier to work with the Linear ("not built up" text version rather than the Object model. TBD!
  • Related