unit AHWord97;

interface

(*
   These objects are based on the TWordObject demo in the directory
   C:\Program Files\Borland\DelphiX\Demos\Activex\Oleauto\Word8

   This is the Delphi 4/5 version (separate version for Delphi 3)

	 Feel free to add to this unit. My only condition is that you e-mail me your additions
	 and put in small comments indicating updates/changes (eg '<- added by JB 1/3/99')
	 and large comments where things are not obvious.

	 You are free to use this unit in any program, even commercial (although I'd like to know
  about where it is being used). You are not allowed to sell this, or a variation on it, as
  that would be dishonest. If you give away your source code in a program and use this unit,
  please keep this header so bugs/bug-fixes come to me.

	 Dr Allan Harkness
	 Scotland
  e-mail Allan@capturebeat.com

  Latest version available from www.capturebeat.com


  History: see Word97 Hx.txt
  Version 2.2

  NB Event sinking will not work "as advertised" in Word 2000. I think this is because
   MS have finally implemented Word application events properly and my code goes out of its way
   to get round the bugs in version 97. If someone want to test the Word 2000 events (eg using
   the Delphi components) and tell me exactly what causes them to fire, I may be able to change
   the event code to work in Word 2000. I don't have Word 2000 so it's rather hard to test...

  NB If you wish to use Word 2000, make a compiler define for USE_WORD2K
   Either put the following in the interface of appropriate units or better still,
   use Project.Options.Conditional Defines to set this compiler define globally
    {$DEFINE USE_WORD2K}

  NB As far as I am aware, a program compiled "use"ing any of the type libraries
   should be able to talk to Word 97 and Word 2000.
   This is because Word 2000 continues to support the Word 97 interfaces.
   However, I would suggest you compile your program with the new Word2000.pas
   provided in the Delphi 5 update.
   If you define USE_WORD2K then my code will call the old version of some of the Word
   methods (eg AddOld rather than Add). This compiler directive simply tells my code which
   type library you are using - it will not use any new features found in Word 2000.
   If you wish to use the new features in Word 2000 (eg macro parameters),
   you can do so and cover yourself with an $IFDEF to make sure your code works
   for Word 97 users too (if you need to).

  If you make references to the following type libraries in other units, copy the series of $IFDEF's

*)

{$DEFINE USE_WORD2K}

uses
  {$IFDEF USE_WORD2K} Word2000, {$ELSE} Word97, {$ENDIF}
  {$IFDEF USE_WORD2K} Office2000, {$ELSE} Office97, {$ENDIF}
  Windows, Classes, SysUtils, ActiveX, OleCtrls, Forms;


const
  wdTrue : Integer = -1;
  wdFalse : Integer = 0;

type
  TWordDoc = class;
  TWordApp = class;

  TWordDocEvent = procedure (WordApp: TWordApp; WordDoc : TWordDoc) of object;

  TWordEventSink = class(TInterfacedObject, IUnknown, IDispatch)
  private
    FWordApp : TWordApp;
    FAppDispatch: IDispatch;
    FDocDispatch: IDispatch;
    FAppDispIntfIID: TGUID;
    FDocDispIntfIID: TGUID;
    FAppConnection: Integer;
    FDocConnection: Integer;
  protected
    { IUnknown }
    function QueryInterface(const IID: TGUID; out Obj): HRESULT; stdcall;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
    { IDispatch }
    function GetTypeInfoCount(out Count: Integer): HRESULT; stdcall;
    function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HRESULT; stdcall;
    function GetIDsOfNames(const IID: TGUID; Names: Pointer;
      NameCount, LocaleID: Integer; DispIDs: Pointer): HRESULT; stdcall;
    function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
      Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HRESULT; stdcall;
  public
    constructor Create(WordApp : TWordApp; AnAppDispatch: IDispatch; const AnAppDispIntfIID, ADocDispIntfIID: TGUID);
    destructor Destroy; override;
  end;

  TWordApp = class
  private
    FComApp : _Application;
    FComGlobal : _Global;
    FUsedExisting : Boolean;
    FEventSink : TWordEventSink;
    FOnQuit : TNotifyEvent;
    FOnChangeDocument : TWordDocEvent;
    FOnOpenDocument : TWordDocEvent;
    FOnPreCloseDocument : TNotifyEvent;
    FOnCloseDocument : TWordDocEvent;
    function GetCaption : String;
    procedure SetCaption(Value : String);
    function GetZoomPerCent : Integer;
    procedure SetZoomPerCent (Value : Integer);
    function GetVisible : Boolean;
    procedure SetVisible(Value : Boolean);
    function GetScreenUpdating : Boolean;
    procedure SetScreenUpdating (Value : Boolean);
    function GetWindowState : TOleEnum;
    procedure SetWindowState (Value : TOleEnum);
    function GetDocument(Index : Integer) : TWordDoc;
    function GetNoOfDocuments : Integer;
    function GetOnQuit : TNotifyEvent;
    procedure SetOnQuit(Value : TNotifyEvent);
    function GetOnChangeDocument : TWordDocEvent;
    procedure SetOnChangeDocument(Value : TWordDocEvent);
    function GetOnOpenDocument: TWordDocEvent;
    procedure SetOnOpenDocument(Value : TWordDocEvent);
    function GetOnPreCloseDocument: TNotifyEvent;
    procedure SetOnPreCloseDocument(Value : TNotifyEvent);
    function GetOnCloseDocument: TWordDocEvent;
    procedure SetOnCloseDocument(Value : TWordDocEvent);
    procedure FreeDocumentsAndSink;
  protected
    FDocuments : TList;
    procedure RemoveDoc(Index : Integer);
    procedure QuitAppEvent;
    procedure PreCloseDocEvent;
    procedure SyncWithWord;
    procedure ChangeDocEvent;
  public
    constructor Create(UseExisting : Boolean = True; Sink : Boolean = True);
    constructor CreateFromOleObject(OleObject : OleVariant; Sink : Boolean = True);
    destructor Destroy; override;
    destructor CloseApp(oeSaveChanges: TOleEnum);

    procedure DisableSystemCloseBox;
    function AddNewDoc(Template : String) : TWordDoc;
    function AddOpenDoc(DocName : String) : TWordDoc;
    function AddActiveDoc : TWordDoc;
    procedure CloseActiveDoc(oeSaveChanges: TOleEnum);
    procedure InsertFile(FileName : string); overload;
    procedure InsertFile(FileName, Bookmark : string); overload;
    procedure Move(oeUnit : TOleEnum = wdCharacter; Count : Integer = 1);
    procedure MoveEnd(oeUnit : TOleEnum = wdCharacter; Count : Integer = 1);
    procedure MoveStart(oeUnit : TOleEnum = wdCharacter; Count : Integer = 1);
    procedure MoveRight(oeUnit : TOleEnum = wdCharacter; Count : Integer = 1; Extend : TOleEnum = wdMove);
    procedure MoveLeft(oeUnit : TOleEnum = wdCharacter; Count : Integer = 1; Extend : TOleEnum = wdMove);
    procedure MoveUp(oeUnit : TOleEnum = wdLine; Count : Integer = 1; Extend : TOleEnum = wdMove);
    procedure MoveDown(oeUnit : TOleEnum = wdLine; Count : Integer = 1; Extend : TOleEnum = wdMove);
    procedure GotoBookmark(Bookmark : String);
    procedure GoTo_(oeWhat, oeWhich : TOleEnum; oeCount: Integer = 1; oeName: String = '');
    procedure GoToNext(oeWhat : TOleEnum);
    procedure GoToPrevious(oeWhat : TOleEnum);
    procedure UpdateActiveDocFields;
    Procedure RunMacro(MacroName : string);
    procedure ScreenRefresh;
    procedure Cut;
    procedure Copy;
    procedure Paste;
    procedure Activate;
    procedure InsertText(Text : String);
    procedure PrintActiveDoc;
    procedure SaveActiveDocAs(Filename : String);
    procedure ZoomFullPage;
    procedure ZoomFullWidth;
    property Global : _Global read FComGlobal;
    property Application : _Application read FComApp;
    property UsedExisting : Boolean read FUsedExisting;
    property Caption : String read GetCaption write SetCaption;
    property Visible : Boolean read GetVisible write SetVisible;
    property ZoomPerCent : Integer read GetZoomPerCent write SetZoomPerCent;
    property ScreenUpdating : Boolean read GetScreenUpdating write SetScreenUpdating;
    property WindowState : TOleEnum read GetWindowState write SetWindowState;
    property Document [Index: Integer] : TWordDoc read GetDocument;
    property NoOfDocuments : Integer read GetNoOfDocuments;
    property OnQuit : TNotifyEvent read GetOnQuit write SetOnQuit;
    property OnChangeDocument : TWordDocEvent read GetOnChangeDocument write SetOnChangeDocument;
    property OnOpenDocument : TWordDocEvent read GetOnOpenDocument write SetOnOpenDocument;
    property OnPreCloseDocument : TNotifyEvent read GetOnPreCloseDocument write SetOnPreCloseDocument;
    property OnCloseDocument : TWordDocEvent read GetOnCloseDocument write SetOnCloseDocument;
  end;

  TWordRange = class;

  TWordDocMode = (wdmCreating, wdmExisting, wdmDestroying);

  TWordDoc = class
  private
    FComDoc : _Document;
    FWordApp : TWordApp;
    FFullname : String;
    FItemIndex : Integer;
    function GetActive : Boolean;
    procedure SetActive(Value : Boolean);
    function GetRange(Index : Integer) : TWordRange;
    function GetNoOfRanges : Integer;
    function GetNoOfBookMarks : Integer;
    function GetBookmarkByName (BookmarkName: String) : {$IFDEF USE_WORD2K} Word2000.Bookmark {$ELSE} Word97.Bookmark {$ENDIF};
    function GetBookmarkByIndex(Index: Integer) : {$IFDEF USE_WORD2K} Word2000.Bookmark {$ELSE} Word97.Bookmark {$ENDIF};
    procedure SetBuiltInProperty(Index : TOleEnum; Const Value: Variant);
    function GetBuiltInProperty(Index : TOleEnum) : Variant;
    procedure SetCustomProperty(Index : String; Const Value : Variant);
    function GetCustomProperty(Index : String) : Variant;
    function GetRTF : String;
    procedure SetRTF (sRTF : String);
    procedure FreeRangesAndRemoveDoc;
    function GetAutoTextEntries : OleVariant;
  protected
    FMode : TWordDocMode;
    FRanges : TList;
    procedure RemoveRange(Index : Integer);
  public
    constructor CreateNewDoc(WordApp : TWordApp; Template : String);
    constructor CreateOpenDoc(WordApp : TWordApp; FileName : String);
    constructor CreateFromComDoc(WordApp : TWordApp; ComDoc : _Document);
    constructor CreateFromActiveDoc(WordApp : TWordApp);
    destructor Destroy; override;
    destructor CloseDoc(oeSaveChanges: TOleEnum);
    procedure Print;
    procedure PrintPreview;
    function AddRangeFromBookMark(BookmarkName : String) : TWordRange;
    function AddRangeFromSelection : TWordRange;
    function AddRangeFromDoc(iStart : Integer = 1; iEnd : Integer = 1) : TWordRange;
    function AddRangeFromRange(ComRange : Range) : TWordRange;
    function GoTo_(oeWhat, oeWhich : TOleEnum; oeCount: Integer = 1; oeName: String = '') : {$IFDEF USE_WORD2K} Word2000.Range {$ELSE} Word97.Range {$ENDIF};
    function GoToNext(oeWhat : TOleEnum) : {$IFDEF USE_WORD2K} Word2000.Range {$ELSE} Word97.Range {$ENDIF};
    function GoToPrevious(oeWhat : TOleEnum) : {$IFDEF USE_WORD2K} Word2000.Range {$ELSE} Word97.Range {$ENDIF};
    function GoToSection(NumSec: Integer = 1) : {$IFDEF USE_WORD2K} Word2000.Range {$ELSE} Word97.Range {$ENDIF};
    function NoOfPages (IncludeFootnotesAndEndnotes : Boolean = False) : Integer;
    function NoOfWords (IncludeFootnotesAndEndnotes : Boolean = False) : Integer;
    procedure ReplaceBookmark (BookmarkName, ReplaceText : String; ReassignBookmark : Boolean = True);
    function BookmarkExists(BookmarkName : String): Boolean;
    procedure AddCustomProperty(Index: String; Value : Variant; oePropertyType : TOleEnum); overload;
    procedure AddCustomProperty(Index: String; Value : String); overload;
    procedure AddCustomProperty(Index: String; Value : Integer); overload;
    procedure AddCustomProperty(Index: String; Value : Double); overload;
    procedure AddCustomProperty(Index: String; Value : TDateTime); overload;
    procedure AddCustomProperty(Index: String; Value : Boolean); overload;
    procedure DeleteCustomProperty(Index: String);
    procedure DeleteRange(Index : Integer);
    function DocStillInWord : Boolean;
    procedure UpdateFields;
    procedure UpdateFullname;
    procedure SaveAs(Filename : String);
    property WordApp : TWordApp read FWordApp;
    property Document : _Document read FComDoc write FComDoc;
    property Fullname : String read FFullname;
    property Active : Boolean read GetActive write SetActive;
    property AutoTextEntries : OleVariant read GetAutoTextEntries;
    property Range [Index: Integer] : TWordRange read GetRange;
    property NoOfRanges : Integer read GetNoOfRanges;
    property BookmarkByIndex [Index: Integer] : Bookmark read GetBookmarkByIndex;
    property Bookmark [BookmarkName: String] : Bookmark read GetBookmarkByName;
    property NoOfBookmarks : Integer read GetNoOfBookmarks;
    property BuiltInProperty [Index : TOleEnum] : Variant read GetBuiltInProperty write SetBuiltInProperty;
    property CustomProperty [Index : String] : Variant read GetCustomProperty write SetCustomProperty;
    property ItemIndex : Integer read FItemIndex;
    property Mode : TWordDocMode read FMode;
    property RTF : String read GetRTF write SetRTF;
  end;

  TWordRange = class
  private
    FComRange : {$IFDEF USE_WORD2K} Word2000.Range {$ELSE} Word97.Range {$ENDIF};
    FWordDoc : TWordDoc;
    FItemIndex : Integer;
    procedure SetStart(Value : Integer);
    function GetStart : Integer;
    procedure SetEnd(Value : Integer);
    function GetEnd : Integer;
    procedure SetText(Value : String);
    function GetText : String;
    procedure SetBold(Value : Boolean);
    function GetBold : Boolean;
    procedure SetItalic(Value : Boolean);
    function GetItalic : Boolean;
    procedure SetUnderline(Value : Boolean);
    function GetUnderline : Boolean;
    procedure SetCase(oeValue : TOleEnum);
    function GetCase :TOleEnum;
    procedure SetFont(fFont : _Font);
    function GetFont : _Font;
    procedure SetStyle(Style : Variant);
    function GetStyle : Variant;
  protected
  public
    constructor CreateFromBookMark(WordDoc : TWordDoc; BookmarkName : String);
    constructor CreateFromSelection(WordDoc : TWordDoc);
    constructor CreateFromDoc(WordDoc : TWordDoc; iStart : Integer = 1; iEnd : Integer = 1);
    constructor CreateFromRange(WordDoc : TWordDoc; ComRange : {$IFDEF USE_WORD2K} Word2000.Range {$ELSE} Word97.Range {$ENDIF});
    destructor Destroy; override;
    procedure Collapse(oeDirection : TOleEnum = wdCollapseStart);
    function EndOf(oeUnit : TOleEnum = wdWord; oeExtend : TOleEnum = wdMove) : Integer;
    function Expand(oeUnit : TOleEnum = wdWord) : Integer;
    function GoTo_(oeWhat, oeWhich : TOleEnum; oeCount: Integer = 1; oeName: String = '') : {$IFDEF USE_WORD2K} Word2000.Range {$ELSE} Word97.Range {$ENDIF};
    function GotoBookmark(BookmarkName : string) : {$IFDEF USE_WORD2K} Word2000.Range {$ELSE} Word97.Range {$ENDIF};
    function GoToNext(oeWhat : TOleEnum) : {$IFDEF USE_WORD2K} Word2000.Range {$ELSE} Word97.Range {$ENDIF};
    function GoToPrevious(oeWhat : TOleEnum) : {$IFDEF USE_WORD2K} Word2000.Range {$ELSE} Word97.Range {$ENDIF};
    function NoOfWords : Integer;
    procedure InsertAfter(Text : String);
    procedure InsertAutoText;
    procedure InsertGivenAutoText (AutoText : String; UseRichText : Boolean = True);
    procedure InsertBefore(Text : String);
    procedure InsertBreak(oeType : TOleEnum = wdPageBreak);
    procedure InsertParagraph;
    procedure InsertParagraphAfter;
    procedure InsertParagraphBefore;
    procedure InsertSymbol(CharacterNumber: Integer; Font: String;
                            Unicode: Boolean = False; oeBias : TOleEnum = wdFontBiasDefault);
    function Move(oeUnit : TOleEnum = wdCharacter; oeCount : Integer = 1) : Integer;
    function MoveWhile(Cset : String; Count : Integer = wdForward) : Integer;
    function MoveUntil(Cset : String; Count : Integer = wdForward) : Integer;
    function MoveStart(oeUnit : TOleEnum = wdCharacter; oeCount : Integer = 1) : Integer;
    function MoveStartWhile(Cset : String; Count : Integer = wdForward) : Integer;
    function MoveStartUntil(Cset : String; Count : Integer = wdForward) : Integer;
    function MoveEnd(oeUnit : TOleEnum = wdCharacter; oeCount : Integer = 1) : Integer;
    function MoveEndUntil(Cset : String; Count : Integer = wdForward) : Integer;
    function MoveEndWhile(Cset : String; Count : Integer = wdForward) : Integer;
    function Next(oeUnit : TOleEnum = wdCharacter; oeCount : Integer = 1) : {$IFDEF USE_WORD2K} Word2000.Range {$ELSE} Word97.Range {$ENDIF};
    function Previous(oeUnit : TOleEnum = wdCharacter; oeCount : Integer = 1) : {$IFDEF USE_WORD2K} Word2000.Range {$ELSE} Word97.Range {$ENDIF};
    function GetNextRange(oeUnit : TOleEnum = wdCharacter; oeCount : Integer = 1) : {$IFDEF USE_WORD2K} Word2000.Range {$ELSE} Word97.Range {$ENDIF};
    function GetPreviousRange(oeUnit : TOleEnum = wdCharacter; oeCount : Integer = 1) : {$IFDEF USE_WORD2K} Word2000.Range {$ELSE} Word97.Range {$ENDIF};
    procedure SetRange(iStart, iEnd : Integer);
    function StartOf(oeUnit : TOleEnum = wdWord; oeExtend : TOleEnum = wdMove) : Integer;
    procedure CreateBookMark(BookmarkName : String);
    procedure Select;
    procedure Cut;
    procedure Copy;
    procedure Paste;

    property Range : {$IFDEF USE_WORD2K} Word2000.Range {$ELSE} Word97.Range {$ENDIF} read FComRange write FComRange;
    property WordDoc : TWordDoc read FWordDoc;
    property Start : Integer read GetStart write SetStart;
    property End_ : Integer read GetEnd write SetEnd;
    property Text : String read GetText write SetText;
    property Bold : Boolean read GetBold write SetBold;
    property Italic : Boolean read GetItalic write SetItalic;
    property Underline : Boolean read GetUnderline write SetUnderline;
    property Case_ : TOleEnum read GetCase write SetCase;
    property Font : _Font read GetFont write SetFont;
    property Style : Variant read GetStyle write SetStyle;
    property ItemIndex : Integer read FItemIndex;
  end;

function ImprimirDoc(Fichero : String) : Boolean;

implementation

uses
  Variants, ComObj;

{ General purpose routines }

function GetRTFFormat(DataObject: IDataObject; var RTFFormat: TFormatEtc): Boolean;
var
  Formats: IEnumFORMATETC;
  TempFormat: TFormatEtc; 
  cfRTF: LongWord; 
  Found: Boolean; 
begin 
  try 
    OleCheck(DataObject.EnumFormatEtc(DATADIR_GET, Formats)); 
    cfRTF := RegisterClipboardFormat('Rich Text Format'); 
    Found := False; 
    while (not Found) and (Formats.Next(1, TempFormat, nil) = S_OK) do 
      if (TempFormat.cfFormat = cfRTF) then begin 
        RTFFormat := TempFormat; 
        Found := True; 
      end; 
    Result := Found;
  except
    Result := False;
  end; 
end;

{ TWordEventSink implementation }

constructor TWordEventSink.Create(WordApp : TWordApp; AnAppDispatch: IDispatch; const AnAppDispIntfIID, ADocDispIntfIID: TGUID);
begin
  inherited Create;

  FWordApp := WordApp;
  FAppDispIntfIID := AnAppDispIntfIID;
  FDocDispIntfIID := ADocDispIntfIID;
  FAppDispatch := AnAppDispatch;

  // Hook the sink up to the automation server (Word97)
  InterfaceConnect(FAppDispatch,FAppDispIntfIID,Self,FAppConnection);
end;

destructor TWordEventSink.Destroy;
begin
  // Unhook the sink from the automation server (Word97)
  InterfaceDisconnect(FAppDispatch,FAppDispIntfIID,FAppConnection);

  inherited Destroy;
end;

function TWordEventSink.QueryInterface(const IID: TGUID; out Obj): HRESULT;
begin
  // We need to return the two event interfaces when they're asked for
  Result := E_NOINTERFACE;
  if GetInterface(IID,Obj) then
    Result := S_OK;
  if IsEqualGUID(IID,FAppDispIntfIID) and GetInterface(IDispatch,Obj) then
    Result := S_OK;
  if IsEqualGUID(IID,FDocDispIntfIID) and GetInterface(IDispatch,Obj) then
    Result := S_OK;
end;

function TWordEventSink._AddRef: Integer;
begin
// Skeleton implementation
  Result := 2;
end;

function TWordEventSink._Release: Integer;
begin
// Skeleton implementation
  Result := 1;
end;

function TWordEventSink.GetTypeInfoCount(out Count: Integer): HRESULT;
begin
// Skeleton implementation
  Count  := 0;
  Result := S_OK;
end;

function TWordEventSink.GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HRESULT;
begin
// Skeleton implementation
  Result := E_NOTIMPL;
end;

function TWordEventSink.GetIDsOfNames(const IID: TGUID; Names: Pointer;
  NameCount, LocaleID: Integer; DispIDs: Pointer): HRESULT;
begin
// Skeleton implementation
  Result := E_NOTIMPL;
end;

function TWordEventSink.Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
  Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HRESULT;
begin
  // Fire the different event handlers when the different event methods are invoked
  // Only do this if FDocuments has not been destroyed.
  // Usually the "closing events" occur before FreeDocumentsAndSink when TWordApp is
  // destroyed. This results in a clean clear-up. However occasionally the "closing events"
  // occurs after FreeDocumentsAndSink where any reference to FDocuments will cause an
  // exception. I cannot seem to predict/prevent this and may be due to message queues.
  // Therefor in this situation, the event sink will not fire closing/quiting events.
  if FWordApp.FDocuments <> nil then
    case DispID of
      2 : FWordApp.QuitAppEvent;
      3 : begin
            FWordApp.ChangeDocEvent;
            // When we see a document change, we also need to disconnect the
            // sink from the old document, and hook it up to the new document
            InterfaceDisconnect(FDocDispatch,FDocDispIntfIID,FDocConnection);
            try
              // Added check for *any* document before trying to connect to it
              // Otherwise closing the last document causes an exception here
              // Note Word events are unreliable - in my demo:-
              // Closing a document -> DispID 6 and then 3 !!!
              // Opening a document -> DispID 3 but not 5 !!!
              if _Application(FAppDispatch).Documents.Count > 0 then
              begin
                FDocDispatch := _Application(FAppDispatch).ActiveDocument;
                InterfaceConnect(FDocDispatch,FDocDispIntfIID,Self,FDocConnection);
              end;
            except;
            end;
          end;
      {4 : if Assigned(FWordApp.OnNewDocument) then  // never seems to be called
            FWordApp.OnNewDocument(FWordApp, nil);}
      {5 : if Assigned(FWordApp.OnOpenDocument) then // never seems to be called
            FWordApp.OnOpenDocument(FWordApp, nil);}
      6 : FWordApp.PreCloseDocEvent;
    end;
  Result := S_OK;
end;

{ TWordApp implementation }

constructor TWordApp.Create(UseExisting : Boolean = True; Sink : Boolean = True);
var
  TempIUnknown : IUnknown;
  Result: HResult;
begin
  inherited Create;
  FUsedExisting := False;
  if UseExisting then
  begin
    Result := GetActiveObject(CLASS_WordApplication, nil, TempIUnknown);
    if Result = MK_E_UNAVAILABLE then // Word application does not exist
        FComApp := CoWordApplication.Create
    else
    begin // Word application exists
      // make sure no other error occured while trying to get Application class
      OleCheck(Result);
      // get _Application interface from TempIUnknown
      OleCheck(TempIUnknown.QueryInterface(_Application, FComApp));
      FUsedExisting := True; // actually got an existing instance
    end;
  end
  else
      FComApp := CoWordApplication.Create;

  //Now get global object
    {$IFDEF USE_WORD2K}
      FComGlobal := CoWordGlobal.Create;
    {$ELSE}
      FComGlobal := CoGlobal.Create;
    {$ENDIF}

  if Sink then // Create the event sink if required
    FEventSink := TWordEventSink.Create(Self,FComApp,ApplicationEvents,DocumentEvents)
  else
    FEventSink := nil;
  FDocuments := TList.Create;
  if FUsedExisting then SyncWithWord;
end;

constructor TWordApp.CreateFromOleObject(OleObject : OleVariant; Sink : Boolean = True);
begin
  inherited Create;
  FUsedExisting := False;
  try
      FComApp := IDISPATCH (OleObject.Application) as WordApplication;
      {$IFDEF USE_WORD2K}
        FComGlobal := CoWordGlobal.Create;
      {$ELSE}
        FComGlobal := CoGlobal.Create;
      {$ENDIF}
    FUsedExisting := True; // actually got an existing instance
  except
    raise Exception.Create ('Word not active in OLE container');
  end;
  if Sink then
    FEventSink := TWordEventSink.Create(Self,FComApp,ApplicationEvents,DocumentEvents)
  else
    FEventSink := nil;
  FDocuments := TList.Create;
  if FUsedExisting then SyncWithWord;
end;

procedure TWordApp.FreeDocumentsAndSink;
var
  i : Integer;
begin
  // faster to free if we go backward
  // also when a document is destroyed it removes itself from this list
  if FDocuments.Count > 0 then
    for i := FDocuments.Count - 1 downto 0 do
      if Assigned (FDocuments [i]) then
        TWordDoc (FDocuments [i]).Free;
  FEventSink := nil;
  FDocuments.Free;
  FDocuments := nil;
end;

destructor TWordApp.Destroy;
begin
  FreeDocumentsAndSink;
  inherited Destroy;
end;

destructor TWordApp.CloseApp(oeSaveChanges: TOleEnum);
var
  ovSaveChanges,
  OriginalFormat,
  RouteDocument  : OleVariant;
begin
  ovSaveChanges := oeSaveChanges; //wdDoNotSaveChanges
  OriginalFormat := EmptyParam;
  RouteDocument := EmptyParam;
  FComApp.Quit (ovSaveChanges, OriginalFormat, RouteDocument);
  FreeDocumentsAndSink;
  inherited Destroy;
end;

procedure TWordApp.DisableSystemCloseBox;
var
  hWordWIndow : HWND;
  hSysMenu    : HMENU;
begin
  // Find the Word 97 handle
  hWordWindow := FindWindow ('OpusApp', nil);
  if hWordWindow <> 0 then
  begin
    hSysMenu := GetSystemMenu (hWordWindow, false);
    EnableMenuItem (hSysMenu, SC_CLOSE, MF_BYCOMMAND or MF_GRAYED)
  end;
end;

procedure TWordApp.SetCaption(Value : String);
begin
  FComApp.Caption := Value;
end;

function TWordApp.GetCaption : String;
begin
  Result := FComApp.Caption;
end;

procedure TWordApp.ZoomFullPage;
begin
	 FComApp.ActiveWindow.View.Zoom.PageFit := wdPageFitFullPage;
end;

procedure TWordApp.ZoomFullWidth;
begin
	 FComApp.ActiveWindow.View.Zoom.PageFit := wdPageFitBestFit;
end;

procedure TWordApp.SetZoomPerCent (Value : Integer);
begin
	 FComApp.ActiveWindow.View.Zoom.Percentage := Value;
end;

function TWordApp.GetZoomPerCent : Integer;
begin
	 Result := FComApp.ActiveWindow.View.Zoom.Percentage;
end;

procedure TWordApp.SetVisible(Value : Boolean);
begin
  FComApp.Visible := Value;
end;

function TWordApp.GetVisible : Boolean;
begin
  Result := FComApp.Visible;
end;

function TWordApp.GetDocument(Index : Integer) : TWordDoc;
begin
  Assert ((Index >= 0) and (Index < FDocuments.Count),
          'Index out of range for GetDocument (' + IntToStr (Index) + ')');
  GetDocument := TWordDoc (FDocuments [Index]);
end;

function TWordApp.GetNoOfDocuments : Integer;
begin
  GetNoOfDocuments := FDocuments.Count;
end;

procedure TWordApp.RemoveDoc(Index : Integer);
// remove Document object from list (but do not free it)
// should rarely be used as onus then on developer to free this Document object
var
  i : Integer;
  wd : TWordDoc;
begin
  Assert ((Index >= 0) and (Index < FDocuments.Count),
          'Index out of range for RemoveDocument (' + IntToStr (Index) + ')');
  FDocuments.Delete (Index);
  i := Index;
  while i < FDocuments.Count do
  begin
    wd := TWordDoc (FDocuments [i]);
    if Assigned (wd) then wd.FItemIndex := i;
    inc (i);
  end;
end;

function TWordApp.GetOnQuit : TNotifyEvent;
begin
  Result := FOnQuit;
end;

procedure TWordApp.SetOnQuit(Value : TNotifyEvent);
begin
  FOnQuit := Value;
end;

function TWordApp.GetOnChangeDocument : TWordDocEvent;
begin
  Result := FOnChangeDocument;
end;

procedure TWordApp.SetOnChangeDocument(Value : TWordDocEvent);
begin
  FOnChangeDocument := Value;
end;

function TWordApp.GetOnOpenDocument : TWordDocEvent;
begin
  Result := FOnOpenDocument;
end;

procedure TWordApp.SetOnOpenDocument(Value : TWordDocEvent);
begin
  FOnOpenDocument := Value;
end;

function TWordApp.GetOnPreCloseDocument : TNotifyEvent;
begin
  Result := FOnPreCloseDocument;
end;

procedure TWordApp.SetOnPreCloseDocument(Value : TNotifyEvent);
begin
  FOnPreCloseDocument := Value;
end;

function TWordApp.GetOnCloseDocument : TWordDocEvent;
begin
  Result := FOnCloseDocument;
end;

procedure TWordApp.SetOnCloseDocument(Value : TWordDocEvent);
begin
  FOnCloseDocument := Value;
end;

procedure TWordApp.InsertText(Text : String);
begin
  FComApp.Selection.TypeText(Text);
end;

function TWordApp.AddNewDoc(Template : String) : TWordDoc;
var
  wd : TWordDoc;
begin
  wd := TWordDoc.CreateNewDoc (Self, Template);
  AddNewDoc := wd;
end;

function TWordApp.AddOpenDoc (DocName : String) : TWordDoc;
var
  wd : TWordDoc;
begin
  wd := TWordDoc.CreateOpenDoc (Self, DocName);
  AddOpenDoc := wd;
end;

function TWordApp.AddActiveDoc : TWordDoc;
// tries to see if active doc in list & just return it
// else try to get active doc from Word
// if no active doc then return nil and leave list alone
var
  wd : TWordDoc;
  i : Integer;
begin
  wd := nil;
  i := 0;
  while (i < FDocuments.Count) and (wd = nil) do
  begin
    wd := TWordDoc (FDocuments [i]);
    if not wd.Active then wd := nil;
    inc (i)
  end;
  if wd = nil then
  begin
    wd := TWordDoc.CreateFromActiveDoc (Self);
    if wd.FComDoc = nil then
    begin
      wd.Destroy;
      wd := nil
    end
  end;
  AddActiveDoc := wd;
end;

procedure TWordApp.CloseActiveDoc(oeSaveChanges: TOleEnum);
var
	wd : TWordDoc;
begin
  wd := AddActiveDoc;
  if wd <> nil then wd.CloseDoc (oeSaveChanges);
end;

procedure TWordApp.PrintActiveDoc;
begin
  {$IFDEF USE_WORD2K}
    FComApp.PrintOutOld (EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam,
                         EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam,
                         EmptyParam, EmptyParam, EmptyParam)
  {$ELSE}
    FComApp.PrintOut (EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam,
                      EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam,
                      EmptyParam, EmptyParam, EmptyParam)
  {$ENDIF}
end;

procedure TWordApp.SaveActiveDocAs(Filename : String);
var
	wd : TWordDoc;
begin
  wd := AddActiveDoc;
  if wd <> nil then wd.SaveAs (Filename);
end;

procedure TWordApp.QuitAppEvent;
var
  MyDoc : TWordDoc;
  iMyDocs : Integer;
begin
  iMyDocs := FDocuments.Count;
  while iMyDocs > 0 do
  begin
    MyDoc := TWordDoc (FDocuments [iMyDocs - 1]);
    if Assigned (FOnCloseDocument) then FOnCloseDocument (Self, MyDoc);
    if MyDoc.Mode <> wdmDestroying then MyDoc.Free; // will remove document too
    Dec (iMyDocs);
  end;
  if Assigned (FOnQuit) then FOnQuit (Self);
end;

procedure TWordApp.PreCloseDocEvent;
var
	wd : TWordDoc;
begin
  // The pre-close event is really what Word sends as a close event
  // However, it occurs before the document closes. The document is still
  // accessible using COM. If there a changes, Word may display the
  // "Do you want Save ..." dialog
  // If the user cancels, the document won't actually close
  // There is no way to know which document is about to close but usually
  // it is the active document (but not necessarily)
  // If the user tries to quits Word and there are documents present,
  // you will get just one pre-close event and no way to know that actually the
  // whole shebang is about to disappear. Don't you just love Word?
  // This is why no document is passes in this event
  // (Note I update the active document's name here in case this is useful
  //  in the OnClose event, when you can no longer use COM)
  // After such a pre-close event you will get a quit event, however, if
  // there are no documents present when the user quits, you get no events!
  wd := AddActiveDoc;
  if wd <> nil then wd.UpdateFullname;
  if Assigned (FOnPreCloseDocument) then FOnPreCloseDocument (Self);
end;

procedure TWordApp.SyncWithWord;
var
  MyDoc : TWordDoc;
  WdDoc : _Document;
  iWrdDocs, iMyDocs : OleVariant;
begin
  // The change event occurs when many events occur. By looking to see what
  // has actually changed, I can fire off sensible events.
  // First lets see if there are any missing documents
  // If there are, generate a close event for the document and free it
  // NB the COM document is NOT accessible in the close event as Word has
  // already destroyed it by now. To catch a closing document where you can
  // still get at the COM document, see the pre-close event - but NB it is
  // not reliably called by Word
  iMyDocs := 0;
  while iMyDocs < FDocuments.Count do
  begin
    MyDoc := TWordDoc (FDocuments [iMyDocs]);
    iWrdDocs := 1;
    WdDoc := nil;
    while (iWrdDocs <= FComApp.Documents.Count) and (WdDoc = nil) do
    begin
      WdDoc := FComApp.Documents.Item (iWrdDocs);
      if MyDoc.Document <> WdDoc then WdDoc := nil;
      iWrdDocs := iWrdDocs + 1;
    end;
    if WdDoc = nil then
    begin
      if Assigned (FOnCloseDocument) then FOnCloseDocument (Self, MyDoc);
      if MyDoc.Mode <> wdmDestroying then MyDoc.Free;
    end;
    iMyDocs := iMyDocs + 1;
  end;

  // Now lets see if there are any new documents
  // If there are, add them to my list and generate an open doc event
  iWrdDocs := 1;
  while iWrdDocs <= FComApp.Documents.Count do
  begin
    WdDoc := FComApp.Documents.Item (iWrdDocs);
    iMyDocs := 0;
    MyDoc := nil;
    while (iMyDocs < FDocuments.Count) and (MyDoc = nil) do
    begin
      MyDoc := TWordDoc (FDocuments [iMyDocs]);
      if MyDoc.Document <> WdDoc then MyDoc := nil;
      iMyDocs := iMyDocs + 1;
    end;
    if MyDoc = nil then TWordDoc.CreateFromComDoc (Self, WdDoc);
    iWrdDocs := iWrdDocs + 1;
  end;
end;

procedure TWordApp.ChangeDocEvent;
begin
  SyncWithWord;
  if (FComApp.Documents.Count > 0) and (Assigned (FOnChangeDocument)) then
    FOnChangeDocument (Self, AddActiveDoc);
end;

procedure TWordApp.Move(oeUnit : TOleEnum = wdCharacter; Count : Integer = 1);
var
	ovUnit : OleVariant;
	ovCount : OleVariant;
begin
	ovUnit := oeUnit;
	ovCount := Count;

	FComApp.selection.Move(ovUnit, ovCount);
end;

procedure TWordApp.MoveEnd(oeUnit : TOleEnum = wdCharacter; Count : Integer = 1);
var
	ovUnit : OleVariant;
	ovCount : OleVariant;
begin
	ovUnit := oeUnit;
	ovCount := Count;

	FComApp.selection.MoveEnd(ovUnit, ovCount);
end;

procedure TWordApp.MoveStart(oeUnit : TOleEnum = wdCharacter; Count : Integer = 1);
var
	ovUnit : OleVariant;
	ovCount : OleVariant;
begin
	ovUnit := oeUnit;
	ovCount := Count;

	FComApp.selection.MoveStart(ovUnit, ovCount);
end;

procedure TWordApp.MoveLeft(oeUnit : TOleEnum = wdCharacter; Count : Integer = 1; Extend : TOleEnum = wdMove);
var
	ovUnit : OleVariant;
	ovCount : OleVariant;
	ovExtend : OleVariant;
begin
	ovUnit := oeUnit;
	ovCount := Count;
  ovExtend := Extend;

	FComApp.selection.MoveLeft(ovUnit, ovCount, ovExtend);
end;

procedure TWordApp.MoveRight(oeUnit : TOleEnum = wdCharacter; Count : Integer = 1; Extend : TOleEnum = wdMove);
var
	ovUnit : OleVariant;
	ovCount : OleVariant;
	ovExtend : OleVariant;
begin
	ovUnit := oeUnit;
	ovCount := Count;
  ovExtend := Extend;

	FComApp.selection.MoveRight(ovUnit, ovCount, ovExtend);
end;

procedure TWordApp.MoveUp(oeUnit : TOleEnum = wdLine; Count : Integer = 1; Extend : TOleEnum = wdMove);
var
	ovUnit : OleVariant;
	ovCount : OleVariant;
	ovExtend : OleVariant;
begin
	ovUnit := oeUnit;
	ovCount := Count;
  ovExtend := Extend;

	FComApp.selection.MoveUp(ovUnit, ovCount, ovExtend);
end;

procedure TWordApp.MoveDown(oeUnit : TOleEnum = wdLine; Count : Integer = 1; Extend : TOleEnum = wdMove);
var
	ovUnit : OleVariant;
	ovCount : OleVariant;
	ovExtend : OleVariant;
begin
	ovUnit := oeUnit;
	ovCount := Count;
  ovExtend := Extend;

	FComApp.selection.MoveDown(ovUnit, ovCount, ovExtend);
end;

procedure TWordApp.GoTo_(oeWhat, oeWhich: TOleEnum; oeCount: Integer = 1; oeName: String = '');
var
  ovWhat, ovWhich, ovCount, ovName : OleVariant;
begin
  ovWhat := oeWhat;
  ovWhich := oeWhich;
  ovCount := oeCount;
  if oeName = '' then
    ovName := EmptyParam
  else
    ovName := oeName;
  FComApp.Selection.GoTo_ (ovWhat, ovWhich, ovCount, ovName);
end;

procedure TWordApp.GoToNext(oeWhat: TOleEnum);
var
  ovWhat : OleVariant;
begin
  ovWhat := oeWhat;
  FComApp.Selection.GoToNext (ovWhat);
end;

procedure TWordApp.GoToPrevious(oeWhat: TOleEnum);
var
  ovWhat : OleVariant;
begin
  ovWhat := oeWhat;
  FComApp.Selection.GoToPrevious (ovWhat);
end;

procedure TWordApp.GotoBookmark(Bookmark : string);
var
  What : OLEVariant;
  Which : OLEVariant;
  Count : OLEVariant;
  Name : OLEVariant;
begin
  What := wdGoToBookmark;
  Which := EmptyParam;
  Count := EmptyParam;
  Name := Bookmark;

  FComApp.Selection.GoTo_(What, Which, Count, Name);
end;

procedure TWordApp.Cut;
begin
  FComApp.Selection.Cut;
end;

procedure TWordApp.Copy;
begin
  FComApp.Selection.Copy;
end;

procedure TWordApp.Paste;
begin
  FComApp.Selection.Paste;
end;

procedure TWordApp.Activate;
begin
	 FComApp.Activate;
end;

procedure TWordApp.UpdateActiveDocFields;
begin
	 FComApp.ActiveDocument.Fields.Update;
end;

procedure TWordApp.RunMacro(MacroName : string);
begin
  {$IFDEF USE_WORD2K}
    FComApp.RunOld(MacroName);
  {$ELSE}
    FComApp.Run(MacroName);
  {$ENDIF}

  // NB in Word 2000, to run a macro with parameters use late binding, ie
  // OleVariant(WordApp.Application).Run (MacroName, varg1, varg2..varg30)
  // Where WordApp is the name of your TWordApp variable
  // It is not worth using an early bound method call here because of all the
  // optional parameters
end;

procedure TWordApp.ScreenRefresh;
begin
	 FComApp.ScreenRefresh;
end;

function TWordApp.GetScreenUpdating : Boolean;
begin
  GetScreenUpdating := FComApp.ScreenUpdating;
end;

procedure TWordApp.SetScreenUpdating (Value : Boolean);
begin
	FComApp.ScreenUpdating := Value;
end;

function TWordApp.GetWindowState : TOleEnum;
begin
  GetWindowState := FComApp.WindowState;
end;

procedure TWordApp.SetWindowState (Value : TOleEnum);
begin
	FComApp.WindowState := Value;
end;

procedure TWordApp.InsertFile(FileName : string);
var
  oAttachment : OleVariant;
begin
  oAttachment := False;
  Application.Selection.InsertFile(FileName,EmptyParam, EmptyParam,
                                   EmptyParam, oAttachment);
end;

procedure TWordApp.InsertFile(FileName, Bookmark : string);
begin
  GotoBookmark(Bookmark);
  InsertFile(FileName);
end;

{ TWordDoc }

constructor TWordDoc.CreateNewDoc(WordApp : TWordApp; Template: String);
var
  DocTemplate,
  NewTemplate,
  ovDocumentType,
  ovVisible : OleVariant;
begin
  Create;
  FMode := wdmCreating;
  FItemIndex := -1;
  FRanges := TList.Create;
  DocTemplate := Template;
  NewTemplate := False;
  FWordApp := WordApp;
  ovDocumentType := 0;
  ovVisible := True;
  FComDoc := FWordApp.FComApp.Documents.Add (DocTemplate, NewTemplate, ovDocumentType, ovVisible);
  FItemIndex := FWordApp.NoOfDocuments;
  FWordApp.FDocuments.Add (self);
  // The following gives Word the opportunity to send events (eg open/change doc)
  // since it requests the document's Fullname.
  // It is important that the document has been added to the WordApp list so that
  // the event handler sees the document and doesn't try to create it again
  UpdateFullname;
  if Assigned (WordApp.OnOpenDocument) then WordApp.OnOpenDocument (WordApp, Self);
  FMode := wdmExisting;
end;

constructor TWordDoc.CreateOpenDoc(WordApp : TWordApp; FileName: String);
var
  ovFileName : OleVariant;
begin
  Create;
  FMode := wdmCreating;
  FItemIndex := -1;
  FRanges := TList.Create;
  ovFileName := FileName;
  FWordApp := WordApp;
  {$IFDEF USE_WORD2K}
    FComDoc := FWordApp.FComApp.Documents.OpenOld (ovFileName, EmptyParam, EmptyParam, EmptyParam, EmptyParam,
                                                   EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam);
  {$ELSE}
    FComDoc := FWordApp.FComApp.Documents.Open (ovFileName, EmptyParam, EmptyParam, EmptyParam, EmptyParam,
                                                EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam);
  {$ENDIF}
  FItemIndex := FWordApp.NoOfDocuments;
  FWordApp.FDocuments.Add (self);
  // The following gives Word the opportunity to send events (eg open/change doc)
  // since it requests the document's Fullname.
  // It is important that the document has been added to the WordApp list so that
  // the event handler sees the document and doesn't try to create it again
  UpdateFullname;
  if Assigned (WordApp.OnOpenDocument) then WordApp.OnOpenDocument (WordApp, Self);
  FMode := wdmExisting;
end;

constructor TWordDoc.CreateFromComDoc(WordApp : TWordApp; ComDoc : _Document);
begin
  Create;
  FMode := wdmCreating;
  FItemIndex := -1;
  FWordApp := WordApp;
  FComDoc := ComDoc;
  FItemIndex := FWordApp.NoOfDocuments;
  FRanges := TList.Create;
  FWordApp.FDocuments.Add (self);
  // The following gives Word the opportunity to send events (eg open/change doc)
  // since it requests the document's Fullname.
  // It is important that the document has been added to the WordApp list so that
  // the event handler sees the document and doesn't try to create it again
  UpdateFullname;
  if Assigned (WordApp.OnOpenDocument) then WordApp.OnOpenDocument (WordApp, Self);
  FMode := wdmExisting;
end;

constructor TWordDoc.CreateFromActiveDoc(WordApp : TWordApp);
begin
  Create;
  FMode := wdmCreating;
  FItemIndex := -1;
  FWordApp := WordApp;
  FRanges := TList.Create;
  if WordApp.FComApp.Documents.Count = 0 then
    FComDoc := nil // indicates that no valid document
  else
  begin
    FComDoc := FWordApp.FComApp.ActiveDocument;
    FItemIndex := FWordApp.NoOfDocuments;
    FWordApp.FDocuments.Add (self);
    // The following gives Word the opportunity to send events (eg open/change doc)
    // since it requests the document's Fullname.
    // It is important that the document has been added to the WordApp list so that
    // the event handler sees the document and doesn't try to create it again
    UpdateFullname;
    if Assigned (WordApp.OnOpenDocument) then WordApp.OnOpenDocument (WordApp, Self);
  end;
  FMode := wdmExisting;
end;

function TWordDoc.DocStillInWord : Boolean;
// New function to check if document is still loaded in Word
// If user were to close document himself, further calls to FComDoc would
// generate an error. Therefor, you should check for the presence of the
// document before using the object, if user intervention could have occurred
// since the document was first created. This is most likely to be useful if
// you have a button on your program that closes Word or the document.
// NB if the user closes the document while your code is actively filling in
// the document, exceptions will still occur. Unless you check the document before
// every use of TWordDoc, you cannot get round this - you should warn users to leave
// well alone while your program does its magic.
var
  i : OleVariant;
  bPresent : boolean;
  docs : Documents;
begin
  bPresent := False;
  i := 1;
  docs := FWordApp.FComApp.Documents;
  while (not bPresent) and (i <= docs.Count) do
  begin
    if docs.Item (i) = FComDoc then bPresent := True;
    inc (i);
  end;
  Result := bPresent;
end;

procedure TWordDoc.FreeRangesAndRemoveDoc;
var
  i : Integer;
begin
  if Assigned (FRanges) and (FRanges.Count > 0) then
    for i := FRanges.Count - 1 downto 0 do // faster to free if we go backward
      if Assigned (FRanges [i]) then
        TWordRange (FRanges [i]).Free;
  FRanges.Free;
  if FItemIndex <> -1 then FWordApp.RemoveDoc (FItemIndex);
end;

destructor TWordDoc.Destroy;
begin
  FMode := wdmDestroying;
  FreeRangesAndRemoveDoc;
  inherited;
end;

destructor TWordDoc.CloseDoc(oeSaveChanges: TOleEnum);
var
  ovSaveChanges,
  OriginalFormat,
  RouteDocument  : OleVariant;
begin
  FMode := wdmDestroying;
  ovSaveChanges := oeSaveChanges;
  OriginalFormat := EmptyParam;
  RouteDocument := EmptyParam;
  FComDoc.Close (ovSaveChanges, OriginalFormat, RouteDocument);
  Application.ProcessMessages;
  FreeRangesAndRemoveDoc;
  inherited Destroy;
end;

function TWordDoc.GetActive : Boolean;
begin
  if FWordApp.FComApp.ActiveDocument = nil then
    GetActive := False
  else
    GetActive := (FWordApp.FComApp.ActiveDocument = FComDoc);
end;

procedure TWordDoc.SetActive(Value: Boolean);
begin
  FComDoc.Activate;
end;

procedure TWordDoc.Print;
begin
  {$IFDEF USE_WORD2K}
    FComDoc.PrintOutOld (EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam,
                         EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam,
                         EmptyParam, EmptyParam)
  {$ELSE}
    FComDoc.PrintOut (EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam,
                      EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam,
                      EmptyParam, EmptyParam)
  {$ENDIF}
end;

procedure TWordDoc.PrintPreview;
begin
  FComDoc.PrintPreview
end;

procedure TWordDoc.SaveAs(Filename: String);
var
  DocName : OleVariant;
begin
  DocName := FileName;
  FComDoc.SaveAs (DocName, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam,
                  EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam);
  UpdateFullname;
end;

function TWordDoc.GetRTF : String;
var
  DataObject: IDataObject;
  RTFFormat: TFormatEtc;
  ReturnData: TStgMedium;
  iLen : Integer;
  Buffer: PChar;
  sRTF : String;
begin
  FComDoc.QueryInterface(IDataObject, DataObject);
  if GetRTFFormat(DataObject, RTFFormat) then
  begin
    OleCheck(DataObject.GetData(RTFFormat, ReturnData));
    try
      //RTF is passed through global memory
      Buffer := GlobalLock(ReturnData.hglobal);
      //Buffer is a pointer to the RTF text
      iLen := StrLen (Buffer);
      SetLength (sRTF, iLen);
      StrLCopy(PChar(sRTF), Buffer, iLen);
      GetRTF := sRTF;
      OleCheck (DataObject.SetData (RTFFormat, ReturnData, True));
    finally
      GlobalUnlock(ReturnData.hglobal);
    end;
  end;
end;

procedure TWordDoc.SetRTF (sRTF : String);
var
  DataObject: IDataObject;
  RTFFormat: TFormatEtc;
  Medium: TStgMedium;
  iLen : Integer;
  Release : BOOL;
begin
  //get the data object
  OleCheck(FComDoc.QueryInterface(IDataObject, DataObject));
  //get the RTF & MetaFile format
  if GetRTFFormat(DataObject, RTFFormat) then
  begin
    Assert(RTFFormat.cfFormat <> 0);
    //set the data in the Word document (RTF)
    iLen := Length(sRTF) + 1;
    ZeroMemory(@Medium, sizeof(TStgMedium));
    Medium.tymed := RTFFormat.tymed;
    Medium.unkForRelease := nil;
    Medium.hGlobal := GlobalAlloc(GPTR or GMEM_SHARE, iLen);
    CopyMemory(Pointer(Medium.hGlobal), pchar(sRTF), iLen);
    Release := False;
    DataObject.SetData(RTFFormat, Medium, Release);
    GlobalFree(Medium.hGlobal);
  end;
end;

procedure TWordDoc.UpdateFields;
begin
	FComDoc.Fields.Update;
end;

procedure TWordDoc.UpdateFullname;
begin
	FFullname := FComDoc.FullName;
end;

function TWordDoc.AddRangeFromBookMark(BookmarkName : String) : TWordRange;
var
  wr : TWordRange;
begin
  wr := TWordRange.CreateFromBookMark (Self, BookMarkName);
  AddRangeFromBookMark := wr;
end;

function TWordDoc.AddRangeFromSelection : TWordRange;
var
  wr : TWordRange;
begin
  wr := TWordRange.CreateFromSelection (Self);
  AddRangeFromSelection := wr;
end;

function TWordDoc.AddRangeFromDoc(iStart : Integer = 1; iEnd : Integer = 1) : TWordRange;
var
  wr : TWordRange;
begin
  wr := TWordRange.CreateFromDoc (Self, iStart, iEnd);
  AddRangeFromDoc := wr;
end;

function TWordDoc.AddRangeFromRange(ComRange : Range) : TWordRange;
var
  wr : TWordRange;
begin
  wr := TWordRange.CreateFromRange (Self, ComRange);
  AddRangeFromRange := wr;
end;

function TWordDoc.GetRange(Index : Integer) : TWordRange;
begin
  Assert ((Index >= 0) and (Index < FRanges.Count),
          'Index out of range for GetRange (' + IntToStr (Index) + ')');
  GetRange := TWordRange (FRanges [Index]);
end;

function TWordDoc.GetNoOfRanges : Integer;
begin
  GetNoOfRanges := FRanges.Count;
end;

procedure TWordDoc.DeleteRange(Index : Integer);
// remove and free range object
begin
  Assert ((Index >= 0) and (Index < FRanges.Count),
          'Index out of range for DeleteRange (' + IntToStr (Index) + ')');
  TWordRange (FRanges [Index]).Free;
end;

procedure TWordDoc.RemoveRange(Index : Integer);
// remove range object from list (but do not free it)
// should rarely be used as onus then on developer to free this range object
var
  i : Integer;
  wr : TWordRange;
begin
  Assert ((Index >= 0) and (Index < FRanges.Count),
          'Index out of range for RemoveRange (' + IntToStr (Index) + ')');
  FRanges.Delete (Index);
  i := Index;
  while i < FRanges.Count do
  begin
    wr := TWordRange (FRanges [i]);
    if Assigned (wr) then wr.FItemIndex := wr.FItemIndex - 1;
    inc (i);
  end;
end;

function TWordDoc.GoTo_(oeWhat, oeWhich: TOleEnum; oeCount: Integer = 1; oeName: String = '') : range;
var
  ovWhat, ovWhich, ovCount, ovName : OleVariant;
begin
  ovWhat := oeWhat;
  ovWhich := oeWhich;
  ovCount := oeCount;
  if oeName = '' then
    ovName := EmptyParam
  else
    ovName := oeName;
  GoTo_ := FComDoc.Range (EmptyParam, EmptyParam).GoTo_ (ovWhat, ovWhich, ovCount, ovName);
end;

function TWordDoc.GoToNext(oeWhat: TOleEnum) : {$IFDEF USE_WORD2K} Word2000.Range {$ELSE} Word97.Range {$ENDIF};
var
  ovWhat : OleVariant;
begin
  ovWhat := oeWhat;
  GotoNext := FComDoc.Range (EmptyParam, EmptyParam).GoToNext (ovWhat);
end;

function TWordDoc.GoToPrevious(oeWhat: TOleEnum) : {$IFDEF USE_WORD2K} Word2000.Range {$ELSE} Word97.Range {$ENDIF};
var
  ovWhat : OleVariant;
begin
  ovWhat := oeWhat;
  GotoPrevious := FComDoc.Range (EmptyParam, EmptyParam).GoToPrevious (ovWhat);
end;

function TWordDoc.GoToSection(NumSec: Integer = 1) : {$IFDEF USE_WORD2K} Word2000.Range {$ELSE} Word97.Range {$ENDIF};
var
  ovWhat : OleVariant;
  ovWhich : OleVariant;
  ovCount : OleVariant;
begin
  ovWhat := wdGoToSection;
  ovWhich := wdGoToFirst;
  ovCount := NumSec;
  GoToSection := FComDoc.Range(EmptyParam, EmptyParam).GoTo_(ovWhat, ovWhich, ovCount, EmptyParam);
end;

function TWordDoc.NoOfPages (IncludeFootnotesAndEndnotes : Boolean = False) : Integer;
var
  ovIncudeHF : OleVariant;
begin
  ovIncudeHF := IncludeFootnotesAndEndnotes;
  NoOfPages := FComDoc.ComputeStatistics (wdStatisticPages, ovIncudeHF)
end;

function TWordDoc.NoOfWords (IncludeFootnotesAndEndnotes : Boolean = False) : Integer;
var
  ovIncudeHF : OleVariant;
begin
  ovIncudeHF := IncludeFootnotesAndEndnotes;
  NoOfWords := FComDoc.ComputeStatistics (wdStatisticWords, ovIncudeHF)
end;

function TWordDoc.GetNoOfBookMarks : Integer;
begin
  GetNoOfBookMarks := FComDoc.Bookmarks.Count;
end;

function TWordDoc.GetBookmarkByIndex (Index: Integer) : {$IFDEF USE_WORD2K} Word2000.Bookmark {$ELSE} Word97.Bookmark {$ENDIF};
var
  ovBookMarkName : OleVariant;
begin
  Result := nil;
  try
    ovBookMarkName := Index;
    Result := FComDoc.Bookmarks.Item(ovBookMarkName);
  except
    raise Exception.CreateFmt ('Bookmark No %d not found in document "%s"',
                               [Index, FComDoc.Name]);
  end;
end;

function TWordDoc.GetBookmarkByName (BookmarkName: String) : {$IFDEF USE_WORD2K} Word2000.Bookmark {$ELSE} Word97.Bookmark {$ENDIF};
var
  ovBookMarkName : OleVariant;
begin
  Result := nil;
  try
    ovBookMarkName := BookmarkName;
    Result := FComDoc.Bookmarks.Item(ovBookMarkName);
  except
    raise Exception.CreateFmt ('Bookmark "%s" not found in document "%s"',
                               [BookmarkName, FComDoc.Name]);
  end;
end;

procedure TWordDoc.ReplaceBookmark (BookmarkName, ReplaceText : String; ReassignBookmark : Boolean = True);
var
  ovBookMarkName : OleVariant;
  TempRange : {$IFDEF USE_WORD2K} Word2000.Range {$ELSE} Word97.Range {$ENDIF};
  ovRange : OleVariant;
begin
  ovBookMarkName := BookMarkName;
  try
    TempRange := FComDoc.Bookmarks.Item (ovBookMarkName).Range;
    TempRange.Text := ReplaceText;
    ovRange := TempRange;
    if ReassignBookMark then FComDoc.Bookmarks.Add (BookmarkName, ovRange);
  except
    raise Exception.CreateFmt ('Bookmark "%s" not found in document "%s"',
                               [BookMarkName, FComDoc.Name]);
  end;
end;

//added by BDP 07/22/1999
function TWordDoc.BookmarkExists(BookmarkName : String): Boolean;
begin
	if FComDoc.Bookmarks.Exists(BookmarkName) then
		Result := True
	else
		Result := False;
end;

function TWordDoc.GetAutoTextEntries : OleVariant;
begin
	 GetAutoTextEntries := FComDoc.Get_AttachedTemplate.AutoTextEntries;
end;

procedure TWordDoc.SetBuiltInProperty(Index : TOleEnum; Const Value: Variant);
//added by BDP 05/08/1999 amended by AH 26/11/99
var
  DocumentProperties : OLEVariant;
  DocumentProperty   : OLEVariant;
  ovValue            : OLEVariant;
begin
  try
    ovValue := Value;
    DocumentProperties := FComDoc.BuiltInDocumentProperties;
    DocumentProperty := DocumentProperties.Item [Index];
    DocumentProperty.Value := ovValue;
  except
    raise Exception.CreateFmt ('Error setting document property: "%d"', [Index]);
  end;
end;

function TWordDoc.GetBuiltInProperty(Index : TOleEnum) : Variant;
//added by BDP 05/08/1999 amended by AH 26/11/99
var
  DocumentProperties : OLEVariant;
  DocumentProperty   : OLEVariant;
begin
  try
    DocumentProperties := FComDoc.BuiltInDocumentProperties;
    DocumentProperty := DocumentProperties.Item [Index];
    Result := DocumentProperty.Value;
  except
    raise Exception.CreateFmt ('Error getting document property: "%d"', [Index]);
  end;
end;

procedure TWordDoc.SetCustomProperty(Index : String; Const Value : Variant);
//added by BDP 05/08/1999 amended by AH, bug fix 7/2/00
var
  CustomProperties : OLEVariant;
  CustomProperty   : OLEVariant;
  ovValue          : OLEVariant;
begin
  try
    CustomProperties := FComDoc.CustomDocumentProperties;
    CustomProperty := CustomProperties.Item [Index];
    ovValue := Value;
    CustomProperty.Value := ovValue;
  except
    raise Exception.CreateFmt ('Error setting custom property: "%s"', [Index]);
  end;
end;

function TWordDoc.GetCustomProperty(Index : String) : Variant;
//added by BDP 05/08/1999
var
  CustomProperties : OLEVariant;
  CustomProperty   : OLEVariant;
begin
  try
    CustomProperties := FComDoc.CustomDocumentProperties;
    CustomProperty := CustomProperties.Item [Index];
    Result := CustomProperty.Value;
  except
    raise Exception.CreateFmt ('Error getting custom property: "%s"', [Index]);
  end;
end;

procedure TWordDoc.AddCustomProperty(Index: String; Value : Variant; oePropertyType : TOleEnum);
var
  CustomProperties : OLEVariant;
  ovValue          : OLEVariant;
begin
  try
    CustomProperties := FComDoc.CustomDocumentProperties;
    ovValue := Value;
    CustomProperties.Add(Index, wdFalse, oePropertyType, ovValue);
  except
    raise Exception.CreateFmt ('Error creating custom property: "%s"', [Index]);
  end;
end;

procedure TWordDoc.AddCustomProperty(Index: String; Value : String);
begin
  AddCustomProperty (Index, Value, msoPropertyTypeString);
end;

procedure TWordDoc.AddCustomProperty(Index: String; Value : Integer);
begin
  AddCustomProperty (Index, Value, msoPropertyTypeNumber);
end;

procedure TWordDoc.AddCustomProperty(Index: String; Value : Double);
begin
  AddCustomProperty (Index, Value, msoPropertyTypeFloat);
end;

procedure TWordDoc.AddCustomProperty(Index: String; Value : TDateTime);
begin
  AddCustomProperty (Index, Value, msoPropertyTypeDate);
end;

procedure TWordDoc.AddCustomProperty(Index: String; Value : Boolean);
begin
  AddCustomProperty (Index, Value, msoPropertyTypeBoolean);
end;

procedure TWordDoc.DeleteCustomProperty(Index: String);
var
  CustomProperties : OLEVariant;
  CustomProperty   : OLEVariant;
begin
  try
    CustomProperties := FComDoc.CustomDocumentProperties;
    CustomProperty := CustomProperties.Item [Index];
    if not VarIsNull (CustomProperty) then CustomProperty.Delete;
  except
    raise Exception.CreateFmt ('Error deleting custom property: "%s"', [Index]);
  end;
end;

{ TWordRange }

procedure TWordRange.Collapse(oeDirection: TOleEnum = wdCollapseStart);
var
  ovDirection : OleVariant;
begin
  ovDirection := oeDirection;
  FComRange.Collapse (ovDirection);
end;

constructor TWordRange.CreateFromBookMark(WordDoc: TWordDoc; BookmarkName: String);
var
  ovBookMarkName : OleVariant;
begin
  Create;
  FItemIndex := -1;
  ovBookMarkName := BookMarkName;
  FWordDoc := WordDoc;
  try
    FComRange := FWordDoc.FComDoc.Bookmarks.Item (ovBookMarkName).Range;
    FItemIndex := FWordDoc.NoOfRanges;
    FWordDoc.FRanges.Add (self);
  except
    FComRange := nil;
    raise Exception.CreateFmt ('Bookmark "%s" not found in document "%s"',
                               [BookMarkName, FWordDoc.FComDoc.Name]);
    // NB raising an exception here will cause TWordRange.Destroy to be called
    // The exception will be from trying to get an invalid bookmark
    // ie before ItemIndex is given a valid number, hence preset to -1
    // TWordRange.Destroy will only try to remove the range from the TWordDoc list
    // if ItemIndex <> -1
  end
end;

constructor TWordRange.CreateFromSelection(WordDoc : TWordDoc);
begin
  Create;
  FItemIndex := -1;
  FWordDoc := WordDoc;
  FComRange := FWordDoc.WordApp.Application.Selection.Range;
  FItemIndex := FWordDoc.NoOfRanges;
  FWordDoc.FRanges.Add (self);
end;

constructor TWordRange.CreateFromDoc(WordDoc : TWordDoc; iStart : Integer = 1; iEnd : Integer = 1);
var
  ovStart, ovEnd : OleVariant;
begin
  Create;
  FItemIndex := -1;
  ovStart := iStart;
  ovEnd := iEnd;
  FWordDoc := WordDoc;
  FComRange := FWordDoc.FComDoc.Range (ovStart, ovEnd);
  FItemIndex := FWordDoc.NoOfRanges;
  FWordDoc.FRanges.Add (self);
end;

constructor TWordRange.CreateFromRange(WordDoc : TWordDoc; ComRange : {$IFDEF USE_WORD2K} Word2000.Range {$ELSE} Word97.Range {$ENDIF});
begin
  Create;
  FItemIndex := -1;
  FWordDoc := WordDoc;
  FComRange := ComRange;
  FItemIndex := FWordDoc.NoOfRanges;
  FWordDoc.FRanges.Add (self);
end;

destructor TWordRange.Destroy;
begin
  if FItemIndex <> -1 then FWordDoc.RemoveRange (FItemIndex);
  inherited;
end;

function TWordRange.EndOf(oeUnit : TOleEnum = wdWord; oeExtend : TOleEnum = wdMove) : Integer;
var
  ovUnit,
  ovExtend : OleVariant;
begin
  ovUnit := oeUnit;
  ovExtend := oeExtend;
  EndOf := FComRange.EndOf (ovUnit, ovExtend);
end;

function TWordRange.Expand(oeUnit: TOleEnum = wdWord) : Integer;
var
  ovUnit : OleVariant;
begin
  ovUnit := oeUnit;
  Expand := FComRange.Expand (ovUnit);
end;

function TWordRange.GetBold: Boolean;
begin
  GetBold := FComRange.Bold  = wdTrue;
end;

function TWordRange.GetCase: TOleEnum;
begin
  GetCase := FComRange.Case_
end;

function TWordRange.GetEnd: Integer;
begin
  GetEnd := FComRange.End_;
end;

function TWordRange.GetFont: _Font;
begin
  GetFont := FComRange.Font;
end;

function TWordRange.GetItalic: Boolean;
begin
  GetItalic := FComRange.Italic  = wdTrue
end;

function TWordRange.GetStart: Integer;
begin
  GetStart := FComRange.Start;
end;

function TWordRange.GetStyle: Variant;
begin
  GetStyle := FComRange.Get_Style;
end;

function TWordRange.GetText: String;
begin
  GetText := FComRange.Text;
end;

function TWordRange.GetUnderline: Boolean;
begin
  GetUnderline := FComRange.Underline  = wdTrue
end;

function TWordRange.GoTo_(oeWhat, oeWhich: TOleEnum; oeCount: Integer = 1; oeName: String = '') : {$IFDEF USE_WORD2K} Word2000.Range {$ELSE} Word97.Range {$ENDIF};
var
  ovWhat, ovWhich, ovCount, ovName : OleVariant;
begin
  ovWhat := oeWhat;
  ovWhich := oeWhich;
  ovCount := oeCount;
  if oeName = '' then
    ovName := EmptyParam
  else
    ovName := oeName;
  FComRange := FComRange.GoTo_ (ovWhat, ovWhich, ovCount, ovName);
  Goto_ := FComRange;
end;

function TWordRange.GotoBookmark(BookmarkName : string) : {$IFDEF USE_WORD2K} Word2000.Range {$ELSE} Word97.Range {$ENDIF};
var
	What : OLEVariant;
  Which : OLEVariant;
  Count : OLEVariant;
  Name : OLEVariant;
begin
	What := wdGoToBookmark;
  Which := EmptyParam;
	Count := EmptyParam;
  Name := BookmarkName;
  try
    FComRange := FComRange.GoTo_(What, Which, Count, Name);
  except
    FComRange := nil;
    raise Exception.CreateFmt ('Bookmark "%s" not found in document "%s"',
                               [BookMarkName, FWordDoc.FComDoc.Name]);
  end;
  GotoBookmark := FComRange;
end;

function TWordRange.GoToNext(oeWhat: TOleEnum) : {$IFDEF USE_WORD2K} Word2000.Range {$ELSE} Word97.Range {$ENDIF};
// moves range to next
var
  ovWhat : OleVariant;
begin
  ovWhat := oeWhat;
  FComRange := FComRange.GoToNext (ovWhat);
  GotoNext := FComRange;
end;

function TWordRange.GoToPrevious(oeWhat: TOleEnum) : {$IFDEF USE_WORD2K} Word2000.Range {$ELSE} Word97.Range {$ENDIF};
// moves range to previous
var
  ovWhat : OleVariant;
begin
  ovWhat := oeWhat;
  FComRange := FComRange.GoToPrevious (ovWhat);
  GotoPrevious := FComRange;
end;

function TWordRange.NoOfWords : Integer;
begin
  NoOfWords := FComRange.ComputeStatistics (wdStatisticWords)
end;

procedure TWordRange.InsertAfter(Text: String);
begin
  FComRange.InsertAfter (Text);
end;

procedure TWordRange.InsertAutoText;
begin
  FComRange.InsertAutoText
end;

procedure TWordRange.InsertGivenAutoText (AutoText : String; UseRichText : Boolean = True);
var
  ATemplate : OleVariant;
  AnAutoText : OleVariant;
begin
  // NB Word_TLB import seems to fail with Get_AttachedTemplate and returns an OleVariant
  // rather than a Template type. Therefor this bit of code is late bound and may not work in
  // other languages - sorry
  try
    ATemplate := FWordDoc.FComDoc.Get_AttachedTemplate;
    AnAutoText := ATemplate.AutoTextEntries.Item (AutoText);
    AnAutoText.Insert (FComRange, UseRichText);
  except
    on E : EOleSysError do
      if E.ErrorCode = 5941 then
        raise Exception.CreateFmt ('Error Autotext "%s" not found in template "%s"',
                                  [AutoText, ATemplate.Name])
      else
        raise Exception.CreateFmt ('Error inserting Autotext "%s" from template "%s"',
                                  [AutoText, ATemplate.Name])
  end;
end;

procedure TWordRange.InsertBefore(Text: String);
begin
  FComRange.InsertBefore (Text);
end;

procedure TWordRange.InsertBreak(oeType: TOleEnum = wdPageBreak);
var
  ovType : OleVariant;
begin
  ovType := oeType;
  FComRange.InsertBreak (ovType);
end;

procedure TWordRange.InsertParagraph;
begin
  FComRange.InsertParagraph
end;

procedure TWordRange.InsertParagraphAfter;
begin
  FComRange.InsertParagraphAfter
end;

procedure TWordRange.InsertParagraphBefore;
begin
  FComRange.InsertParagraphBefore
end;

procedure TWordRange.InsertSymbol(CharacterNumber: Integer; Font: String;
                                  Unicode: Boolean = False; oeBias : TOleEnum = wdFontBiasDefault);
var
  ovFont, ovUnicode, ovBias : OleVariant;
begin
  ovFont := Font;
  ovUnicode := UniCode;
  ovBias := oeBias;
  FComRange.InsertSymbol (CharacterNumber, ovFont, ovUnicode, ovBias);
end;

function TWordRange.Move(oeUnit: TOleEnum = wdCharacter; oeCount: Integer = 1) : Integer;
var
  ovUnit,
  ovCount : OleVariant;
begin
  ovUnit := oeUnit;
  ovCount := oeCount;
  Move := FComRange.Move (ovUnit, ovCount);
end;

function TWordRange.MoveEnd(oeUnit: TOleEnum = wdCharacter; oeCount: Integer = 1) : Integer;
var
  ovUnit,
  ovCount : OleVariant;
begin
  ovUnit := oeUnit;
  ovCount := oeCount;
  MoveEnd := FComRange.MoveEnd (ovUnit, ovCount);
end;

function TWordRange.MoveEndUntil(Cset: String; Count: Integer = wdForward) : Integer;
var
  ovCset, ovCount : OleVariant;
begin
  ovCset := Cset;
  ovCount := Count;
  MoveEndUntil := FComRange.MoveEndUntil (ovCset, ovCount);
end;

function TWordRange.MoveEndWhile(Cset: String; Count: Integer = wdForward) : Integer;
var
  ovCset, ovCount : OleVariant;
begin
  ovCset := Cset;
  ovCount := Count;
  MoveEndWhile := FComRange.MoveEndWhile (ovCset, ovCount);
end;

function TWordRange.MoveStart(oeUnit: TOleEnum = wdCharacter; oeCount: Integer = 1) : Integer;
var
  ovUnit,
  ovCount : OleVariant;
begin
  ovUnit := oeUnit;
  ovCount := oeCount;
  MoveStart := FComRange.MoveStart (ovUnit, ovCount);
end;

function TWordRange.MoveStartUntil(Cset: String; Count: Integer = wdForward) : Integer;
var
  ovCset, ovCount : OleVariant;
begin
  ovCset := Cset;
  ovCount := Count;
  MoveStartUntil := FComRange.MoveStartUntil (ovCset, ovCount);
end;

function TWordRange.MoveStartWhile(Cset: String; Count: Integer = wdForward) : Integer;
var
  ovCset, ovCount : OleVariant;
begin
  ovCset := Cset;
  ovCount := Count;
  MoveStartWhile := FComRange.MoveStartWhile (ovCset, ovCount);
end;

function TWordRange.MoveUntil(Cset: String; Count: Integer = wdForward) : Integer;
var
  ovCset, ovCount : OleVariant;
begin
  ovCset := Cset;
  ovCount := Count;
  MoveUntil := FComRange.MoveUntil (ovCset, ovCount);
end;

function TWordRange.MoveWhile(Cset: String; Count: Integer = wdForward) : Integer;
var
  ovCset, ovCount : OleVariant;
begin
  ovCset := Cset;
  ovCount := Count;
  MoveWhile := FComRange.MoveWhile (ovCset, ovCount);
end;

function TWordRange.Next(oeUnit: TOleEnum = wdCharacter; oeCount: Integer = 1) : {$IFDEF USE_WORD2K} Word2000.Range {$ELSE} Word97.Range {$ENDIF};
// moves range to next
var
  ovUnit,
  ovCount : OleVariant;
begin
  ovUnit := oeUnit;
  ovCount := oeCount;
  FComRange := FComRange.Next (ovUnit, ovCount);
  Next := FComRange;
end;

function TWordRange.Previous(oeUnit: TOleEnum = wdCharacter; oeCount: Integer = 1) : {$IFDEF USE_WORD2K} Word2000.Range {$ELSE} Word97.Range {$ENDIF};
// moves range to previous
var
  ovUnit,
  ovCount : OleVariant;
begin
  ovUnit := oeUnit;
  ovCount := oeCount;
  FComRange := FComRange.Previous (ovUnit, ovCount);
  Previous := FComRange;
end;

function TWordRange.GetNextRange(oeUnit: TOleEnum = wdCharacter; oeCount: Integer = 1) : {$IFDEF USE_WORD2K} Word2000.Range {$ELSE} Word97.Range {$ENDIF};
// returns a range next to this one (does not alter this range)
var
  ovUnit,
  ovCount : OleVariant;
begin
  ovUnit := oeUnit;
  ovCount := oeCount;
  GetNextRange := FComRange.Next (ovUnit, ovCount);
end;

function TWordRange.GetPreviousRange(oeUnit: TOleEnum = wdCharacter; oeCount: Integer = 1) : {$IFDEF USE_WORD2K} Word2000.Range {$ELSE} Word97.Range {$ENDIF};
// returns a range prior to this one (does not alter this range)
var
  ovUnit,
  ovCount : OleVariant;
begin
  ovUnit := oeUnit;
  ovCount := oeCount;
  GetPreviousRange := FComRange.Previous (ovUnit, ovCount);
end;

procedure TWordRange.SetBold(Value: Boolean);
begin
  if Value then FComRange.Bold := wdTrue else FComRange.Bold := wdFalse;
end;

procedure TWordRange.SetCase(oeValue: TOleEnum);
begin
  FComRange.Case_ := oeValue;
end;

procedure TWordRange.SetEnd(Value: Integer);
begin
  FComRange.End_ := Value;
end;

procedure TWordRange.SetFont(fFont: _Font);
begin
  FComRange.Font := fFont;
end;

procedure TWordRange.SetItalic(Value: Boolean);
begin
  if Value then FComRange.Italic := wdTrue else FComRange.Italic := wdFalse;
end;

procedure TWordRange.SetRange(iStart, iEnd: Integer);
begin
  FComRange.SetRange (iStart, iEnd);
end;

procedure TWordRange.SetStart(Value: Integer);
begin
  FComRange.Start := Value;
end;

procedure TWordRange.SetStyle(Style: Variant);
var
  ovStyle : OleVariant;
begin
  ovStyle := Style;
  FComRange.Set_Style (ovStyle);
end;

procedure TWordRange.SetText(Value: String);
begin
  FComRange.Text := Value;
end;

procedure TWordRange.SetUnderline(Value: Boolean);
begin
  if Value then FComRange.Underline := wdTrue else FComRange.Underline := wdFalse;
end;

function TWordRange.StartOf(oeUnit : TOleEnum = wdWord; oeExtend : TOleEnum = wdMove): Integer;
var
  ovUnit,
  ovExtend : OleVariant;
begin
  ovUnit := oeUnit;
  ovExtend := oeExtend;
  StartOf := FComRange.StartOf (ovUnit, ovExtend);
end;

procedure TWordRange.CreateBookMark(BookmarkName : String);
var
  ovRange : OleVariant;
begin
  ovRange := FComRange;
  FWordDoc.FComDoc.Bookmarks.Add (BookmarkName, ovRange);
end;

procedure TWordRange.Select;
begin
  FComRange.Select;
end;

procedure TWordRange.Cut;
begin
  FComRange.Cut;
end;

procedure TWordRange.Copy;
begin
  FComRange.Copy;
end;
procedure TWordRange.Paste;
begin
  FComRange.Paste;
end;

function ImprimirDoc(Fichero : String) : Boolean;
var
  FWordApp : TWordApp;
  FWordDoc : TWordDoc;
  Dialogo : Dialog;
begin
  Result := False;
  FWordApp := TWordApp.Create(False, True);
  try
    FWordDoc := TWordDoc.CreateOpenDoc(FWordApp, Fichero);
    FWordApp.Visible := True;
    FWordDoc.Active := True;
    FWordApp.Application.Options.PrintBackground := False;
    Dialogo := FWordApp.Application.Dialogs.Item(wdDialogFilePrint);
    Dialogo.Show(EmptyParam);
    // Esperamos a que Word termine de imprimir antes de cerrarlo.
    while FWordApp.Application.BackgroundPrintingStatus = 1 do;

    FWordDoc.CloseDoc(wdDoNotSaveChanges);
    FWordDoc := NIL;
  finally
    FWordApp.CloseApp (wdDoNotSaveChanges);
    FWordApp := NIL;
  end;
  Result := True;
end;

end.


