Googleスライドのグループ操作

スライドのグループ化された要素の特定の文字列を変更するためのコードを書きました。
結構意外とハマりポイントがあってもう書きたくないのでメモ。

実際はスプレッドシート情報を反映させるという使い方をしています。

グループ化せずに処理することも可能ですが、
グループ化していた方が、指定したスライドで `getPageElements()`したときに
取得する要素数が少なく、グループ内の構造と処理内容が同じであれば、
グループ化するメリットはあるかなと思っています。

# グループ内の特定の文字を変更

元々getTitleでやっていましたが、オブジェクトが変わるとタイトルは消えてしまうので、
記載されているテキストが一致したら変更するというコードに変更しました。


![図1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428239/559a64da-c082-c9b7-ffaf-122823810c69.png)


```javascript:
function myFunction() {
  const pre = SlidesApp.getActivePresentation();
  const slide = pre.getSlides()[1]; //2ページ目取得
  const elems = slide.getPageElements();
  
  for(const elem of elems) {
    if(elem.getPageElementType().toString() == 'GROUP') {
      replaceText_(elem);
    }
  }
}

/**
* グループのオブジェクトを渡して、各子要素のテキストが一致した場合に置換
*
* @param {Object} グループのオブジェクト
*/
function replaceText_(groupElem) {
  const elemsInGroup = groupElem.asGroup().getChildren();
  let text;
  
  for(const elem of elemsInGroup) {
    console.log(elem.getPageElementType().toString());
    if (elem.getPageElementType().toString() !== 'SHAPE') continue;
    text = elem.asShape().getText();

    switch (text.asString()) {
      case 'PJ名\n':
        text.replaceAllText(text.asString(), 'PJ名_変更');
        break;
      case '氏名\n':
        text.replaceAllText(text.asString(), '氏名_変更');
        break;
      case 'しめい\n':
        text.replaceAllText(text.asString(), 'しめい_変更');
        break;
      case '目標\n':
        text.replaceAllText(text.asString(), '目標_変更');
        break;
      case '実績\n':
        text.replaceAllText(text.asString(), '実績_変更');
        break;
      case '達成率\n':
        text.replaceAllText(text.asString(), '達成率_変更');
        break;
    }
  }
}

```

理由はわからないですが、各テキストの後ろに改行が入るという
謎の仕様があったため一致条件の中に`\n`を加えています。

if文を下記のように処理しても大丈夫です。

```javascript
if(/PJ名\s/.test(text.asString())) {
  //処理
} else if (/氏名\s/.test(text.asString())) {
  //処理
}
```

# 写真の差し替えも行う場合


```javascript
function myFunction() {
  const pre = SlidesApp.getActivePresentation();
  const slide = pre.getSlides()[1]; //2ページ目取得
  const elems = slide.getPageElements();
  
  for(const elem of elems) {
    if(elem.getPageElementType().toString() == 'GROUP') {
      replaceText_(elem);
    }
  }
}

/**
* グループのオブジェクトを渡して、各子要素のテキストが一致した場合に置換
*
* @param {Object} グループのオブジェクト
*/

function replaceText_(groupElem) {
  const elemsInGroup = groupElem.asGroup().getChildren();
  let text;
  
  const pictureObj = getPictures_('フォルダID');
  
  for(const elem of elemsInGroup) {
    console.log(elem.getPageElementType().toString());
    if (elem.getPageElementType().toString() === 'SHAPE') {
      text = elem.asShape().getText();
      
      switch (text.asString()) {
        case 'PJ名\n':
          text.replaceAllText(text.asString(), 'PJ名_変更');
          break;
        case '氏名\n':
          text.replaceAllText(text.asString(), '氏名_変更');
          break;
        case 'しめい\n':
          text.replaceAllText(text.asString(), 'しめい_変更');
          break;
        case '目標\n':
          text.replaceAllText(text.asString(), '目標_変更');
          break;
        case '実績\n':
          text.replaceAllText(text.asString(), '実績_変更');
          break;
        case '達成率\n':
          text.replaceAllText(text.asString(), '達成率_変更');
          break;
      }
    } else if (elem.getPageElementType().toString() === 'IMAGE') {      
      elem.asImage().replace(pictureObj['写真の名前']);
    }
  }
}


/**
* フォルダ内の名前とBlobデータをオブジェクトで返す
* 
* @param {String} フォルダID
* @return {Object} key：拡張子を除いた写真の名前, value：写真のblobオブジェクト
*/
function getPictures_(folderId) {

  const folder = DriveApp.getFolderById(folderId);
  const files = folder.getFiles();
  
  const obj = {};
  
  while (files.hasNext()) {
    const file = files.next();
    obj[file.getName().replace(/\..+/g, '')] = file.getBlob();
  }
  
  return obj;
}
```