Delphi and работа с шаблоном word

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
unit Unit1;
 
interface
 
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;
 
type
  TForm1 = class(TForm)
    Button1: TButton;
    OpenDialog1: TOpenDialog;
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: TEdit;
    Edit4: TEdit;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
 
var
  Form1: TForm1;
 
implementation
 
{$R *.dfm}
 
uses
  ComObj;
 
//Функция записывает новый текст для закладки с именем aBmName.
//Функция возвращает значение True, если закладка найдена и её текст изменён
//и False - если закладка не найдена.
//Переустановка текста закладки выплоняется так:
//- Получаем ссылку на объект-диапазон, который содержит текст закладки.
//- Удаляем закладку.
//- Устанавливаем новый текст для объекта-диапазона.
//- Создаём новую закладку с диапазоном, который содержит новый текст.
function SetBmText(var aBms : Variant; const aBmName, aText : String) : Boolean;
var
  Bm, Rng : Variant;
begin
  //Проверяем - существует ли закладка с заданным именем.
  Result := aBms.Exists(aBmName);
  //Если закладка не найдена - выходим.
  if not Result then Exit;
 
  //Ссылка на закладку.
  Bm := aBms.Item(aBmName);
  //Ссылка на диапазон, связанный с закладкой.
  Rng := Bm.Range;
  //Удаление закладки.
  Bm.Delete;
  //Заменяем текст в диапазоне.
  Rng.Text := aText;
  //Добавляем новую закладку с таким же именем.
  aBms.Add(aBmName, Rng);
end;
 
procedure TForm1.Button1Click(Sender: TObject);
var
  wdApp, wdDocs, wdDoc, wdBms : Variant;
  Od : TOpenDialog;
begin
  Od := OpenDialog1;
 
  if Od.InitialDir = '' then
    Od.InitialDir := ExtractFilePath( Application.ExeName )
  ;
  Od.Title := 'Выберите шаблон, на основе которого будет создан новый документ';
  if not Od.Execute then Exit;
  if not FileExists(Od.FileName) then begin
    MessageDlg(
      'Файл с заданным именем не найден. Действие отменено.'
      ,mtWarning, [mbOK], 0
    );
    Exit;
  end;
  try
    wdApp := CreateOleObject('Word.Application');
  except
    ShowMessage('Не удалось запустить MS Word. Действие отменено.');
    Exit;
  end;
 
  //Делаем видимым окно MS Word.
  wdApp.Visible := True;
  //Ссылка на коллекцию документов.
  wdDocs := wdApp.Documents;
  //Попытка открыть выбранный файл.
  wdDoc := wdDocs.Open(FileName:=Od.FileName);
  //Подключаемся к коллекции закладок.
  wdBms := wdDoc.Bookmarks;
  //Ищем закладки с нужными именами и изменяем их текст, в соответствие
  //с данными, введёнными на форме.
  SetBmText(wdBms, 'N_DOC', Edit1.Text);
  SetBmText(wdBms, 'DATE_CREATE', Edit2.Text);
  SetBmText(wdBms, 'N_TAB', Edit3.Text);
  SetBmText(wdBms, 'FIO', Edit4.Text);
 
  //Сохранять документ следует под другим именем, чтобы не перезаписать шаблон.
  //wdApp.DisplayAlerts := False; //Отключаем режим показа предупреждений.
  //wdDoc.SaveAs(FileName:=...);
  //wdApp.DisplayAlerts := True; //Включаем режим показа предупреждений.
  //Закрываем документ.
  //wdDoc.Close;
  //Закрываем MS Word.
  //wdApp.Quit;
end;
 
end.

уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.

Сегодня, в последний рабочий день недели, практически весь день провозился над передачей данных из Delphi в Word. Так как подозрение есть, что работа продолжится то решил кое-какие моменты по работе с Microsoft Word в Delphi запечатлеть и у себя в блоге. Написать такую мини-шпаргалку (тем более, что по Excel уже кое что есть).

Для начала, немного общих моментов по работе с MS Office в Delphi. И первое, что мы сделаем — это создадим объект Word.Application. Создается этот объект абсолютно также, как и объект Excel.Application:

uses ComObj;
var Word: variant;
[...]
procedure CreateWord(const Visible: boolean);
begin
  Word:=CreateOleObject('Word.Application');
  Word.Visible:=Visible;
end;

Всё достаточно просто. Далее мы можем работать с объектом следующим образом:

  1. Создавать документ Word с нуля
  2. Открыть уже существующий документ и изменить в нем текст для получения необходимой формы документа.

Рассмотрим оба варианта, т.к. оба они имеют как свои плюсы, так и недостатки.

Чтобы создать новый документ необходимо выполнить метод Add у коллекции Documents, т.е.:

[...]
Word.Documents.Add
[...]

и после этой операции уже начинать работать с документам обращаясь к нему по индексу или имени в коллекции. Также, можно создать новый документ по шаблону (*.dot). Для этого необходимо выполнить тот же метод Add, но с одним входным параметром — путем к файлу-шаблону:

[...]
Word.Documents.Add(TamplatePath:string);
[...]

Чтобы получить список всех открытых в данный момент документов Word можно воспользоваться следующим листингом:

[...]
var List: TStringList;
    i: integer;
begin
  List:=TStringList.Create;
  for i:=1 to Word.Documents.Count do
    List.Add(Word.Documents.Item(i).Name);
end;
[...]

Обратите внимание, что нумерация начинается с 1, а не с нуля. Чтобы активировать любой документ из коллекции для работы, необходимо выполнить метод Activate:

Word.Documents.Item(index).Activate

где index — номер документа в коллекции.

Теперь можно приступать к записи и чтению документа. Для работы с текстов в документе Word, как и в Excel для работы с ячейками таблицы, определен объект Range. Именно методы этого объекта и дают нам возможность работы с текстом. Для начала рассмотрим работу двух основных методов: InsertBefore и InsertAfter.

Как следует из название — первый метод вставляет текст в начало содержимого Range, а второй — в конец. При этом сам объект Range может содержать как весть документ (Document) так и какую-либо его часть. Например, в следующем листинге я вставлю строку в начало документа и затем методом InsertAfter буду добавлять несколько строк текста в конец документа:

[...]
Word.ActiveDocument.Range.InsertBefore('Hello World');
Word.ActiveDocument.Range.InsertAfter('текст после Hello World');
Word.ActiveDocument.Range.InsertAfter('окончание строки в документа');
[...]

При выполнении этих трех операции Range содержал весь документ.

Если работать со всем документом неудобно, а необходимо, например выделить фрагмент с 50 по 100 символ и работать с ним, то можно воспользоваться функцией Range, которая вернет нам необходимый объект Range:

var MyRange: variant;
begin
  MyRange:=WordActiveDocument.Range(50,100);
  MyRange.InsertBefore('Привет');//всё, что было после 50-го символа сдвинулось вправо
end;

Это что касается записи текста. Решение обратной задачи — чтения текста из документа ещё проще. Достаточно воспользоваться свойством Text у объекта Range:

[...]
ShowMessage(Word.ActiveDocument.Range.Text) //весь текст в документе
[...]

Также для чтения документа можно воспользоваться коллекцией документа Words (слова). За слово принимается непрерывный набор символов — цифр и букв, который оканчивается пробелом.

Перечисляются слова документа точно также как и при работе с коллекцией документов, т.е. первое слово имеет индекс 1 последнее — Word.Count.

[...]
  ShowMessage(Word.ActiveDocument.Words.Item(Word.ActiveDocument.Words.Count).Text)
[...]

В данном случае я вывел на экран последнее слово в документе.

Очевидно, что приведенный выше способ работы с документам хорош в случае, когда требуется создать относительно простой документ Word и не требуется лишний раз рассчитывать фрагменты текста, правильно вставлять таблицы и т.д. Если же необходимо работать с документами, которые имеют сложное содержание, например текст в перемежку с рисунками, таблицами, а сам текст выводится различными шрифтами, то, на мой взгляд наиболее удобно использовать второй способ работы с Word в Delphi — просто заменить текст в уже заранее заготовленном документа.

2. Работа с документами Word в Delphi. Открытие готового документа и замена текста.

Чтобы открыть заранее заготовленный документ Word в Delphi достаточно воспользоваться методом Open у коллекции Documents, например так:

var FilePath: string;
[...]
  Word.Documents.Open(FilePath)
[...]

Метод Open можно вызывать с несколькими аргументами:

  • FileName: string — путь и имя файла;
  • ConfirmConversions: boolean — False — не открывать диалоговое окно «Преобразование файла» при открытии файла, формат которого не соответствует формату Word (doc или docx)
  • ReadOnly:boolean — True — открыть документ в режиме «Только для чтения»
  • AddToRecentFiles: boolean — True, чтобы добавить документ в список недавно открытых документов.
  • PasswordDocument: string — пароль для открытия документа
  • PasswordTemplate: string — пароль для открытия шаблона
  • Revert : boolean — True, чтобы вернуться к сохраненному документу, если этот документ открывается повторно.
  • WritePasswordDocument: string — пароль для сохранения измененного документа в файле
  • WritePasswordTemplate:string — пароль для сохранения изменений в шаблоне
  • Format:integer — формат открываемого документа.

Обязательным параметром метода Open является только FileName, остальные — могут отсутствовать. Если же Вам необходимо воспользоваться несколькими параметрами, то их необходимо явно указывать при вызове метода, например:

[...]
  Word.Documents.Open(FileName:=FilePath, ReadOnly:=true)
[...]

В этом случае документ открывается в режиме «Только для чтения». При таком способе вызова (с явным указанием аргументов) положение аргументов может быть произвольным.

Что касается последнего аргумента — Format, то он может принимать  целочисленные значения (применительно к версиям Microsoft Word 2007 и выше) от 0 до 13. При этом, для того, чтобы открыть «родные» вордовские документы (doc) достаточно использовать значения 0 или 6.

Теперь, когда документ открыт его необходимо преобразовать. Обычно я делаю следующим образом: в тех местах документа, в которые необходимо вставить текст я расставляю либо закладки, либо простые строки текста, например, обрамленные символом $ или #. И затем просто выполняю поиск и замену подстрок следующим образом:

function FindAndReplace(const FindText,ReplaceText:string):boolean;
  const wdReplaceAll = 2;
begin
  Word.Selection.Find.MatchSoundsLike := False;
  Word.Selection.Find.MatchAllWordForms := False;
  Word.Selection.Find.MatchWholeWord := False;
  Word.Selection.Find.Format := False;
  Word.Selection.Find.Forward := True;
  Word.Selection.Find.ClearFormatting;
  Word.Selection.Find.Text:=FindText;
  Word.Selection.Find.Replacement.Text:=ReplaceText;
  FindAndReplace:=Word.Selection.Find.Execute(Replace:=wdReplaceAll);
end;

Приведенная выше функция позволяет провести поиск и замену текстового фрагмента во всём документе. Для того, чтобы ограничить возможности пользователя при работе с шаблоном документа я обычно ставлю на необработанный файл пароль, а после обработки — пароль снимаю и сохраняю документ с другим названием в необходимую директорию.

Вот, наверное, самые-самые простые методы работы с Word в Delphi. Кстати, пишу пост и, думаю, что у кого-то из читателей может возникнуть вопрос: причём тут Delphi в Internet и Word в Delphi? :) Честно говоря, приведенный выше фрагменты кода можно использовать для нужд в Internet с натяжкой, например, при автосоставлении небольших отчётов по чему-либо. А вообще, в недалеком будущем, есть в планах поразбираться с Тезаурусом Word и попробовать составить небольшой синонимайзер для собственных нужд — он-то и пригодится нам в Internet :)

3.1
8
голоса

Рейтинг статьи

уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.


Форум программистов Vingrad

Модераторы: MetalFan

Поиск:

Ответ в темуСоздание новой темы
Создание опроса
> Работа в Delphi с шаблонами MS Word 

:(

   

Опции темы

Delphist
  Дата 5.1.2010, 16:30 (ссылка)
| (нет голосов)
Загрузка ... Загрузка …




Быстрая цитата

Цитата

Delphist Эксперт
****

Профиль
Группа: Завсегдатай
Сообщений: 2145
Регистрация: 3.2.2004
Где: всегда в сети

Репутация: нет
Всего: 3

Скажите, пожалуйста, как происходит работа с шаблонами MS Word с помощью Delphi. Т.е. есть подготовленный шаблон в MS Word’e, а в нужные поля этого шаблона должна пуступать информация из программы. если можно поделитесь информацией или примерами по данному вопросу.

———————

ProcessInfo 1-ая моя программа (аналог spyxx.exe с гораздо большим функц-ом — внедрение dll в адр. простр. процесса, перехват API-функций, разбор приложения на окна мн.др).
Когда-то давным-давно использовал это…

PM MAIL ICQ   Вверх
Данкинг
Дата 5.1.2010, 17:41 (ссылка)
| (нет голосов)
Загрузка ... Загрузка …




Быстрая цитата

Цитата

Yersinia pestis
****

Профиль
Группа: Завсегдатай
Сообщений: 8302
Регистрация: 7.11.2006
Где: მოსკოვი

Репутация: 5
Всего: 130

Конкретно с шаблоном не работал, но вот пример заполнения нужных полей в файле .doc:

Код

worduk:variant;
...
worduk:=createoleobject('word.application');
worduk.documents.open ('zakaz.doc');
worduk.visible:=false;
worduk.selection.find.text:='<zeus1>';
  if  worduk.selection.find.execute then worduk.selection.text:='Значение поля <zeus1>';
  worduk.selection.start:=0;
  worduk.selection.end:=worduk.activedocument.characters.count;
 worduk.selection.find.text:='<zeus2>';
  if  worduk.selection.find.execute then worduk.selection.text:='Значение поля <zeus2>';
  worduk.selection.start:=0;
  worduk.selection.end:=worduk.activedocument.characters.count;
worduk.activedocument.saveas ('zakaz.doc');
worduk.activedocument.close;
worduk.application.quit;
worduk:=unassigned;

———————

There’s nothing left but silent epitaphs.

PM MAIL WWW   Вверх
Delphist
  Дата 6.1.2010, 09:47 (ссылка)
| (нет голосов)
Загрузка ... Загрузка …




Быстрая цитата

Цитата

Delphist Эксперт
****

Профиль
Группа: Завсегдатай
Сообщений: 2145
Регистрация: 3.2.2004
Где: всегда в сети

Репутация: нет
Всего: 3

Цитата(Данкинг @  5.1.2010,  18:41 Найти цитируемый пост)
Конкретно с шаблоном не работал, но вот пример заполнения нужных полей в файле .doc:

А как при этом должен выглядеть документ ворда, нужно ли там <zeus1>, <zeus2> обрамлять в тег var.
Просто хотелось бы увидеть пример, чтобы он показывал:
   1. как создать в *.dot  переменные (сейчас вычитал, что это так называемые bookmark’еры), чтобы в них попадала информация из нашей программы. как должен выглядеть наш код, который передавал бы конкретные значения в наши переменные заданные в *.dot
  2. в шаблоне помимо текста будет таблица динамическая, ее размер — число строк и колонок  должно формироваться из программы, возникает вопрос во-первых как программно сформировать такую таблицу и вывести ее в нужном месте.

Это сообщение отредактировал(а) Delphist — 6.1.2010, 10:50

———————

ProcessInfo 1-ая моя программа (аналог spyxx.exe с гораздо большим функц-ом — внедрение dll в адр. простр. процесса, перехват API-функций, разбор приложения на окна мн.др).
Когда-то давным-давно использовал это…

PM MAIL ICQ   Вверх
Regulum
Дата 7.1.2010, 09:22 (ссылка)
| (нет голосов)
Загрузка ... Загрузка …




Быстрая цитата

Цитата

Новичок

Профиль
Группа: Участник
Сообщений: 29
Регистрация: 16.6.2009

Репутация: 1
Всего: 1

1. Вашу задачу в двух словах не решить. Ознакомьтесь с азами работы в MS Office из Delphi, например в книгах: 
    Корняков В.Н. Программирование документов и приложений MS Office в Delphi. — СПб.: БХВ-Петербург,2005.
    Архангельский А.Я. Приемы программирования в Delphi на основе VCL. — М.: БИНОМ, 2006

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

Код

uses ComObj;
var
   W,Range,Table: Variant;
.....

//запускаем Word и создаем новый документ
   W:= CreateOleObject('Word.Application');
   W.Documents.Add;
//заголовок
   Range:= W.ActiveDocument.Range;
   Range.Font.Size:= 14;
   Range.Font.Name:= 'Times New Roman';
   Range.Font.Bold:= true;
   Range.ParagraphFormat.Alignment:= 1;  //по центру
   Range.InsertAfter('Заголовок моего любимого документа');
   Range.InsertAfter(Char(#13)); //переход на следующую строку
   Range.InsertAfter(Char(#13)); 
//вставляем таблицу 
   Range:= W.ActiveDocument.Range(Range.End-1,Range.End-1);
   W.ActiveDocument.Tables.Add(Range,2{количество строк},3{количество столбцов});
//настраиваем шапку таблицы
   Table:= W.ActiveDocument.Tables.Item(1);
   Table.Columns.Item(1).Width:= 20;  //ширина первой колонки
   Table.Cell(1,1).Range.Text:= '№';     //заголовок первой колонки
   Table.Columns.Item(2).Width:= 200;  //ширина второй колонки
   Table.Cell(1,2).Range.Text:= 'Ф.И.О.';//заголовок второй колонки
   ...
{обращение к остальным ячейкам таблицы аналогично - по номеру строки и столбца}
//выделим шапку таблицы (первая строка) жирным начертанием
   Table.Rows.Item(1).Range.Font.Bold:= true;
//и сделаем выравнивание текста по центру
   Table.Rows.Item(1).Range.ParagraphFormat.Alignment:= 1;

 

Вот, собственно, и почти все приемы, которые вам понадобятся. Подробнее о настройках есть в книгах

PM MAIL   Вверх
Regulum
Дата 7.1.2010, 09:40 (ссылка)
| (нет голосов)
Загрузка ... Загрузка …




Быстрая цитата

Цитата

Новичок

Профиль
Группа: Участник
Сообщений: 29
Регистрация: 16.6.2009

Репутация: 1
Всего: 1

Цитата(Delphist @ 6.1.2010,  09:47)
А как при этом должен выглядеть документ ворда, нужно ли там <zeus1>, <zeus2> обрамлять в тег var.

Никаких тегов в документе Word не нужно. Однако лучше каким-то образом выделять заменяемый текст, чтобы он наверняка не совпадал с другим фрагментом документа, который заменять не нужно. Ну например так: <текст, который надо заменить> или так ###текст, который надо заменить&.
Механизм вставки при этом такой: ищем фрагмент, который надо заменить и заменяем его полезной информацией. 
Простейшая реализация:

Код

uses ComObj;
var W: Variant;
....
W:= CreateOleObject('Word.Basic');
W.FileNew('C:MpyShablon.dot'); //создаем документ по шаблону
W.StartOfDocument; //переходим в начало документа
W.EditFind('###текст, который надо заменить&'); //ищем то, что заменить
W.Insert('текст, который должен быть'); //и вставляем то, что надо
W.FileSaveAs('C:MyFile.doc'); //сохраняем документ

PS. Прошу прощения, не нашел, как объединить 2 поста в один. Уважаемый модератор, не судите строго за 2 поста подряд.

Это сообщение отредактировал(а) Regulum — 7.1.2010, 09:43

PM MAIL   Вверх
Данкинг
Дата 7.1.2010, 11:11 (ссылка)
| (нет голосов)
Загрузка ... Загрузка …




Быстрая цитата

Цитата

Yersinia pestis
****

Профиль
Группа: Завсегдатай
Сообщений: 8302
Регистрация: 7.11.2006
Где: მოსკოვი

Репутация: 5
Всего: 130

Цитата(Delphist @  6.1.2010,  09:47 Найти цитируемый пост)
А как при этом должен выглядеть документ ворда, нужно ли там <zeus1>, <zeus2> обрамлять в тег var.

Нет, просто я обрамляю тегами для удобства. А по факту — в моём примере реализуется просто поиск-замена слова.

———————

There’s nothing left but silent epitaphs.

PM MAIL WWW   Вверх
ZVano
Дата 14.1.2010, 18:06 (ссылка)
| (нет голосов)
Загрузка ... Загрузка …




Быстрая цитата

Цитата

Опытный
**

Профиль
Группа: Участник
Сообщений: 258
Регистрация: 11.12.2006
Где: Украина, Кривой Р ог

Репутация: нет
Всего: 4

Используем подстановочные ключи вида <#НазваниеПодстановки>
Используем компонент fWApp:TWordApplication;

0. Создаем функцию, которая должна вернуть значение тега по его имени «_TagText».

Код

function GetKeyValueByName(_TagText:string):string;
begin
  result := '$' + _TagText + '$';
end;

1. Настраиваем поиск. все переменные «_ИмяПеременной» типа OleVariant;

Код

      _FindText := '<#*>';// Искомый текст (Регулярное выражение :) )
      _MatchCase := False; // Регистрозависимый?
      _MatchWholeWord := False;
      _MatchWildcards := True;
      _MatchSoundsLike := False;
      _MatchAllWordForms := False;
      _Forward := True; //Вперед
      //_Wrap := wdFindContinue;//По достижению конца документа перейти в начало = бесконечный цикл
      _Wrap := wdFindStop;
      _Format := False;
      _ReplaceWith := '';
      _Replace := wdReplaceNone; // Способ замены (Не заменять|Все|Один)
      _MatchKashida := EmptyParam;
      _MatchDiacritics := EmptyParam;
      _MatchAlefHamza := EmptyParam;
      _MatchControl := EmptyParam;

2. Ищем и заменяем ключи

Код

      while fWApp.Selection.Find.Execute(
        _FindText, _MatchCase, _MatchWholeWord, _MatchWildcards, _MatchSoundsLike,
        _MatchAllWordForms, _Forward, _Wrap, _Format, _ReplaceWith, _Replace,
        _MatchKashida, _MatchDiacritics, _MatchAlefHamza, _MatchControl
      ) do
        begin
          vTagText :=  fWApp.Selection.Text; //Найденый текст выделен
          vTagPosBeg := fWApp.Selection.Range.Start;//Его начальная позиция(от начала документа)
          vTagPosEnd := fWApp.Selection.Range.End_;//Его конечная позиция(от начала документа)
          // Можно добавить информацию о найденом фрагменте в массив...
          //TagPosAdd(vTagText, vTagPosBeg, vTagPosEnd);
          //...но мы сразу подменяем текст
          fWApp.Selection.Text := GetKeyValueByName(vTagText);
        end;

PS: 
Лучше сохранять в массив объект fWApp.Selection.Range т.к. при изменении текста перед найденым фрагментом не нужно заботиться о смещении позиции.

Это сообщение отредактировал(а) ZVano — 12.2.2010, 12:08

———————

НЕ ФЛУДИМ. Пользуемся кнопками «+» или «-» для выражения своего отношения к теме или сообщению.
Гуглим «Как правильно задавать вопросы»

PM MAIL Skype   Вверх



















Ответ в темуСоздание новой темы
Создание опроса
Правила форума «Delphi: ActiveX/СОМ/CORBA»

Rrader
Girder

Запрещено:

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делиться вскрытыми компонентами

  • Литературу по Delphi обсуждаем здесь
  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь
  • 90% ответов на свои вопросы можно найти в DRKB (Delphi Russian Knowledge Base) — крупнейшем в рунете сборнике материалов по Delphi
  • Вопросы по SQL и вопросы по базам данных, не связанные с Delphi, задавать здесь

Если Вам помогли, и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, Rrader, Girder.

 

0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Delphi: ActiveX/СОМ/CORBA | Следующая тема »

>
Заполнение и печать шаблона MS Word
, Как это делается?

  • Подписаться на тему
  • Сообщить другу
  • Скачать/распечатать тему



Сообщ.
#1

,
14.04.04, 19:00

    Необходимо программно вставить данные из программы в шаблон документа MS Word и распечатать результат, при этом шаблон оставить прежним. Как это сделать, используя компоненты WordApplication и WordDocument?

    Пример: есть шаблон pattern.doc, содержащий текст:
    Фамилия:
    Имя:

    Нужно программно заполнить этот шаблон:
    Фамилия: Иванов
    Имя: Иван

    И распечатать. После чего закрыть файл шаблона без изменений.

    Открывать и писать в документы я научился. Печатать тоже, наверное, смогу. А как писать текст в ОПРЕДЕЛЕННЫЕ места документа? Самое первое что напрашивается, это сделать метки в шаблоне и осуществлять замену. Но метода вроде Replace ни где не нашел.

    Буду очень благодарен за советы и примеры.


    zx



    Сообщ.
    #2

    ,
    15.04.04, 06:25

      НА места куда надо вписать данные ставь таблички пустые, потом такой код

      fil: OleVariant;

      wordApplication1.Connect;
      fil:=’c:Shablon_sotr.doc’;
      //WordApplication1.Documents.Add(EmptyParam,EmptyParam);
      WordApplication1.Documents.Open(fil,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam,EmptyParam);
      WordDocument1.ConnectTo(WordApplication1.ActiveDocument);
      WordApplication1.Options.CheckSpellingAsYouType:=false;
      WordApplication1.Options.CheckGrammarAsYouType:=false;

      WordDocument1.Tables.Item(1).Range.Text:= ‘текст’;
      WordDocument1.Tables.Item(2).Range.Text:= ‘текст’;
      WordDocument1.Tables.Item(3).Range.Text:= ‘текст’;
      WordDocument1.Tables.Item(4).Range.Text:= ‘текст’;

      только кто нить знает как потом закрыть всё это корректно, без вопросов и без провисания сервака в памяти?


      zx



      Сообщ.
      #3

      ,
      15.04.04, 06:25

        Для печати
        WordDocument1.PrintOut;


        AlexAnt



        Сообщ.
        #4

        ,
        15.04.04, 07:21

          Спасибо!

          А что если уже в самом шаблоне есть таблицы? У меня именно такая ситуация. Причем заносить данные нужно не просто в пустые ячейки, а в ячейки содержащие уж какой-то текст. Поэтому я сразу пытался избежать обращения к таблицам, шаблон большой и посчитать где какая таблица — практически невыполнимая задача для меня.


          AlexAnt



          Сообщ.
          #5

          ,
          15.04.04, 08:48

            Аналогичную задачу в Perl я всегда решал расстановкой меток в шаблоне, с последующей их заменой. Например:

            Фамилия: %surname%
            Имя: %name%

            Затем используется простое регулярное выражение s/%surname%/Name/g, которое заменяет все метки %surname% на значение переменной Name. И т.д.

            Неужели ничего подобного нет в сервере MS Word? Я имею ввиду не регулярные выражения естественно, а замену текста на текст. Можете есть еще какие-то объекты, которые можно заменять на текст?


            zx



            Сообщ.
            #6

            ,
            15.04.04, 08:56

              Попробуй сделать так, на месте где тебе надо установить переменную, делаешь метку, например %name%, тоесть простой текст.
              Потом выполняешь команду поиска и замены. Заменяешь на то что тебе надо.
              Посмотри на www.delphikingdom.ru, есть 2 статейки по word+delphi.


              AlexAnt



              Сообщ.
              #7

              ,
              15.04.04, 09:38

                В том-то и дело, что я ни где не могу найти инфу о том, как делать замену! :) Куча доков и книг по работе с Word из Delphi и ни где нет и слова о замене! :)

                Сейчас поищу на дельфикингдум…


                ych_boriss



                Сообщ.
                #8

                ,
                15.04.04, 09:45

                  Senior Member

                  ****

                  Рейтинг (т): 22

                  ExpandedWrap disabled

                    text := AnsiReplaceText(text, ‘%surname%’, surname); // or

                    text := StringReplace(text, ‘%surname%’, surname, [rfReplaceAll]); // or

                    // …

                    // or написать свою замену

                  Сообщение отредактировано: ych_boriss — 15.04.04, 09:46


                  Song



                  Сообщ.
                  #9

                  ,
                  15.04.04, 10:03

                    ych_boriss, тут разговор о замене средствами VBS.


                    AlexAnt



                    Сообщ.
                    #10

                    ,
                    15.04.04, 10:52

                      Всем кому интересно, приведенный ниже кусок открывает доковский файл FileName, находит первый текст %name% и заменяет его на ‘Вася’, распечатывает и закрывает файл без сохранения (распечатку еще не проверял):

                      procedure TForm1.APrintExecute(Sender: TObject);
                      var
                      FileName, wdDoNotSaveChanges: OleVariant;
                      FindText, ReplaceText, OleFalse, OleTrue, Unknow: OleVariant;
                      begin
                      FileName := Directory + PatternFile;
                      try
                      WordApplication1.Connect;
                      WordApplication1.Visible := True;
                      try
                      WordApplication1.Documents.Add(FileName, EmptyParam,
                      EmptyParam, EmptyParam);

                      FindText := ‘%name%’;
                      ReplaceText := ‘Вася’;
                      OleFalse := False;
                      OleTrue := True;
                      Unknow := wdFindContinue;

                      WordApplication1.Selection.Find.Execute(FindText, OleFalse, OleFalse,
                      OleFalse, OleFalse, OleFalse, OleTrue, Unknow, OleFalse, ReplaceText,
                      OleTrue, EmptyParam, EmptyParam, EmptyParam, EmptyParam);

                      WordApplication1.PrintOut;
                      WordApplication1.Quit(wdDoNotSaveChanges);
                      WordApplication1.Disconnect;
                      except
                      ShowMessage(‘Не могу найти файл шаблона: ‘ + PatternFile)
                      end;
                      except
                      ShowMessage(‘Не могу подключиться к серверу MS Word.’)
                      end;
                      end;

                      Wizard

                      Bas



                      Сообщ.
                      #11

                      ,
                      15.04.04, 10:57

                        ExpandedWrap disabled

                            oldStr,newStr,replace:OleVariant;

                            replace:=1;  oldStr:=’@1′;  newStr:=DateTimeToStr(Now);

                          //Находим в документе метки и производим их замены

                            WordDocument1.Range.Find.Execute(oldStr,EmptyParam,EmptyParam,

                           EmptyParam,EmptyParam,EmptyParam,EmptyParam,

                                          EmptyParam,EmptyParam,newStr,replace);


                        TarZan



                        Сообщ.
                        #13

                        ,
                        05.05.04, 14:48

                          Морока с этими док’ами…
                          Я для себя когда-то делал так.
                          Шаблон сохранял в RTF и ставил че-то типа @001 в качестве метки.
                          Во время формирования нового документа делал так: копировал посимвольно файл и если встречал «@» то читал следующие 3 символа, проганял полученное значение по кейсу и на выходе получал некоторую строку, которую и влепливал вместо @001 в новый файл.
                          ПРЕИМУЩЕСТВА:
                          RTF — кроссплатформенный формат
                          не могут жить вирусы
                          открывается Word’ом по умолчанию (в TOLEcontainer закинуть имя файла и сказать DoVerb(0))
                          может открываться другим текстовым редактором
                          Исходный макет никогда не затрется
                          …короче, решайте сами…

                          0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)

                          0 пользователей:

                          • Предыдущая тема
                          • Delphi: Общие вопросы
                          • Следующая тема

                          [ Script execution time: 0,0429 ]   [ 16 queries used ]   [ Generated: 13.04.23, 22:37 GMT ]  

                          Шаблоны и отчёты MS Word
                          Многие популярные информационно-правовые системы содержат шаблоны различных документов в формате Word. Используя их и информацию данной статьи, вы можете легко и быстро создавать отчеты, если пишете свои программы на Delphi. Если пишете на другом языке, это не помеха, описанный подход справедлив к различным языкам.
                          Когда мы формируем сложный документ по шаблону, и когда в этом шаблоне есть таблицы, количество строк которых в выходном документе может быть произвольным и зависеть от объема информации, то нам недостаточно просто функций создания таблиц и записи в ячейки определенной информации. Для создания такого документа необходимо, как минимум, еще несколько функций работы с таблицами, в перечень которых входит перемещение по таблицам, добавление строк (столбцов) в конец или в середину таблицы. Также необходимо определять размер (количество строк, столбцов) и номер текущего столбца и строки. Чтобы понимать, о чем речь, необходимо просмотреть части 1-3 данной статьи, опубликованных в предыдущих номерах.
                          Для того, чтобы применить свои знания к конкретной задаче, сделаем ее постановку. Например, в нашем документе есть таблица, которая представляет собой шаблон и заполняется из массива информации, который имеет произвольную длину. Таким документом может быть счет-фактура, заголовок которой представляет собой сложную таблицу, средняя часть представляет таблицу переменной длины, а нижняя также представляет сложную таблицу. Для заполнения такого шаблона можно использовать способ, описанный во второй части данной статьи. Этот способ основан на поиске в шаблоне переменных (неповторяющиеся строковые значения длиной 3-5 символов) и подстановке вместо них реальных значений на этапе формирования документа. Поэтому для добавления информации в такую таблицу придется осуществить поиск и позиционирование в строку (по переменной), в которую и перед которой необходимо вставлять строки, и запомнить, в какие колонки какую записывать информацию, но для начала необходимо определить, находится курсор в таблице или нет.
                          Для этого используем свойство Information объекта Selection, в качестве параметра которого будет константа wdWithInTable. В этом случае этот метод возвращает TRUE, если курсор в таблице, или FALSE, если нет. Для использования в нашем приложении создадим функцию GetSelectionTable.

                          Function GetSelectionTable:boolean;
                           const wdWithInTable=12;
                          begin
                           GetSelectionTable:=true;
                           try
                           GetSelectionTable:=W.Selection.Information(wdWithInTable);
                           except
                           GetSelectionTable:=false;
                           end;
                          End;

                          Если в нашем документе может быть более одной таблицы, то, скорее всего, необходима возможность перехода и позиционирование курсора на следующей или предыдущей таблице. Объект Selection дает нам эту возможность через методы GoTo и GoTo, в этом случае в качестве их параметров должна использоваться константа wdGoToTable.

                          Function GoToTable (table_:integer):boolean;
                           const wdGoToTable=2;
                          begin
                           GoToTable:=true;
                           try
                           W.Selection.GoTo (wdGoToTable);
                           except
                           GoToTable:=false;
                           end;
                          End;
                          Function GoToTable (table_:integer):boolean;
                           const wdGoToTable=2;
                          begin
                           GoToTable:=true;
                           try
                           W.Selection.GoTo(wdGoToTable);
                           except
                           GoToTable:=false;
                           end;
                          End;

                          Когда мы позиционируемся на таблице, можем определить количество столбцов и строк в ней. Для этого также используем свойство Information объекта Selection, но в качестве аргументов используем константы wdMaximum Number Of Columns и wdMaximum NumberOfRows.

                          Function GetColumnsRowsTable(table_:integer;

                           var Columns,Rows:integer):boolean;

                           const

                           wdMaximumNumberOfColumns=18;

                           wdMaximumNumberOfRows=15;

                          begin

                           GetColumnsRowsTable:=true;

                           try

                           Columns:=W.Selection.Information (wdMaximumNumberOfColumns);

                           Rows:=W.Selection.Information (wdMaximumNumberOfRows);

                           except

                           GetColumnsRowsTable:=false;

                           end;

                          End;

                          Кроме размера таблицы, нам может быть необходим номер колонки и строки, на которой позиционирован курсор. Для этого так же используем свойство Information объекта Selection, но в качестве аргументов используем константы wdStartOfRangeColumnNumber, wdStartOfRangeRowNumber. Для реализации этого в Delphi создадим функцию GetColumnRowTable.

                          Function GetColumnRowTable(table_:integer;

                           var Column,Row:integer):boolean;

                           const

                           wdStartOfRangeColumnNumber=16;

                           wdStartOfRangeRowNumber=13;

                          begin

                           GetColumnRowTable:=true;

                           try

                           Column:=W.Selection.Information (wdStartOfRangeColumnNumber);

                           Row:=W.Selection.Information (wdStartOfRangeRowNumber);

                           except

                           GetColumnRowTable:=false;

                           end;

                          End;

                          После того, как мы нашли таблицу в шаблоне документа и позиционировались на определенной ячейке, нам необходимо выполнить некоторые действия с ней (добавить, вставить строки, записать информацию). Очевидно, что нам нужен будет набор функций для ее модификации.
                          Обычно во время формирования таблицы мы не знаем, сколько будет строк. Они могут добавляться в конец или вставляться в середину таблицы. Если для формирования документа мы используем шаблон таблицы и в нем уже есть, например, заголовок, то нам не обойтись без процедур добавления или вставления строк. Добавить строку в конец таблицы можно, используя метод Add коллекции Rows. Чтобы это сделать из приложения на Delphi, достаточно создать и использовать функцию. Определим ее как AddRowTableDoc.

                          Function AddRowTableDoc (table_:integer):boolean;

                          begin

                           AddRowTableDoc:=true;

                           try

                           W.ActiveDocument.Tables.Item(table_).Rows.Add;

                           except

                           AddRowTableDoc:=false;

                           end;

                          End;

                          Для того, чтобы вставлять строки в середину таблицы, удобно использовать пару операторов. Первый выделяет строку, перед которой необходимо вставить новую, второй вставляет строку (строки). Смотрите функцию InsertRowsTableDoc.

                          Function InsertRowsTableDoc(table_,position_,

                           count_:integer): boolean;

                          begin

                           InsertRowsTableDoc:=true;

                           try

                           W.ActiveDocument.Tables.Item(table_).Rows.Item(position_).Select;

                           W.Selection.InsertRows (count_);

                           except

                           InsertRowsTableDoc:=false;

                           end;

                          End;

                          Для добавления одной строки можно использовать также и метод Add коллекции Rows, но с параметром, в качестве которого выступает ссылка на строку, перед которой необходимо вставить новую. Первый оператор получает ссылку на строку, второй вставляет новую. Смотрите реализацию на Delphi (InsertRowTableDoc).

                          Function InsertRowTableDoc(table_,position_: integer):boolean;

                           var row_:variant;

                          begin

                           InsertRowTableDoc:=true;

                           try

                           row_:=W.ActiveDocument.Tables.Item(table_).Rows.Item(position_);

                           W.ActiveDocument.Tables.Item(table_).Rows.Add(row_);

                           except

                           InsertRowTableDoc:=false;

                           end;

                          End;

                          Когда мы в своем распоряжении имеем набор функций для изменения таблицы, можно приступать к решению задачи — созданию документа типа счета-фактуры на базе шаблона. Полный исходный текст и полную версию шаблона счета-фактуры можно скачать по адресу www.kornjakov.ru/st1_4.zip. Здесь мы рассмотрим фрагмент данного документа. Создадим шаблон — документ формата DOC — и разместим его на диске в каталоге нашего проекта. Внешний вид шаблона смотрите на рисунке.
                          clip0070
                          Здесь будем заполнять только табличную часть. О том, как заполнять остальное, читайте вторую часть данной статьи. Для начала наши новые функции скопируем в библиотеку MyWord, которую мы создавали, начиная с первой части статьи. Затем создадим новый проект, на форме которого разместим кнопку, а в процедуре обработки ее нажатия напишем следующий программный текст.

                          procedure TForm1.Button1Click(Sender: TObject);

                           var tablica_:integer;

                            col_,row_:integer;

                            a_:integer;

                            metki_:array[1..12] of record

                            col:integer;

                            row:integer;

                            metka:string;

                            end;

                            tovar:array[1..2,1..12] of variant;

                          begin

                          // Заполняем массив данными. Массив используется

                          //для простоты демонстрации, в реальной программе

                          //данные берутся из базы данных.

                          tovar[1,1]:=‘Стул офисный’; tovar[1,2]:=‘шт.’;

                          tovar[1,3]:=2; tovar[1,4]:=520.00; tovar[1,5]:=1040.00;

                          tovar[1,6]:=‘-‘; tovar[1,7]:=20; tovar[1,8]:=208.0;

                          tovar[1,9]:=1248.00; tovar[1,10]:=62.40;

                          tovar[1,11]:=‘Россия’; tovar[1,12]:=‘-‘;

                          tovar[2,1]:=‘Телефон’; tovar[2,2]:=‘шт.’;

                          tovar[2,3]:=3; tovar[2,4]:=315.25; tovar[2,5]:=945.75;

                          tovar[2,6]:=‘-‘; tovar[2,7]:=20; tovar[2,8]:=189.15;

                          tovar[2,9]:=1134.90; tovar[2,10]:=56.70;

                          tovar[2,11]:=‘Беларусь’; tovar[2,12]:=‘-‘;

                          if CreateWord then begin

                           VisibleWord(true);

                           If OpenDoc(ExtractFileDir (application.ExeName) +‘sf.doc’)

                           then begin

                           tablica_:=1;

                           for a_:=1 to 12 do begin

                            StartOfDoc;

                            if FindTextDoc(‘###M’+inttostr(a_)+‘&’) then

                            if GetSelectionTable then begin

                            messagebox(handle,‘Находимся в таблице, запоминаем

                            метку(переменную), номер колонки и строки!’

                          ,

                            pchar(‘Номер колонки/строки = ‘+inttostr(col_)+‘/’+inttostr(row_)),0);

                            metki_[a_].col:=col_;

                            metki_[a_].row:=row_;

                            metki_[a_].metka:=‘###M’+inttostr(a_)+‘&’;

                            end;

                            end;

                            Messagebox(handle,‘Заполняем первую строку’,»,0);

                            for a_:=1 to 12 do begin

                            SetTextToTable(tablica_,metki_[a_].row,metki_[a_].col,tovar[1,a_]);

                            end;

                            a_:=1;

                            Messagebox(handle,‘Добавляем строку’,»,0);

                            InsertRowTableDoc(tablica_, metki_[a_].row);

                            Messagebox(handle,‘Заполняем вторую строку’,»,0);

                            for a_:=1 to 12 do begin

                            SetTextToTable(tablica_,metki_[a_].row,metki_[a_].col,tovar[2,a_]);

                            end;

                            SaveDocAs(ExtractFileDir(application.ExeName)+‘Счет — фактура.doc’);

                            Messagebox(handle,‘Текст сохранен’,»,0);

                            CloseDoc;

                           end;

                           Messagebox(handle,‘ Текст закрыт’,»,0);

                           CloseWord;

                           end;

                          end;

                          Мы сформировали фрагмент сложного документа, но вы, возможно, захотите в дальнейшем сами развивать эту тему и использовать все возможности Word.Application. В следующей части я постараюсь на примерах объяснить, каким образом это сделать. По всем вопросам вы можете обратиться к автору по адресу www.kornjakov.ru или _kvn@mail.ru.
                          Василий КОРНЯКОВ
                          Литература: Н. Елманова, С. Трепалин, А. Тенцер «Delphi 6 и технология COM» «Питер» 2002.

                          Понравилась статья? Поделить с друзьями:
                        • Delphi and microsoft word
                        • Delete word from string java
                        • Delphi and excel листы
                        • Delete what if table excel
                        • Delphi and excel copy