├── .codeclimate.yml ├── DocumentAppp.js ├── ExcelApp.js ├── LICENCE ├── README.md ├── SlidesAppp.js ├── SpreadsheetAppp.js ├── WordApp.js ├── appsscript.json ├── images ├── demo1.png ├── fig1.png ├── fig2.png ├── fig3.png ├── fig4.png └── fig5.png └── mainMethods.js /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | engines: 3 | duplication: 4 | enabled: true 5 | config: 6 | languages: 7 | - ruby 8 | - javascript 9 | - python 10 | - php 11 | eslint: 12 | enabled: true 13 | fixme: 14 | enabled: true 15 | ratings: 16 | paths: 17 | - "**.inc" 18 | - "**.js" 19 | - "**.jsx" 20 | - "**.module" 21 | - "**.php" 22 | - "**.py" 23 | - "**.rb" 24 | exclude_paths: 25 | - "tests/" 26 | - "spec/" 27 | - "**/vendor/" 28 | -------------------------------------------------------------------------------- /DocumentAppp.js: -------------------------------------------------------------------------------- 1 | // --- DocumentAppp (DocumentApp plus) --- 2 | (function(r) { 3 | var DocumentAppp; 4 | DocumentAppp = (function() { 5 | var gToM, putError, putInternalError; 6 | 7 | class DocumentAppp { 8 | constructor(id_) { 9 | this.name = "DocumentAppp"; 10 | if (id_ !== "create") { 11 | if (id_ === "" || DriveApp.getFileById(id_).getMimeType() !== MimeType.GOOGLE_DOCS) { 12 | putError.call(this, "This file ID is not the file ID of Document."); 13 | } 14 | this.obj = { 15 | documentId: id_ 16 | }; 17 | } 18 | this.headers = { 19 | Authorization: `Bearer ${ScriptApp.getOAuthToken()}` 20 | }; 21 | this.mainObj = {}; 22 | } 23 | 24 | // --- begin methods 25 | getTableColumnWidth() { 26 | gToM.call(this); 27 | return new WordApp(this.mainObj.blob).getTableColumnWidth(); 28 | } 29 | 30 | }; 31 | 32 | DocumentAppp.name = "DocumentAppp"; 33 | 34 | // --- end methods 35 | gToM = function() { 36 | var obj, url; 37 | url = `https://www.googleapis.com/drive/v3/files/${this.obj.documentId}/export?mimeType=${MimeType.MICROSOFT_WORD}`; 38 | obj = UrlFetchApp.fetch(url, { 39 | headers: this.headers 40 | }); 41 | if (obj.getResponseCode() !== 200) { 42 | putError.call(this, "Document ID might be not correct. Please check it again."); 43 | } 44 | this.mainObj.blob = obj.getBlob(); 45 | }; 46 | 47 | putError = function(m) { 48 | throw new Error(`${m}`); 49 | }; 50 | 51 | putInternalError = function(m) { 52 | throw new Error(`Internal error: ${m}`); 53 | }; 54 | 55 | return DocumentAppp; 56 | 57 | }).call(this); 58 | return r.DocumentAppp = DocumentAppp; 59 | })(this); 60 | -------------------------------------------------------------------------------- /ExcelApp.js: -------------------------------------------------------------------------------- 1 | // --- ExcelApp --- 2 | (function(r) { 3 | var ExcelApp; 4 | ExcelApp = (function() { 5 | var a1NotationToRowCol, columnToLetter, disassembleExcel, getCommentsAsObject, getImagesAsObject, getNamedFunctionsAsObject, getQuotePrefixCellsAsObject, getSharedStrings, getStyleAsObject, getValuesAsObject, getValuesFromObj, getXmlObj, letterToColumn, parsXLSX, putError, putInternalError, sheetNameToFileName; 6 | 7 | class ExcelApp { 8 | constructor(blob_) { 9 | this.name = "ExcelApp"; 10 | if (!blob_ || blob_.getContentType() !== MimeType.MICROSOFT_EXCEL) { 11 | throw new Error("Please set the blob of data of XLSX format."); 12 | } 13 | this.obj = { 14 | excel: blob_ 15 | }; 16 | this.contentTypes = "[Content_Types].xml"; 17 | this.sharedStrings = "xl/sharedStrings.xml"; 18 | this.workbook = "xl/workbook.xml"; 19 | this.styles = "xl/styles.xml"; 20 | this.mainObj = {}; 21 | parsXLSX.call(this); 22 | } 23 | 24 | // --- begin methods 25 | getSheetByName(sheetName_) { 26 | if (!sheetName_ || sheetName_.toString() === "") { 27 | putError.call(this, "No sheet name."); 28 | } 29 | this.obj.sheetName = sheetName_; 30 | return this; 31 | } 32 | 33 | getAll() { 34 | return this.mainObj.sheetsObj.sheetAr.map(({sheetName}, i) => { 35 | this.obj.sheetName = sheetName; 36 | return { 37 | sheetName: sheetName, 38 | values: getValuesAsObject.call(this), 39 | images: getImagesAsObject.call(this), 40 | comments: getCommentsAsObject.call(this) 41 | }; 42 | }); 43 | } 44 | 45 | getSheets() { 46 | return this.mainObj.sheetsObj.sheetAr.map(({sheetName}, i) => { 47 | return { 48 | index: i, 49 | sheetName: sheetName 50 | }; 51 | }); 52 | } 53 | 54 | getValues() { 55 | return getValuesFromObj.call(this, "value"); 56 | } 57 | 58 | getFormulas() { 59 | return getValuesFromObj.call(this, "formula"); 60 | } 61 | 62 | getImages() { 63 | return getImagesAsObject.call(this); 64 | } 65 | 66 | getComments() { 67 | return getCommentsAsObject.call(this); 68 | } 69 | 70 | getQuotePrefixCells() { 71 | return getQuotePrefixCellsAsObject.call(this); 72 | } 73 | 74 | getNamedFunctions() { 75 | return getNamedFunctionsAsObject.call(this); 76 | } 77 | 78 | }; 79 | 80 | ExcelApp.name = "ExcelApp"; 81 | 82 | // --- end methods 83 | getNamedFunctionsAsObject = function() { 84 | var definedNames, root, xmlObj1; 85 | xmlObj1 = getXmlObj.call(this, "xl/workbook.xml"); 86 | root = xmlObj1.getRootElement(); 87 | definedNames = root.getChild("definedNames", root.getNamespace()).getChildren(); 88 | return definedNames.map((e) => { 89 | return { 90 | definedName: e.getAttribute("name").getValue(), 91 | definedFunction: e.getValue() 92 | }; 93 | }); 94 | }; 95 | 96 | getQuotePrefixCellsAsObject = function() { 97 | var checkFilename, quotePrefix, sr, styler, xmlObj1, xmlObj2; 98 | if (this.mainObj.sheetsObj.sheetsObj.hasOwnProperty(this.obj.sheetName)) { 99 | checkFilename = this.mainObj.sheetsObj.sheetsObj[this.obj.sheetName].sheet; 100 | if (this.mainObj.fileObj.hasOwnProperty(checkFilename)) { 101 | xmlObj1 = getXmlObj.call(this, "xl/styles.xml"); 102 | styler = xmlObj1.getRootElement(); 103 | quotePrefix = styler.getChild("cellXfs", styler.getNamespace()).getChildren().map((e) => { 104 | if (e.getAttribute("quotePrefix")) { 105 | return true; 106 | } else { 107 | return false; 108 | } 109 | }); 110 | xmlObj2 = getXmlObj.call(this, checkFilename); 111 | sr = xmlObj2.getRootElement(); 112 | return sr.getChild("sheetData", sr.getNamespace()).getChildren().reduce((ar, r, i) => { 113 | r.getChildren().forEach((c, j) => { 114 | var v; 115 | r = c.getAttribute("r").getValue(); 116 | v = Number(c.getAttribute("s").getValue()); 117 | if (quotePrefix[v]) { 118 | return ar.push(r); 119 | } 120 | }); 121 | return ar; 122 | }, []); 123 | } 124 | } 125 | }; 126 | 127 | parsXLSX = function() { 128 | disassembleExcel.call(this); 129 | getSharedStrings.call(this); 130 | getStyleAsObject.call(this); 131 | sheetNameToFileName.call(this); 132 | }; 133 | 134 | disassembleExcel = function() { 135 | var blobs; 136 | blobs = Utilities.unzip(this.obj.excel.setContentType(MimeType.ZIP)); 137 | this.mainObj.fileObj = blobs.reduce((o, b) => { 138 | return Object.assign(o, { 139 | [b.getName()]: b 140 | }); 141 | }, {}); 142 | }; 143 | 144 | getSharedStrings = function() { 145 | var rootChildren, xmlObj; 146 | if (this.mainObj.fileObj.hasOwnProperty(this.sharedStrings)) { 147 | xmlObj = getXmlObj.call(this, this.sharedStrings); 148 | rootChildren = xmlObj.getRootElement().getChildren(); 149 | this.mainObj.valueSharedStrings = rootChildren.map((e) => { 150 | var temp; 151 | temp = e.getChild("t", e.getNamespace()); 152 | if (temp) { 153 | return temp.getValue(); 154 | } else { 155 | return ""; 156 | } 157 | }); 158 | } else { 159 | putInternalError.call(this, "This Excel data cannot be analyzed."); 160 | } 161 | }; 162 | 163 | sheetNameToFileName = function() { 164 | var rootChildren, sheets, xmlObj; 165 | if (this.mainObj.fileObj.hasOwnProperty(this.workbook)) { 166 | xmlObj = getXmlObj.call(this, this.workbook); 167 | rootChildren = xmlObj.getRootElement(); 168 | sheets = rootChildren.getChild("sheets", rootChildren.getNamespace()).getChildren(); 169 | return this.mainObj.sheetsObj = sheets.reduce((o, e) => { 170 | var sheetId, sheetName; 171 | sheetId = e.getAttribute("sheetId").getValue(); 172 | sheetName = e.getAttribute("name").getValue(); 173 | Object.assign(o.sheetsObj, { 174 | [sheetName]: { 175 | sheet: `xl/worksheets/sheet${sheetId}.xml`, 176 | rels: `xl/worksheets/_rels/sheet${sheetId}.xml.rels` 177 | } 178 | }); 179 | o.sheetAr.push({ 180 | sheetIndex: sheetId, 181 | sheetName: sheetName 182 | }); 183 | return o; 184 | }, { 185 | sheetsObj: {}, 186 | sheetAr: [] 187 | }); 188 | } else { 189 | return putInternalError.call(this, "This Excel data cannot be analyzed."); 190 | } 191 | }; 192 | 193 | getStyleAsObject = function() { 194 | var cellXfs, cellXfsC, fills, fillsC, fillsObj, fonts, fontsC, fontsObj, numFmts, numFmtsC, numFmtsObj, root, styleObj, xmlObj; 195 | if (this.mainObj.fileObj.hasOwnProperty(this.styles)) { 196 | xmlObj = getXmlObj.call(this, this.styles); 197 | root = xmlObj.getRootElement(); 198 | numFmtsC = root.getChild("numFmts", root.getNamespace()); 199 | if (numFmtsC) { 200 | numFmts = numFmtsC.getChildren(); 201 | numFmtsObj = numFmts.reduce((o, e) => { 202 | return Object.assign(o, { 203 | [e.getAttribute("numFmtId").getValue()]: e.getAttribute("formatCode").getValue() 204 | }); 205 | }, {}); 206 | } 207 | fontsC = root.getChild("fonts", root.getNamespace()); 208 | if (fontsC) { 209 | fonts = fontsC.getChildren(); 210 | fontsObj = fonts.map((e) => { 211 | return e.getChildren().reduce((o, f) => { 212 | return Object.assign(o, { 213 | [f.getName()]: f.getAttributes().reduce((o2, g) => { 214 | return Object.assign(o2, { 215 | [g.getName()]: g.getValue() || true 216 | }); 217 | }, {}) 218 | }); 219 | }, {}); 220 | }); 221 | } 222 | fillsC = root.getChild("fills", root.getNamespace()); 223 | if (fillsC) { 224 | fills = fillsC.getChildren(); 225 | fillsObj = fills.map((e) => { 226 | var c, children, temp; 227 | c = e.getChild("patternFill", e.getNamespace()); 228 | temp = { 229 | patternType: c.getAttribute("patternType").getValue() 230 | }; 231 | children = c.getChildren(); 232 | if (children.length > 0) { 233 | children.forEach((f) => { 234 | return temp[f.getName()] = f.getAttributes().reduce((o, g) => { 235 | return Object.assign(o, { 236 | [g.getName()]: g.getValue() || true 237 | }); 238 | }, {}); 239 | }); 240 | } 241 | return temp; 242 | }); 243 | } 244 | cellXfsC = root.getChild("cellXfs", root.getNamespace()); 245 | if (cellXfsC) { 246 | cellXfs = cellXfsC.getChildren(); 247 | styleObj = cellXfs.map((e) => { 248 | return e.getAttributes().reduce((o, f) => { 249 | var c, n, v; 250 | n = f.getName(); 251 | v = f.getValue(); 252 | switch (n) { 253 | case "numFmtId": 254 | o.numFmt = numFmtsObj ? numFmtsObj[v] || null : null; 255 | break; 256 | case "fontId": 257 | o.font = fontsObj ? fontsObj[v] || null : null; 258 | break; 259 | case "fillId": 260 | o.fill = fillsObj ? fillsObj[v] || null : null; 261 | } 262 | c = e.getChild("alignment", e.getNamespace()); 263 | if (c) { 264 | o.alignment = c.getAttributes().reduce((o2, g) => { 265 | return Object.assign(o2, { 266 | [g.getName()]: g.getValue() || true 267 | }); 268 | }, {}); 269 | } 270 | return o; 271 | }, {}); 272 | }); 273 | return this.mainObj.styleObj = styleObj; 274 | } 275 | } else { 276 | return putInternalError.call(this, "Style file cannot be used."); 277 | } 278 | }; 279 | 280 | getValuesAsObject = function() { 281 | var checkFilename, root, rows, sheetData, xmlObj; 282 | if (this.mainObj.sheetsObj.sheetsObj.hasOwnProperty(this.obj.sheetName)) { 283 | checkFilename = this.mainObj.sheetsObj.sheetsObj[this.obj.sheetName].sheet; 284 | if (this.mainObj.fileObj.hasOwnProperty(checkFilename)) { 285 | xmlObj = getXmlObj.call(this, checkFilename); 286 | root = xmlObj.getRootElement(); 287 | sheetData = root.getChild("sheetData", root.getNamespace()).getChildren(); 288 | rows = []; 289 | sheetData.forEach((s) => { 290 | var cols, rowNum; 291 | cols = []; 292 | rowNum = 0; 293 | s.getChildren().forEach((c) => { 294 | var cObj, fv, fval, ra, sa, ta, tav, temp, tobj, vv, vval; 295 | vval = c.getChild("v", c.getNamespace()); 296 | fval = c.getChild("f", c.getNamespace()); 297 | ra = c.getAttribute("r"); 298 | ta = c.getAttribute("t"); 299 | sa = c.getAttribute("s"); 300 | vv = ""; 301 | fv = ""; 302 | if (ta) { 303 | tav = ta.getValue(); 304 | if (tav === "s") { 305 | if (vval) { 306 | vv = this.mainObj.valueSharedStrings[vval.getValue()]; 307 | } 308 | if (fval) { 309 | fv = fval.getValue(); 310 | } 311 | } else if (tav === "str") { 312 | if (vval) { 313 | vv = vval.getValue(); 314 | } 315 | if (fval) { 316 | fv = fval.getValue(); 317 | } 318 | } else if (tav === "b") { 319 | if (vval) { 320 | vv = vval.getValue() === "1" ? true : false; 321 | } 322 | if (fval) { 323 | fv = fval.getValue(); 324 | } 325 | } else { 326 | if (vval) { 327 | vv = vval.getValue(); 328 | } 329 | if (fval) { 330 | fv = fval.getValue(); 331 | } 332 | } 333 | } else { 334 | if (vval) { 335 | vv = Number(vval.getValue()); 336 | } 337 | if (fval) { 338 | fv = fval.getValue(); 339 | } 340 | } 341 | if (ra) { 342 | temp = ra.getValue(); 343 | cObj = a1NotationToRowCol.call(this, temp); 344 | rowNum = cObj.row; 345 | tobj = {}; 346 | tobj.value = {}; 347 | tobj.range = {}; 348 | tobj.value.value = vv.toString() !== "" ? vv : null; 349 | tobj.value.formula = fv.toString() !== "" ? `=${fv}` : null; 350 | tobj.range.col = cObj.col; 351 | tobj.range.row = cObj.row; 352 | tobj.range.a1Notation = temp; 353 | if (sa) { 354 | tobj.style = this.mainObj.styleObj[sa.getValue()]; 355 | } 356 | return cols[cObj.col - 1] = tobj; 357 | } 358 | }); 359 | return rows[rowNum - 1] = cols; 360 | }); 361 | return rows; 362 | } else { 363 | putInternalError.call(this, "No sheet file."); 364 | } 365 | } else { 366 | putInternalError.call(this, "No sheet name."); 367 | } 368 | }; 369 | 370 | getImagesAsObject = function() { 371 | var drawingFilename, imageObj, rootChildren, xmlObj; 372 | if (!this.mainObj.sheetsObj.sheetsObj.hasOwnProperty(this.obj.sheetName)) { 373 | putError.call(this, "No sheet name."); 374 | } 375 | drawingFilename = this.mainObj.sheetsObj.sheetsObj[this.obj.sheetName].sheet.replace("worksheets", "drawings").replace("sheet", "drawing"); 376 | if (this.mainObj.fileObj.hasOwnProperty(drawingFilename)) { 377 | xmlObj = getXmlObj.call(this, drawingFilename); 378 | rootChildren = xmlObj.getRootElement().getChildren(); 379 | imageObj = rootChildren.map((e) => { 380 | var cNvPr, description, filename, form, formn, n, nvPicPr, nvPicPrn, pic, sp, temp, title; 381 | temp = {}; 382 | n = e.getNamespace(); 383 | form = e.getChild("from", n); 384 | formn = form.getNamespace(); 385 | temp.range = { 386 | col: Number(form.getChild("col", formn).getValue()) + 1, 387 | colOff: Number(form.getChild("colOff", formn).getValue()), 388 | row: Number(form.getChild("row", formn).getValue()) + 1, 389 | rowOff: Number(form.getChild("rowOff", formn).getValue()) 390 | }; 391 | temp.range.a1Notation = (columnToLetter.call(this, temp.range.col)) + temp.range.row; 392 | pic = e.getChild("pic", n); 393 | sp = e.getChild("sp", n); // Drawing 394 | if (pic) { 395 | nvPicPr = pic.getChild("nvPicPr", pic.getNamespace()); 396 | nvPicPrn = nvPicPr.getNamespace(); 397 | cNvPr = nvPicPr.getChild("cNvPr", nvPicPrn); 398 | filename = "xl/media/" + cNvPr.getAttribute("name").getValue(); 399 | temp.image = {}; 400 | description = cNvPr.getAttribute("descr"); 401 | if (description) { 402 | temp.image.description = description.getValue(); 403 | } 404 | title = cNvPr.getAttribute("title"); 405 | if (title) { 406 | temp.image.title = title.getValue(); 407 | } 408 | temp.image.blob = (this.mainObj.fileObj[filename] && this.mainObj.fileObj[filename].setName(filename.split("/").pop())) || null; 409 | temp.image.innerCell = temp.range.colOff === 0 && temp.range.rowOff === 0 ? true : false; 410 | } else if (sp) { 411 | temp.drawing = { 412 | message: "In the current stage, the object of drawing cannot be directly retrieved as the blob." 413 | }; 414 | } 415 | delete temp.range.colOff; 416 | delete temp.range.rowOff; 417 | return temp; 418 | }); 419 | return imageObj; 420 | } else { 421 | return putInternalError.call(this, "Internal error: Image files cannot be retrieved."); 422 | } 423 | }; 424 | 425 | getCommentsAsObject = function() { 426 | var checkFilename, commentFilename, commentList, comments, f, i, p, ref1, root, rootChildren, t, target, temp, xmlObj; 427 | commentFilename = ""; 428 | if (this.mainObj.sheetsObj.sheetsObj.hasOwnProperty(this.obj.sheetName)) { 429 | checkFilename = this.mainObj.sheetsObj.sheetsObj[this.obj.sheetName].rels; 430 | if (this.mainObj.fileObj.hasOwnProperty(checkFilename)) { 431 | xmlObj = getXmlObj.call(this, checkFilename); 432 | rootChildren = xmlObj.getRootElement().getChildren(); 433 | for (i = p = 0, ref1 = rootChildren.length; (0 <= ref1 ? p < ref1 : p > ref1); i = 0 <= ref1 ? ++p : --p) { 434 | target = rootChildren[i].getAttribute("Target"); 435 | if (target) { 436 | t = target.getValue(); 437 | if (t.includes("comments")) { 438 | commentFilename = t; 439 | break; 440 | } 441 | } 442 | } 443 | } else { 444 | putInternalError.call(this, "No sheet file."); 445 | } 446 | } else { 447 | putError.call(this, "No sheet name."); 448 | } 449 | temp = commentFilename.split("\/")[1]; 450 | f = Object.entries(this.mainObj.fileObj).filter(([k, v]) => { 451 | return k.includes(temp); 452 | }); 453 | comments = []; 454 | if (f.length > 0) { 455 | xmlObj = XmlService.parse(f[0][1].getDataAsString()); 456 | root = xmlObj.getRootElement(); 457 | commentList = root.getChild("commentList", root.getNamespace()).getChildren(); 458 | comments = commentList.reduce((ar, e) => { 459 | var cObj, commentObj, ref, tempComment, text, tmp; 460 | temp = {}; 461 | ref = e.getAttribute("ref"); 462 | if (ref) { 463 | tmp = ref.getValue(); 464 | cObj = a1NotationToRowCol.call(this, tmp); 465 | temp.range = {}; 466 | temp.range.col = cObj.col; 467 | temp.range.row = cObj.row; 468 | temp.range.a1Notation = tmp; 469 | text = e.getChild("text", e.getNamespace()); 470 | t = text.getChild("t", text.getNamespace()); 471 | if (t) { 472 | tempComment = t.getValue(); 473 | comments = tempComment.split(/\t\-\w.+/); 474 | commentObj = [...tempComment.matchAll(/\t\-\w.+/g)].map(([h]) => { 475 | return h.replace(/\t-/, ""); 476 | }).map((h, l) => { 477 | return { 478 | user: h, 479 | comment: comments[l] 480 | }; 481 | }); 482 | temp.comment = commentObj; 483 | } 484 | ar.push(temp); 485 | } 486 | return ar; 487 | }, []); 488 | } 489 | return comments; 490 | }; 491 | 492 | getValuesFromObj = function(v_) { 493 | var maxColumnLength, obj, values; 494 | obj = getValuesAsObject.call(this); 495 | maxColumnLength = obj.reduce((c, e) => { 496 | if (c < e.length) { 497 | return e.length; 498 | } else { 499 | return c; 500 | } 501 | }, 0); 502 | values = obj.reduce((ar, e) => { 503 | var temp; 504 | temp = new Array(maxColumnLength).fill(""); 505 | e.forEach(({value, range}) => { 506 | return temp[range.col - 1] = value[v_] === null ? "" : value[v_]; 507 | }); 508 | ar.push(temp); 509 | return ar; 510 | }, []); 511 | return values; 512 | }; 513 | 514 | getXmlObj = function(k_) { 515 | return XmlService.parse(this.mainObj.fileObj[k_].getDataAsString()); 516 | }; 517 | 518 | putError = function(m) { 519 | throw new Error(`${m}`); 520 | }; 521 | 522 | putInternalError = function(m) { 523 | throw new Error(`Internal error: ${m}`); 524 | }; 525 | 526 | a1NotationToRowCol = function(a1Notation_) { 527 | var num, str; 528 | str = a1Notation_.match(/[a-zA-Z]+/)[0]; 529 | num = Number(a1Notation_.match(/\d+/)[0]); 530 | return { 531 | row: num, 532 | col: letterToColumn.call(this, str) 533 | }; 534 | }; 535 | 536 | columnToLetter = function(column) { 537 | var letter, temp; 538 | temp = 0; 539 | letter = ""; 540 | while (column > 0) { 541 | temp = (column - 1) % 26; 542 | letter = String.fromCharCode(temp + 65) + letter; 543 | column = (column - temp - 1) / 26; 544 | } 545 | return letter; 546 | }; 547 | 548 | letterToColumn = function(letter) { 549 | var column, i, length, p, ref1; 550 | column = 0; 551 | length = letter.length; 552 | for (i = p = 0, ref1 = length; (0 <= ref1 ? p < ref1 : p > ref1); i = 0 <= ref1 ? ++p : --p) { 553 | column += (letter.charCodeAt(i) - 64) * Math.pow(26, length - i - 1); 554 | } 555 | return column; 556 | }; 557 | 558 | return ExcelApp; 559 | 560 | }).call(this); 561 | return r.ExcelApp = ExcelApp; 562 | })(this); 563 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2020 Kanshi TANAIKE 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DocsServiceApp 2 | 3 | 4 | 5 | [![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENCE) 6 | 7 | 8 | 9 | # Overview 10 | 11 | **This is a Google Apps Script library for supporting Document service, Docs API, Spreadsheet service, Sheets API, Slides service and Slides API.** The aim of this library is to compensate the processes that they services cannot achieve. 12 | 13 | ![](images/demo1.png) 14 | 15 | 16 | 17 | # Description 18 | 19 | The Google services, which are Document service, Docs API, Spreadsheet service, Sheets API, Slides service and Slides API, are growing now. But, unfortunately, there are still the processes that they cannot done. I created this GAS library for supporting the Google services. 20 | 21 | The basic method of DocsServiceApp is to directly create and edit the data of Microsoft Docs (Word, Excel and Powerpoint). The created data of Microsoft Docs can be reflected to Google Docs (Document, Spreadsheet and Slides) by converting and copying the values. By this, the processes which cannot be achieved by Google services are achieved. So it can be considered that this DocsServiceApp is used as the wrapper for supporting Google service. I believe that this method will be able to be also applied for various scenes as the methodology. So I would like to grow this library. 22 | 23 | # Feature 24 | 25 | ## For Google Docs 26 | 27 | ### [Google Document](#googledocument) 28 | 29 | - Retrieve table width and column width from the table. The tables inserted with the default width are included. 30 | 31 | ### [Google Spreadsheet](#googlespreadsheet) 32 | 33 | - Retrieve all images in Google Spreadsheet as an object including the cell range and image blob. 34 | - Retrieve all comments in Google Spreadsheet as an object including the cell range and comments. 35 | - Insert images in cells of Google Spreadsheet using the image blob. 36 | - Create new Google Spreadsheet by setting the custom header and footer. 37 | - Retrieve cell coordinates of cells with the quote prefix. 38 | - Retrieve named functions. 39 | 40 | ### [Google Slides](#googleslides) 41 | 42 | - Create new Google Slides by setting the page size. 43 | 44 | ## For Microsoft Docs 45 | 46 | In the current stage, there are not methods for directly parsing Microsoft Docs files. This library can achieve this. 47 | 48 | ### [Microsoft Word](#microsoftword) 49 | 50 | - Retrieve table width and column width. 51 | 52 | ### [Microsoft Excel](#microsoftexcel) 53 | 54 | - Retrieve all values and formulas of the cells. 55 | - Retrieve all sheet names. 56 | - Retrieve all images as an object including the cell range and image blob. 57 | - Retrieve all comments as an object including the cell range and comments. 58 | 59 | ### [Microsoft Powerpoint](#microsoftpowerpoint) 60 | 61 | There are no methods yet. 62 | 63 | # Library's project key 64 | 65 | ``` 66 | 108j6x_ZX544wEhGkgddFYM6Ie09edDqXaFwnW3RVFQCLHw_mEueqUHTW 67 | ``` 68 | 69 | # How to install 70 | 71 | ## Install this library 72 | 73 | - Open Script Editor. Click as follows: 74 | - -> Resource 75 | - -> Library 76 | - -> Input the Script ID in the text box. The Script ID is **`108j6x_ZX544wEhGkgddFYM6Ie09edDqXaFwnW3RVFQCLHw_mEueqUHTW`**. 77 | - -> Add library 78 | - -> Please select the latest version 79 | - -> Developer mode ON (Or select others if you don't want to use the latest version) 80 | - -> The identifier is "**`DocsServiceApp`**". This is set under the default. 81 | 82 | [You can read more about libraries in Apps Script here](https://developers.google.com/apps-script/guide_libraries). 83 | 84 | Please use this library with enabling V8 runtime. 85 | 86 | ## About Google APIs 87 | 88 | This library uses the following Google APIs. So when you want to use the library, please enable the following APIs at Advanced Google services. [Ref](https://developers.google.com/apps-script/guides/services/advanced#enabling_advanced_services) 89 | 90 | - Drive API: This is used for all methods. 91 | - Sheets API: This is used for Google Spreadsheet. 92 | 93 | ## About scopes 94 | 95 | This library uses the following scope. This is installed in the library, and nothing further is required from the user. But if you want to manually control the scopes, please set the required scopes to the manifest file (`appsscript.json`) in your client Google Apps Script project. 96 | 97 | - `https://www.googleapis.com/auth/drive` 98 | - This is used for all methods. 99 | - `https://www.googleapis.com/auth/script.external_request` 100 | - This is used for all methods. 101 | - `https://www.googleapis.com/auth/documents` 102 | - This is used for Google Document. 103 | - `https://www.googleapis.com/auth/spreadsheets` 104 | - This is used for Google Spreadsheet. 105 | - `https://www.googleapis.com/auth/presentations` 106 | - This is used for Google Slides. 107 | 108 | ## About including GAS libraries 109 | 110 | This library uses the following Google Apps Script library. 111 | 112 | - [ImgApp](https://github.com/tanaikech/ImgApp) 113 | 114 | 115 | 116 | # Methods 117 | 118 | In the current stage, there are the following methods in this library. 119 | 120 | 121 | 122 | ## For Google Document 123 | 124 | ### 1. `getTableColumnWidth()` 125 | 126 | Retrieve the column width of the table in the Google Document. For example, when a new table, which has 1 row and 2 columns, is manually inserted to the Document body as the default format, the table width and column width retrieved by `getColumnWidth()` return `null`. By this, in the current stage, the table width and column width cannot be retrieved. This method achieves this. 127 | 128 | #### Sample script 129 | 130 | ```javascript 131 | const documentId = "###"; // Google Document ID 132 | const res = DocsServiceApp.openByDocumentId(documentId).getTableColumnWidth(); 133 | console.log(res); 134 | ``` 135 | 136 | #### Result 137 | 138 | ```json 139 | [ 140 | { 141 | "tableIndex": 0, // 0 means the 1st table in Google Document. 142 | "unit": "pt", 143 | "tableWidth": 451.3, // Table width 144 | "tebleColumnWidth": [225.65, 225.65] // Column width of each column. Array index is the column index. 145 | }, 146 | , 147 | , 148 | , 149 | ] 150 | ``` 151 | 152 | - For example, when the table which has the columns "A" and "B" of 100 pt and 200 pt are checked by above script, the same values of 100 and 200 for the columns "A" and "B" could be confirmed. So from this result, it is found that the column width of DOCX data and Google Document is the same. 153 | 154 | 155 | 156 | ## For Google Spreadsheet 157 | 158 | ### 1. `getImages()` 159 | 160 | Retrieve images in and over the cell from Google Spreadsheet as blob. In the current stage, there are no methods for retrieving the images over the cells and inner the cells in the existing Google Spreadsheet service and Sheets API. This method achieves this. 161 | 162 | #### Sample script 163 | 164 | ```javascript 165 | const spreadsheetId = "###"; // Google Spreadsheet ID 166 | const res = DocsServiceApp.openBySpreadsheetId(spreadsheetId) 167 | .getSheetByName("Sheet1") 168 | .getImages(); 169 | console.log(res); 170 | ``` 171 | 172 | In this script, the images are retrieved from "Sheet1" of `spreadsheetId`. 173 | 174 | And 175 | 176 | ```javascript 177 | const spreadsheetId = "###"; // Google Spreadsheet ID 178 | const res = DocsServiceApp.openBySpreadsheetId(spreadsheetId).getImages(); 179 | console.log(res); 180 | ``` 181 | 182 | In this script, the images are retrieved from all sheets of `spreadsheetId`. 183 | 184 | When you want to save all images in the Spreadsheet as the files, you can use the following script. 185 | 186 | ```javascript 187 | const spreadsheetId = "###"; // Google Spreadsheet ID 188 | const res = DocsServiceApp.openBySpreadsheetId(spreadsheetId).getImages(); 189 | const folder = DriveApp.getFolderById("### folderId ###"); 190 | res.forEach(({ images }) => 191 | images.forEach((e) => { 192 | if (e.image) folder.createFile(e.image.blob); 193 | }) 194 | ); 195 | ``` 196 | 197 | #### Result 198 | 199 | ```json 200 | [ 201 | { 202 | "range": { "col": 3, "row": 8, "a1Notation": "C8" }, 203 | "image": { 204 | "description": "sample description", 205 | "title": "sample title", 206 | "blob": BLOB, 207 | "innerCell": false // "false" means that the image is over a cell. 208 | } 209 | }, 210 | { 211 | "range": { "col": 2, "row": 2, "a1Notation": "B2" }, 212 | "image": { 213 | "description": "sample description", 214 | "title": "sample title", 215 | "blob": BLOB, 216 | "innerCell": true // "true" means that the image is in a cell. 217 | } 218 | }, 219 | , 220 | , 221 | , 222 | ] 223 | ``` 224 | 225 | - You can create the image file from `BLOB`. 226 | 227 | - When `getSheetByName()` is not used, above array is put in each sheet as follows. 228 | 229 | ```json 230 | [ 231 | { "sheetName": "Sheet1", "images": [[Object], [Object], [Object]] }, 232 | { "sheetName": "Sheet2", "images": [] }, 233 | { "sheetName": "Sheet3", "images": [[Object], [Object]] } 234 | ] 235 | ``` 236 | 237 | #### Limitation 238 | 239 | - When the images are retrieved from XLSX data, it seems that the image is a bit different from the original one. The image format is the same. But the data size is smaller than that of the original. When the image size is more than 2048 pixels and 72 dpi, the image is modified to 2048 pixels and 72 dpi. Even when the image size is less than 2048 pixels and 72 dpi, the file size becomes smaller than that of original one. So I think that the image might be compressed. Please be careful this. 240 | - In the current stage, the drawings cannot be retrieved yet. I apologize for this. 241 | 242 | ### 2. `getComments()` 243 | 244 | Retrieve comments in Google Spreadsheet. In the current stage, there are no methods for retrieving the comments with the cell coordinate in the existing Google Spreadsheet service and Sheets API. This method achieves this. 245 | 246 | #### Sample script 247 | 248 | ```javascript 249 | const spreadsheetId = "###"; // Google Spreadsheet ID 250 | const res = DocsServiceApp.openBySpreadsheetId(spreadsheetId) 251 | .getSheetByName("Sheet1") 252 | .getComments(); 253 | console.log(res); 254 | ``` 255 | 256 | In this script, the images are retrieved from "Sheet1" of `spreadsheetId`. 257 | 258 | And 259 | 260 | ```javascript 261 | const spreadsheetId = "###"; // Google Spreadsheet ID 262 | const res = DocsServiceApp.openBySpreadsheetId(spreadsheetId).getComments(); 263 | console.log(res); 264 | ``` 265 | 266 | In this script, the images are retrieved from all sheets of `spreadsheetId`. 267 | 268 | #### Result 269 | 270 | ```json 271 | [ 272 | { 273 | "range": { 274 | "col": 2, 275 | "row": 11, 276 | "a1Notation": "B11" 277 | }, 278 | "comment": [ 279 | { 280 | "user": "user name", 281 | "comment": "comment" 282 | }, 283 | , 284 | , 285 | , 286 | ] 287 | }, 288 | , 289 | , 290 | , 291 | ] 292 | ``` 293 | 294 | - When `getSheetByName()` is not used, above array is put in each sheet as follows. 295 | 296 | ```json 297 | [ 298 | { "sheetName": "Sheet1", "images": [[Object], [Object], [Object]] }, 299 | { "sheetName": "Sheet2", "images": [] }, 300 | { "sheetName": "Sheet3", "images": [[Object], [Object]] } 301 | ] 302 | ``` 303 | 304 | ### 3. `insertImage()` 305 | 306 | Insert images in and over the cell from Google Spreadsheet from the image blob. In the current stage, there are no methods for directly inserting an image in a cell in the existing Google Spreadsheet service and Sheets API. For example, when the user wants to insert an image on own Google Drive in a cell, the image is required to be publicly shared for using `=IMAGE(URL)`. In this method, the image can be put without publicly sharing the image and using `=IMAGE(URL)`. 307 | 308 | #### Sample script 309 | 310 | ```javascript 311 | const spreadsheetId = "###"; // Google Spreadsheet ID 312 | const blob1 = DriveApp.getFileById("###fileId###").getBlob(); 313 | const blob2 = UrlFetchApp.fetch("###URL###").getBlob(); 314 | const object = [ 315 | { blob: blob1, range: { row: 1, column: 1 } }, // Image is inserted in a cell "A1". 316 | { blob: blob2, range: { row: 5, column: 2 } }, // Image is inserted in a cell "B5". 317 | ]; 318 | DocsServiceApp.openBySpreadsheetId(spreadsheetId) 319 | .getSheetByName("Sheet1") 320 | .insertImage(object); 321 | ``` 322 | 323 | - **In this method, no values are returned.** 324 | - In above sample script, 2 images are inserted into the cells "A1" and "B5" in "Sheet1", respectively. 325 | 326 | #### Result 327 | 328 | ![](images/fig1.png) 329 | 330 | - The sample image of cell "A1" is from [https://www.deviantart.com/k3-studio/art/Rainbow-painting-281090729](https://www.deviantart.com/k3-studio/art/Rainbow-painting-281090729) 331 | - The sample image of cell "B5" is from [https://www.deviantart.com/k3-studio/art/Chromatic-lituus-415318548](https://www.deviantart.com/k3-studio/art/Chromatic-lituus-415318548) 332 | 333 | #### Limitation 334 | 335 | - When the images are retrieved from XLSX data, it seems that the image is a bit different from the original one. The image format is the same. But the data size is smaller than that of the original. When the image size is more than 2048 pixels and 72 dpi, the image is modified to 2048 pixels and 72 dpi. Even when the image size is less than 2048 pixels and 72 dpi, the file size becomes smaller than that of original one. So I think that the image might be compressed. Please be careful this. 336 | - In the current stage, the drawings cannot be retrieved yet. I apologize for this. 337 | 338 | ### 4. `createNewSpreadsheetWithCustomHeaderFooter()` 339 | 340 | Create new Google Spreadsheet by setting the header and footer. In the current stage, there are no methods for setting the header and footer for Google Spreadsheet in the existing Google Spreadsheet service and Sheets API. This method achieves this. 341 | 342 | #### Sample script 343 | 344 | ```javascript 345 | const object = { 346 | title: "sample title", // Title of created Spreadsheet. 347 | parent: "###", // folder ID 348 | header: { l: "left header", c: "center header", r: "right header" }, 349 | footer: { l: "left footer", c: "center footer", r: "right footer" }, 350 | }; 351 | const res = DocsServiceApp.createNewSpreadsheetWithCustomHeaderFooter(object); 352 | console.log(res); 353 | ``` 354 | 355 | - In this method, the spreadsheet ID of created Spreadsheet is returned. 356 | 357 | #### Result 358 | 359 | ![](images/fig2.png) 360 | 361 | 362 | 363 | ### 5. `getQuotePrefixCells()` 364 | 365 | ![](images/fig4.png) 366 | 367 | Retrieve cell coordinates of cells with the quote prefix. In Google Spreadsheet, when a single quote is add to the top letter of the cell value, the cell is used as the text value. When we want to search the cells with the quote prefix in Spreadsheet, unfortunately, in the current stage, this cannot be achieved using Spreadsheet service (SpreadsheetApp) and Sheets API. In this method, such cells can be retrieved. The output values are the cell coordinates of the cells with the quote prefix. 368 | 369 | #### Sample script 370 | 371 | ```javascript 372 | const spreadsheetId = "###"; // Google Spreadsheet ID 373 | const res = DocsServiceApp.openBySpreadsheetId(spreadsheetId) 374 | .getSheetByName("Sheet1") // Please set sheet name. 375 | .getQuotePrefixCells(); 376 | console.log(res); 377 | ``` 378 | 379 | #### Result 380 | 381 | ```json 382 | ["A1", "C3", "E1", , ,] 383 | ``` 384 | 385 | - I answered this method to [this thread at Stackoverflow](https://stackoverflow.com/a/73616511). 386 | 387 | 388 | 389 | ### 6. `getNamedFunctions()` 390 | 391 | ![](images/fig5.png) 392 | 393 | This method is for retrieving the named functions from Google Spreadsheet using Google Apps Script. 394 | 395 | Recently, the named functions got to be able to be used in Google Spreadsheet. [Ref](https://workspaceupdates.googleblog.com/2022/08/named-functions-google-sheets.html) When several named functions are added, I thought that I wanted to retrieve these functions using a script. But, unfortunately, in the current stage, it seems that there are no built-in methods (SpreadsheetApp and Sheets API) for directly retrieving the named functions. So, I created this method. 396 | 397 | #### Sample script 398 | 399 | ```javascript 400 | const spreadsheetId = "###"; // Google Spreadsheet ID 401 | const res = 402 | DocsServiceApp.openBySpreadsheetId(spreadsheetId).getNamedFunctions(); 403 | console.log(res); 404 | ``` 405 | 406 | #### Result 407 | 408 | When this script is run to the top sample situation in this section, the following result is obtained. 409 | 410 | ```json 411 | [ 412 | { 413 | "definedName": "CONTAINS", 414 | "definedFunction": "LAMBDA(cell, range, NOT(ISERROR(MATCH(cell,range,0))))" 415 | }, 416 | { "definedName": "SAMPLE1", "definedFunction": "LAMBDA(range, SUM(range))" } 417 | ] 418 | ``` 419 | 420 | - Unfortunately, in the current stage, the description of the named function cannot be obtained. 421 | 422 | - At XLSX format, the named functions are used as LAMBDA function. If you want to directly use this LAMBDA function, for example, please put a function like =LAMBDA(range, SUM(range))(A1:A5) into a cell. By this, the LAMBDA function can be run. Of course, you can retrieve the function from this result and put it as the named function again. 423 | 424 | - I posted this method in my [blog](https://tanaikech.github.io/2022/09/28/retrieving-named-functions-from-google-spreadsheet-using-google-apps-script/). 425 | 426 | 427 | 428 | ## For Google Slides 429 | 430 | ### 1. `createNewSlidesWithPageSize()` 431 | 432 | Create new Google Slides by setting the page size. In the current stage, there are no methods for setting the page size for Google Slides in the existing Google Slides service and Slides API, although Slides API has the method of "presentations.create". This method achieves this. 433 | 434 | #### Sample script 435 | 436 | ```javascript 437 | const object = { 438 | title: "sample title", // Title of created Slides. 439 | parent: "###", // folder ID 440 | width: { unit: "pixel", size: 200 }, 441 | height: { unit: "pixel", size: 300 }, 442 | }; 443 | const res = DocsServiceApp.createNewSlidesWithPageSize(object); 444 | console.log(res); 445 | ``` 446 | 447 | - In this method, the presentation ID of created Slides is returned. 448 | - "pixel" and "point" can be used for `unit` in above object. 449 | 450 | #### Sample situations 451 | 452 | When this method is used, the following application can be created. 453 | 454 | 1. [Inserting Text on Image using Google Apps Script](https://gist.github.com/tanaikech/835642df109731a559e52d831bd3342d) : This is a sample script for inserting a text on an image using Google Apps Script. 455 | 456 | 457 | 458 | ## For Microsoft Word 459 | 460 | ### 1. `getTableColumnWidth()` 461 | 462 | Retrieve the column width of the table in the Microsoft Word. In this case, the column width of the table are directly retrieved from Microsoft Word. 463 | 464 | #### Sample script 465 | 466 | ```javascript 467 | const blob = "BLOB"; // Blob of Microsoft Word file. 468 | const res = DocsServiceApp.openByWordFileBlob(blob).getTableColumnWidth(); 469 | console.log(res); 470 | ``` 471 | 472 | #### Result 473 | 474 | ```json 475 | [ 476 | { 477 | "tableIndex": 0, // 0 means the 1st table in Google Document. 478 | "unit": "pt", 479 | "tableWidth": 451.3, // Table width 480 | "tebleColumnWidth": [225.65, 225.65] // Column width of each column. Array index is the column index. 481 | }, 482 | , 483 | , 484 | , 485 | ] 486 | ``` 487 | 488 | 489 | 490 | ## For Microsoft Excel 491 | 492 | ![](images/fig3.png) 493 | 494 | > IMPORTANT: About `getValues()` and `getFormulas()` methods, in the current stage, the process costs of them is much higher than those of Google Spreadsheet service. So when you want to retrieve the values and formulas from XLSX data, I would like to recommend to use Google Spreadsheet service by converting XSLX data to Google Spreadsheet. 495 | 496 | ### 1. `getImages()` 497 | 498 | Retrieve images in and over the cell from Microsoft Excel as blob. In this case, the images are directly retrieved from Microsoft Excel. 499 | 500 | #### Sample script 501 | 502 | ```javascript 503 | const blob = "BLOB"; // Blob of Microsoft Excel file. 504 | const res = DocsServiceApp.openByExcelFileBlob(blob) 505 | .getSheetByName("Sheet1") 506 | .getImages(); 507 | console.log(res); 508 | ``` 509 | 510 | In this script, the images are retrieved from "Sheet1" of `spreadsheetId`. 511 | 512 | And 513 | 514 | ```javascript 515 | const blob = "BLOB"; // Blob of Microsoft Excel file. 516 | const res = DocsServiceApp.openByExcelFileBlob(blob).getImages(); 517 | console.log(res); 518 | ``` 519 | 520 | In this script, the images are retrieved from all sheets of `spreadsheetId`. 521 | 522 | - **`blob`** : Blob of XLSX file. 523 | - **`sheetName`** : Sheet name in XLSX file. The formulas are retrieved from the sheet. 524 | 525 | #### Result 526 | 527 | ```json 528 | [ 529 | { 530 | "range": { "col": 3, "row": 8, "a1Notation": "C8" }, 531 | "image": { 532 | "description": "sample description", 533 | "title": "sample title", 534 | "blob": BLOB, 535 | "innerCell": false // "false" means that the image is over a cell. 536 | } 537 | }, 538 | { 539 | "range": { "col": 2, "row": 2, "a1Notation": "B2" }, 540 | "image": { 541 | "description": "sample description", 542 | "title": "sample title", 543 | "blob": BLOB, 544 | "innerCell": true // "true" means that the image is in a cell. 545 | } 546 | }, 547 | , 548 | , 549 | , 550 | ] 551 | ``` 552 | 553 | - When `getSheetByName()` is not used, above array is put in each sheet as follows. 554 | 555 | ```json 556 | [ 557 | { "sheetName": "Sheet1", "images": [[Object], [Object], [Object]] }, 558 | { "sheetName": "Sheet2", "images": [] }, 559 | { "sheetName": "Sheet3", "images": [[Object], [Object]] } 560 | ] 561 | ``` 562 | 563 | #### Limitation 564 | 565 | - When the images are retrieved from XLSX data, it seems that the image is a bit different from the original one. The image format is the same. But the data size is smaller than that of the original. When the image size is more than 2048 pixels and 72 dpi, the image is modified to 2048 pixels and 72 dpi. Even when the image size is less than 2048 pixels and 72 dpi, the file size becomes smaller than that of original one. So I think that the image might be compressed. Please be careful this. 566 | - In the current stage, the drawings cannot be retrieved yet. I apologize for this. 567 | 568 | ### 2. `getComments()` 569 | 570 | Retrieve comments in Microsoft Excel. In this case, the comments are directly retrieved from Microsoft Excel. 571 | 572 | #### Sample script 573 | 574 | ```javascript 575 | const blob = "BLOB"; // Blob of Microsoft Excel file. 576 | const res = DocsServiceApp.openByExcelFileBlob(blob) 577 | .getSheetByName("Sheet1") 578 | .getComments(); 579 | console.log(res); 580 | ``` 581 | 582 | In this script, the comments are retrieved from "Sheet1" of Blob of Microsoft Excel file. 583 | 584 | - **`blob`** : Blob of XLSX file. 585 | - **`sheetName`** : Sheet name in XLSX file. The formulas are retrieved from the sheet. 586 | 587 | #### Result 588 | 589 | ```json 590 | [ 591 | { 592 | "range": { 593 | "col": 2, 594 | "row": 11, 595 | "a1Notation": "B11" 596 | }, 597 | "comment": [ 598 | { 599 | "user": "user name", 600 | "comment": "comment" 601 | }, 602 | , 603 | , 604 | , 605 | ] 606 | }, 607 | , 608 | , 609 | , 610 | ] 611 | ``` 612 | 613 | ### 3. `getAll()` 614 | 615 | This method is used for retrieving all values (in the current stage, those are values, formulas, images and comments.) from all sheets of XLSX data. The returned value is JSON object. 616 | 617 | #### Sample script 618 | 619 | ```javascript 620 | function myFunction() { 621 | const fileId = "###"; // Please set the file ID of XLSX file. 622 | const blob = DriveApp.getFileById(fileId).getBlob(); 623 | 624 | const res = DocsServiceApp.openByExcelFileBlob(blob).getAll(); 625 | console.log(res); 626 | } 627 | ``` 628 | 629 | - **`blob`** : Blob of XLSX file. 630 | - The values are returned as JSON object. The returned values include the values, formulas, images and comments of all sheets in the XLSX data. 631 | 632 | ### 4. `getSheets()` 633 | 634 | This method is used for retrieving the sheet list from XLSX data. 635 | 636 | #### Sample script 637 | 638 | ```javascript 639 | function myFunction() { 640 | const fileId = "###"; // Please set the file ID of XLSX file. 641 | const blob = DriveApp.getFileById(fileId).getBlob(); 642 | 643 | const res = DocsServiceApp.openByExcelFileBlob(blob).getSheets(); 644 | console.log(res); 645 | } 646 | ``` 647 | 648 | - **`blob`** : Blob of XLSX file. 649 | 650 | ### 5. `getValues()` 651 | 652 | This method is used for updating the values from a sheet of XLSX data. 653 | 654 | #### Sample script 655 | 656 | ```javascript 657 | function myFunction() { 658 | const fileId = "###"; // Please set the file ID of XLSX file. 659 | const sheetName = "###"; // Please set the sheet name. 660 | const blob = DriveApp.getFileById(fileId).getBlob(); 661 | 662 | const res = DocsServiceApp.openByExcelFileBlob(blob) 663 | .getSheetByName(sheetName) 664 | .getValues(); 665 | console.log(res); 666 | } 667 | ``` 668 | 669 | - **`blob`** : Blob of XLSX file. 670 | - **`sheetName`** : Sheet name in XLSX file. The values are retrieved from the sheet. 671 | 672 | Sample result value is as follows. The values are returned as 2 dimensional array. 673 | 674 | ```json 675 | [ 676 | ["a1", "b1", "c1"], 677 | ["", "b2", "c2"], 678 | ["a3", "b3", "c3"], 679 | ["a4", "b4", "c4"], 680 | ["a5", "b5", "c5"] 681 | ] 682 | ``` 683 | 684 | 685 | 686 | ### 6. `getFormulas()` 687 | 688 | This method is used for updating the formulas from a sheet of XLSX data. 689 | 690 | ### Sample script 691 | 692 | ```javascript 693 | function myFunction() { 694 | const fileId = "###"; // Please set the file ID of XLSX file. 695 | const sheetName = "###"; // Please set the sheet name. 696 | const blob = DriveApp.getFileById(fileId).getBlob(); 697 | 698 | const res = DocsServiceApp.openByExcelFileBlob(blob) 699 | .getSheetByName(sheetName) 700 | .getFormulas(); 701 | console.log(res); 702 | } 703 | ``` 704 | 705 | - **`blob`** : Blob of XLSX file. 706 | - **`sheetName`** : Sheet name in XLSX file. The formulas are retrieved from the sheet. 707 | - The values are returned as 2 dimensional array. 708 | 709 | 710 | 711 | ## For Microsoft Powerpoint 712 | 713 | There are no methods yet. 714 | 715 | --- 716 | 717 | 718 | 719 | # Licence 720 | 721 | [MIT](LICENCE) 722 | 723 | 724 | 725 | # Author 726 | 727 | [Tanaike](https://tanaikech.github.io/about/) 728 | 729 | 730 | 731 | # Update History 732 | 733 | - v1.0.0 (September 24, 2020) 734 | 735 | 1. Initial release. 736 | 737 | - v1.1.0 (September 28, 2022) 738 | 739 | 1. Added a new method of [`getQuotePrefixCells()`](#getQuotePrefixCells). This method can detect the cells with the quote prefix cells. 740 | 741 | - v1.2.0 (September 29, 2022) 742 | 743 | 1. Added a new method of [`getNamedFunctions()`](#getnamedfunctions). This method can retrieve the named functions from Google Spreadsheet. 744 | 745 | - v1.2.1 (December 28, 2022) 746 | 747 | 1. Remove a bug in `ExcelApp`. 748 | 749 | - v1.2.2 (January 30, 2024) 750 | 751 | 1. Remove a bug in `ExcelApp`. When the inserted image had no data, an error occurred. This issue was removed. 752 | 753 | [TOP](#top) 754 | -------------------------------------------------------------------------------- /SlidesAppp.js: -------------------------------------------------------------------------------- 1 | // --- SlidesAppp (SlidesApp plus) --- 2 | (function(r) { 3 | var SlidesAppp; 4 | SlidesAppp = (function() { 5 | var newPPTXdata, pptxObjToBlob, putError, putInternalError, setPageSize; 6 | 7 | class SlidesAppp { 8 | constructor(id_) { 9 | this.name = "SlidesAppp"; 10 | if (id_ !== "create") { 11 | if (id_ === "" || DriveApp.getFileById(id_).getMimeType() !== MimeType.GOOGLE_SLIDES) { 12 | putError.call(this, "This file ID is not the file ID of Google Slides."); 13 | } 14 | this.obj = { 15 | presentationId: id_ 16 | }; 17 | } 18 | this.headers = { 19 | Authorization: `Bearer ${ScriptApp.getOAuthToken()}` 20 | }; 21 | this.mainObj = {}; 22 | } 23 | 24 | // --- begin methods 25 | createNewSlidesWithPageSize(obj_) { 26 | var blob, createObj, e, pptxObj, tmpId; 27 | if (!obj_ || Object.keys(obj_).length === 0) { 28 | putError.call(this, "Object was not found. Please confirm it again."); 29 | } 30 | pptxObj = newPPTXdata.call(this); 31 | setPageSize.call(this, obj_, pptxObj); 32 | blob = pptxObjToBlob.call(this, pptxObj); 33 | createObj = { 34 | title: "SlidesSample", 35 | mimeType: MimeType.GOOGLE_SLIDES 36 | }; 37 | if (obj_.hasOwnProperty("title")) { 38 | createObj.title = obj_.title; 39 | } 40 | if (obj_.hasOwnProperty("parent")) { 41 | createObj.parents = [ 42 | { 43 | id: obj_.parent 44 | } 45 | ]; 46 | } 47 | try { 48 | tmpId = Drive.Files.insert(createObj, blob).id; 49 | } catch (error) { 50 | e = error; 51 | if (e.message === "Drive is not defined") { 52 | putError.call(this, "Please enable Drive API at Advanced Google services, and try again."); 53 | } else { 54 | putError.call(this, e.message); 55 | } 56 | } 57 | return tmpId; 58 | } 59 | 60 | }; 61 | 62 | SlidesAppp.name = "SlidesAppp"; 63 | 64 | // --- end methods 65 | newPPTXdata = function() { 66 | var data; 67 | data = "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"; 68 | return JSON.parse(Utilities.unzip(Utilities.newBlob(Utilities.base64Decode(data), MimeType.ZIP))[0].getDataAsString()); 69 | }; 70 | 71 | setPageSize = function(obj_, obj) { 72 | var filename, h, root, unitX, unitY, w, xmlObj; 73 | if (obj_.hasOwnProperty("width") || obj_.hasOwnProperty("height")) { 74 | unitX = "pixel"; 75 | unitY = "pixel"; 76 | if (obj_.width.hasOwnProperty("unit")) { 77 | unitX = obj_.width.unit; 78 | } 79 | if (obj_.height.hasOwnProperty("unit")) { 80 | unitY = obj_.height.unit; 81 | } 82 | if ((unitX !== "pixel" && unitX !== "point") || (unitY !== "pixel" && unitY !== "point")) { 83 | putError.call(this, "Unit is wrong."); 84 | } 85 | if (!obj_.width.hasOwnProperty("size") || !obj_.height.hasOwnProperty("size")) { 86 | putError.call(this, "Size was not found."); 87 | } 88 | w = (unitX === "pixel" ? obj_.width.size * 0.75 : obj_.width.size) * 12700; 89 | h = (unitX === "pixel" ? obj_.height.size * 0.75 : obj_.height.size) * 12700; 90 | filename = "ppt/presentation.xml"; 91 | xmlObj = XmlService.parse(obj[filename]); 92 | root = xmlObj.getRootElement(); 93 | root.getChild("sldSz", root.getNamespace("p")).setAttribute("cx", w).setAttribute("cy", h); 94 | obj[filename] = XmlService.getRawFormat().format(root); 95 | } 96 | }; 97 | 98 | pptxObjToBlob = function(pptxObj) { 99 | var blobs; 100 | blobs = Object.entries(pptxObj).reduce((ar, [k, v]) => { 101 | ar.push(v.toString() === "Blob" ? v : Utilities.newBlob(v, MimeType.PLAIN_TEXT, k)); 102 | return ar; 103 | }, []); 104 | return Utilities.zip(blobs, "temp.pptx").setContentType(MimeType.MICROSOFT_POWERPOINT); 105 | }; 106 | 107 | putError = function(m) { 108 | throw new Error(`${m}`); 109 | }; 110 | 111 | putInternalError = function(m) { 112 | throw new Error(`Internal error: ${m}`); 113 | }; 114 | 115 | return SlidesAppp; 116 | 117 | }).call(this); 118 | return r.SlidesAppp = SlidesAppp; 119 | })(this); 120 | -------------------------------------------------------------------------------- /SpreadsheetAppp.js: -------------------------------------------------------------------------------- 1 | // --- SpreadsheetAppp (SpreadsheetApp plus) --- 2 | (function (r) { 3 | var SpreadsheetAppp; 4 | SpreadsheetAppp = function () { 5 | var ContentTypesXml_, 6 | createDrawing1Xml_, 7 | createSheet1Xml_, 8 | drawing1XmlRels_, 9 | gToM, 10 | imagesToObj, 11 | newXLSXdata, 12 | putError, 13 | putInternalError, 14 | setheaderFooter, 15 | xlsxObjToBlob; 16 | 17 | class SpreadsheetAppp { 18 | constructor(id_) { 19 | this.name = "SpreadsheetAppp"; 20 | if (id_ !== "create") { 21 | if ( 22 | id_ === "" || 23 | DriveApp.getFileById(id_).getMimeType() !== MimeType.GOOGLE_SHEETS 24 | ) { 25 | putError.call( 26 | this, 27 | "This file ID is not the file ID of Spreadsheet." 28 | ); 29 | } 30 | this.obj = { 31 | spreadsheetId: id_, 32 | }; 33 | } 34 | this.headers = { 35 | Authorization: `Bearer ${ScriptApp.getOAuthToken()}`, 36 | }; 37 | this.mainObj = {}; 38 | } 39 | 40 | // --- begin methods 41 | getSheetByName(sheetName_) { 42 | if (!sheetName_ || sheetName_.toString() === "") { 43 | putError.call(this, "No sheet name."); 44 | } 45 | this.obj.sheetName = sheetName_; 46 | return this; 47 | } 48 | 49 | getImages() { 50 | gToM.call(this); 51 | if (this.obj.hasOwnProperty("sheetName")) { 52 | return new ExcelApp(this.mainObj.blob) 53 | .getSheetByName(this.obj.sheetName) 54 | .getImages(); 55 | } else { 56 | return new ExcelApp(this.mainObj.blob) 57 | .getAll() 58 | .map(({ sheetName, images }) => { 59 | return { sheetName, images }; 60 | }); 61 | } 62 | } 63 | 64 | getComments() { 65 | gToM.call(this); 66 | if (this.obj.hasOwnProperty("sheetName")) { 67 | return new ExcelApp(this.mainObj.blob) 68 | .getSheetByName(this.obj.sheetName) 69 | .getComments(); 70 | } else { 71 | return new ExcelApp(this.mainObj.blob) 72 | .getAll() 73 | .map(({ sheetName, comments }) => { 74 | return { sheetName, comments }; 75 | }); 76 | } 77 | } 78 | 79 | insertImage(objAr_) { 80 | var ar, 81 | blob, 82 | dstSS, 83 | dstSheet, 84 | dstSheetId, 85 | e, 86 | requests, 87 | tmpId, 88 | tmpSheet, 89 | tmpSheetId, 90 | xlsxObj; 91 | if ( 92 | !Array.isArray(objAr_) || 93 | objAr_.some(({ blob, range }) => { 94 | var height, identification, width; 95 | if ( 96 | blob.toString() !== "Blob" || 97 | !range.row || 98 | !range.column || 99 | isNaN(range.row) || 100 | isNaN(range.column) 101 | ) { 102 | return true; 103 | } 104 | ({ width, height, identification } = ImgApp.getSize(blob)); 105 | if (width * height > 1048576) { 106 | return true; 107 | } 108 | if ( 109 | !["GIF", "PNG", "JPG"].some((e) => { 110 | return e === identification; 111 | }) 112 | ) { 113 | return true; 114 | } 115 | return false; 116 | }) 117 | ) { 118 | putError.call( 119 | this, 120 | "Wrong object. Please confirm it again. By the way, the maximum image size is 'width x height < 1048576'. And the mimeTypes are PNG, JPG and GIF." 121 | ); 122 | } 123 | xlsxObj = newXLSXdata.call(this); 124 | ar = imagesToObj.call(this, xlsxObj, objAr_); 125 | ContentTypesXml_.call(this, xlsxObj, ar); 126 | createSheet1Xml_.call(this, xlsxObj, ar); 127 | createDrawing1Xml_.call(this, xlsxObj, ar); 128 | drawing1XmlRels_.call(this, xlsxObj, ar); 129 | blob = xlsxObjToBlob.call(this, xlsxObj); 130 | try { 131 | tmpId = Drive.Files.insert( 132 | { 133 | title: "SpreadsheetAppp_temp", 134 | mimeType: MimeType.GOOGLE_SHEETS, 135 | }, 136 | blob 137 | ).id; 138 | } catch (error) { 139 | e = error; 140 | if (e.message === "Drive is not defined") { 141 | putError.call( 142 | this, 143 | "Please enable Drive API at Advanced Google services, and try again." 144 | ); 145 | } else { 146 | putError.call(this, e.message); 147 | } 148 | } 149 | dstSS = SpreadsheetApp.openById(this.obj.spreadsheetId); 150 | dstSheet = dstSS.getSheetByName(this.obj.sheetName); 151 | dstSheetId = dstSheet.getSheetId(); 152 | tmpSheet = SpreadsheetApp.openById(tmpId) 153 | .getSheets()[0] 154 | .setName(`SpreadsheetAppp_${Utilities.getUuid()}`) 155 | .copyTo(dstSS); 156 | DriveApp.getFileById(tmpId).setTrashed(true); 157 | tmpSheetId = tmpSheet.getSheetId(); 158 | requests = ar.map((e) => { 159 | e.from.sheetId = tmpSheetId; 160 | e.to.sheetId = dstSheetId; 161 | return { 162 | copyPaste: { 163 | source: e.from, 164 | destination: e.to, 165 | pasteType: "PASTE_VALUES", 166 | }, 167 | }; 168 | }); 169 | try { 170 | Sheets.Spreadsheets.batchUpdate( 171 | { 172 | requests: requests, 173 | }, 174 | this.obj.spreadsheetId 175 | ); 176 | } catch (error) { 177 | e = error; 178 | if (e.message === "Sheets is not defined") { 179 | putError.call( 180 | this, 181 | "Please enable Sheets API at Advanced Google services, and try again." 182 | ); 183 | } else { 184 | putError.call(this, e.message); 185 | } 186 | } 187 | dstSS.deleteSheet(tmpSheet); 188 | return null; 189 | } 190 | 191 | createNewSpreadsheetWithCustomHeaderFooter(obj_) { 192 | var blob, createObj, e, tmpId, xlsxObj; 193 | if (!obj_ || Object.keys(obj_).length === 0) { 194 | putError.call(this, "Object was not found. Please confirm it again."); 195 | } 196 | xlsxObj = newXLSXdata.call(this); 197 | setheaderFooter.call(this, obj_, xlsxObj); 198 | blob = xlsxObjToBlob.call(this, xlsxObj); 199 | createObj = { 200 | title: "SpreadsheetSample", 201 | mimeType: MimeType.GOOGLE_SHEETS, 202 | }; 203 | if (obj_.hasOwnProperty("title")) { 204 | createObj.title = obj_.title; 205 | } 206 | if (obj_.hasOwnProperty("parent")) { 207 | createObj.parents = [ 208 | { 209 | id: obj_.parent, 210 | }, 211 | ]; 212 | } 213 | try { 214 | tmpId = Drive.Files.insert(createObj, blob).id; 215 | } catch (error) { 216 | e = error; 217 | if (e.message === "Drive is not defined") { 218 | putError.call( 219 | this, 220 | "Please enable Drive API at Advanced Google services, and try again." 221 | ); 222 | } else { 223 | putError.call(this, e.message); 224 | } 225 | } 226 | return tmpId; 227 | } 228 | 229 | getQuotePrefixCells() { 230 | gToM.call(this); 231 | if (this.obj.hasOwnProperty("sheetName")) { 232 | return new ExcelApp(this.mainObj.blob) 233 | .getSheetByName(this.obj.sheetName) 234 | .getQuotePrefixCells(); 235 | } 236 | return "Please set sheet name."; 237 | } 238 | 239 | getNamedFunctions() { 240 | gToM.call(this); 241 | return new ExcelApp(this.mainObj.blob).getNamedFunctions(); 242 | } 243 | } 244 | 245 | SpreadsheetAppp.name = "SpreadsheetAppp"; 246 | 247 | // --- end methods 248 | gToM = function () { 249 | var obj, url; 250 | url = `https://www.googleapis.com/drive/v3/files/${this.obj.spreadsheetId}/export?mimeType=${MimeType.MICROSOFT_EXCEL}`; 251 | obj = UrlFetchApp.fetch(url, { 252 | headers: this.headers, 253 | }); 254 | if (obj.getResponseCode() !== 200) { 255 | putError.call( 256 | this, 257 | "Spreadsheet ID might be not correct. Please check it again." 258 | ); 259 | } 260 | this.mainObj.blob = obj.getBlob(); 261 | }; 262 | 263 | newXLSXdata = function () { 264 | var data; 265 | data = 266 | "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"; 267 | return JSON.parse( 268 | Utilities.unzip( 269 | Utilities.newBlob(Utilities.base64Decode(data), MimeType.ZIP) 270 | )[0].getDataAsString() 271 | ); 272 | }; 273 | 274 | imagesToObj = function (obj_, ar_) { 275 | var dupCheckAr; 276 | dupCheckAr = []; 277 | return ar_.reduce((ar, e, i) => { 278 | var dupObj, 279 | ext, 280 | fileSize, 281 | filename, 282 | imgFilename, 283 | mimeType, 284 | orgFilename, 285 | tempObj; 286 | orgFilename = e.blob.getName(); 287 | fileSize = e.blob.getBytes().length; 288 | mimeType = e.blob.getContentType(); 289 | ext = ""; 290 | switch (mimeType) { 291 | case MimeType.PNG: 292 | ext = "png"; 293 | break; 294 | case MimeType.JPEG: 295 | ext = "jpg"; 296 | break; 297 | case MimeType.GIF: 298 | ext = "gif"; 299 | break; 300 | case MimeType.BMP: 301 | e.blob = e.blob.getAs(MimeType.PNG); 302 | ext = "png"; 303 | break; 304 | default: 305 | putError.call( 306 | this, 307 | "In the current stage, this file type cannot be used." 308 | ); 309 | } 310 | filename = `xl/media/image${i + 1}.${ext}`; 311 | dupObj = dupCheckAr.filter((e) => { 312 | return ( 313 | e.orgFilename === orgFilename && 314 | e.fileSize === fileSize && 315 | e.mimeType === mimeType 316 | ); 317 | }); 318 | if (dupObj.length === 0) { 319 | imgFilename = `image${i + 1}.${ext}`; 320 | dupCheckAr.push({ 321 | filename: imgFilename, 322 | orgFilename: orgFilename, 323 | fileSize: fileSize, 324 | mimeType: mimeType, 325 | }); 326 | tempObj = { 327 | range: `A${i + 1}`, 328 | filename: imgFilename, 329 | rowIndex: i, 330 | mimeType: mimeType, 331 | from: { 332 | startRowIndex: i, 333 | endRowIndex: i + 1, 334 | startColumnIndex: 0, 335 | endColumnIndex: 1, 336 | }, 337 | to: { 338 | startRowIndex: e.range.row - 1, 339 | endRowIndex: e.range.row, 340 | startColumnIndex: e.range.column - 1, 341 | endColumnIndex: e.range.column, 342 | }, 343 | }; 344 | ar.push(tempObj); 345 | obj_[filename] = e.blob.copyBlob().setName(filename); 346 | } else { 347 | tempObj = { 348 | range: `A${i + 1}`, 349 | filename: dupObj[0].filename, 350 | rowIndex: i, 351 | mimeType: mimeType, 352 | from: { 353 | startRowIndex: i, 354 | endRowIndex: i + 1, 355 | startColumnIndex: 0, 356 | endColumnIndex: 1, 357 | }, 358 | to: { 359 | startRowIndex: e.range.row - 1, 360 | endRowIndex: e.range.row, 361 | startColumnIndex: e.range.column - 1, 362 | endColumnIndex: e.range.column, 363 | }, 364 | }; 365 | ar.push(tempObj); 366 | } 367 | return ar; 368 | }, []); 369 | }; 370 | 371 | ContentTypesXml_ = function (obj, ar) { 372 | var filename, mimeTypeToExtension, mimeTypes, n, root, xmlObj; 373 | mimeTypeToExtension = { 374 | [MimeType.PNG]: "png", 375 | [MimeType.JPEG]: "jpg", 376 | [MimeType.GIF]: "gif", 377 | }; 378 | mimeTypes = [ 379 | ...new Set( 380 | ar.map(({ mimeType }) => { 381 | return mimeType; 382 | }) 383 | ), 384 | ]; 385 | filename = "[Content_Types].xml"; 386 | xmlObj = XmlService.parse(obj[filename]); 387 | root = xmlObj.getRootElement(); 388 | n = root.getNamespace(); 389 | mimeTypes.forEach((e) => { 390 | var Default; 391 | Default = XmlService.createElement("Default", n) 392 | .setAttribute("ContentType", e) 393 | .setAttribute("Extension", mimeTypeToExtension[e]); 394 | return root.addContent(Default); 395 | }); 396 | obj[filename] = XmlService.getRawFormat().format(root); 397 | }; 398 | 399 | createSheet1Xml_ = function (obj, ar) { 400 | var filename, n, root, xmlObj; 401 | filename = "xl/worksheets/sheet1.xml"; 402 | xmlObj = XmlService.parse(obj[filename]); 403 | root = xmlObj.getRootElement(); 404 | n = root.getNamespace(); 405 | ar.forEach((e) => { 406 | var c, row; 407 | c = XmlService.createElement("c", n) 408 | .setAttribute("r", e.range) 409 | .setAttribute("s", "1"); 410 | row = XmlService.createElement("row", n) 411 | .setAttribute("r", e.rowIndex + 1) 412 | .addContent(c); 413 | return root.getChild("sheetData", n).addContent(row); 414 | }); 415 | obj[filename] = XmlService.getRawFormat().format(root); 416 | }; 417 | 418 | createDrawing1Xml_ = function (obj, ar) { 419 | var filename, n, n2, n3, root, xmlObj; 420 | filename = "xl/drawings/drawing1.xml"; 421 | xmlObj = XmlService.parse(obj[filename]); 422 | root = xmlObj.getRootElement(); 423 | n = root.getNamespace("xdr"); 424 | n2 = root.getNamespace("r"); 425 | n3 = root.getNamespace("a"); 426 | ar.forEach((e, i) => { 427 | var avLst, 428 | blip, 429 | blipFill, 430 | cNvPicPr, 431 | cNvPr, 432 | clientData, 433 | col, 434 | colOff, 435 | ext, 436 | fillRect, 437 | form, 438 | nvPicPr, 439 | oneCellAnchor, 440 | pic, 441 | prstGeom, 442 | row, 443 | rowOff, 444 | spPr, 445 | stretch; 446 | col = XmlService.createElement("col", n).setText(0); 447 | colOff = XmlService.createElement("colOff", n).setText(0); 448 | row = XmlService.createElement("row", n).setText(e.rowIndex); 449 | rowOff = XmlService.createElement("rowOff", n).setText(0); 450 | form = XmlService.createElement("from", n) 451 | .addContent(col) 452 | .addContent(colOff) 453 | .addContent(row) 454 | .addContent(rowOff); 455 | ext = XmlService.createElement("ext", n) 456 | .setAttribute("cx", "314325") 457 | .setAttribute("cy", "200025"); 458 | cNvPr = XmlService.createElement("cNvPr", n) 459 | .setAttribute("id", "0") 460 | .setAttribute("name", e.filename); 461 | cNvPicPr = XmlService.createElement("cNvPicPr", n).setAttribute( 462 | "preferRelativeResize", 463 | "0" 464 | ); 465 | nvPicPr = XmlService.createElement("nvPicPr", n) 466 | .addContent(cNvPr) 467 | .addContent(cNvPicPr); 468 | blip = XmlService.createElement("blip", n3) 469 | .setAttribute("cstate", "print") 470 | .setAttribute("embed", `rId${i + 1}`, n2); 471 | fillRect = XmlService.createElement("fillRect", n3); 472 | stretch = XmlService.createElement("stretch", n3).addContent(fillRect); 473 | blipFill = XmlService.createElement("blipFill", n) 474 | .addContent(blip) 475 | .addContent(stretch); 476 | avLst = XmlService.createElement("avLst", n3); 477 | prstGeom = XmlService.createElement("prstGeom", n3) 478 | .setAttribute("prst", "rect") 479 | .addContent(avLst); 480 | spPr = XmlService.createElement("spPr", n).addContent(prstGeom); 481 | pic = XmlService.createElement("pic", n) 482 | .addContent(nvPicPr) 483 | .addContent(blipFill) 484 | .addContent(spPr); 485 | clientData = XmlService.createElement("clientData", n).setAttribute( 486 | "fLocksWithSheet", 487 | "0" 488 | ); 489 | oneCellAnchor = XmlService.createElement("oneCellAnchor", n) 490 | .addContent(form) 491 | .addContent(ext) 492 | .addContent(pic) 493 | .addContent(clientData); 494 | return root.addContent(oneCellAnchor); 495 | }); 496 | obj[filename] = XmlService.getRawFormat().format(root); 497 | }; 498 | 499 | drawing1XmlRels_ = function (obj, ar) { 500 | var Relationships, filename, n, root; 501 | filename = "xl/drawings/_rels/drawing1.xml.rels"; 502 | root = XmlService.createDocument(); 503 | n = XmlService.getNamespace( 504 | "http://schemas.openxmlformats.org/package/2006/relationships" 505 | ); 506 | Relationships = XmlService.createElement("Relationships").setNamespace(n); 507 | ar.forEach(({ rowIndex, filename }) => { 508 | var Relationship; 509 | Relationship = XmlService.createElement("Relationship", n) 510 | .setAttribute("Id", `rId${rowIndex + 1}`) 511 | .setAttribute( 512 | "Type", 513 | "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" 514 | ) 515 | .setAttribute("Target", `../media/${filename}`); 516 | Relationships.addContent(Relationship); 517 | }); 518 | root.addContent(Relationships); 519 | obj[filename] = XmlService.getRawFormat() 520 | .format(root) 521 | .replace( 522 | /<\?xml version="1.0" encoding="UTF-8"\?>/, 523 | `` 524 | ); 525 | }; 526 | 527 | setheaderFooter = function (obj_, obj) { 528 | var f, 529 | filename, 530 | h, 531 | headerFooter, 532 | n, 533 | oddFooter, 534 | oddHeader, 535 | root, 536 | unitX, 537 | unitY, 538 | xmlObj; 539 | if (obj_.hasOwnProperty("header") || obj_.hasOwnProperty("footer")) { 540 | unitX = "pixel"; 541 | unitY = "pixel"; 542 | if ( 543 | !obj_.header.hasOwnProperty("l") && 544 | !obj_.header.hasOwnProperty("r") && 545 | !obj_.header.hasOwnProperty("c") && 546 | !obj_.footer.hasOwnProperty("l") && 547 | !obj_.footer.hasOwnProperty("r") && 548 | !obj_.footer.hasOwnProperty("c") 549 | ) { 550 | putError.call(this, "Please set header and/or footer."); 551 | } 552 | filename = "xl/worksheets/sheet1.xml"; 553 | xmlObj = XmlService.parse(obj[filename]); 554 | root = xmlObj.getRootElement(); 555 | n = root.getNamespace(); 556 | h = `&L${obj_.header.hasOwnProperty("l") ? obj_.header.l : ""}&C${ 557 | obj_.header.hasOwnProperty("c") ? obj_.header.c : "" 558 | }&R${obj_.header.hasOwnProperty("r") ? obj_.header.r : ""}`; 559 | f = `&L${obj_.footer.hasOwnProperty("l") ? obj_.footer.l : ""}&C${ 560 | obj_.footer.hasOwnProperty("c") ? obj_.footer.c : "" 561 | }&R${obj_.footer.hasOwnProperty("r") ? obj_.footer.r : ""}`; 562 | oddHeader = XmlService.createElement("oddHeader", n).setText(h); 563 | oddFooter = XmlService.createElement("oddFooter", n).setText(f); 564 | headerFooter = XmlService.createElement("headerFooter", n) 565 | .addContent(oddHeader) 566 | .addContent(oddFooter); 567 | root.addContent(headerFooter); 568 | obj[filename] = XmlService.getRawFormat().format(root); 569 | } 570 | }; 571 | 572 | xlsxObjToBlob = function (xlsxObj) { 573 | var blobs; 574 | blobs = Object.entries(xlsxObj).reduce((ar, [k, v]) => { 575 | ar.push( 576 | v.toString() === "Blob" 577 | ? v 578 | : Utilities.newBlob(v, MimeType.PLAIN_TEXT, k) 579 | ); 580 | return ar; 581 | }, []); 582 | return Utilities.zip(blobs, "temp.xlsx").setContentType( 583 | MimeType.MICROSOFT_EXCEL 584 | ); 585 | }; 586 | 587 | putError = function (m) { 588 | throw new Error(`${m}`); 589 | }; 590 | 591 | putInternalError = function (m) { 592 | throw new Error(`Internal error: ${m}`); 593 | }; 594 | 595 | return SpreadsheetAppp; 596 | }.call(this); 597 | return (r.SpreadsheetAppp = SpreadsheetAppp); 598 | })(this); 599 | -------------------------------------------------------------------------------- /WordApp.js: -------------------------------------------------------------------------------- 1 | // --- WordApp --- 2 | (function(r) { 3 | var WordApp; 4 | WordApp = (function() { 5 | var disassembleWord, getXmlObj, parsDOCX, putError, putInternalError; 6 | 7 | class WordApp { 8 | constructor(blob_) { 9 | this.name = "WordApp"; 10 | if (!blob_ || blob_.getContentType() !== MimeType.MICROSOFT_WORD) { 11 | throw new Error("Please set the blob of data of DOCX format."); 12 | } 13 | this.obj = { 14 | excel: blob_ 15 | }; 16 | this.contentTypes = "[Content_Types].xml"; 17 | this.document = "word/document.xml"; 18 | this.mainObj = {}; 19 | parsDOCX.call(this); 20 | } 21 | 22 | // --- begin methods 23 | getTableColumnWidth() { 24 | var body, n1, obj, root, xmlObj; 25 | if (this.mainObj.fileObj.hasOwnProperty(this.document)) { 26 | xmlObj = getXmlObj.call(this, this.document); 27 | root = xmlObj.getRootElement(); 28 | n1 = root.getNamespace("w"); 29 | body = root.getChild("body", n1).getChildren("tbl", n1); 30 | obj = body.map((e, i) => { 31 | var tblGrid, tblPr, tblW, temp, w; 32 | temp = { 33 | tableIndex: i, 34 | unit: "pt" 35 | }; 36 | tblPr = e.getChild("tblPr", n1); 37 | if (tblPr) { 38 | tblW = tblPr.getChild("tblW", n1); 39 | if (tblW) { 40 | w = tblW.getAttribute("w", n1); 41 | if (w) { 42 | temp.tableWidth = Number(w.getValue()) / 20; 43 | } 44 | } 45 | } 46 | tblGrid = e.getChild("tblGrid", n1); 47 | if (tblGrid) { 48 | temp.tebleColumnWidth = tblGrid.getChildren("gridCol", n1).map((f) => { 49 | return Number(f.getAttribute("w", n1).getValue()) / 20; 50 | }); 51 | } 52 | return temp; 53 | }); 54 | return obj; 55 | } 56 | } 57 | 58 | }; 59 | 60 | WordApp.name = "WordApp"; 61 | 62 | // --- end methods 63 | parsDOCX = function() { 64 | disassembleWord.call(this); 65 | }; 66 | 67 | disassembleWord = function() { 68 | var blobs; 69 | blobs = Utilities.unzip(this.obj.excel.setContentType(MimeType.ZIP)); 70 | this.mainObj.fileObj = blobs.reduce((o, b) => { 71 | return Object.assign(o, { 72 | [b.getName()]: b 73 | }); 74 | }, {}); 75 | }; 76 | 77 | getXmlObj = function(k_) { 78 | return XmlService.parse(this.mainObj.fileObj[k_].getDataAsString()); 79 | }; 80 | 81 | putError = function(m) { 82 | throw new Error(`${m}`); 83 | }; 84 | 85 | putInternalError = function(m) { 86 | throw new Error(`Internal error: ${m}`); 87 | }; 88 | 89 | return WordApp; 90 | 91 | }).call(this); 92 | return r.WordApp = WordApp; 93 | })(this); 94 | -------------------------------------------------------------------------------- /appsscript.json: -------------------------------------------------------------------------------- 1 | { 2 | "timeZone": "Asia/Tokyo", 3 | "dependencies": { 4 | "enabledAdvancedServices": [{ 5 | "userSymbol": "Docs", 6 | "serviceId": "docs", 7 | "version": "v1" 8 | }, { 9 | "userSymbol": "Drive", 10 | "serviceId": "drive", 11 | "version": "v2" 12 | }, { 13 | "userSymbol": "Slides", 14 | "serviceId": "slides", 15 | "version": "v1" 16 | }, { 17 | "userSymbol": "Sheets", 18 | "serviceId": "sheets", 19 | "version": "v4" 20 | }], 21 | "libraries": [{ 22 | "userSymbol": "ImgApp", 23 | "libraryId": "1T03nYHRho6XMWYcaumClcWr6ble65mAT8OLJqRFJ5lukPVogAN2NDl-y", 24 | "version": "8", 25 | "developmentMode": true 26 | }] 27 | }, 28 | "exceptionLogging": "STACKDRIVER", 29 | "runtimeVersion": "V8" 30 | } 31 | -------------------------------------------------------------------------------- /images/demo1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanaikech/DocsServiceApp/2b3d7171c6093630ddcb3f207a3c048157f9ac36/images/demo1.png -------------------------------------------------------------------------------- /images/fig1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanaikech/DocsServiceApp/2b3d7171c6093630ddcb3f207a3c048157f9ac36/images/fig1.png -------------------------------------------------------------------------------- /images/fig2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanaikech/DocsServiceApp/2b3d7171c6093630ddcb3f207a3c048157f9ac36/images/fig2.png -------------------------------------------------------------------------------- /images/fig3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanaikech/DocsServiceApp/2b3d7171c6093630ddcb3f207a3c048157f9ac36/images/fig3.png -------------------------------------------------------------------------------- /images/fig4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanaikech/DocsServiceApp/2b3d7171c6093630ddcb3f207a3c048157f9ac36/images/fig4.png -------------------------------------------------------------------------------- /images/fig5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanaikech/DocsServiceApp/2b3d7171c6093630ddcb3f207a3c048157f9ac36/images/fig5.png -------------------------------------------------------------------------------- /mainMethods.js: -------------------------------------------------------------------------------- 1 | /** 2 | * GitHub https://github.com/tanaikech/DocsServiceApp
3 | * Library name 4 | * @type {string} 5 | * @const {string} 6 | * @readonly 7 | */ 8 | const appName = "DocsServiceApp"; 9 | 10 | /** 11 | * @param {String} id Spreasheet ID. 12 | * @return {DocsServiceApp} 13 | */ 14 | function openBySpreadsheetId(id) { 15 | return new SpreadsheetAppp(id); 16 | } 17 | 18 | /** 19 | * @param {Object} blob Blob of Excel file (XLSX file). 20 | * @return {DocsServiceApp} 21 | */ 22 | function openByExcelFileBlob(blob) { 23 | return new ExcelApp(blob); 24 | } 25 | 26 | /** 27 | * @param {String} id Document ID. 28 | * @return {DocsServiceApp} 29 | */ 30 | function openByDocumentId(id) { 31 | return new DocumentAppp(id); 32 | } 33 | 34 | /** 35 | * @param {Object} blob Blob of Word file (DOCX file). 36 | * @return {DocsServiceApp} 37 | */ 38 | function openByWordFileBlob(blob) { 39 | return new WordApp(blob); 40 | } 41 | 42 | /** 43 | * @param {object} object Object including parameter for createing new Google Spreadsheet. 44 | * @return {string} Presentation ID of cerated Google Slides. 45 | */ 46 | function createNewSpreadsheetWithCustomHeaderFooter(object) { 47 | return new SpreadsheetAppp("create").createNewSpreadsheetWithCustomHeaderFooter(object); 48 | } 49 | 50 | /** 51 | * @param {object} object Object including parameter for createing new Google Slides. 52 | * @return {string} Presentation ID of cerated Google Slides. 53 | */ 54 | function createNewSlidesWithPageSize(object) { 55 | return new SlidesAppp("create").createNewSlidesWithPageSize(object); 56 | } 57 | 58 | // DriveApp.createFile() // This is used for automatically detected the scope of "https://www.googleapis.com/auth/drive" 59 | // SpreadsheetApp.create() // This is used for automatically detected the scope of "https://www.googleapis.com/auth/spreadsheets" 60 | // SlidesApp.create(name) // This is used for automatically detected the scope of "https://www.googleapis.com/auth/presentations" 61 | ; 62 | 63 | --------------------------------------------------------------------------------