【问题标题】:How do I format text I am copying from Google Document in Google App Script?如何在 Google App Script 中格式化从 Google Document 复制的文本?
【发布时间】:2013-07-10 16:19:18
【问题描述】:

我正在尝试从一个文档中复制格式化文本并将其粘贴到另一个文档中。我想获取整个文档并通过 Google App Script 将其添加到另一个文档中。

调用body.getText() 满足我的用例,但将文本作为字符串获取,而不是格式化。

如果能够将格式化文本从一个文档复制到另一个文档,那就太好了。

编辑: 接受我目前编写的更多代码的建议。几乎完全按照另一个答案,我仍然只得到文本而不是格式。

  for(var i = 0; i < numElements; ++i) {
  var element = copyBody.getChild(i)
  var type = element.getType();
   if (type == DocumentApp.ElementType.PARAGRAPH)
   {
     var newElement = element.copy().asParagraph();
     newBody.appendParagraph(newElement); 
   }
   else if(type == DocumentApp.ElementType.TABLE)
   {
     var newElement = element.copy().asTable();
     newBody.appendTable(newElement); 
   }
   else if(type == DocumentApp.ElementType.LIST_ITEM)
   {     
     var newElement = element.copy().asListItem();
     newBody.appendListItem(newElement);
   }
    else{
    Logger.log("WRONG ELEMENT")    
    }
  }    

【问题讨论】:

  • "Almost" - Henrique 的函数不使用.asParagraph() 等。尝试使用普通的.copy()。我的答案中的脚本按原样工作。

标签: google-apps-script google-docs


【解决方案1】:

This answer 覆盖它。

您需要遍历源文档的元素,将每个元素附加到目标文档。您不会复制 Text 版本的段落等,而是复制整个元素,包括格式等。

脚本

由于 Documents 现在支持可编程 UI 元素,这里有一个基于 Henrique 之前的回答(上图)的脚本,其中包括一个自定义菜单来驱动文档的合并。您可以选择在附加文档之间包含分页符 - 如果您尝试创建多章文档,这很有用。

此脚本必须包含在文档中(或者没有适合您的 UI)!

/**
 * The onOpen function runs automatically when the Google Docs document is
 * opened. 
 */
function onOpen() {
  DocumentApp.getUi().createMenu('Custom Menu')
      .addItem('Append Document','appendDoc')
      .addToUi();
}

/**
 * Shows a custom HTML user interface in a dialog above the Google Docs editor.
 */
function appendDoc() {
  // HTML for form is rendered inline here.
  var html =
      '<script>'
  +     'function showOutput(message) {'
  +       'var div = document.getElementById("output");'
  +       'div.innerHTML = message;'
  +     '}'
  +   '</script>'
  +   '<form id="appendDoc">'
  +     'Source Document ID: <input type="text" size=60 name="docID"><br>'
  +     'Insert Page Break: <input type="checkbox" name="pagebreak" value="pagebreak">'
  +     '<input type="button" value="Begin" '
  +       'onclick="google.script.run.withSuccessHandler(showOutput).processAppendDocForm(this.parentNode)" />'
  +   '</form>' 
  +   '<br>'
  +   '<div id="output"></div>'

  DocumentApp.getUi().showDialog(
    HtmlService.createHtmlOutput(html)
               .setTitle('Append Document')
               .setWidth(400 /* pixels */)
               .setHeight(150 /* pixels */));
}

/**
 * Handler called when appendDoc form submitted.
 */
function processAppendDocForm(formObject) {
  Logger.log(JSON.stringify(formObject));
  var pagebreak = (formObject.pagebreak == 'pagebreak');
  mergeDocs([DocumentApp.getActiveDocument().getId(),formObject.docID],pagebreak);
  return "Document appended.";
}

/**
 * Updates first document in list by appending all others.
 *
 * Modified version of Henrique's mergeDocs().
 * https://stackoverflow.com/a/10833393/1677912
 *
 * @param {Array} docIDs      Array of documents to merge.
 * @param {Boolean} pagebreak Set true if a page break is desired
 *                              between appended documents.
 */
function mergeDocs(docIDs,pagebreak) {
  var baseDoc = DocumentApp.openById(docIDs[0]);
  var body = baseDoc.getBody();

  for( var i = 1; i < docIDs.length; ++i ) {
    if (pagebreak) body.appendPageBreak();
    var otherBody = DocumentApp.openById(docIDs[i]).getBody(); 
    Logger.log(otherBody.getAttributes());
    var totalElements = otherBody.getNumChildren();
    var latestElement;
    for( var j = 0; j < totalElements; ++j ) {
      var element = otherBody.getChild(j).copy();
      var attributes = otherBody.getChild(j).getAttributes();
      // Log attributes for comparison
      Logger.log(attributes);
      Logger.log(element.getAttributes());
      var type = element.getType(); 
      if (type == DocumentApp.ElementType.PARAGRAPH) {
        if (element.asParagraph().getNumChildren() != 0 && element.asParagraph().getChild(0).getType() == DocumentApp.ElementType.INLINE_IMAGE) {
          var pictattr = element.asParagraph().getChild(0).asInlineImage().getAttributes();
          var blob = element.asParagraph().getChild(0).asInlineImage().getBlob();
          // Image attributes, e.g. size, do not survive the copy, and need to be applied separately
          latestElement = body.appendImage(blob);
          latestElement.setAttributes(clean(pictattr));
        }
        else latestElement = body.appendParagraph(element);
      }
      else if( type == DocumentApp.ElementType.TABLE )
        latestElement = body.appendTable(element);
      else if( type == DocumentApp.ElementType.LIST_ITEM )
        latestElement = body.appendListItem(element);
      else
        throw new Error("Unsupported element type: "+type);
      // If you find that element attributes are not coming through, uncomment the following
      // line to explicitly copy the element attributes from the original doc.
      //latestElement.setAttributes(clean(attributes));
    }
  }
}


/**
 * Remove null attributes in style object, obtained by call to
 * .getAttributes().
 * https://code.google.com/p/google-apps-script-issues/issues/detail?id=2899
 */
function clean(style) {
  for (var attr in style) {
    if (style[attr] == null) delete style[attr];
  }
  return style;
}

编辑: 采用 Serge 的回答中的内联图像处理,处理图像大小属性。正如 cmets 所指出的,一些属性在附加中被捕获存在问题,因此引入了 clean() 辅助函数并使用了 .setAttributes()然而,你会注意到对.setAttributes() 的调用被注释掉了;那是因为它也有一个副作用,会删除一些格式。您可以选择处理哪种烦恼。

【讨论】:

  • 嗨,Mogsdad,我出于好奇测试了您的代码,主要是因为我喜欢脚本中的 html 构造,但弹出面板中出现错误:Google Drive 遇到错误。如果重新加载页面没有帮助,请报告错误。是一时的失败吗?对你有用吗?
  • 脚本服务刚刚打嗝......我看到了几分钟。 (你先授权脚本了吗?)
  • 我在同一条船上服务。我认为 getActiveSection() 是一个不推荐使用的方法。
  • 是的,但它仍然有效。我将“baseDoc”的getActiveSection() 更改为.getBody(),这样我就可以在玩游戏时获得自动完成功能以进一步工作,但错过了第二个实例。我这里已经更新了,但由于服务宕机,目前无法触摸真正的脚本进行测试。
  • 我逐字复制并粘贴了您的代码,目前格式不完全正确。例如,标题的字体不同。项目符号列表也不是项目符号,只是缩进。
【解决方案2】:

这里是对 Mogsdad 脚本的轻微改进,可以同时复制内联图像。

唯一的问题是,如果调整了图像的大小,它不会在副本中保留这个新大小,图像会以其原始大小显示......现在不知道如何解决这个问题。

function mergeDocs(docIDs,pagebreak) {
  var baseDoc = DocumentApp.openById(docIDs[0]);
  var body = baseDoc.getBody();

  for( var i = 1; i < docIDs.length; ++i ) {
    if (pagebreak) body.appendPageBreak();
    var otherBody = DocumentApp.openById(docIDs[i]).getBody();
    var totalElements = otherBody.getNumChildren();
    for( var j = 0; j < totalElements; ++j ) {
      var element = otherBody.getChild(j).copy();
      var type = element.getType(); 
      if (type == DocumentApp.ElementType.PARAGRAPH) {
        if (element.asParagraph().getNumChildren() != 0 && element.asParagraph().getChild(0).getType() == DocumentApp.ElementType.INLINE_IMAGE) {
          var blob = element.asParagraph().getChild(0).asInlineImage().getBlob();
          body.appendImage(blob);
        }
        else body.appendParagraph(element.asParagraph());
      }
      else if( type == DocumentApp.ElementType.TABLE )
        body.appendTable(element);
      else if( type == DocumentApp.ElementType.LIST_ITEM )
        body.appendListItem(element);
      else
        throw new Error("According to the doc this type couldn't appear in the body: "+type);
    }
  }
}

您可以在此文档 ID 上对其进行测试:1E6yoROb52QjICsEbGVXIBdz8KhdFU_5gimWlJUbu8DI

(执行成功 [43.896 秒总运行时间])!!!耐心点!

此代码来自from this other post

【讨论】:

  • Serge,我发现了图像调整大小的问题,并更新了答案中的代码。
  • 现在...也许您可以修复我的表单处理,以便提供进度反馈!
  • 我对 html 服务不够满意,在 UiApp 中,我会在几秒钟内使用 clientHandler 和动画 gif 完成它,但我仍然有很多东西要学习这种新的“思维方式”我可以阅读您的代码,但我还不能写它;-)(一个好点:相反会更糟,对吧?^^)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-01
  • 1970-01-01
  • 2017-06-13
  • 2023-03-19
  • 1970-01-01
相关资源
最近更新 更多