PHPで縦書きPDF作成（TCPDF）　カッコなどの文字回転対応

#概要
PHPで縦書きのPDFを作成するときに実現した方法のメモです。
※執筆時点でまだ未完全です。

#縦書き対応方針
いくつかネット上の記事がありますが、こちらの記事の内容をベースとさせていただきました。
[PDFで日本語縦書きしたい](https://dbweb.0258.net/wiki.cgi?page=tcpdf%A4%C7%C6%FC%CB%DC%B8%EC%A4%CE%BD%C4%BD%F1%A4%AD)

この記事の方法と特徴としては以下です。
- TCPDF自体のソースやフォント設定の書き換えは行わない
- 1文字ずつばらして縦に配置していくという力業で縦書きを実現する

最終的にこちらを採用したのは、かなり手間かかりますが、文字単位で細かく調整ができるからです。

##その他検討した方法
[TCPDFマニュアル（勝手訳） 縦書き対応](http://tcpdf.penlabo.net/vertical.html)
フォント埋め込みでなくなるというのが、見栄え的に厳しかった。

[TCPDFで縦書きテキストをフォント埋め込みに対応させる](http://blog.penlabo.net/archives/2296)
こちらの方法は、フォント埋め込みができる。ソースを書き換えるが、たった１か所だけで対応。最初これを採用しようと思っていたが…後述する文字の回転ができずに諦めることに。

#実装
実際かいたコードなどです。

##環境情報
- PHP 7.2.14
- FDPI 2.2　※今回のコードではたぶん不要。いちおう入れた環境で試したので明記。
- TCPDF 6.2
- 使用フォント：[IPAex明朝 Ver.002.01](https://ipafont.ipa.go.jp/old/ipaexfont/download.html)


##準備
Composerにて、FPDIとTCPDFをインストール

###注意点(2020.03追記)
Composerでインストールしたフォルダの配下
/vendor/tecnickcom/tcpdf/fonts
のパーミッションを書き込み許可しておかないと、フォントが正しく埋め込まれません！！！

##コード
参考サイトのコードをベースに、主として以下を変更しています。
- 「　」など特定の文字回転を回転するコードを追加
- 拗音に使われる小さい字「っ」などの位置調整を追加
- 文字ごとの調整係数は、表示させながら微調整した値なので・・・これといった算出根拠はないです。

```php

<?php
require_once "lib/composer/vendor/autoload.php";

// create new PDF document
// A4 : 210×297mm
$pdf = new TCPDF('L', 'mm', 'A4', true, 'UTF-8', false);
$paper_h = 210;
$paper_w = 297;
$pageMargin = 15;

// remove default header/footer
$pdf->setPrintHeader(false);
$pdf->setPrintFooter(false);

// ---------------------------------------------------------

// IPAフォント埋め込み
$font = new TCPDF_FONTS();   
$fontfile = 'fonts/ipaexm.ttf';
$myFont = $font->addTTFfont($fontfile);

// ページ追加
$pdf->AddPage();

//線のスタイル
$lineStyle = array(
	'width'=>0.1
);

$xPosi = $paper_w-$pageMargin;
$pdf->Line( $xPosi, $pageMargin, $xPosi, $paper_h-$pageMargin, $lineStyle);
$xPosi -= pt2mm(12)+3;
tateText($xPosi, $pageMargin, '令和元年　第一回「Ａクラス」漢字テスト', 12);
$xPosi -= pt2mm(12)+2;
tateText($xPosi, $pageMargin+100, '年 　　組 　　番', 12);
$xPosi -= pt2mm(12)+2;
tateText($xPosi, $pageMargin+85, '名前', 12);
$xPosi -= 2;
$pdf->Line( $xPosi, $pageMargin, $xPosi, $paper_h-$pageMargin, $lineStyle);
$xPosi -= pt2mm(12)+10;
tateText($xPosi, $pageMargin, '〔一〕次の《　》を漢字に直しなさい。', 12);
$xPosi -= pt2mm(12)+5;
tateText($xPosi, $pageMargin, '①標識の案内に《シタガッ》て歩く。', 12);
tateText($xPosi, $pageMargin+110, '〔①', 12);
tateText($xPosi, $paper_h - $pageMargin - pt2mm(12) - 8, '〕', 12);
$xPosi -= pt2mm(12)+5;
tateText($xPosi, $pageMargin, '②欠員を《オギナウ》。', 12);
tateText($xPosi, $pageMargin+110, '〔②', 12);
tateText($xPosi, $paper_h - $pageMargin - pt2mm(12) - 8, '〕', 12);
$xPosi -= pt2mm(12)+5;
tateText($xPosi, $pageMargin, '③国民の義務として、税金を《オサメル》。', 12);
tateText($xPosi, $pageMargin+110, '〔③', 12);
tateText($xPosi, $paper_h - $pageMargin - pt2mm(12) - 8, '〕', 12);

$xPosi -= pt2mm(12)+10;
tateText($xPosi, $pageMargin, '〔二〕次の《　》の読みを書きなさい。', 12);
$xPosi -= pt2mm(12)+5;
tateText($xPosi, $pageMargin, '①電車を《降》りて職場に向かった。', 12);
tateText($xPosi, $pageMargin+110, '〔①', 12);
tateText($xPosi, $paper_h - $pageMargin - pt2mm(12) - 8, '〕', 12);
$xPosi -= pt2mm(12)+5;
tateText($xPosi, $pageMargin, '②叔父から卒業祝いが《届》いた。', 12);
tateText($xPosi, $pageMargin+110, '〔②', 12);
tateText($xPosi, $paper_h - $pageMargin - pt2mm(12) - 8, '〕', 12);
$xPosi -= pt2mm(12)+5;
tateText($xPosi, $pageMargin, '③あらかじめ《危》ない場所を確認する。', 12);
tateText($xPosi, $pageMargin+110, '〔③', 12);
tateText($xPosi, $paper_h - $pageMargin - pt2mm(12) - 8, '〕', 12);

$xPosi -= pt2mm(12)+10;
tateText($xPosi, $pageMargin, '〔三〕次の意味になるように《　》部を漢字に直しなさい。', 12);
$xPosi -= pt2mm(12)+5;
tateText($xPosi, $pageMargin, '①広く大きな見通しをもっていること。《タッカン》', 12);
$xPosi -= pt2mm(12)+2;
tateText($xPosi, $pageMargin+110, '〔①', 12);
tateText($xPosi, $paper_h - $pageMargin - pt2mm(12) - 8, '〕', 12);
$xPosi -= pt2mm(12)+5;
tateText($xPosi, $pageMargin, '②説得力をもって力強く話すこと。《ユウベン》', 12);
$xPosi -= pt2mm(12)+2;
tateText($xPosi, $pageMargin+110, '〔②', 12);
tateText($xPosi, $paper_h - $pageMargin - pt2mm(12) - 8, '〕', 12);
$xPosi -= pt2mm(12)+5;
tateText($xPosi, $pageMargin, '③他人の心をおしはかって相手に配慮すること。《ソンタク》', 12);
$xPosi -= pt2mm(12)+2;
tateText($xPosi, $pageMargin+110, '〔③', 12);
tateText($xPosi, $paper_h - $pageMargin - pt2mm(12) - 8, '〕', 12);

// ---------------------------------------------------------

//Close and output PDF document
//$pdf->Output('tate01.pdf', 'F');
$pdf->Output();

//============================================================+
// END OF FILE
//============================================================+

function tateText($x, $y, $str, $size) {
	global $pdf, $myFont;
	//フォントサイズ設定
	$pdf->SetFont($myFont, '', $size);
	//字間
	$fh = pt2mm($size*1.1);
	//文字数
	$l = mb_strlen($str, 'UTF-8');
	for ($i=0; $i<$l; $i++) {
		$s1 = mb_substr($str, $i, 1, 'UTF-8');
		$adj_x = 0; $adj_y = 0;
		// 文字ごとの位置や回転調整
		switch($s1){
			case '「':
				$adj_rotate_x = pt2mm($size);
				$adj_rotate_y = pt2mm($size*0.5);
				$adj_rotate_posi_x = pt2mm($size*0.4);
				$adj_rotate_posi_y = 0;
				$pdf->Rotate(270,$x+$adj_x+$adj_rotate_x,$y+$fh*$i+$adj_y+$adj_rotate_y);
				$pdf->Text($x+$adj_x+$adj_rotate_posi_x, $y+$fh*$i+$adj_y+$adj_rotate_posi_y, $s1,0);
				$pdf->Rotate(90,$x+$adj_x+$adj_rotate_x,$y+$fh*$i+$adj_y+$adj_rotate_y);
			break;
			case '」':
				$adj_rotate_x = pt2mm($size);
				$adj_rotate_y = pt2mm($size*0.5);
				$adj_rotate_posi_x = pt2mm($size*0.4);
				$adj_rotate_posi_y = pt2mm($size*0.3);
				$pdf->Rotate(270,$x+$adj_x+$adj_rotate_x,$y+$fh*$i+$adj_y+$adj_rotate_y);
				$pdf->Text($x+$adj_x+$adj_rotate_posi_x, $y+$fh*$i+$adj_y+$adj_rotate_posi_y, $s1,0);
				$pdf->Rotate(90,$x+$adj_x+$adj_rotate_x,$y+$fh*$i+$adj_y+$adj_rotate_y);
			break;
			case '《':
			case '》':
			case '～':
			case '〔':
			case '〕':
				//$adj_rotate_x = pt2mm($size);
				//$adj_rotate_y = pt2mm($size*0.3);
				//$adj_rotate_posi_x = pt2mm($size*0.7);
				$adj_rotate_x = pt2mm($size);
				$adj_rotate_y = pt2mm($size*0.5);
				$adj_rotate_posi_x = pt2mm($size*0.4);
				if($size<=12){
					$adj_rotate_posi_y = pt2mm($size*0.15);
				}else{
					$adj_rotate_posi_y = pt2mm($size*0.2);
				}
				$pdf->Rotate(270,$x+$adj_x+$adj_rotate_x,$y+$fh*$i+$adj_y+$adj_rotate_y);
				$pdf->Text($x+$adj_x+$adj_rotate_posi_x, $y+$fh*$i+$adj_y+$adj_rotate_posi_y, $s1,0);
				$pdf->Rotate(90,$x+$adj_x+$adj_rotate_x,$y+$fh*$i+$adj_y+$adj_rotate_y);
			break;
			case '。':
			case '、':
				$adj_x = $fh * 0.7; $adj_y = -$fh * 0.7;
				$pdf->Text($x+$adj_x, $y+$fh*$i+$adj_y, $s1,0);
			break;
			case 'っ':
			case 'ッ':
				$adj_x = pt2mm($size*0.1);
				$adj_y = -1*pt2mm($size*0.1);
				$pdf->Text($x+$adj_x, $y+$fh*$i+$adj_y, $s1,0);
			break;
			default:
				$pdf->Text($x+$adj_x, $y+$fh*$i+$adj_y, $s1,0);
			break;
		}
	}
}

// 1 インチ = 25.4 ミリメートル
// 1 ポイント = 1/72 インチ
// 1mm　は、 25.4分の1インチ
function mm2pt($mm) {
	$pt = $mm / 25.4 * 72;
	return $pt;
}
function pt2mm($pt) {
	$mm = $pt / 72  * 25.4;
	return $mm;
}
```

##出来上がりPDFイメージ
![qiita_sample_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24420/db4155de-d925-7bd4-315a-4ec97ed8f5db.png)

##今後の追加予定
- 特殊処理をする文字を追加する
- 文字サイズによる位置調整の細分化
- テキストの折り返しと改ページの処理
- その他もろもろ処理の汎用化

#役に立ったサイト
- [TCPDF](https://tcpdf.org/)　公式サイトです。サンプルが豊富にあります。
- [TCPDFマニュアル（勝手訳）](http://tcpdf.penlabo.net/)　縦書きの方法は使いませんでしたが、リファレンスとして一番活用させていただきました。