モダンブラウザとElectronでの印刷時に余白をピッタリ調整する


## モチベーション

Electronでは`webContents.print()`関数や`webContents.printToPDT()`関数を使って簡単に印刷やPDF作成ができます。この時、CSSで印刷サイズをmm等の実物長さで指定することで、実際の印刷時の各種要素の大きさを指定できるので便利です。が、注意しないと余分な余白が入ってしまいます。経験則からくる注意点を情報共有します。

※2020/2/19：色々試行錯誤して、`@page`との関連、`print()`と`printToPDF()`の違いなど大幅に改定しました。

## 最重要注意点


- ChromeおよびChromium系ブラウザ、Electron(7.1.9～8.0.1で確認)において、`@page`内に記述した`margin`, `margin-left`, `margin-right`,`margin-top`,`margin-bottom`については、単位の変換におそらくbugがあり、設定値よりも1.5～1.8倍程度大きく設定されてしまう。
  - 確認したversion(これ以外も確認していないだけでおそらくbugがある)
    - Chrome: 80.0.3987.106
    - Electron: 7.1.9～8.0.1
    - Edge (Chromium): 80.0.361.54
- 回避策として、`@page`内では`margin`系の設定値の 単位として`%`を使う。
- Firefox、Edge(Legacy)では`%`以外の単位も問題なく動く。
- `margin-top`、`margin-bottom`に`%`を使う際は、ブラウザによって解釈が異なる
  - Chrome, Firefox, Edge(Chromium)：**ページ横幅に対する割合**を設定する         
  - Edge(Legacy)：**ページ縦幅に対する割合**を設定する

## 注意点（試行錯誤の結果）


- CSSで`padding`や`margin`を正しく設定する。特に設定しない時は、明示的に`0mm`を設定すること。デフォルトでは`0mm`でないことがある。
- body要素にも`padding`と`margin`を明示的に設定する事。実際に余分な余白が消えずに悩んだが、body要素の`margin`がデフォルトで`0mm`ではなかったことが原因だった。
- 印刷時だけ設定したいCSSは`@media print`項目で設する。
- ブラウザ上の表示はどれだけ長くても縦に長い1枚ページであるのに対し、印刷時は縦幅も固定で複数ページに分割される。
- 印刷時のページ毎のマージンは、HTML要素のプロパティーではなく、`@page`のプロパティーとして設定するべき。さもなくば、 改ページ部分での上下マージンが反映されない。
- `webContents.print()`はElectron 7.xでは`marginType:"custom"`を設定するとクラッシュする。
- `webContents.print()`の`marginType`に"default"以外の値を設定しても、"default"と表示が変わらない。（バグとも思える。Electron version 8.0.1)。
- `webContents.printToPDF()`で`marginsType:0`(デフォルトマージン)にすると CSSの`@page`に設定した`margin`, `margin-left`, `margin-right`,`margin-top`,`margin-bottom`が適用される。 `marginsType:1`(マージンなし)にすると、これらのCSS設定も無視される。
- `webContents.printToPDF()`の`marginsType:0`と、`webContents.print()`の`marginType: "default"`はCSSの適用と表示に関して(おそらく)同じ挙動。

## 例

body要素の中にdiv要素があり、その中に文章が書かれている場合のCSS記述は次のようになります。


```CSS
@page{
  size: A4;
/*  本来は以下の記述で良いはずが、Chromium系バグで設定値より大きくなってしまう
  margin-left:   15mm;
  margin-right:  15mm;
  margin-top:    20mm;
  margin-bottom: 20mm;
回避策として以下のように%で指定する
*/
  margin-left:   7.143%;   /* = 15mm / 210mm */
  margin-right:  7.143%;
  margin-top:    9.524%;      /* = 20mm / 210mm "横幅"に対する割合*/
  margin-bottom: 9.524%;
}
@media print{
  body{
    padding: 0mm;  
    margin: 0mm;   /* これが無いと余分な余白が入る */
  }
  div{
    padding: 0mm;
    margin: 0mm;
    width:100%;
  }
}
```

上記の様なCSSに対して、印刷のJavascriptコードは`print()`関数のoptionとして次のように`marginType:"default"`を指定する。

```javascript
  const wc = browserWindow.webContents;
  wc.print({margins:{marginType:"default"}},
    (success, error) => {
        if(success){
            console.log('Print successfully.');
        }else{
            console.log(error);
        }
    });
```

また、PDF出力時のJavascriptコードは`printToPDF()`関数のoptionとして次のように`marginsType:0`を指定する。

```javascript
  const wc = browserWindow.webContents;
  wc.printToPDF({pageSize:"A4", marginsType:0}).then(data => {
     fs.writeFile(result.filePath, data, (error) => {
       if (error) throw error;
         console.log('Write PDF successfully.');
       })
     }).catch(error => {
       console.log(error);
     });
```
 

## 雑感

Electronの印刷はとても楽でいいですね。CSSで微調整できるところも素敵です。すごい時代になってますね。

子供の頃、親父にPCを買ってもらった際、弟と協力して年賀状印刷ソフトを作ったのを思い出しました。当時はVisual Basic6で印刷物の位置合わせをするのに苦労しました。

## 雑感（改定時）

Chrome,Chromiumのバグと気づくまで今回は時間が掛かりました。Firefoxがあってよかった。Edge(Legacy)が無くなってしまった弊害として、今回の様なブラウザエンジンのバグに気づきにくくなりますね。そういう意味で復活して欲しいなぁ、EHTMLエンジン。