Как получить точную копию изображения из Документа Гугл

Как получить точную копию изображения из Документа Гугл

Замечали, что при попытке копирования изображения из Документа Гугл в другой Документ пападает его упрощенная версия?

Решение “в лоб”

Первое, что приходит в голову, это copy(). Синоним многих операций в Goole Apps Script, при котором происходит непонятно что, т.к. реальное копирование экземпляра не всегда доступно.

var image = DocumentApp.openById('ABC').getBody().getImages()[0].copy();
DocumentApp.openById('XYZ').getBody().insertImage(0, image);

Этот метод хорош, он подойдет для копирования внутри Документа. Но изображения в копии для другого Документа будут упрощенными, а анимация вообще исчезнет, оставив место первому кадру.

Поиск проблемы

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

Исходя из этого представления, метод “в лоб” уже не кажется таким очевидным. Даже больше, он сразу отпадает, как неуместный. проблема найдена: как получить источник, из которого формируется состав Документа?

Получение исходников

Понятно, что никаких исходников от проприетарного удаленного и защищенного законом другого государства сервера никто никогда не получит. Но можно получить сжатую совместимую версию. В таких задачах экспорт - это всё. Получаем zip:

var template =
  'https://docs.google.com/feeds/download/documents/export/Export?id=%s&exportFormat=zip';
var url = Utilities.formatString(template, from);
var file = UrlFetchApp.fetch(url).getBlob();

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

Поиск индекса

Что самое сложное в этой задаче? Как ни странно, это поиск индекса заветного изображения, которое будет копироваться. Т.к. картинки были подвергнуты оптимизации, то для порядка их еще и переименовали в ./images/image1.jpg, ./images/image2.gif, ./images/image3.png и т.д. Причем, если картинка создана на базе существующего изображения из Документа, то нового файла не появляется (следует учитывать при индексе).

Распаковка

var blobs = Utilities.unzip(file);

Порядок изображений

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

var htmlContent = blobs
  .find(function(b) {
    return /^.+?\.html$/.test(b.getName());
  })
  .getDataAsString();

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

var imageTag = htmlContent
  .match(/<img.+?src="images\/image\d+\..{2,4}".+?>/g)[index];

Ну, а далее, дело техники - найти имя из тега и по этому имени еще раз перебрать данный blob.

Результат

Самым эффективным для подобной функции будет возвращение изображения в blob, т.к. другие методы позволяют создавать inlineImages из blob.

Полный код