Статьи из блога

Статьи из блога

Особые колонтитулы для нечетных страниц

Тема "раздельных" колонтитулов поднималась на этом сайте неоднократно. Но очередной вопрос читателя Владимира поставил меня в тупик. В частности, Владимир спрашивал:

По ГОСТ18675-79, если текст в разделе заканчивается на нечетной странице, то номер этой страницы должен иметь вид n/n+1, например 7/8, так как на следующей - четной странице текст отсутствует, а новый раздел начинается с нечетной страницы (9). Предыдущие страницы имеют нормальную нумерацию. Можно ли в Ворде реализовать такую нумерацию автоматически?

Я обратился в Клуб программистов и через день мой соавтор Александр Витер предложил практическое решение, основанное на применении макроса и использовании полей в колонтитулах. Ход размышлений Александра и обсуждение возможных решений можно почитать на здесь.

 

В этой же заметке с разрешения Александра я публикую только результат найденного решения со своими пояснениями.

 

Если вы хотите реализовать это решение, то необходимо:

- открыть шаблон документа (или сам документ) в режиме редактирования;

- перейти в нижний или верхний колонтитул;

- вставить следующее поле (Напоминаю, что поля вставляются через нажатие сочетания клавиш «CTRL+F9»):

{ IF {=(({ =MOD({ PAGE };2) }<>0)*({PAGE}={=INT({ DOCVARIABLE { QUOTE Sec{ SECTION }PageCount } }) })*({ PAGE }<{ NUMPAGES }) )} = 1 { QUOTE { PAGE }/{ =SUM({ PAGE };1) } } { PAGE } }

 

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

- открыть редактор VIsual Basic («ALT+F11») и вставить следующий код:

 

[code lang="vb"]Sub StupidNumbering()

'Такое название потому, что я считаю глупым следовать стандартам,

'разработанным бог знает когда и не несущим практической пользы

On Error Resume Next

Dim oSec As Section 'Переменная для перечисления разделов

Dim nPagesCountEndSec As Long 'Количество страниц от начала документа до конца раздела

Dim ovar As Variable 'Переменная для перечисления переменных документа

For Each oSec In ActiveDocument.Sections

'Количество страниц до конца раздела

nPagesCountEndSec = oSec.Range.Information(wdActiveEndPageNumber)

'Добавляем переменную в документ

ActiveDocument.Variables.Add "Sec" & oSec.Index & "PageCount", nPagesCountEndSec

If Err.Number = 5903 Then 'Если такая переменная в документе уже есть

'Обновляем значение переменной

ActiveDocument.Variables("Sec" & oSec.Index & "PageCount").Value = nPagesCountEndSec

'Очищаем ошибку

Err.Clear

End If

Next

'Обновляем поля в документе

ActiveDocument.Fields.Update

'Это для контроля имен переменных и их значений. На

'функциональность это не влияет.

For Each ovar In ActiveDocument.Variables

Debug.Print ovar.Name & vbTab & ovar.Value

Next

End Sub[/code]

 

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

 

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

twitter.com facebook.com vkontakte.ru odnoklassniki.ru mail.ru ya.ru rutvit.ru myspace.com technorati.com digg.com friendfeed.com pikabu.ru blogger.com liveinternet.ru livejournal.ru memori.ru google.com bobrdobr.ru mister-wong.ru yahoo.com yandex.ru del.icio.us

Еще записи по вопросам использования Microsoft Word:

Комментариев: 24

  1. Владимир Шнейдер
    19.10.2009 в 00:55 | #1

    Как преобразовать колонтитулы в текст (ввести их в текст) на каждой странице?

    Спасибо!

  2. 19.10.2009 в 16:34 | #2

    Владимир Шнейдер, объясните подробнее. Ведь если содержимое колонтитула внести на каждую страницу в текст, то само содержание документа изменится, страницы поплывут

  3. Владимир
    19.10.2009 в 22:06 | #3

    Здравствуйте, Антон! Спасибо за проведенный труд, однако у меня есть несколько замечаний. К сожалению, Александр не ознакомился с самим ГОСТом (ну, хотя бы чтобы знать предмет).

    Во-первых, вся техническая документация на авиационную технику во всем мире разрабатывается по принципам, заложенным в нем. Собственно, это адаптация международного стандарта АТА100. И самая последняя версия его сейчас АЕСМА S1000D. И там такие же требования. Это про StupidNumbering.

    Ну а теперь по сути.

    1. Макрос создает переменную "Число страниц в разделе", которая потом сравнивается с номером страницы. Это бы работало бы, если нумерация начиналась с первой страницы. Однако, по ГОСТ вся информация в разделе (не путать с вордовскими разделами) делится на тематические блоки, каждый из которых должен начинаться с определенной страницы (Описание и работа - со стр.1, Поиск неисправностей - со стр.101, Обслуживание - со стр.201 и т.д, независимо от фактического числа страниц в блоке). Так что здесь такой подход работать не будет.

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

    Но наверное, я поставил невыполнимую задачу (для нынешнего Ворда). Может быть в следующем Ворде появится "Различать колонтитул поледней страницы"?! :)

    Все равно спасибо всем за помощь.

    Владимир

  4. Владимир Шнейдер
    19.10.2009 в 23:47 | #4

    Мне необходимо получить текстовый файл, в котором бы содержанию страницы предшествовал ее номер в виде: {стр. №} контент страницы. Вот номер страницы мне бы и нужно перенести из колонтитула в текст.

  5. 20.10.2009 в 06:52 | #5

    Для Владимира по ГОСТу:

    Владимир, позволю себе дать небольшие пояснения. Мы все работаем по ГОСТам и понимаем, что они в большинстве своем устарели (по части оформления документации). Я сам с трудом нашел в сети указанный вами стандарт (с грифом ДСП).

    Александр выполнил задачу (блестяще, если учесть, что редактор вообще не позволяет стандартно делать разные колонтитулы в одном разделе) исходя из заданного вопроса.

  6. 20.10.2009 в 19:12 | #6

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

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

    Ну а над нумерацией я еще подумаю.

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

    Код поля выглядит так (измененная часть выделена курсивом):

    IF {=(({ =MOD({ PAGE };2) }<>0)*({PAGE}={=INT({ DOCVARIABLE { QUOTE Sec{ SECTION }PageCount } }) })*({ SECTION }<>{ DOCVARIABLE "SectionsCount"}) )} = 1 { QUOTE { PAGE }/{ =SUM({ PAGE };1) } } { PAGE } }

  7. Владимир
    21.10.2009 в 00:33 | #7

    Да, теперь нумерация идет правильно. Если бы еще действительно сделать так, чтобы макрос запускался автоматически перед печатью (а еще лучше - и перед печатью и при предварительном просмотре) это было бы просто здорово!

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

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

    Спасибо!

    Владимир

  8. 21.10.2009 в 08:49 | #8

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

    { IF {PAGE}={=INT({ DOCVARIABLE { QUOTE Sec{ SECTION }PageCount } }) } { QUOTE { PAGE }/{ =SUM({ PAGE };1) } } { PAGE } }

    А чтобы автоматически обновлять колонтитулы, нужно дописать свои обработчики для стандартных команд, где вызывать нашу процедуру:

    Sub GOSTNumbering()
      On Error Resume Next
      Dim oSec As Section 'Переменная для перечисления разделов
      Dim nPagesCountEndSec As Long 'Количество страниц, заполненных текстом, до конца раздела
      Dim ovar As Variable 'Переменная для перечисления переменных документа
      
      For Each oSec In ThisDocument.Sections
        'Количество страниц до конца раздела
        nPagesCountEndSec = oSec.Range.Information(wdActiveEndAdjustedPageNumber)
        'Добавляем переменную в документ
        ActiveDocument.Variables.Add "Sec" & oSec.Index & "PageCount", nPagesCountEndSec
        If Err.Number = 5903 Then 'Если такая переменная в документе уже есть
          'Обновляем значение переменной
          ThisDocument.Variables("Sec" & oSec.Index & "PageCount").Value = nPagesCountEndSec
          'Очищаем ошибку
          Err.Clear
        End If
      Next
      'Обновляем поля в документе
      ThisDocument.Fields.Update
      'Это для контроля имен переменных и их значений. На
      'функциональность это не влияет.
      For Each ovar In ThisDocument.Variables
        Debug.Print ovar.Name & vbTab & ovar.Value
      Next
    End Sub
    'Процедура, которая выполняется при печати документа
    Sub FilePrint()
      'Вызываем процедуру, которая обновляет содержимое колонтитулов
      Call GOSTNumbering
      'Показываем диалог печати
      Dialogs(wdDialogFilePrint).Show
    End Sub
    'Процедура, которая вызывается при печати по умолчанию (без диалога печати)
    Sub FilePrintDefault()
      'Вызываем процедуру, которая обновляет содержимое колонтитулов
      If ActiveDocument = ThisDocument Then Call GOSTNumbering
      ActiveDocument.PrintOut
    End Sub
    'Процедура, вызываемая при предварительном просмотре
    Sub FilePrintPreview()
      'Вызываем процедуру, которая обновляет содержимое колонтитулов
      If ActiveDocument = ThisDocument Then Call GOSTNumbering
      ActiveDocument.PrintPreview
    End Sub
    'Процедура, вызываемая при предварительном просмотре в полноэкранном режиме
    Sub FilePrintPreviewFullScreen()
      'Вызываем процедуру, которая обновляет содержимое колонтитулов
      If ActiveDocument = ThisDocument Then Call GOSTNumbering
      ActiveDocument.PrintPreview
    End Sub
    'Процедура, вызываемая при сохранении файла
    Sub FileSave()
      'Вызываем процедуру, которая обновляет содержимое колонтитулов
      If ActiveDocument = ThisDocument Then Call GOSTNumbering
      ActiveDocument.Save
    End Sub
    'Процедура, вызываемая при вызове диалога «Сохранить как…»
    Sub FileSaveAs()
      'Вызываем процедуру, которая обновляет содержимое колонтитулов
      If ActiveDocument = ThisDocument Then Call GOSTNumbering
      Dialogs(wdDialogFileSaveAs).Show
    End Sub

    Наверное, все. Вот пример

  9. Владимир
    21.10.2009 в 22:47 | #9

    Да! Все работает как надо. Отличная работа ребята! Респект и огромное Спасибо, Александр! Удачи!

    Владимир.

  10. Андрей
    01.11.2009 в 15:00 | #10

    Извините за флуд - не нашёл, как задать свой вопрос. Помогите пжлст. Word 2007 при написании адреса, сокращение кв. (квартира) автоматически заменяет на кВ. (киловатт). В списках автозамены - не значится. Что делать?

    Буду очень признателен, кто поможет. Спасибо.

  11. 01.11.2009 в 15:37 | #11

    Андрей, вы уверены, что это сокращение не находится в автозамене? Как вы это проверили?

    И еще вопрос-тест: когда вы вводите это сокращение и когда оно заменяется на другое сокращение попробуйте нажать клавишу Backspase на клавиатуре. Что вы заметили? Сокращение должно принять тот вид, как вы его вводили (без заглавной буквы). У вас так?

  12. 01.11.2009 в 15:39 | #12

    И вот еще проверьте: откройте вкладку Автозамена и нажмите кнопку ИСключения. Посмотрите, есть ли там ваше сокращение. Если нет, то введите его.

  13. Андрей
    01.11.2009 в 15:53 | #13

    Антон - спасибо ОГРОМНОЕ. Первый Вас коммент - ничего не изменил. Второй - да, кв. были в списке исключений автозамены. Жизнь показалась прекрасной. Спасибо.

    P.S. Пока оно там было (кв.) оно заменялось на кВ. Я его удалил - и всё стало ОК. Спасибо.

  14. darklumen
    17.11.2009 в 15:41 | #14

    скажите, а есть какие-нибудь ограничение размера исходного текста, который будет прогоняться макросом?

  15. 17.11.2009 в 20:07 | #15

    Собственно, ограничений как таковых нет. Но есть ограничения по размеру макроса (ограничен объемом опер.памяти) и по размеру документа (файла)- 32 мб.

  16. Юрий
    21.11.2009 в 11:10 | #16

    извините, что не по теме. Я не понял, как можно задавать вопросы на вашем сайте?

  17. 22.11.2009 в 10:03 | #17

    Скоро откроется форум, где вы сможете задать вопрос. Пока же просто подготовьте ваши вопросы.

  18. Алексей
    03.12.2009 в 08:46 | #18

    Здравствуйте! Не нашёл где можно задать вопрос.

    Есть документ, в котором около 2000 страниц и много разделов (около 300). Есть верхний и нижний колонтитулы, в них указана информация о листах в разделе, название раздела и т.д. Хочу в нижний колонтитул поместить номера страниц по порядку, но когда делаю это страницы проставляются только в 1 разделе, в следующем их надо опять проставлять и они начинаются заного.

    В разделе может быть как 1 страница, так и больше.

    Нужно чтобы номера старниц проставлялись автоматом во всех разделах и по порядку, не прерываясь. Как в обычном документе допустим (со 2 стр. и до конца, вне зависимости от разделов) Помогите! Спасибо заранее!

  19. 03.12.2009 в 20:17 | #19

    Алексей, попробуйте такой макрос:

    Sub InsertPageNumIntoEachSection()
      Dim oSec As Section
      
      For Each oSec In ActiveDocument.Sections
        With oSec.Footers(wdHeaderFooterPrimary).PageNumbers
          .Add wdAlignPageNumberRight, True
          .RestartNumberingAtSection = False
        End With
      Next
    End Sub

  20. Алексей
    04.12.2009 в 19:45 | #20

    Александр! Спасибо огромное. Дальше сам уже подкорректирую под свои нужды! Вы мне дали основу! Еще раз спасибо!

  21. Аноним
    31.01.2012 в 11:29 | #21

    Добрый день!

    Подскажите пожалуйста, есть документ в нем двойная нумерация, например сверху страницы должны идти номера 5, 6, 7, а снизу как обычно 1, 2, 3. как это сделать.

    Заранее спасибо Александр

  22. Шмелева Ирина
    09.07.2013 в 11:53 | #22

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

    Но я совсем новичок в макросах. Как поправить чтобы нумерация была по центру?

    Sub InsertPageNumIntoEachSection()
    Dim oSec As Section
     
    For Each oSec In ActiveDocument.Sections
    With oSec.Footers(wdHeaderFooterPrimary).PageNumbers
    .Add wdAlignPageNumberRight, True
    .RestartNumberingAtSection = False
    End With
    Next
    End Sub

  23. 14.07.2013 в 14:50 | #23

    Ирина, замените wdAlignPageNumberRight на wdAlignPageNumberCenter

  24. Алекс
    14.07.2013 в 17:34 | #24

    Почему-то нумерация каждого раздела начинается с нуля

    Sub InsertPageNumIntoEachSection()
    Dim oSec As Section

    For Each oSec In ActiveDocument.Sections
    With oSec.Footers(wdHeaderFooterPrimary).PageNumbers
    .Add wdAlignPageNumberRight, True
    .RestartNumberingAtSection = True
    End With
    Next
    End Sub

Оставьте комментарий!

(обязательно)

^ Наверх