You mean like this createOutline function (which actually copy all headings from a source word document into a new word document):
(I believe the astrHeadings = docSource.GetCrossReferenceItems(wdRefTypeHeading)
function is the key in this program, and should allow you to retrieve what you are asking for)
Public Sub CreateOutline()
Dim docOutline As Word.Document
Dim docSource As Word.Document
Dim rng As Word.Range
Dim astrHeadings As Variant
Dim strText As String
Dim intLevel As Integer
Dim intItem As Integer
Set docSource = ActiveDocument
Set docOutline = Documents.Add
' Content returns only the main body of the document, not the headers/footer.
Set rng = docOutline.Content
' GetCrossReferenceItems(wdRefTypeHeading) returns an array with references to all headings in the document
astrHeadings = docSource.GetCrossReferenceItems(wdRefTypeHeading)
For intItem = LBound(astrHeadings) To UBound(astrHeadings)
' Get the text and the level.
strText = Trim$(astrHeadings(intItem))
intLevel = GetLevel(CStr(astrHeadings(intItem)))
' Add the text to the document.
rng.InsertAfter strText & vbNewLine
' Set the style of the selected range and
' then collapse the range for the next entry.
rng.Style = "Heading " & intLevel
rng.Collapse wdCollapseEnd
Next intItem
End Sub
Private Function GetLevel(strItem As String) As Integer
' Return the heading level of a header from the
' array returned by Word.
' The number of leading spaces indicates the
' outline level (2 spaces per level: H1 has
' 0 spaces, H2 has 2 spaces, H3 has 4 spaces.
Dim strTemp As String
Dim strOriginal As String
Dim intDiff As Integer
' Get rid of all trailing spaces.
strOriginal = RTrim$(strItem)
' Trim leading spaces, and then compare with
' the original.
strTemp = LTrim$(strOriginal)
' Subtract to find the number of
' leading spaces in the original string.
intDiff = Len(strOriginal) - Len(strTemp)
GetLevel = (intDiff / 2) + 1
End Function
UPDATE by @kol on March 6, 2018
Although astrHeadings
is an array (IsArray
returns True
, and TypeName
returns String()
) I get a type mismatch
error when I try to access its elements in VBScript (v5.8.16384 on Windows 10 Pro 1709 16299.248). This must be a VBScript-specific problem, because I can access the elements if I run the same code in Word’s VBA editor. I ended up iterating the lines of the TOC, because it works even from VBScript:
For Each Paragraph In Doc.TablesOfContents(1).Range.Paragraphs
WScript.Echo Paragraph.Range.Text
Next
Unfortunately, table captions are not actually associated with their tables through the Word Object Model. When a table caption is created, it is just text placed in a separate Paragraph object.
So, the short answer is that no, there is no good way to find the caption for a given table.
I did write some code that may help you though. It basically iterates through all Paragraph objects in the document and looks for the «Caption» style (alternatively, you could look for text formatted «Table #:», or whatever you’d like). If the very next Paragraph contains tables, it places the text of the caption into the first cell of the first table found.
Dim p As Paragraph
Dim lastParagraphWasCaption As Boolean
Dim lastCaptionText As String
lastParagraphWasCaption = False
For Each p In ActiveDocument.Paragraphs
If lastParagraphWasCaption And p.Range.Tables.Count > 0 Then
p.Range.Tables(1).Cell(1, 1).Range.Text = lastCaptionText
End If
If p.Range.Style = "Caption" Then
lastParagraphWasCaption = True
lastCaptionText = p.Range.Text
Else
lastParagraphWasCaption = False
End If
Next
Keep in mind that this is just an example of how you could tie together a caption with its table. With that said, it is not a very reliable method and I would not recommend using it unless you absolutely need to because table captions could presumably not have the caption styling, or there could be caption styling on something that isn’t a caption, etc.
First things first, let’s clean everything up.
Proper descriptive naming, proper validation variables, making the code clear and obvious about what’s happening where
Public Function GetHeadingFromStyle(ByVal styleToFind As String) As String
'/ Iteratively checks the style of all paragraphs, starting at the current selection and working towards the start of the document.
'/ If the style matches the inputStyle, return the text of the paragraph.
'/ If no match is found, return "No heading Found"
Const NO_HEADING_FOUND_TEXT As String = "No heading Found"
Dim currentRange As Range
currentRange = Selection.Range
Dim wholeDocumentSearched As Boolean
wholeDocumentSearched = False
Dim headingFound As Boolean
headingFound = False
Do Until headingFound Or wholeDocumentSearched
Set currentRange = currentRange.Previous(wdParagraph, 1)
headingFound = (currentRange.Style = styleToFind)
wholeDocumentSearched = ActiveDocument.Range(0, currentRange.Paragraphs(1).Range.End).Paragraphs.Count = 0
Loop
If headingFound Then
GetHeadingFromStyle= currentRange.Paragraphs(1).Range.Text
Else
GetHeadingFromStyle= NO_HEADING_FOUND_TEXT
End If
End Function
Okay, here’s where we’re taking so much time:
Do Until headingFound Or wholeDocumentSearched
Set currentRange = currentRange.Previous(wdParagraph, 1)
headingFound = (currentRange.Style = styleToFind)
wholeDocumentSearched = ActiveDocument.Range(0, currentRange.Paragraphs(1).Range.End).Paragraphs.Count = 0
Loop
You mentioned in your previous question that these documents could be 1,000+ pages. Iteratively looping over something that huge is going to be slow. What we need is some kind of map/lookup.
I’m not very familiar with the Word object model, so there might be something blindingly obvious/easy I’m missing. That aside, here are ideas:
Idea #1:
Map the entire document once and then reference the map. This will mean having a slow execution once, and then all subsequent searches should be lightning fast.
Note, this only works if the document structure is not going to change between some number of iterations (5-10 would be a good minimum). Because as soon as a new paragraph gets inserted/deleted/moved, our entire map is going to be inaccurate.
So, something like this:
Option Explicit
Public ParagraphStyles As Variant
Public Const INDEX_INDEX2 As Long = 1 '/ index2 to distinguish between dimensions of the array
Public Const STYLE_INDEX2 As Long = 2
Public Sub SetupDocumentSearch()
ParagraphStyles = MapParagraphStyles
'/ Call Main sub here
End Sub
Public Function MapParagraphStyles(ByRef targetDocument As Document)
'/ Loop through the document and, for each paragraph:
'/ Add the paragraph Index and Style to an array
With targetDocument
Dim styleMap As Variant
ReDim styleMap(1 To .Paragraphs.Count, 1 To 2)
Dim currentParagraph As Range
Dim paragraphCounter As Long
For paragraphCounter = 1 To .Paragraphs.Count
styleMap(paragraphCounter, INDEX_INDEX2) = indexcounter
styleMap(paragraphCounter, STYLE_INDEX2) = .Paragraphs(indexcounter).Style
Next paragraphCounter
End With
MapParagraphStyles = styleMap
End Function
Then we can loop over an array searching for our style. Iterating over arrays is orders of magnitude faster than working with high-level objects like documents:
Public Function GetHeadingFromStyle(ByVal styleToFind As String) As String
'/ Iteratively checks the style of all paragraphs, starting at the current selection and working towards the start of the document.
'/ If the style matches the inputStyle, return the text of the paragraph.
'/ If no match is found, return "No heading Found"
Const NO_HEADING_FOUND_TEXT As String = "No heading Found"
Dim paragraphIndex As Long
paragraphIndex = ActiveDocument.Range(0, Selection.Paragraphs(1).Range.End).Paragraphs.Count
Dim wholeDocumentSearched As Boolean
wholeDocumentSearched = False
Dim headingFound As Boolean
headingFound = False
Do Until headingFound Or wholeDocumentSearched
paragraphIndex = paragraphIndex - 1
headingFound = ParagraphStyles(paragraphIndex, STYLE_INDEX2) = styleToFind
wholeDocumentSearched = (paragraphIndex = 1)
Loop
If headingFound Then
GetHeadingFromStyle = ActiveDocument.Paragraphs(paragraphIndex).Range.Text
Else
GetHeadingFromStyle = NO_HEADING_FOUND_TEXT
End If
End Function
- krez0n
- Начинающий
- Сообщения: 17
- Зарегистрирован: 16.02.2007 (Пт) 10:51
Поиск заголовков в word
нужно найти заголовки, к примеру, первого уровня и применить к ним соответствующее форматирование. пробовал писать такой код:
- Код: Выделить всё
Sub Смена_заголовков()
With ActiveDocument.Styles(wdStyleHeading1).Font
.Name = "Arial"
.Size = 10
End With
End Sub
но только проблема в том, что word ищет заголовок по списку стилей. И если в списке стилей есть несколько стилей заголовков первого уровня, то идет применение только к первому из спика, а нужно чтобы ко всем стилям. может у кого есть идеи?
P.S. названия заголовков первого уровня изначально неизвестны[/code]
- RayShade
- Scarmarked
- Сообщения: 5511
- Зарегистрирован: 02.12.2002 (Пн) 17:11
- Откуда: Russia, Saint-Petersburg
-
- Сайт
- ICQ
RayShade » 03.10.2007 (Ср) 8:42
Проверять сперва список силей а предмет тех, которые базируются на heading 1 а потом уже, получив их список в цикле менять форматирование для каждого?
I don’t understand. Sorry.
- krez0n
- Начинающий
- Сообщения: 17
- Зарегистрирован: 16.02.2007 (Пт) 10:51
krez0n » 03.10.2007 (Ср) 8:53
RayShade писал(а):Проверять сперва список силей а предмет тех, которые базируются на heading 1 а потом уже, получив их список в цикле менять форматирование для каждого?
а как это можно сделать? как проверить список на наличие заголовков?
- RayShade
- Scarmarked
- Сообщения: 5511
- Зарегистрирован: 02.12.2002 (Пн) 17:11
- Откуда: Russia, Saint-Petersburg
-
- Сайт
- ICQ
RayShade » 03.10.2007 (Ср) 10:06
У каждого стиля в Word есть проперть BaseStyle которая хранит в себе имя стиля на котором он основан То есть получается что то типа
- Код: Выделить всё
Sub Смена_заголовков(a)
With ActiveDocument.Styles(a).Font
.Name = "Arial"
.Size = 10
End With
End Subdim coll as new collection
for i=1 to activedocument.styles.count
if (styles.item(i).name="heading") or (styles.item(i).basename="heading") then coll.add styles.item(i).name
net ifor i=1 to coll.count
Смена_заголовков(coll.item(i))
next i
I don’t understand. Sorry.
- krez0n
- Начинающий
- Сообщения: 17
- Зарегистрирован: 16.02.2007 (Пт) 10:51
krez0n » 04.10.2007 (Чт) 8:33
не знаю почему, но в этой строке пишет, что требует объект и выкидывает ошибку
- Код: Выделить всё
if (styles.item(i).name="heading") or (styles.item(i).basename="heading") then coll.add styles.item(i).name
- alibek
- Большой Человек
- Сообщения: 14205
- Зарегистрирован: 19.04.2002 (Пт) 11:40
- Откуда: Russia
alibek » 04.10.2007 (Чт) 8:37
ActiveDocument.Styles разумеется.
Lasciate ogni speranza, voi ch’entrate.
- krez0n
- Начинающий
- Сообщения: 17
- Зарегистрирован: 16.02.2007 (Пт) 10:51
krez0n » 04.10.2007 (Чт) 8:43
alibek писал(а):ActiveDocument.Styles разумеется.
да нет. это я поставил. дело по ходу не в этом.
у меня сейчас часть кода так
- Код: Выделить всё
For i = 1 To ActiveDocument.Styles.Count
If (ActiveDocument.Styles.Item(i) = "Заголовок 1") Or (ActiveDocument.Styles.Item(i).BaseStyle = wdStyleHeading1) Then coll.Add Styles.Item(i).Name
Next i
и во второй строке ошибка
- alibek
- Большой Человек
- Сообщения: 14205
- Зарегистрирован: 19.04.2002 (Пт) 11:40
- Откуда: Russia
alibek » 04.10.2007 (Чт) 8:58
Ты не везде указал ActiveDocument.
Lasciate ogni speranza, voi ch’entrate.
- krez0n
- Начинающий
- Сообщения: 17
- Зарегистрирован: 16.02.2007 (Пт) 10:51
krez0n » 04.10.2007 (Чт) 9:19
да я уже нашел проблемные места. осталось понять почему в ActiveDocument.Styles.Item(i).BaseStyle не входят добавленные вручную стили. Видны только стили из Normal.dot
Вернуться в VBA
Кто сейчас на конференции
Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 2
Автор KR, 30 июня 2019, 13:46
Подскажите, как найти ближайший заголовок (любой уровень), который находится над текстовым курсором?
Необходимо найти и изменить текст сразу после ближайшего заголовка.
Администратор
- Administrator
- Сообщения: 2,254
- Записан
Sub Макрос()
Dim rng As Range
‘1. Создание ссылки на выделенный фрагмент или на мигающий курсор.
Set rng = Selection.Range.Duplicate
‘2. Переставляем ссылку в начало ближайшего заголовка, который находится над курсором.
Set rng = rng.GoToPrevious(wdGoToHeading)
‘3. Создаём ссылку на фрагмент, в котором находится абзац, который находится после заголовка.
Set rng = rng.Paragraphs(1).Next.Range
‘4. Убираем с конца абзаца знак абзаца, чтобы не удалить абзац при вставке текста.
rng.MoveEnd Unit:=wdCharacter, Count:=-1
‘5. Заменяем в абзаце текст на новый.
rng.Text = «text»
End Sub
[свернуть]
Все прекрасно работает. Спасибо!
- Форум по VBA, Excel и Word
-
►
Word -
►
Макросы в Word -
►
Word VBA Макросы: Искать ближайший заголовок.