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