Русский
Русский
English
Статистика
Реклама

Ms office

Препарируем Compound File Binary format (CFB), или начинаем парсить DOC

11.01.2021 10:24:00 | Автор: admin
Compound File это довольно сложный универсальный бинарный формат файлов, лежащий в основе форматов офисных документов до MS Office 2007 (doc, xls, ppt, msg, ), отчасти MS Office 2007+ (например vbaProject.bin внутри xlsm) и других.
Под катом краткое описание как Compound File устроен внутри, которое, надеюсь, будет полезно как ликбез и поможет читателю лучше понимать что делают утилиты или про что пишут в статьях про CFB файлы.


Устройтсво CFB файла сильно напоминает устройство диска с FAT. Логически CFB хранит древовидную структуру объектов, подобную файловой системе с директориями (тип объекта в CFB STORE) и файлами (в CFB STREAM). Каждый объект имеет имя.
Весь файл разбит на сектора размером 512 байт для v3 и 4096 байт для v4. Это, на мой взгляд, главная разница между v3 и v4. Дальше цифры для примеров я буду приводить для v3 (и в скобках для v4). В начальном секторе хранится заголовок файла и начало таблицы DIFAT (см. ниже). Значение содержимого других секторов определяется динамически по таблицам DIFAT и FAT (да, она прям так и называется, как в файловой системе), фиксированных значений сектор n содержит информацию типа t больше нет.
Всего максимум может быть 0xFFFFFFFA секторов. Номера секторов больше 0xFFFFFFFA являются маркерами и фактически ни на какой сектор не указывают. Например 0xFFFFFFFE (EndOfChain) означает, что текущий сектор последний в списке.

DIFAT


DIFAT Double Indirection File Access Table. Это массив 4х байтных номеров секторов, в которых находится FAT. Первые 109 записей хранятся в первом секторе файла (пять из них выделены жёлтым внизу КДПВ). Если файл больше 6.8 Мбайт (436 Мбайт для v4), то следующие сектора DIFAT хранятся как односвязный список. Номер первого сектора из этого списка хранится в заголовке. В кажом из этих секторов хранится 127 (1023) указателей на сектора с FAT, а в последних 4х байтах номер следующего сектора DIFAT.

FAT


FAT File Access Table. Это массив из 4х байтных значений. Работает так же, как в одноимённой файловой системе. Каждому сектору в файле (кроме заголовочного) соответствует 4х байтное значение в FAT.
Для чтения потока данных (STREAM) нужно знать длину потока (в байтах) и номер первого сектора этого потока. Номер следующего сектора всегда записан в элементе FAT, соответствующем текущему сектору. В элементе FAT, соответствующем последнему сектору в потоке, будет записано значение 0xFFFFFFFE (EndOfChain).
Пример цепочки в FAT
Если данные в потоке записаны в 3 сектора: 0, 1 и 4, то в FAT будут записаны такие значения: 1, 4, x, x, 0xFFFFFFFE (где x какое-то значение, к данному потоку отношения не имеющее).
Для чтения этого потока мы читает данные из сектора 0, расположенного сразу после заголовка файла (сам заголовочный сектор имеет номер -1), читаем номер следующего сектора из FAT[0] = 1, читаем данные из сектора 1 и номер следующего сектора из FAT[1] = 4, читаем данные из сектора 4 и в FAT[4] видим что это последний сектор этого потока (EndOfChain).


Directory


Это структура, которая хранит информацию обо всех остальных объектах в файле, аналог структуры каталогов. Сама она хранится в виде потока данных, длина и первый сектор которого записаны в заголовке файла. Представляет из себя массив 128 байтных записей. Каждая запись соответствует хранимому объекту. Объекты в directory имеют имя и бывают 4х видов:
  1. ROOT корень дерева каталогов. В directory хранится в элементе 0, имя всегда Root Entry. По сути является директорией, но хранит информацию о потоке данных с MiniStream (см. ниже).
  2. STORE директория. Логически является вместилищем других объектов типа STORE или STREAM. Физически дочерние объекты хранятся в виде списка (ещё точнее чёрно-красного дерева), а в STORE хранится указатель на один из дочерних объектов. Никакой поток данных к STORE не прицеплен, но есть ClSid GUID приложения, к которому относится содержимое этой директории.
  3. STREAM файл. Хранит информацию о потоке данных (первый сектор и длина); дочерних объектов и ClSid нет.
  4. UNKNOWN пустой объект. Запись, (почти) забитая 0, чисто технически нужна для дополнения списка Directory до длины, кратной длине сектора.

У каждого объекта есть временные метки: время создания и время модификации, правда часто они просто заполнены 0.
Пример дерева директорий небольшого doc файла:
Root_storage:Root Entry 06090200-0000-0000-c000-000000000046
- Stream:\x01CompObj[106]
- Stream:\x01Ole[20]
- Stream:1Table[4640]
- Stream:Data[374]
- Stream:\x05SummaryInformation[172]
- Stream:WordDocument[198510]
- Storage:ObjectPool 00000000-0000-0000-0000-000000000000
- Stream:\x05DocumentSummaryInformation[116]


MiniStream и MiniFAT


В принципе описанного выше достаточно для хранения данных, но в CFB предусмотрена ещё оптимизация для хранения коротких потоков данных меньше 4096 байт.
При хранении потока данных, длина которого не кратна длине сектора, в последнем секторе остаётся некоторое количество неиспользуемых байт. Чем больше сектор тем больше таких байт в среднем остаётся. Для борьбы с этой проблемой все небольшие (меньше 4096 байт) потоки данных хранятся не в обычных секторах по 512 (4096) байт и FAT, а в отдельном потоке, разбитом на сектора по 64 байта. Этот поток называется MiniStream, а информация для сцепления его секторов в потоки хранится в MiniFAT. Работает это всё совершенно аналогично основной FAT.
И MiniFAT, и MiniStream хранятся как обычные (не Mini) потоки данных. Номер первого сектора и длина MiniFAT хранятся в заголовке, а информация о MiniStream в объекте Root Entry (логической причины хранить именно там не вижу).
Получается такая матрёшка: файл разделён на 512 (4096) байтные сектора, а один из потоков, состоящих из этих секторов, рассматривается как последовательность 64 байтных секторов, и в нём уже хранятся мини потоки данных.
Откуда читать поток данных: из большой FAT или из MiniStream, определяется только по его размеру (меньше 4096 из MiniStream, иначе как обычный поток).

Где же текст/картинка/таблица/макрос?


CFB используется как хранилище, позволяющее сохранить данные разных приложений в одном файле. Если вы хотите докопаться до человекочитаемых данных, то надо парсить потоки данных, извлечённые из CFB, в соответствии с форматом данных приложения. Эта тема выходит за пределы данной статьи (но см. ссылки).

Выводы


По идее такая структура файла была разработана для ускорения внесения отдельных изменений в файл без его полной перезаписи. Не думаю, что это свойство ценно сейчас для обычных офисных документов размером несколько мегабайт.
В общем структура файла CFB просто кладезь для стеганографии, основанной на формате файла. Т. е. сделать совершенно корректный файл, который при открытии в ворде покажет Hello world, а внутри будет ещё много, которые правильный парсер будет считать просто мусором вообще не проблема.
С другой стороны огромный простор для ошибок, путаницы и т.п., огромное количество избыточных записей. Например, можно зациклить цепочку секторов, образующую поток. Наивный парсер при чтении такого файла зависнет.
P.S. Файлы MS Office 2007 и старше (docx, xlsx, pptx, docm,) имеют совершенно другой формат. Внутри там тоже дерево директорий, только вместо CFB там используется ZIP (да, их можно прям раззиповать). Однако макросы, например, хранятся внутри документа в файле vbaProject.bin, который является Compound файлом.

Ссылки


  • Сам CFB очень хорошо (на мой взгляд) документирован: [MS-CFB]: Compound File Binary File Format
  • [MS-DOC]: Word (.doc) Binary File Format для тех, кто хочет полноценно разобрать doc.
  • [MS-OVBA]: Office VBA File Format Structure о том, как хранятся макросы, в том числе в Office 2007+ (docm, xlsm, ...)
  • Ещё пара статей на Хабре про парсинг DOC от Rembish:
    Текст любой ценой: WCBFF и DOC и Текст любой ценой: Miette
  • Для практического анализа файлов MS Office (как до 2007 Офиса, так и после), в том числе подозрительных, рекомендую набор утилит OleTools. Их можно использовать и как отдельные программы, и как модули Python.
  • Ещё полезный набор утилит OleDump
  • CFB extractor, появившийся в процессе написания этой статьи. Из полезного умеет доставать из CFB файла все потоки данных (включая потоки, не имеющие соответствующей записи в директории).
Подробнее..

А я говорю, возьми Excel и позвони

01.04.2021 14:11:52 | Автор: admin

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

Но в современном мире иметь API недостаточно мало кто хочет формировать HTTP-запросы, передавать параметры, думать про правильную авторизацию. Поэтому мы предлагаем SDK для разных языков программирования: Python, PHP, C# и многих других. И кажется, что этого достаточно, чтобы сделать нашу платформу лёгкой в использовании для очень большой аудитории. Или всё-таки недостаточно?

Обратимся к статистике. По разным данным сейчас в мире насчитывается где-то 15-30 миллионов разработчиков цифра несомненно впечатляющая. Но, например, пользователей MS Excel в мире не менее 100 миллионов. Почему же они должны страдать? Ведь, будем честны, почти каждый из тех, кто хоть раз открывал Excel, явно ощущал недостаток возможностей по управлению коммуникационными платформами в этом без сомнения очень гибком программном продукте. Практически каждый день мы получаем на наш email сотни запросов, которые сводятся к очень простой просьбе: Я хочу звонить из Excel!. Однажды у окон нашего офиса даже выстроились люди с такими требованиями (видели фото выше?) Мы просто не могли оставаться в стороне.

Однако звонки это всё-таки слишком революционно, а главное, потребует установки дополнительных ActiveX-компонентов, что, безусловно, противоречит всем существующим и несуществующим политикам информационной безопасности, поэтому давайте начнём с более простой вещи SDK для работы с нашим API. Из средств разработки в Экселе доступен VBA, для него мы и создадим SDK.

Для того, чтобы выполнить API-запрос, необходимо:

  1. Сформировать URL и тело POST-запроса.

  2. Добавить аутентификационные параметры.

  3. Непосредственно выполнить запрос.

  4. Распарсить результат (в нашем случае это JSON).

Формируем URL и тело POST-запроса

Первая часть, казалось бы, самая простая: нужно просто закодировать параметры в URL-кодировку и склеить их. Но в стандартом VBA не предусмотрена URL-кодировка (позже мы поймём, почему). Ничего страшного, на просторах Интернета есть множество разных решений, выберем одно из них.

Public Function URL_Encode(ByRef txt As String) As String    Dim buffer As String, i As Long, c As Long, n As Long    buffer = String$(Len(txt) * 12, "%")     For i = 1 To Len(txt)        c = AscW(Mid$(txt, i, 1)) And 65535         Select Case c            Case 48 To 57, 65 To 90, 97 To 122, 45, 46, 95  ' Unescaped 0-9A-Za-z-._ '                n = n + 1                Mid$(buffer, n) = ChrW(c)            Case Is <= 127            ' Escaped UTF-8 1 bytes U+0000 to U+007F '                n = n + 3                Mid$(buffer, n - 1) = Right$(Hex$(256 + c), 2)            Case Is <= 2047           ' Escaped UTF-8 2 bytes U+0080 to U+07FF '                n = n + 6                Mid$(buffer, n - 4) = Hex$(192 + (c \ 64))                Mid$(buffer, n - 1) = Hex$(128 + (c Mod 64))            Case 55296 To 57343       ' Escaped UTF-8 4 bytes U+010000 to U+10FFFF '                i = i + 1                c = 65536 + (c Mod 1024) * 1024 + (AscW(Mid$(txt, i, 1)) And 1023)                n = n + 12                Mid$(buffer, n - 10) = Hex$(240 + (c \ 262144))                Mid$(buffer, n - 7) = Hex$(128 + ((c \ 4096) Mod 64))                Mid$(buffer, n - 4) = Hex$(128 + ((c \ 64) Mod 64))                Mid$(buffer, n - 1) = Hex$(128 + (c Mod 64))            Case Else                 ' Escaped UTF-8 3 bytes U+0800 to U+FFFF '                n = n + 9                Mid$(buffer, n - 7) = Hex$(224 + (c \ 4096))                Mid$(buffer, n - 4) = Hex$(128 + ((c \ 64) Mod 64))                Mid$(buffer, n - 1) = Hex$(128 + (c Mod 64))        End Select    Next    URL_Encode = Left$(buffer, n)End Function

Следующий нюанс передача даты и времени. В API Voximplant временные метки принимаются в UTC в формате YYYY-MM-DD hh:mm:ss. В Excel же дата и время хранятся без учёта часового пояса (на самом деле, в самой таблице они вообще хранятся как число с плавающей точкой). Поэтому нам придётся принимать дату/время из таблицы тоже UTC. Мы думаем, что все 100+ миллионов пользователей Excel знают, что такое UTC, и это не вызовет у них никаких вопросов.

Кстати, в VBA есть функция форматирования даты, и она даже работает, но весьма необычным образом. Интересующий нас формат даты описывается так: yyyy-mm-dd hh:mm:ss. То есть mm это либо месяц, либо минуты в зависимости от того, за чем оно следует: за hhили за yyyy (это не шутка, это даже в MSDN описано). В общем, если кто-то захочет вывести время без часов, придётся импровизировать.

Переходим к аутентификации

Здесь нас ожидает самое большое разочарование. Мы в Voximplant предлагаем нашим клиентам использовать JWT, что, конечно, весьма мудрёно, если выполнять запросы из консоли или браузера, но при использовании наших SDK это совершенно никак не усложняет жизнь разработчику. В то же время JWT обеспечивает крайне высокий уровень безопасности.

А что же VBA? К сожалению, разумно простого способа сформировать JWT-подпись просто не существует. Причина в том, что в VBA доступен фреймворк .NET версии 4.x, а функция RSA.ImportPkcs8PrivateKey, необходимая для загрузки приватного ключа из PKCS8, появилась только в .NET 5. Да и вообще, все .NET-разработчики используют для таких задач сторонние библиотеки.

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

Кадр из кинофильма Большой Лебовски (The Big Lebowski (1998), Polygram Filmed Entertainment, Working Title Films)Кадр из кинофильма Большой Лебовски (The Big Lebowski (1998), Polygram Filmed Entertainment, Working Title Films)

Выполняем запрос

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

Но, тем не менее, это достаточно тривиальная манипуляция подключаем необходимый фреймворк MSXML 6.0 и Microsoft Scripting Runtime и выполняем запрос, подключая через COM сам MSXML. Просто!

Function makeRequest(name As String, params As Dictionary, accountId As Integer, apiKey As String) As Object    Dim objHTTP As New MSXML2.XMLHTTP60    Dim jsonData As String    Dim parsedJson As Object    Dim postString As String    postString = ""        Dim iterKey As Variant        For Each iterKey In params.Keys        postString = postString & "&" & iterKey & "=" & URL_Encode(params(iterKey))    Next    Url = "https://api.voximplant.com/platform_api/" + name    objHTTP.Open "POST", Url, False    objHTTP.send "account_id=" & accountId & "&api_key=" & apiKey & postString    jsonData = objHTTP.responseText    Set parsedJson = JsonConverter.ParseJson(jsonData)    Set makeRequest = parsedJsonEnd Function

Парсим JSON

Ну и, наконец, JSON. Как и всё остальное, парсер JSON надо искать где-то вовне экосистемы VBA. К счастью, на дворе 2021 год, есть GitHub, и кто-то уже озадачился созданием JSON-парсера для VBA. Мы взяли вот такой.

Он подключается как отдельный модуль и превращает JSON-строку в Dictionary. То, что нужно!

Дальше берём генератор одного из наших SDK (мы взяли питоновский), заменяем шаблоны и заставляем его генерировать код на VBA. В итоге получаем готовый SDK, который можно скачать на нашем GitHub.

SDK представляет собой Class Module, который можно подключить к вашей любимой книге в Excel и делать с его помощью разные странные вещи. В принципе, можно даже звонки запускать, SDK поддерживает все необходимые для этого функции API в). Но это будет не лучшей идеей, учитывая, что не до конца известно, когда Excel решит пересчитать все формулы (именно в этот момент произойдёт вызов функции API).

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

Для этого пишем вот такую функцию:

Function getTotalCallCost(FromDate, ToDate, Username) As Double    Dim totalCost As Double    Dim lastCount As Integer    Dim offset As Integer    Dim res As Dictionary    Dim RecordsPerRequest As Integer    Dim api As New VoximplantAPI    totalCost = 0    lastCount = 1    offset = 0    RecordsPerRequest = 100        'Pass Voximplant account id and API key    api.SetCredentials 100, "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"        Do While lastCount > 0        Set res = api.GetCallHistory(FromDate, ToDate, remote_number:=Username, with_calls:=True, with_records:=True, with_other_resources:=True, offset:=offset, count:=RecordsPerRequest)                Dim session As Variant        Dim item As Variant                For Each session In res("result")            For Each item In session("calls")                totalCost = totalCost + item("cost")            Next            For Each item In session("records")                totalCost = totalCost + item("cost")            Next            For Each item In session("other_resource_usage")                totalCost = totalCost + item("cost")            Next        Next                lastCount = res("count")        offset = offset + RecordsPerRequest    Loop        getTotalCallCost = totalCostEnd Function

И вызываем её следующим образом:

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


Резюме:

При желании можно и для VBA сделать какое-то подобие SDK. При его создании не пострадал ни один разработчик. Ах да, с 1 апреля! :D

Подробнее..

Перевод Бесшовная интеграция Microsoft Excel и Word с помощью Python

22.04.2021 16:18:43 | Автор: admin

Хотя в среднем для каждодневных задач автоматизация не требуется, бывают случаи, когда она может быть необходима.Создание множества диаграмм, рисунков, таблиц и отчётов может утомить, если вы работаете вручную. Так быть не должно.Можно построить конвейер на Python, с помощью которого Excel и Word легко интегрировать: нужно создать таблицы в Excel, а затем перенести результаты в Word, чтобы практически мгновенно получить отчёт.


Openpyxl

Встречайте Openpyxl возможно, одну из самых универсальных связок [биндингов] с Python, которая сделает взаимодействие с Excel очень простым. Вооружившись этой библиотекой, вы сможете читать и записывать все нынешние и устаревшие форматы Excel, то есть xlsx и xls.

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

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

Python-docx

Затем идёт Python-docx, этот пакет для Word то же самое, что Openpyxl для Excel. Если вы ещё не изучили его документацию, вам, вероятно, стоит взглянуть на неё. Python-docx без преувеличения один из самых простых и понятных мне наборов инструментов, с которыми я работал с тех пор, как начал работать с самим Python.

Python-docx позволяет автоматизировать создание документов путём автоматической вставки текста, заполнения таблиц и рендеринга изображений в отчёт без каких-либо накладных расходов. Без лишних слов давайте создадим наш собственный автоматизированный конвейер. Запустите Anaconda (или любую другую IDE по вашему выбору) и установите эти пакеты:

pip install openpyxlpip install python-docx

Автоматизация Microsoft Excel

Сначала загрузим уже созданный лист Excel, вот так:

workbook = xl.load_workbook('Book1.xlsx')sheet_1 = workbook['Sheet1']

Теперь переберём все строки в нашей таблице, чтобы вычислить и вставить значения мощности, умножив ток на напряжение:

for row in range(2, sheet_1.max_row + 1):    current = sheet_1.cell(row, 2)    voltage = sheet_1.cell(row, 3)    power = float(current.value) * float(voltage.value)    power_cell = sheet_1.cell(row, 1)    power_cell.value = power

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

values = Reference(sheet_1, min_row = 2, max_row = sheet_1.max_row, min_col = 1, max_col = 1)chart = LineChart()chart.y_axis.title = 'Power'chart.x_axis.title = 'Index'chart.add_data(values)sheet_1.add_chart(chart, 'e2') workbook.save('Book1.xlsx')
Автоматически созданная таблица ExcelАвтоматически созданная таблица Excel

Извлечение диаграммы

Теперь, когда мы сгенерировали нашу диаграмму, нам нужно извлечь её как изображение, чтобы мы могли использовать её в нашем отчёте Word. Сначала укажем точное местоположение файла Excel, а также место, где должно быть сохранено изображение диаграммы:

input_file = "C:/Users/.../Book1.xlsx"output_image = "C:/Users/.../chart.png"

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

operation = win32com.client.Dispatch("Excel.Application")operation.Visible = 0operation.DisplayAlerts = 0workbook_2 = operation.Workbooks.Open(input_file)sheet_2 = operation.Sheets(1)

Позднее вы сможете перебирать все объекты диаграммы в электронной таблице (если их несколько) и сохранять их в указанном месте:

for x, chart in enumerate(sheet_2.Shapes):    chart.Copy()    image = ImageGrab.grabclipboard()    image.save(output_image, 'png')    passworkbook_2.Close(True)operation.Quit()

Автоматизация Microsoft Word

Теперь, когда у нас есть сгенерированное изображение диаграммы, мы должны создать шаблон документа, который в принципе является обычным документом Microsoft Word (.docx), сформированным именно так, как мы хотим: отчёт содержит шрифты, размеры шрифтов, структуру и форматирование страниц.

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

Шаблон документа Microsoft WordШаблон документа Microsoft Word

Любой сгенерированный контент, включая текст и изображения, может быть объявлен в двойных фигурных скобках {{ variable_name }}. В случае таблиц вам нужно создать таблицу со строкой шаблона со всеми включёнными столбцами, затем нужно добавить одну строку вверху и одну строку ниже со следующей нотацией:

Первая строка:

{%tr for item in variable_name %}

Последняя строка:

{%tr for item in variable_name %}

На рисунке выше имена переменных:

  • table_contents для словаря Python, в котором будут храниться наши табличные данные;

  • Index для ключей словаря (первый столбец);

  • Power, Current и Voltage для значений словаря (второй, третий и четвёртый столбцы).

Затем импортируем наш шаблонный документ в Python и создаём словарь, в котором будут храниться значения нашей таблицы:

template = DocxTemplate('template.docx')table_contents = []for i in range(2, sheet_1.max_row + 1):    table_contents.append({        'Index': i-1,        'Power': sheet_1.cell(i, 1).value,        'Current': sheet_1.cell(i, 2).value,        'Voltage': sheet_1.cell(i, 3).value        })

Далее импортируем ранее созданное в Excel изображение диаграммы и создадим другой словарь для создания экземпляров всех объявленных в документе шаблона переменных-заполнителей:

image = InlineImage(template,'chart.png',Cm(10))context = {    'title': 'Automated Report',    'day': datetime.datetime.now().strftime('%d'),    'month': datetime.datetime.now().strftime('%b'),    'year': datetime.datetime.now().strftime('%Y'),    'table_contents': table_contents,    'image': image    }

И, наконец, визуализируем отчёт с нашей таблицей значений и изображением диаграммы:

template.render(context)template.save('Automated_report.docx')

Результаты

И вот автоматически сгенерированный отчёт Microsoft Word с числами и созданной в Microsoft Excel диаграммой. Мы получили полностью автоматизированный конвейер, его можно использовать, чтобы создать столько таблиц, диаграмм и документов, сколько вам потребуется.

Автоматически сгенерированный отчётАвтоматически сгенерированный отчёт

Исходный код

import openpyxl as xlfrom openpyxl.chart import LineChart, Referenceimport win32com.clientimport PILfrom PIL import ImageGrab, Imageimport osimport sysfrom docx.shared import Cmfrom docxtpl import DocxTemplate, InlineImagefrom docx.shared import Cm, Inches, Mm, Emuimport randomimport datetimeimport matplotlib.pyplot as plt######## Generate automated excel workbook ########workbook = xl.load_workbook('Book1.xlsx')sheet_1 = workbook['Sheet1']  for row in range(2, sheet_1.max_row + 1):    current = sheet_1.cell(row, 2)    voltage = sheet_1.cell(row, 3)    power = float(current.value) * float(voltage.value)    power_cell = sheet_1.cell(row, 1)    power_cell.value = power  values = Reference(sheet_1, min_row = 2, max_row = sheet_1.max_row, min_col = 1, max_col = 1)chart = LineChart()chart.y_axis.title = 'Power'chart.x_axis.title = 'Index'chart.add_data(values)sheet_1.add_chart(chart, 'e2')  workbook.save('Book1.xlsx')######## Extract chart image from Excel workbook ########input_file = "C:/Users/.../Book1.xlsx"output_image = "C:/Users/.../chart.png"operation = win32com.client.Dispatch("Excel.Application")operation.Visible = 0operation.DisplayAlerts = 0    workbook_2 = operation.Workbooks.Open(input_file)sheet_2 = operation.Sheets(1)    for x, chart in enumerate(sheet_2.Shapes):    chart.Copy()    image = ImageGrab.grabclipboard()    image.save(output_image, 'png')    passworkbook_2.Close(True)operation.Quit()######## Generating automated word document ########template = DocxTemplate('template.docx')#Generate list of random valuestable_contents = []for i in range(2, sheet_1.max_row + 1):        table_contents.append({        'Index': i-1,        'Power': sheet_1.cell(i, 1).value,        'Current': sheet_1.cell(i, 2).value,        'Voltage': sheet_1.cell(i, 3).value        })#Import saved figureimage = InlineImage(template,'chart.png',Cm(10))#Declare template variablescontext = {    'title': 'Automated Report',    'day': datetime.datetime.now().strftime('%d'),    'month': datetime.datetime.now().strftime('%b'),    'year': datetime.datetime.now().strftime('%Y'),    'table_contents': table_contents,    'image': image    }#Render automated reporttemplate.render(context)template.save('Automated_report.docx')

Вот мой репозиторий на GitHub с шаблоном документа и исходным кодом для этого туториала. А вот ссылка на курс Fullstack-разработчик на Python, который сделает из вас настоящего универсального солдата от кодинга.

Узнайте, как прокачаться и в других специальностях или освоить их с нуля:

Другие профессии и курсы
Подробнее..

Конвертируем doc в docx и xml на C

23.11.2020 10:05:21 | Автор: admin

Продолжаю свой цикл статей, посвященный конвертации различных текстовых файлов с помощью решений, реализованных на языке C#.


С момента моей последней публикации Конвертация xls в xlsx и xml на C# прошло более полугода, за которые я успел сменить как работодателя, так и пересмотреть свои взгляды на некоторые аспекты коммерческой разработки. Сейчас, работая в международной компании с совершенно иным подходом к разработке ПО (ревью кода, юнит-тестирование, команда автотестеров, строгое соблюдение СМК, заботливый менеджер, очаровательная HR и прочие корпоративные плюшки), я начинаю понимать, почему некоторые из комментаторов интересовались целесообразностью предлагаемых мной велокостылей, когда на рынке есть очень достойные готовые решения, например, от e-iceblue. Но давайте не забывать, что ситуации бывают разные, компании тем более, и если потребность в решении какой-то задачи с использованием определенного инструментария возникла у одного человека, то со значительной долей вероятности она возникнет и у другого.



Итак, дано:


  1. Неопределенное множество файлов в формате .doc, которые нужно конвертировать в xml (например, для парсинга и организации автоматизированной навигации внутри текста), желательно с сохранением форматирования.
  2. На сервере памяти чуть больше, чем у рыбки, а на процессоре уже можно жарить яичницу, да и у компании нет лишней лицензии на Word, поэтому конвертация должна происходить без запуска каких-либо офисных приложений.
  3. Сервис должен быть написан на языке C# и в последующем интегрирован в код другого продукта.
  4. На решение задачи два дня и две ночи, которые истекли вчера.

Поехали!


  • Во-первых, нужно сразу уяснить, что старые офисные форматы файлов, такие как .doc и .xls, являются бинарными, и достать что-нибудь человекочитаемое из них без использования текстовых редакторов/процессоров не получится. Прочитать об этом можно в официальной документации. Если есть желание поковыряться поглубже, посчитать нолики с единичками и узнать, что они означают, то лучше сразу перейти сюда.
  • Во-вторых, несмотря на наличие бесплатных решений для работы с .doc, большинство из них написаны на Python, Ruby и чем угодно еще, но не C#.
  • В-третьих, найденное мной решение, а именно библиотека b2xtranslator, является единственным доступным бесплатным инструментом такого рода, еще и написана при поддержке Microsoft, если верить вот этому источнику. Если вдруг вы встречали какие-нибудь аналоги данной библиотеки, пожалуйста, напишите об этом в комментариях. Даже это душеспасительное решение не превратит .doc в .xml, однако поможет нам превратить его в .docx, с которым мы уже умеем работать.

Довольно слов давайте к делу


Установка b2xtranslator


Для работы нам понадобиться библиотека b2xtranslator. Ее можно подключить через менеджера пакетов NuGet.

Однако я настоятельно рекомендую скачать ее из официального git-репозитория по следующим причинам:


  • a) Библиотека представляет собой комбайн, работающий с различными бинарными офисными документами (.doc, .xls, .ppt), что может быть избыточным
  • b) Проект достаточно долго не обновляется и вам, возможно, придется доработать его напильником
  • c) Задача, с которой я столкнулся, как раз потребовала внесения некоторых изменений в работу библиотеки, а также изучения ее алгоритмов и используемых структур для успешной интеграции в свое решение
    Для дальнейшей работы нам понадобиться подключить в свое решение два проекта из библиотеки: b2xtranslator\Common\b2xtranslator.csproj и b2xtranslator\Doc\b2xtranslator.doc.csproj

Конвертация .doc в .docx


Конвертация документов строится по следующему алгоритму:


  1. Инициализация дескриптора для конвертируемого файла.
    Для этого необходимо создать экземпляр класса StructuredStorageReader, конструктор которого в качестве аргумента может принимать или путь до файла, или последовательность байтов (Stream), что делает его крайне удобным при работе с файлами, загружаемыми по сети. Также обращаю внимание, что так как библиотека b2xtranslator является комбайном для конвертации бинарных офисных форматов в современный OpenXML, то независимо от того, какой формат мы хотим конвертировать (.ppt, .xls или .doc) инициализация дескриптора всегда будет происходить с помощью указанного класса (StructuredStorageReader).
    StructuredStorageReader reader = new StructuredStorageReader(docPath);
    
  2. Парсинг бинарного .doc файла с помощью объекта класса WordDocument, конструктор которого в качестве аргумента принимает объект типа StructuredStorageReader.
    WordDocument doc = new WordDocument(reader);
    
  3. Создание объекта, который будет хранить данные для файла в формате .docx.
    Для этого используется статический метод cs public static WordprocessingDocument Create(string fileName, OpenXmlPackage.DocumentType type) класса WordprocessingDocument. В первом аргументе указываем имя нового файла (вместе с путем), а вот во втором мы должны выбрать тип файла, который должен получиться на выходе:
    a. Document (обычный документ с расширением .docx);
    b. MacroEnabledDocument (файл, содержащий макросы, с расширением .docm);
    c. Template (файл шаблонов word с расширением .dotx);
    d. MacroEnabledTemplate (файл с шаблоном word, содержащий макросы. Имеет расширение .dotm).
    WordprocessingDocument docx = WordprocessingDocument.Create(docxPath, DocumentType.Document);
    
  4. Конвертация данных из бинарного формата в формат OpenXML и их запись в объект типа WordprocessingDocument.
    За выполнение указанной процедуры отвечает статический метод
    public static void Convert(WordDocument doc, WordprocessingDocument docx)
    

    класса Converter, который заодно и записывает получившийся результат в файл.

    Converter.Convert(doc, docx);
    

    В результате у вас должен получиться вот такой код:

    using b2xtranslator.StructuredStorage.Reader;using b2xtranslator.DocFileFormat;using b2xtranslator.OpenXmlLib.WordprocessingML;using b2xtranslator.WordprocessingMLMapping;using static b2xtranslator.OpenXmlLib.OpenXmlPackage;namespace ConverterToXml.Converters{    public class DocToDocx    {        public void ConvertToDocx(string docPath, string docxPath)        {            StructuredStorageReader reader = new StructuredStorageReader(docPath);            WordDocument doc = new WordDocument(reader);            WordprocessingDocument docx = WordprocessingDocument.Create(docxPath, DocumentType.Document);            Converter.Convert(doc, docx);        }    }}
    

    Внимание!
    Если вы используете платформу .Net Core 3 и выше в своем решении, обратите внимание на целевые среды для подключенных проектов b2xtranslator. Так как библиотека была написана довольно давно и не обновляется с 2018 года, по умолчанию она собирается под .Net Core 2.
    Чтобы сменить целевую среду, щелкните правой кнопкой мыши по проекту, выберите пункт Свойства и поменяйте целевую рабочую среду. В противном случае вы можете столкнуться с проблемой невозможности конвертации файлов .doc, содержащих в себе таблицы.
    Я не стал разбираться, почему так происходит, но энтузиастам могу подсказать, что причину стоит искать в 40 строчке файла ~\b2xtranslator\Doc\WordprocessingMLMapping\MainDocumentMapping.cs в момент обработки таблицы.
    Кроме того, рекомендую собирать все проекты и само решение под 64-битную платформу во избежание всяких непонятных ошибок.



    Сохранение результата в поток байтов


    Так как моей целью при использовании данного решения была конвертация .doc в .xml, а не в .docx, предлагаю вовсе не сохранять промежуточный OpenXML файл, а записать его в виде потока байтов. К сожалению, b2xtranslator не предоставляет нам подходящих методов, но это довольно легко исправить:
    В абстрактном классе OpenXmlPackage (см. ~\b2xtranslator\Common\OpenXmlLib\OpenXmlPackage.cs) давайте создадим виртуальный метод:


    public virtual byte[] CloseWithoutSavingFile(){    var writer = new OpenXmlWriter();    MemoryStream stream = new MemoryStream();    writer.Open(stream);    this.WritePackage(writer);    writer.Close();    byte[] docxStreamArray = stream.ToArray();    return docxStreamArray;}
    

    По большому счету, данный метод будет заменять собой метод Close(). Вот его исходный код:


    public virtual void Close(){     // serialize the package on closing    var writer = new OpenXmlWriter();    writer.Open(this.FileName);    this.WritePackage(writer);    writer.Close();}
    

    Скажем спасибо разработчикам библиотеки за то, что не забыли перегрузить метод Open(), который может принимать или имя файла, или поток байтов. Однако, библиотечный метод Close(), который как раз и отвечает за запись результата в файл, вызывается в методе Dispose() в классе OpenXmlPackage. Чтобы ничего лишнего не поломать и не заморачиваться с архитектурой фабрик (тем более в чужом проекте), я предлагаю просто закомментировать код внутри метода Dispose() и вызвать метод CloseWithoutSavingFile(), но уже внутри нашего метода после вызова Converter.Convert(doc, docx).
    Для сохранения результата конвертации вызываем вместо docx.Close() метод docx.CloseWithoutSavingFile():


    public MemoryStream ConvertToDocxMemoryStream(Stream stream){    StructuredStorageReader reader = new StructuredStorageReader(stream);    WordDocument doc = new WordDocument(reader);    var docx = WordprocessingDocument.Create("docx", DocumentType.Document);    Converter.Convert(doc, docx);    return new MemoryStream(docx.CloseWithoutSavingFile());}
    

    Теперь библиотека b2xtranslator будет возвращать сконвертированный из формата .doc в .docx файл в виде потока байтов. Даже если у вас нет цели получить на выходе .xml, такой метод может оказаться более подходящим для дальнейшей работы с файлами, тем более что стрим всегда можно сохранить в виде файла там, где вам надо.
    Для тех, кому все-таки очень хочется получить на выходе .xml документ, еще и с сохраненной структурой, предлагаю дойти до кухни, сварить кофе покрепче, добавить в него рюмку коньяка и приготовиться к приключению на 20 минут.


    Конвертация .doc в .xml



    Теперь, когда, казалось бы, можно воспользоваться классом-конвертором DocxToXml, работа которого была описана вот в этой статье, нас поджидает сюрприз, связанный с особенностями работы b2xtranslator.
    Давайте посмотрим на результат работы библиотеки повнимательнее и сравним с оригинальным .docx файлом, из которого был экспортирован .doc файл для конвертации. Для этого достаточно изменить расширение сравниваемых файлов с .docx на .zip. Вот отличия, которые мы увидим, заглянув внутрь архивов:


    1. В результате конвертации в новом .docx файле (справа) отсутствуют папки customXml и docProps.
    2. Внутри папки word, мы также найдем определенные отличия, перечислять которые я, конечно же, не буду:
    3. Естественно, что и метаданные, по которым осуществляется навигация внутри документа, также отличаются. Например, на представленном скрине и далее оригинальный .docx слева, сгенерированный b2xtranslator cправа.

      Налицо явное отличие в атрибутах тега w:document, но этим отличия не заканчиваются. Всю "мощь" библиотеки мы ощутим, когда захотим обработать списки и при этом:
      a. Сохранить их нумерацию
      b. Не потерять структуру вложенности
      c. Отделить один список от другого

    Давайте сравним файлы document.xml для вот этого списка:


    1.1 Первый.Первый1.2 Первый.Второй1.2.1   Первый.Второй.Первый1.2.2   Первый.Второй.ВторойКакая-то строчка 1.2.3   Первый.Второй.Третий2.  Второй2.1 Второй.Первый
    

    Вот так будет выглядеть .xml для первого элемента списка.


    -Во-первых, мы видим, что сама структура документов несколько отличается (например, точка внутри строк рассматривается как отдельный элемент, что, как оказалось, совсем не страшно).
    -Во-вторых, у тегов остался только один атрибут (w:rsidR), а вот w:rsidR, w14:textId, w:rsidRDefault, w:paraId и w:rsidP пропали. Все эти особенности приводят к тому, что наш класс-конвертер DocxToXml(про него подробно можно почитать здесь) подавится и поднимет лапки вверх с ошибкой NullReferenceException, что указывает на отсутствие индексирования параграфов внутри документа.

    Вместе с тем, если мы попытаемся такой файл отрыть в Word, то увидим, что все хорошо отображается, а таблицы и списки покоятся на своих местах! Магия!
    В общем, когда в поисках решения я потратил N часов на чтение документации, мои красные от дебагера глаза омылись горькими слезами, а один лишь запах кофе стремился показать коллегам мой дневной рацион, решение было найдено!
    Исходя из документации к формату doc и алгоритмов работы b2xtranslator, можно сделать вывод, что исторически в бинарных офисных текстовых документах отсутствовала индексация по параграфам*. Возникает задача расставить необходимые теги в нужных местах.
    За индекс параграфа отвечает атрибут тега paraId, о чем прямо написано здесь. Данный атрибут относится к пространству имен w14, о чем можно догадаться при изучении document.xml из архива .docx. В принципе, на скринах выше вы это тоже видите. Объявление пространства имен в .xml выглядит так:


    xmlns:wp14="http://personeltest.ru/away/schemas.microsoft.com/office/word/2010/wordprocessingDrawing"
    

    Теперь давайте заставим b2xtranslator добавлять это пространство имен и идентификатор каждому параграфу. Для этого в файле ~\b2xtranslator\Common\OpenXmlLib\ContentTypes.cs после 113 строки добавим вот эту строчку:


    public const string WordprocessingML2010 = "http://schemas.microsoft.com/office/word/2010/wordml";
    

    Кстати, если посмотрите на комментарии в коде, то увидите, что в этом блоке как раз располагаются поддерживаемые пространства имен для вордовых документов:

    Далее наша задача заставить библиотеку вставлять в начало файла ссылку на данное пространство имен. Для этого в файле ~\b2xtranslator\Doc\WordprocessingMLMapping\MainDocumentMapping.cs в 24 строке вставим код:


    this._writer.WriteAttributeString("xmlns", "w14", null, OpenXmlNamespaces.WordprocessingML2010);
    

    Разработчики библиотеки также позаботились о документации:


    Теперь дело за малым заставить b2xtranslator индексировать параграфы. В качестве индексов предлагаю использовать рандомно сгенерированные GUID может быть, это несколько тяжеловато, но зато надежно!

    Переходим в файл ~\b2xtranslator\Doc\WordprocessingMLMapping\DocumentMapping.cs и в 504 и 505 строки вставляем вот этот код:


    this._writer.WriteAttributeString("w14", "paraId", OpenXmlNamespaces.WordprocessingML2010, Guid.NewGuid().ToString());            this._writer.WriteAttributeString("w14", "textId", OpenXmlNamespaces.WordprocessingML2010, "77777777");
    

    Что касается второй строчки, в которой мы добавляем каждому тегу параграфа атрибут w14:textId = "77777777", то тут можно лишь сказать, что без этого атрибута ничего работать не будет. Для пытливых умов вот ссылка на документацию.
    Если серьезно, то, как я понимаю, атрибут используется, когда текст разделен на разные блоки, внутри которых происходит индексация тегов, которые могут иметь одинаковый Id внутри одного документа. Видимо, для этих случаев используется дополнительная индексация текстовых блоков. Однако, так как мы используем GUID, который в несколько раз больше индексов, используемых в вордовских документах по умолчанию, то генерацией отдельных индексов для текстовых блоков можно и пренебречь.


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


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


    Наконец, бонус для тех, кто хочет разобраться, что значат все эти бесконечные теги и их атрибуты в документах .docx и как они мапаются на бинарный .doc: советую заглянуть в файл ~\b2xtranslator\Doc\DocFileFormat\CharacterProperties.cs, а также посмотреть спецификацию для docx и doc.

Подробнее..

Получится ли сэкономить, отказавшись от Microsoft Office?

16.04.2021 14:07:36 | Автор: admin


Так или иначе, свободное программное обеспечение всегда конкурирует с проприетарным, явно или скрыто. Даже если сами авторы свободных программ не противопоставляют их закрытому софту, конкуренция идет в головах пользователей, которые выбирают платные программы или их бесплатные аналоги. Не один десяток лет с переменным успехом идут горячие баталии между сторонниками открытого и закрытого программного обеспечения: Linux против Windows, Gimp против Photoshop и, конечно же, OpenOffice/Libreoffice против Microsoft Office.

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

Немного истории


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

OpenOffice имеет древнего предшественника StarOffice. Он был выпущен в 1985 году компанией Star Division, основанной немецким подростком Марко Беррисом, когда ему было всего 16 лет. Стив Джобс и Билл Гейтс были гораздо старше, когда они основали свои империи! Естественно, силы были неравны, потому что в 1990 году Microsoft была уже крупной и успешной компанией. Сразу после выпуска Windows 3.0 одна только недельная прибыль составила больше 100 000 долларов. Глобально конкурировать с гигантом Билла Гейтса было нереально, но в 1997 году на своей родине продукт Марко завоевал четверть рынка офисного программного обеспечения за счет меньшей цены (Word и Excel тогда стоили примерно по 50 долларов).

Забавный факт: до того, как офисный пакет Microsoft Word стал доминирующим на рынке, в конце 80-х и начале 90-х самым популярным был WordPerfect, и цена на него была около 150 долларов. Конкуренция положительно сказывается на ценах, пока какая-нибудь компания не добивается монополии.

На волне успеха, в 1998 году, Марк делает офисный пакет бесплатным для личных целей, а через год продает свою компанию Sun Microsystems, которая в свою очередь делает StarOffice бесплатным, но уже и для коммерческого использования. В 2000 году был открыт исходный код офисного пакета, и началась история проекта OpenOffice.org. В 2010 году Sun Microsystems была поглощена Oracle, а немного позднее из-за разногласий в политике управления проект разделился на OpenOffice, который берет под крыло фонд Apache, и LibreOffice, развиваемый The Document Foundation. Различия в этих пакетах не настолько велики, как между ними обоими и Microsoft, поэтому дальше речь пойдет преимущественно про OpenOffice.

Microsoft всегда славилась агрессивной политикой продвижения своих продуктов. Огромное количество денег позволяло сильно демпинговать на рынке программного обеспечения и разорять конкурентов. Кроме того, софтверный гигант никогда не стеснялся грязной игры, их торговые представители щедро раздавали взятки и откаты чиновникам, которые принимали решения о внедрении программных продуктов в частных компаниях и государственных организациях. Ответственные лица закупали программные продукты у Microsoft со скидками до 30%, а государству продавали уже по полной цене, прикарманивая разницу. Ходят слухи, что в МТС в 2012 году не просто так стали рекламировать Windows Phone вместо iPhone. В 2013 году разразился скандал: Microsoft взятками продвигала свое программное обеспечение в России, Пакистане, Китае и странах Восточной Европы. До суда дело снова не дошло, но шум был поднят изрядный. Подавляющее большинство подобных нарушений не получается довести до суда, потому что богатая компания успевает обрубить хвосты и подчистить за собой грязь, но в 2019 году ей пришлось выплатить 25 миллионов долларов за досудебное урегулирование дела о коррупции.

Понятно, что фонды, поддерживающие разработку свободного программного обеспечения, не могут позволить себе подобную политику продвижения, как по этическим соображениям, так и финансово. Потому им невероятно сложно выдерживать конкуренцию с софтверным гигантом. Фактически благодаря стараниям Microsoft пакет Microsoft Office стал стандартом для делопроизводства во всем мире. Судебные иски никак не изменили этот факт, а выплачиваемые штрафы и прочие судебные расходы пренебрежимо малы по сравнению с прибылью компании.

Проблемы совместимости, постепенная эволюция


Исходя из сказанного выше, становится понятно, что, если программный пакет позиционируется как конкурент Microsoft Office, ему, волей-неволей, придется обеспечивать совместимость с документами Microsoft, иначе им никто не будет пользоваться. Но в пакете Microsoft Office используется проприетарные форматы файлов. Вплоть до 2008 года, когда под давлением ISO Microsoft была вынуждена открыть форматы своих файлов на условиях FRAND, сторонние офисные программы были вынуждены обеспечивать совместимость с файлами Microsoft Office с помощью реверс инжиниринга. Естественно, это не могло обеспечить полной совместимости и сильно мешало распространению альтернатив. Более того, в опубликованных спецификациях есть много белых пятен, из-за чего даже сейчас все еще нет полной совместимости между Microsoft Office, Libreoffice и OpenOffice.

Кроме того, форматы файлов Microsoft Office очень долго несли на себе бремя обратной совместимости со старыми версиями, которые были чрезвычайно сложными. Открытая документация только на один формат Excel 97-2003 содержит несколько сотен страниц! Из-за этого казалось, что файлы намеренно запутаны и созданы сумасшедшими программистами. Но это не совсем так. Форматы файлов настолько сложны для того, чтобы можно было обеспечить быструю работу с ними на очень старых и медленных компьютерах, даже при большом объеме данных. Ранние версии Microsoft Office были рассчитаны на работу машин с процессорами Intel 386 и всего 1 Мб оперативной памяти, а большие документы приходилось редактировать уже тогда. Потому документ был чем-то вроде контейнера с внутренней файловой системой, очень похожей на Microsoft-DOS, с таблицей разделов, секторами и файлами, содержащими отдельные части документа. Это было необходимо для обеспечения быстрой работы с данными. Например, в пакете версии 95-97 была команда Fast save, которая позволяя делать инкрементальные сохранения, не перезаписывая весь документ, а добавляя изменения в конец файла.

Еще одна причина сложности формата поддержка составных документов, которая позволяет встраивать в один документ часть из другого документа как объекта OLE, например, таблицу из Excel в документ Word, которая будет обновляться, когда изменится файл таблицы.

В работе над пакетом Microsoft Office, чтобы он имел все современные возможности входящих в него программ, были потрачены тысячи человеко-часов компанией, которая имела достаточно средств, чтобы нанимать высококвалифицированных инженеров. Увы, но команды, разрабатывающие OpenOffice и Libreoffice, намного меньше. При том, что нельзя реализовать только 20% самого популярного функционала и надеяться, что 80% пользователей будут довольны вашей программой. Хороший пример Google Docs, при работе с которым иногда очень не хватает возможностей Microsoft Word, не реализованных в облачном редакторе. Потому перед разработчиками конкурентов Microsoft Office стоит практически непосильная задача, с которой они почти справляются. В течении многих лет постепенно были решены многие проблемы совместимости, но перечислим те, которые еще остались и могут представлять проблемы (в основном, на примере OpenOffice):

  • Очень долгое время была проблема с графиками и диаграммами, созданными в программе Excel. Если файл Excel открывался в Calc, то часть графиков слетала, а если добавить графики программой Calc, то наоборот они слетали при открытии файла в Экселе. Эта проблема не решена полностью до сих пор. Например: круговая диаграмма и диаграмма пирог из Excel (Calc преобразует в 2D-столбцы), невозможность повернуть ось, в частности, чтобы Y внизу была положительной, отсутствие бухгалтерского формата с символом валюты у левого края ячейки.
  • Если в компании практикуется совместное редактирование документов, которые лежат на общем сервере, то перейти на OpenOffice будет сложно или невозможно. Документ, открытый по сети одним пользователем, будет доступен другим только для чтения, а кнопка Редактировать документ, скорее всего, создаст локальную копию.
  • В Calc можно обеспечить совместное редактирование, хоть и с некоторыми ограничениями (нельзя удалять и добавлять строки и столбцы). Для решения этой проблемы во Writer надо создать специальный файл типа Составной документ, который придется экспортировать, чтобы потом его мог открыть пользователь Microsoft Office.
  • Работа со связанными документами в OpenOffice и Microsoft Office реализована по разному, и потому файлы Microsoft Office, открытые в OpenOffice, потеряют все связи. В Microsoft Office можно вставить документ из другого файла с сохранением связи между ними. При открытии такого документа в OpenOffice связь будет потеряна. Например, диаграмма или таблица из Excel, вставленная в текстовый документ, останется, но если отредактировать документ с таблицей, в текстовом файле изменения уже не отобразятся.
  • До сих пор не решена проблема с паролями в документах электронных таблиц. Calc сбрасывает пароль, заданный в файле Excel и наоборот.
  • Ограниченное количество словарей для проверки грамматики и орфографии, навскидку, нет финского языка. Нельзя сказать, что он очень распространенный, но Microsoft Office предоставляет гораздо большее разнообразие в этом вопросе.
  • Не реализован корректный импорт формата RTF. Он все менее популярен и его поддержка прекратилась аж в 2008 году, но если в компании имеются такие документы, работать с ними будет сложно.
  • В пакете OpenOffice до сих пор не работают макросы, написанные на VBA для Microsoft Office.
  • Слабая интеграция с облаками. В OpenOffice вообще нет возможности прозрачно работать с документами в облаке, в Libreoffice есть опция Сохранить в облако, можно настроить несколько сервисов, но обеспечить совместную работу с облачным документом, как это реализовано в Office 365 или Google Docs, эти программные пакеты не позволяют.
  • Корпоративные пользователи будут недовольны тем, что отсутствует интеграция с Outlook.
  • Из небольших и странных несовместимостей: OpenOffice не поддерживает буквицы, а Impress (программа для создания презентаций), не понимает сложные градиенты и анимации PowerPoint, их надо упрощать перед тем, как открыть презентацию OpenOffice. Кроме того, отсутствуют некоторые инструменты чисто отечественной специфики, такие, как автоматическое создание отчетности для налоговой службы и пенсионного фонда. Имеется плохая совместимость с программой 1С. До сих пор есть шероховатости при импорте и экспорте математических формул, а некоторые файлы Microsoft Office слишком долго открываются. Отдельные виды теней и стрелок не поддерживаются в OpenOffice и могут слетать при открытии документа.

Самое любопытное то, что, если создавать текстовые документы по всем правилам, не пытаясь превратить его в чертеж или редактировать в фотошопе заголовки во все цвета радуги, избегать дилетантского форматирования пробелами, пользоваться стилями абзацев и шаблонами, несовместимость документов стремится к нулю. Но такой уровень работы с Офисом (любым) встречается очень редко. Обычно люди используют текстовый процессор на пару процентов от его возможностей, как чуть более продвинутый Блокнот.

Все это создает сложности при миграции с Microsoft Office на открытый и бесплатный софт, несколько примеров ниже это подтверждают.

Самые крупные попытки миграции


В далеком 2003 году власти Мюнхена решили начать проект LiMux. Так назывался дистрибутив на основе Ubuntu с небольшими модификациями для установки на компьютеры муниципальных чиновников. В 2004 году Microsoft прекратила поддержку Windows NT, и надо было решать, как быть: переходить на открытое и бесплатное программное обеспечение или на новую систему от Microsoft и обновлять остальные программы. В 2004 году власти объявили о намерении перейти на открытое программное обеспечение. Фактический он начался в 2005 году и был почти полностью завершен в 2012-2013 годах.

Недолго музыка играла. В 2014 году было сказано, что переход на Linux был катастрофой, и все компьютеры будут обратно переведены на Windows и Microsoft Office. Проблем было несколько.

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

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

В-третьих, оказалось, что поддерживать парк машин на Linux дороже, потому что пришлось нанять новых программистов и системных администраторов, которые просили больше денег, чем специалисты по обслуживанию Windows, а ко всему прочему, их оказалось сложнее найти. При этом, все компьютеры так и не были переведены на LiMux, часть из них продолжала работать на Windows со всеми вытекающими проблемами совместимости.

Обратный переход обошелся казне почти в 90 миллионов евро и продолжался до 2020 года.
Забавнее всего, что за год сменилась администрация города, были выбраны новые политики, и они снова решили попытаться перевести городскую администрацию на открытый софт. Далеко не всем нравится подобная чехарда, и критики называют это решение политическим, а не рациональным.

Любителям теорий заговора понравится обоснование перехода обратно на Windows. В 2014 году мэром Мюнхена стал Дитер Рейтер, открыто симпатизирующий Microsoft, а в 2016 году исследование эффективности работы муниципальных служб было заказано компании Accenture, которая сотрудничает с Microsoft. Учитывая, что последняя не раз была поймана на взятках, подозрения имеют под собой все основания.

Другая неудачная попытка соскочить с иглы Windows тоже была предпринята в Германии. В 2004 году МИД объявил, что собирается отказаться от продукции Microsoft в пользу открытого ПО. В 2008 году они бодро отрапортовали, что все идет хорошо, экономится много денег, а проблемы совсем незначительные, и через год все закончится. Но в 2011 году Министерство заявило, что отказывается от открытого ПО и возвращается на Windows. Причины были предсказуемы: возникало слишком много проблем в обмене документами с другими ведомствами.

В 2005 году Министерство юстиции Бельгии заявило, что собирается переходить на открытое ПО, а в 2008 половина компьютеров уже работала на SUSE Linux и OpenOffice вместо Microsoft. Основная сложность снова очевидна: проблемы в обмене документами, плюс ко всему не было открытого аналога, который умеет работать с файлами Microsoft Access. Судя по всему, у них это получилось лучше, чем у немцев, потому что другие министерства и администрации городов тоже постепенно переходят на Linux и новостей об громком отказе пока нет, а проблему с Microsoft Access решили переходом на PostgreSQL.

Итоги


Какие выводы можно сделать из всей этой информации? Выгодно ли переходить на открытое ПО и сколько можно сэкономить?

Если говорить о большой компании, где документооборот десятилетиями идет на Microsoft Office, написано множество легаси-кода в макросах, изощренные таблицы с разнообразными диаграммами, а документы разных форматов содержат связи друг на друга, используя OLE, вся отчетность построена на множестве готовых шаблонов, а инфраструктура на Active Directory, переходить на свободное ПО почти невозможно, на грани смерти подобно. Усилия по переучиванию сотрудников, переписыванию шаблонов и макросов, созданию и поддержке новой инфраструктуры несоизмеримы с выгодой отказа от платного ПО.

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

Если работа компании плотно завязана на специфичный софт типа 1С, то им по любому придется оставить несколько машин работающих на Windows с Microsoft Office, чтобы избежать проблем интеграции с этой чудо-системой. Фирмам, чья специфика подразумевает совместную работу с документами, OpenOffice тоже может создать слишком большие сложности, чтобы оправдать переход от платных конкурентов.

Зато молодая и небольшая компания, не создающая изощренных документов, чья работа не связана с интенсивным обменом документами с другими организациями, легко может отказаться от платного ПО и пользоваться одним из открытых Офисов, даже если они останутся на Windows.
Что касается обычных пользователей, то тут проблем вообще нет. Редко кому при обычном домашнем использовании требуются такие возможности, как связанные документы, совместное редактирование и изощренные макросы. А интерфейс, аналогичный старому Офису, с нормальным выпадающим меню, как у всех программ, куда удобнее, чем нелепые панели, которые были введены каким-то врагом дизайна, и предназначены для использования чужаками или хищниками.

***
И что же выбрать, на мой взгляд?

Google Docs!

Шутки шутками, но облачная модель работы с документами существенно привлекательнее, чем традиционная оффлайновая работа. Речь идет не только о Google Docs, но и о Office 365, Dropbox и множестве других сервисов облачной работы с документами. В них из коробки имеется коллективная работа над документами, там просто разграничивать права, делиться ими с другими пользователями или организациями достаточно послать ссылку, не скачивая или пересылая файлы. Сохранность документа не зависит от капризов софта или неполадок железа. Встроенная версионность куда удобнее, чем Undo/Redo, история которых теряется при закрытии локального документа. Работа с документом или его просмотр доступны практически с любого устройства, где есть браузер, от десктопа до смартфона. Эта статья, как и все остальные, написана в Google Docs. И пусть мне иногда не хватает возможностей полноценного Офиса, это компенсируют другие удобства. А в случае чего, подписка на облачные сервисы стоит на порядок дешевле, чем когда-то приходилось тратить на однократную покупку лицензионного софта.
И на этом поле открытое ПО пока проигрывает большим компаниям и платным решениям.



Наши серверы можно использовать для установки любой панели управления.

Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru