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

На написание данной статьи меня натолкнули уже существующие статьи от @Lachrimae.

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

Оглавление:

  1. Структура файлов;

  2. Разбор файла _rels/.rels;

  3. Разбор docProps/app.xml;

  4. Разбор docProps/core.xml;

  5. Разбор xl/_rels/workbook.xml.rels;

  6. Разбор xl/printerSettings/printerSettings1.bin;

  7. Разбор xl/theme/theme1.xml;

  8. Разбор xl/worksheets/_rels/sheet1.xml.rels;

  9. Разбор xl/worksheets/sheet1.xml;

  10. Разбор xl/caclChain.xml;

  11. Разбор xl/sharedStrings.xml;

  12. Разбор xl/styles.xml;

  13. Разбор xl/workbook.xml;

  14. Разбор [Content_Types].xml;

  15. Завершение.

Структура файлов

Так как Excel - это архив файлов .xml, то мы можем его распаковать и увидеть следующее содержание:

Дерево файлов и подкаталогов
Дерево файлов и подкаталогов

Некоторые файлы могут отсутствовать, такие как: xl/worksheets/_rels/sheet.xml.rels, xl/calcChain.xml, xl/printerSettings/printerSettings1.bin и sharedString.xml

В папках xl/worksheets, xl/printerSettings и xl/worksheets/_rels могут быть по несколько файлов.

Давайте разберёмся, для чего все эти файлы, начнём по порядку:

_rels/.rels - описание связей файлов, касаемых самой работы Excel;

docProps/app.xml - описание и настройки приложения Excel;

docProps/core.xml - здесь записывается имя создателя файла, время создания и последнего редактирования файла;

xl/_rels/workbook.xml.rels - перечень и описание зависимостей файлов, используемых в книге;

xl/printerSettings/printerSettings1.bin - описание настроек для печати листа;

xl/theme/theme1.xml - описание стилей приложения;

xl/worksheets/_rels/sheet1.xml.rels - описание связей листа xl/worksheets/sheet1.xml с другими документами;

xl/worksheets/sheet1.xml - описание всего происходящего на листе, который находится на первой позиции в списке листов книги. Название листа и название файла никак не связаны, файл всегда называется sheet1, sheet2 и т.д. На каждый лист приходится один такой файл;

xl/caclChain.xml - цепочка вычислений. Конструкция, указывающая порядок вычислений ячеек в книге в последний раз;

xl/sharedStrings.xml - перечень строковых значений, используемых во всей книге;

xl/styles.xml - описание стилей, используемых во всей книге;

xl/workbook.xml - описание настроек книги и перечень используемых листов;

[Content_Types].xml - описание всех файлов и их типов.

Разбор файла _rels/.rels

Если открыть файл - мы увидим следующее содержание:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
    <Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>
    <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>
    <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>
</Relationships>

В первой строке у нас объявляется тип документа - xml с его версией, кодировкой и автономности.

standalone (автономность) - Это объявление указывает, содержит ли внешнее подмножество DTD (Document Type Definition — определение типа документа) какие-либо объявления, которые могут повлиять на текущее содержимое документа.

Вторая строчка - открывающий тег для описания связей документов. Атрибут xmlns - означает, что используется пространство имен, от сюда и название самого атрибута - xml NameSpace.

Далее идут 3 строки связей с документами. У каждого есть атрибуты: Id - уникальное имя для связи, Type - ссылка на стандарт, описывающий нужный нам тип документа, Target - путь к исполняемому файлу.

Разбор docProps/app.xml

Содержимое данного файла примерно такая (не все элементы могут присутствовать и иметь тот же вид, что и у меня):

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes">
    <Application>Microsoft Excel</Application>
    <DocSecurity>0</DocSecurity>
    <ScaleCrop>false</ScaleCrop>
    <HeadingPairs>
        <vt:vector size="2" baseType="variant">
            <vt:variant>
                <vt:lpstr>Worksheets</vt:lpstr>
            </vt:variant>
            <vt:variant>
                <vt:i4>1</vt:i4>
            </vt:variant>
        </vt:vector>
    </HeadingPairs>
    <TitlesOfParts>
        <vt:vector size="1" baseType="lpstr">
            <vt:lpstr>Лист 1</vt:lpstr>
        </vt:vector>
    </TitlesOfParts>
    <Company></Company>
    <LinksUpToDate>false</LinksUpToDate>
    <SharedDoc>false</SharedDoc>
    <HyperlinksChanged>false</HyperlinksChanged>
    <AppVersion>14.0300</AppVersion>
</Properties>

Первая строка нам уже знакома.

Во второй строке открывающий тег properties и эта строка похожа на рассмотренную нами ранее.

Третья строка содержит название приложения. В данном случае - Microsoft Excel (что не удивительно). Данную строку лучше не изменять, ибо приложение упадет.

Следующая строка:

<DocSecurity>0</DocSecurity>

Означает безопасность документа и в зависимости от числа имеет следующий посыл:

0 - Документ не защищен

1 - Документ защищен паролем.

2 - Рекомендуется открывать документ только для чтения.

4 - Документ принудительно открыт только для чтения.

8 - Документ заблокирован для заметок.

Строка:

<ScaleCrop>false</ScaleCrop>

указывает режим отображения эскиза документа. Установите для этого элемента значение TRUE, чтобы включить масштабирование эскиза документа на экране. Установите для этого элемента значение FALSE, чтобы включить обрезку эскиза документа, чтобы отображались только те разделы, которые соответствуют отображаемому значению (из документации microsoft).

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

Внутри HeadingPairs мы имеем 1 векторный контент, в котором имеются 2 его части (подробнее о векторах и baseType можно почитать в документации microsoft).

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

Следующий тег - TitlesOfParts. Он описывает наименования частей документа. в данном случае - названия листов в книге. Здесь также указывается количество частей векторного контента.

В теге Company можно записать название компании.

Следующий элемент - LinksUpToDate - указывает, актуальны ли гиперссылки в документе. Установите для этого элемента значение TRUE, чтобы показать, что гиперссылки обновлены. Установите для этого элемента значение FALSE, чтобы указать, что гиперссылки устарели (из документации microsoft).

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

HyperlinksChanged указывает, что одна или несколько гиперссылок в этой части были обновлены исключительно в этой части производителем. Следующий производитель, который откроет этот документ, обновит отношения гиперссылок новыми гиперссылками, указанными в этой части.

Тег AppVersion указывает версию используемого приложения Excel при создании файла

Разбор docProps/core.xml

Содержимое файла примерно таково:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <dc:creator>Виктор</dc:creator>
  <dcterms:created xsi:type="dcterms:W3CDTF">2006-09-16T00:00:00Z</dcterms:created>
  <dcterms:modified xsi:type="dcterms:W3CDTF">2006-09-16T00:00:00Z</dcterms:modified>
</cp:coreProperties>

Первая строка нам уже знакома.

Во второй строке открывающий тег cp:coreProperties и эта строка похожа на рассмотренную нами ранее. Внутри него описываются свойства приложения:

dc:creator - Имя создателя документа;

dcterms:created - дата и время создания файла;

dcterms:modified - дата и время последнего изменения файла;

Разбор xl/_rels/workbook.xml.rels

Примерное содержимое файла:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
    <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/>
    <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>
    <Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme" Target="theme/theme1.xml"/>
    <Relationship Id="rId4" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" Target="sharedStrings.xml"/>
    <Relationship Id="rId5" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/calcChain" Target="calcChain.xml"/>
</Relationships>

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

Здесь записаны зависимости всех листов в книге, файла со строковыми значениями, цепочками вычислений и прочих файлов (мы разберем эти файлы дальше)

Разбор xl/printerSettings/printerSettings1.bin

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

Разбор xl/theme/theme1.xml

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

Разбор xl/worksheets/_rels/sheet1.xml.rels

Содержимое этого файла может быть следующим:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
  <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/printerSettings" Target="../printerSettings/printerSettings1.bin"/>
</Relationships>

Здесь описана одна зависимость с файлом xl/printerSettings/printerSettings1.bin - настройками для печати.

Разбор xl/worksheets/sheet1.xml

Начинается самое интересное и большое в этой статье.

Начинается такой файл с обычного объявления типа документа xml и некоторых настроек:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x14ac" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac">
  <dimension ref="A1"/>
  <sheetViews>
    <sheetView tabSelected="1" workbookViewId="0">
      <selection activeCell="J10" sqref="J10"/>
      <pane ySplit="1" topLeftCell="A2" activePane="bottomRight" state="frozen"/>
    </sheetView>
  </sheetViews>
  <sheetFormatPr defaultRowHeight="15" x14ac:dyDescent="0.25"/>
  <cols>
    <col min="1" max="1" width="12" customWidth="1"/>
    <col min="3" max="5" width="14" customWidth="1"/>
    <col min="6" max="7" width="31" customWidth="1"/>
  </cols>
  <sheetData/>
  <mergeCells count="40">
    <mergeCell ref="G15:I15"/>
    <mergeCell ref="E3:F3"/>
    <mergeCell ref="E4:F4"/>
  </mergeCells>
  <autoFilter ref="A1:N1"/>
  <printOptions headings="1" gridLines="1"/>
  <pageMargins left="0.7" right="0.7" top="0.7" bottom="0.7" header="0.3" footer="0.3"/>
  <pageSetup paperSize="9" pageOrder="overThenDown" orientation="portrait" blackAndWhite="1" draft="1" cellComments="atEnd" errors="NA" r:id="rId1"/>
</worksheet>

Строка третья означает размер экспортируемого диапазона (с какой по какую ячейку находятся данные).

В теге sheetView - selection описывается выделенная клетка (или диапазон клеток).

Атрибут activeCell - активная ячейка, sqref - выделенная ячейка или диапазон ячеек.

такая строка может выглядеть и вот так:

<selection activeCellId="2" sqref="A1:B2 B2:C3 A1:C3"/>

Здесь уже вместо атрибута activeCell стоит activeCellId, потому что в атрибуте sqref мы видим несколько диапазонов. исходя из этого выясняем, что активный диапазон является A1:C3. на изображении ниже показано, как выглядит такой вариант выделения ячеек.

Выделенные ячейки
Выделенные ячейки

Про тег pane, sheetFormatPr и cols хорошо рассказано в статье от @Lachrimae:

Собственно, закрепление строки — тег <pane />. И вот какие здесь использованы атрибуты:

ySplit — показывает количество закрепленных строк. Для закрепления столбцов есть аналогичный атрибут xSplit;

topLeftCell — указание левой верхней ячейки, видимой по умолчанию НЕзакрепленной области;

activePane — указание местонахождения НЕзакрепленной области. В руководствах сказано, что этот атрибут регулирует, с какой стороны будет НЕзакрепленная область. Правда, попробовав разные значения, я почему-то получил одинаковый результат. Как вариант «by default» я для себя выбрал bottomRight;

state — указатель состояния закрепленной области. Для простого закрепления строки используется значение frozen

Тег <sheetFormatPr />. Пример:

<sheetFormatPr defaultRowHeight="15" x14ac:dyDescent="0.25"/>

Интересен нам здесь в основном атрибут defaultRowHeight, то есть высота столбца по умолчанию. Стандартный, привычный нам вариант — 15 у.е. Если назначить его, скажем, 30 у.е., то строки, для которых высота не указана отдельно, станут в 2 раза выше. Однако, для того чтоб применить значение, отличное от дефолтного, необходимо указать атрибут customHeight со значением «true». Выглядит это примерно так:

<sheetFormatPr defaultRowHeight="30" customHeight="true" x14ac:dyDescent="0.25"/>

Тег </cols>. Помогает установить ширину столбцов отличную от дефолтной. В заполненном виде выглядит примерно так:

Вложенные теги </col> обозначают не каждый один столбец, как могло показаться, а группу столбцов, идущих подряд и имеющих единую ширину.

Атрибут min — первый столбец группы;

Атрибут max — последний столбец группы;

Атрибут width — ширина столбца из группы;

Атрибут customWidth — флаг применения кастомной ширины, без него ширина все равно будет дефолтной;

Тег sheetData - описание содержимого ячеек и их настроек.

здесь структура такова:

<sheetData>
  <row r="1" spans="1:5" ht="18" customHeight="1" x14ac:dyDescent="0.25">
    <c r="A1">
      <v>1</v>
    </c>
    <c r="B1" t="s">
      <v>0</v>
    </c>
    <c r="C1" t="s">
      <v>1</v>
    </c>
    <c r="D1" t="s">
      <v>2</v>
    </c>
    <c r="E1">
      <v>1</v>
    </c>
  </row>
  <row r="4" spans="1:5" x14ac:dyDescent="0.25">
    <c r="A4" t="s">
      <v>2</v>
    </c>
    <c r="B4">
      <f>SUM(A1:E1)</f>
      <v>2</v>
    </c>
  </row>
</sheetData>

В Excel это будет выглядеть вот так:

Ячейки с данными
Ячейки с данными

Давайте разбираться, что же все-таки в коде происходит.

Мы видим два тега row - это наши строки. У каждой есть атрибут r - это номер строки. Атрибут spans означает сколько столбцов задействовано, dyDescent - вертикальное расстояние в пикселях между ячейками. Атрибут ht устанавливает высоту всей строки в пунктах, а тег customHeight говорит, что мы используем нестандартную высоту строки.

В теге row есть теги c - это ячейки в строке. у каждого тега есть атрибут r - означающий позицию ячейки. Но атрибут t - присутствует не у всех, потому что запись t="s" - означает, что у ячейки установлен тип строки, а у кого этого атрибута нет - тип устанавливается стандартный - числовой. Еще у тегов c может присутствовать атрибут s, в котором записывается номер применяемого к ячейке стиля из файла xl/styles.xml (мы доберемся до него позже).

Внутри тегов c есть теги v - это наши значения, записываемые в ячейки. Но не все так просто. Те значения, которые находятся в теге c без атрибута t - те значения записываются без изменений, т.е. записывается в ячейку само число из тега v, а вот те значения, которые находятся в теге c с атрибутом t - уже обрабатываются по-другому: в теге v записан порядковый номер строки в файле xl/sharedStrings.xml (мы доберемся до него позже). В ячейку уже записывается строка, которая имеет порядковый номер, записанный в теге v.

Но мы можем заметить, что одна ячейка имеет помимо тега v еще тег f. Это тег с формулой, в данном случае формула означает: сумма ячеек от A1 до E1. А в теге v записан уже посчитанный ответ. Делать это не обязательно, но если не записать - то при открытии документа excel предложит сохранить изменения, т.к. он сам автоматически посчитал и записал этот результат.

С тегом sheetData разобрались, идем дальше.

Про теги mergeCells и autoFilter снова обратимся к статье от @Lachrimae:

Тег <mergeCells />.

Как мы знаем, в Excel есть возможность объединения ячеек. Все объединенные ячейки на листе перечислены здесь. В заполненном виде тег выглядит примерно так:

<mergeCells count="40">
  <mergeCell ref="G15:I15"/>
  <mergeCell ref="E3:F3"/>
  <mergeCell ref="E4:F4"/>
</mergeCells>

Как видно, одна объединенная ячейка обозначена одним тегом <mergeCell /> с единственным атрибутом ref, задающим диапазон объединения.

Тег <autoFilter />. Фильтры, которые так любят видеть в отчетах наши пользователи. В заполненном виде тег выглядит так:

<autoFilter ref="A1:N1"/>

Нетрудно понять, что атрибут ref задает зону, занимаемую активными ячейками фильтров.

Тег printOptions - параметры печати. Атрибут headings - означает, что будут печататься заголовки, а атрибут gridLines - что будут печататься линии сетки.

Тег pageMargins задает поля сверху, снизу, справа, слева, у заголовков и у подвала для печатаемой страницы.

Тег pageSetup предпочтительные настройки бумаги, опять же, для печати.

Атрибут paperSize - устанавливает размер бумаги.

Используемые значения:

Значение

Описание

16

10 в. x 14 в.

17

11 в. x 17 в.

8

A3 (297 мм x 420 мм)

9

A4 (210 мм x 297 мм)

10

A4 Small (210 мм x 297 мм)

11

A5 (148 мм x 210 мм)

12

B4 (250 мм x 354 мм)

13

A5 (148 мм x 210 мм)

24

Лист размеров C

25

Лист размеров D

20

Конверт #10 (4-1/8 в. x 9-1/2 в.)

21

Конверт #11 (4-1/2 в. x 10-3/8 in.)

22

Конверт #12 (4-1/2 в. x 11 in.)

23

Конверт #14 (5 в. x 11-1/2 в.)

19

Конверт #9 (3-7/8 в. x 8-7/8 in.)

33

Конверт B4 (250 мм x 353 мм)

34

Конверт B5 (176 мм x 250 мм)

35

Конверт B6 (176 мм x 125 мм)

29

Конверт C3 (324 мм x 458 мм)

30

Конверт C4 (229 мм x 324 мм)

28

Конверт C5 (162 мм x 229 мм)

31

Конверт C6 (114 мм x 162 мм)

32

Конверт C65 (114 мм x 229 мм)

27

DL конверта (110 мм x 220 мм)

36

Конверт (110 мм x 230 мм)

37

Envelope Monarch (3-7/8 in. x 7-1/2 in.)

38

Конверт (3-5/8 в. x 6-1/2 in.)

26

Лист размеров E

7

Executive (7-1/2 в. x 10-1/2 in.)

41

Немецкий юридический фанфолд (8-1/2 в. x 13 in.)

40

Немецкий юридический фанфолд (8-1/2 в. x 13 in.)

39

Стандартный фанфолд США (14-7/8 в. x 11 in.)

14

Фолио (8-1/2 в. x 13 in.)

4

Книга (17 в. x 11 in.)

5

Юридический (8-1/2 в. x 14 in.)

1

Письмо (8-1/2 в. x 11 in.)

2

Letter Small (8-1/2 in. x 11 in.)

18

Примечание (8-1/2 в. x 11 in.)

15

Quarto (215 мм x 275 мм)

6

Заявление (5-1/2 в. x 8-1/2 in.)

3

Таблоид (11 в. x 17 in.)

256

Пользовательский

Атрибут pageOrder - направление печати. Если значение "overThenDown" - то будет печататься слева направо, потом нижняя часть снова слева направо и т.д. Если такого атрибута нет - то печататься будет сначала вся левая сторона сверху-вниз, потом та часть, что справа и т.д.

Атрибут orientation - задает ориентацию листов. "portrait" - портретная (вертикальная) ориентация, "landscape" - альбомная (горизонтальная) ориентация.

Атрибут blackAndWhite - если установлена 1 ил true - лист будет напечатан в черно-белом варианте.

Атрибут draft - если установлена 1 ил true - лист будет напечатан без графики.

Атрибут cellComments - печать комментариев к ячейкам. Используемые значения:

  • AsDisplayed - Распечатать Комментарии Как отображается;

  • AtEnd - Печать в конце;

  • None - Не печатать.

Атрибут errors - Печать обработки ошибок.

  • Blank - Показать ошибки ячейки как пустые;

  • Dash - Ошибки ячейки Dash;

  • Displayed - Отображение ошибок ячейки;

  • NA - Отображает "NA".

Атрибут r:id - идентификатор настроек.

Разбор xl/caclChain.xml

По традиции, начнем с содержимого файла:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<calcChain xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
  <c r="B4" i="1"/>
</calcChain>

Здесь нам важна строка 3. видим тег c - наша ячейка, у нее есть атрибут r - адрес ячейки. Индексный атрибут i указывает индекс листа, с которым связана ячейка.

Разбор xl/sharedStrings.xml

Обратимся к статье от @Lachrimae:

Представим, что у нас есть таблица, заполненная строковыми данными, и что она большая. При этом крайне маловероятно, что все значения в ней будут уникальны. Некоторые из них нет-нет, да повторятся где-нибудь в разных частях таблицы. Хранить такой массив «как есть» внутри XML-разметки листа нерационально с точки зрения ресурсов ПК. Поэтому все строковые значения вынесены в отдельный файл, /xl/sharedStrings.xml. Часть его, которая нас интересует, выглядит, допустим, так:

<sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="4" uniqueCount="3">
    <si><t>Вася</t></si>
    <si><t>Петя</t></si>
    <si><t>Саша</t></si>
</sst>

Обратите внимание на атрибуты тега <sst /> «count» и «uniqueCount»: их значения различаются. Дело в том, что в книге одну из строк я использовал дважды. При этом атрибуты не обязательны, то есть если их убрать, то Excel ошибки не выдаст, но при сохранении файла нарисует опять.

Здесь же можно сказать, что здесь, внутри тега <si /> можно играть с настройками шрифта. Для этого используется доработанная напильником система пробегов, применяемая в MS Word (до него мы еще доберемся). Выглядит это примерно так:

    <si>
        <r>
            <t xml:space="preserve">Мама </t>
        </r>
        <r>
            <rPr>
                <b/>
            </rPr>
            <t>мыла</t>
        </r>
        <r>
            <rPr>
                <sz val="18"/>
            </rPr>
            <t xml:space="preserve"> раму</t>
        </r>
    </si>

Обратите внимание: в корневой тег <si/> в предыдущем примере был встроен непосредственно тег <t/>, содержавший текст. Здесь же он обернут тегом <r/>, то есть Run; по-русски его принято назвать «пробег». Пробег — это, если в двух словах — кусок текста, имеющий одинаковые стилевые настройки.

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

Первый:

<r>
    <t xml:space="preserve">Мама </t>
</r>

Этот пробег не содержит секции <rPr/>, поэтому использует стилевые настройки ячейки, в которой находится. В нем интересно другое: атрибут xml:space=«preserve». Дело в том, что по умолчанию что Excel, что Word обрезают концевые пробелы со всех пробегов. Может показаться, что в этом случае в месте стыка пробегов всегда должна получаться примерно такая картина: "ВасяПетя". Но по опыту общения с тем же MS Word мы знаем, что это не так. Из-за чего? Вот как раз из-за xml:space=«preserve».

Второй:

<r>
    <rPr>
        <b/>
    </rPr>
    <t>мыла</t>
</r>

Здесь нет атрибута xml:space=«preserve». Нам без разницы, что Excel сделает с концевыми пробелами, которых нет. Зато есть блок <rPr/>. В принципе, в него можно поместить любые настройки шрифта, которые только есть в Excel. Я же сделал всего один, чтобы не раздувать объем примера.

Третий:

<r>
    <rPr>
        <sz val="18"/>
    </rPr>
    <t xml:space="preserve"> раму</t>
</r>

А здесь у нас есть и блок настроек шрифта, и сохранение концевых пробелов.

Ну и еще коротенькая ремарка. Если есть необходимость сделать многострочную запись в ячейке, то здесь в строке просто будет обычный символ переноса, chr(10). Сам атрибут многострочности ячейки расположен в файле разметки листа. В однострочной ячейке символ переноса будет проигнорирован. Excel просто сделает вид, что его нет.

Добавлю: каждый тег si имеет порядковый номер, начиная с 0. Он нигде не записывается. Этот номер и записывается в файле xl/worksheets/sheet1.xml в теге sheetData, который мы рассматривали ранее.

Разбор xl/styles.xml

Снова обратимся к статье от @Lachrimae. Буду добавлять от себя дополнительную информацию.

Как нетрудно догадаться, здесь хранится информация об оформлении ячеек. Причем в угоду оптимизации, хранится она в достаточно интересном виде. Файл состоит из следующих секций:

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/>) и другие.

На месте тега <b/> можно написать:

  • <i/> - курсив;

  • <u/> - подчеркнутый,
    либо <u val="double"/> - двойное подчеркивание;

  • <strike/> - зачеркнутый;

  • <vertAlign val="superscript"/> - надстрочный текст,
    либо <vertAlign val="subscript"/> - подстрочный текст;

либо сочетание нескольких тегов.

Если хотите установить цвет шрифта какой-нибудь свой (возьмем #123456), то в теге color вместо атрибута theme пишем rgb и вставляем hex-код цвета, должно получиться вот так:

<color rgb="123456"/>

2. Заливка ячеек:

<fills count="2">
    <fill>
        <patternFill patternType="none"/>
    </fill>
    <fill>
        <patternFill patternType="gray125"/>
    </fill>
</fills>

Как видно, первый вариант — без заливки вообще, а второй — сплошная заливка библиотечного цвета «gray125».

Здесь тот же принцип с установкой своего цвета, только немного по-другому записывается:

<fill>
	<patternFill patternType="solid">
    <fgColor rgb="123456"/>
    <bgColor indexed="64"/>
  </patternFill>
</fill>

Тег fgcolor - отвечает за цвет переднего плана, а bgcolor - за цвет заднего плана (indexed="64" - черный цвет).

У тега patternFill атрибут patternType может иметь следующие значения:

  • none - Нет заливки;

  • solid - Сплошная заливка; (пример выше)

  • darkGray - Серый 75%;

    Пример
    <fill>
        <patternFill patternType="darkGray">
        <fgColor rgb="FF0000"/>
        <bgColor rgb="00FF00"/>
      </patternFill>
    </fill>

    Цвет выглядит вот так:

    Цвет ячейки
    Цвет ячейки

    Здесь мы можем наблюдать, что цвета расположены в сетку

  • mediumGray - Серый 50%;

    Пример
    <fill>
        <patternFill patternType="mediumGray">
        <fgColor rgb="FF0000"/>
        <bgColor rgb="00FF00"/>
      </patternFill>
    </fill>

    Цвет выглядит вот так:

    Цвет ячейки
    Цвет ячейки

    Здесь сетка уже поменьше и цвет из тега fgcolor уже меньше прорисовывается.

  • lightGray - Серый 25%;

    Пример
    <fill>
        <patternFill patternType="lightGray">
        <fgColor rgb="FF0000"/>
        <bgColor rgb="00FF00"/>
      </patternFill>
    </fill>

    Цвет выглядит вот так:

    Цвет ячейки
    Цвет ячейки

    Здесь сетка еще меньше и цвет из тега fgcolor еще меньше прорисовывается.

  • gray125 - Серый 12.5%;

    Пример
    <fill>
        <patternFill patternType="gray125">
        <fgColor rgb="FF0000"/>
        <bgColor rgb="00FF00"/>
      </patternFill>
    </fill>

    Цвет выглядит вот так:

    Цвет ячейки
    Цвет ячейки

    Посыл, думаю, понятен

  • gray0625 - Серый 0.025%

    Пример
    <fill>
        <patternFill patternType="gray0625">
        <fgColor rgb="FF0000"/>
        <bgColor rgb="00FF00"/>
      </patternFill>
    </fill>

    Цвет выглядит вот так:

    Цвет ячейки
    Цвет ячейки
  • darkHorizontal - Полосатый цвет с горизонтальными линиями;

    Пример
    <fill>
        <patternFill patternType="darkHorizontal">
        <fgColor rgb="FF0000"/>
        <bgColor rgb="00FF00"/>
      </patternFill>
    </fill>

    Цвет выглядит вот так:

    Цвет ячейки
    Цвет ячейки
  • darkVertical - Полосатый цвет с вертикальными линиями;

    Пример
    <fill>
        <patternFill patternType="darkVertical">
        <fgColor rgb="FF0000"/>
        <bgColor rgb="00FF00"/>
      </patternFill>
    </fill>

    Цвет выглядит вот так:

    Цвет ячейки
    Цвет ячейки
  • darkDown - Полосатый цвет с диагональными линиями сверху-вниз;

    Пример
    <fill>
        <patternFill patternType="darkDown">
        <fgColor rgb="FF0000"/>
        <bgColor rgb="00FF00"/>
      </patternFill>
    </fill>

    Цвет выглядит вот так:

    Цвет ячейки
    Цвет ячейки
  • darkUp - Полосатый цвет с диагональными линиями снизу-вверх;

    Пример
    <fill>
        <patternFill patternType="darkUp">
        <fgColor rgb="FF0000"/>
        <bgColor rgb="00FF00"/>
      </patternFill>
    </fill>

    Цвет выглядит вот так:

    Цвет ячейки
    Цвет ячейки
  • darkGrid - Диагональный клетчатый;

    Пример
    <fill>
        <patternFill patternType="darkGrid">
        <fgColor rgb="FF0000"/>
        <bgColor rgb="00FF00"/>
      </patternFill>
    </fill>

    Цвет выглядит вот так:

    Цвет ячейки
    Цвет ячейки

    В этом варианте сетки четко видно, клетки цветов одинаковые и их поровну. Как шахматная доска

  • darkTrellis - Толстый диагональный клетчатый;

    Пример
    <fill>
        <patternFill patternType="darkTrellis">
        <fgColor rgb="FF0000"/>
        <bgColor rgb="00FF00"/>
      </patternFill>
    </fill>

    Цвет выглядит вот так:

    Цвет ячейки
    Цвет ячейки
  • lightHorizontal - Полосатый цвет с горизонтальными тонкими линиями;

    Пример
    <fill>
        <patternFill patternType="lightHorizontal">
        <fgColor rgb="FF0000"/>
        <bgColor rgb="00FF00"/>
      </patternFill>
    </fill>

    Цвет выглядит вот так:

    Цвет ячейки
    Цвет ячейки
  • lightVertical - Полосатый цвет с вертикальными тонкими линиями;

    Пример
    <fill>
        <patternFill patternType="lightVertical">
        <fgColor rgb="FF0000"/>
        <bgColor rgb="00FF00"/>
      </patternFill>
    </fill>

    Цвет выглядит вот так:

    Цвет ячейки
    Цвет ячейки
  • lightDown - Полосатый цвет с диагональными тонкими линиями сверху-вниз;

    Пример
    <fill>
        <patternFill patternType="lightDown">
        <fgColor rgb="FF0000"/>
        <bgColor rgb="00FF00"/>
      </patternFill>
    </fill>

    Цвет выглядит вот так:

    Цвет ячейки
    Цвет ячейки
  • lightUp - Полосатый цвет с диагональными тонкими линиями снизу-вверх;

    Пример
    <fill>
        <patternFill patternType="lightUp">
        <fgColor rgb="FF0000"/>
        <bgColor rgb="00FF00"/>
      </patternFill>
    </fill>

    Цвет выглядит вот так:

    Цвет ячейки
    Цвет ячейки
  • lightGrid - Тонкий горизонтальный клетчатый;

    Пример
    <fill>
        <patternFill patternType="lightGrid">
        <fgColor rgb="FF0000"/>
        <bgColor rgb="00FF00"/>
      </patternFill>
    </fill>

    Цвет выглядит вот так:

    Цвет ячейки
    Цвет ячейки
  • lightTrellis - Тонкий диагональный клетчатый;

    Пример
    <fill>
        <patternFill patternType="lightTrellis">
        <fgColor rgb="FF0000"/>
        <bgColor rgb="00FF00"/>
      </patternFill>
    </fill>

    Цвет выглядит вот так:

    Цвет ячейки
    Цвет ячейки

Можно еще заливку сделать градиентом:

  • Горизонтальный, вертикальный, диагональный:

    Примеры

    2 цвета:

    <fill>
    	<gradientFill degree="90">
        <stop position="0">
          <color rgb="0000FF"/>
        </stop>
        <stop position="1">
          <color rgb="FFFFFF"/>
        </stop>
      </gradientFill>
    </fill>
    градиент 2 цвета
    градиент 2 цвета

    Атрибут degree - угол поворота, изменяя который можно менять направление градиента.

    3 цвета:

    <fill>
    	<gradientFill degree="90">
        <stop position="0">
          <color rgb="0000FF"/>
        </stop>
        <stop position="0.5">
          <color rgb="FFFFFF"/>
        </stop>
        <stop position="1">
          <color rgb="FF0000"/>
        </stop>
      </gradientFill>
    </fill>
    Градиент 3 цвета
    Градиент 3 цвета
  • От угла:

    Примеры

    От левого верхнего угла

    <fill>
      <gradientFill type="path">
        <stop position="0">
        	<color rgb="0000FF"/>
        </stop>
        <stop position="1">
        	<color rgb="FFFFFF"/>
        </stop>
      </gradientFill>
    </fill>
    Градиент от угла
    Градиент от угла

    От правого верхнего угла - в тег gradientFill добавить атрибуты left="1" right="1", чтобы получилось:

    <gradientFill left="1" right="1" type="path">

    От левого нижнего угла - в тег gradientFill добавить атрибуты bottom="1" top="1";

    От правого нижнего угла - в тег gradientFill добавить атрибуты bottom="1" top="1" left="1" right="1";

  • От центра:

    <fill>
      <gradientFill bottom="0.5" top="0.5" right="0.5" left="0.5" type="path">
        <stop position="0">
        	<color rgb="0000FF"/>
        </stop>
        <stop position="1">
        	<color rgb="FFFFFF"/>
        </stop>
      </gradientFill>
    </fill>
    Градиент от центра
    Градиент от центра

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.

Атрибут style означает стиль границы и может иметь следующие значения:

  • thin – тонка сплошная;

  • hair – мелкая пунктирная;

  • dotted – точечная пунктирная;

  • dashed – пунктирная линия;

  • dashDot – пунктир линия точка;

  • dashDotDot – пунктир линия точка точка;

  • double – двойная сплошная;

  • medium – сплошная средней толщины;

  • mediumDashed – пунктирная линия средней толщины;

  • mediumDashDot – пунктир линия точка средней толщины;

  • mediumDashDotDot – пунктир линия точка точка средней толщины;

  • slantDashDot – косая пунктир линия точка средней толщины;

  • thick – сплошная большой толщины.

Для установки цвета границы используйте уже известную нам запись:

<color rgb="123FFF"/>

Чтобы установить диагональную границу, нужно в тег diagonal записать атрибуты:

  • diagonalDown="1" - для границы сверху-вниз;

  • diagonalUp="1" - для границы снизу вверх.

Должно получиться:

<diagonal diagonalDown="1"/>
<diagonal diagonalUp="1"/>
<diagonal diagonalDown="1" diagonalUp="1"/>

Ну и не забудьте внутри тега diagonal записать цвет для границы - тег color.

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.

В теге alignment могут быть атрибуты:

  • horizontal - выравнивание текста в горизонтальной плоскости. Может иметь значения: left – по левому краю, center – по центру, right – по правому краю, justify – по ширине, fill - заполнение, distributed – распределенное

  • vertical - выравнивание текста по вертикали. Значения: top – по верхнему краю, center – по центру, bottom – по нижнему краю, justify – по высоте, distributed – распределенное

  • textRotation - угол поворота текста. Если установить 255 - то текст будет вертикальный.

  • wrapText="1" - перенос текста

Разбор xl/workbook.xml

Из статьи от @Lachrimae:

Основное его назначение — манифест, то есть перечень листов, из которых наша книга 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 сам знает, как должны называться эти файлы и при попытке их переименовать, сочтет всю книгу поврежденной.

Разбор [Content_Types].xml

Содержимое файла может быть следующим:

<?xml version="1.0" encoding="UTF-8" standalone="true"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
  <Default ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.printerSettings" Extension="bin"/>
  <Default ContentType="application/vnd.openxmlformats-package.relationships+xml" Extension="rels"/>
  <Default ContentType="application/xml" Extension="xml"/>
  <Override ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml" PartName="/xl/workbook.xml"/>
  <Override ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml" PartName="/xl/worksheets/sheet1.xml"/>
  <Override ContentType="application/vnd.openxmlformats-officedocument.theme+xml" PartName="/xl/theme/theme1.xml"/>
  <Override ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml" PartName="/xl/styles.xml"/>
  <Override ContentType="application/vnd.openxmlformats-package.core-properties+xml" PartName="/docProps/core.xml"/>
  <Override ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml" PartName="/docProps/app.xml"/>
  <Override ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml" PartName="/xl/sharedStrings.xml"/>
	<Override ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.calcChain+xml" PartName="/xl/calcChain.xml"/>
</Types>

В этом файле прописываются определения типов всех файлов и их расположения.

Как видно, здесь записываются все файлы листов, стилей, строковых значений, формул и т.д.

Если в документе вы не используете, к примеру, формул, то строки 13 не будет записано.

Завершение

Я разобрал основные моменты содержания файла excel. Если вам будет интересно узнать про диаграммы и другие возможности excel - пишите в комментариях, я постараюсь сделать дополнение к этой статье из ваших просьб.

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

Если вам также будет интересно - напишу статью, как манипулировать с файлами excel посредством PHP/Java/Ruby.

Спасибо за внимание и уделенное время этой статье.

Комментарии (67)


  1. itsoft
    04.12.2021 13:51
    +3

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

    Микрософт совершила большую ошибку в своё время, что для выбрала закрытый формат. В конце 90х я имел дело с LaTex — отличная вещь наподобие того же HTML, только сильно мощнее для формул и для подготовки книг и статей для печати. Но главный плюс в том, что формат читаемый, можно руками набирать. А майкрософт офис много крови всем попил. С закрытыми форматами была проблема и с генерацией документов и с их отображением.

    У Google Docs те же грабли. В прошлом году делал там параметризацию документов, их API — сущий ад. Нет чтобы взять открытый формат и дать всем возможность редактировать документы в исходниках.


    1. web-viktor Автор
      04.12.2021 14:02
      +2

      Я давно работал с word, но только поверхностно, в глубь не лез.

      Если мне будет под силу разобрать его - то напишу статью


    1. rogoz
      04.12.2021 16:14
      +15

      Микрософт совершила большую ошибку в своё время, что для выбрала закрытый формат.
      Это для кого ошибка? Чем тяжелее для альтернатив открывать файлы Офиса, тем больше будет куплено Офисов.


      1. drWhy
        04.12.2021 16:35

        На днях отрывал 30-мегабайтный xlsx, распакованный на порядок больше — с открытием можно сказать что справились OnlyOffice и свежий 7.2 LibreOffice (6.4 не осилил). Процесс потребовал несколько минут времени и до 6 ГБ ОЗУ.
        Отдельное спасибо авторам таких документов.


        1. Browning
          04.12.2021 21:00
          +1

          docx, xlsx и pptx всегда были открытыми форматами. Закрытыми долгое время были doc, xls и ppt.


          1. Browning
            04.12.2021 21:34
            +1

            Точнее, вот правильная ссылка на стандарт. (Ниже @sumbooody её тоже запостил.)


      1. itsoft
        04.12.2021 17:01

        Офис был бы куплен всё равно. Правда с появлением Google.docs офис почти умер.

        Ошибка как раз для Микрософта. Народ использовал другие форматы. Например, мы используем HTML для многих документов. Потому что его сильно проще и быстрее обновить.

        И если бы формат офиса был открытым, то продукт типа гугл.докс появился бы сильно раньше, и не только он один. Майкрософт бы такой продукт либо купил либо сам бы сделал. Майкрософт потерял практически этот рынок сейчас. Все перешли на гуглюдокс. Собственно у Майкрософта есть шанс, запустить аналог гуглюдокс, но с простым открытым форматом, который будет легко генерить программно и ручками. Тогда будет смысл перейти всем на этот формат и генерить документы в нём.


        1. a-tk
          04.12.2021 17:14
          +2

          LaTeX не должен был допустить появления MSWord-а. На деле сколько-нибудь сложное форматирование в Google Docs сделать невозможно.


          1. itsoft
            04.12.2021 17:30

            Битву выигрывает тот, у кого лучше маркетинг.

            Так и не нужно. Сила в простоте. Зачем сложное форматирование, которое потом едет даже в том же Майкрософт Офис?

            Мы у нас в компании последние годы используем Гугл.Докс, да и народ вокруг тоже его использует. Для формул LaTex скорее всего лучше. А для обычной офисной жизни и для написания статей, исковых полностью его хватает. Главное в нём очень удобно организована совместная работа.


            1. saege5b
              04.12.2021 18:58

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

              А Г.Д удобен совместной работой. Мелкомягких на эту тему с 9х годов пинают.


              1. evil_me
                04.12.2021 19:33
                +1

                Извините, а зачем нужен отдельный кубический корень, если есть степень?


              1. itsoft
                04.12.2021 20:17

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


                1. Py6uH
                  05.12.2021 12:31

                  И спокойно используется SQL-запросы


        1. NAI
          04.12.2021 19:00

          Правда с появлением Google.docs офис почти умер.

          Ну да, как же. Оформите мне хоть один документ по ЕСКД (2.105, 2.610) в гугл.доке. Ну такой, чтобы с полями, рамками, колонтитулами.


          1. itsoft
            04.12.2021 20:19
            +1

            Это специфическая задача. У нас в компании нет такого. И полагаю, что у большинства нет. Если вам надо, то мучайтесь с Майкрософт Офисом.


            1. NAI
              04.12.2021 21:33
              +1

              Если у вас чего-то нет, это не означает, что в остальном мире этого нет, у вас просто выборка не репрезентативная.

              То же касается и "специфичности" задачи. Практически любое производство в РФ, которое готовит документацию, сталкивается с проблемой оформления по ЕСКД. И даже не смотря на робкие статьи прикрутить ASCIIDOC к ЕСКД, проще найти человека знающего word, отсыпать ему шаблонов, макросов и отправить работать с миром.

              Я уж не говорю про требования заказчиков о предоставлении документов в "редактируемом формате" для передачи в электронный архив и прочие секретности, да NDA'шности.


              1. itsoft
                04.12.2021 23:20
                +3

                Это у вас выборка нерепрезентативная. Сколько у нас в России производства и сколько у всего остального, где никакого ЕСКД нет. Этого ЕСКД нет в ВУЗах в рабочей документации, в тендерах, в судах. Мелкому бизнесу и физлицам ЕСКД не нужен. Их гораздо больше, чем производства.


                1. daggert
                  05.12.2021 00:39
                  +2

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

                  На моей памяти из всех клиентов что я обслуживаю, а это более трех сотен фирм - никто не перешел в на гуглодокс, потому что простенькая выгрузка от поставщика на десяток страниц тормозит в браузере безбожно даже на i5. Кроме этого накладывается зависимость от постоянного онлайна, что с ценами на интернет для юрлиц (в моем регионе это сильно дороже чем физикам) убирает с конкурентного рынка гуглодоки вовсе.

                  А в ВУЗах и всяких министерствах все еще 2003 не могут до конца убрать. вижу это постоянно в рабочей переписке с ведущими вузами страны, по характерным косякам при перегоне docx->doc в возвращенном письме.


                1. NAI
                  05.12.2021 12:03
                  +2

                  Сколько у нас в России производства и сколько у всего остального, где никакого ЕСКД нет. Этого ЕСКД нет в ВУЗах в рабочей документации, в тендерах, в судах.

                  В судах? Вы серьезно? Как вы предлагаете с гугло-доком соблюдать 152 ФЗ?

                  Как пользоваться гугло-доком в закрытых периметрах? Как пользоваться гугло-доком там где интернета или нет или он медленный (привет сёлам и всей инфраструктуре между городами).

                  Я не знаю сколько всего остального, но не видел ни одного предприятия\организации\ИП которое использовало бы гугло-док в продакшене, как основную систему ведения и разработки документации. Энерго-вырабатывающая промышленность, которая на секундочку местами является градообразующим предприятием, вся сидит на офисе - см. кучу тендеров. То же касается нефтянки и всего прочего.

                  Даже в шутке "Вся мировая экономика держится на Excel", есть доля шутки.


                  1. itsoft
                    05.12.2021 14:47

                    У нас в компании я уже давно не вижу документов в ворде и екселе. Всё в гугле.

                    Так что можете и вы теперь говорить, что знаете как минимум одну организацию.


                    1. NAI
                      05.12.2021 23:46
                      +3

                      Так что можете и вы теперь говорить, что знаете как минимум одну организацию.

                      Пилите пост, как ваш отдел кадров, бухгалтерия и прочие отделы (уж не знаю сколько их) соблюдают 152ФЗ. Заодно, как протащили все это через безопасников.

                      Яб с удовольствием почитал.


                      1. itsoft
                        06.12.2021 00:37

                        При чём тут отдел кадров? Персональные данные хранятся в БД. Все документы у нас по возможности в электронном виде. Те что нужны на бумаге генерятся на php в HTML распечатываются и подписываются. Гугл.Докс нам тут зачем? Гугл.докс используется для нетиповых документов. Статью, исковое написать. А там нет персональных данных. Наличие персональных данных как раз признак того, что документ типовой и должен генериться из базы.


            1. RedWolf
              05.12.2021 12:29
              +3

              У меня уже пять лет есть специфичная задача: фабрика мебели шлёт жене пару сотен xlsx, в которых надо поменять ячейку, пересчитать формулы и конвертнуть в pdf. За пять лет вышло немало свежих версий либреофиса и опенофиса, но ни одна из них не может нормально конвертнуть в пдф ячейки с картинками, ну хоть ты тресни. Ну и правка файла в либреофисе портит его для экселя, и картинки остаются такими же кривыми в экспорте в либреофисе. То есть либреофис тупо не вариант. В итоге, учитывая нежелание покупать мс офис ради такой фигни пару раз в год, мое решение свелось к следующему: в яве через apache poi правим ячейки и пересчитываем формулы, потом вызываем скрипт autoit, который запускает ms excel viewer для нужного файла и отправляет его на печать на пдф принтер. Дёшево и сердито, но доступно только тем, кто успел обзавестись ms excel viewer-ом, ибо ms его выпилило, как Канада свой перехватчик Avro Arrow - с концами.

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


              1. drWhy
                05.12.2021 15:31

                Из Libreoffice экспортировать не пробовал, в исходном MS Excel xlsx проверьте, чтобы соседние изображения не соприкасались — тогда они заменяются единым gif'ом с соответствующей потерей качества, если не соприкасаются — экспортируются в jpg с меньшей потерей. Возможно в новых версиях пофикшено.

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

                А МС да, выпиливает все свои полезные плюшки.


                1. RedWolf
                  05.12.2021 15:43
                  +1

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


          1. ciuafm
            05.12.2021 12:30

            В 2000 писал диплом в QuarkXpress. Не было никаких проблем с рамочками, полями, колонтитулами, расползания и шрифтами. Не знаю почему до сих пор все мучаются с MS Wordом. Работает под Windows 3.11 или 95/98. Под NT не проверял.


            1. NAI
              05.12.2021 12:53
              +1

              1. QuarkXpress это про верстку для печати. Т.е. чтобы картинка на экране 100% соответствовало тому что вылезет из принтера\печатной станции.

              2. Прямой аналог QuarkXpress это Microsoft Publisher

              3. Цена. 470 евро (~40 000\год) за Кварк и 9 000-12 000\год за офис 365. Или ~17к за фиксированную (не подписочную) версию с word, excel, PP и outlook. Выбор очевиден.

              4. Документы можно хоть в Автокаде оформлять, вопрос лишь в том где меньше боли.


              1. Devoter
                05.12.2021 21:48

                Если сравнивать с QuarkXpress, то тогда стоит учитывать и LibreOffice с ценою (без поддержки) ₽0. Но тут же проблема в том, что все вокруг сидят именно на MS.


                1. NAI
                  05.12.2021 23:56
                  +2

                  Не стоит, Кварк это приложение для публикаций (верстка, печать), Word и Writer это текстовые процессоры. Не смотря на кажущуюся одинаковость и, местами пересекающийся функционал, у этих приложений разное назначение.

                  QuarkXPress is a desktop publishing software for creating & editing complex page layouts in a WYSIWYG

                  Writer - A word processor with similar functionality and file support to Microsoft Word or WordPerfect. It has extensive WYSIWYG word processing capabilities, but can also be used as a basic text editor.


        1. elfukado
          05.12.2021 10:04
          +2

          Правда с появлением Google.docs офис почти умер.

          Спасибо за поднятие настроения, поржал с этой фразы.

          Вы, судя по нику, разработчик из it-конторы, а во всём остальном мире есть, к примеру, x5 retail, которому надо заполнить таблицу с макросами в excel не ниже 2013. И сотрудники, быстро работающие в google docs (если это вообще возможно), на дороге не валяются.


          1. mrbald
            05.12.2021 14:03
            +1

            Всё точно так. В финансовых конторах, включая очень большие, типа JP, у всех предустановлен офис с экселем и дофига кода на VBA (исторически и не очень). А у большинства малого бизнеса в Европе есть умный айтишник на подхвате, который понимает, что трахаться с опенсорсом гораздо проблематичнее, чем установить офис и решать проблемы через поддержку мелко-мягких.

            Так что офис живее всех живых, несмотря ни на что.


      1. Loki3000
        06.12.2021 09:13
        +1

        Ну это как посмотреть. Мне кажется Майкрософт перехитрил сам себя: у меня есть несколько xls файлов, которые в одной версии офиса открываются, а в другой нет. И, что примечательно, пересохранение в xlsx проблемы не решает. Иронично, но приходится открывать их с помощью OpenOffice.


        1. axe_chita
          06.12.2021 16:50

          Было подобное в Word. При интенсивном редактировании, и замудренном форматировании получались нередактируемые файлы. Точнее так, закрузить его можно было, но при любой операции (изменения одного символа, просмотре перед печатью, попытка пересохранения приводила к крашу Word. Причем после этого он пытался восстановить файл, загружал сохраненную копию, крашился вновь, и так по кругу если не прервать. Документы и нервы спас какой то бесплатный текстовый редактор? способный открывать doc, но игнорирующий из документа все «нестандартное». После чего отфильтрованный файл сохранялся в RTF, и благополучно допиливался в Word-е. Чаще всего камнем преткновения становились таблицы (которых в Word нет)


          1. DaemonGloom
            07.12.2021 11:40

            WordPad такое умеет. Является частью Windows до сих пор, хотя в 10 и 11 дали возможность его удалять.


        1. ZyXI
          07.12.2021 01:16

          У меня Excel как‐то умудрился сохранить файл с большинством формул в локализованном виде. А потом при открытии эти формулы просто выкидывал (правда, не молча, а утверждая, что файл повреждён). LibreOffice при этом ничего не выкидывал, но и формулы не считал. Проблема решилась нахождением последней рабочей версии в СКВ, сравнении текущей с ней в распакованном виде (после LibreOffice мне стало только понятно, что формулы там есть, а не что с ними не так) и делокализацией скриптами на VimL с последующей запаковкой обратно.


          И я не помню, чтобы я обновлял в это время Office, или трогал связанные с локализацей настройки.


      1. Aggle
        06.12.2021 11:07
        +1

        Забавно, но пару раз (довольно давно) сталкивался со следующей ситуацией:

        Excel отказывается открывать последнюю сохранённую копию файла (без объяснения причин);

        Открываю файл в OpenOffice или LibreOffice;

        Пересохраняю его, как *.xlsx;

        Вуаля - Excel открывает файл как родной.


    1. navferty
      05.12.2021 00:27

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

      В этом и плюс, и минус. В то время скорость записи на носители данных были очень низкие, и в случае выбора plain text формата вставка фрагмента в начале файла привела бы к необходимости перезаписывать весь файл (несколько мегабайт), что могло занимать минуты. Поэтому они заморочились с бинарным форматом, который позволял при редактировании в середине документа дописывать данные в конце файла.


      1. itsoft
        05.12.2021 01:07

        LaTex компилировался в бинарный формат. И им тоже не мешало ничего свой визивиг прикрутить к формату LaTex. Никаких минут я не помню. У меня в LaTex целая книга была.
        Нормально всё записывалось совершенно незаметно.


    1. Timofeuz
      06.12.2021 09:24
      +1

      Микрософт совершила большую ошибку в своё время, что для выбрала закрытый формат.

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


      1. drWhy
        06.12.2021 11:45

        Вероятно имеется в виду статья "Почему форматы файлов Microsoft Office такие сложные".

        «Формат файлов запутан в нужных местах для ускорения типичных операций. К примеру, у Excel 95 и 97 была функция «простого сохранения», которая использовалась в качестве ускоренного варианта документа OLE, полная версия которого была не слишком быстрой для повсеместного использования. У Word было нечто подобное под названием „быстрое сохранение“. Для быстрого сохранения длинных документов 14 раз из 15 все изменения просто добавлялись в конец файла, а весь файл не перезаписывался с нуля. Для жёстких дисков того времени это означало, что можно было успеть сохранить документ, допустим, за 1 секунду вместо 30. Также это означало, что удалённые части документа всё ещё хранились в файле – а людям, как оказалось, это не было нужно.»


  1. Azzrael
    04.12.2021 15:53
    +1

    Хорошая статья. Спасибо.

    Если вам также будет интересно - напишу статью, как манипулировать с файлами excel посредством PHP/Java/Ruby

    На PHP бы посмотрел. Особенно формулы и стили.


    1. web-viktor Автор
      04.12.2021 15:56

      Хорошо, я постараюсь написать в скором об этом


    1. SDKiller
      04.12.2021 16:49

      Так альтернатив особо и нет - наследник phpexcel

      https://phpspreadsheet.readthedocs.io/en/latest/

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

      Поэтому да, если файлы тяжелые, приходится работать непосредственно с xml


      1. web-viktor Автор
        04.12.2021 17:38

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

        У меня есть 2 класса: в первом записываются данные, во втором - запись xml и архивация .xlsx файла

        Возможно, я изобрёл велосипед, но это, прежде всего, опыт и я хорошо разобрался в устройстве excel

        Падение по памяти и производительности скрипта можно исправить рефакторингом кода и уменьшением сложности алгоритмов, чем я сейчас занимаюсь


      1. Azzrael
        04.12.2021 18:54

        Спс, я в курсе про PHPExcel, но это такой комбайн, а мне любопытна лишь часть его функционала - рендер в PDF (а точнее в картинку). А ещё PhpSpreadsheet рендерит путем сборки html c css, вдруг есть способ пооптимальнее. Для винды с установленным Excel все очень элегантно решается через Win32 API и буфер обмена, вот хотелось бы и под никсы что-нибудь компактное. И, если со стилями что-то можно придумать, то вот что делать с формулами...


  1. NAI
    04.12.2021 19:04
    +2

    У меня самый большой вопрос по Excel - как вести разработку макросов? И я сейчас не про встроенную IDE и Rubberduck, а про методологию.

    Вопросы - где макросы хранятся? В каком формате? Можно ли прикрутить к этому нормальную систему контроля версий? Желательно, не используя импорт\экспорт модулей.


    1. web-viktor Автор
      04.12.2021 19:19
      +2

      Хороший вопрос

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


    1. ZyXI
      04.12.2021 20:32
      +1

      Я что‐то подобное делал для Word. Там макросы хранятся в бинарном файле, имитирующем файловую систему (OLE2), который много кто умеет разбирать, но мало кто умеет создавать. Я в итоге просто решил хранить документ без макросов в распакованном виде в VCS, отдельно там же хранить макросы и скриптом запаковывать файл обратно с последующим добавлением файлов с макросами посредством API Word.


  1. sumbooody
    04.12.2021 21:18
    +4

    https://www.ecma-international.org/publications-and-standards/standards/ecma-376/. вот официальная документация


    1. web-viktor Автор
      04.12.2021 21:21

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

      Но все равно, спасибо за ссылку, кому-нибудь она точно пригодится


      1. sumbooody
        04.12.2021 21:47
        +1

        да, с этим согласен. сам в свое время штудировал это все неделями. кстати есть ещё OpenXml productivity tool. она позволяет просматривать содержимое office документов, word , excel, ppt, а также генерирует код, правда на C#. https://github.com/OfficeDev/Open-XML-SDK/releases/tag/v2.5


        1. web-viktor Автор
          04.12.2021 23:47

          Спасибо за клад, думаю, мне это поможет в разборе word, и, может, дополнительных аспектах excel, которые я в этой статье не разобрал


          1. Codenamed
            05.12.2021 10:22
            +1

            Можете еще посмотреть wordroller, там только основная функциональность, поэтому он не такой громоздкий, как Open XML SDK.

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


            1. web-viktor Автор
              05.12.2021 10:47

              Спасибо


  1. axe_chita
    05.12.2021 10:19
    +1

    Спасибо за разбор, всегда интересно увидеть внутреннюю кухню формата файла. Однозначно в закладки. Надеюсь что продолжение следует.


    1. web-viktor Автор
      05.12.2021 10:49

      Да, уже попросили рассказать про макросы и разбор .docx

      Надеюсь победить и выложить статью


      1. axe_chita
        06.12.2021 16:14

        Будем ждать!


  1. vgogolin
    05.12.2021 12:09
    +1

    подскажите, вы планируете делать и поддерживать собственный PhpSpreadsheet?


    1. web-viktor Автор
      05.12.2021 12:26
      +1

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


  1. FrankSinatra
    05.12.2021 16:52
    +1

    Есть еще такое "колесо" на PEAR. В свое время выручило. https://pear.php.net/manual/en/package.fileformats.spreadsheet-excel-writer.intro.php


    1. web-viktor Автор
      05.12.2021 17:43

      Ну, это уже готовое решение, описание документации. По сути, тот же phpspreadsheet, только работает по своему. Да, такое же колесо, но я решил рассказать не о своей библиотеке, а об устройстве excel.

      За ссылку спасибо


    1. FrankSinatra
      05.12.2021 18:08
      +1

      Извините, просто обзор "гвоздя", без обзора "молотка", которым гвоздь можно забить мне показался неполным. Вы же начали статью с идеи "разработки библиотеки".

      Хотелось бы от вас увидеть аналитический обзор ("молотков") решений для эффективной работы с форматом на каких либо языках, раз уж вы на столько погрузились в данный вопрос.

      Возможно это кому-то сохранит время и нервы в дальнейшем.


      1. web-viktor Автор
        05.12.2021 18:49

        Да, я как раз сейчас пишу статью о «молотках».

        На днях опубликую


  1. robert_ayrapetyan
    05.12.2021 18:58
    +1

    Стояла такая же проблема на проекте, но сложнее: нужен экспорт в xls и pptx с графиками и куртизанками из проекта на питоне. Готовых решений нет. Пришлось городить библиотеку на C# с OpenXML SDK и подвязывать ее к питону через pythonnet.

    OpenXML SDK - та еще сдк, спроектировано по принципу: "одна строка в ХМЛ - одна строка кода на C#", вообщем пришлось знатно намучаться. Но сильно выручали генераторы кода из ХМЛ в C#.


    1. web-viktor Автор
      05.12.2021 19:57

      О, да, openXML SDK - это штука, с которой люди встречаются единожды. (Благо, мне пришлось с ней встретиться только в виде документации)

      Возможно, эта статья бы помогла избежать этого хотя бы с excel файлом

      Тем более в скором я напишу про дополнительные функции excel - графики, фигуры, макросы и прочее. Пока собираю информацию и изучаю


  1. opxocc
    06.12.2021 15:23
    +1

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


    1. drWhy
      06.12.2021 15:30

      Или фичи как невозможность в некоторых случаях использования большой буквы «Ж» в названии листа Excel из-за трёхслойной обработки нажатий клавиатуры. А ещё есть люди, которые докапываются до истины.

      "О сколько нам открытий чудных готовит Office Microsoft".