← На главную

Текст в динамике

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

Так получилось, что я в жизни успел поработать с юридическими документами и с кодом. И там, и там версионность текста — естественная вещь. Можно зайти в КонсультантПлюс или в Git, открыть историю изменений, сравнить редакции, увидеть, как текст стал таким, какой он есть.

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

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

Для демонстрации я взял шестое явление «Ревизора» Гоголя (сцена вранья Хлестакова). Во-первых, «Ревизор» хорошо исследован: сохранилось и описано несколько редакций и рукописных источников. Во-вторых, это просто замечательный фрагмент, на котором интересно наблюдать за развитием текста.

Что получилось

Плеер «Текст в динамике»: переход между редакциями «Ревизора» с подсветкой изменений
Фрагмент шестого явления «Ревизора» Н. В. Гоголя.

Попробовать самому

Как это работает

У нас есть несколько версий одного текста: первая черновая редакция, та же рукопись с авторскими правками (осень 1835 года), вторая редакция (декабрь 1835 года) и так далее. Между ними есть последовательные переходы, но иногда история разветвляется. Например, в демонстрации от второй редакции с правками параллельно появляются первое печатное издание и рукопись Малого театра.

В переходе от первого издания 1836 года к сводному тексту можно увидеть, например, как меняется финал явления: реплики становятся резче и отрывистее, а в конце возникает знаменитое «Лабардан! Лабардан!».

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

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

Конкретные изменения подсвечиваются прямо в тексте: удалённые фрагменты становятся серыми и зачёркиваются, добавленные выделяются зелёным, текущая правка отмечена отдельно, чтобы за ней было проще следить. Разница между версиями не размечена вручную — изменения рассчитываются автоматически. Как именно считается эта разница и какие у такого подхода есть ограничения, я расскажу ниже.

Как всё устроено

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

Я решил не считать разницу между редакциями на лету в плеере, а подготовить данные заранее. Для этого я разработал экспериментальный формат TDJ (Text Dynamics JSON) и сделал TDJ Builder на Python: он получает исходные тексты и описание связей между ними, рассчитывает изменения и собирает всё в один файл. Плеер уже просто открывает этот файл и визуализирует историю текста.

Вход Тексты редакции произведения
Python TDJ Builder сравнение версий
Данные TDJ версии и изменения
Frontend Плеер визуализация истории

Формат TDJ

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

Текст внутри TDJ представлен как последовательность токенов (слова, знаки препинания, границы абзацев). Правка сводится к операциям над этой последовательностью. Благодаря этому плеер может воспроизводить переход от одной версии к другой правку за правкой.

Пока TDJ — экспериментальный формат, сделанный под эту демонстрацию, а не попытка предложить готовый стандарт. Полная спецификация лежит на GitHub.

TDJ Builder

TDJ Builder — пока набор Python-скриптов без отдельного интерфейса. Он получает тексты редакций и конфигурацию со связями между ними, а на выходе собирает готовый TDJ-файл.

Отдельно Builder умеет работать с правками внутри одной рукописи. Чтобы не хранить два почти одинаковых файла — до исправления и после него, — замену можно записать прямо в исходном тексте: {-было|стало}. Builder превратит два слоя в две связанные версии.

Как считаются изменения

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

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

Допущения и ограничения

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

  • Порядок правок синтетический. Плеер показывает изменения последовательно, сверху вниз, но это не означает, что автор вносил их именно в таком порядке.
  • Результат зависит от токенизации и diff. То, как текст разбит на элементы и как алгоритм сопоставил версии, определяет, какие правки увидит читатель. Иногда автоматический diff может интерпретировать изменения странно; возможно, здесь понадобится ручная корректировка.
  • Перемещения не выделяются отдельно. Если фрагмент перенесли, плеер увидит удаление в одном месте и добавление в другом.
  • Внутри одной рукописи Builder пока различает только два слоя правок. Для более сложной истории понадобится другая модель разметки.
  • Демонстрация «Ревизора» упрощена. Я использовал ограниченный набор редакций по академическому изданию, а финальная версия в плеере — редакторский свод 1951 года на базе редакции 1842 года.
  • На больших текстах проект пока не проверен. Одно явление «Ревизора» работает нормально, но с целой книгой могут возникнуть вопросы и производительности, и удобства самого подхода.

Что дальше

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

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

Источники и обратная связь

Текст и сведения о редакциях «Ревизора»: Н. В. Гоголь. Полное собрание сочинений в 14 томах. Т. 4. М.; Л.: Издательство Академии наук СССР, 1951. Электронная версия — РВБ.