Max*_*rov 6 javascript google-sheets google-apps-script
我的脚本将选定的范围转换为图像,请参阅。它首先创建一个公共 PDF URL,然后将其转换为 PNG。
\n\n它适用于小范围(10-20 行),并创建包含图像、图表、迷你图和格式的镜头。
\n问题在于大范围(100-1000 行)。它们包含未知大小的边框,我无法计算它。
\n\n粗边框会使行更高,因此图像不适合。
\n\n如果我们没有边框或边框较薄,则实际图像尺寸会比计算的尺寸小一些。这会在图像下方创建一个空白区域。
\n我的代码示例用于获取范围大小(以像素为单位):
\n // get row height in pixels\n var h = 0;\n for (var i = rownum; i <= rownum2; i++) {\n if (i <= options.measure_limit) {\n size = sheet.getRowHeight(i);\n }\n h += size\n /** manual correction */\n if (size === 2) {\n h-=1;\n } else {\n // h -= 0.42; /** TODO \xe2\x86\x92 test the range to make it fit any range */\n }\n \n if ((i % 50) === 0 && i <= options.measure_limit) {\n file.toast(\n \'Done \' + i + \' rows of \' + rownum2,\n \'\xe2\x86\x95Measuring height...\');\n }\n }\n if (i > options.measure_limit) {\n file.toast(\n \'Estimation: all other rows are the same size\',\n \'\xe2\x86\x95Measuring height...\');\n }\nRun Code Online (Sandbox Code Playgroud)\n如您所见,我必须循环所有行,这是效率极低的。我很高兴听到您对代码优化的想法。现在它循环前 150 行,接下来假设所有其他行具有相同的高度。
\n“小”范围是您可以在屏幕上看到的范围。“大”范围有 100 多行,因此不适合普通屏幕。当我创建屏幕截图时,我测试了所有可能的范围大小。
\nCase1 - 无边框或薄边框
\n如果我选择一个大范围,我会得到图像,并看到它的底部有一个空白。这意味着图像的实际大小比我通过调用从脚本中获得的大小略小sheet.getRowHeight(i)。
Case1 - 粗边框
\n如果我选择一个大范围,我会得到图像,并且看到并非我选择的所有行都在该图像上。该范围底部的一些行丢失。这意味着当我添加粗边框时,行的实际大小比我通过调用从脚本中获得的行大sheet.getRowHeight(i)。
我很高兴听到任何想法,包括通过 JavaScript 破解来删除图像下方的空白区域。如果目前不可能,请同时回答并提供文档链接。
\n我相信您的目标如下。
正如我们在评论中讨论的那样,在现阶段,当试图获取单元格区域的正确行高时,存在以下几个问题。
There is also issue with wrapping text inside a cell which on my experience also causes errors in a pixel size of cell. 参考号从上述情况来看,我担心获得所选单元格的正确大小可能很困难。所以,我建议将其作为图像处理来处理。Ref我认为当这个过程与图像处理一起运行时,上述问题也许可以避免。
但不幸的是,为了将其作为图像处理进行处理,Google Apps 脚本中没有内置方法。但是,幸运的是,在您的情况下,Javascript 似乎可以在对话框中使用。因此,我创建了一个 Javascript 库来实现图像处理这一过程。参考号
使用该Javascript库时,示例演示如下。
请创建一个新的电子表格并在单元格中输入几个值。
请将以下脚本复制并粘贴到电子表格的脚本编辑器中。
Code.gsfunction 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);
let 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();
}
Run Code Online (Sandbox Code Playgroud)
index.gs在这里,我使用了CropImageByBorder_js的 Javascript 库来将其处理为图像处理。
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.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 = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.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>
Run Code Online (Sandbox Code Playgroud)
当您测试此脚本时,请选择单元格并运行main()。这样,所选单元格将作为图像 (PNG) 导出到根文件夹,如下所示。在这种情况下,您可以看到上面的演示。
在此示例脚本中,使用以下流程。
main()。在您的显示脚本中,为了使用通过 PDF.js 创建的 PDF 数据,需要公开共享电子表格。但是,对于 PDF.js,似乎可以直接使用数据 URL。因此,在此示例脚本中,创建的 PDF 用作数据 URL (base64)。这样,就不需要公开共享电子表格。
更新于 2024 年 1 月 25 日。
| 归档时间: |
|
| 查看次数: |
1011 次 |
| 最近记录: |