Delphi: робота з MS WORD
Припустимо, у нас вже відкрито файл. Питання відкриття та збереження документів вже були в інших статтях, так що докладно на цьому зупинятися не будемо. Просто по ходу справи буде приведено те, чого раніше не траплялося - вихід з документа без збереження змін. Як-то забув, вибачте:)
Текст
Спочатку про найпростіше - додаванні в документ Word потрібного рядка тексту. Помістимо на форму компоненти WordDocument, WordApplicationі WordParagraphFormat з палітри Servers. Нас цікавлять в першу чергу властивість Range компонента WordDocument і властивість Selection компонента WordApplication. Класики стверджують, що вони є посиланням на об'єкти Range і Selection. Range представляє з себе, простіше кажучи, шматок тексту, це може бути як весь текст документа, так і будь-яка його частина. Його межі задаються двома (або менше) параметрами типу OleVariant.
Наприклад:
var range1, range2, range3, a, b: OleVariant;
...
range1: = WordDocument1.Range;
a: = 5;
b: = 15;
range2: = WordDocument1.Range (a, b);
range3: = WordDocument1.Range (a);
Перший наш об'єкт включає в себе весь текст документа, у другого ми обмежили межі 5-м і 15-м символами, третій представляє з себе весь подальший текст документа, починаючи з 5-го символу. Об'єкт має кілька корисних методів, наприклад, з його допомогою можемо додати текст в документ:
range2.InsertAfter ('MS Word');
Це ми вставили текст після виділеного Range. Точно також можемо вставити текст і перед ним, для цього служить метод InsertBefore (). Текст, укладений в об'єкті Range, можемо отримати так:
WordDocument1.Range (a, b). Text;
Крім того, за допомогою Range можемо змінити шрифт в межах об'єкта. Приклад:
a: = 5;
b: = 15;
WordDocument1.Range (a, b). Font.Bold: = 1;
WordDocument1.Range (a, b). Font.Size: = 14;
WordDocument1.Range (a, b). Font.Color: = clRed;
Якщо хочемо скасувати виділення жирним шрифтом, присвоюємо 0. Аналогічно можна зробити шрифт курсивом, підкресленим - наберіть WordDocument1.Range.Font., І середовище сама підкаже, які можуть бути варіанти. Методи Select, Cut, Copy і Paste працюють як у звичайному тексті. За допомогою Paste можемо на місце вибраного Range вставити не лише рядки, але й малюнок, що знаходиться в буфері обміну.
WordDocument1.Range (a, b). Select;
WordDocument1.Range (a, b). Cut;
WordDocument1.Range (a, b). Copy;
WordDocument1.Range (a, b). Paste;
За допомогою Range можемо знайти в документі потрібний рядок. Нехай у тексті міститься слово "picture". Наприклад, нам на його місце треба буде вставити малюнок.
var a, b, vstart, vend: OleVariant;
j, ilengy: Integer;
...
ilengy: = Length (WordDocument1.Range.Text);
for j: = 0 to ilengy-8 do begin
a: = j;
b: = j +7;
if WordDocument1.Range (a, b). Text = 'picture' then begin
vstart: = j;
vend: = j +7;
end;
end;
WordDocument1.Range (vstart, vend). Select;
Така процедура знаходить і виділяє потрібний шматок тексту.
Тепер про Selection, що вдає із себе виділений фрагмент документа. Якщо виділення немає, це поточна позиція курсору в документі. З його допомогою можемо вставити що-небудь на місце виділеного фрагмента, зробити вирівнювання, змінити шрифт. Він також має методи InsertAfter () і InsertBefore ():
WordApplication1.Selection.InsertAfter ("text1");
WordApplication1.Selection.InsertBefore ("text2");
Форматування виділеного тексту відбувається аналогічно Range, наприклад:
WordApplication1.Selection.Font.Bold: = 1;
WordApplication1.Selection.Font.Size: = 16;
WordApplication1.Selection.Font.Color: = clGreen;
Для вирівнювання простіше скористатися компонентом WordParagraphFormat. Спочатку тільки потрібно "підключити" його до виділеного фрагмента тексту:
WordParagraphFormat1.ConnectTo (WordApplication1.Selection.ParagraphFormat);
WordParagraphFormat1.Alignment: = wdAlignParagraphCenter;
Значення його властивості Alignment може приймати значення wdAlignParagraphCenter, wdAlignParagraphLeft, wdAlignParagraphRight, сенс яких очевидний. Є й методи Cut, Copy і Paste, які в поясненнях навряд чи потребують:
WordApplication1.Selection.Cut;
WordApplication1.Selection.Copy;
WordApplication1.Selection.Paste;
Прибираємо виділення за допомогою методу Collapse. При цьому необхідно вказати, в який бік зміститься курсор, чи буде він до раніше виділеного фрагмента або після:
var vcol: OleVariant;
...
vcol: = wdCollapseStart;
WordApplication1.Selection.Collapse (vcol);
При цьому виділення пропаде, а курсор займе позицію перед фрагментом тексту. Якщо привласнити змінній значення wdCollapseEnd, то курсор переміститься тому. Можна просто поставити в дужках "пустушку":
WordApplication1.Selection.Collapse (EmptyParam);
Тоді згортання виділення проводиться за замовчуванням, до початку виділеного тексту.
Малюнки
Логічно було б припустити, що малюнки документа будуть представляти із себе колекцію, аналогічну таблиць, і ми, звернувшись до конкретної картинці, зможемо міняти її властивості - обтікання, розмір і т.д. Однак нічого подібного в WordDocument не виявляється. Тому можливості управління вбудованими в документ зображеннями сильно обмежені.
Найпростіший метод вставити в документ малюнок - по згаданих причин він же і єдиний - скопіювати його в Word з буфера обміну. Припустимо, малюнок у нас знаходиться в компоненті DBImage. Спочатку треба загнати його в буфер обміну:
Clipboard.Assign (DBImage1.Picture);
Тепер для його вставки слід скористатися методом Paste об'єктів Range або Selection: WordApplication1.Selection.Paste або WordDocument1.Range (a, b). Paste. Залишити для малюнка достатня кількість порожніх рядків і потрапити в потрібне місце - це вже наша турбота. Якщо він потрапить посеред тексту, вигляд буде досить гидкий - при такій вставці обтікання текстом малюнка відбувається якось дивно. Можна приготувати для звіту шаблон, де замінюємо малюнком яке-небудь ключове слово. Про те, як знайти в документі потрібний текст, див. вище.
А тепер про дещо інший спосіб вставки малюнка, який усуває проблеми з обтіканням і дає нам можливість переміщати його по документу, масштабувати і задавати відступи між малюнком і текстом. Спосіб, власне, той самий - копіюємо з буфера обміну, але не прямо в документ, а в "рамку" - текстову вставку. У ній може знаходитися не тільки текст, але і картинка, чим і скористаємося.
"Рамки" утворюють колекцію Frames, нумеруються цілим індексом, що пробігають значення від 1 до WordDocument1.Frames.Count. Додамо до документа рамку, змінимо її розмір і вставимо малюнок:
Clipboard.Assign (DBImage1.Picture);
vstart: = 1;
vend: = 2;
WordDocument1.Frames.Add (WordDocument1.Range (vstart, vend));
i: = 1;
WordDocument1.Frames.Item (i). Height: = DBImage1.Height;
WordDocument1.Frames.Item (i). Width: = DBImage1.Width;
WordDocument1.Frames.Item (i). Select;
WordApplication1.Selection.Paste;
Тут для простоти передбачається, що розмір DBImage дорівнює розміру самої картинки, а також що до цього рамок у нас в документі не було. Звернути увагу слід на декілька моментів. Розмір рамки треба задавати до того, як копіювати в неї малюнок. Інакше вона буде мати розмір за замовчуванням, під який замасштабіруется і наша картинка. При спробі змінити розмір рамки заднім числом розмір картинки вже не зміниться. Крім того, параметр Range при додаванні рамки часто ніякої ролі не грає. Рамка спочатку все одно з'явиться в лівому верхньому кутку документа, а зазначений шматок тексту при цьому не постраждає. Але це тільки в тому випадку, якщо він не виділено. Якщо в документі є виділення, рамка з'явиться замість виділеного фрагменту. Таким чином можемо її вставити в потрібне місце замість якогось ключового слова.
При бажанні можемо її посувати в документі і "вручну". Для цього служать властивості горизонтального і вертикального позиціонування, які задають її відступ від лівого верхнього "кута" документа:
i: = 1;
WordDocument1.Frames.Item (i). VerticalPosition: = 30;
WordDocument1.Frames.Item (i). HorizontalPosition: = 50;
Відступ між краями рамки і текстом задається наступним чином:
WordDocument1.Frames.Item (i). HorizontalDistanceFromText: = 10;
WordDocument1.Frames.Item (i). VerticalDistanceFromText: = 10;
А тепер про масштабування. Для цього достатньо довжину і ширину рамки помножити на одне і те ж число. Наприклад:
WordDocument1.Frames.Item (i). Height: = DBImage1.Height * 1.5;
WordDocument1.Frames.Item (i). Width: = DBImage1.Width * 1.5;
При цьому наша картинка в півтора рази пропорційно розтягнеться. Точно також можна і зменшити, але ділити, як і множити, слід на одне число. Розтягувати довжину і ширину по-різному у мене особисто не виходило. Задавати розмір знову-таки треба ще до вставки малюнка. Ну і, нарешті, видалення рамки:
WordDocument1.Frames.Item (i). Delete;
Списки
Списки в документі утворюють колекцію Lists, до окремого списку звертаємося WordDocument1.Lists.Item (i), де i ціле число від 1 до WordDocument1.Lists.Count ... на цьому все. Ні методів, що дозволяють не те що створити новий список, а навіть додати пункт до вже існуючого. Нічого страшного, справжні герої завжди йдуть в обхід:)) Зараз ми все ж проробимо і те, й інше. Все що нам знадобиться - властивість Range окремого списку, тобто його текст без поділу на пункти, а також можливість його виділити:
WordDocument1.Lists.Item (i). Range.Select;
Для цього в будь-якому випадку буде потрібно заготовка. Неважливо, вставлена вона до загального шаблонний документ або зберігається в окремому файлі. Заготівлю робимо так: вибираємо в меню Формат / Список, і зберігаємо, якщо це окремий шаблон списку. У нас з'являється порожній список без тексту з одним маркером. Далі згадуємо, як ми робили списки вручну - писали текст, натискали "Enter", з'являвся новий елемент списку. Тепер те ж саме, тільки програмно. Припустимо, у нас вже відкрито документ із заготівлею, і ми хочемо внести в список пункти "Item 1" і "Item 2":
var i: Integer;
vcol: OleVariant;
...
i: = 1;
vcol: = wdCollapseEnd;
WordDocument1.Lists.Item (i). Range.Select;
WordApplication1.Selection.Collapse (vcol);
WordApplication1.Selection.InsertAfter ('Item 1');
WordDocument1.Lists.Item (i). Range.Select;
WordApplication1.Selection.Collapse (vcol);
WordApplication1.Selection.InsertAfter (# 13);
WordDocument1.Lists.Item (i). Range.Select;
WordApplication1.Selection.Collapse (vcol);
WordApplication1.Selection.InsertAfter ('Item 2');
WordDocument1.Lists.Items (i). Range.Select;
WordApplication1.Selection.Copy;
Тобто ми вставляємо в документ текст першого пункту списку, він потрапляє на своє місце. Потім посилаємо в Word символ переходу рядки, він чесно переходить і тим самим сам створює нам другий пункт списку, куди і вставляємо потрібний рядок. Ну і так далі, потрібну кількість разів. Останні два рядки потрібні, якщо список заготовлено в окремому файлі - після їх виконання список виявляється в буфері обміну. Тут вигода в тому, що можемо мати заготівлі списків різних стилів і по ходу справи вибирати, який список створити. Потім відкриваємо документ, де має бути список, виділяємо за допомогою Range потрібний шматок, копіюємо з буфера обміну через WordDocument1.Range (a, b). Paste. Щоб не зіпсувати файл із заготівлею, можемо відразу після відкриття перезберегти його під іншим ім'ям, а можемо просто вийти з нього без збереження змін
var vsave: OleVariant;
...
vsave: = wdDoNotSaveChanges;
WordDocument1.Close (vsave);
Константа збереження змін може приймати значення
Символьне позначення | Шістнадцяткове |
wdSaveChanges | $ FFFFFFFF |
wdDoNotSaveChanges | $ 00000000 |
wdPromptToSaveChanges | $ FFFFFFFE |
Перше значення зберігає зміни, другий дає можливість вийти без збереження змін. Остання константа викликає при виході стандартний діалог збереження змін. Можемо зробити і дещо по-іншому. Хоча ми не можемо створити новий елемент списку, але текст у вже існуючому змінити можна:
var i, j: Integer;
...
i: = 1;
j: = 1;
WordDocument1.Lists.Item (i). ListParagraphs.Item (j). Range.Text: = 'Item 1';
Так що можна з допомогою переходів рядка створити потрібну кількість елементів, а потім їх заповнити:
WordDocument1.Lists.Item (i). Range.Select;
WordApplication1.Selection.Collapse (vcol);
WordApplication1.Selection.InsertAfter (# 13);
j: = 1;
WordDocument1.Lists.Item (i). ListParagraphs.Item (j). Range.Text: = 'Item 1';
j: = 2;
WordDocument1.Lists.Item (i). ListParagraphs.Item (j). Range.Text: = 'Item 2';
Це було у припущенні, що у нас один елемент списку в заготівлі вже є. Ну ось, загалом-то, і все про текст, списки і малюнки
Статистика документів
У даному невеликому матеріалі розглядається питання підрахунку статистики файлів *. doc і *. rtf. Таке питання у мене виникло, коли довелося зробити невелику базу даних з обліку документів, куди треба було заносити і статистику документа - число знаків, слів і т.п. Відкривати кожного разу Word, підрахункам і забивати її у форму введення було лінь, так що прийшла в голову думка цю справу автоматизувати. Інформації з цього питання знайти так і не вдалося, так що основним джерелом знань служили заголовний файл Word2000.pas і довідка за Visual Basic for Applications. Ну і, звичайно, безліч різних експериментів.
Відразу обмовлюся, що я не професійний програміст, так що в тонкощі інтерфейсів вникати не будемо - сам у них не особливо розуміюся. Тому, не мудруючи лукаво, просто помістимо на формі компоненти WordApplication і WordDocument з палітри Servers. Для роботи використовуються властивості і методи цих компонентів.
Вбудована статистика Word підраховує статистику звичайного тексту, звичайних і кінцевих виносок. Для підрахунку статистики використовується метод компонента WordDocument ComputeStatistic (). Він має один параметр, що характеризує, що саме вважати, що вдає із себе шістнадцяткову константу. Константи описані в заголовному файлі Word2000.pas, він лежить зазвичай в / Delphi / Ocx / Servers.
Шістнадцяткова | Символьне позначення | Сенс |
$ 00000000 | wdStatisticWords | Кількість слів |
$ 00000001 | wdStatisticLines | Кількість рядків |
$ 00000002 | wdStatisticPages | Кількість сторінок |
$ 00000003 | wdStatisticCharacters | Знаки без пробілів |
$ 00000004 | wdStatisticParagraphs | Кількість розділів |
$ 00000005 | wdStatisticCharactersWithSpaces | Знаки з пробілами |
Це було основне, що треба знати. Ну а тепер по порядку.
Помістивши на форму згадані компоненти, бачимо, що властивостей і методів у них зовсім мало. В першу чергу слід визначитися з методом ConnectKind компонента WordApplication. Воно може набувати різних значень, але ми залишимо присвоюються за замовчуванням значення ckRunningOrNew. Це означає, що з'єднання відбувається з вже працюючим сервером, при його відсутності запускається новий. Як правило, це цілком влаштовує.
Насамперед відкриємо документ. Попередньо треба оголосити змінну FileName, вона буде типу OleVariant, якої привласнимо рядок з ім'ям файлу.
WordApplication1.Connect;
WordApplication1.Documents.Open (FileName,
EmptyParam, EmptyParam, EmptyParam,
EmptyParam, EmptyParam, EmptyParam,
EmptyParam, EmptyParam, EmptyParam,
EmptyParam, EmptyParam);
WordDocument1.ConnectTo (WordApplication1.ActiveDocument);
Зверніть увагу на кількість параметрів-"пустушок". Їх число більше того, яке звичайно приводиться в книжках. Ну, в моїх, у всякому разі. Пояснюється це тим, що "книжкові" функції призначені для MS Word 97, а такий запис для роботи з Word 2000 і Word XP.
"Plain Text"
Оголосивши потрібну кількість змінних типу LongInt (в дуже великому файлі або при підсумовуванні по декількох документах в принципі може виявитися більше знаків, ніж межі звичайного цілого типу), можемо вже і приступати до підрахунку. Наприклад, порахуємо кількість слів, знаків з пробілами та без пробілів звичайного тексту, а також кількість сторінок у документі. Результати збережемо відповідно в "довгих" змінних WCount, SCount, CCount, і PCount.
WCount: = WordDocument1.ComputeStatistics ($ 00000000);
CCount: = WordDocument1.ComputeStatistics ($ 00000003);
SCount: = WordDocument1.ComputeStatistics ($ 00000005);
PCount: = WordDocument1.ComputeStatistics ($ 00000002);
Відкривши потрібний документ у Word'е і викликавши діалог підрахунку статистики, неважко побачити, що значення змінних рівні параметрами вордівський статистики зі скинутим прапорцем "Враховувати всі виноски".
Виноски
Посилання у документах можуть бути звичайні і кінцеві. Тобто якщо перші розташовуються внизу цієї сторінки, то кінцеві - строго в кінці документа. Крім того, вони можуть відрізнятися і нумерацією - автоматичною або заданої користувачем. Почнемо зі звичайних виносок як з самого простого. У термінології об'єктної моделі Word - Footnotes. Спочатку треба обчислити кількість самих виносок:
ifcount: = WordDocument1.DefaultInterface.Footnotes.Count;
Підрахунок статистики тексту у виносці проводиться так:
FWCount: = WordDocument1.DefaultInterface.Footnotes.Item (ifoot). Range.ComputeStatistics ($ 00000000);
Тут ifoot - ціле число, "Нумер" виноску. Для того, щоб врахувати самі номери виносок, зробимо так:
FWCount: = FWCount + WordDocument1.DefaultInterface.Footnotes.Item (ifoot). Reference.ComputeStatistics ($ 00000000);
Це ми порахували для прикладу кількість слів у виносці з номером ifoot і її мітці - при користувача нумерації в якості "номери" може бути ціле речення. Далі починаємо перебирати їх одну за одною. При цьому слід врахувати, що крім статистики виносок необхідно отримати і статистику їх "номерів". Тобто:
for ifoot: = 1 to ifcount do begin
FWCount: = FWCount + WordDocument1.DefaultInterface.Footnotes.Item (ifoot). Range.ComputeStatistics ($ 00000000);
FCCount: = FCCount + WordDocument1.DefaultInterface.Footnotes.Item (ifoot). Range.ComputeStatistics ($ 00000003);
FSCount: = FSCount + WordDocument1.DefaultInterface.Footnotes.Item (ifoot). Range.ComputeStatistics ($ 00000005);
FCCount: = FCCount + WordDocument1.DefaultInterface.Footnotes.Item (ifoot). Reference.ComputeStatistics ($ 00000003);
FSCount: = FSCount + WordDocument1.DefaultInterface.Footnotes.Item (ifoot). Reference.ComputeStatistics ($ 00000005) +1;
if WordDocument1.DefaultInterface.Footnotes.Item (ifoot). Reference.TextIntToStr (ifoot)
then begin
FWCount: = FWCount +
WordDocument1.DefaultInterface.Footnotes.Item (ifoot). Reference.ComputeStatistics ($ 00000000);
end;
end;
Додаток одиниці з'являється від того, що сума статистики виносок і номерів не збігається з тим, що видає вбудована статистика Word. Між номером виноски і текстом виноски Word ставить пробіл, який чомусь не враховується. Умовний оператор визначає, як пронумерована дана виноска - за умовчанням чи ні. В останньому випадку слід перевірити кількість слів у позначенні виноски. Така схема дає результат, що збігається зі свідченнями вбудованої статистики. Крім того, цикл у нас йде від 1 - так починається нумерація виносок в MS Word, та й практично всіх інших об'єктів теж.
Тепер перейдемо до кінцевим виносок. Теоретично все те ж саме, тільки замість слова "Footnotes" пишемо "Endnotes". І тут натрапляємо на сюрприз - чомусь вона вважає неточно. Я в даному випадку поступив так: зберігаю документ під іншим ім'ям, переконвертірую кінцеві виноски в звичайні і далі все, як сказано вище. Збереження документа:
WordDocument1.SaveAs (FileName, FileFormat),
де в дужках стоять два параметри типу OleVariant - ім'я файлу і шістнадцяткова константа, що задає формат файлу. Деякі константи:
|
Повний список констант формату можна знайти все в тому ж файлі Word2000.pas. І ще один цікавий момент - якщо просто поставити в дужки обидві константи, працювати не буде. Слід попередньо оголосити два змінних, присвоїти їм відповідні значення і тільки потім зберігати.
Ну, а тепер, власне, можемо повернутися до виносок. Конвертування кінцевих виносок на звичайні відбувається так:
WordDocument1.DefaultInterface.Endnotes.Convert;
Тепер ми маємо документ, в якому містяться лише звичайні виноски. З ними жодних проблем не виникає, приклад, як з ними працювати, див. вище. Якщо цікавить статистика окремо різних типів виносок, вважаємо попередньо статистику звичайних виносок, зберігаємо її в "буферних" змінних і вважаємо ще раз після конвертації. Різниця дасть статистику кінцевих виносок окремо. Склавши статистику виносок і простого тексту, отримуємо статистику документа з урахуванням виносок так, як її дає сам Word.
Додатково ...
Тут за традицією кілька покритикуємо Microsoft. Як виявилося, Word показує не все, що міститься в документі. Не приймаються в розрахунок колонтитули. Адже в них може міститися неабиякий шматок тексту, особливо у довідках, бланках і т.п. Виявляється, Word їх насправді вважає, але нам не показує. От і подивимося, як же його можна змусити це зробити.
Колонтитули в документі тісно пов'язані з дещо загадковою штукою під назвою "розділи" - Sections. Кожен розділ може мати верхні та нижні колонтитули. Тому першою справою визначаємо кількість абзаців.
isectct: = WordDocument1.DefaultInterface.Sections.Count;
Тут у нас цілі змінні isectct, icofct, icohct позначають відповідно кількість розділів як таких, кількість нижніх і верхніх колонтитулів даного розділу. Змінна isec служить "номером" розділи, змінні icof, icoh "нумерують" відповідно нижні і верхні колонтитули в межах даного розділу. Кількість колонтитулів у розділі визначаємо так:
icofct: = WordDocument1.DefaultInterface.Sections.Item (isec). Footers.Count;
icohct: = WordDocument1.DefaultInterface.Sections.Item (isec). Headers.Count;
Тепер вже можемо "дістати" текст з колонтитула:
CBWCount: =
WordDocument1.DefaultInterface.Sections.Item (isec). Footers.Item (icof). Range.ComputeStatistics ($ 00000000);
У даному випадку ми для прикладу порахували кількість слів, що містяться в нижньому колонтитулі під номером icof, що належить розділу під номером isec. Тепер можемо написати "подвійної" цикл для підрахунку статистики верхніх і нижніх колонтитулів. Повністю це буде виглядати так:
isectct: = WordDocument1.DefaultInterface.Sections.Count;
for isec: = 1 to isectct do begin
icofct: = WordDocument1.DefaultInterface.Sections.Item (isec). Footers.Count;
icohct: = WordDocument1.DefaultInterface.Sections.Item (isec). Headers.Count;
for icof: = 1 to icofct do begin
CBWCount: = CBWCount +
WordDocument1.DefaultInterface.Sections.Item (isec). Footers.Item (icof). Range.ComputeStatistics ($ 00000000);
CBCCount: = CBCCount +
WordDocument1.DefaultInterface.Sections.Item (isec). Footers.Item (icof). Range.ComputeStatistics ($ 00000003);
CBSCount: = CBSCount +
WordDocument1.DefaultInterface.Sections.Item (isec). Footers.Item (icof). Range.ComputeStatistics ($ 00000005);
end;
for icoh: = 1 to icohct do begin
CHWCount: = CHWCount +
WordDocument1.DefaultInterface.Sections.Item (isec). Headers.Item (icoh). Range.ComputeStatistics ($ 00000000);
CHCCount: = CHCCount +
WordDocument1.DefaultInterface.Sections.Item (isec). Headers.Item (icoh). Range.ComputeStatistics ($ 00000003);
CHSCount: = CHSCount +
WordDocument1.DefaultInterface.Sections.Item (isec). Headers.Item (icoh). Range.ComputeStatistics ($ 00000005);
end;
end;
У змінних, до яких на кожному кроці додаються результати статистики, після перебору всіх розділів накопичиться сумарна статистика слів, знаків з пробілами і знаків без пробілів у всіх колонтитулах.
Часто використовуються для малювання схемок текстові вставки з панелі малювання також представляють інтерес. Сам Word формально вважає їх "картинками", які не мають жодної статистики - мабуть, за географічним розташуванням в панелі інструментів. У об'єктної моделі - Shapes. Ось тут починається найцікавіше. По-перше, все, що знаходиться на панелі малювання, є Shapes. Тобто в принципі для Word'а усі були одно, текстова вставка, об'єкт WordArt або геометрична фігура. Разом з тим виглядає досить нелогічно, що цей самий Shape можна переконвертувати, на вибір, у Frame або InlineShape. Вони вже мають статистикою, так що, здавалося б, все в порядку. Але підступності Microsoft, здається, взагалі немає межі. По-перше з подивом виявляємо, що Shapes нумеруються індексом типу OleVariant. Що з ним далі робити, незрозуміло. Якщо просто привласнювати індексом ціле число, при конвертації кожного другого Shape під Frame отримуємо помилку. А якщо обробити виняток, то будемо таки мати статистику половини вставок. Мабуть, є якісь тонкощі з парними і непарними індексами. По-друге, InlineShape штука і зовсім загадкова. Ніяких помилок при конвертації не виникало, але й кількість InlineShapes незмінно виявлялося нульовим. Підрахувати статистику вставок вдалося тільки зберігши файл як RTF і розколупавши його код, але це варто описати окремо. Наводився ж останній абзац в надії, що хтось з таким стикався і знайшов спосіб роботи з Shapes "вбудованими" способами.
Ну, ось практично і все. Підсумовуючи все, що ми отримали, маємо статистику документа навіть точніше вбудованої. Ще пара зауважень. Перед підрахунком Word стоїть "сховати", щоб він не маячила на екрані:
WordApplication1.Visible: = False;
При підрахунку статистики, особливо якщо в документі міститься щось окрім простого тексту, вважається, що у файл внесені зміни. Тому наостанок зберігаємо і закриваємо документ:
WordDocument1.Save;
WordDocument1.Close;
Ну і, звичайно, робимо сервера Word Disconnect, коли він стане нам вже не потрібен.
А тепер попередження тим, хто зацікавився даним питанням і хоче поекспериментувати сам. Офіс занадто тісно пов'язаний з Windows, тому на збої в його роботі система реагує вкрай гостро. При налагодженні програм підрахунку статистики у мене після помилки часто з'являвся "блакитний екран". Те ж саме відбувалося, якщо після розбору RTF - файлу у пошуках Shapes і перетворення їх на звичайний текст у Word завантажувався неправильно "зібраний" файл. Так що дуже рекомендується попередньо зберегти важливі дані або поставити систему надійніше. У мене стоїть WindowsXP, який до таких збоїв виявився нечуствітелен. Крім того, поки наведене тут не було налагоджено, після помилок частенько летів сам офіс. Так що майте під рукою дистрибутив для запуску діагностики офісу і усунення пошкоджень.
Працюємо з таблицями
Кожен, напевно, хоч раз стикався з необхідністю видачі звіту. У Delphi є для цього спеціальні компоненти, але вони накладають на нас досить суворі обмеження на форму представлення даних. Одним з виходів може бути використання програми MS Word. Тут не будемо докладно обговорювати найпростіше запитання, як відкрити документ та додати в нього потрібний рядок тексту, це є практично в кожному підручнику з Delphi, наведемо тільки самі необхідні відомості. А з літератури на цю тему особливо рекомендується знайти книжку А.Я. Архангельського "Мова SQL у Delphi 5". Але що може надати звіту таку читабельність, як представлення результатів у систематизованому табличному вигляді? У цій статті і обговорюється питання програмної роботи з таблицями документа Word.
Тут можуть бути два шляхи. Перший - якщо ми знаємо заздалегідь структуру даних звіту, можемо приготувати шаблон, куди в елементи таблиці потім просто занесемо потрібні дані. І другий - створюємо звіт з нуля, малюємо в документі таблицю, заповнюємо її. При цьому ми можемо програмно додати або видалити рядки і стовпці, об'єднати чи розбити осередку - майже все, що ми робимо в самому Word'e. Все, що знадобиться - компоненти WordApplication і WordDocument з палітри Servers
Тепер все по порядку - відкриваємо файл і приступаємо. Попередньо оголошуємо змінну FileName, типу OleVariant, якої присвоюємо рядок з ім'ям файлу.
WordApplication1.Connect;
WordApplication1.Documents.Open (FileName,
EmptyParam, EmptyParam, EmptyParam,
EmptyParam, EmptyParam, EmptyParam,
EmptyParam, EmptyParam, EmptyParam,
EmptyParam, EmptyParam);
WordDocument1.ConnectTo (WordApplication1.ActiveDocument);
Зверніть увагу на кількість параметрів - "пустушок". Їх кількість не збігається з тим, що звичайно приводиться в книжках. Пояснюється це тим, що "книжкова" функція призначена для MS Word 97, а такий запис для Word 2000 і Word XP.