PDA

View Full Version : Solved: Delete first character in a paragraph if it's a graphic



crowfan
08-31-2007, 12:34 PM
Hello all,

What I'd like to do is loop through all paragraphs in my document, and if the paragraph is a certain style ("C1H Bullet 2") and if the first character is a graphic, then delete that character. Here's what I have so far:Sub delete_graphics()

Dim myPara As Paragraph

For Each myPara In ActiveDocument.Paragraphs
If myPara.Style = "C1H Bullet 2" And XXXXXX Then myPara.Range.Characters(1).Delete
Next

End SubObviously I have to fill in the x's above. That's where I'm having trouble. I want the x's to basically say "myPara.Characters(1).DataType = Graphic" but obviously that doesn't exist. How can I achieve this?

Thanks!

fumei
08-31-2007, 01:05 PM
Why do you want to achieve this?

BTW: if it is a bulleted list the bullet is NOT a graphic.

crowfan
08-31-2007, 01:17 PM
Because I'm importing one document into another. The source document has these graphics on certain lines and I don't want them in the destination document.

I'm not trying to delete the bullet. I want the bullet to remain, but the graphic to be deleted.

Here is an example:

http://i134.photobucket.com/albums/q113/ckugler1/bullet.jpg

I want the bullet to stay but the graphic (and, actually, the tab after it) to be deleted.

Not all bulleted points have that graphic before them, so I can't just delete the first 2 characters.

mdmackillop
09-01-2007, 03:10 AM
First attempt, including the dreaded "Selection"
Sub delete_graphics()
Dim myPara As Paragraph
For Each myPara In ActiveDocument.Paragraphs
If myPara.Style = "C1H Bullet 2" Then
myPara.Range.Characters(1).Select
If Selection.Type = 7 Then
Selection.MoveRight Unit:=wdCharacter, Count:=1, Extend:=wdExtend
Selection.Delete
End If
End If
Next
End Sub

mdmackillop
09-01-2007, 04:07 AM
Second attempt. I've assumed that the graphic can only appear at the start of the paragraph.
Sub delete_graphics()
Dim myPara As Paragraph, MyR As Range
For Each myPara In ActiveDocument.Paragraphs
If myPara.Style = "C1H Bullet 2" Then
Set MyR = myPara.Range
On Error GoTo NoShape
MyR.InlineShapes(1).Delete
If InStr(1, MyR.Text, vbTab) = 1 Then
MyR.Characters(1).Delete
End If
NoShape:
End If
Next
End Sub

fumei
09-01-2007, 11:27 AM
Nice Malcolm. Assuming of course that it IS an InlineShape, not a Shape.

mdmackillop
09-01-2007, 11:56 AM
Thanks Gerry. I'm sure we'll be told!

crowfan
09-01-2007, 02:46 PM
Hi,

Yes, they both work. It is an inline graphic.

Indulge a rookie: why is "selection" something to avoid?

Thanks for the help!

mdmackillop
09-01-2007, 02:57 PM
Indulge a rookie: why is "selection" something to avoid?

It an efficiency thing! When you record code, text etc. is selected and processed and the code records each move. "Better" programming works with the Range of the items and performs the actions without moving through the text on the screen. In small procedures, there is no real penalty, but if there is a lot of looping, it can take much longer.

crowfan
09-01-2007, 03:50 PM
OK, that makes sense to me. Thanks.

One more question, if you would: I've tried both macros and they both work. In your second macro, regarding this line: MyR.InlineShapes(1).Delete. To my mind, that looks like it would simply delete the first inline shape in the paragraph, regardless of where it is in the paragraph (i.e., regardless of whether it's in the first position). That's not how it seems to work, but I don't quite understand why. Or is that what you meant by "I've assumed that the graphic can only appear at the start of the paragraph"?

The problem is, sometimes there may be a graphic inlined in the paragraph that I want to keep. I only want to delete a graphic if it is in the first position.

Thanks for helping me to understand. I do very much appreciate it.

mdmackillop
09-01-2007, 03:54 PM
The second code will delete the first graphic in any position. I'll have a look at restricting it.

fumei
09-02-2007, 10:01 AM
Technically, Selection is a less efficient route because Selection uses GUI resources. It uses memory and processing time to actually change the UI.

Range does not. It does not uses any GUI meory at all.

Further, there are some properties and methods available to Range that are not exposed to Selection.

MyR.InlineShapes(1).Delete should delete the first item in the InlineShapes collection, regardless of its position. Are you saying it does not?

crowfan
09-02-2007, 11:15 AM
MyR.InlineShapes(1).Delete does delete the first item in the InlineShapes collection, which is a problem for me in this instance. Sometimes, there may be a graphic in the middle of the paragraph that I want to keep (for example, "Click the Save button [graphic]." I only want to delete the graphic if it is in the first position, as shown in the screenshot I posted.

Selection is the way I would have gone about it. Indeed, even the pseudo-code example I gave in my first post uses it, and Malcolm's first example is the way I would do it. But I don't know as much about it as you guys, and now that you've explained the reasons for avoiding Selection where possible, I will try to do that.

Thanks again.

mdmackillop
09-02-2007, 11:47 AM
I thought
MyR.InlineShapes(1).Delete
should have worked, but no success.

Sub delete_graphics3()
Dim myPara As Paragraph, MyR As Range
Set MyR = ActiveDocument.Range(0, 0)
For Each myPara In ActiveDocument.Paragraphs
If myPara.Style = "C1H Bullet 2" Then
Set MyR = ActiveDocument.Range(myPara.Range.Start, myPara.Range.Start + 2)
MyR.Select
On Error GoTo NoShape
Selection.InlineShapes(1).Delete
If InStr(1, MyR.Text, vbTab) = 1 Then
MyR.Characters(1).Delete
End If
End If
NoShape:
Next
End Sub

fumei
09-04-2007, 12:49 AM
Very cute Malcolm.

Although I do not understand why you set MyR first: Set MyR = ActiveDocument.Range(0, 0) What is the point of this as you Set it later?

Also:Set MyR = ActiveDocument.Range(myPara.Range.Start, myPara.Range.Start + 2)

can be done as :
Set MyR = myPara.Range
MyR.MoveEnd Unit:=wdCharacter, Count:=-(Len(MyR) - 2)There may be some advantage in creating the range of the whole paragraph first. Say...test for the number of Shapes. If there are 2, maybe delete (1)? Don't know, maybe. Can there BE two graphics?

crowfan
09-04-2007, 07:21 AM
Malcolm,

Your last example works beautifully and leaves alone any other graphics that may appear in the paragraph. Thanks for the help in creating the macro and in understanding everything!

mdmackillop
09-04-2007, 03:36 PM
Hi Gerry
Some old bits left in. I was setting the range and then using MyR.SetRange to change to the relevant text. Have you any idea why
MyR.InlineShapes(1).Delete
doesn't work?

fumei
09-05-2007, 03:02 PM
I don't understand. The OP replied that MyR.InlineShapes(1).Delete did work. Just that in some circumstance - when it is NOT at the starting position - they did not want it to work. You fixed that.

So, ummm, what is not working?

mdmackillop
09-05-2007, 03:53 PM
Hi Gerry,
If I set MyR to a small range, the Delete is failing.

Sub delete_graphics3()
Dim myPara As Paragraph, MyR As Range
Set MyR = ActiveDocument.Range(0, 0)
For Each myPara In ActiveDocument.Paragraphs
If myPara.Style = "C1H Bullet 2" Then
Set MyR = ActiveDocument.Range(myPara.Range.Start, myPara.Range.Start + 2)
On Error Goto NoShape
MyR.InlineShapes(1).Delete

If InStr(1, MyR.Text, vbTab) = 1 Then
MyR.Characters(1).Delete
End If
End If
NoShape:
Next
End Sub

fumei
09-06-2007, 12:51 PM
If it is failing then the range does not have an InlineShape.

If you are telling me that you think there IS a InlineShape in the range, then, ummmm, post a file.

mdmackillop
09-06-2007, 01:05 PM
Hi Gerry,
Here's the sample file
Regards
Malcolm

fumei
09-06-2007, 03:18 PM
Curses, curses, curses.

OK, first of all, I am right. The reason why:MyR.InlineShapes(1).Delete does not work is that there is no InlineShape in MyR.

However......

where I am totally stumped (congratulations BTW), is why. Not only that but it seems to be weirder than you realize. Take a look at this modifed code. For Each myPara In ActiveDocument.Paragraphs
If myPara.Style = "C1H Bullet 2" Then
Set MyR = myPara.Range
MsgBox MyR.InlineShapes.Count
Set MyR = ActiveDocument.Range(Start:=myPara.Range.Start, _
End:=myPara.Range.Start + 14)
MyR.Select
MsgBox MyR.Text
MsgBox "MyR InlineShapes count: " & _
MyR.InlineShapes.Count & _
vbCrLf & _
"Selection InlineShapes count: " & _
Selection.InlineShapes.CountTwo things.

I set MyR to the whole paragraph, and display a messagebox with the InlineShape count.

It displays 1. Good. It should.

OK. Now MyR is resized. Notice I made Start + 14, rather than your + 2.

Now I Select the range object.

In theory, setting MyR to Start + 14 should include some text, right?

Aside: all this is on the first step through, so we are dealing with the paragraph:

• [graphic] Hjkjkl’jkl#;’

InlineShapes are considered characters, so Start + 14 should have some of "Hjkjkl’jkl#;"....yes?

It does not. You can use + 2, + 5, + 8, + 14. it does not matter.

Msgbox MyR.Text.....is blank.

OK, next. A message box that displays:

MyR.InlineShapes count:
Selection.InlineShapes count:

What does it show????

MyR.InlineShapes count: 0
Selection.InlineShapes count: 1

EVEN THOUGH the selection should be exactly the same as the range.

MyR.Select....right?

But, noooooooo, the range object does NOT have a count of an InlineShape, although the Selection (which IS the range) does have a count.

So obviously MyR.InlineShape(1).Delete fails, as there is none.

To go over it again, even though I made myR Start + 14, there is NO text in that. If the InlineShape counts as one character, then there should be text, right.

Well......the InlineShape is NOT be counted as one character. Which is why MyR.InlineShape(1).Delete fails. The range (Start + 2, or even Start + 14) does NOT include the entire InlineShape by the numbers.

If you get the actual number for the "H" of "Hjkjkl’jkl#;", it is 44.

The actual number of myPara.Range.Start = 17.

The InlineShape is using 27 characters. Therefore your 2 character range parameters are not enough. Now note that Tab character is indeed one character, number is 43.

While the range (in your example is Start + 2) is only two characters, when the MyR.Select instruction is fired, Word selects the range. Because the ONLY thing in that range is the InlineShape, it is the only thing that can be selected. So...it is selected.

Therefore:

The range InlineShape count = 0, but the Selection.InlineShape count = 1.

Want more weirdness????? Take a look at this:Set MyR = myPara.Range

MsgBox "Range Start = " & MyR.Start & _
vbCrLf & "Range End = " & MyR.End & _
vbCrLf & "Length = " & Len(MyR)This set MyR as the whole paragraph. It displays the Start number, the End number.....and the Len.

It displays:

Range Start = 17
Range End = 58
Length = 16

Lets see. Start + Length should equal End.

17 + 16 = 33 Ummmm, nope.

"H" = 44
<Tab> = 43

The InlineShape occupies 16 to 42.

Let's go on. If you make MyR = Start + 25, then:

MyR.InlineShapes count: 1
Selection.InlineShapes count: 1

If you make MyR = Start + 24, then:

MyR.InlineShapes count: 0
Selection.InlineShapes count: 1

Ahhhh, in other words, if you make the range terminate BEFORE the tab, it fails. However, if you make the range to cover just before the Tab, it succeeds. Like this:
For Each myPara In ActiveDocument.Paragraphs
If myPara.Style = "C1H Bullet 2" Then
Set MyR = myPara.Range
MyR.Collapse Direction:=wdCollapseStart
MyR.MoveEndUntil Cset:=vbTab
MsgBox MyR.InlineShapes.CountCount?

1

So, For Each myPara In ActiveDocument.Paragraphs
If myPara.Style = "C1H Bullet 2" Then
Set MyR = myPara.Range
MyR.Collapse Direction:=wdCollapseStart
MyR.MoveEndUntil Cset:=vbTab
On Error GoTo NoShape
MyR.InlineShapes(1).Deletewill delete the InlineShape.

Congratulations Malcolm. That took me a while. And I have to say that in terms of the object model, I am once again cursing those Word developers. Those dudes are on some serious drugs.

Len is of course a count of characters - unless you use LenB, which BTW gives interesting results. So from the point of view of characters, there are 16 characters, with the InlineShape counting (as per the Object Model) as ONE character.

However, in terms of actual calculated positions - as per the Object Model for Ranges - the InlineShape is Start= 17, End= 43.

There is a decided conflict. I am not sure I would call it a bug...but it is getting very close to being one.

mdmackillop
09-06-2007, 03:56 PM
A power of work there Gerry. I'll go through it again at the weekend. 5 stars awarded!
(Maybe I'll just stick to Selection :eek:)

fumei
09-06-2007, 04:12 PM
Selection?!?!?!

Tsk, tsk. Now don't go weak knees on me.

It is when Word becomes nonsensical that the sensible have words.

lucas
09-06-2007, 04:52 PM
hmm, I just noticed Gerry's signature.....I've gone throught heck with Word and I stand by it.

TonyJollans
09-07-2007, 01:44 AM
Just dropped in and will look properly later but ...

... the graphics are EMBED fields which is why they take up so much of the Range.

fumei
09-07-2007, 02:43 AM
Thanks Tony. Which is where I.....finally...got to as well.

See? We should have just PM'd Tony right away. It took me all day to figure it out.

I find it odd, though, that Len still considers it as one character.

TonyJollans
09-07-2007, 10:50 AM
I'll try and make this clear!

When you use Len(MyR), the reference to MyR is not a reference to the Range itself, but to its default property (Text) and the length of the text is, in your case 16 characters.

What you see in the Range - and the Text - varies according to the view. If you look at the Selection, it is what you see on screen and varies according to, amongst other things, whether or not you have Field Codes displayed. The equivalent in a Range depends on the settings of TextRetrievalMode.

The actual document content for the field/graphic (behind the scenes) is:

{ EMBED Paint.Picture | ^g }

where ...
{ is the start of field 0x13
| is the delimiter between field code and value 0x14
^g is the placeholder for graphic 0x01
} is the end of the field 0x15

When you explicitly set the start and end of the Range it then has text something like "{ EMBED Paint." and as none of this is 'visible' the text is actually set to "". When you Select this, the UI can't cope with an invisible end and the best it can do is adjust the end so that it is visible - look at Selection.End after MyR.Select and you can see this.

The Range does not include the graphic in the displayed field value but the selection with the adjusted end does include the graphic and so the displayed counts of 0 for the number of inlineshapes in the range, and 1 for the number in the selection are correct.

fumei
09-07-2007, 11:12 AM
See? Tony can explain it in 10 times less words that I do....and better.

mdmackillop
09-07-2007, 12:13 PM
Thanks for the explanation both. Looking at the filed codes certainly makes thing clearer. I'd never given a thought to field codes. I'll remember to check for that in the future. I'll slink off now back to my Cell.

fumei
09-07-2007, 01:25 PM
Actually, now that I remember this, I got caught once similarly with ranges over formfields, as they also technically contain { FORMTEXT }.