ImgApp.js
// Generated by CoffeeScript 1.12.7
/**
* Title ImgApp<br>
* Author Tanaike<br>
* GitHub https://github.com/tanaikech/ImgApp<br>
*<br>
* Retrieve image size (width and height) from file blob.<br>
*<br>
* <h3>usage</h3>
* var blob = DriveApp.getFileById(fileId).getBlob(); // Please retrieve file blob like this.<br>
* var res = ImgApp.getSize(blob);<br>
*<br>
* @param {Object} blob File blob: png, jpg, gif and bmp
* @return {Object} JSON object {identification: [png, jpg, gif and bmp], width: [pixel], height: [pixel], filesize: [bytes]}
*/
function getSize(blob) {
return new ImgApp().GetSize(blob);
}
/**
* Resize image from inputted width. When the source file is Google Docs (spreadsheet, document and slide),<br>
* its thumbnail is created and it is resized.<br>
* In order to use this method, please enable Drive API at Google API console.<br>
*<br>
* <h3>usage</h3>
* var res = ImgApp.doResize(fileId, width);<br>
* DriveApp.createFile(res.blob.setName("filename")); // If you want to save as a file, please use this.<br>
*<br>
* @param {string} fileId File ID on Google Drive
* @param {integer} width Resized width you want
* @return {Object} JSON object {blob: [blob], originalwidth: ###, originalheight: ###, resizedwidth: ###, resizedheight: ###}
*/
function doResize(fileId, width) {
return new ImgApp().DoResize(fileId, width);
}
/**
* Update a thumbnail of a file using an image.<br>
* There are some limitations for updating thumbnail.<br>
* Please confirm the detail information at https://developers.google.com/drive/v3/web/file#uploading_thumbnails.<br>
* - If Drive can generate a thumbnail from the file, then it will use the generated one and ignore any you may have uploaded.<br>
* - If it cannot generate a thumbnail, it will always use yours if you provided one.<br>
*<br>
* <h3>usage</h3>
* ImgApp.updateThumbnail(imgFileId, srcFileId);<br>
*<br>
* @param {string} imgFileId File ID of new thumbnail image on Google Drive
* @param {string} srcFileId File ID of file, which is updated thumbnail, on Google Drive
* @return {Object} JSON object id,mimeType,name,thumbnailVersion,thumbnailLink
*/
function updateThumbnail(imgFileId, srcFileId) {
return new ImgApp().UpdateThumbnail(imgFileId, srcFileId);
}
/**
* This method is for editing images. In the current stage, the image can be cropped. And several images can be merged as an image.<br>
* <br>
* <h3>usage</h3>
* ImgApp.editImage(object);<br>
* <br>
* @param {Object} object Object for using this method.
* @return {Object} Blob of result image.
*/
function editImage(object) {
return new ImgApp().EditImage(object);
}
;
(function(r) {
var ImgApp;
ImgApp = (function() {
var GetImage, GetResizedSize, byte2hex, byte2hexNum, byte2num, cropImage, fetch, getFormat, getImageFromSlide, getInfBMP, getInfGIF, getInfJPG, getInfPNG, hex2num, mergeImages, pixelToEmu, pixelToPt, ptToEmu, ptToPixel;
ImgApp.name = "ImgApp";
function ImgApp(blob) {
this.bytear = [];
}
ImgApp.prototype.EditImage = function(obj_) {
if (obj_.hasOwnProperty("blob") && obj_.hasOwnProperty("crop") && obj_.blob.toString() === "Blob" && typeof obj_.crop === "object") {
return cropImage.call(this, obj_);
} else if (obj_.hasOwnProperty("merge") && Array.isArray(obj_.merge) && Array.isArray(obj_.merge[0])) {
return mergeImages.call(this, obj_);
} else {
throw new Error("Wrong object. Please confirm it again.");
}
};
mergeImages = function(obj_) {
var canvas, croppedBlob, object, presentationId, rs, slide, slides;
canvas = obj_.merge.reduce((function(_this) {
return function(o, r) {
var ar, mHeight, mWidth;
mWidth = 0;
mHeight = 0;
ar = [];
r.forEach(function(c) {
var temp;
if (c && c.toString() === "Blob") {
temp = _this.GetSize(c);
if (temp.width * temp.height > 25000000) {
throw new Error("The image size is too large. Please check https://gist.github.com/tanaikech/9414d22de2ff30216269ca7be4bce462");
}
ar.push({
blob: c,
left: mWidth,
top: o.maxHeight,
width: temp.width,
height: temp.height
});
mWidth += temp.width;
if (mHeight < temp.height) {
return mHeight = temp.height;
}
} else {
return ar.push(null);
}
});
o.images.push(ar);
if (o.maxWidth < mWidth) {
o.maxWidth = mWidth;
}
o.maxHeight += mHeight;
return o;
};
})(this), {
maxWidth: 0,
maxHeight: 0,
images: []
});
object = {
title: "tempForImaApp",
width: {
unit: "pixel",
size: canvas.maxWidth
},
height: {
unit: "pixel",
size: canvas.maxHeight
}
};
presentationId = (new SlidesAppp("create")).createNewSlidesWithPageSize(object);
slides = SlidesApp.openById(presentationId);
slide = slides.getSlides()[0];
canvas.images.forEach(function(r) {
r.forEach(function(c) {
if (c) {
slide.insertImage(c.blob, pixelToPt.call(this, c.left), pixelToPt.call(this, c.top), pixelToPt.call(this, c.width), pixelToPt.call(this, c.height));
}
});
});
slides.saveAndClose();
rs = canvas.maxWidth > 1600 ? 1600 : canvas.maxWidth;
if (obj_.hasOwnProperty("outputWidth") && obj_.outputWidth > 0 && obj_.outputWidth <= 1600) {
rs = obj_.outputWidth;
}
croppedBlob = getImageFromSlide.call(this, presentationId, slide.getObjectId(), rs, obj_.outputFilename);
DriveApp.getFileById(presentationId).setTrashed(true);
return croppedBlob;
};
cropImage = function(obj_) {
var _, b, croppedBlob, height, l, object, presentationId, ref, rs, setHeight, setL, setT, setWidth, size, slide, slides, t, unit, width;
unit = obj_.hasOwnProperty("unit") && typeof obj_.unit === "string" ? obj_.unit : "pixel";
size = this.GetSize(obj_.blob);
if (size.width * size.height > 25000000) {
throw new Error("The image size is too large. Please check https://gist.github.com/tanaikech/9414d22de2ff30216269ca7be4bce462");
}
width = obj_.unit === "point" ? pixelToPt.call(this, size.width) : size.width;
height = obj_.unit === "point" ? pixelToPt.call(this, size.height) : size.height;
ref = ["t", "b", "l", "r"].map(function(k) {
if (obj_.crop.hasOwnProperty(k)) {
return Number(obj_.crop[k]);
} else {
return 0;
}
}), t = ref[0], b = ref[1], l = ref[2], r = ref[3];
object = {
title: "tempForImaApp",
width: {
unit: obj_.unit,
size: width - r - l
},
height: {
unit: obj_.unit,
size: height - b - t
}
};
presentationId = (new SlidesAppp("create")).createNewSlidesWithPageSize(object);
slides = SlidesApp.openById(presentationId);
slide = slides.getSlides()[0];
setWidth = obj_.unit === "pixel" ? pixelToPt.call(this, width) : width;
setHeight = obj_.unit === "pixel" ? pixelToPt.call(this, height) : height;
setL = obj_.unit === "pixel" ? pixelToPt.call(this, l) : l;
setT = obj_.unit === "pixel" ? pixelToPt.call(this, t) : t;
_ = slide.insertImage(obj_.blob, -setL, -setT, setWidth, setHeight);
slides.saveAndClose();
rs = size.width - (obj_.unit === "point" ? ptToPixel.call(this, l) : l) - (obj_.unit === "point" ? ptToPixel.call(this, r) : r);
if (obj_.hasOwnProperty("outputWidth") && obj_.outputWidth > 0 && obj_.outputWidth <= 1600) {
rs = obj_.unit === "point" ? ptToPixel.call(this, obj_.outputWidth) : obj_.outputWidth;
}
croppedBlob = getImageFromSlide.call(this, presentationId, slide.getObjectId(), rs, obj_.blob.getName());
DriveApp.getFileById(presentationId).setTrashed(true);
return croppedBlob;
};
ptToPixel = function(pt_) {
return pt_ * 1.33333;
};
ptToEmu = function(pt_) {
return pt_ * 12700;
};
pixelToPt = function(pixel_) {
return pixel_ * 0.75;
};
pixelToEmu = function(pixel_) {
return pixel_ * 0.75 * 12700;
};
getImageFromSlide = function(presentationId, slideId, rs, filename) {
var croppedBlob, e, er, resObj, url;
croppedBlob = null;
try {
resObj = Slides.Presentations.Pages.getThumbnail(presentationId, slideId, {
"thumbnailProperties.thumbnailSize": "LARGE",
"thumbnailProperties.mimeType": "PNG"
});
try {
url = resObj.contentUrl.replace(/=s\d+/, "=s" + Math.ceil(rs));
croppedBlob = UrlFetchApp.fetch(url).getBlob();
} catch (error) {
er = error;
throw new Error(er.message);
}
croppedBlob = croppedBlob.setName(filename || "outputImageFromImgApp.png");
} catch (error) {
e = error;
if (e.message === "Slides is not defined") {
throw new Error("Please enable Slides API at Advanced Google services, and try again.");
} else {
throw new Error(e.message);
}
}
return croppedBlob;
};
ImgApp.prototype.UpdateThumbnail = function(imgFileId_, srcFileId_) {
var boundary, data, fields, headers, img4thumb, metadata, method, mime, payload, url;
if (imgFileId_ == null) {
throw new Error("No image file ID.");
}
if (srcFileId_ == null) {
throw new Error("No source file ID.");
}
img4thumb = DriveApp.getFileById(imgFileId_);
mime = img4thumb.getMimeType();
if (mime !== "image/png" && mime !== "image/gif" && mime !== "image/jpeg") {
throw new Error("The image format (" + mime + ") cannot be used for thumbnail.");
}
metadata = {
contentHints: {
thumbnail: {
image: Utilities.base64EncodeWebSafe(img4thumb.getBlob().getBytes()),
mimeType: mime
}
}
};
fields = "id,mimeType,name,thumbnailVersion,thumbnailLink";
url = "https://www.googleapis.com/upload/drive/v3/files/" + srcFileId_ + "?uploadType=multipart&supportsAllDrives=true&fields=" + encodeURIComponent(fields);
boundary = "xxxxxxxxxx";
data = "--" + boundary + "\r\n";
data += "Content-Disposition: form-data; name=\"metadata\"\r\n";
data += "Content-Type: application/json; charset=UTF-8\r\n\r\n";
data += JSON.stringify(metadata) + "\r\n";
data += "--" + boundary + "--\r\n";
payload = Utilities.newBlob(data).getBytes();
headers = {
"Authorization": "Bearer " + ScriptApp.getOAuthToken(),
"Content-Type": "multipart/related; boundary=" + boundary
};
method = "patch";
return fetch.call(this, url, method, payload, headers);
};
ImgApp.prototype.DoResize = function(fileId, width) {
var blob, e, headers, method, mimetype, n, res, resized, rs, thumbUrl, turl, url;
try {
url = "https://www.googleapis.com/drive/v3/files/" + fileId + "?supportsAllDrives=true&fields=thumbnailLink%2CmimeType";
method = "get";
headers = {
"Authorization": "Bearer " + ScriptApp.getOAuthToken()
};
res = fetch.call(this, url, method, null, headers);
thumbUrl = res.thumbnailLink;
mimetype = res.mimeType;
r = thumbUrl.split("=");
} catch (error) {
e = error;
throw new Error("'" + fileId + "' is not compatible file. Error message is " + JSON.stringify(e));
}
width = width > 0 ? width : 100;
n = false;
rs = {};
if (~mimetype.indexOf('google-apps') || ~mimetype.indexOf('pdf')) {
n = true;
turl = thumbUrl.replace(r[r.length - 1], "s10000");
rs = GetResizedSize.call(this, GetImage.call(this, turl, "png"), width);
} else if (~mimetype.indexOf('image')) {
rs = GetResizedSize.call(this, DriveApp.getFileById(fileId).getBlob(), width);
} else {
turl = thumbUrl.replace(r[r.length - 1], "s10000");
rs = GetResizedSize.call(this, GetImage.call(this, turl, "png"), width);
}
blob = GetImage.call(this, thumbUrl.replace(r[r.length - 1], "s" + (n ? rs.reheight : rs.rewidth)));
resized = this.GetSize(blob);
return {
blob: blob,
identification: resized.identification,
originalwidth: rs.orgwidth,
originalheight: rs.orgheight,
resizedwidth: resized.width,
resizedheight: resized.height
};
};
GetImage = function(turl) {
return UrlFetchApp.fetch(turl, {
headers: {
Authorization: "Bearer " + ScriptApp.getOAuthToken()
}
}).getBlob();
};
GetResizedSize = function(blob, width) {
var oh, ow, rh, rw, size;
size = this.GetSize(blob);
ow = size.width;
oh = size.height;
if (width > ow) {
rw = ow;
rh = oh;
} else {
rw = width;
rh = Math.ceil(width * oh / ow);
}
return {
orgwidth: ow,
orgheight: oh,
rewidth: rw,
reheight: rh
};
};
ImgApp.prototype.GetSize = function(blob) {
var res;
this.bytear = (function(blob) {
var e;
try {
return blob.getBytes();
} catch (error) {
e = error;
throw new Error("Cannot retrieve file blob.");
}
})(blob);
getFormat.call(this);
switch (this.format) {
case "bmp":
res = getInfBMP.call(this);
break;
case "gif":
res = getInfGIF.call(this);
break;
case "png":
res = getInfPNG.call(this);
break;
case "jpg":
res = getInfJPG.call(this);
break;
default:
res = {
Error: this.format
};
}
return res;
};
getInfBMP = function() {
return {
identification: "BMP",
width: byte2num(this.bytear.slice(18, 22), true),
height: byte2num(this.bytear.slice(22, 26), true),
filesize: this.bytear.length
};
};
getInfGIF = function() {
return {
identification: "GIF",
width: byte2num(this.bytear.slice(6, 8), true),
height: byte2num(this.bytear.slice(8, 10), true),
filesize: this.bytear.length
};
};
getInfPNG = function() {
return {
identification: "PNG",
width: byte2num(this.bytear.slice(16, 20), false),
height: byte2num(this.bytear.slice(20, 24), false),
filesize: this.bytear.length
};
};
getInfJPG = function() {
var i, ma;
i = 0;
while (i < this.bytear.length) {
i += 1;
if ((byte2hexNum.call(this, this.bytear[i])) === "ff") {
i += 1;
ma = byte2hexNum.call(this, this.bytear[i]);
if (ma === "c0" || ma === "c1" || ma === "c2") {
break;
} else {
i += hex2num.call(this, byte2hex.call(this, this.bytear.slice(i + 1, i + 3)));
}
}
}
return {
identification: "JPG",
width: hex2num.call(this, byte2hex.call(this, this.bytear.slice(i + 6, i + 8))),
height: hex2num.call(this, byte2hex.call(this, this.bytear.slice(i + 4, i + 6))),
filesize: this.bytear.length
};
};
getFormat = function() {
var f;
f = (byte2hex.call(this, this.bytear.slice(0, 8))).join("");
this.format = f.slice(0, 16) === "89504e470d0a1a0a" ? "png" : f.slice(0, 4) === "ffd8" ? "jpg" : f.slice(0, 6) === "474946" ? "gif" : f.slice(0, 4) === "424d" ? "bmp" : "Cannot retrieve image size. Now, it can retrive image size from png, jpg, gif and bmp.";
};
byte2hexNum = function(data) {
var conv;
conv = (data < 0 ? data + 256 : data).toString(16);
return (conv.length === 1 ? "0" + conv : conv);
};
byte2hex = function(data) {
return data.map(function(e) {
return (e < 0 ? e + 256 : e).toString(16);
}).map(function(e) {
return (e.length === 1 ? "0" + e : e);
});
};
byte2num = function(data, byteorder) {
var conv;
if (byteorder) {
conv = data.reduceRight(function(ar, e) {
var temp;
temp = (e < 0 ? e + 256 : e).toString(16);
if (temp.length === 1) {
temp = "0" + temp;
}
ar.push(temp);
return ar;
}, []);
} else {
conv = byte2hex.call(this, data);
}
return hex2num.call(this, conv);
};
hex2num = function(data) {
return parseInt(data.join(""), 16);
};
fetch = function(url, method, payload, headers) {
var e, res;
try {
res = UrlFetchApp.fetch(url, {
method: method,
payload: payload,
headers: headers,
muteHttpExceptions: true
});
} catch (error) {
e = error;
throw new Error(e);
}
try {
r = JSON.parse(res.getContentText());
} catch (error) {
e = error;
r = res.getContentText();
}
return r;
};
return ImgApp;
})();
return r.ImgApp = ImgApp;
})(this);