I need to collect the names of all the authors making revisions or adding comments to a Word document. I do something like this:
Public Function collectAuthors() As String
Dim cmt As Word.Comment
Dim r As String: r = vbCr
Dim t As String
Dim i As Long: i = 0
Dim rev As Word.Revision
For Each cmt In ActiveDocument.Comments
t = cmt.Author
If InStr(r, vbCr & t & vbCr) = 0 Then r = r & t & vbCr
Next cmt
For Each rev In ActiveDocument.Revisions
t = rev.Author
If InStr(r, vbCr & t & vbCr) = 0 Then r = r & t & vbCr
Next rev
...
Recently t = rev.Author started to fail with
Run-time error '-2147467259 (80004005)':
Method 'Author' of object 'Revision' failed
This perhaps has something to do with the size of the document. I am using Word 2016, 64-bit version on Windows 7.
I also tried a loop where the member of the collection is indexed explicitly as in
t = ActiveDocument.Revisions(i).Author
and it stops after a few (maybe 10) iterations.
What is the cause of this error and can it be eliminated by a different coding approach?
Or should I forget about this and extract the author names from word\document.xml and word\comments.xml?
Thanks.
In the end, I read the author names from the XML directly. There are several obstacles which are not difficult:
The relevant XML needs to be extracted from the .docx file. I used
7-Zip as it's free and easy to automate, plus will work well when it
comes to write the XML back to the document.
The resulting files are UTF-8. UTF-8 is not supported directly by
VBA.
The author names need to be extracted from the XML. It is simple
enough to do this with a simple linear search through the XML. I did
not do any XML parsing or lexical analysis.
I had no control over the input documents, thus was in no position to break it up into more manageable parts. However reading the XML directly sidestepped the errors completely. Lastly, this is much better than attempting to process RTF.
Related
I am trying to write a helper script for a colleague that will automatically open up all .doc(x) files in a directory, find any and all chinese characters, set their Font, save and close.
I already have a working version of this script. The file opening/saving/closing part is handled in Python/win32com and works fine. My big point of contention is still the VBA macro.
I know there is a regex (\p{Han}) that should be able to catch all Chinese characters, but this does not seem to work in VBA. Similarly, I have tried using Unicode Ranges and Chr(W). Nothing so far produced any output, let alone correct output.
Out of frustration, I made one last ditch attempt and simply inverted the search paramters. This is how it is now:
Sub FindReplace_zh(Rng As Range)
With Rng.Find
Do While .Execute(FindText:="[!A-ZÄÖÜa-zäöü0-9><_ ^11^13§$²³%#&/\+-]", MatchWildcards:=True)
If Rng.Font.Bold = True And Rng.Font.Name Like "Arial*" Then
Rng.Font.Name = "SimHei"
ElseIf Rng.Font.Bold = False And Rng.Font.Name Like "Arial*" Then
Rng.Font.Name = "SimSun"
End If
Rng.Collapse 0
Loop
End With
End Sub
AT LEAST THIS WORKS, but its far from elegant and still produces some undesired output.
I have yet to understand how I can substitute "[!A-ZÄÖÜa-zäöü0-9><_ ^11^13§$²³%#&/+-]" with a variable, or most anything else. Many characters are not covered by this regex, such as "(", ")" etc., but adding them (even escaped with ) will result in runtime errors in VBA.
I found a lot of tutorials and questions dealing with removing or inserting text, but my specific case of finding text and then changing the font, while leaving everything else untouched, seems rather specific.
Fun fact:
I had to add ^11 and ^13 to the regex list, as not including them would lead to the Macro inserting new linebreaks in random positions of the .doc
EDIT:
New try with comment:
Dim searchPattern As String
searchPattern = "[" & ChrW(&H2E80) & "-" & ChrW(&HFFED) & "]{1,}"
With Rng.Find
Do While .Execute(FindText:=searchPattern, MatchWildcards:=True)
Invalid operation on final line!
I also would not have concatinated a string like this. I am not sure how VBA parses this, but apprently not the way we hoped.
EDIT2: FIX
Removing "{1,}" from searchPattern did it. Now it works exactly as I expected it to :)
searchPattern = "[" & ChrW(&H2E80) & "-" & ChrW(&HFFED) & "]"
It is possible to find the value of characters that cannot be represented in the VBIDE by pasting them into an empty Word document and then using VBA to print the AscW values of each character in the text you wish to investigate. You can then use ChrW in VBA to reassemble the text in a VBA friendly way.
From
pinyin.info/news/2016/…
You can use the find string "[⺀-■]{1,}" to find any Chinese character. However as you have noted when you paste this text into the VBA IDE you get [?-?]{1,} because VBA uses UTD-8 as its character set. (I think).
The following code
Public Sub PrintCharacterValues()
Dim myIndex As Long
With ActiveDocument.Paragraphs(1).Range
For myIndex = 1 To 8
Debug.Print .Characters(myIndex), AscW(.Characters(myIndex)), Hex(AscW(.Characters(myIndex)))
Next
End With
End Sub
Gives the output of
" 34 22
[ 91 5B
? 11904 2E80
- 45 2D
? -19 FFED
] 93 5D
" 34 22
160 A0
Thus you can get the critical section of the find string as
"[" & ChrW(&H2£80) & "-" & ChrW(&HFFED) &"]"
I have scoured the net for days trying to figure this out, but apparently my gaps in Access are too severe and the answer eludes me. Someone has apparently already answered this question, however I'm not able utilize the information.
My specific situation:
Table1 has 30,000+ rows and multiple columns. "Photo Path" is a text field with the path and filename of an image. "Photo" is an OLE Object field currently empty.
What I would like to do is store the image specified in "Photo Path" as an OLE object in "Photo".
Table1 Current State:
Name - Photo Path - Photo
Impala - C:\Cars\Impala.jpg -
Jeep - C:\Cars\Jeep.jpg -
Table1 Desired Result:
Name - Photo Path - Photo
Impala - C:\Cars\Impala.jpg - LONG BINARY DATA
Jeep - C:\Cars\Jeep.jpg - LONG BINARY DATA
I don't know how to execute FileToBlob() against my entire database using the generously provided code. The authors seem to expect me to use a form, which I was unable to get to work as well.
What I think I want is an SQL statement that will execute against every row in Table1 using FileToBlob() or something close to it.
I've tried variations of the following statement in the SQL Query to no avail.
SELECT Table1.[Photo Path], FileToBlob(Table1.[Photo Path],Table1.Photo) As Photo
FROM Table1;
Thank you for taking the time to read this and providing an answer.
Had to figure this one out for myself as there were no responses. For those may follow looking for an actual answer, here it is.
I modified the code that that I found to fit my specific problem.
Create a new module and put the code below in it. If by chance the code does not work, you can try going to Tools-->References and if not already selected, select "Microsoft DAO X.x Object Library" where X.x is the latest library. If it still doesn't run you'll have to check to see if you need to select any other references.
There are so many records to go through, I felt better doing this through code instead of a query that may take a long time to execute and one won't know what is going on. In the code I have it writing to the status bar in Access so you know where you are at (but if the files are small it will probably fly by, but at least you know it is working).
To run the code, just put your cursor anywhere in the routine and I first like to press F8 which steps into the code just to make sure I'm in the right routine. Then press F5 to run the rest of the code. If you want to create a form to run the code instead you can do that too. Just create a button and on the "on click" event add the code:
call Load_Photo()
If you want to see the status updates, make sure the main access window is visible before you run the code (If you run from a form, it will already be there).
Note I renamed the field "Name" in Table1 to "strName" because "Name" is a reserved word. I'd suggest not using "Name" as a field name. You might be OK, but you could run into issues at some point, especially when referencing the field through code. If you choose not to change the field name, change the code.
Also note that the sample code provided stored as a binary. So if you create an Access form to show the records, the image will not automatically appear - there is some other manipulation necessary that I am not familiar with off hand.
Without further ado, here's the code to solution I was looking for:
Option Compare Database
Option Explicit
Public Sub Load_Photo()
On Error GoTo LoadFileError
Dim strSQL As String
Dim rstTable As DAO.Recordset
Dim strStatus As String
Dim count As Integer
Dim strFile As String
Dim nFileNum As Integer
Dim byteData() As Byte
Dim varStatus As Boolean
'
' In case something happens part way through the load, just load photos that have not been loaded yet.
'
strSQL = "Select [strName], [Photo Path], [Photo] from Table1 Where [Photo] is null"
Set rstTable = CurrentDb.OpenRecordset(strSQL)
If rstTable.RecordCount > 0 Then
rstTable.MoveFirst
count = 0
Do While Not rstTable.EOF
strFile = rstTable![Photo Path]
If Len(Dir(strFile)) > 0 Then
nFileNum = FreeFile()
Open strFile For Binary Access Read As nFileNum
If LOF(nFileNum) > 0 Then
count = count + 1
'
' Show user status of loading
'
strStatus = "Loading photo " & count & " for " & rstTable![strName] & ": " & rstTable![Photo Path]
varStatus = SysCmd(acSysCmdSetStatus, strStatus)
DoEvents
ReDim byteData(1 To LOF(nFileNum))
Get #nFileNum, , byteData
rstTable.Edit
rstTable![Photo] = byteData
rstTable.Update
Else
MsgBox ("Error: empty file, can't load for Name = " & rstTable![strName] & " and Photo Path = " & rstTable![Photo Path])
End If
Close nFileNum
Else
MsgBox ("Error: File not found for Name = " & rstTable![strName] & " and Photo Path = " & rstTable![Photo Path])
End If
rstTable.MoveNext
Loop
End If
LoadFileExit:
If nFileNum > 0 Then Close nFileNum
rstTable.Close
strStatus = " "
varStatus = SysCmd(acSysCmdSetStatus, strStatus)
Exit Sub
LoadFileError:
MsgBox "Error " & Err.Number & ": " & Err.Description, vbCritical, "Error on " & strFile
Resume LoadFileExit
End Sub
I've been searching around on Google to find a better way to show images in Access without actually inserting the image into the database.
I found this article 'http://support.microsoft.com/kb/285820' via this thread 'Is there a way to get ms-access to display images from external files' which goes into great detail on how to set paths to pictures through folders/files, and it works great, for a 'set' picture. However, I want a different picture to display when I switch to a different record.
Here is the code from the article:
Option Compare Database
Option Explicit
Public Function DisplayImage(ctlImageControl As Control, strImagePath As Variant) As String
On Error GoTo Err_DisplayImage
Dim strResult As String
Dim strDatabasePath As String
Dim intSlashLocation As Integer
With ctlImageControl
If IsNull(strImagePath) Then
.Visible = False
strResult = "No image name specified."
Else
If InStr(1, strImagePath, "\") = 0 Then
' Path is relative
strDatabasePath = CurrentProject.FullName
intSlashLocation = InStrRev(strDatabasePath, "\", Len(strDatabasePath))
strDatabasePath = Left(strDatabasePath, intSlashLocation)
strImagePath = strDatabasePath & strImagePath
End If
.Visible = True
.Picture = strImagePath
strResult = "Image found and displayed."
End If
End With
Exit_DisplayImage:
DisplayImage = strResult
Exit Function
Err_DisplayImage:
Select Case Err.Number
Case 2220 ' Can't find the picture.
ctlImageControl.Visible = False
strResult = "Can't find image in the specified name."
Resume Exit_DisplayImage:
Case Else ' Some other error.
MsgBox Err.Number & " " & Err.Description
strResult = "An error occurred displaying image."
Resume Exit_DisplayImage:
End Select
End Function
I have a feeling that I need to place a line of code that states something like, show image where Image.ID = "ID", but I can't figure out where to put it without getting errors. Am I over-looking something perhaps, or am I approaching this the wrong way? I just don't want to clutter my database, memory-wise, with .bmp images, but I feel like I am going to have to.
SOLVED: A much easier solution is as Gord Thompson has described below in the comments. And from my own experience, using this method for .bmp images leaves the picture distorted and out of contrast. I tested the image for .jpg and it worked perfectly! I hope this helps others who are having trouble with similar problems finds this post helpful.
The Microsoft Support article you cited applies to Access 2003. In Access 2010 (and later) there is a much simpler way to do it. All you need to do is place an Image control on the form and bind it to the field in your table that contains the path to the image file.
For example, with an [Employees] table like this
EmployeeID FirstName LastName PhotoPath
---------- --------- -------- ---------------------------------
1 Gord Thompson C:\Users\Public\Pictures\Gord.jpg
2 Hank Kingsley C:\Users\Public\Pictures\Hank.png
you could use an Image control whose Control Source is the [PhotoPath] field ...
... and the image will automatically be retrieved from the file specified
No VBA required.
Note also that the image files do not have to be .bmp files.
Added by an anonymous user:
For users of the Microsoft Office 365 version of MS Access (c. 2020), here is what you may need to know in order to get this terrific solution to work:
The [PhotoPath] field in its data table needs to be "Long Text" data type, if using long paths or long file names. The [PhotoPath] field format "Is Hyperlink" may need to be set to "No." I was getting additional, unwanted coding from Access on my text inputs. The [Image3] control may need to specify "Linked" rather than "Embedded."
I'm wordering if there is a way (directly or using VBA) to get a list of all the building blocks as they appear in the Building Blocks Organizer, that is, the names of the building blocks, the Gallery, the Category, the Template, the Behavior, etc. I don't want to extract auto text parts or anything like that. I just want to be able to get and print the complete list of Bilding Blocks and the rest of the info dispayed in the Building Blocks Organizer.
Thanks a lot!
D
Building block entries are stored within several Word template files. If you want to iterate over all available building blocks, you must therefore iterate over all loaded Word templates. You can do so using the following macro:
Sub ListBuildingBlocks()
Dim oTemplate As Template
Dim oBuildingBlock As BuildingBlock
Dim i As Integer
For Each oTemplate In Application.Templates
For i = 1 To oTemplate.BuildingBlockEntries.Count
Set oBuildingBlock = oTemplate.BuildingBlockEntries.item(i)
Debug.Print oBuildingBlock.Name + vbTab _
+ oBuildingBlock.Type.Name + vbTab _
+ oBuildingBlock.Category.Name + vbTab _
+ oTemplate.FullName
Next
Next
End Sub
I am creating a sql query for an access database that will be exported to a text file. The requirements include a line feed separating each line. Does that happen by default, or its something that I need to add in?
If I need to add it, how do I do that?
TIA
TransferText includes LineFeed and I am fairly sure most methods of getting text out of Access will include linefeed, unless you do something to stop it. It is not too difficult to check.
Dim fs As New FileSystemObject
s = "c:\docs\test.txt"
DoCmd.TransferText acExportDelim, , "query6", s
Set f = fs.OpenTextFile(s)
a = f.ReadAll
''Split at linefeed: Chr(10)
aa = Split(a, Chr(10))
''Test 1
Debug.Print UBound(aa)
''Test 2
For Each itm In aa
Debug.Print itm
Next