更新指向Google文档标题的链接

时间:2019-04-30 14:43:53

标签: google-docs

在Google文档中,您可以轻松添加标题并从文档内部链接至它们。但是,当标题文本更改时,链接文本不会更改。 有没有办法改变这种行为或自动更新链接文本?

1 个答案:

答案 0 :(得分:0)

我知道这大约是1 1/2年,但这也许会有所帮助。我遇到了完全相同的问题,并编写了一个函数来更新文档中所有标题的链接。由于找不到任何内置函数或附加组件,因此唯一的方法是编写脚本。

要考虑的一些事情:

  • 这需要当前目录才能工作。如果您没有(或不需要)目录,则可以插入一个目录,然后运行该功能然后将其删除。另外,我仅使用包含页码的目录对它进行了测试。
  • 它将更新指向文档标题的链接的所有文本。但是,指向其他所有内容的链接保持不变。
  • 使用时需要您自担风险(也许可以在文档副本中试用)。我已经测试过了,但是测试本来可以更彻底。另外,这是我第一次编写文档脚本。

将其粘贴到文档的脚本编辑器中,然后运行replaceHeadingLinks。脚本无法更新的链接(因为它们链接到不再存在的标题)将在控制台中输出。

function replaceHeadingLinks() {
  var curDoc = DocumentApp.getActiveDocument();
  var links = getAllLinks_(curDoc.getBody());
  var headings = getAllHeadings_(curDoc.getBody());
  var deprecatedLinks = []; // holds all links to headings that do not exist anymore.
    
  links.forEach(function(link) {
  
    if(link.url.startsWith('#heading')) {
    
      // get the new heading text
      var newHeadingText = headings.get(link.url);
      
      // if the link does not exist anymore, we cannot update it.
      if(typeof newHeadingText !== "undefined") {
        
        var newOffset = link.startOffset + newHeadingText.length - 1;
        
        // delete the old text, insert new one and set link
        link.element.deleteText(link.startOffset, link.endOffsetInclusive);
        link.element.insertText(link.startOffset, newHeadingText);
        link.element.setLinkUrl(link.startOffset, newOffset, link.url);
      
      } else {
        deprecatedLinks.push(link);
      }
      
    }
  
  }
  ) 
  
  // error handling: show deprecated links:
  
  if(deprecatedLinks.length > 0) {
    Logger.log("Links we could not update:");
    
    for(var i = 0; i < deprecatedLinks.length; i++) {
      var link = deprecatedLinks[i];
      var oldText = link.element.getText().substring(link.startOffset, link.endOffsetInclusive);
      Logger.log("heading: " + link.url + " / description: " + oldText);
    }
  } else {
    Logger.log("all links updated");
  }
  
}


/**
 * Get an array of all LinkUrls in the document. The function is
 * recursive, and if no element is provided, it will default to
 * the active document's Body element.
 *
 * @param {Element} element The document element to operate on. 
 * .
 * @returns {Array}         Array of objects, vis
 *                              {element,
 *                               startOffset,
 *                               endOffsetInclusive, 
 *                               url}
 *
 * Credits: https://stackoverflow.com/questions/18727341/get-all-links-in-a-document/40730088
 */
function getAllLinks_(element) {
  var links = [];
  element = element || DocumentApp.getActiveDocument().getBody();

  if (element.getType() === DocumentApp.ElementType.TEXT) {
    var textObj = element.editAsText();
    var text = element.getText();
    var inUrl = false;
    var curUrl = {};
    for (var ch=0; ch < text.length; ch++) {
      var url = textObj.getLinkUrl(ch);
      if (url != null) {
        if (!inUrl) {
          // We are now!
          inUrl = true;
          curUrl = {};
          curUrl.element = element;
          curUrl.url = String( url ); // grab a copy
          curUrl.startOffset = ch;
        }
        else {
          curUrl.endOffsetInclusive = ch;
        }          
      }
      else {
        if (inUrl) {
          // Not any more, we're not.
          inUrl = false;
          links.push(curUrl);  // add to links
          curUrl = {};
        }
      }
    }
    // edge case: link is at the end of a paragraph
    // check if object is empty
    if(inUrl && (Object.keys(curUrl).length !== 0 || curUrl.constructor !== Object)) {
      links.push(curUrl);  // add to links
      curUrl = {};
    }
  }
  else {
    // only traverse if the element is traversable
    if(typeof element.getNumChildren !== "undefined") {
        var numChildren = element.getNumChildren();
      
        for (var i=0; i<numChildren; i++) {
  
        // exclude Table of Contents
       
        child = element.getChild(i);
        if(child.getType() !== DocumentApp.ElementType.TABLE_OF_CONTENTS) {
          links = links.concat(getAllLinks_(element.getChild(i)));
        }
      }
    }
  }

  return links;
}


/**
 * returns a map of all headings within an element. The map key
 * is the heading ID, such as h.q1xuchg2smrk
 *
 * THIS REQUIRES A CURRENT TABLE OF CONTENTS IN THE DOCUMENT TO WORK PROPERLY.
 *
 * @param {Element} element The document element to operate on. 
 * .
 * @returns {Map} Map with heading ID as key and the heading element as value.
 */
function getAllHeadings_(element) {
  
  var headingsMap = new Map();
  
  var p = element.findElement(DocumentApp.ElementType.TABLE_OF_CONTENTS).getElement();

  if(p !== null) {
      var toc = p.asTableOfContents();
      for (var ti = 0; ti < toc.getNumChildren(); ti++) {
        
        var itemToc = toc.getChild(ti).asParagraph().getChild(0).asText();
        var itemText = itemToc.getText();
        var itemUrl =  itemToc.getLinkUrl(0);
        var itemDesc = null;
    
        // strip the line numbers if TOC contains line numbers
        var itemText = itemText.match(/(.*)\t/)[1];
        headingsMap.set(itemUrl,itemText);
      }
    }
    return headingsMap;
}