Презентация PowerPoint – это набор слайдов. Каждый слайд – холст с текстовыми полями, фигурами, картинками, таблицами и диаграммами. Есть и всякие продвинутые штуки вроде анимаций, SmartArt и видео, но для 90% рабочих слайдов они не нужны.

Файл PowerPoint – место, где весь этот контент хранится так, чтобы его мог прочитать компьютер. Внутри лежит и текст, который вы набрали для заголовка диаграммы, и встроенные файлы Excel с данными для этой диаграммы, и .png-файлы с логотипом вашей компании, и свойства, которые определяют, как всё это отрисуется на экране: размер шрифта заголовка, цвет подписей данных, высота строк таблицы, показывать ли сетку на диаграмме.

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

Как файл хранит всю информацию?

Можно попробовать записать всё в текстовый файл. Перечислить каждый элемент и его свойства. Вот примитивный пример: пустой слайд с единственным текстовым полем, где написано «Hello world!».

Можно открыть «Блокнот» и набрать .txt-файл примерно такого вида:

Слайд 1
- Ширина = 13.33 дюйма
- Высота = 7.5 дюйма
- Фон = белый
- Текстовый блок 1
  - Текст = "Hello world!"
  - Семейство шрифтов = Arial
  - Размер шрифта = 18
  - Цвет шрифта = чёрный
  - Позиция = 5.9 дюйма от левого края, 3.5 дюйма от верхнего края

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

В .txt-файл добавится вот это:

Слайд 2
- Ширина = 13.33 дюйма
- Высота = 7.5 дюйма
- Фон = белый
- Текстовый блок 1
  - Текст = "This is a circle"
  - Семейство шрифтов = Arial
  - Размер шрифта = 18
  - Цвет шрифта = чёрный
  - Позиция = 5.9 дюйма от левого края, 2 дюйма от верхнего края
- Фигура 1
  - Тип фигуры = круг
  - Заливка = красная
  - Высота = 2 дюйма
  - Ширина = 2 дюйма
  - Позиция = 5.7 дюйма от левого края, 2.75 дюйма от верхнего края

Смотрите вы на это и думаете: «Хм, я переписываю одни и те же свойства. У всех слайдов одинаковая ширина и высота, да и шрифт я собираюсь использовать однотипный. Дай-ка соберу всё это в одном месте».

Презентация
- Ширина слайда = 13.33 дюйма
- Высота слайда = 7.5 дюйма
- Фон = белый
- Цвет шрифта = чёрный
- Семейство шрифтов = Arial
- Размер шрифта = 18

Слайд 1
- Текстовый блок 1
  - Текст = "Hello world!"
  - Позиция = 5.9 дюйма от левого края, 3.5 дюйма от верхнего края

Слайд 2
- Текстовый блок 1
  - Текст = "This is a circle"
  - Позиция = 5.9 дюйма от левого края, 2 дюйма от верхнего края
- Фигура 1
  - Тип фигуры = круг
  - Заливка = красная
  - Высота = 2 дюйма
  - Ширина = 2 дюйма
  - Позиция = 5.7 дюйма от левого края, 2.75 дюйма от верхнего края

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

presentation/
├── presentation_properties.txt
└── slides/
    ├── slide1.txt
    └── slide2.txt

Диаграммы и картинки

Пока что на ваших слайдах живут только текст и фигуры. А как быть с логотипом компании?

Раз вы уже работаете с папками, просто добавьте папку pictures и складывайте туда .png- и .jpg-файлы.

presentation/
├── presentation_properties.txt
├── slides/
│   ├── slide1.txt
│   └── slide2.txt
└── pictures/
    └── logo.png

А в слайде вы просто ссылаетесь на файл картинки и указываете её размер и положение.

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

Слайд 3
- Картинка
  - Источник = logo.png
  - Высота = 7.5 дюйма
  - Ширина = 7.5 дюйма
  - Позиция = 2.92 дюйма от левого края, 0 дюймов от верхнего края

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

presentation/
├── presentation_properties.txt
├── slides/
│   ├── slide1.txt
│   ├── slide2.txt
│   └── slide3.txt
├── pictures/
│   └── logo.png
└── excel/
    └── book1.xlsx

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

Слайд 4
- Диаграмма
  - Источник = book1.xlsx
  - Высота = 6 дюймов
  - Ширина = 9 дюймов
  - Позиция = 2.17 дюйма от левого края, 0.75 дюйма от верхнего края
  - Заголовок = Chart Title
  - Показывать сетку = true
  - Максимум оси = 6
  - Шаг оси = 1
  .
  .
  .
  - Заливка ряда 3 = серая

Эти три точки делают очень много работы. У диаграмм очень много свойств. Можно сделать жирной одну-единственную букву в подписи данных, можно сделать линию горизонтальной оси толще.

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

Вы переносите свойства диаграммы в отдельный файл chart1.txt и упрощаете slide4.txt до:

Слайд 4
- Диаграмма
  - Источник = chart1.txt
  - Высота = 6 дюймов
  - Ширина = 9 дюймов
  - Позиция = 2.17 дюйма от левого края, 0.75 дюйма от верхнего края

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

presentation/
├── presentation_properties.txt
├── slides/
│   ├── slide1.txt
│   ├── slide2.txt
│   ├── slide3.txt
│   └── slide4.txt
├── pictures/
│   └── logo.png
├── excel/
│   └── book1.xlsx
└── charts/
    └── chart1.txt

Поздравляю! Вы воссоздали базовую архитектуру .pptx-файлов.

Под капотом .pptx-файл – это ZIP-архив, содержащий множество отдельных файлов. И вместо нашего самодельного .txt-формата в нём используется структурированный текст в формате XML.

Попробуйте переименовать один из своих файлов PowerPoint из .pptx в .zip и убедитесь сами.

А вот что внутри папки slides:

Мы не затронули макеты (layouts) и образцы слайдов (masters), но ваши папки ложатся на реальную структуру один в один, если переименовать «excel» в «embeddings», а «pictures» – в «media».

Чем XML отличается от нашего простого текста?

Давайте-ка распакуем фрагмент того самого slide1.xml, где хранится ваш простенький пример «Hello world!».

<p:cSld>
  <p:spTree>
    <p:sp>
      <p:nvSpPr>
        <p:cNvPr id="5" name="TextBox 4">
          <a:extLst>...</a:extLst>
        </p:cNvPr>
        <p:cNvSpPr txBox="1"/>
      </p:nvSpPr>
      <p:spPr>
        <a:xfrm>
          <a:off x="5359675" y="3244334"/>
          <a:ext cx="1472650" cy="369332"/>
        </a:xfrm>
        <a:prstGeom prst="rect">
          <a:avLst/>
        </a:prstGeom>
        <a:noFill/>
      </p:spPr>
      <p:txBody>
        <a:bodyPr wrap="square" rtlCol="0">
          <a:spAutoFit/>
        </a:bodyPr>
        <a:lstStyle/>
        <a:p>
          <a:pPr algn="ctr"/>
          <a:r>
            <a:rPr lang="en-US" dirty="0"/>
            <a:t>Hello world!</a:t>
          </a:r>
        </a:p>
      </p:txBody>
    </p:sp>
  </p:spTree>
</p:cSld>

Нечитаемая машинная каша. Становится чуть лучше, если заменить сокращения на нормальные слова:

<CommonSlideData>
  <ShapeTree>
    <Shape>
      <NonVisualShapeProperties>
        <NonVisualDrawingProperties id="5" name="TextBox 4">
          <ExtensionList>...</ExtensionList>
        </NonVisualDrawingProperties>
        <NonVisualShapeDrawingProperties isTextBox="1"/>
      </NonVisualShapeProperties>
      <ShapeProperties>
        <Transform>
          <Offset x="5359675" y="3244334"/>
          <Extents width="1472650" height="369332"/>
        </Transform>
        <PresetGeometry shape="rectangle">
          <AdjustValues/>
        </PresetGeometry>
        <NoFill/>
      </ShapeProperties>
      <TextBody>
        <BodyProperties wrap="square" rightToLeftColumns="0">
          <ShapeAutoFit/>
        </BodyProperties>
        <ListStyle/>
        <Paragraph>
          <ParagraphProperties alignment="center"/>
          <Run>
            <RunProperties language="en-US" dirty="0"/>
            <Text>Hello world!</Text>
          </Run>
        </Paragraph>
      </TextBody>
    </Shape>
  </ShapeTree>
</CommonSlideData>

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

Слайд
- Фигуры
  - TextBox 4
    - Координата X: 5 359 675
    - Координата Y: 3 244 334
    - Высота: 369 332
    - Ширина: 1 472 650
    - Preset Geometry: прямоугольник
    - Без заливки
    - Текст: Hello world!

Похоже на ваш исходный .txt-файл! Числа для позиции и размера указаны не в дюймах, а в единицах под названием EMU – English Metric Unit. Они огромные – это для того, чтобы обходиться без чисел с плавающей точкой и работать только с целыми числами.

Размера шрифта 18 и шрифта Arial здесь нет, но вы этого и ожидали. Шрифты и их размеры вы записали в presentation_properties.txt, чтобы не повторять их для каждого текстового блока.

В настоящем .pptx-файле семейства шрифтов темы могут жить в theme1.xml в папке theme:

<a:fontScheme name="Office">
  <a:majorFont>
    <a:latin typeface="Arial"/>
  </a:majorFont>
  <a:minorFont>
    <a:latin typeface="Arial"/>
  </a:minorFont>
</a:fontScheme>

То есть по сути .pptx-файл – это набор XML-файлов с данными презентации. Есть папка с отдельными файлами под каждый слайд. И есть ещё несколько XML-файлов под общие свойства вроде шрифтов, которые пришлось бы повторять снова и снова.

Значит, редактировать PPTX легко?

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

Общая идея здравая, но вот реализация – невероятно грязная.

В 2005 году, под давлением правительств, требовавших открытых стандартов для документов, Microsoft подала форматы OOXML (Office Open XML) на стандартизацию в Ecma International. Ecma опубликовала стандарт в 2006 году, а в 2008-м он был принят как международный стандарт ISO.

Основной документ насчитывает больше 5000 страниц. Это как 4 полные трилогии «Властелина колец». Если распечатать его на одной стороне листа, стопка бумаги получится высотой около 50 см.

Кажется чересчур?

The Document Foundation (создатели LibreOffice – свободной альтернативы Microsoft Office) считает это намеренной уловкой Microsoft, чтобы намертво привязать пользователей к своей экосистеме:

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

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

Теория приятная. Так и представляешь, как Билл Гейтс звонит Стиву Балмеру:

— Стив, мне нужны самые злобные инженеры, каких только сможешь найти. Пришли мне команду, которая работала над Clippy и Windows Update. Я хочу, чтобы наш Office был недоступен ни одному конкуренту.

— Без проблем, сниму их с проекта Vista и отправлю к тебе.

Это, безусловно, объяснило бы кучу странных решений в OOXML.

Есть и другая, менее приятная теория. PowerPoint создали в 1987 году, а на Windows он вышел в 1990-м. Инженеры тех лет никак не могли знать, чем PowerPoint станет и что ему в итоге придётся уметь. Учитель естествознания в средней школе использует его, чтобы читать лекции с анимациями. А инвестиционные банкиры на том же софте готовят материалы для заседания совета директоров, где одобряют слияние публичных компаний на миллиарды долларов.

Легко критиковать архитектуру 40 лет спустя, задним умом и с накопленным багажом знаний в разработке ПО. Самому OOXML всего около 20 лет, но ему пришлось описать десятилетия функций PowerPoint и уже существующие презентации. Вполне возможно, что OOXML – такой же бардак, как и любая 40-летняя кодовая база. Просто у обычного кода этот хаос спрятан внутри, а формат файла ломать нельзя: старые презентации должны открываться всегда. Поэтому все накопленные за десятилетия костыли ради совместимости и фичи, прикрученные сбоку, никуда не делись, а застыли в спецификации.

Заключение

Файл PowerPoint – это ZIP-архив, содержащий XML-файлы и бинарные ресурсы. В нём хранятся все свойства, которые вы задали в презентации PowerPoint, и он пытается разложить их по отдельным файлам, чтобы избежать повторов и упростить поиск нужного.

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