Pull to refresh

Препарирование файлов .XLSX: введение, стили ячеек

Reading time 5 min
Views 25K
Не так давно по долгу службы понадобилось редактировать файлы 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, собственно, и состоит.
Выглядит этот перечень, к примеру, так:

<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 />, представляющий собой один стиль, является собранием ссылок на предыдущие секции, то есть он сам по себе не содержит объявления шрифта, границ и заливки. Рассмотрим интересные атрибуты ближе:

  1. numFmtId — указание формата текста в ячейке (дата, валюта, число, текст, ...). Полный перечень этих типов есть здесь
  2. fontId, fillId, borderId — ссылка на шрифт/заливку/границы (см. секцию 1, 2 и 3 соответственно). Нумерация начинается с 0.
  3. applyFont, applyFill, applyBorder — указание на то, что при оформлении этой ячейки вообще используется кастомный шрифт/заливка/границы. По умолчанию 0, так что можно не указывать вообще, как видно на примере в элементе #0.
  4. applyAlignment — указание на то, что выравнивание текста в ячейке будет отличаться от стандартного. По умолчанию 0, но если указано «1», то в родительский тег <xf />
    встраивается тег <alignment />, как видно на примере начиная с #1.

Далее разговор пойдет про текстовые данные и собственно разметку листов.
Tags:
Hubs:
+22
Comments 10
Comments Comments 10

Articles