OLE Automation: как копировать текст между документами Word без использования буфера обмена

Когда я делаю автоматизацию Word из Delphi XE, у меня одновременно открываются два документа. Я хочу скопировать содержимое заданного диапазона одного документа в другой диапазон другого документа. Как я могу это сделать?

Рассмотрим следующий код:

procedure TForm1.ManipulateDocuments;
var
  vDoc1,vDoc2 : TWordDocument;
  vFilename : olevariant;
  vRange1,vRange2 : Range;
begin
  vDoc1 := TWordDocument.Create(nil);
  vDoc2 := TWordDocument.Create(nil);
  try
    vFilename := 'c:\temp\test1.doc';
    vDoc1.ConnectTo(FWordApp.Documents.Open(vFilename,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam));

    vFilename := 'c:\temp\test2.doc';
    vDoc2.ConnectTo(FWordApp.Documents.Open(vFilename,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam));

    vRange1 := GetSourceRange(vDoc1);
    vRange2 := GetDestinationRange(vDoc2);

    vRange2.CONTENTS := vRange1.CONTENTS; //What should I substitute for CONTENTS?
  finally
    vDoc1.Free;
    vDoc2.Free;
  end;
end;

Могу ли я чем-то заменить СОДЕРЖАНИЕ? Я не могу использовать текст, так как хочу скопировать форматирование, закладки, коды полей и т. Д. Нужно ли мне вообще делать это по-другому? Какие-либо предложения?


person Svein Bringsli    schedule 22.03.2011    source источник


Ответы (4)


Я не знаю способа для более ранних версий Word, но для более новых версий (2007 и новее) вы можете экспортировать диапазон из документа в файл фрагмента, а затем импортировать его из другого документа. Если вам нужна ранняя привязка, вам может потребоваться импортировать библиотеку типов (msword.olb), я не знаю, есть ли она в Delphi XE. В противном случае код мог бы выглядеть так:

function GetTempFileName(Prefix: string): string;
begin
  SetLength(Result, MAX_PATH);
  GetTempPath(MAX_PATH, PChar(Result));
  windows.GetTempFileName(PChar(Result), PChar(Prefix), 0, PChar(Result));
end;

procedure TForm2.Button1Click(Sender: TObject);
const
//  wdFormatDocument = 0;
  wdFormatRTF = $00000006;
var
  WordApp : OleVariant;
  fragment: string;
  vDoc1, vDoc2: OleVariant;
  vRange1, vRange2: OleVariant;
begin
  try
    WordApp := GetActiveOleObject('Word.Application');
  except
    WordApp := CreateOleObject('Word.Application');
  end;
  WordApp.Visible := True;

  vDoc1 := WordApp.Documents.Open(ExtractFilePath(Application.ExeName) + 'test1.doc');
  vRange1 := vDoc1.Range(20, 120);     // the export range
  fragment := GetTempFileName('frg');
  vRange1.ExportFragment(fragment, wdFormatRTF);
  try
    vDoc2 := WordApp.Documents.Open(ExtractFilePath(Application.ExeName) + 'test2.doc');
    vRange2 := vDoc2.Range(15, 15);    // where to import
    vRange2.ImportFragment(fragment);
  finally
    DeleteFile(fragment);
  end;
end;

В моем тесте формат «документ» вызывал ошибку (что-то вроде невозможности вставить форматирование XML), следовательно, использовался формат RTF.

изменить:

В более ранних версиях кажется возможным вставить именованный выбор из одного документа в выбор в другом документе. Результат кажется не идеальным с точки зрения форматирования, если один из выделенных фрагментов находится в середине некоторого текста. Но в остальном вроде работает хорошо.

  ...
  WordApp.Visible := True;

  vDoc1 := WordApp.Documents.Open(ExtractFilePath(Application.ExeName) + 'test1.doc');
  vRange1 := vDoc1.Range(20, 188);                 // the transfer range
  vDoc1.Bookmarks.Add('TransferSection', vRange1); // arbitrary bookmark name

  vDoc2 := WordApp.Documents.Open(ExtractFilePath(Application.ExeName) + 'test2.doc');
  vRange2 := vDoc2.Range(103, 104);           // where to import the bookmark
  vRange2.Select;
  vDoc2.ActiveWindow.Selection.InsertFile(vDoc1.FullName, 'TransferSection');

  vDoc1.Bookmarks.Item('TransferSection').Delete; // no need for the bookmark anymore
 
person Sertac Akyuz    schedule 24.03.2011
comment
Я заметил эти функции (exportfragment / importfragment), и они идеально подходят для моих нужд. К сожалению, Word 2007 мне не подходит. Наше решение должно быть доступно для пользователей Word 2003, может быть, даже раньше, где эти функции были недоступны. Но все равно спасибо. Лучшее предложение на данный момент :-) - person Svein Bringsli; 24.03.2011
comment
Обновление: поскольку других ответов не было, я приму этот как лучший. Тем не менее меня беспокоит, что нет хорошего способа сделать это без использования буфера обмена или использования внешних файлов. Это кажется таким простым: - / - person Svein Bringsli; 04.04.2011
comment
@Svein - Я обновил ответ альтернативой. Если все равно не получается, я бы предложил добавить msword в теги и оставить вопрос без ответа какое-то время. - person Sertac Akyuz; 06.04.2011

Если вы можете использовать формат Office Open XML (т. Е. Формат файла docx, представленный в Word 2007), то вы можете сделать это без автоматизации.

Версии Word до 2007 должны устанавливать пакет совместимости, который включает файлы docx для Word 2003, 2002 и 2000.

На самом деле docx-файл представляет собой zip-файл, содержащий несколько xml-файлов. Попробуйте изменить расширение docx-файла с .docx на .zip и открыть этот файл, например. WinZip.

Итак ... Разархивируйте docx-файл и возьмите нужную xml-часть. Как чистая строка или как XML-документ. Затем вы можете вставить эту xml-часть в другой docx-файл. Однако вам нужно знать где в xml-структуре, чтобы получить / вставить xml. Это будет зависеть от того, насколько хорошо вы знаете структуру документа и насколько пользователю разрешено редактировать документ.

Я не знаю, как Word при таком подходе будет обрабатывать повторяющиеся имена закладок и т. Д.

person Jørn E. Angeltveit    schedule 24.03.2011

Кажется, я нашел каноническое решение этого вопроса, копаясь в аналогичной проблеме. Свойство FormattedText объекта Range - это именно то, что вам нужно. Просто используйте:

vRange2.FormattedText := vRange1;

и содержимое vRange1 будет скопировано в vRange2. Кроме того, это тоже работает:

vRange2 := vRange1;

Хотя второй оператор не копирует форматирование.

person Danatela    schedule 21.05.2014

Почему бы не использовать буфер обмена? Если весь текст выделен в vDoc1, то для его копирования в буфер обмена требуется один простой вызов: vDoc1.copy. Точно так же для копирования содержимого буфера обмена во второй документ требуется один простой вызов: vDoc2.paste. Буфер обмена будет содержать всю информацию о форматировании.

person No'am Newman    schedule 23.03.2011
comment
Прежде всего, если я использую буфер обмена, я удалю все, что туда поместил пользователь. Это нехорошо. Кроме того, это приложение часто запускается на сервере терминалов, где буфер обмена синхронизируется с локальным буфером обмена, что вызывает проблемы с производительностью. - person Svein Bringsli; 23.03.2011
comment
-1 Буфер обмена принадлежит пользователю, а не вам. Вы не должны делать абсолютно ничего, что влияет на содержимое буфера обмена, если пользователь не инициирует это (например, копирование, вырезание или вставка). - person Ken White; 23.03.2011