Часть первая (надеюсь, что не последняя)
Долгое время для меня OpenOffice оставался вещью-в-себе. Я знал, что он прекрасно автоматизируется питонами и бейсиками, но, вот, для PHP никак не мог найти подходящего инструмента. Совершенно случайно обнаружил такую интересную возможность OpenOffice: получение доступа к содержимому буфера обмена Windows. Тогда мне очень не хватало возможности писать простые CLI-скрипты, обрабатывающие текст в буфере на языке PHP. Поэтому я решил основательно разобраться, как можно рулить опен-офисом с помощью рнр из-под винды.
Вот, собственно, решение
<?php
// PHP OpenOffice: работа с COM-объектами
$oo = new COM("com.sun.star.ServiceManager");
$clipboard = $oo->CreateInstance(
"com.sun.star.datatransfer.clipboard.SystemClipboard");
$converter = $oo->CreateInstance("com.sun.star.script.Converter");
$contents = $clipboard->getContents();
$flavors = $contents->getTransferDataFlavors();
$result = false;
foreach ($flavors as $mm)
{
$mime = $mm->MimeType;
// echo "$mime\r\n"; // DEBUG
if ($mime=="text/plain;charset=utf-16")
{
$data = $contents->getTransferData($mm);
// "com.sun.star.uno.TypeClass.STRING" ==> 12
$result = $converter->convertToSimpleType($data, 12);
break;
}
}
echo $result;
Как это все работает
Сначала создается компонент служебного менеджера,
"com.sun.star.ServiceManager"
, который нужен для подключения компонентов буфера и конвертера, поскольку, напрямую создать компонент буфера "com.sun.star.datatransfer.clipboard.SystemClipboard"
не получится. Менеждер занимается диспетчеризацией вызовов функций UNO-интерфейса. В результате, в ответ на запросы CreateInstance()
в высшую «инстанцию», получаются полноценные экземпляры нужных нам COM-компонентов.Содержимое буфера извлекается методом
getContents()
. Это содержимое очень хитро устроено, представлено в нескольких различных форматах (на вкус и цвет). Полный набор getTransferDataFlavors()
. В результате имеем составной объект, элементы которого можно перебрать в цикле foreach (..as..)
.Каждый элемент сам по себе тоже не менее хитрый. С помощью свойства
MimeType
определяется тип содержимого. Этот тип содержимого возвращается в виде обычной строки. Нас будет интересовать только "text/plain;charset=utf-16"
.Для получения же самих переносимых данных буфера, понадобится метод
getTransferData()
.И, тут нас ожидает первый облом:
В отличие от
MimeType
, который представляет собой простое текстовое значение, результат метода выдается не строкой, (которую можно было бы потом просто перекодировать функцией iconv()
в нужную кодировку), а, вариантным типом, с которым не так-то просто подружиться в PHP.Вероятнее всего это сделано так, потому что кроме текстового содержимого, в буфере могут лежать, картинки, музыка и прочая мультимедия, и выдавать ее в виде строки не всегда кошерно.
Конверсия
Эту проблему решает специальный компонент-конвертер
"com.sun.star.script.Converter"
, который так же создается менеджером.У конвертера есть метод, преобразующий вариантные значения в простые типы
convertToSimpleType()
, которому нужно скормить сам вариант, и передать «магическую» константу 12 ("com.sun.star.uno.TypeClass.STRING"
), соответствующую обычным строкам.Но, тут — второй облом:
В результате получается строка в кодировке Windows-1251, что может привести к искажению или потере исходных символов (в кодировке Unicode) не вписывающихся в прокрустово ложе виндовой кодовой таблицы.
Дисклеймер
На мой вздляд, решение получилось весьма изящным, однако я ожидаю получить и противоположную реакцию со стороны настоящих гуру программирования, что, типа, опять, мол, в Хабре появляется новое поколение школоло PHP-Быдлокодеров, сидящих в Виндузовой командной строке, и пишущих Хэлловорды для офисной COM-автоматизации чтобы всего-то, прочитать текст из буфера.
В общем-то, место для данного топика должно быть в блоге «Q&A», и его содержание было искусственно раздуто до размера «полновесной» статьи.
К сожалению в интерфейсе Хабра-Песочницы нет возможности указать предпочитаемый блог для публикации поста.
Так же из Read-Only аккаунта нет возможности написать письмо кому-нибудь из Хабраюзеров напрямую, с просьбой запостить вопрос в блог Q&A.
Вот, собственно, сами вопросы:
1. Есть ли еще какие-нибудь альтернативные способы получения доступа к содержимому буфера обмена Windows, аналогичные OpenOffice, без подключения дополнительных php-расширений через COM-автоматизацию, например, какого-нибудь MS-Office или даже Internet Explorer? Статья получилась бы более интересной, если бы читателю было предложено на выбор несколько различных способов решения проблемы, и возможность автоматически получить доступ к содержимому буфера, любым доступным способом, в зависимости от того, какое конкретное дополнительное программное обеспечение в системе было установлено. То есть, обеспечить некую кроссплатформенность (ну, или же «кросс-офисность», если хотите).
2. Ну, читать буфер мы кое-как научились, а, как в этот буфер теперь что-то записать? Сразу вынужден предупредить, что решение по записи в буфер будет выглядеть не столь изящно и прозрачно. По крайненй мере, самостоятельно «навелосипедить» решение этой задачи мне так и не удалось. И, разумеется, в полноценной статье описание обратной операции просто обязано присутствовать. Хотя, еще раз, повторю, что мне самому было в первую очередь нужно именно прочитать содержимое текстового буфера, и кодировка Windows-1251 полностью соответствовала моим аппетитам.
3. Ну, если, с текстовым содержимым буфера, все понятно, то, как быть с графикой? Очень бы хотелось бы получить графическое содержимое буфера, например в виде GD2-объекта, и, еще, чтоб можно было «рисовать» прямо-в-буфере, то есть, иметь возможность синхронизировать содержимое буфера с состоянием GD2-объекта. Помню, как, еще, во времена 98 Windows, один мой друган произвел на меня неизгладимое впечатление, вставив в MsPaint из буфера обмена ФИЛЬМ, скопированный из Медиаплейера в режиме воспроизведения. Я был тогда просто в шоке, когда увидел движущееся изображение на фоне открытого рисунка. В то время я еще плохо понимал, как устроена винда, и мною это воспринималось, как настоящая магия.
P.S.
Статья, конечно же, была бы более полезной, если бы описывала универсальный доступ на чтение-запись к любому типу содержимого, чтобы можно было, например, экспортировать содержимое конкретного типа в файлы соответствующего формата.
Очень надеюсь, найти в комментариях к статье ответы на все эти вопросы, или, хотя бы сами эти вопросы, любезно перенесенные заинтересованными читателями этой статьи в блог «Q&A» в случае, если этому топику так и не удастся покинуть пределы Хабрапесочницы.
Каюсь, ссылок на авторитетные источники вдохновения не дал, надеюсь, в комментариях этот недостаток будет скомпенсирован.