Get Paragraphs in specific Section of Word Document - perl

I'm having problems finding a specific section in word. It was recommended I try looking through the VB Object Browser in Word for help. I know there are at least 5 heading "sets" (I.E. if you look in the Document Map, I see numbered 1,2,3,4,5...). I don't know how to navigate to that fifth heading, initially I thought it was sections, but when I viewed sections I realized that almost all of it is in one section, but in case anyone is looking for information on how to do sections, the below seems to work, since I already went through the trouble of writing it.
my($document) = $Word->Documents->Open($input) || die("Unable to open document ", Win32::OLE->LastError());
my $section = $document->{Sections}->Item(1); # put section number you're looking for in here
$section_five_paragraphs = $section->{Range}->Paragraphs();
$enumerate = new Win32::OLE::Enum($section_five_paragraphs);
while (defined($paragraph = $enumerate->Next()))
{
print $paragraph->{Range}->{Text} . "\n";
}
So does anyone know how to get to this 5th heading area, or can point me to something that might help?

Tell me if I didn't follow you correctly but you're trying to find the 5th Heading 1 in the a certain section? If that's the case, although Word clearly defines sections (which you note as $document->{Sections}->Item(1)), it does not clearly define Headings in specific or styles in general. For that you'll have to go through all the styles looking for those of interest. The following VBA code (and I apologize for not writing perl) does just that and looks only in a specific section.
Sub FindHeading1()
On Error GoTo MyErrorHandler
Dim currentDocument As Document
Set currentDocument = ActiveDocument
Dim findRange As Range
Set findRange = currentDocument.Sections(2).Range 'which section you want
Dim endRange As Long
endRange = findRange.end
findRange.Find.ClearFormatting
findRange.Find.Style = ActiveDocument.Styles("Heading 1")
Dim headingCountFound As Long
Do While findRange.Find.Execute(FindText:="")
If findRange.End > endRange Then Exit Sub
findRange.Select
headingCountFound = headingCountFound + 1
If headingCountFound = 3 Then 'which occurance you want
MsgBox "Found."
Exit Do
End If
DoEvents
Loop
Exit Sub
MyErrorHandler:
MsgBox "FindHeading1" & vbCrLf & vbCrLf & "Err = " & Err.Number & vbCrLf & "Description: " & Err.Description
End Sub

Related

Is there a way to count the number of actual replaces when using replaceAll in OO Basic?

Considering the example for search & replace of specific uk-to-us words from the Editing Text Documents OO Wiki:
Dim I As Long
Dim Doc As Object
Dim Replace As Object
Dim BritishWords(5) As String
Dim USWords(5) As String
BritishWords() = Array("colour", "neighbour", "centre", "behaviour", _
"metre", "through")
USWords() = Array("color", "neighbor", "center", "behavior", _
"meter", "thru")
Doc = ThisComponent
Replace = Doc.createReplaceDescriptor
For I = 0 To 5
Replace.SearchString = BritishWords(I)
Replace.ReplaceString = USWords(I)
Doc.replaceAll(Replace)
Next I
Question: is there a way to get the count of actual replacement that has been made ? (if any) I don't mind the individual count for each term, but just globally – i.e. if, say, the original text included 2 occurences for 'colour' and 1 for 'behaviour', in the end to get the number 3 (purpose: to report this number to user as info via MsgBox).
As shown in the example at https://www.openoffice.org/api/docs/common/ref/com/sun/star/util/XReplaceable.html, the number found is returned.
Dim TotalFound As Long
TotalFound = 0
...
TotalFound = TotalFound + Doc.replaceAll(Replace)
Next I
MsgBox "Replaced " & TotalFound & " occurrences"
Result: Replaced 3 occurrences

insert and delete text after an Range-position in Word

I have a SET-field in Word 2007. After the set-field there could be everything (text,bookmark, SET field,...). I want to add a text (e.g. "exampletext") in between.
After this I want to delete this inserted text (but I don't want to search through the whole document).
Is there a method?
Trial 1 (it inserts it in the field - and not after the field):
' xStartReturn is a field
Dim myExampletext As WordApp.Range = objDoc.Range(xStartReturn.Code.End, xStartReturn.Code.End )
myExampletext.Text = "exampletext"
Trial 2 (leads to the problem that I don't get the Range-field to delete the exampletext afterwards):
xEndeReturn.insertAfter("exampletext")
Trial 3:
'xStartReturn.Code.End + 1 doesn't work.. but I found out that the "}"-Sign in the setField is +20 after xStartReturn.Code.End. Theoretical this should work - but there could be e.g. also paragraph afterwards.
'-> I can automatically check that there is a paragraph - but why is the exampletext added **after** the paragraph?
Dim example As WordApp.Range = objDoc.Range(xStartReturn.Code.End + 20, xStartReturn.Code.End + 20)
example.Text = "exampletext"
Dim later As WordApp.Range = objBasisvorlage_.Range(objXStartReturn.Code.End + 20, objXStartReturn.Code.End + 20 + "SDFSD".Length) 'this is wrong?!
later.Delete()
The following works for me. Since you didn't give us a minimum code with which to reproduce the problem I don't know how relevant the framework is that I used. But you should be able to follow the steps.
Watch what I do with r_f_Code (field code range). You can ignore/remove r_f_Result as I had that in for reference and debugging purposes.
Collapsing the field code range to its end-point leaves the range just within the field braces. Moving the starting point one character to the right puts it just outside the braces, but before anything else. (Note: I tested with two immediately adjacent SET fields.)
My code then enters some text and bookmarks it. That's the only way you do what you ask if what follows the SET field can be "anything". Although I suppose you could insert a Content Control - that would be uniquely identifiable if you go about it correctly...
Sub PositionAfterFieldCode()
Dim f As word.Field
Dim r_f_Code As word.Range, r_f_Result As word.Range
For Each f In ActiveDocument.Fields
If f.Type = wdFieldSet Then
Set r_f_Code = f.code
Set r_f_Result = f.result
'Debug.Print Len(r_f_Code), r_f_Code.Text, Len(r_f_Result), r_f_Result.Text
r_f_Code.Collapse wdCollapseEnd
r_f_Code.MoveStart wdCharacter, 1
'r_f_Code.Select
r_f_Code.Text = "abc"
r_f_Code.Bookmarks.Add "AfterSet", r_f_Code
Exit For
End If
Next
End Sub

Finding text AND fields with variable content in Word

I need to find and delete every occurrence of the following pattern in a Word 2010 document:
RPDIS→ text {INCLUDEPICTURE c:\xxx\xxx.png" \*MERGEFORMAT} text ←RPDIS
Where:
RPDIS→ and ←RPDIS are start and end delimiters
Between the start and end delimiters there can be just text or text and fields with variable content
The * wildcard in the Word Find and Replace dialog box will find the pattern if it contains text only but it will ignore patterns where text is combined with fields. And ^19 will find the field but not the rest of the pattern until the end delimiter.
Can anyone help, please?
Here's a VBA solution. It wildcard searches for RPDIS→*←RPDIS. If the found text contains ^19 (assuming field codes visible; if objects are visible instead of field codes, then the appropriate test is text contains ^01), the found text is deleted. Note that this DOES NOT care about the type of embedded field --- it will delete ANY AND ALL embedded fields that occur between RPDIS→ and ←RPDIS, so use at your own risk. Also, the code has ChrW(8594) and ChrW(8592) to match right-arrow and left-arrow respectively. You may need to change that if your arrows are encoded differently.
Sub test()
Dim wdDoc As Word.Document
Dim r As Word.Range
Dim s As String
' Const c As Integer = 19 ' Works when field codes are visible
Const c As Integer = 1 ' Works when objects are visible
Set wdDoc = ActiveDocument
Set r = wdDoc.Content
With r.Find
.Text = "RPDIS" & ChrW(8594) & "*" & ChrW(8592) & "RPDIS"
.MatchWildcards = True
While .Execute
s = r.Text
If InStr(1, s, chr(c), vbTextCompare) > 0 Then
Debug.Print "Delete: " & s
' r.Delete ' This line commented out for testing; remove comments to actively delete
Else
Debug.Print "Keep: " & s
End If
Wend
End With
End Sub
Hope that helps.

List an MS Access form's controls with their events

I have a humongous Access form with hundreds of controls, all with some events.
One of them is throwing an error crying about "Validation or After Update event". No control name, nothing more to go by.
So I figured I'd list all the forms with all their validation rules and all their events (not all of them are "[Event procedure]", some are custom function calls).
Is there any way to get a list of a control's events?
My code so far:
Sub ListAllControlsAndTheirEvents(FormToCheck As Form)
Dim ctrlCurrent As Control
For Each ctrlCurrent In FormToCheck.Controls
Debug.Print "Name: " & ctrlCurrent.Name
On Error Resume Next
Debug.Print "Validation: " & ctrlCurrent.ValidationRule 'Not all controls have a ValidationRule property.
On Error GoTo 0
For Each eventCurrent In ctrlCurrent.events 'this thing doesn't exist :(
Debug.Print "Event: " & eventCurrent.Name & "; value: " & eventCurrent.whatever
Next
Next
End Sub
So, is there a way to implement this part?
For Each eventCurrent In ctrlCurrent.events 'this thing doesn't exist :(
Debug.Print "Event: " & eventCurrent.Name & "; value: " & eventCurrent.whatever
Next
Digging through the code module won't give me the custom function event calls, just the [Event Procedure] codes.
You could use typename to determine the type of control, then set so something like if typename(x) ="CommanButton" then set cb=x, then loop the properties, for property names beginning with On, then if there is an [Event Procedure] you know there's an event, also you can then narrow down your search to x_Click
The code below should identify and list all events (whether blank, [Event Procedure], or a custom function) for each control on the form. I tested it on Access 2010 by creating a blank form and adding several controls. I made sure to assign some standard events and custom functions to a few controls in order to validate.
Public Sub ListAllControlsAndTheirEvents(FormToCheck As Access.Form)
Dim ctrlProp As Object
Dim ctrlCurrent As Access.Control
For Each ctrlCurrent In FormToCheck.Controls
Debug.Print "Name: " & ctrlCurrent.Name
If PropertyExists(ctrlCurrent, ctrlProp, "ValidationRule") Then
Debug.Print "Validation: " & ctrlProp.Value
End If
For Each ctrlProp In ctrlCurrent.Properties
' Category 4 type 8 identifies an event property
If ctrlProp.Category = 4 And ctrlProp.Type = 8 Then
Debug.Print _
"Event: " & ctrlProp.Name & "; " & _
"Value: " & ctrlProp.Value
End If
Next
Next
End Sub
Private Function PropertyExists( _
ByRef ctrlCurrent As Access.Control, _
ByRef ctrlProp As Object, _
ByRef PropName As String _
) As Boolean
On Error Resume Next
Set ctrlProp = ctrlCurrent.Properties(PropName)
PropertyExists = Err.Number = 0
End Function
I know that this question has been marked as answered like 4 years ago.
I have been to this question in my search for the same thing in Excel.
This question was the source behind my search for an answer to a similar question.
I have found an answer to this question using TLI.
The answer I provided here is meant to help other people reach for a solution quickly and painlessly.
There is NO intention of gaining merits/points/reputation but just to help others get an answer I've only reached after spending so much time and energy.
I am no expert in Access VBA nor am I in Excel VBA but I think TypeLib Info can be used in Access too.
Requirement: Reference to TypeLib Information library at C:\Windows\SysWow64\TLBINF32.DLL
Sub printControlEventNames(ByVal o As Object)
Dim t As TLI.TLIApplication
Set t = New TLI.TLIApplication
Dim ti As TLI.TypeInfo
Set ti = t.ClassInfoFromObject(o)
Dim mi As TLI.MemberInfo
For Each mi In ti.DefaultEventInterface.Members
Debug.Print mi.Name
Next
End Sub
My question and my own solution can be found here.

How to edit all word documents in a directory?

I've been given the scut job of correcting some hundred or so code testing reports that have been filled out incorrectly by a senior coder who has more import work to do.
Unluckily for me all the files are ms-word documents. But luckily for the formatting is all the same and the errors are all made in the same cells in the same table.
In the past I wrote a bash to edit to change single quotes to double quotes on multiple xml files. But that was with a linux machine. This time around I have only a window machine.
Any hints where to begin?
The answer was to use VBA. I built two subroutines.
The first subRoutine loops through the directory and
opens each *.doc file it finds. Then on the open document file it calls
the second subRoutine. After the second subRoutine is finished the document
is saved and then closed.
Sub DoVBRoutineNow()
Dim file
Dim path As String
path = "C:\Documents and Settings\userName\My Documents\myWorkFolder\"
file = Dir(path & "*.doc")
Do While file <> ""
Documents.Open FileName:=path & file
Call editCellsTableRow2
ActiveDocument.Save
ActiveDocument.Close
file = Dir()
Loop
End Sub
~~~~~~
The second subRoutine only works if all documents have the same formating.
For example: The second row of the only table in the document has cells numbered 6, 7, 8. These contain "dd/MM/yyyy" , "Last Name", "First Name"
These cells need to be changed to "yyyy/MM/dd", "Surname", "Given Name"
Sub editCellsTableRow2()
Application.ScreenUpdating = False
Dim Tbl As Table, cel As Cell, i As Long, n As Long
With ActiveDocument
For Each Tbl In .Tables
Tbl.Rows(2).Alignment = xlCenter
For Each cel In Tbl.Rows(2).Cells
If cel.ColumnIndex = 6 Then
cel.Range.Text = vbCrLf + "yyyy/MM/dd"
End If
If cel.ColumnIndex = 7 Then
cel.Range.Text = vbCrLf + "Surname"
End If
If cel.ColumnIndex = 8 Then
cel.Range.Text = vbCrLf + "Given Name"
End If
Next cel
Next Tbl
End With
Set cel = Nothing: Set Tbl = Nothing
Application.ScreenUpdating = True
End Sub