Google Apps Scriptでスライドの箇条書きから図形をつくる

思いの外、時間がかかってしまったので、個人用メモ。

# やりたいこと
Google スライドのページ内につくった箇条書きの文章をそれぞれ、別々のオブジェクト（Shape）として生成したい。
つまり...
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197285/c7e2efdb-acce-ac24-bcab-68fba588a50b.png)
こういうスライドから...
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197285/1c8a7d00-6a8c-c20f-ebad-cc647e2d41a7.png)
こういうスライドを自動的に生成したい。

# GASを使おう
Googleのサービスの大部分は、Google Apps Script(GAS)から叩けるAPIが用意されている。とはいえ、ネットに上がっている記事の多くはGoogle Spreadsheetを操作するサンプルコードばかりである。 ~~そんなにSpreadsheet扱いたいんだろうか？~~ 

## 起動メニューの作成
とりあえず、起動するためのメニューを追加する。
```javascript:code.gs
function onOpen() {
  const menu = SlidesApp.getUi().createMenu('追加の機能');
  menu.addItem('テキストボックスを図形に変換する', 'text2box');
  menu.addToUi();
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197285/79b81345-eeca-513e-70b0-46b94c7ed8b7.png)
これで、多少は起動しやすくなった。

## text2box関数
続いてtext2box関数を作っていく。
```javascript:code.gs
const presentation = SlidesApp.getActivePresentation();
const selection = presentation.getSelection();
const selectionType = selection.getSelectionType();
const new_slide = presentation.appendSlide();
if(selectionType == SlidesApp.SelectionType.PAGE_ELEMENT) {
  selection.getPageElementRange().getPageElements().forEach(item=>create_box(item.asShape()));
}else{
  selection.getCurrentPage().getShapes().forEach(s=>create_box(s));
}
new_slide.selectAsCurrentPage();
```
スライド内のShapeを選択している場合はそのShapeを、そうでない場合は選択中のスライドにあるすべてのShapeを対象とする。ただし、ここで気をつけたいのは、選択の仕方である。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197285/6f478e08-6a21-7cc8-3427-60f1906c7ff7.png)
画像の左右で、一見、同じものを同じように選択しているように見えるが、`getSelectionType()`で取得できる値は、右が`SlidesApp.SelectionType.PAGE_ELEMENT`で左が`SlidesApp.SelectionType.TEXT`である。しかも都合の悪いことに左側の場合、`getSelectionType().getTextRange().asString()`で取得できる値が空である（何の文字も選択していないので）。よって、左側の場合、そのページ内にあるすべてのShapeの文字列を取得することとなる。
また、指定したスライドを表示させる（画面を移動させる）には、スライドオブジェクトにある`selectAsCurrentPage`を呼び出す。

## create_box関数
text2box関数内で、shapeまで切り分けたので、create_box関数ではshapeを受け取って、切り分けを行い、図形を書き出す。ということで、text2box関数内にcreate_box関数を定義する。

```javascript:code.gs
let [x,y]=[0,0];
const [x_max, y_max, x_size,y_size]=[presentation.getPageWidth(),presentation.getPageHeight(), 80, 80];
const create_box=shape=>{
  const text_array=shape.getText().asString().split("\n");
  text_array.forEach(text=>{
    if(text!=""){
      const new_shape=new_slide.insertShape(SlidesApp.ShapeType.ROUND_RECTANGLE, x, y, x_size, y_size);
      new_shape.getText().setText(text);
      new_shape.getFill().setSolidFill("#a1d2ff");
      if(y+2*y_size>y_max){
        if(x+2*x_size<=x_max) x+=x_size;y=0;
      }else{
        y+=y_size;
      }
    }
  });
};
```
スライド上に新しいshapeを追加するにはスライドオブジェクトの`insertShape`関数を用いる。また、shapeの背景色を変更するには`getFill`関数でFillオブジェクトを取得する。なお、`split('\n')`で分けるのではなく、箇条書きなので、
```javascript:code.gs
const get_all_list=shape.getText().getListStyle().getList();
if(get_all_list){
  get_all_list.getListParagraphs().forEach(list=>{
    const trang=list.getRange();
    const level=trange.getListStyle().getNestingLevel();
    /*　中略　*/
    if(level==0){
      new_shape.getFill().setSolidFill("#f4cccc");
    }else if(level==1){
      new_shape.getFill().setSolidFill("#a1d2ff");
    }
    /*　中略　*/
  });
}
```
のようにしてあげると、箇条書きのレベルごとに違う色を割り当てることが出来る。

# おわりに
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197285/eb90bc55-37d4-ff1f-6ffd-06f7e22160b9.png)
手動で並べ替えてあげて、矢印で結んであげれば、ちょっとした思考ツールになるかも...？

　
以上

# 参考サイト

https://developers.google.com/apps-script/reference/slides/slides-app?hl=ja

公式リファレンスが最強。

https://myfunc.jp/

どうしてもGASのサンプルコードを見たい場合は、この辺を見る。