IE7の新しい印刷機能:その実装

Internet Explorer 7で導入された新しい印刷機能のうち、以前のバージョンと比べてもっとも改善されたのが縮小機能関連だろう。
特に「縮小して全体を印刷する」と「オーファン制御」は目に見えて便利になったと感じる点だと思われる。(むしろやっとあるべき姿になったと言うべきか。)


これらの機能はInternet Explorer 5.5から導入された「印刷テンプレート」機能を使っているため、原理的に言えばIE5.5、IE6でもIE7と同じ印刷機能が利用できることは既に検証した。(IE5.5は試してないが。)


今回は、この新しい機能がどういう仕組みで動作しているかを解説してみる。

縮小して全体を印刷する

これはコンテンツに合わせて印刷倍率を自動的に小さくすることで、用紙の横方向のはみ出しをなくすというもの。


実装はそれほど難しくなく、JavaScriptにして60行程度の内容。やってる処理もこれと言って難解というわけでもない。

  • 一旦すべてのページのレイアウトが完了すると、CalcAutoFit()が呼ばれる。
  • まずはみ出る部分を含めたコンテンツの横幅を計算する。これは全てのページのLAYOUTRECT領域のscrollWidthプロパティの最大値である。
  • コンテンツの横幅がページ幅よりも大きければ(はみ出していれば)、横幅が収まるよう、ズーム比率を (ページ幅÷コンテンツの横幅)に設定する。
  • 算出したズーム比率を使ってすべてのページのレイアウトを最初からやり直す。

...
if(g_fCheckAutoFit)
{
g_fCheckAutoFit = false;
var fitScale = CalcAutoFit(); // 縮小率の計算
if(fitScale < 30) fitScale = 30;
selectScale.selectedIndex = 0;
cellCustomScale.style.display = "none";
g_nScalePercent = fitScale;
if(fitScale!=100 )
{
EnsureDocuments(true); // レイアウトのやり直し
return;
}
}
...

function CalcAutoFit()
{
var docWidthPx = 0;
var docWidthIn = 0;
var printerWidth = Printer.pageWidth / 100;
var scale = 100;
var docClientWidthPx = 0;
if (g_nScreenDPI == 0)
{
return scale;
}
printerWidth -= g_nMarginLeft;
printerWidth -= g_nMarginRight;
var nCount = TotalDisplayPages();
var i;
for(i = 1; i<=nCount; i++)
{
var oRect = DisplayPageLayoutRect(i);
if(oRect!=null)
{
if (oRect.scrollWidth > docWidthPx)
{
docWidthPx = oRect.scrollWidth;
}
if (oRect.clientWidth > docClientWidthPx)
{
docClientWidthPx = oRect.clientWidth;
}
}
}
docWidthIn = docWidthPx * (1/g_nScreenDPI);
if (docWidthPx == 0 || docClientWidthPx >= docWidthPx)
{
return scale;
}
scale = (printerWidth / docWidthIn) * 100;
scale = Math.floor(scale+0.05);
if (scale < 30)
{
scale = 30;
}
if (scale > 100)
{
scale = 100;
}
return scale;
}

ソースコードは読みやすくなるようインデント、コメントを追加してある。


またソースコードを見ると、自動的な縮小は30%までが限度ということが分かる。

オーファン制御

オーファン(orphan)というと日本語ではなじみが無い言葉でさっぱり意味が分からないが、要するに1ページにギリギリ収まらないコンテンツを印刷した時に、最後の段落が2ページ目の頭に数行分だけ泣き別れになってしまう現象を指している。(詳しくは図解DTP用語辞典


実際には総ページ数が2ページになった場合にオーファンの可能性があると判断し、オーファンチェックを行う。

  • 2ページ目の印刷量がページ高さの10%未満であった場合にオーファンであると判定する。2ページ目の印刷量はLAYOUTRECT領域のscrollHeightで得られる。
  • オーファンだった場合は、オーファン部分を含めた2ページ分の印刷量が1ページ分に収まるよう、現在のズーム比率に対して更に(ページ高さ÷2ページ分の印刷量)の縮小をかける。
  • 算出したズーム比率を使ってすべてのページのレイアウトを最初からやり直す。

...
if (g_fCheckOrphan)
{
g_fCheckOrphan = false;
if(IsOrphaned()) // オーファンチェック
{
var orphanScale = CalcOrphanRemovalScale(); // 縮小率の算出
g_nScalePercent = g_nScalePercent * orphanScale/100;
if(g_nScalePercent < 30) g_nScalePercent = 30;
EnsureDocuments(true); // レイアウトのやり直し
return;
}
}
...

function IsOrphaned()
{
if((g_nFramesetLayout!=0 && g_nFramesetLayout!=1) || TotalDisplayPages()!=2)
{
return false;
}
var printerHeight = Printer.pageHeight / 100;
printerHeight -= g_nMarginTop;
printerHeight -= g_nMarginBottom;
if(printerHeight <= 0) return false;
var oSecondRect = DisplayPageLayoutRect(2);
if(g_nScreenDPI==0) return false;
var layoutHeight = oSecondRect.scrollHeight/g_nScreenDPI;
var pageCoverage = layoutHeight * 100 / printerHeight;
return pageCoverage < 10;
}

function CalcOrphanRemovalScale()
{
var scale = 100;
var printerHeight = Printer.pageHeight / 100;
printerHeight -= g_nMarginTop;
printerHeight -= g_nMarginBottom;
if(printerHeight <= 0) return scale;
var oSecondRect = DisplayPageLayoutRect(2);
if(oSecondRect==null) return scale;
if(g_nScreenDPI==0) return false;
var totalHeight = oSecondRect.scrollHeight;
totalHeight = totalHeight / g_nScreenDPI;
totalHeight += printerHeight;
if(totalHeight <= 0) return scale;
scale = printerHeight*100/totalHeight;
scale = Math.floor(scale+0.05);
if(scale>100) scale = 100;
return scale;
}