Добавление водяного знака и колонтитула в документ. Вариант решения задачи

15 6

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

Полезные материалы по теме

Вставка изображения в документ Word - как добавлять водяной знак в документ Word

Вставка изображения в документ Excel - в документ Excel 

Два способа вставить изображение (штрихкод) в документ Word.Application - как добавлять изображение в колонтитул

Несколько способов форматирования и обработки данных в Word документах с помощью IS-Builder - полезности при программной работе с документами в Word

Описание процесса

В компании N есть определенного вида документы, которые могут находиться в 3 условно названных стадиях: «Разработка», «Действующий», «Устарел» (эти 3 условно названные стадии ни коим образом могут не зависеть и не отражать стадии ЖЦ документа и/или состояние версий документа в системе).

Каждая стадия характеризуется следующими параметрами:

1. Если документ в стадии «Разработка», то у документа должна быть в теле подложка (водяной знак) с определенным текстом, например, «Разработка».

Дополнительно в нижнем колонтитуле документа должна была появиться строка: «СПРАВОЧНО. ВЫГРУЖЕНО ИЗ DIRECTUM <дата и время открытия документа>». Здесь нужно добавить, что при экспорте документа на диск функциональность по показу текущих даты и времени должна сохраниться. Т.е. экспортировала я документ вчера, открыла сегодня в 17:10, то и в документе должно отразиться 17:10.

Документы могут быть в формате .doc, .docx, .rtf.

Ну и конечно не важно, открыла я документ на чтение или редактирование, дата и время должны обновляться.

Выглядеть должно это примерно так:

2. Если документ в стадии «Действующий», то подложка из документа должна удалиться, колонтитул должен остаться на месте.

3. Если документ в стадии «Устарел», то в документ снова должен добавиться водяной знак, но теперь с текстом «Устарел».

Стадии документа могут изменяться только в порядке: Разработка → Действующий → Устарел. Смена стадии на значение «Действующий» происходит при регистрации приказа по вводу в действие такого документа, а в стадию «Устарел» документ переходит при актуализации/переработке документа (при актуализации/переработке создается новый документ, а не версия этого же документа), и опять же, только при регистрации приказа.

Реализация

Для начала разделим задачу на части:

1. Во-первых. Как добавить водяной знак в документ?

Есть сходу 2 способа:

  • Нагуглить.
  • Записать макрос при вставке водяного знака и переписать полученный код на ISBL.

Следуя любому из способов, в итоге получим примерно такой код:


…
  PRESENT_TEXT_EFFECT = 0 // Текстовый эффект, значения совпадают формам, перечисленным в диалоговом окне WordArt Gallery 
  FONT_NAME = "Times New Roman" // Наименование шрифта
  FONT_SIZE = 122 // Размер шрифта
  FONT_BOLD = FALSE // Использовать жирный шрифт
  FONT_ITALIC = FALSE // Использовать курсив
  LEFT_POINTS = 1 // Положение надписи от левого края
  TOP_POINTS = 1 // Положение надписи от верхнего края
  Text = ‘Текст водяного знака’
…
  WordArt = Selection.HeaderFooter.Shapes.AddTextEffect(PRESENT_TEXT_EFFECT; 
          Text; FONT_NAME; FONT_SIZE; FONT_BOLD; FONT_ITALIC; LEFT_POINTS; TOP_POINTS)
  WordArt.Select
    
  WordArt.TextEffect.NormalizedHeight = FALSE
  WordArt.Fill.Visible = TRUE
  WordArt.Fill.Solid
  
  WordArt.Fill.ForeColor.RGB = 255  // цвет - красный
  WordArt.Fill.Transparency = 0.5  // полупрозрачность
  WordArt.Rotation = 315  // поворот в градусах
        
  WordArt.Line.Visible = FALSE
  WordArt.LockAspectRatio = FALSE
        
  WordArt.ZOrder(5)  // За текстом
… 

2. Во-вторых. В какой момент добавлять строку в нижний колонтитул таким образом, чтобы в документе отражались текущие дата и время?

Исходя из требований, самый доступный и простой способ, это добавлять элемент управления «Поле» с заданными свойствами:


…
  FONT_SIZE = 5
  wdLine = 5
  TEXT = "СПРАВОЧНО. ВЫГРУЖЕНО ИЗ DIRECTUM: "
  wdColor = -671055617 // BLUE https://msdn.microsoft.com/en-us/library/bb237561(v=office.12).aspx -671055617  Font.Color = -671055617
…
  Field = Footer.Range.Fields.Add(FooterRange; -1; 'DATE  \@ "dd.MM.yyyy H:mm:ss" '; True)
  FooterRange.SetRange(0; 69)
  FooterRange.Font.Color = wdColor // Blue  
  FooterRange.Font.Size = FONT_SIZE
  FooterRange.InsertBefore(TEXT)
  Selection.EndKey(wdLine)
…

И так, небольшие заготовки кода есть, идем дальше.

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

Как только такой документ появляется в системе, то у него в теле должен быть водяной знак, соответствующий стадии «Разработка».

Если с шаблоном все понятно – просто настраиваем в нужном формате шаблон для определенного вида документа, то с созданием из файла придется помучиться.

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

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

Итого: в событии данного типа карточки Сохранение После для документов с расширением (.doc, .docx, .rtf), созданным из файла, стартуем задачу, в рамках которой будет происходить добавление подложки в документ.

Очевидный минус такого решения – необходимые водяной знак и колонтитул может появиться гораздо позже создания документа при наличии слабого сервера со службой WF.

4. Т.к. стадии документа могут проходить строго последовательно, а в рамках бизнес-процесса стадия документа изменяется только в типовом маршруте при определенных условиях (при регистрации приказа), то с удалением водяного знака при смене стадии документа на «Действует» и добавлением водяного знака в случае смены стадии документа на «Устарел» проблем быть не должно – выполняем данные действия на сервере, где установлена служба WF, в рамках все того же типового маршрута по регистрации приказа.

Трудности, возникшие при реализации, и варианты их решения.

1. Первая проблема, которая возникла, это собственно вставка водяного знака в документе посередине.

Казалось бы, вариантов сходу есть два:

  • Использовать константы MS Word для расположения текста посередине
  • Или использовать незатейливые формулы для вычисления середины листа

Первый способ требует усилий, требуется «нагуглить» нужные переменные, а математику вроде и так знаем. Поэтому, пишем код!

Здесь возникла проблема следующего характера.

При вставке текста методом .AddTextEffect() требуется задать координаты будущей фигуры. Затем, если местоположение фигуры на странице листа не нравится, фигуру можно расположить в другом месте методами .Left и .Top. Но на некоторых документах результат мог быть непредсказуем: текст мог располагаться как где-то совсем слева, что едва появляется текст «…водяного знака», так и где-то далеко справа, и результатом была подложка «Текст во…».

Такая картина совсем не радовала. Поэтому необходимо было определить, почему так происходит.

Для этого потребовалось:

    1) Записать макрос при вставке водяного знака в документ, где текст знака располагался не посередине. Результат был такой же, как и при добавлении водяного знака в документ, в котором водяной знак располагался как нужно:


…
    Selection.ShapeRange.RelativeHorizontalPosition = _
        wdRelativeVerticalPositionMargin
    Selection.ShapeRange.RelativeVerticalPosition = _
        wdRelativeVerticalPositionMargin
    Selection.ShapeRange.Left = wdShapeCenter
    Selection.ShapeRange.Top = wdShapeCenter
…

    2) Ну что ж, надо узнать значения констант (оставлю полезную ссылку на значения констант здесь https://msdn.microsoft.com/en-us/library/office/aa211923(v=office.11).aspx

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

2.Здесь возникла другая проблема, о которой вспоминается только тогда, когда она встречается: ограниченная возможность передачи данных определенного типа (например, «single») в ISBL.

Решилось все достаточно просто – кусок функции переписался на VBS:


…
  PRESENT_TEXT_EFFECT = 0 // Текстовый эффект, значения совпадают формам, перечисленным в диалоговом окне WordArt Gallery 
  FONT_NAME = "Times New Roman" // Наименование шрифта
  FONT_SIZE = 122 // Размер шрифта
  FONT_BOLD = FALSE // Использовать жирный шрифт
  FONT_ITALIC = FALSE // Использовать курсив
  LEFT_POINTS = 1 // Положение надписи от левого края
  TOP_POINTS = 1 // Положение надписи от верхнего края
  Text = ‘Текст водяного знака’
  wdRelativeHorizontalPositionPage = 1 // значение одноименной константы в MS Word
  wdRelativeVerticalPositionPage = 1 // значение одноименной константы в MS Word
  wdRelativeHorizontalSizePage = 1 // значение одноименной константы в MS Word
  wdRelativeVerticalSizePage = 1 // значение одноименной константы в MS Word
  wdShapeCenter = -999995 // значение одноименной константы в MS Word
  wdShapePositionRelativeNone = -999999 // значение одноименной константы в MS Word
  wdShapeSizeRelativeNone = -999999 // значение одноименной константы в MS Word  
…
  ScriptControl = CreateObject("MSScriptControl.ScriptControl")
  ScriptControl.Language = "VBScript"
  ScriptControl.Reset
  ScriptControl.AddObject("DocApplication"; DocApplication)

  ScriptText = Format('
            Function Insert()       
              Set Selection = DocApplication.Selection

              Set WordArt = Selection.HeaderFooter.Shapes.AddTextEffect(%0:s, "%1:s", "%2:s", %3:s, %4:s, %5:s, 0, 0)
              WordArt.Select        
             
              WordArt.TextEffect.NormalizedHeight = FALSE
              WordArt.Fill.Visible = TRUE
              WordArt.Fill.Solid
              WordArt.Fill.ForeColor.RGB = RGB(255, 0, 0)
              WordArt.Fill.Transparency = 0.5  
              WordArt.Rotation = 315
              WordArt.Line.Visible = FALSE
              WordArt.LockAspectRatio = FALSE 
              WordArt.ZOrder(5) 
      
              WordArt.RelativeHorizontalPosition = %6:s
              WordArt.RelativeVerticalPosition = %7:s
              WordArt.RelativeHorizontalSize = %8:s
              WordArt.RelativeVerticalSize = %9:s
              WordArt.Left = %10:s
              WordArt.LeftRelative = %11:s
              WordArt.Top = %10:s
              WordArt.TopRelative = %11:s
              WordArt.WidthRelative = %12:s
              WordArt.HeightRelative = %12:s

            End Function
          '; ArrayOf(PRESENT_TEXT_EFFECT; Text; FONT_NAME; FONT_SIZE; FONT_BOLD;
                     FONT_ITALIC; wdRelativeHorizontalPositionPage; wdRelativeVerticalPositionPage;
                     wdRelativeHorizontalSizePage; wdRelativeVerticalSizePage; wdShapeCenter;
                     wdShapePositionRelativeNone; wdShapeSizeRelativeNone 
              )
            )
              
          ScriptControl.AddCode(ScriptText)
          ScriptControl.Run("Insert")
…

Но, к сожалению, первоначальную проблему по расположению текста посередине полностью решить таким способом не удалось.

3. Методом тщательного сравнения документов было установлено следующее: что водяной знак добавляется не посередине документа в случае, если в верхнем колонтитуле документа есть таблица. И выше этой таблицы нет пустой строки.

В результате была добавлена и обработка таких ситуаций:


  wdSeekCurrentPageHeader = 9
  wdStory = 6
  wdWithInTable = 12
  wdCollapseStart = 1
  wdSeekMainDocument = 0
…
  ActiveWindow.ActivePane.View.SeekView = wdSeekCurrentPageHeader

  Selection.HomeKey(wdStory)
  if Selection.Information(wdWithInTable) = True
    Selection.SplitTable
  endif

  HeaderRange.Collapse(wdCollapseStart)
 
  ScriptText = Format('
            Function Insert()
… 
… <здесь vbs-скрипт добавления водяного знака>
…
  ScriptControl.Run("Insert")
  
  ActiveWindow.ActivePane.View.SeekView = wdSeekMainDocument
…

P.S.: и кстати, добавленный таким способом водяной знак удалить вручную (т.е. через стандартный пункт меню MS Word 2010 ПодложкаУдалить подложку) не получается.

4. Следующая проблема была связана с добавлением строки «СПРАВОЧНО. ВЫГРУЖЕНО ИЗ DIRECTUM <дата и время открытия документа>» в нижний колонтитул документа способом, представленным выше.

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

Как итог, код был переписан следующим образом (вместо .Range.Fields.Add() использовать метод .InsertDateTime()):


…
  wdColor = -671055617 // BLUE https://msdn.microsoft.com/en-us/library/bb237561(v=office.12).aspx
  wdStory = 6
  wdLine = 5
  wdCalendarWestern = 0
  TEXT = "СПРАВОЧНО. ВЫГРУЖЕНО ИЗ DIRECTUM: "
  wdExtend = 1
  FONT_SIZE = 5
…
  Selection.EndKey(wdStory)
          
  Selection.InsertDateTime('dd.MM.yyyy H:mm:ss'; TRUE; 0; wdCalendarWestern; FALSE)
  Selection.HomeKey(wdLine) 
  Selection.TypeText(TEXT)
          
  Selection.EndKey(wdLine)

  // Выделить цветом
  Selection.HomeKey(wdLine; wdExtend)        
  Selection.Font.Name = 'Arial'
  Selection.Font.Color = wdColor // Blue  
  Selection.Font.Size = FONT_SIZE
…

Результаты

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

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

Алексей Язынин

Как работа всех этих вычислений отражается на открытии документа из DIRECTUM? Не тормозит ли?

Наталия Соколова

Алексей, добрый день!

Т.к. добавление водяного знака и колонтитула в документ отрабатывает на WF, то к пользователю приходит по сути уже готовый документ - подложка и колонтитул хранятся в теле, при открытии документа подложка не перерисовывается.

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

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

Водяной знак "Разработка" (и любой другой водяной знак) не закроет процитированный кейс. Актуальность и законченность содержимого достоверно известны только автору документа. Любой другой пользователь, обратившийся к документу, не знает, внесены ли в него все планируемые правки, или документ только на полпути к своему финальному виду, отправляемому на согласование.

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

Как вы предполагаете обеспечивать смену водяного знака на документе, подписанном электронной подписью?

Алексей Семакин

Валентина, мне кажется, тут многое зависит от принятого в компании порядка работы с документами. Например, документ сразу при создании получает водяной знак "В разработке" и так и ходит с ним, пока не пройдет согласование. После согласования все водяные знаки удаляются, и ставится утверждающая подпись, если требуется. При отмене документа создается новая версия с водяным знаком "Отменён" ("Устаревший" и т.п.). Нужна ли подпись на этой версии — затрудняюсь, но ничто не мешает ее поставить. Тут особенность в том, что отменяющий документ создается как отдельный документ, а в отмененном появляется лишняя (увы!) версия, чтобы поставить водяной знак, не поломав подписи на когда-то действовавшей версии. Можно и без лишней версии, если утверждающую подпись на отмененном документе хранить не нужно. Или при утверждении отменяющего документа, утверждающая подпись одновременно ставится и на отмененный, на водяной знак "Отменён".

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

Валентина Писанова

Алексей, когда

документ сразу при создании получает водяной знак "В разработке" и так и ходит с ним, пока не пройдет согласование

этот водяной знак никоим образом не позволяет ответить на вопросы:

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

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

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

Наталия Соколова

Валентина, добрый день!

Видимо, до конца тему не получилось раскрыть)

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

При этом одним из требований было автоматическое управление водяными знаками в документе в зависимости от стадии "работы" над документом. Имелось ввиду, что если документ в стадии = Разработка, то он только разрабатывается, что он ещё не введен в действие. Здесь не имелся ввиду конкретный подэтап стадии Разработки (условно "готовность" документа к вводу в действие).

Авторизуйтесь, чтобы написать комментарий