谷歌表格。如何获取实际范围大小(以像素为单位)

2024-02-20

我的脚本将选定的范围转换为图像,请参见 https://docs.google.com/spreadsheets/d/1qNQ6iCMoBCQgJzBB63ymtBQ6BedXZFhjgZZKGItjeVA/copy。它首先创建一个公共 PDF URL,然后将其转换为 PNG。

它适用于小范围(10-20 行),并创建包含图像、图表、迷你图和格式的镜头。

问题在于大范围(100-1000 行)。它们包含未知大小的边框,我无法计算它。

粗边框会使行更高,因此图像不适合。

如果我们没有边框或边框较薄,则实际图像尺寸会比计算的尺寸小一些。这会在图像下方创建一个空白空间。

我的代码示例用于获取范围大小(以像素为单位):

  // get row height in pixels
  var h = 0;
  for (var i = rownum; i <= rownum2; i++) {
    if (i <= options.measure_limit) {
      size = sheet.getRowHeight(i);
    }
    h += size
    /** manual correction */
    if (size === 2) {
      h-=1;
    } else {
      // h -= 0.42; /** TODO → test the range to make it fit any range */
    }
    
    if ((i % 50) === 0 &&  i <= options.measure_limit) {
      file.toast(
        'Done ' + i + ' rows of ' + rownum2,
        '↕????Measuring height...');
    }
  }
  if (i > options.measure_limit) {
    file.toast(
      'Estimation: all other rows are the same size',
      '↕????Measuring height...');
  }

如您所见,我必须循环所有行,这是效率极低的。我很高兴听到您对代码优化的想法。现在它循环前 150 行,接下来假设所有其他行具有相同的高度。

示例情况

“小”范围是您可以在屏幕上看到的范围。 “大”范围有 100 多行,因此不适合普通屏幕。当我创建屏幕截图时,我测试了所有可能的范围大小。

Case1 - 无边框或薄边框

如果我选择一个大范围,我会得到图像,并看到它的底部有一个空白。这意味着图像的实际大小比我通过调用从脚本中获得的大小略小sheet.getRowHeight(i).

Case1 - 粗边框

如果我选择一个大范围,我会得到图像,并且看到并非我选择的所有行都在该图像上。该范围底部的一些行丢失。这意味着当我添加粗边框时,行的实际大小比我通过调用从脚本中获得的行大sheet.getRowHeight(i).

结论

我很高兴听到任何想法,包括通过 JavaScript 破解来删除图像下方的空白区域。如果目前不可能,请同时回答并提供文档链接。


我相信您的目标如下。

  • 您想要使用 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.

在此示例脚本中,使用以下流程。

  1. 手动选择单元格,然后运行脚本main().
  2. 在脚本中,由单行和单列包围的选定单元格被创建为临时表。
  3. 将临时表导出为 Base64 的 PDF 数据。这里,PDF数据被发送到Javascript端。
  4. 使用 PDF.js 将 PDF 数据的第一页转换为图像。
  5. 使用 CropImageByBorder_js 裁剪选定的单元格,并将结果图像返回到 Google Apps 脚本端。
  6. 将图像作为文件保存到 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
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

谷歌表格。如何获取实际范围大小(以像素为单位) 的相关文章

随机推荐

  • 形成正则表达式来解析数字表达式

    我正在尝试解析字符串形式的数字比较 我想标记一个字符串 例如45 30 2 lt 50这样得到的组是45 30 2 lt and 50 我知道我可以将我的组定义为 w 对于数字项 对于括号内的术语 lt gt 1 2 对于运营商条款 但我不
  • 将 URL 参数添加到 Nutch/Solr 索引和搜索结果

    我找不到任何关于如何设置 nutch 来不过滤 删除我的 URL 参数的提示 我想抓取一些页面并为其建立索引 其中大量内容隐藏在相同的基本 URL 后面 例如 news jsp id 1 news jsp id 2 news jsp id
  • 所有音频频率

    我怎样才能获得 可能吗 所有音频现在都在系统中播放以编写一些声音可视化工具 有没有c 或c 库 提前致谢 基本步骤是 将窗函数应用于样本块 例如 1024 个样本 汉宁窗 对加窗样本执行实数到复数 FFT 取每个 FFT 输出 bin 的幅
  • Chrome 53 在第二页及以后的页面上打印两次表格标题的解决方法?

    我网站的用户需要能够打印包含第一页打印内容和第二页表格的网页 精简版本是 jsFiddle athttps jsfiddle net jaydeedub n3qhvtvx 25 https jsfiddle net jaydeedub n3
  • 根据另一列的排名计算一列的总和

    我有一个数据集 Security market value return Quintile 1 0 07 100 3 2 0 10 88 2 3 0 08 78 1 4 0 12 59 1 5 0 20 106 4 6 0 04 94 3
  • 我可以在运行时指定maven project.artifactId吗?

    我有一个 Maven POM 我想将其用作模板 根据我传递给它的参数生成具有不同名称的工件 但我不知道如何在运行时指定artifactId 如果我参数化
  • 用于 python 3.X 的 OpenCV

    有谁知道我是否可以在 python 3 X 中使用 OpenCV 库 或者我应该坚持使用scikit image用于物体识别之类的任务 我想尝试一下 openCV 但去年决定从 2 7 开始切换 我在这里搜索了数据包 http www lf
  • Java 中的 Windows 7 任务栏进度条

    我想知道是否可以像Windows资源管理器那样在进行文件操作时在任务栏上显示进度条 我看到很多例子 但都是C 的 SWT 不会削减它 我发现这个功能包含在Java 9 它是一部分AWT而且使用起来非常简单 这是一个简短的例子 import
  • 使用 Pandoc 和 Markdown 时出现紧密列表错误

    我在 Mac OS 10 12 上使用 pandoc v 1 18 将此 Markdown 文件转换为 pdf 使用简单的命令行 A list that does not work one two three 我收到此错误 Undefine
  • 使用 R 扩展时 NetLogo BehaviourSpace 崩溃

    我正在我的机器上运行动物饲养场的模型 我已将 R 扩展添加到 NetLogo 代码中 以计算每个家庭范围的最小凸多边形 当我在多个内核上的BehaviorSpace 中运行模型时 NetLogo 将在几个时间步骤后消失 即停止运行 我在 5
  • Visual Studio Code 中 Spring Boot 的热插拔

    我刚刚使用 Angular 4 启动了一个 Spring Boot 项目 对于 Angular 我可以运行npm start以便 NPM 编译每个更改 但我不知道如何为 Spring Boot 做到这一点 我可以找到一些 Eclipse I
  • Woocommerce 添加到购物车按钮重定向到结帐

    我使用插件 woocommerce 创建了一个电子商务 我只出售订阅 因此 cart 页面毫无用处 我正在尝试摆脱它 以便当我的客户单击 添加到购物车 按钮时 他最终会进入结帐页面 在 WooCommerce 3 6 或更高版本中 您可以使
  • 杰青 |更新由`select`选择的数组元素

    在 JSON 数组中 我想根据节点的值选择一个数组元素 然后更新同一数组元素中的不同节点 例如 在下面的 JSON 中 apiVersion vlabs properties orchestratorProfile orchestrator
  • 如何在屏幕旋转后禁用/避免片段自定义动画

    我刚刚发现 每次在片段上 setRetainInstance true 时 它都会按预期工作 保留片段数据 但这会导致片段的自定义动画在屏幕旋转后再次执行 有没有办法避免 禁用屏幕旋转时的这些动画 该片段是使用以下动画创建的 setCust
  • 如何放置我的网页以便我可以在谷歌搜索中找到我的页面? [关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 如何上传我的网页以便我可以在 Google 搜索中找到我的网页 搜索引擎优化可能是一个耗时的过程 但其中有几个因素 您的网站应该具有良好的关键字丰富的
  • 在Python中将命名列表向量从R翻译成rpy2?

    python 中的 Rpy2 中的以下 R 代码相当于什么 Var1 c navy darkgreen names Var1 c Class1 Class2 ann colors list Var1 Var1 目前尚不清楚是什么ann co
  • php ssh2_exec 不执行“su”命令

    我在 php 的 ssh2 中获得了很多乐趣 我正在通过 ssh ing 进入 localhost 运行 ubuntu 进行测试 我已经成功地使用我的用户名 而不是 root 进行连接和身份验证 并且一些命令 例如 ls 返回一些信息 这是
  • 单击 iAd 时暂停 SpriteKit 场景

    我正在研究一个SpriteKit项目 我正在努力在点击 iAd 时暂停游戏 并在点击 iAd 时取消暂停iAd被解雇 主要问题是从 ViewController 暂停场景 我知道如果我想从 ViewController 暂停场景Scene
  • 使用子进程时如何限制程序的执行时间?

    我想使用子进程来运行程序 并且需要限制执行时间 例如 如果它运行超过2秒我想杀死它 对于普通程序 kill 效果很好 但如果我尝试跑步 usr bin time something kill 并不能真正杀死程序 我下面的代码似乎不能很好地工
  • 谷歌表格。如何获取实际范围大小(以像素为单位)

    我的脚本将选定的范围转换为图像 请参见 https docs google com spreadsheets d 1qNQ6iCMoBCQgJzBB63ymtBQ6BedXZFhjgZZKGItjeVA copy 它首先创建一个公共 PDF