mikeo_410
  1. 2.GASで作ったCMS
    1. 1.googleドキュメントでサイト
    2. 2.googleドキュメントのCMS
    3. 3.このCMSアプリの使い方
    4. 4.数式

googleドキュメントのCMS

WYSIWYG(What You See Is What You Get)でなければ使えないので、図表や数式の入ったブラウズ可能な文書編集ツールを希求してきました。

何とか、googleドキュメントで文書を作成し、フォルダ階層を反映した目次を自動生成できるようになりました。ドキュメントは自動的にHTML化して送られ、「共有」ではありません。

これは、googleドキュメントをフォルダ階層に作成して階層化された目次を公開するgoogle Apps Scriptで作成したCMSです。

これを作り、使って見て、問題を検討します。

1. 問題が起きる原因

最初から予期される問題の原因は3つあります。

1つは、googleドライブ自体がCMSであって、ファイルを直接提供する単なるストレージではないと言うことです。googleドライブに置かれたデータは何かしらのプリケーションによってられ、保存したデータそのものではありません。

2つ目は、googleドキュメントがWordのような印刷文書作成アプリケーションであって、HTML文書とは異なる点です。WEBブラウザは窓の大きさに合わせて動的にレイアウトするためのツールであり、一方、googleドキュメントは用紙のサイズに合わせて印刷用のレイアウトをします。通常HTMLにはページ区切りはなく、横の文字数もウインドウの大きさに追従します。googleドキュメントをHTMLに変換することにはさまざまな問題が付きまといます。

最後は、google Apps Scriptで作成したアプリケーションが、googleドライブのサービスを行なうサーバの機能で公開されることです。googleドキュメントやgoogleスプレッドシートもgoogleドライブを基礎にした応用プログラムであって、google Apps Scriptで作成したアプリケーションも同様に直接外部にHTMLを出力出来る訳ではありません。具体的には、googleの定めたHTML内のSandBoxのiframe内に表示されるHTMLを作ることになります。

2. リンク

googleドキュメントには「挿入」「リンク」があります。同じドキュメント内の見出し項目や、外部のURLへのリンクには問題はないものと思います。

同じサイト内の他のドキュメントをリンクするには工夫が必要です。それは、ドキュメントのURLはCMSに送られなければならないからです。

リンクに設定するURLは、

[CMSアプリケーションのURL]?doc=[ドキュメントのURL]

です。

ここで、「ドキュメントのURL」は、「ファイル」「ウェブに公開」で割り当てられるURLです。「共有」のURLではありません。「共有」を設定する必要はありません。

「ファイル」「ウェブに公開」で割り当てられるURLは、ドキュメントをHTML形式で受け取るためのものです。「共有」のURLはエディタ(ドキュメントの編集)を開きます。

いずれドキュメントの編集時にリンクを挿入するスクリプトを作りたいと思います。躊躇するのは「ドキュメントのURL」を知る方法がないことです。最初に開かれるindex.htmlの目次は、ドキュメントの公開の際に手作業で作成する表に基づいています。

3. ドキュメントのURL

ドキュメントを作成したら、「ファイル」「ウェブに公開」をします。この操作で発行されるURLにアクセスするとサーバはHTML化したドキュメントを返します。

この「ドキュメントのURL」は、この操作でしか取得できずファイルIDなどから引くことが出来ません。

止むを得ず、別にApps Scriptアプリを作ってスプレッドシートに登録する方法を取ります。このアプリは、「ドキュメントのURL」が既に入力されたものはファイルIDとURLの対応を保存して、いったん全てクリアします。/docフォルダ以下をスキャンして、その時点の全ドキュメントを列挙します。この際に、ファイルIDとURLの対応が保存されていれば「ドキュメントのURL」を補います。

下図は「このCMSアプリの使い方」と言うドキュメントを作って所定の場所に「移動」した状態です。空の場所に「ドキュメントのURL」を貼り付けて登録します。

 index.htmlが要求された時に、この表を使って目次を作ります。

4. サンドボックス内のHTML

ミニムのサイト」に挙げた資料は、googleドキュメントですが、以下のようなHTMLをドキュメントごとに作ってあります。

この方法でHTML化されたgoogleドキュメントは、大きなマージンも上部のgoogleの帯もなく通常のWEBページに見えます。

<!DOCTYPE html>

<html>

    <head>

        <style>

            iframe {display: block; position:fixed; width: 99%; height: 100%; border: none;}

        </style>

    </head>

    <body>

        <div>

            <iframe id="f1" src="https://docs.google.com/xxx/pub?embedded=true"

             allowfullscreen="allowfullscreen"></iframe>

        </div>

    </body>

</html>

これと同じことが出来ると考えて始めましたが、違っていました。

Apps Scriptアプリケーションが返すHTMLは全てgoogleの用意したiframe内に表示されます。

5. 自動変換されたHTMLへの修正

「ファイル」「ウェブに公開」で付与される「ドキュメントのURL」を読み出すとHTMLが得られます。コード.gs内のdoGet()で、これにJavaScriptを挿入して、大きなマージンの問題を解決することにしました。

</body>タグを置き換えて挿入しましたが消されてしまいます。</div>を置き換えて挿入しています。これは、ドキュメントのHTMLはbodyの直下がdivで、これ以外にdivタグがないことで上手くいっています。JavaScriptで行なう修正内容も、このdivタグのスタイルに対するものです。

JavaScriptの挿入は単純な文字列処理です。

6. HtmlServiceとテンプレート

ブラウザはURLを送ってサーバにリクエストします。このリクエストに対する応答はコード.gsに記述したdoGet()の戻り値で与えます。

index.html

<!DOCTYPE html>

<html>

  <head>

    <base target="_top">

    <?!= HtmlService

    .createHtmlOutputFromFile('css')

    .getContent(); ?>

  </head>

  <body>

    <h1> <?= a_variable ?> </h1>

    <?!= HtmlService

    .createHtmlOutputFromFile('js')

    .getContent(); ?>

  </body>

</html>

css.html

<style>

  body{background:aqua}

</style>

js.html

<script>

  alert("JavaScript");

</script>

index.html、css.html、js.jtml の3つのファイルを追加して、コード.gs に 以下のようなdoGet()関数を書きます。

function doGet() {

  let ct = HtmlService.createTemplateFromFile("index");

  ct.a_variable = "テンプレートの効能";

  return ct.evaluate();

}

結果、ブラウザには以下のようなHTMLが送られます。

doGet()関数の戻り値は以下のような関数で作ります。

HtmlService.createTemplateFromFile("ファイル名").evaluate();

HtmlService.createTemplate(html).evaluate();

前者はHTMLファイルから、後者は文字列のHTMLから、戻り値を作ります。

あるいは、以下のような関数を使います。

HtmlService.createHtmlOutputFromFile("ファイル");

HtmlService.createHtmlOutput(html);

Template を evaluate したものが HtmlOutput のようです。

<?= ?> と言ったスクリプトレット・タグを含まないなら、どちらでも同じになるはずのものだと思います。

分かったことは、下図のようなテンプレートにHTML記述を作用させるのは上手くいかないということです。

  let ct = HtmlService.createTemplateFromFile("index");

  ct.a_variable = "<p></p>"

  Logger.log(ct.evaluate().getContent());

出力は、&lt;p&gt;&lt;/p&gt; と文字参照に置き換えられてしまいます。

試行錯誤する間には、引用符で囲まれてしまったり、先頭だけが参照文字になっているのを見ましたが諦めました。

結局、単純に文字列の replace でHTMLを編集しました。

7. 章番号

「見出し」を指定した行に、番号付きリストを設定して「章」と考えていますが、大きな文字で表示され、番号も同じサイズです。しかし、HTML化したものは番号が普通の文字サイズで、中には見出しの一部だけが太字だったりします。

どうも最初の単語を認識すると言った高度なことが行なわれているように見えます。何かの習慣によるのかもしれません。

また、番号はcssで自動的に振られています。少し大きすぎますが「見出し1」がH1に相当し、発番には「見出し1」から順に使う他ないようです。

見出しの一部だけが太字になる件は、見出しのスタイルを変更して適用し直すとなくなりました。これはドキュメントの作成時に工夫すれば改善できそうです。

章番号が小さいのは、h1タグではなく、liタグを指定して番号を表示しているためでした。文字の大きさなど見出しのスタイルを指定する目的で、h1、h2、・・・と使い分けるので、そのタグの前に同じスタイルで番号を付けるのは自然に思えます。そうすれば、下図の右のような表示になります。

liタグは本文の箇条書きにも使うので、指定しなければ本文の文字と同じで不思議はありません。下図の左のようになります。

それぞれの表示に使ったHTMLの記述は以下の通りです。

 

<style type="text/css">

  .css_num>li:before {

    content: ""counter(css_counter1, decimal) ". "

  }

  ol.css_num {

    list-style-type: none

  }

  ol.css_num.start {

    counter-reset: css_counter1 0

  }

  .css_num>li {

    counter-increment: css_counter1

  }

</style>

<style type="text/css">

  h1.start {

    counter-reset: css_counter1 0

  }

  h1.css_num:before {

    content: ""counter(css_counter1, decimal) ". "

  }

  h1.css_num {

    counter-increment: css_counter1

  }

</style>

<ol class="css_num start">

  <li style="display: inline-block;">

    <h1 style="display:inline">

      <span>問題が起きる原因</span>

    </h1>

    </li>

</ol>

<ol class="css_num">

  <li style="display: inline-block;">

    <h1 style="display:inline">

      <span>リンク</span>

    </h1>

  </li>

</ol>

 <h1 class="css_num start">問題が起きる原因</h1>

<h1 class="css_num">リンク</h1>

<h1>クラス指定なし</h1>

良くある例は、右のようなものです。この場合、h1だけでなく、h2、・・・と同じ記述をすることになる欠点があります。

オーダーリストは、もともと番号を付けるためにある訳ですから、それで済むなら入れ子になった場合など便利に違いありません。しかし、実際に行っているのはliタグの前にh1と同様の方法で番号を書いています。

また、実際にドキュメントをHTML化したものは olタグに start=n と章番号が記されていて自動発番は必ずしも必要ではないようです。ただし、これは使われておらず、表示されている番号は css で割り当てたカウンタの方です。なぜ、liタグに相当する番号が表示されないのかは、olタグに list-style-type:none と設定されているからです。

事情は分かりましたが、今のところ重要ではないので置いておきます。

※ 「見出し」を指定した行に、番号付きリストを設定して「章」と考えています。2章目以降は、「書式コピーアイコン」(上のツールバーの右寄りのローラー形のアイコン)を使って1章目の書式を、2章目の見出しとする行に貼り付けています。

書式を貼り付ける際に、2章目の見出しとする行の何処にマウスポインタを置いて貼り付けても行全体の文字が大きくなり番号も振られます。

書式を貼り付ける操作を、見出しとする部分全体の選択操作で行なうと、部分的に文字の太さの異なる見出しが回避できているようです。どうやら、適用範囲を探索する処理で見出しは分割されるようです。

8. 簡体字のフォントが有効でない

googleドキュメントで指定したフォント指定はHTML化のときに失われるだろうと先入観を持っていました。実際にはちゃんとfont-familyが設定されていました。

簡体字のフォントが有効でないのは、リンクされていないだけでした。

は同じUnicodeです。

これは、図を貼って表しています。ドキュメントの編集時に標準(Arial)は「愈」、noto sans SCフォントを指定すると「愈」となります。

ここに書いた2字が同じに見えるならフォント指定が機能していません。

noto sans SCフォントは、googleが提供していますが、googleドキュメントの標準ではないので別途リンクすることが必要なようです。

<link href='https://fonts.googleapis.com/css2?family=Noto+Sans+SC&display=swap' rel='stylesheet'>

もう一つ、行間の問題があります。googleドキュメントで編集中は noto sans SCフォントを選ぶと、概ね行間が2倍になります。そこで、行間を1/2に設定して調製しました。おそらく、下図は2行が重なって見えていると思います。googleドキュメントでは重ならずに適当な行間になっています。

专丝两严丰举乐乡亚从价传俭儿关兽净击剂剑劝・・・

専糸両厳豊挙楽郷亜従価伝倹児関獣浄撃剤剣勧・・・

HTML化した後では、line-height:0.5 と行間の1/2が反映されています。問題は、googleドキュメントの方がなぜ2倍の行間になるのかの方に思えます。

これは何も浮かばないので諦めます。行間は調整しないことにします。

9. ファビコンが出ない

文書に直接図を貼れるので図を別にファイル管理しようとは思いません。しかし、ファビコンはドキュメントの一部ではないので図形のURLが必要になります。

ファビコンは HtmlService の HtmlOutput の setFaviconUrl() で設定するようです。この関数の目的は、googleドライブのサーバが最終的に返すHTMLのヘッダに設定を記述するためのようです。扱っているのは iframe内に含まれるHTMLですが、ファビコンはトップのheadに設定が必要なのだと思います。

画像をgoogleドライブにアップロードして、その「リンクの取得」を行なうと以下のようなURLになります。

https://drive.google.com/file/d/xxx/view?usp=sharing

 このURLを使って、ローカルにHTMLを作って確認しましたがファビコンにはなりません。img タグのsrc でも、この URL は有効ではありません。

異なる形式を上げるサイトがあるので、

        https://drive.google.com/uc?export=view&id=xxx

を、試すと img タグのsrc としては有効でした。

最終的に

        https://drive.google.com/uc?&id=xxx

で、ファビコンが設定できています。

ただし、setFaviconUrl() は、この形式をエラーにします。拡張子をチェックするものと思われます。

 https://drive.google.com/uc?&id=xxx&.png

のようなトリックが必要です。これはクエリパラメータなので何を書いても受け取った側が処理しないものなら悪影響はありません。

知りたいことは、1)googleドライブ上にファイルとして表示されているものをHTML記述から参照するためのURL、2)ファビコンを含めて img タグの src のような要求に対する応答の方法です。

googleドライブにある favicon.png のリンクとして、「リンクを取得」で受け取る URL はHTML記述に使用するリンクとしては無効です。

 有効なのは、

 https://drive.google.com/uc?&id=xxx
と言った記述で、xxxはファイルIDです。

実際に、setFaviconUrl() で設定した、最上位のヘッダは以下のようになっています。

<link rel="shortcut icon" type="image/png" href="https://drive.google.com/uc?id=1X5QuCmvGF9i48yAoiveu4ghqTCIX4CTx&amp;.png">

&.png は、setFaviconUrl() を騙すために付加したものです。実際のURLは、

  https://drive.google.com/uc

までで、以降はクエリパラメータです。&.png と、不要なクエリパラメタを書いても普通は何の影響もありません。

また、通常のHTML記述では、<head>は一つです。この状態では、body に書いたリンクも有効でした。ファビコンもOKです。しかし、ここでは、ドキュメントが iframe によって階層を成しています。

setFaviconUrl() は、最上位の head にリンクを追加します。

コード.gs のdoGet()関数は、リンクされたファイルを返すことにも使えます。

ContentService.createTextOutput()、Utilities.base64Encode()、UrlFetchApp.fetch()、DriveApp.getFileById()を組合せて、imgタグのsrcに応答しようとしました上手くいきませんでした。これが出来るようになれば、リンクの書き方を気にせず、ファイルIDでファイルを返せます。

10. 字下げが上手くいっていない

全角スペースで字下げして段落としているのですが、HTML化で全角スペースは &nbsp; になるようです。インデントを設定すれば、HTML化で text-indent が設定できます。

しかし、全角スペースが字下げなのは原理であって何とかならないものかと思います。

先頭行のインデントを設定して回避しますが、これは字下げの量を絶対値で指定します。テキストの文字サイズに従いません。手順は調べながら下記に書きます。

このCMSアプリの使い方

11. 数式は図になる

漠然と数式はMathMLになるのだろうと思っていましたが図になるようです。

ルートの中に分数を書いてみました。それ自体は図ではなく、編集可能なものです。HTML化されたものは図です。また、アドオンの数式エディタは図をドキュメントに挿入します。

「挿入」「数式」で表示されるパーツには三角関数がないようですがなんとかなるのでしょう。

 


題目一覧へmikeo_410@hotmail.com(updated: 2022/09/09)