views:

57

answers:

2

For example, my docx file contains the following sentences:

This is a Perl example
This is a Python example
This is another Perl example

I want to apply bold style to all the occurrences of the word "Perl" like so:

This is a Perl example
This is a Python example
This is another Perl example

I've so far come up with the following script:

use strict; use warnings;
use Win32::OLE::Const 'Microsoft Word';

my $file = 'E:\test.docx';

my $Word = Win32::OLE->new('Word.Application', 'Quit');
$Word->{'Visible'} = 0;
my $doc = $Word->Documents->Open($file);
my $paragraphs = $doc->Paragraphs() ;
my $enumerate = new Win32::OLE::Enum($paragraphs);


while(defined(my $paragraph = $enumerate->Next())) {

    my $text = $paragraph->{Range}->{Text};
    my $sel = $Word->Selection;
    my $font = $sel->Font;

    if ($text =~ /Perl/){
        $font->{Bold} = 1;              
    }   
    $sel->TypeText($text);          
}

$Word->ActiveDocument->Close ;
$Word->Quit;

But it has applied bold style to the whole paragraph and it does not edit the sentences in their original place. It gives me both the modified version and the original version like this:

This is a Perl example
This is a Python example
This is another Perl example
This is a Perl example
This is a Python example
This is another Perl example

How should I fix my problem. Any pointers? Thanks like always :)

UPDATE

Problem solved! Big thanks to @Zaid, and @cjm :)

Here's the code that works lovely:

while ( defined (my $paragraph = $enumerate->Next()) ) {

    my $words = Win32::OLE::Enum->new( $paragraph->{Range}->{Words} );

    while ( defined ( my $word = $words->Next() ) ) {

        my $font = $word->{Font};
        $font->{Bold} = 1 if $word->{Text} =~ /Perl/;
    }
}
A: 

I dont know anything about perl. But you look at office open xml

You can use treat the .docx file as a zip file, and do a simple search and replace, wich works a million times quicker than the interop. and you dont have to worry about the million things that can go wrong with it also.

Rename your .docx file to .zip and open it and you will see what I mean.

Dimestore Cowboy
+3  A: 

Try using the Words method instead of Text.

Untested:

while ( defined (my $paragraph = $enumerate->Next()) ) {

    my $words = Win32::OLE::Enum->new( $paragraph->{Range}->{Words} );

    while ( defined ( my $word = $words->Next() ) ) {

        my $font = $word->{Font};
        $font->{Bold} = 1 if $word->{Text} =~ /Perl/;
    }
}
Zaid
@Zaid, thanks for the code. But perl throws me the error "Not an ARRAY reference at E:\test.pl line 17". I think it means $paragraph->{Range}->{Words} returns a hashref, rather than an array ref.
Mike
@Mike, try `Win32::OLE::Enum->new($paragraph->{Range}->{Words})`, just like you do for paragraphs. (And I recommend avoiding the indirect object syntax; use `Class->new` instead of `new Class`. When you use indirect object syntax, Perl has to guess whether it's a method call or a normal function, and it can occasionally guess wrong.)
cjm
@Zaid and @cjm, thanks for the guidance. Now this seems closer. But the @words array is not a collection of words, punctuations etc. I use the print @words statement to print the contents of it and I'm receiving something like this: Win32::OLE::Enum=SCALAR(0x19c2f3c). So the contents of @words are neither Hash refs nor are they array refs and I can't use $word->{Font}
Mike
The VB example on MSDN seems simple: For Each aWord In myRange.Words If aWord.Text = "Franklin " Then aWord.DeleteNext aWord. And according to MSDN, Range.Words returns a Words collection that represents all the words in a range. But somehow it does not work as expected using Win32::OLe :( Frustrated
Mike
@Mike : Silly me. The reason why the `@words` doesn't work is because `Win32::OLD::Enum->new()` returns an `Enum` object. Your `$paragraph` example shows how to do it, so I updated the code accordingly. Bear in mind that if this is the only task that you need to perform, there is no need for the `$paragraph` Enum at all. Just iterate over all the words and you're done.
Zaid
@Zaid, thanks again for the guidance :) It's now closer! I'm testing...
Mike
Hahaha, @Zaid, we've made it! Thanks :) I'll be upgrading my post. We still can't directly use the $word =~ /Perl/; statement.
Mike
`$word->{Text} =~ /Perl/`, perhaps?
Zaid
@Zaid, yup! $word->{Text} works lovely :) Thanks for your guidance!
Mike