Что написано пером, или как проверить документы в форматах MS Office

от автора

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



Оговорюсь сразу, что под проверкой в данном случае я понимаю проверку на соответствие полученного результата стандарту выбранного формата, позволяющую с достаточно большой вероятностью утверждать, что документ как минимум откроется в Word или Excel без неприятных сообщений о возникших проблемах с предложением попытаться восстановить поврежденный файл. Независимо от того, насколько серьёзным является разрабатываемое приложение и насколько солидным должен быть набор поддерживаемых типов файлов, в него скорее всего будут входить стандартные форматы Office Open XML, более известные как docx и xlsx, а также их двоичные предшественники, doc и xls. О некоторых механизмах работы с ними я и буду рассказывать.

Прогулки с динозаврами

Хотя Microsoft и выложила на своем сайте официальные спецификации форматов doc и xls, они нередко оказываются скупы и немногословны в своих описаниях. Стоит понимать, что со временем форматы претерпевали существенные изменения (с сохранением обратной совместимости). При этом единый подход использовался не только для хранения документов Word, но и для таблиц Excel и презентаций PowerPoint. Проще говоря, даже при наличии мануалов возможностей почувствовать себя Жоржем Кювье, пытающимся восстановить облик загадочной зверюшки по разрозненным костям, предостаточно. К счастью, существует способ локализовать имеющиеся в документе проблемы без необходимости расхлебывать байтовую кашу в любимом hex-редакторе.

Способ этот заключается в использовании Microsoft Office Binary File Format Validator. Это достаточно простая в использовании утилита командной строки, в комплекте с которой идут три dll-ки (по одной на каждый поддерживаемый формат – doc, xls, ppt). Несмотря на то, что с момента первого официального анонса на сайте так и осталась выложенной бета-версия, инструмент вполне себе работоспособен и справляется со своими задачами. Единственная существенная проблема, с которой я столкнулся за время работы с валидатором – это отсутствие поддержки кириллических имен файлов. Для запуска утилиты достаточно ввести команду

bffvalidator.exe [-l log.xml] filename.ext

где filename.ext — это имя исследуемого файла, а -l log.xml – необязательный параметр, указывающий куда сохранить лог (по умолчанию лог пишется в ту же папку, где лежит проверяемый документ).
Для облегчения жизни и уменьшения количества рутинных действий я использую два сценария работы с валидатором. При проверке отдельного файла удобно пользоваться Far-ом: достаточно завести отдельную папку, например, c:\Temp\Bff, положить туда экзешник валидатора и сопутствующие dll-ли, а потом завести команду через F9-Commands-File Associations:


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

public class FileFormatValidationFailedException : Exception {     public FileFormatValidationFailedException(string msg) : base(msg) { } } public void RunBFFValidator(string filePath) {     string fileName = Path.GetFileName(filePath);     string workingDirectory = Path.GetDirectoryName(filePath);     string startupPath = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);     StageName = String.Format("RUNNING BFFValidator for FILE {0}", fileName);     outputManager.BeginWriteInfoLine(String.Format("Running BFFValidator for saved file '{0}'", fileName));     ProcessStartInfo startInfo = new ProcessStartInfo(Path.Combine(startupPath, "BFFValidator.exe"));     startInfo.Arguments = string.Format("-l bfflog.xml \"{0}\"", Path.Combine(workingDirectory, fileName));     startInfo.WorkingDirectory = workingDirectory;     startInfo.WindowStyle = ProcessWindowStyle.Hidden;     Process validatorProcess = Process.Start(startInfo);     validatorProcess.WaitForExit();     if (validatorProcess.ExitCode != 0) {         using (StreamReader reader = new StreamReader(Path.Combine(workingDirectory, "bfflog.xml"))) {             string logContent = reader.ReadToEnd();             throw new FileFormatValidationFailedException(logContent);         }     } } 

Вторая часть Мерлезонского балета

Ситуация с Office Open XML существенно проще. Если открыть несколько файлов hex-редактором, то в начале данных можно будет увидеть инициалы Фила Каца:


Это означает, что файлы представляют собой переименованный в docx/xlsx zip-архив, который можно открыть и увидеть вполне читаемую структуру. Однако и в этом случае можно не пытаться вручную искать расхождения с документацией из Редмонда, а поручить анализ файла специально предназначенным для этого инструментам. Для этого качаем Open XML SDK 2.5 и устанавливаем его (нам понадобятся OpenXMLSDKV25.msi и OpenXMLSDKToolV25.msi). После этого можно будет создать приложение для проверки файлов на наличие невалидной разметки (понадобится референс на DocumentFormat.OpenXml.dll). Простейший код для анализа документов выглядит следующим образом:

public void RunOpenXmlValidation(string filePath, string openXmlFormatVersion) {     string fileName = Path.GetFileName(filePath);     StageName = String.Format("RUNNING OpenXmlValidation for FILE {0}", fileName);     outputManager.BeginWriteInfoLine(String.Format("Running OpenXmlValidation for saved file '{0}'", fileName));     using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(filePath, false)) {         DocumentFormat.OpenXml.FileFormatVersions formatVersion = DocumentFormat.OpenXml.FileFormatVersions.Office2010;         if (openXmlFormatVersion == "office2007")             formatVersion = DocumentFormat.OpenXml.FileFormatVersions.Office2007;         else if (openXmlFormatVersion == "office2013")             formatVersion = DocumentFormat.OpenXml.FileFormatVersions.Office2013;         OpenXmlValidator validator = new OpenXmlValidator(formatVersion);         var errors = validator.Validate(wordDoc);         StringBuilder builder = new StringBuilder();         foreach (ValidationErrorInfo error in errors) {             string errorMsg = string.Format("{0}: {1}, {2}, {3}", error.ErrorType.ToString(), error.Part.Uri, error.Path.XPath, error.Node.LocalName);             builder.AppendLine(errorMsg);             builder.AppendLine(error.Description);         }         string logContent = builder.ToString();         if (!string.IsNullOrEmpty(logContent))             throw new FileFormatValidationFailedException(logContent);     } } 

And we need to go deeper…

Рассмотренные выше способы валидации стоит рассматривать как попытку быстрого поиска, где именно могут быть проблемы в документе, а не как абсолютную гарантию того, что все в порядке. Вполне реальны ситуации, когда валидатор выдает сообщение об ошибке, а Word или Excel нормально открывает файл и наоборот – не удается открыть прошедший валидацию документ. Поэтому если необходима более надежная проверка, то не обойтись без использования COM. Это требует установленного Microsoft Office, не является thread-safe, требует дополнительных телодвижений для x64, зато позволяет убедиться в соответствие документа требованиям MS-Office и поисследовать его структуру с точки зрения целевой платформы.

Внеклассное чтение

Если понадобится более серьезный анализ файла с углубленным пониманием его структуры, то можно обратиться к документации по OpenXML SDK и непосредственно форматам.
Также при исследовании внутреннего устройства документов может помочь утилита OffVis.
Несколько полезных ссылок по взаимодействию с офисными приложениями: Primary Interop Assemblies (PIAs), Microsoft.Office.Interop.Excel namespace, Microsoft.Office.Interop.Word namespace.

Надеюсь, что использование полезных инструментов от Microsoft сбережет ваше время и нервы. Спасибо за внимание!

ссылка на оригинал статьи http://habrahabr.ru/post/270205/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *