我相信您的目标如下。
- 您想要使用 Google Apps 脚本和 Javascript 将范围导出为图像。
- 为了实现这一点,在这个问题中,您想要计算所选单元格区域的行高。
问题和解决方法:
正如我们在评论中讨论的那样,在现阶段,当试图获取单元格区域的正确行高时,存在以下几个问题。
- 当单元格使用边框时,行高+边框大小似乎与导出的结果不同。Ref https://stackoverflow.com/questions/73204237/google-sheets-how-to-get-the-real-range-size-in-pixels#comment129310159_73204237
- 像素大小可能不会随行高和边框大小的值线性变化。Ref https://stackoverflow.com/questions/73204237/google-sheets-how-to-get-the-real-range-size-in-pixels#comment129315115_73204237
- 当我测试包括边框在内的单元格尺寸时,我认为高度和宽度之间的尺寸变化趋势可能不同。Ref https://stackoverflow.com/questions/73204237/google-sheets-how-to-get-the-real-range-size-in-pixels#comment129375393_73204237
- 当行高为默认值(getRowHeight 中的 21)并且单元格中的文本字体大小增大时,getRowHeight 检索到的值不会从 21 发生变化。Ref https://stackoverflow.com/questions/73204237/google-sheets-how-to-get-the-real-range-size-in-pixels#comment129375393_73204237
-
There is also issue with wrapping text inside a cell which on my experience also causes errors in a pixel size of cell.
Ref https://stackoverflow.com/questions/73204237/google-sheets-how-to-get-the-real-range-size-in-pixels#comment129378643_73204237
- 从你的问题来看,当选定的单元格范围很大时,页面数超过2。在这种情况下,所有页面都无法正确合并为图像。
从上述情况来看,我担心获得所选单元格的正确大小可能很困难。所以,我建议将其作为图像处理来处理。Ref https://stackoverflow.com/questions/73204237/google-sheets-how-to-get-the-real-range-size-in-pixels#comment129378735_73204237我想,当这个过程与图像处理一起运行时,上述问题也许可以避免。
但不幸的是,为了将其作为图像处理进行处理,Google Apps 脚本中没有内置方法。但是,幸运的是,在您的情况下,Javascript 似乎可以在对话框中使用。因此,我创建了一个 Javascript 库来实现图像处理这一过程。Ref https://github.com/tanaikech/CropImageByBorder_js
使用该Javascript库时,示例演示如下。
Usage:
1. 准备一个电子表格。
请创建一个新的电子表格并在单元格中输入几个值。
2. 示例脚本。
请将以下脚本复制并粘贴到电子表格的脚本编辑器中。
Google Apps 脚本端:Code.gs
function getActiveRange_(ss, borderColor) {
const space = 5;
const sheet = ss.getActiveSheet();
const range = sheet.getActiveRange();
const obj = { startRow: range.getRow(), startCol: range.getColumn(), endRow: range.getLastRow(), endCol: range.getLastColumn() };
const temp = sheet.copyTo(ss);
const r = temp.getDataRange();
r.copyTo(r, { contentsOnly: true });
temp.insertRowAfter(obj.endRow).insertRowBefore(obj.startRow).insertColumnAfter(obj.endCol).insertColumnBefore(obj.startCol);
obj.startRow += 1;
obj.endRow += 1;
obj.startCol += 1;
obj.endCol += 1;
temp.setRowHeight(obj.startRow - 1, space).setColumnWidth(obj.startCol - 1, space).setRowHeight(obj.endRow + 1, space).setColumnWidth(obj.endCol + 1, space);
const maxRow = temp.getMaxRows();
const maxCol = temp.getMaxColumns();
if (obj.startRow + 1 < maxRow) {
temp.deleteRows(obj.endRow + 2, maxRow - (obj.endRow + 1));
}
if (obj.startCol + 1 < maxCol) {
temp.deleteColumns(obj.endCol + 2, maxCol - (obj.endCol + 1));
}
if (obj.startRow - 1 > 1) {
temp.deleteRows(1, obj.startRow - 2);
}
if (obj.startCol - 1 > 1) {
temp.deleteColumns(1, obj.startCol - 2);
}
const mRow = temp.getMaxRows();
const mCol = temp.getMaxColumns();
const clearRanges = [[1, 1, mRow], [1, obj.endCol, mRow], [1, 1, 1, mCol], [obj.endRow, 1, 1, mCol]];
temp.getRangeList(clearRanges.map(r => temp.getRange(...r).getA1Notation())).clear();
temp.getRange(1, 1, 1, mCol).setBorder(true, null, null, null, null, null, borderColor, SpreadsheetApp.BorderStyle.SOLID);
temp.getRange(mRow, 1, 1, mCol).setBorder(null, null, true, null, null, null, borderColor, SpreadsheetApp.BorderStyle.SOLID);
SpreadsheetApp.flush();
return temp;
}
function getPDF_(ss, temp) {
const url = ss.getUrl().replace(/\/edit.*$/, '')
+ '/export?exportFormat=pdf&format=pdf'
// + '&size=20x20' // If you want to increase the size of one page, please use this. But, when the page size is increased, the process time becomes long. Please be careful about this.
+ '&scale=2'
+ '&top_margin=0.05'
+ '&bottom_margin=0'
+ '&left_margin=0.05'
+ '&right_margin=0'
+ '&sheetnames=false'
+ '&printtitle=false'
+ '&pagenum=UNDEFINED'
+ 'horizontal_alignment=LEFT'
+ '&gridlines=false'
+ "&fmcmd=12"
+ '&fzr=FALSE'
+ '&gid=' + temp.getSheetId();
const res = UrlFetchApp.fetch(url, { headers: { authorization: "Bearer " + ScriptApp.getOAuthToken() } });
return "data:application/pdf;base64," + Utilities.base64Encode(res.getContent());
}
// Please run this function.
function main() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const temp = getActiveRange_(ss, "#000000");
const base64 = getPDF_(ss, temp);
const htmltext = HtmlService.createTemplateFromFile('index').evaluate().getContent();
htmltext = htmltext.replace(/IMPORT_PDF_URL/m, base64);
const html = HtmlService.createTemplate(htmltext).evaluate().setSandboxMode(HtmlService.SandboxMode.NATIVE);
SpreadsheetApp.getUi().showModalDialog(html, 'sample');
ss.deleteSheet(temp);
}
function saveFile(data) {
const blob = Utilities.newBlob(Utilities.base64Decode(data), MimeType.PNG, "sample.png");
return DriveApp.createFile(blob).getId();
}
HTML 和 JavaScript 方面:index.gs
在这里,我使用了一个 Javascript 库CropImageByBorder_js https://github.com/tanaikech/CropImageByBorder_js将其作为图像处理进行处理。
<script src="//mozilla.github.io/pdf.js/build/pdf.js"></script>
<script src="https://cdn.jsdelivr.net/gh/tanaikech/CropImageByBorder_js@latest/cropImageByBorder_js.min.js"></script>
<canvas id="canvas"></canvas>
<script>
var pdfjsLib = window['pdfjs-dist/build/pdf'];
pdfjsLib.GlobalWorkerOptions.workerSrc = '//mozilla.github.io/pdf.js/build/pdf.worker.js';
const base64 = 'IMPORT_PDF_URL'; //Loaading the PDF from URL
const cvs = document.getElementById("canvas");
pdfjsLib.getDocument(base64).promise.then(pdf => {
const {numPages} = pdf;
if (numPages > 1) {
throw new Error("Sorry. In the current stage, this sample script can be used for one page of PDF data. So, please change the selected range to smaller.")
}
pdf.getPage(1).then(page => {
const viewport = page.getViewport({scale: 2});
cvs.height = viewport.height;
cvs.width = viewport.width;
const ctx = cvs.getContext('2d');
const renderContext = { canvasContext: ctx, viewport: viewport };
page.render(renderContext).promise.then(async function() {
const obj = { borderColor: "#000000", base64Data: cvs.toDataURL() };
const base64 = await CropImageByBorder.getInnerImage(obj).catch(err => console.log(err));
const img = new Image();
img.src = base64;
img.onload = function () {
cvs.width = img.naturalWidth;
cvs.height = img.naturalHeight;
ctx.drawImage(img, 0, 0);
}
google.script.run.withSuccessHandler(id => console.log(id)).saveFile(base64.split(",").pop());
});
});
});
</script>
3. 测试
当您测试此脚本时,请选择单元格并运行main()
。这样,所选单元格将作为图像 (PNG) 导出到根文件夹,如下所示。在这种情况下,您可以看到上面的演示。
4. Flow.
在此示例脚本中,使用以下流程。
- 手动选择单元格,然后运行脚本
main()
.
- 在脚本中,由单行和单列包围的选定单元格被创建为临时表。
- 将临时表导出为 Base64 的 PDF 数据。这里,PDF数据被发送到Javascript端。
- 使用 PDF.js 将 PDF 数据的第一页转换为图像。
- 使用 CropImageByBorder_js 裁剪选定的单元格,并将结果图像返回到 Google Apps 脚本端。
- 将图像作为文件保存到 Google 云端硬盘。
局限性:
- 在此示例脚本中,假设将选定范围放在一个 PDF 页面上。因此,当您选择较大范围时,当 PDF 页数超过 2 时,不幸的是,该脚本无法使用。所以,请小心这一点。
- 而且,在本例中,Javascript 用于对话框。因此,当您使用此示例脚本时,需要打开电子表格并选择单元格并运行脚本。
Note:
- 在您的显示脚本中,为了使用通过 PDF.js 创建的 PDF 数据,需要公开共享电子表格。但是,对于 PDF.js,似乎可以直接使用数据 URL。因此,在此示例脚本中,创建的 PDF 用作数据 URL (base64)。这样,就不需要公开共享电子表格。
参考:
- PDF.js https://mozilla.github.io/pdf.js/
- CropImageByBorder_js https://github.com/tanaikech/CropImageByBorder_js