Работа с Ворд через СОМ-Объект

15.02.21

Интеграция - Внешние источники данных

Статья представляет собой еще один велосипед на тему формирования документов на основе шаблонов через СОМ-Объект. Как и все другие велосипеды, этот написан потому, что другие велосипеды - плохие. В действительности, если погуглить, мы получим огромное количество ссылок на советы в стиле "сделай так и будет тебе счастье". Но почему "так", зачем "так", какие есть альтернативы - авторы советов не раскрывают. Хуже того, половина найденных таким способом рецептов окажутся откровенно вредными. Судя по тому, как вредные рецепты копируются из совета в совет, большинство разработчиков, из тех, что переписываются на форумах, просто воспроизводят то, что когда-то нагуглили сами. Ну да, работает - не трогай :-)

Цель статьи - собрать в одном месте необходимый и достаточный набор рецептов по заполнению документов Ворд на основании шаблонов с помощью СОМ-Объекта

Оглавление:

Зачем и почему нужны файлы Ворд?

Что значит сформировать файл Ворд?

Способы формирования файла Ворд

Объектная модель Ворд

Запуск Ворд, открытие файла

Закрытие файла, завершение работы с СОМ-Объектом

Заполнение шаблона с помощью именованных полей (свойств документа)

Заполнение шаблона через поиск и замену

Особенности работы с колонтитулами

Заполнение шаблона с помощью закладок

Удаление фрагментов текста

Вставка картинок

Работа с таблицами

 

Зачем и почему нужны файлы Ворд?

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

Потому, что пользователю нужны файлы. Не распечатанные из информационной системы листочки, а именно текстовые файлы, которые можно отправить клиенту по эл. почте или мессенджеру, которые можно отредактировать, подправить, получить по эл. почте и переслать дальше с комментариями, сохранить в качестве образца и распечатать. Конечно, пользователь пользователю рознь. В некоторых корпоративных структурах тратятся огромные силы и средства на то, чтобы убрать у пользователя возможность что-то сделать с файлом, а еще лучше убрать сами файлы. Пользователь из лучших побуждений подправит документ, внесет в него ошибку, которую потом разгребать будут десятки людей. Но мы на них равняться не будем. Всегда будут процессы не дошедшие до стадии "делать по регламенту или никак", и всегда будут пользователи которые хотят работать с файлами.

Потому, что пользователь для работы с текстом использует текстовый процессор. Строго говоря, кроме Ворда всегда существовали альтернативные программы, не уступающие по функционалу. Сейчас 2021 год и пользователи давно привыкли к таким вещам как жесткий пробел, запрет висящих строк, абзац, начинающийся с новой страницы, межстрочный интервал, колонтитулы и абзацные отступы. Довольно часто можно столкнуться с заявлением "наша разработка может формировать документы в формате Ворд". При дальнейшем рассмотрении оказывается, что "шаблон" хранится непосредственно в информационной базе либо в виде текста, либо в виде форматированного текста, либо в виде набора абзацев и так далее. Как бы ни старались разработчики платформы и приложения, полученный файл это не тот "Ворд", которого ожидает пользователь. Этот эрзац всегда является компромиссом между полноценным файлом Ворда и возможностями системы автоматизации. Для того, чтобы формировать полноценный файл Ворда, со всеми возможностями форматирования, которыми пользователи привыкли пользоваться, требуется пересоздать Ворд внутри информационной системы, что практически недостижимо.

Потому, что никакие другие файлы кроме файлов Ворда не будут приняты партнерами пользователя. В этом смысле Ворд безальтернативен т.к. все пользуются именно Вордом. Можно сколько угодно рассуждать о преимуществах Лайбре Офиса, но, когда ежедневно нужно обмениваться документами с десятком новых людей, без Ворда не обойтись.

Что значит сформировать файл Ворд?

В связи с неоднозначностью сложившейся терминологии, считаю нужным пояснить. Под формированием файла Ворд по пользовательскому шаблону я подразумеваю следующий процесс. Пользователь передает разработчику файл с образцом документа, который хочет получать из информационной системы в автоматизированном режиме. Разработчик дорабатывает информационную систему таким образом, чтобы она позволяла формировать файлы, точно соответствующие первоначальному образцу. Точно, значит без явных указаний пользователя разработчик не меняет по своему усмотрению ни одного элемента форматирования, ни шрифт, ни размер, ни поля, ни колонтитулы. НИЧЕГО не меняет. Совершенно ничего. На выходе пользователь должен получить документ неотличимый от документа, созданного собственноручно.

Способы формирования файла Ворд

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

Существует два популярных способа работы с файлами Ворд:

1. Через СОМ-Объект Ворд.

2. Через непосредственную модификацию файлов docx

Главное отличие между ними заключается в том, что первый способ предоставляет разработчику инструмент - полнофункциональный API, позволяющий делать с документом все то же самое, что может делает пользователь, непосредственно работая в редакторе. Второй же способ предполагает собственную разработку инструмента, на основании документации по формату хранения файла. Если функциональность разработанного инструмента перестанет удовлетворять возникающим потребностям - придется разрабатывать (или искать) новый инструмент. В то время как модернизация кода основанного на API СОМ-объектов не представляет сложностей.

Есть еще и третий способ - через API для работы с файлами формата docx (он же формат Open XML) платформы .NET Framework (набор средств для работы с файлами docx Майкрософт продвигает под названием Open XML SDK). Для мира 1С этот путь совсем уж экзотический, нужно создать собственный интерфейсный слой (на другом языке программирования) между приложением 1С и API платформы .NET.

Второй способ основан на том, что любой файл .docx представляет собой архив zip с содержимым документа в виде файлов xml и, в некоторых случаях, графических файлов. Работа с zip, текстовыми и xml файлами штатно поддерживается языком 1С, поэтому, если разбираться в формате файла, то создавать или модифицировать их можно быстро и просто. Ключевое слово, как обычно, "если" :-) Этот путь в статье не рассматривается. Всем желающим рекомендую готовое решение, которое перекрывает большую часть потребностей пользователей: //infostart.ru/public/675307/

Первый способ предполагает, что на том компьютере, где будет исполняться код приложения и формироваться файл, установлен Майкрософт Офис. При установке, Офис регистрирует в Виндоус СОМ-объекты для каждого из приложений входящих в пакет. Таким образом, другие приложения, запущенные на компьютере, могут через интерфейс СОМ-объектов выполнять любые действия с документами офисных приложений, возможности предоставленного программного интерфейса, как минимум, не уступают возможностям пользователя, редактирующего документ непосредственно в приложении.

Строго говоря, работать с СОМ-объектами Ворд можно как на сервере, так и на клиенте. Но Макрософт не считает компоненты Офиса серверной технологией: http://support.microsoft.com/ru-ru/topic/%.... Как следует из статьи, работать с СОМ-Объектами Ворд на сервере не рекомендуется, работать это начнет только после танцев с бубном и работа будет нестабильной. Хотя в интернете и есть множество примеров работы с СОМ-объектами именно из серверного кода, я буду исходить из того, что СОМ-Объект исполняется на клиентском компьютере.

Главная проблема примеров из интернета по рассматриваемой теме - игнорируется тот факт, что за компьютером сидит живой пользователь. Видимо, предполагается, что после нажатия кнопки "сформировать файл", пользователь должен замереть, не дышать, не дотрагиваться до мыши и клавиатуры в течение всего времени создания файла. А ведь формирование каждого файла может занимать заметное время, в некоторых случаях - больше минуты. И ладно бы вопрос стоял "или так или никак". Но ведь на самом деле, нет. Можно работать с СОМ-Объектом параллельно с работой пользователя, код чувствительный действиям пользователя не имеет никаких преимуществ перед кодом, не мешающим пользователю. Единственная причина, по которой он используется - нежелание разработчика вникнуть в проблему и рассмотреть альтернативные варианты решения.

Код языка 1С для работы с СОМ-Объектами на сервере и на клиенте ничем не отличается. Единственное, за сервером не работает реальный пользователь, поэтому проблема неудачного кода не выплывает с такой остротой. Повторюсь, преимуществ у такого кода все равно нет. Тем более, что код нужно отлаживать, часто это удобнее делать на клиенте, вот и появился пользователь. А еще, может потребоваться перенос работающего кода с сервера на клиент, например, в рамках рефакторинга и изменения архитектуры приложения. А еще, код может работать в файловой базе, где "сервер" очень условен - программный слой, запускаемый на машине каждого пользователя. Таким образом, лучше всего исходить из того, что пользователь за компьютером есть всегда.

Объектная модель Ворд

Ворд, запущенный в качестве СОМ-Объекта, предоставляет доступ к множеству объектов приложения. Понятие "объект" такое же как везде - программная абстракция, имеющая методы и свойства. Методы могут быть как процедурами, так и функциями, при этом функции можно вызывать как процедуры, игнорируя возвращаемое значение. Свойства могут быть либо объектами, либо обычными (скалярными, примитивными) данными, такими как число, строка, логический тип.

Среди объектов особо выделяются специальные объекты-контейнеры, называемые коллекциями. Каждая коллекция предназначена для хранения ссылок на группу однотипных объектов. Например, коллекция Documents хранит ссылки на объекты Document, коллекция Tables хранит ссылки на объекты Table и т.д. Все коллекции именованы во множественном числе, а обычные объекты в единственном. Нумерация объектов в коллекции начинается с единицы. Коллекции Ворд можно перебирать циклом "Для каждого" языка программирования 1С.

Полный список объектов можно, и нужно, смотреть в справочной системе Ворд (раздел справки по Вижуал Бейсику (VBA)). Для старых версий справка откроется локально на компьютере пользователя. Новые версии отправят на сайт Майкрософт: https://docs.microsoft.com/ru-ru/office/vba/api/overview/word/object-model Текст переведен с английского автоматически и местами понять написанное невозможно. Переключение на английский оригинал делается очень просто, достаточно нажать кнопочку "почитать на английском" в правом верхнем углу страницы. Или в адресе ссылки "ru-ru" заменить на "en-us": https://docs.microsoft.com/en-us/office/vba/api/overview/word/object-model

Многие методы объектов Ворда в качестве параметров принимают числовые значения. Для того, чтобы не путаться в числовых значениях, в Вижуал Бейсике предусмотрены именованные константы. Например, константа wdFormatRTF соответствует числу 6. В Вижуал Бейсике можно писать код так:

Document.SaveAs ("имя файла", wdFormatRTF)

Вне VBA именованные константы недоступны, поэтому код 1С будет таким:

Document.SaveAs ("имя файла", 6)

Или, без “магических” чисел, таким:

wdFormatRTF = 6;
Document.SaveAs ("имя файла", wdFormatRTF);

Я предпочитают последний вариант, терпеть не могу непонятных чисел в коде.

Значения всех констант нужно смотреть в справочной системе, там они объединены в смысловые группы, называемые "перечисления" (Enumerations): https://docs.microsoft.com/ru-ru/office/vba/api/word(enumerations)

Запуск Ворд, открытие файла

Традиционно, первые три строчки работы СОМ-Объектом Ворд выглядят так:

Word = Новый COMОбъект ("Word.Application");
Word.Visible = Ложь;
Word.Documents.Open ("C:\готовые документы\файл.docx");

… и это уже неправильно! Третья строчка сразу говорит о том, что перед вами бездумная копипаста. Справедливости ради, бывают и толковые примеры, начинающиеся именно так, но, это лишь подтверждает, что даже в толковых примерах присутствует копипаста, не осмысленная автором. Что неправильно с третьей командой будет объяснено ниже, сначала разберем первые две строки.

Word = Новый COMОбъект("Word.Application");

Запускает приложение Ворд и возвращает на него ссылку. Теперь по ссылке Word мы можем обращаться к СОМ-объекту. Возникает закономерный вопрос, влияет ли как-то окружение, в момент выполнения команды, на результат её работы? Из окружения нас интересует в первую очередь интерактивная работа пользователя с Вордом. Возможны два варианта:

- в момент выполнения Ворд запущен (имеются открытые пользователем файлы)

- в момент вызова Ворд не запущен

Так вот, нет, не влияет. Ворд запускается как новый экземпляр приложения, коллекция Documents запущенного приложения не содержит ни одной ссылки, т.е. доступа к ранее открытым документам через ссылку Word не получить.
Но, после запуска СОМ-Объекта, новые, открываемые пользователем, файлы могут открыться именно в этом экземпляре Ворда. Для пользователя разницы нет, но нам это следует, на всякий случай, учитывать.

Word.Visible = Ложь; // (или Word.Visible = Истина;)

Скрывает Ворд. Т.е. пользователь не будет видеть обрабатываемый файл в отдельном окне. Никакой особой пользы скрытие окна Ворд не несет. Если программа написана корректно, то пользователь не будет мешать программе, а программа не будет мешать пользователю, вне зависимости от того, видимо ли окно обрабатываемого файла. Естественно, при условии, что пользователь не начнет редактировать открытый документ.

Свойству Visible желательно присвоить одно из значений. В противном случае, СОМ-Объект Ворд откроется в скрытом режиме. Но если пользователь откроет какой-нибудь файл, то наш редактируемый файл может неожиданно стать видимым. С другой стороны, если пользователь успеет открыть свой файл после запуска СОМ-Объекта, но перед тем, как будет выполнено присвоение свойству значения Ложь, то открытый файл станет невидимым и недоступным для редактирования пользователем.

В общем, лично я предпочитаю оставлять редактируемый документ в видимом окне.

Перейдем к неправильной части примера

Word.Documents.Open ("C:\готовые документы\файл.docx");

Открывает файл, в терминологии Ворд открытый файл называется "документ". Далее нужно получить ссылку на открытый документ, чтобы потом по этой ссылке можно было с ним работать. Вы можете найти такие примеры:

Word.Documents.Open ("C:\готовые документы\файл.docx");
Документ = Word.Documents (1);

Ну что ж, разумно. Предполагаем, что открыт только наш документ, соответственно в коллекции Documents есть единственный элемент, значит этот элемент имеет индекс равный единице. Это работает, вероятность того, что пользователь успеет открыть файл в промежуток времени, между запуском СОМ-Объекта и этим присваиванием, ничтожно мала.

Или так:

Word.Documents.Open ("C:\готовые документы\файл.docx");
Документ = Word.ActiveDocument;

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

Примеры чуточку безумные:

Word.Documents.Open ("C:\готовые документы\файл.docx");
Word.Documents(1).Activate();
Документ = Word.ActiveDocument;

WTF? Хотя, безусловно, работает. С теми же оговорками, что и выше.

Самые безумные примеры, которые вы сможете найти на просторах Интернета, сводятся к тому, что ссылку на документ не сохраняют в отдельной переменной, а каждый раз обращаются к документу через свойство ActiveDocument. И это, безусловно и неизбежно, порождает проблемы при одновременной работе пользователя и программы. Если пользователь кликнет мышью в другой файл, то значение свойства ActiveDocument перестанет соответствовать нашему файлу и произойдет ошибка времени исполнения. Это в лучшем случае. В худшем - будет “отредактирован”, читай поврежден, файл пользователя. Вот, посмотрите на код, это оно и есть: https://www.cyberforum.ru/1c-custom/thread2724654.html

Как программировать неправильно, выяснили, теперь о том, как открывать документы правильно.

Для открытия документа можно использовать два метода коллекции Documents. Первый -  Open() приведен выше, второй - Add().

Метод Open() открывает файл для редактирования в Ворде, документ остается связанным с этим файлом - в заголовке окна Ворд указывается имя файла. Открыть можно любой файл, поддерживаемый Вордом, формат файла при редактировании не меняется. Например, открыли файл .rtf, отредактировали, сохранили, файл остался .rtf. Открывать непосредственно шаблон из папки, в которой хранятся шаблоны таким способом не стоит. Если что-то произойдет не так, например, сработает автосохранение в Ворде, шаблон будет испорчен. Сначала файл шаблона следует скопировать (при работе в клиент-серверном варианте это само собой разумеющиеся действие), а потом можно открывать.

Метод Add() создает новый документ на основании указанного шаблона. Шаблоном может быть любой файл, не обязательно родные для Ворда .doc или .docx или .dot. Открытый документ с файлом-шаблоном не связывается, в заголовке окна Ворд будет надпись "Документ1 - Word". При этом информация о формате исходного файла не сохраняется. Открывается просто документ Ворд, содержащий в себе всю информацию из указанного файла. При сохранении без явного указания типа файла он будет сохранен как .docx. Единственное исключение - документ, открытый на основе файла .doc, такие документы по умолчанию сохраняются в файлы .doc. Метод Add() можно использовать для создания документов на основании оригинала шаблона, шаблон повредить невозможно даже случайно.

Методы Open() и Add() являются функциями, т.е. возвращают значение. Причем возвращают они ссылку на новый документ. И ловить потом этот документ в коллекции Documents нам не нужно, ссылку можно сразу сохранить в переменной для дальнейшего использования. Таким образом, для открытия используются следующие команды:

Документ = Word.Documents.Open ("C:\готовые документы\файл.docx");

Или

Документ = Word.Documents.Add ("C:\шаблоны\файл.docx");

Закрытие файла, завершение работы с СОМ-Объектом

Когда шаблон заполнен требуемым образом его необходимо сохранить. Самый простой вариант, если файл открыли методом Open(), для сохранения достаточно вызвать метод Save():

Документ.Save();

Если же документ еще не связан с файлом, нужно вызвать метод SaveAs():

Документ.SaveAs ("C:\готовые документы\файл");

Если формат по умолчанию нас не устраивает, вторым параметром метода можно явно задать тип файла:

wdFormatRTF = 6;
Документ.SaveAs ("C:\готовые документы\файл", wdFormatRTF);

Обратите внимание, не стоит указывать расширение файла. Ошибки не будет, если расширение будет указано явно, например так:

Документ = Word.Documents.Add ("C:\шаблоны\файл.doc");
//...
Документ.SaveAs ("C:\готовые документы\файл.doc");

Имеется в виду, что не будет ошибки времени выполнения. Однако, приведенный выше пример идеологически неверен. Допустим, через некоторое время шаблон был заменен и файл шаблона получил расширение .docx. Для внесения изменений в программу нам потребуется две правки, первая в методе Add(), вторая в методе SaveAs(). В этом и проблема, код дублируется, одно изменение должно вносится в одном месте программы, а не в разных. Если расширение файла не будет указано, при сохранении будет добавлено расширение, соответствующее типу сохраняемого файла. Если же расширение будет указано неверно, т.е. не будет соответствовать типу файла, то... либо получаем ошибку времени исполнения, либо файл сохранится в формате, не соответствующем расширению (в зависимости от комбинации тип/расширение возможно и то и другое). Обе ситуации ненормальны. Ошибка времени выполнения, конечно, будет отловлена на этапе тестирования. А ошибка несоответствия расширения фактическому типу данных выйдет потом боком пользователю.

Иногда после сохранения файла нужно узнать его полное имя, т.е. включая путь и расширение. Например, чтобы отправить файл по электронной почте или записать в базу. Если мы сохранили файл методом SaveAs(), то полного имени мы не знаем, ведь расширение к имени файла добавил Ворд. Ну так из Ворда же полное имя можно получить, у документа есть доступное для чтения свойство FullName:

ПолноеИмяФайла = Документ.FullName;

Завершение работы с СОМ-Объектом рекомендуют (неправильно) делать так:

Документ.Close();
Word.Quit(0);

На первый взгляд, можно и не заметить нолик в параметре метода Quit(). А он важен. Это просто константа wdDoNotSaveChanges :-). Закрыться-то Ворд закроется, вместе со всеми файлами, которые пользователь, возможно, открыл и редактирует.

Если мы исходим из предположения, а мы из него исходим, что пользователь может работать интерактивно со своими собственными файлами, которые могут быть открыты в том же экземпляре Ворда, что и наш СОМ-Объект, то торопиться принудительно выгонять пользователей и закрывать экземпляр приложения не стоит. Можно проверить, есть ли еще открытые документы, кроме нашего. Если есть, не закрывать экземпляр Ворда, пусть пользователь спокойно работает, если других документов нет - закрыть:

Документ.Close();
Если (Word.Documents.Count = 0) Тогда
    Word.Quit();
КонецЕсли;

Заполнение шаблона с помощью именованных полей (свойств документа)

Не очень популярный, но иногда встречающийся способ.

Сначала в документ добавляются пользовательские свойства. Для Ворда 2016 это делается так:

- открыть документ

- вкладка "Файл"

- Сведения /Свойства / Дополнительные свойства

 

- вкладка "Прочие"

 

- ввести название (имя) свойства и какое-нибудь значение

- нажать "Добавить"

- и т.д. создать все необходимые для заполнения шаблона свойства.

Имя свойства вводим разумное, по нему будем обращаться к свойству из программы, значение - произвольный текст, для того чтобы видеть, как свойство отображается в документе.

Если у файла .doc уже есть хотя бы одно дополнительное свойство, то в свойствах файла появляется соответствующая закладка и работать со свойствами (добавлять, удалять) можно прямо из проводника, не запуская Ворд

 

Забавно, что для файлов .docx это не работает. Наличие дополнительных свойств никак не отображается в свойствах файла.

Для того чтобы удобнее было работать с текстом документа включим выделение полей:

- вкладка "Файл"

- Параметры

- Дополнительно

- Затенение полей установить "Всегда"

 

В результате поля в тексте документа будут выделены серой заливкой. Серая заливка отображается только при просмотре документа на экране. При печати же никакой заливки не будет.

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

- Вкладка "Вставка"

- Раздел "Текст"

- Кнопка "Экспресс-блоки"

- "Поле..."

 

- выбрать вид поля "DocProperty"

- в списке "Свойства поля" будут все добавленные нами свойства документа. Если имена свойств давались русскими символами, то искать их надо внизу списка:

 

- "Ок"

Для того чтобы быстро посмотреть, какому свойству документа соответствует то или иное вставленное в текст поле можно кликнуть правой мышью по полю, в контекстном меню выбрать либо пункт "Изменить поле...", тогда вернемся к окну отображенному на скриншоте выше. Либо выбрать пункт "Коды/значения полей", тогда вместо значения поля будет отображена служебная информация, в которой указано имя свойства:

Получить доступ к свойствам можно через коллекцию CustomDocumentProperties документа:

Документ.CustomDocumentProperties("ЗаказчикДолжностьПодписантаРП").Value = "директора";

Важный момент, который стоит учитывать, заключается в том, что присвоение свойствам новых значений никак не отображается в тексте документа. Свойство само по себе, поле, отображающее значение свойства в тексте документа, само по себе. Для того чтобы поле отобразило актуальное значение свойства, поле необходимо обновить. Обновить все поля документа можно командой:

Документ.Fields.Update();

Такой метод заполнения шаблонов обладает рядом недостатков, поэтому используется редко.

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

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

Наконец, в результате пользователь получает не совсем такой файл, который передал разработчику. При редактировании поля себя ведут не так как простой текст. Если удалять или вставлять текст внутри поля, то никаких аномалий не проявляется. Если же попытаться удалить текст с переходом через границу поля (клавишами <Delete> или <BackSpace>), то при достижении границы поля оно норовит удалиться целиком. Это очень неудобно и раздражает.

Если пользователь возьмет заполненный документ и использует его как основу для создания нового документа вручную, то текст документа не будет соответствовать значениям свойств документа. И если в какой-то момент пользователь по ошибке ли, по подсказке ли Ворда, сам ли Ворд, обновит значения полей, то ручные правки пользователя окажутся потерянными. Хуже того, значения полей могут хранить информацию, разглашение которой нежелательно, а пользователь даже не подозревает об этом.

Если пользователь скопирует фрагмент заполненного документа, содержащего поля, то поля скопируются вместе со значениями, т.е. пользователь не увидит никаких изъянов после копирования. Но свойства документа вслед за текстом не копируются. Если в новом документе по каким-то причинам будут обновлены значения полей, то вместо разумного текста в документе появятся надписи "Ошибка! Неизвестное имя свойства документа".

Заполнение шаблона через поиск и замену

В основе метода лежит размещение в тексте шаблона специально выделенных с помощью редко встречающихся символов блоков текста:

В данном случае для выделения блоков использованы символы "$(" и ")$". Затем средствами поиска и замены Ворда каждый такой блок заменяется на соответствующее значение из базы. Эти заменяемые блоки можно называть полями, параметрами, закладками, реквизитами, но все эти термины уже используются в обсуждаемой предметной области. Чтобы не создавать дополнительной путаницы, далее по тексту я буду называть такие блоки якорями.

Внутри метода существуют два подхода. Первый (неправильный и при этом наиболее распространенный) основан на использовании объектов Selection, второй на использовании объектов Range.

Объект Selection представляет текущий выделенный фрагмент. Выделенный фрагмент представляет выбранную (или выделенную) область в документе или представляет точку вставки, если в документе ничего не выбрано. Каждый раз, когда пользователь выделяет фрагмент мышью или удерживая <Shift> - создается объект Selection. В каждом документе может быть одновременно не более одного объекта Selection.

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

Для редактирования документа без интерактивного взаимодействия с пользователем наиболее удобны объекты Range. В одном документе может быть сколько угодно объектов Range. Каждый документ Range представляет собой ссылку на фрагмент документа, определяемый номерами начального и конечного символов. Свойства Range.Start (номер первого символа диапазона) и Range.End (номер последнего символа диапазона) доступны для записи. Записывая новые значения в эти свойства, можно перемещать объект Range в документе.

У объектов Selection и Range есть свойство Text доступное для записи. Присвоение этому свойству нового значения эквивалентно замене текста в документе.

У объектов Selection и Range есть объектное свойство Find, с помощью которого доступен функционал поиска и замены Ворда. Для выполнения поиска нужно вызвать метод Execute() объекта Find. Поиск осуществляется внутри выделения Selection или внутри диапазона Range . После вызова Execute() соответствующий объект Selection или Range изменяется и соответствует найденному фрагменту:

Документ.Select();
Word.Selection.Find.Execute("$(ЗаказчикДолжностьПодписантаРП)$");
Word.Selection.Text = "директора";

или

Range = Документ.Content;
Range.Find.Execute("$(ЗаказчикДолжностьПодписантаРП)$");
Range.Text = "директора";

В этих примерах если текст (якорь) "$(ЗаказчикДолжностьПодписантаРП)$" есть в тексте шаблона, он будет заменен на текст "директора". Если же искомого фрагмента в тексте документа нет, то весь текст документа будет заменен единственным словом, т.к. размеры объектов Selection и Range не изменятся. Если мы предполагаем, что в шаблоне может и не содержаться какого-то якоря, то нужно проверять значение, возвращаемое функцией Execute(). Если поиск успешен, то возвращается Истина, иначе Ложь.

Range = Документ.Content;
Если Range.Find.Execute("$(ЗаказчикДолжностьПодписантаРП)$") Тогда
    Range.Text = "директора";
КонецЕсли;

Опять же, в тексте может быть и несколько одинаковых якорей, тогда нужно организовать цикл.... Но так никто не делает, т.к., во-первых, метод Execute(), может сразу выполнять замену найденного элемента. Для этого десятым(!) параметром в него нужно передать строку, на которую будет заменен найденный фрагмент. Во-вторых, метод Execute() может заменить сразу все вхождения строки поиска, для этого одиннадцатым параметром в метод нужно передать значение константы wdReplaceAll

Range = Документ.Content;
WdReplaceAll = 2;
Range.Find.Execute("$(ЗаказчикДолжностьПодписантаРП)$",,,,,,,,, "директора", wdReplaceAll);

За кадром остался вопрос, если обращение к выделению через Word.Selection - вселенское зло, то как следует правильно обращаться к объекту Selection? Конечно, правильно к нему вообще не обращаться и пользоваться только объектами Range.

Но если все-таки почему-то нужен именно объект Selection, то для минимизации интерактивного взаимодействия с пользователем обращаться нужно именно к объекту Selection нашего документа, а не всего приложения. Но у объекта Document нет свойства Selection! Это потому, что активный документ может состоять не из одного окна, а из нескольких. Например, открыли вы окно свойств абзаца у документа, документ тот же, а окна у документа уже два - одно с текстом документа, второе с параметрами абзаца. Так вот, Selection, это свойство именно окна. Поэтому для работы с объектами Selection нужно сразу сохранить ссылку на главное окно документа. Сделать это разумнее всего сразу после открытия документа методами Add() или Open():

Документ = Word.Documents.Add ("C:\шаблоны\файл.docx");
ОкноДокумента = Документ.Windows(1);
//...
Документ.Select();
ОкноДокумента.Selection.Find.Execute("$(ЗаказчикДолжностьПодписантаРП)$");
ОкноДокумента.Selection.Text = "директора";

Особенности работы с колонтитулами

На самом деле, указанные выше приемы поиска и замены текста работают только в основном тексте документа и не затрагивают колонтитулы. Вообще, в Ворде колонтитулы очень обособлены и даже в простом документе их может быть несколько. Во-первых, есть нижние и верхние колонтитулы, во-вторых, колонтитулы первой и последующей страниц могут быть различными, наконец, у каждого раздела документа могут быть собственные колонтитулы. Для того, чтобы получить доступ ко всем колонтитулам документа нужно перебрать коллекции Headers и Footers, затем в каждом колонтитуле выполнить поиск и замену якорей. Но в документе нет этих коллекций. В документе есть коллекция Sections (разделы), в каждом разделе есть свои коллекции Headers и Footers. Таким образом, перебор всех колонтитулов выполняется так:

WdReplaceAll = 2;
Для Каждого Section Из Документ.Sections Цикл
    Для Каждого Header Из Section.Headers Цикл
        Range = Header.Range;
        Range.Find.Execute("$(якорь)$",,,,,,,,, "значение", wdReplaceAll);
    КонецЦикла;
    Для Каждого Footer Из Section.Footers Цикл
        Range = Footer.Range;
        Range.Find.Execute("$(якорь)$",,,,,,,,, "значение", wdReplaceAll);
    КонецЦикла
КонецЦикла;

Более лаконичное решение заключается в использовании коллекции StoryRanges. Эта коллекция содержит диапазоны (range) всех блоков(?), частей(?) (в справке используется термин story) документа. Тело документа, это главный блок (main story), тоже есть в этой коллекции. Т.е. необязательно искать якоря отдельно в теле документа, отдельно в колонтитулах, все можно сделать одним циклом:

Для Каждого Range Из Документ.StoryRanges Цикл
    Range.Find.Execute("$(якорь)$",,,,,,,,, "значение", wdReplaceAll);
КонецЦикла;

Заполнение шаблона с помощью закладок

Технология неплохо описана в этой статье: http://blagin.ru/ispolzovanie-shablonov-word-v-1s/

Только вместо

ШаблонВорд.Bookmarks("Закладка1").Select();
ШаблонВорд.Application.Selection.TypeText("Текст для закладки №1.");

Нужно использовать

Range = ШаблонВорд.Bookmarks("Закладка1").Range;
Range.Text = "Текст для закладки №1.";

Теперь вы знаете почему и, вообще, готовы к статьям из Интернета :-)

Замечу, оба варианта удаляют закладку, на место которой вставляется текст. В большинстве случаев это удобно: пользователь получает полностью чистый документ, без остаточных следов шаблона. Да и программу можно писать через цикл "пока в документе есть закладки повторять..." 

Если же в готовом документе закладки должны остаться, то сразу после вставки текста удаленную закладку можно пересоздать. В варианте с Range для этого достаточно добавить команду: 

ШаблонВорд.Bookmarks.Add("Закладка1", Range);

Вариант с Selection.TypeText() придется немного переделать, т.к. после метода TypeText() объект Selection будет указывать на точку ввода после последнего добавленного символа. Чтобы выделение не схлопнулось в точку ввода, а соответствовало добавленному тексту, можно воспользоваться обращением к свойству Text объекта Selection

ШаблонВорд.Bookmarks("Закладка1").Select(); 
ШаблонВорд.Application.Selection.Text = "Текст для закладки №1."; 
ШаблонВорд.Bookmarks.Add("Закладка1", ШаблонВорд.Application.Selection.Range); 

На первый взгляд может показаться, что решение с закладками - вариация решения со свойствами документа. Но это совсем не так, закладки позволяют делать то же самое, что и поиск - установить объект Range на нужный фрагмент в тексте. Далее найденную позицию можно использовать как угодно - заменить текст, вставить в эту точку картинку, удалить большой блок текста между двумя найденными позициями и т.д. Со свойствами, отображаемыми с помощью полей, ничего подобного организовать не получится.

У закладок есть особенность, которой не обладают якоря для поиска и замены - имя закладки уникально. В этом основной смысл закладок - однозначно определить участок текста. Но при заполнении закладок реквизитами из базы это может вызвать некоторое неудобство. Вот, к примеру, статья: //infostart.ru/1c/articles/1122573/

Главная проблема у автора не в закладках, а в сакральности кода движка, осуществляющего заполнение шаблона Ворд. Да, если возможности подправить движок нет, то приходится мириться и с автозапуском макросов в документах и с добавлением в документы полей. Если же контроль над движком у нас есть, то проблема и не проблема вовсе. Вот ключевая фраза из обсуждаемой статьи:

Действительно, для решения проблемы, закладку "Ответственный" не надо создавать вообще. Нужно сразу создавать закладки вида "Ответственный0" или "Ответственный_". Последний символ не важен, лишь бы требованиям к именам закладок удовлетворял.

При заполнении шаблона, перебираем все закладки, отбрасываем последний символ имени закладки и по оставшейся смысловой части имени ищем значение в заранее подготовленной структуре данных:

Пока Документ.Bookmarks.Count > 0 Цикл	 
	Закладка = Документ.Bookmarks(1); 
	ИстинноеИмяЗакладки = Лев(Закладка.Name, СтрДлина(Закладка.Name) - 1); 
	Range = Закладка.Range; 
	Range.Text = СтруктураСДанными[ИстинноеИмяЗакладки]; 
КонецЦикла;

Удаление фрагментов текста

Сложные документы могут содержать вариативные части. Например, если предусмотрена предоплата, то соответствующий раздел договора излагается в одной редакции, а если рассрочка, то в другой. Пользователь готовит шаблон со всеми возможными вариантами редакций, блоки, которые могут быть удалены помечаются специальными якорями. Затем, либо удаляется весь отмеченный блок, либо удаляются только якоря, а блок остается.

RangeBegin = Документ.Range();
RangeBegin.Find.Execute("#(Блок1_begin)#");
Range = Документ.Range();
Range.Find.Execute("#(Блок1_end)#");
Range.Start = RangeBegin.Start - 1;
Range.Text = "";

Range = Документ.Range();
Range.Find.Execute("#(Блок2_begin)#");
Range.Start = Range.Start - 1;
Range.Text = "";

Range = Документ.Range();
Range.Find.Execute("#(Блок2_end)#");
Range.Start = Range.Start - 1;
Range.Text = "";

Может вызвать удивление, что за таинственные константы "1" используются для расчета начального символа удаляемого диапазона. Дело в том, что якорь всегда будет отделен от остального текста либо пробелом, либо символом конца абзаца, т.е. будет находится на отдельной строке. Если удалить только якорь, то в точке, где он находился, останется либо двойной пробел, либо пустая строка (абзац). Чтобы этого избежать, удаляется один дополнительный символ перед удаляемым блоком, это всегда будет либо пробел, либо "энтер".

Вставка картинок

Все картинки в документе Ворд делятся на два вида - вставленные как символ в текст и произвольно расположенные в графическом слое документа (текст их может либо обтекать, либо нет, зависит от параметров каждого объекта). Первый вид картинок представляется объектом InlineShape и доступен в коллекции InlineShapes.  Второй вид - объектом Shape и доступен в коллекции Shapes документа. Для деловых документов возиться с размещением картинок в графическом слое нет смысла, достаточно объектов InlineShape.

Принцип прост, находим соответствующий якорь и заменяем его картинкой:

Range = Документ.Range();
Range.Find.Execute("$[ЯкорьКартинки]$");
Range.Text = "";
Документ.InlineShapes.AddPicture (ИмяФайлаКартинки,,, Range);

Работа с таблицами

В основе всех подходов к заполнению таблиц принцип - программа знает о шаблоне документа и структуре таблицы достаточно информации, чтобы её заполнить. Заполнение деловых документов сводится к тому, что многократно копируется (вставляется) какая-то строка таблицы шаблона, затем ячейки вставленной строки заполняются текстом. Т.е. нам заранее, при создании шаблона, не известно, сколько строк будет в таблице. Если бы было известно, то задача свелась бы к обычному поиску и замене с помощью якорей или закладок. Но шапка, подвал таблицы заранее известны. В шаблоне размещается заготовка таблицы с шапкой, подвалом и единственной строкой-образцом для заполнения данными.

В статье http://blagin.ru/1s-word-shablon-zapolnenie-tablic-v-shablone-word/ описан один из подходов к заполнению таблиц. Вы можете увидеть следующие этапы:

- поиск таблицы в документе с помощью размещенной внутри таблицы закладки.

- знание о структуре таблицы видно в применении команды MoveDown(), для таблиц с другой структурой шапки или другом расположении закладки, набор команд для перемещения выделения может быть другим. Также знание о структуре таблицы проявляется в знании о том, какие якоря нужно заменять, т.е. программа знает, что после вставки строки в документе появятся три якоря и какие именно тоже знает.

- копирование строки и вставка через буфер обмена

- заполнение вставленной строки через поиск и замену якорей

- удаление строки-образца перед завершением алгоритма

Однако, метод, предложенный автором статьи, совсем плох с точки зрения интерактивного воздействия на окружение пользователя. Если у пользователя что-то было помещено в буфер обмена до начала формирования файла, то это что-то будет тут же утеряно, вместо этого в буфере пользователь найдет строку таблицы с якорями. Во время работы приложения нельзя работать не только с Вордом, нельзя вообще ничего делать. Ведь при работе пользователю буфер обмена нужен очень часто. Даже при копировании файлов, многие им пользуются. Как только пользователь поместит что-то в буфер обмена, строка-образец из буфера исчезнет и программа не сможет закончить работу.

Заполнение таблицы по фен-шую:

- размещаем якорь в любой ячейке строки-образца

- средствами поиска устанавливаем объект Range на якорь. Теперь через свойства этого объекта Range у нас есть ссылки на таблицу, строку, столбец и ячейку.

- вставляем строку над строкой-образцом. Вставленная строка копирует все параметры (шрифт, цвет, заливка) строки-образца.

- заполняем каждую вставленную строку требуемым текстом, затем вставляем следующую строку и т.д. Каждая новая вставленная строка вставляется между уже заполненными строками и строкой-образцом

- удаляем строку-образец (как раз вместе с якорем, так что отдельно якорь удалять не нужно)

Range = Документ.Range();
Range.Find.Execute("#[ЯкорьТаблицы]#");
Table = Range.Tables(1);
СтрокаОбразец = Range.Rows(1);
Для Счетчик = 1 По 10 Цикл
    НоваяСтрока = Table.Rows.Add(СтрокаОбразец);
    НоваяСтрока.Cells(1).Range.Text = Счетчик;
    НоваяСтрока.Cells(2).Range.Text = Счетчик + 10;
    НоваяСтрока.Cells(3).Range.Text = Счетчик + 100;
КонецЦикла;
СтрокаОбразец.Delete();

 

 

Word Ворд COM

См. также

Перенос данных из Парус 8 в ЗГУ 3

Зарплата Внешние источники данных Бюджетный учет Платформа 1С v8.3 Сложные периодические расчеты 1С:Зарплата и кадры государственного учреждения 3 Государственные, бюджетные структуры Россия Бухгалтерский учет Бюджетный учет Платные (руб)

Обработка позволяет перенести кадровую информацию и данные по заработной плате, фактических удержаниях, НДФЛ, вычетах, страховых взносах из базы Парус 8 учреждений в конфигурацию 1С:Зарплата и кадры государственного учреждения ред. 3 (ЗГУ) и начать с ней работать с любого месяца года.

84000 руб.

19.08.2020    22612    19    1    

22

Перенос данных из Парус 10 в ЗГУ ред.3

Внешние источники данных Кадровый учет Файловый обмен (TXT, XML, DBF), FTP Обмен между базами 1C Платформа 1С v8.3 Сложные периодические расчеты 1С:Зарплата и кадры государственного учреждения 3 Государственные, бюджетные структуры Россия Бухгалтерский учет Бюджетный учет Платные (руб)

Обработка позволяет перенести кадровую информацию и данные по заработной плате, фактических удержаниях, НДФЛ, вычетах, страховых взносах из базы Парус 10 учреждений в конфигурацию 1С:Зарплата и кадры государственного учреждения ред. 3 (ЗГУ) и начать с ней работать с любого месяца года.

60000 руб.

05.10.2022    9303    9    8    

11

Экстрактор данных 1С в BI - выгрузка данных из 1С в BI-аналитику

Внешние источники данных Платформа 1С v8.3 Управляемые формы Анализ и прогнозирование Конфигурации 1cv8 Узбекистан Беларусь Кыргызстан Молдова Россия Казахстан Платные (руб)

Готовое решение для автоматической выгрузки данных из 1С 8.3 в базу данных ClickHouse, PostgreSQL или Microsoft SQL для работы с данными 1С в BI-системах. «Экстрактор данных 1С в BI» работает со всеми типовыми и нестандартными конфигурациями 1С 8.3 и упрощает работу бизнес-аналитиков. Благодаря этому решению, специалистам не требуется быть программистами, чтобы легко получать данные из 1С в вашей BI-системе.

15.11.2022    13628    12    SQV0    47    

28

Перенос данных из Парус 7.хх в ЗГУ ред.3

Внешние источники данных Зарплата Бюджетный учет Платформа 1С v8.3 Сложные периодические расчеты 1С:Зарплата и кадры государственного учреждения 3 Государственные, бюджетные структуры Россия Бухгалтерский учет Бюджетный учет Платные (руб)

Обработка позволяет перенести кадровую информацию и данные по заработной плате, фактических удержаниях, НДФЛ, вычетах, страховых взносах из базы Парус 7.хх учреждений в конфигурацию 1С:Зарплата и кадры государственного учреждения ред. 3 (ЗГУ) и начать с ней работать с любого месяца года.

24000 руб.

24.04.2017    48825    97    163    

86

Перенос данных из Парус 10 (Торнадо) в ЗГУ ред.3 через Excel

Внешние источники данных Загрузка и выгрузка в Excel Зарплата Бюджетный учет Платформа 1С v8.3 Сложные периодические расчеты 1С:Зарплата и кадры государственного учреждения 3 Государственные, бюджетные структуры Россия Бухгалтерский учет Бюджетный учет Платные (руб)

Обработка позволяет перенести кадровую информацию и данные по заработной плате из Парус 10(Торнадо) учреждений через файлы Excel в конфигурацию 1С:Зарплата и кадры государственного учреждения ред. 3 (ЗГУ). В принципе, обработка может быть использована для загрузки из файлов Excel, полученных из любых информационных систем.

24000 руб.

16.11.2018    30078    20    31    

21

Загрузка в БГУ из УРМ "Криста"

Внешние источники данных Банковские операции Платформа 1С v8.3 Бухгалтерский учет 1С:Бухгалтерия государственного учреждения Россия Бухгалтерский учет Платные (руб)

Обработки для загрузки данных из УРМ "Криста" в бухгалтерию государственного учреждения редакция 2.0. Есть Демо доступ на вкладке Бесплатные файлы на 1 месяц со дня получения демонстрационного ключа регистрации. Поддерживает ПО "Web-исполнение" от НПО "Криста".

4800 руб.

19.06.2013    38481    137    90    

30
Вознаграждение за ответ
Показать полностью
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. gubanoff 63 15.02.21 12:25 Сейчас в теме
(0) С СОМ объектом все хорошо. Только работает нестабильно, тормозит, зависит от версии Word, требует наличия Word на машине, не работает на Linux.
Dethmontt; Лис Р; +2
2. Spektr 999 15.02.21 20:09 Сейчас в теме
Недавно видел еще один способ - Variables https://infostart.ru/public/669785/
+
3. biimmap 1860 16.02.21 17:18 Сейчас в теме
Хорошая статья. Недавно долбался с опен офисом... Который не требует наличия офиса))) Не хватало подобного материала. Пришлось самому писать). В профиле можно увидеть если интересно кому.
+
4. moff 15 16.02.21 20:58 Сейчас в теме
А популярный кроссплатформенный способ формирования .docx с помощью БСП почему проигнорирован?
+
5. Yashazz 4722 17.02.21 09:05 Сейчас в теме
(4) Потому что не надо связываться с БСП, граблей не оберёшься.
+
10. moff 15 17.02.21 12:40 Сейчас в теме
(5) о каких именно граблях идет речь? зачем строить свои грабли, когда 95% потребностей по формированию печатных форм по шаблонам .docx легко закрываются функционалом БСП?
+
19. Yashazz 4722 17.02.21 18:03 Сейчас в теме
(10) Затем, что свои грабли с очередным релизом, который главбух вечерком грузанул через сервис 1С, внезапно не сломаются. А в БСП любят менять смысл, состав и порядок параметров, переименовывать и переносить функции и модули) Словом, встанете пару раз на оные грабли - познаете дзен))
+
11. Sindbad_M 90 17.02.21 15:19 Сейчас в теме
(4)
Потому, что БСП работает не через COM, а непосредственно модифицирует .docx. А это совсем за рамками заявленной в статье темы.

БСП - пример готового решения, а не инструмента. Об этом в статье упоминание есть. По сути, БСП предоставляет широкий, но ограниченный функционал по заполнению шаблона с помощью подстановочных полей-параметров. Если потребуется выйти за рамки возможностей этого функционала -- тупик. Можно начинать разработку собственного инструмента с нуля. И на данный момент широта функционала лишь повторяет функционал встроенных в конфигурацию макетов печатных форм. Из мануала даже не понять, можно ли использовать предопределенное оформление разных столбцов при заполнении таблицы: https://its.1c.ru/db/bsp314doc#content:4:1:issogl3_%D1%80%D0%B0%D0%B7%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%­BA%D0%B0_%D0%BF%D0%B5%D1%87%D0%B0%D1%82%D0%BD%D1%8B%D1%85_%D­1%84%D0%BE%D1%80%D0%BC_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB­%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B­C%D0%B0%D0%BA%D0%B5%D1%82%D0%BE%D0%B2_%D0%B2_%D1%84%D0%BE%D1­%80%D0%BC%D0%B0%D1%82%D0%B5_%D0%BE%D1%84%D0%B8%D1%81%D0%BD%D­1%8B%D1%85_%D0%B4%D0%BE%D0%BA%D1%83%D0%BC%D0%B5%D0%BD%D1%82%­D0%BE%D0%B2_office_open_xml
+
18. Sindbad_M 90 17.02.21 16:47 Сейчас в теме
(4)
Потому, что БСП работает не через COM, а непосредственно модифицирует .docx. А это совсем за рамками заявленной в статье темы.

БСП - пример готового решения, а не инструмента. Об этом в статье упоминание есть. По сути, БСП предоставляет широкий, но ограниченный функционал по заполнению шаблона с помощью подстановочных полей-параметров. Если потребуется выйти за рамки возможностей этого функционала -- тупик. Можно начинать разработку собственного инструмента с нуля. И на данный момент широта функционала лишь повторяет функционал встроенных в конфигурацию макетов печатных форм. Из мануала даже не понять, можно ли использовать предопределенное оформление разных столбцов при заполнении таблицы: https://its.1c.ru/db/bsp314doc#content:4:1:issogl3_%D1%80%D0%B0%D0%B7%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%­BA%D0%B0_%D0%BF%D0%B5%D1%87%D0%B0%D1%82%D0%BD%D1%8B%D1%85_%D­1%84%D0%BE%D1%80%D0%BC_%D1%81_%D0%B8%D1%81%D0%BF%D0%BE%D0%BB­%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D0%BC_%D0%B­C%D0%B0%D0%BA%D0%B5%D1%82%D0%BE%D0%B2_%D0%B2_%D1%84%D0%BE%D1­%80%D0%BC%D0%B0%D1%82%D0%B5_%D0%BE%D1%84%D0%B8%D1%81%D0%BD%D­1%8B%D1%85_%D0%B4%D0%BE%D0%BA%D1%83%D0%BC%D0%B5%D0%BD%D1%82%­D0%BE%D0%B2_office_open_xml
+
6. Yashazz 4722 17.02.21 09:07 Сейчас в теме
Предпочитаю обходиться без шаблонов, так оно более предсказуемо. https://infostart.ru/1c/articles/391493/
+
7. Yashazz 4722 17.02.21 09:08 Сейчас в теме
А вообще, неплохой обзор, и вовсе даже не велосипед. Некоторые неочевидные моменты хорошо разъяснены.
POLGA; +1
8. DmitryKSL 155 17.02.21 09:25 Сейчас в теме
Только вместо

ШаблонВорд.Bookmarks("Закладка1").Select();
ШаблонВорд.Application.Selection.TypeText("Текст для закладки №1.");
Нужно использовать

Range = ШаблонВорд.Bookmarks("Закладка1").Range;
Range.Text = "Текст для закладки №1.";
Теперь вы знаете почему и, вообще, готовы к статьям из Интернета :-)

Так и не понял почему?
Замечу, оба варианта удаляют закладку, на место которой вставляется текст

Не удаляют. Можно повторно вставить текст в закладку.
+
12. Sindbad_M 90 17.02.21 15:29 Сейчас в теме
(8)
Так и не понял почему?

Потому, что использование объектов Selection делает невозможной работу пользователя с Вордом во время заполнения шаблона. А объекты Range пользователю не мешают.
(8)
Не удаляют. Можно повторно вставить текст в закладку.

Удаляют. НО можно повторно вставить закладку в текст. И в статье как раз это написано.
+
13. DmitryKSL 155 17.02.21 15:43 Сейчас в теме
(12) Возможно нюанс в чем-то другом. Т.к.
использование объектов Selection делает невозможной работу пользователя с Вордом во время заполнения
не проявляется.
Например могу формировать для документа с табличной частью десятки документов word (для каждой строки по одному), и это не мешает мне работать с открытым документом.
Удаляют

У меня это не так, все закладки остаются.
+
16. Sindbad_M 90 17.02.21 16:14 Сейчас в теме
(13)
Дальше надо смотреть код. Это касается и Selection и закладок.
В статье описано, опасность работы с Selection проявляется тогда, когда пользовательский документ открыт в том же экземпляре Ворда, что и СОМ-Объект. Она выше, если обращаться к свойству ActiveSelection приложения и почти не заметна, если обращаться к свойству Selection окна документа.

Фразу про количество документов, связанных с количеством строк, честно говоря, не понял.
+
9. DmitryKSL 155 17.02.21 09:47 Сейчас в теме
Пользователь передает разработчику файл с образцом документа, который хочет получать из информационной системы в автоматизированном режиме. Точно, значит без явных указаний пользователя разработчик не меняет по своему усмотрению ни одного элемента форматирования, ни шрифт, ни размер, ни поля, ни колонтитулы. НИЧЕГО не меняет. Совершенно ничего. На выходе пользователь должен получить документ неотличимый от документа, созданного собственноручно.

Т.е. я просто должен вставить в нужные места текст и рассчитывать что ничего на странице не разъедется? Это так не работает, практически никогда.
+
14. Sindbad_M 90 17.02.21 15:44 Сейчас в теме
(9)
вставить в нужные места текст и рассчитывать что ничего на странице не разъедется? Это так не работает, практически никогда.

Это так работает практически всегда. Просто потому, что повторяет действия пользователя при ручном заполнении шаблонов -- пользователь стирает неактуальный текст, заменяя его новым, актуальным. Зависит, конечно, от качества шаблона. Если пользователь зашлифовал шаблон для себя любимого, то и автоматическое заполнение пройдет как по маслу.
Да и что может "разъехаться"? Это же Ворд, текст перенесется на новую строку и/или страницу с учетом заданного форматирования абзацев.
+
15. DmitryKSL 155 17.02.21 16:09 Сейчас в теме
(14)
Да и что может "разъехаться"? Это же Ворд, текст перенесется на новую строку и/или страницу с учетом заданного форматирования абзацев

Вот простейший пример. Если заполнять город автоматически, то например, Санкт-Петербург сильно длинее чем Москва и блок под дату сдвинется на строку ниже, что по мнению пользователя означает разъедется. Я то знаю что надо вставить таблицу, настроить выравнивание в ячейках, убрать видимость границ и тогда это будет заполняться как надо. Но ни один пользователь еще ни разу не дал такой шаблон, чтобы ничего не переделывать самому.
Прикрепленные файлы:
+
17. Sindbad_M 90 17.02.21 16:41 Сейчас в теме
(15)
Но ни один пользователь еще ни разу не дал такой шаблон, чтобы ничего не переделывать самому.

А зачем это пользователю? Ведь вы же за них это делаете.
Впрочем, я не имел в виду такую степень занудства.
Имелось в виду, что разработчик, вместо того чтобы взять и сделать, начинает диктовать пользователю что и как теперь будет в шаблоне. Вроде такого:
- ну и что, что в образце написано "А.С. Пушкин", теперь будет "Пушкин А.С."
- ну и что, что средний столбец был выделен курсивом, теперь вся таблица будет одним шрифтом
- ну и что, что в образце "уважаемый/ая" с привязкой к полу. Переделайте образец на унисекс.
+
20. DmitryKSL 155 17.02.21 18:38 Сейчас в теме
(17)Т.е по существу сказать нечего?
+
21. Sindbad_M 90 17.02.21 20:21 Сейчас в теме
(20) По существу я раскрыл мысль с "ничего не менять". В конечном итоге, самый лучший вариант - запросить у пользователя, действительно ли он хочет, чтобы при подстановке "Санкт-Петербург" съезжала дата на новую строчку? Если съезжающая дата - не лучший для пользователя вариант, подсказать способы улучшения шаблона (таблица, символ табуляции), но за пользователя правки не вносить.
Batman; +1
22. user747134 19.02.21 20:05 Сейчас в теме
У объекта Fields есть метод Unlink(), который заменяет каждое поле на его последний результат, https://docs.microsoft.com/ru-ru/office/vba/api/word.fields.unlink.
Можно делать так:
Документ.Fields.Update();
Документ.Fields.Unlink(); // для каждого из полей документа заменить вхождение поля на значение этого поля
Sindbad_M; +1
23. G_100268594770204174954 26.04.21 11:55 Сейчас в теме
Анатолий, как с Вами связаться?
+
24. Sindbad_M 90 26.04.21 12:54 Сейчас в теме
(23) Например, через личные сообщения. Кликнуть по нику, затем по кнопке "написать".
+
25. G_100268594770204174954 26.04.21 14:55 Сейчас в теме
На пропускает сообщения в ЛС
+
26. пользователь 26.04.21 15:55
Сообщение было скрыто модератором.
...
27. Sindbad_M 90 27.04.21 09:39 Сейчас в теме
(25) Действительно. Оказывается ЛС доступно только для пользователей имеющих более двадцати комментариев на форуме. Напишите A-точка-G-точка-VASILIEV-собачка-мэил-точка-ру
+
28. MikhailDr 25.08.21 15:50 Сейчас в теме
Великолепный материал. Редко встретишь такую качественную статью. Все по делу, с конкретными, понятными примерами и объяснением почему так, а не иначе. Я в восторге. БОЛЬШОЕ спасибо автору. Мне очень помогло.
user591389_aska_rabota; +1
29. MikhailDr 26.08.21 09:37 Сейчас в теме
Может автор поможет немного. Столкнулся с одной проблемой. Мне нужна таблица как на скриншоте 1, а получается как на скриншоте 2, можно ли как-то это обойти?
Прикрепленные файлы:
+
30. Sindbad_M 90 26.08.21 11:41 Сейчас в теме
(29) Сейчас на ум приходит только вариант с форматированием таблицы из кода:
- В шаблоне создаем строку-образец из двух ячеек, которая будет заготовкой и для промежуточных заголовков и для параметров, размещаем в ней якорь
- Если нужно вставить строку-заголовок, то вставляем копию образца, объединяем ячейки, устанавливаем заливку ячейки, параметры шрифта, выравнивание по центру. И вставляем в ячейку нужный текст.
- Если нужно вставить строку-параметр, то вставляем копию образца, при необходимости устанавливаем параметры шрифта (хотя в качестве образца лучше сразу записать правильно отформатированную строку-параметр) и записываем текст в каждую из ячеек
- В финале удаляем строку-образец из таблицы.
+
31. MikhailDr 27.08.21 07:18 Сейчас в теме
(30) Спасибо за ответ. Я плохо знаю VBA и честно сказать не знаю как объединять ячейки и делать заливку цветом, но проблему я решил, может и не лучшим способом, но это работает. На скриншоте показал как. Т.е. я создаю 10 шаблонов, которые идут друг за другом. Если количество промежуточных заголовков меньше, то лишнее просто удаляю как в вашем примере из статьи. Итог на втором скриншоте.

Ограничение здесь в том, что количество промежуточных должно быть не более 10, но это можно заранее согласовать, в принципе можно и 100 сделать для запаса.
Прикрепленные файлы:
+
33. Sindbad_M 90 30.08.21 15:49 Сейчас в теме
(31)
Я плохо знаю VBA и честно сказать не знаю как объединять ячейки и делать заливку цветом,

Это же не VBA. Объектная модель Ворд, скорее, библиотека доступная из любого языка.
Объединение ячеек:
https://docs.microsoft.com/ru-ru/office/vba/api/word.cell.merge
Заливка ячейки цветом:
https://docs.microsoft.com/ru-ru/office/vba/api/word.cell.shading
https://docs.microsoft.com/ru-ru/office/vba/api/word.shading

Сейчас я нахожу свое первое решение, с форматированием ячеек из кода, ужасным. Нарушается принцип "программа знает то, что нужно, о структуре документа и ничего не знает о форматировании документа". Если, например, потом потребуется заменить оранжевую заливку на голубую, то придется изменения вносить не в шаблон, а в программу. А если часть файлов будет с оранжевой заливкой, часть с желтой, а часть с голубой? Это значит, что общий код один раз для любых шаблонов написать не получится, каждый шаблон должен обрабатываться своей программой, сопровождать этот зоопарк через некоторое время станет проблематично.

Ваше решение в этом плане, несомненно, лучше. Да, строки нужно создать с запасом. Ведь удалить лишние можно в цикле, т.е. количество строк программы не увеличивается при увеличении строк в таблице.

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

Решение, лишенное указанных недостатков, видится таким:
- в шаблоне оставляем три строки: "строка, которую будем копировать", "строка-образец № 1" и "строка-образец № 2"
- каждый раз после копирования строки, читаем требуемые свойства соответствующий строки-образца и устанавливаем их для новой строки.
- в финале удаляем все три первоначальные строки, остается чистая сформированная таблица.

В частном случае решение можно упростить до двух строк в шаблоне, т.к. одна из строк-образцом может служить одновременно строкой, которую будем копировать.
+
32. MikhailDr 27.08.21 16:00 Сейчас в теме
(30) Может вы подскажете, есть ли возможность программно создавать в word диаграммы?
+
34. Sindbad_M 90 30.08.21 16:10 Сейчас в теме
(32) В Ворде можно программно создавать все, что может делать пользователь с помощью мыши и клавиатуры.
Один из классических способов познания объектной модели Ворд (Эксель и т.д.) заключается в следующем:
- включаем запись макроса
- выполняем действия, аналогичные тем, которые хотим автоматизировать
- останавливаем запись макроса
- просматриваем код получившегося макроса и на его основе пишем свой собственный. Если подойти к делу ответственно, то код требуется переработать, заменив обращения через объекты Selection на объекты Range.

Если воспользоваться данным способом, запустить макрорекордер, нажать вставка/диаграмма и остановить макрорекордер, то макрос будет содержать единственную команду:
Selection.InlineShapes.AddOLEObject ClassType:="MSGraph.Chart.8", LinkToFile:=False, DisplayAsIcon:=False
Сразу становится понятно, что:
1. Диаграмма это что-то вроде картинки, разновидность объектов Shape или InlineShape
2. Диаграмма есть суть внешнее для Ворда приложение (MSGraph.Chart.8)
Далее направление экспериментов в том, чтобы записывать более полные макросы (с выбором данных перед созданием диаграммы) и анализировать их. Параллельно читая документацию по встречающимся в маркосе командам:
https://docs.microsoft.com/ru-ru/office/vba/api/word.inlineshapes.addoleobject
и т.д.
Если возможностей стандартного "MSGraph" окажется недостаточно, имеет смысл поэкспериментировать с диаграммами Эксель. Благо, объектная модель Эксель такая же серьезная, как и у Ворда.
+
35. psyhobear 01.09.21 14:21 Сейчас в теме
Что меня смущает, Вы пишите, что некорректно использовать:
Word.Documents.Open ("C:\готовые документы\файл.docx");
Документ = Word.ActiveDocument;

Но при этом, при заполнении шаблона Word с помощью закладок, ссылаетесь как раз на такой вариант работы
+
36. Sindbad_M 90 01.09.21 15:34 Сейчас в теме
(35) В блоке про заполнение с помощью закладок нет кода открывающего файлы.

В статьях, на которые даю ссылки, полно плохого кода, но это не мои статьи. О том, что в интернете на тему работы с Вордом через СОМ очень много вредных советов, я предупреждаю, эта мысль проходит красной нитью через всю статью. Предполагается, что из этих статей можно понять идею, и это не в коем случае не образец для копирования в свое приложение.
+
37. psyhobear 01.09.21 16:56 Сейчас в теме
(36) Спасибо за разъяснение
+
38. пользователь 18.06.23 21:47
Сообщение было скрыто модератором.
...
39. Дмитрий74Чел 234 25.01.24 11:15 Сейчас в теме
Добрый день.
В статье есть использование числовых констант вида wdName, когда передаем число.
В документации MS вижу что есть "перечисления", когда передается значение вида wdConstantsX.ConstantY, например WdCollapseDirection.wdCollapseEnd, и как я понял, обращение к ним идет через word.WdCollapseDirection.wdCollapseEnd. В 1С у ComОбъекта Word я не могу найти таких свойств. Такие "перечисления" недоступны?
+
40. Sindbad_M 90 25.01.24 14:07 Сейчас в теме
(39) Вы ссылаетесь на раздел справки, посвященный работе через .NET. Вообще не уверен, что описанное в этом разделе применимо для работы с COM. Скорее наоборот, уверен, что не применимо.

Если перейдете по ссылке к описанию перечисления: https://learn.microsoft.com/ru-ru/dotnet/api/microsoft.office.interop.word.wdcollapsedirection?view=word-pia#microsoft-office-interop-word-wdcollapsedirection-wdcollapsestart , то увидите, что за перечислением скрываются просто две константы 0 и 1. Однако, использовать их вместо перечисления скорее всего не получится.
+
Оставьте свое сообщение