Не так давно по долгу службы понадобилось редактировать файлы MS Office (в первую очередь MS Word и MS Excel) средствами PL/SQL, то есть языка, с .NET не связанного практически никак. В связи с этим возникла проблема, что в руководстве от Microsoft про редактирование этих файлов, что называется, «руками» не сказано практически ничего, а единственный вменяемый сайт-справочник по этому делу, видимо, не обновлялся года этак с 2010. Понимание необходимости свести воедино все, что я по крупицам собрал со Stackoverflow и собственных экспериментов, пришло почти сразу.
Сразу хочется сделать несколько замечаний.
Первое. В основном говорить буду про то, с чем сталкивался лично. Претензий на стопроцентное знание «изнанки» формата у меня нет.
Второе. Как многие, вероятно, знают, файлы MS Office 2007 и выше представляют собой архив, который можно открыть с помощью любого архиватора (WinRAR, 7zip и так далее).
Третье. «Под капотом» у этих файлов — в основном XML-разметка, гордо именуемая OOXML или просто OpenXML. Поэтому, в принципе, для понимания принципов редактирования файлов «руками» достаточно будет Блокнота (или, что удобнее, Notepad++).
Итак, начнем с формата MS Excel как наиболее употребимого для генерации всевозможных отчетов, выгрузок из БД и иже с ними.
Когда имеешь дело с форматом .XLSX, на каждый вопрос «почему оно реализовано… так?» следует отвечать: «того требовала оптимизация». Когда Microsoft расширили допустимый размер листа до неприличного с точки зрения наблюдателя (около миллиона строк и 16 тысяч с хвостиком столбцов), они понимали, что найдутся маньяки, которые все это забьют данными под завязку. А через это, если подходить к хранению данных «в лоб», то даже все возрастающих мощностей ПК не хватит ни при каких условиях. Следовательно, надо сделать так, чтобы объем хранимых данных был меньше, чем объем данных в книге. Как это сделать? Забегая вперед: например, исключить дубликаты строк.
Первый интересующий нас файл — %file%/xl/workbook.xml. Основное его назначение — манифест, то есть перечень листов, из которых наша книга Excel, собственно, и состоит.
Выглядит этот перечень, к примеру, так:
Это значит, что в книге 4 листа, и их имена указаны в атрибутах name. Каждому тегу должен соответствовать файл в папке %file%/xl/worksheets. Excel сам знает, как должны называться эти файлы и при попытке их переименовать, сочтет всю книгу поврежденной.
Еще в папке %file%/xl нас интересует файл styles.xml.
Как нетрудно догадаться, здесь хранится информация об оформлении ячеек. Причем в угоду оптимизации, хранится она в достаточно интересном виде. Файл состоит из следующих секций:
1. Шрифты:
Как можно понять, здесь перечислены только уникальные стили оформления текста, использованные в книге. Каждый тег — один стиль. Вложенные теги — особенности стиля, такие как полужирное написание (тег <b/>), кегль (<sz/>) и другие.
2. Заливка ячеек:
Как видно, первый вариант — без заливки вообще, а второй — сплошная заливка библиотечного цвета «gray125».
3. Границы:
Как видно, одно наименование здесь состоит из пяти элементов, 4 основных границы и диагональная, то есть все то, что можно настроить через GUI самого Excel.
4. Стили ячеек:
А вот здесь надо объяснить подробнее. Когда в файле листа мы будем указывать стиль ячейки, мы будем ссылаться как раз на эту секцию. Каждый тег <xf />, представляющий собой один стиль, является собранием ссылок на предыдущие секции, то есть он сам по себе не содержит объявления шрифта, границ и заливки. Рассмотрим интересные атрибуты ближе:
Далее разговор пойдет про текстовые данные и собственно разметку листов.
Сразу хочется сделать несколько замечаний.
Первое. В основном говорить буду про то, с чем сталкивался лично. Претензий на стопроцентное знание «изнанки» формата у меня нет.
Второе. Как многие, вероятно, знают, файлы MS Office 2007 и выше представляют собой архив, который можно открыть с помощью любого архиватора (WinRAR, 7zip и так далее).
Третье. «Под капотом» у этих файлов — в основном XML-разметка, гордо именуемая OOXML или просто OpenXML. Поэтому, в принципе, для понимания принципов редактирования файлов «руками» достаточно будет Блокнота (или, что удобнее, Notepad++).
Итак, начнем с формата MS Excel как наиболее употребимого для генерации всевозможных отчетов, выгрузок из БД и иже с ними.
Когда имеешь дело с форматом .XLSX, на каждый вопрос «почему оно реализовано… так?» следует отвечать: «того требовала оптимизация». Когда Microsoft расширили допустимый размер листа до неприличного с точки зрения наблюдателя (около миллиона строк и 16 тысяч с хвостиком столбцов), они понимали, что найдутся маньяки, которые все это забьют данными под завязку. А через это, если подходить к хранению данных «в лоб», то даже все возрастающих мощностей ПК не хватит ни при каких условиях. Следовательно, надо сделать так, чтобы объем хранимых данных был меньше, чем объем данных в книге. Как это сделать? Забегая вперед: например, исключить дубликаты строк.
Первый интересующий нас файл — %file%/xl/workbook.xml. Основное его назначение — манифест, то есть перечень листов, из которых наша книга Excel, собственно, и состоит.
Выглядит этот перечень, к примеру, так:
<sheets>
<sheet name="Лист1" sheetId="1" r:id="rId1"/>
<sheet name="Лист2" sheetId="2" r:id="rId2"/>
<sheet name="Лист3" sheetId="3" r:id="rId3"/>
<sheet name="Лист4" sheetId="4" r:id="rId4"/>
</sheets>
Это значит, что в книге 4 листа, и их имена указаны в атрибутах name. Каждому тегу должен соответствовать файл в папке %file%/xl/worksheets. Excel сам знает, как должны называться эти файлы и при попытке их переименовать, сочтет всю книгу поврежденной.
Еще в папке %file%/xl нас интересует файл styles.xml.
Как нетрудно догадаться, здесь хранится информация об оформлении ячеек. Причем в угоду оптимизации, хранится она в достаточно интересном виде. Файл состоит из следующих секций:
1. Шрифты:
<fonts count="2" x14ac:knownFonts="1">
<font>
<sz val="11"/>
<color theme="1"/>
<name val="Calibri"/>
<family val="2"/>
<scheme val="minor"/>
</font>
<font>
<b/>
<sz val="11"/>
<color theme="1"/>
<name val="Calibri"/>
<family val="2"/>
<charset val="204"/>
<scheme val="minor"/>
</font>
</fonts>
Как можно понять, здесь перечислены только уникальные стили оформления текста, использованные в книге. Каждый тег — один стиль. Вложенные теги — особенности стиля, такие как полужирное написание (тег <b/>), кегль (<sz/>) и другие.
2. Заливка ячеек:
<fills count="2">
<fill>
<patternFill patternType="none"/>
</fill>
<fill>
<patternFill patternType="gray125"/>
</fill>
</fills>
Как видно, первый вариант — без заливки вообще, а второй — сплошная заливка библиотечного цвета «gray125».
3. Границы:
<borders count="2">
<border>
<left/>
<right/>
<top/>
<bottom/>
<diagonal/>
</border>
<border>
<left style="thin">
<color indexed="64"/>
</left>
<right style="thin">
<color indexed="64"/>
</right>
<top style="thin">
<color indexed="64"/>
</top>
<bottom style="thin">
<color indexed="64"/>
</bottom>
<diagonal/>
</border>
</borders>
Как видно, одно наименование здесь состоит из пяти элементов, 4 основных границы и диагональная, то есть все то, что можно настроить через GUI самого Excel.
4. Стили ячеек:
<cellXfs count="4">
<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/>
<xf numFmtId="0" fontId="1" fillId="0" borderId="0" xfId="0" applyFont="1" applyAlignment="1">
<alignment wrapText="1"/>
</xf>
<xf numFmtId="0" fontId="0" fillId="0" borderId="1" xfId="0" applyBorder="1" applyAlignment="1">
<alignment horizontal="center" vertical="center"/>
</xf>
<xf numFmtId="0" fontId="1" fillId="2" borderId="1" xfId="0" applyFont="1" applyFill="1" applyBorder="1" applyAlignment="1">
<alignment horizontal="center" vertical="center"/>
</xf>
</cellXfs>
А вот здесь надо объяснить подробнее. Когда в файле листа мы будем указывать стиль ячейки, мы будем ссылаться как раз на эту секцию. Каждый тег <xf />, представляющий собой один стиль, является собранием ссылок на предыдущие секции, то есть он сам по себе не содержит объявления шрифта, границ и заливки. Рассмотрим интересные атрибуты ближе:
- numFmtId — указание формата текста в ячейке (дата, валюта, число, текст, ...). Полный перечень этих типов есть здесь
- fontId, fillId, borderId — ссылка на шрифт/заливку/границы (см. секцию 1, 2 и 3 соответственно). Нумерация начинается с 0.
- applyFont, applyFill, applyBorder — указание на то, что при оформлении этой ячейки вообще используется кастомный шрифт/заливка/границы. По умолчанию 0, так что можно не указывать вообще, как видно на примере в элементе #0.
- applyAlignment — указание на то, что выравнивание текста в ячейке будет отличаться от стандартного. По умолчанию 0, но если указано «1», то в родительский тег <xf />
встраивается тег <alignment />, как видно на примере начиная с #1.
Далее разговор пойдет про текстовые данные и собственно разметку листов.