src/Eccube/Service/OrderPdfService.php line 32

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of EC-CUBE
  4.  *
  5.  * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved.
  6.  *
  7.  * http://www.ec-cube.co.jp/
  8.  *
  9.  * For the full copyright and license information, please view the LICENSE
  10.  * file that was distributed with this source code.
  11.  */
  12. namespace Eccube\Service;
  13. use Eccube\Common\EccubeConfig;
  14. use Eccube\Entity\BaseInfo;
  15. use Eccube\Entity\OrderItem;
  16. use Eccube\Entity\Shipping;
  17. use Eccube\Repository\BaseInfoRepository;
  18. use Eccube\Repository\OrderPdfRepository;
  19. use Eccube\Repository\OrderRepository;
  20. use Eccube\Repository\ShippingRepository;
  21. use Eccube\Twig\Extension\EccubeExtension;
  22. use Eccube\Twig\Extension\TaxExtension;
  23. use setasign\Fpdi\Tcpdf\Fpdi;
  24. /**
  25.  * Class OrderPdfService.
  26.  * Do export pdf function.
  27.  */
  28. class OrderPdfService extends Fpdi
  29. {
  30.     /** @var OrderRepository */
  31.     protected $orderRepository;
  32.     /** @var ShippingRepository */
  33.     protected $shippingRepository;
  34.     /** @var OrderPdfRepository */
  35.     protected $orderPdfRepository;
  36.     /** @var TaxRuleService */
  37.     protected $taxRuleService;
  38.     /**
  39.      * @var EccubeConfig
  40.      */
  41.     protected $eccubeConfig;
  42.     /**
  43.      * @var EccubeExtension
  44.      */
  45.     protected $eccubeExtension;
  46.     /**
  47.      * @var TaxExtension
  48.      */
  49.     protected $taxExtension;
  50.     // ====================================
  51.     // 定数宣言
  52.     // ====================================
  53.     /** ダウンロードするPDFファイルのデフォルト名 */
  54.     public const DEFAULT_PDF_FILE_NAME 'nouhinsyo.pdf';
  55.     /** FONT ゴシック */
  56.     public const FONT_GOTHIC 'kozgopromedium';
  57.     /** FONT 明朝 */
  58.     public const FONT_SJIS 'kozminproregular';
  59.     // ====================================
  60.     // 変数宣言
  61.     // ====================================
  62.     /** @var BaseInfo */
  63.     public $baseInfoRepository;
  64.     /** 購入詳細情報 ラベル配列
  65.      * @var array
  66.      */
  67.     protected $labelCell = [];
  68.     /*** 購入詳細情報 幅サイズ配列
  69.      * @var array
  70.      */
  71.     protected $widthCell = [];
  72.     /** 最後に処理した注文番号 @var string */
  73.     protected $lastOrderId null;
  74.     // --------------------------------------
  75.     // Font情報のバックアップデータ
  76.     /** @var string フォント名 */
  77.     protected $bakFontFamily;
  78.     /** @var string フォントスタイル */
  79.     protected $bakFontStyle;
  80.     /** @var string フォントサイズ */
  81.     protected $bakFontSize;
  82.     // --------------------------------------
  83.     // lfTextのoffset
  84.     protected $baseOffsetX 0;
  85.     protected $baseOffsetY = -4;
  86.     /** ダウンロードファイル名 @var string */
  87.     protected $downloadFileName null;
  88.     /** 発行日 @var string */
  89.     protected $issueDate '';
  90.     /**
  91.      * OrderPdfService constructor.
  92.      *
  93.      * @param EccubeConfig $eccubeConfig
  94.      * @param OrderRepository $orderRepository
  95.      * @param ShippingRepository $shippingRepository
  96.      * @param TaxRuleService $taxRuleService
  97.      * @param BaseInfoRepository $baseInfoRepository
  98.      * @param EccubeExtension $eccubeExtension
  99.      * @param TaxExtension $taxExtension
  100.      *
  101.      * @throws \Exception
  102.      */
  103.     public function __construct(EccubeConfig $eccubeConfigOrderRepository $orderRepositoryShippingRepository $shippingRepositoryTaxRuleService $taxRuleServiceBaseInfoRepository $baseInfoRepositoryEccubeExtension $eccubeExtensionTaxExtension $taxExtension)
  104.     {
  105.         $this->eccubeConfig $eccubeConfig;
  106.         $this->baseInfoRepository $baseInfoRepository->get();
  107.         $this->orderRepository $orderRepository;
  108.         $this->shippingRepository $shippingRepository;
  109.         $this->taxRuleService $taxRuleService;
  110.         $this->eccubeExtension $eccubeExtension;
  111.         $this->taxExtension $taxExtension;
  112.         parent::__construct();
  113.         // 購入詳細情報の設定を行う
  114.         // 動的に入れ替えることはない
  115.         $this->labelCell[] = '商品名 / 商品コード';
  116.         $this->labelCell[] = '数量';
  117.         $this->labelCell[] = '単価';
  118.         $this->labelCell[] = '金額(税込)';
  119.         $this->widthCell = [110.31221.724.5];
  120.         // Fontの設定しておかないと文字化けを起こす
  121.         $this->SetFont(self::FONT_SJIS);
  122.         // PDFの余白(上左右)を設定
  123.         $this->SetMargins(1520);
  124.         // ヘッダーの出力を無効化
  125.         $this->setPrintHeader(false);
  126.         // フッターの出力を無効化
  127.         $this->setPrintFooter(true);
  128.         $this->setFooterMargin();
  129.         $this->setFooterFont([self::FONT_SJIS''8]);
  130.     }
  131.     /**
  132.      * 注文情報からPDFファイルを作成する.
  133.      *
  134.      * @param array $formData
  135.      *                        [KEY]
  136.      *                        ids: 注文番号
  137.      *                        issue_date: 発行日
  138.      *                        title: タイトル
  139.      *                        message1: メッセージ1行目
  140.      *                        message2: メッセージ2行目
  141.      *                        message3: メッセージ3行目
  142.      *                        note1: 備考1行目
  143.      *                        note2: 備考2行目
  144.      *                        note3: 備考3行目
  145.      *
  146.      * @return bool
  147.      */
  148.     public function makePdf(array $formData)
  149.     {
  150.         // 発行日の設定
  151.         $this->issueDate '作成日: '.$formData['issue_date']->format('Y年m月d日');
  152.         // ダウンロードファイル名の初期化
  153.         $this->downloadFileName null;
  154.         // データが空であれば終了
  155.         if (!$formData['ids']) {
  156.             return false;
  157.         }
  158.         // 出荷番号をStringからarrayに変換
  159.         $ids explode(','$formData['ids']);
  160.         foreach ($ids as $id) {
  161.             $this->lastOrderId $id;
  162.             // 出荷番号から出荷情報を取得する
  163.             /** @var Shipping $Shipping */
  164.             $Shipping $this->shippingRepository->find($id);
  165.             if (!$Shipping) {
  166.                 // 出荷情報の取得ができなかった場合
  167.                 continue;
  168.             }
  169.             // テンプレートファイルを読み込む
  170.             $Order $Shipping->getOrder();
  171.             if ($Order->isMultiple()) {
  172.                 // 複数配送の時は読み込むテンプレートファイルを変更する
  173.                 $userPath $this->eccubeConfig->get('eccube_html_admin_dir').'/assets/pdf/nouhinsyo_multiple.pdf';
  174.             } else {
  175.                 $userPath $this->eccubeConfig->get('eccube_html_admin_dir').'/assets/pdf/nouhinsyo.pdf';
  176.             }
  177.             $this->setSourceFile($userPath);
  178.             // PDFにページを追加する
  179.             $this->addPdfPage();
  180.             // タイトルを描画する
  181.             $this->renderTitle($formData['title']);
  182.             // 店舗情報を描画する
  183.             $this->renderShopData();
  184.             // 注文情報を描画する
  185.             $this->renderOrderData($Shipping);
  186.             // メッセージを描画する
  187.             $this->renderMessageData($formData);
  188.             // 出荷詳細情報を描画する
  189.             $this->renderOrderDetailData($Shipping);
  190.             // 備考を描画する
  191.             $this->renderEtcData($formData);
  192.         }
  193.         return true;
  194.     }
  195.     /**
  196.      * PDFファイルを出力する.
  197.      *
  198.      * @return string|mixed
  199.      */
  200.     public function outputPdf()
  201.     {
  202.         return $this->Output($this->getPdfFileName(), 'S');
  203.     }
  204.     /**
  205.      * PDFファイル名を取得する
  206.      * PDFが1枚の時は注文番号をファイル名につける.
  207.      *
  208.      * @return string ファイル名
  209.      */
  210.     public function getPdfFileName()
  211.     {
  212.         if (!is_null($this->downloadFileName)) {
  213.             return $this->downloadFileName;
  214.         }
  215.         $this->downloadFileName self::DEFAULT_PDF_FILE_NAME;
  216.         if ($this->PageNo() == 1) {
  217.             $this->downloadFileName 'nouhinsyo-No'.$this->lastOrderId.'.pdf';
  218.         }
  219.         return $this->downloadFileName;
  220.     }
  221.     /**
  222.      * フッターに発行日を出力する.
  223.      */
  224.     public function Footer()
  225.     {
  226.         $this->Cell(00$this->issueDate00'R');
  227.     }
  228.     /**
  229.      * 作成するPDFのテンプレートファイルを指定する.
  230.      */
  231.     protected function addPdfPage()
  232.     {
  233.         // ページを追加
  234.         $this->AddPage();
  235.         // テンプレートに使うテンプレートファイルのページ番号を取得
  236.         $tplIdx $this->importPage(1);
  237.         // テンプレートに使うテンプレートファイルのページ番号を指定
  238.         $this->useTemplate($tplIdxnullnullnullnulltrue);
  239.         $this->setPageMark();
  240.     }
  241.     /**
  242.      * PDFに店舗情報を設定する
  243.      * ショップ名、ロゴ画像以外はdtb_helpに登録されたデータを使用する.
  244.      */
  245.     protected function renderShopData()
  246.     {
  247.         // 基準座標を設定する
  248.         $this->setBasePosition();
  249.         // ショップ名
  250.         $this->lfText(12558$this->baseInfoRepository->getShopName(), 8'B');
  251.         //郵便番号
  252.         $this->lfText(12163"\u{3012}"' ' mb_substr($this->baseInfoRepository->getPostalCode(), 03) . ' - ' mb_substr($this->baseInfoRepository->getPostalCode(), 34), 8);
  253.         // 都道府県+所在地
  254.         $text $this->baseInfoRepository->getPref().$this->baseInfoRepository->getAddr01();
  255.         $this->lfText(12566$text8);
  256.         $this->lfText(12569$this->baseInfoRepository->getAddr02(), 8);
  257.         // 電話番号
  258.         $text 'TEL: '.$this->baseInfoRepository->getPhoneNumber();
  259.         $this->lfText(12572$text8); // TEL・FAX
  260.         // メールアドレス
  261.         if (strlen($this->baseInfoRepository->getEmail01()) > 0) {
  262.             $text 'Email: '.$this->baseInfoRepository->getEmail01();
  263.             $this->lfText(12575$text8); // Email
  264.         }
  265.         // インボイス登録番号
  266.         if (!empty($this->baseInfoRepository->getInvoiceRegistrationNumber())) {
  267.             $text '登録番号: '.$this->baseInfoRepository->getInvoiceRegistrationNumber();
  268.             $this->lfText(12579$text8);
  269.         }
  270.         // user_dataにlogo.pngが配置されている場合は優先的に読み込む
  271.         $logoFile $this->eccubeConfig->get('eccube_html_dir').'/user_data/assets/pdf/logo.png';
  272.         if (!file_exists($logoFile)) {
  273.             $logoFile $this->eccubeConfig->get('eccube_html_admin_dir').'/assets/pdf/logo.png';
  274.         }
  275.         $this->Image($logoFile1244640);
  276.     }
  277.     /**
  278.      * メッセージを設定する.
  279.      *
  280.      * @param array $formData
  281.      */
  282.     protected function renderMessageData(array $formData)
  283.     {
  284.         $this->lfText(2770$formData['message1'], 8); // メッセージ1
  285.         $this->lfText(2774$formData['message2'], 8); // メッセージ2
  286.         $this->lfText(2778$formData['message3'], 8); // メッセージ3
  287.     }
  288.     /**
  289.      * PDFに備考を設定数.
  290.      *
  291.      * @param array $formData
  292.      */
  293.     protected function renderEtcData(array $formData)
  294.     {
  295.         // フォント情報のバックアップ
  296.         $this->backupFont();
  297.         $this->Cell(010''01'C'0'');
  298.         // 行頭近くの場合、表示崩れがあるためもう一個字下げする
  299.         if (270 <= $this->GetY()) {
  300.             $this->Cell(010''01'C'0'');
  301.         }
  302.         $this->SetFont(self::FONT_GOTHIC'B'9);
  303.         $this->MultiCell(06'< 備考 >''T'2'L'0'');
  304.         $this->SetFont(self::FONT_SJIS''8);
  305.         $this->Ln();
  306.         // rtrimを行う
  307.         $text preg_replace('/\s+$/us'''$formData['note1']."\n".$formData['note2']."\n".$formData['note3']);
  308.         $this->MultiCell(04$text''2'L'0'');
  309.         // フォント情報の復元
  310.         $this->restoreFont();
  311.     }
  312.     /**
  313.      * タイトルをPDFに描画する.
  314.      *
  315.      * @param string $title
  316.      */
  317.     protected function renderTitle($title)
  318.     {
  319.         // 基準座標を設定する
  320.         $this->setBasePosition();
  321.         // フォント情報のバックアップ
  322.         $this->backupFont();
  323.         // 文書タイトル(納品書・請求書)
  324.         $this->SetFont(self::FONT_GOTHIC''15);
  325.         $this->Cell(010$title02'C'0'');
  326.         $this->Cell(066''02'R'0'');
  327.         $this->Cell(50''00'R'0'');
  328.         // フォント情報の復元
  329.         $this->restoreFont();
  330.     }
  331.     /**
  332.      * 購入者情報を設定する.
  333.      *
  334.      * @param Shipping $Shipping
  335.      */
  336.     protected function renderOrderData(Shipping $Shipping)
  337.     {
  338.         // 基準座標を設定する
  339.         $this->setBasePosition();
  340.         // フォント情報のバックアップ
  341.         $this->backupFont();
  342.         // =========================================
  343.         // 購入者情報部
  344.         // =========================================
  345.         $Order $Shipping->getOrder();
  346.         // 購入者郵便番号(3012は郵便マークのUTFコード)
  347.         $text "\u{3012}" ' ' mb_substr($Shipping->getPostalCode(), 03) . ' - ' mb_substr($Shipping->getPostalCode(), 34);
  348.         $this->lfText(2243$text10);
  349.         // 購入者都道府県+住所1
  350.         // $text = $Order->getPref().$Order->getAddr01();
  351.         $text $Shipping->getPref().$Shipping->getAddr01();
  352.         $this->lfText(2747$text10);
  353.         $this->lfText(2751$Shipping->getAddr02(), 10); // 購入者住所2
  354.         // 購入者氏名
  355.         if (null !== $Shipping->getCompanyName()) {
  356.             // 会社名
  357.             $text $Shipping->getCompanyName();
  358.             $this->lfText(2757$text11);
  359.             // 氏名
  360.             $text $Shipping->getName01().' '.$Shipping->getName02().' 様';
  361.             $this->lfText(2763$text11);
  362.         } else {
  363.             $text $Shipping->getName01().' '.$Shipping->getName02().' 様';
  364.             $this->lfText(2759$text11);
  365.         }
  366.         // =========================================
  367.         // お買い上げ明細部
  368.         // =========================================
  369.         $this->SetFont(self::FONT_SJIS''10);
  370.         // ご注文日
  371.         $orderDate $Order->getCreateDate()->format('Y/m/d H:i');
  372.         if ($Order->getOrderDate()) {
  373.             $orderDate $Order->getOrderDate()->format('Y/m/d H:i');
  374.         }
  375.         $this->lfText(25125$orderDate10);
  376.         // 注文番号
  377.         $this->lfText(25135$Order->getOrderNo(), 10);
  378.         // 総合計金額
  379.         if (!$Order->isMultiple()) {
  380.             $this->SetFont(self::FONT_SJIS'B'15);
  381.             $paymentTotalText $this->eccubeExtension->getPriceFilter($Order->getPaymentTotal());
  382.             $this->setBasePosition(12095.5);
  383.             $this->Cell(57''00''0'');
  384.             $this->Cell(678$paymentTotalText02'R'0'');
  385.             $this->Cell(045''02''0'');
  386.         }
  387.         // フォント情報の復元
  388.         $this->restoreFont();
  389.     }
  390.     /**
  391.      * 購入商品詳細情報を設定する.
  392.      *
  393.      * @param Shipping $Shipping
  394.      */
  395.     protected function renderOrderDetailData(Shipping $Shipping)
  396.     {
  397.         $arrOrder = [];
  398.         // テーブルの微調整を行うための購入商品詳細情報をarrayに変換する
  399.         // =========================================
  400.         // 受注詳細情報
  401.         // =========================================
  402.         $i 0;
  403.         $isShowReducedTaxMess false;
  404.         $Order $Shipping->getOrder();
  405.         /* @var OrderItem $OrderItem */
  406.         foreach ($Shipping->getOrderItems() as $OrderItem) {
  407.             if (!$Order->isMultiple() && !$OrderItem->isProduct()) {
  408.                 continue;
  409.             }
  410.             // class categoryの生成
  411.             $classCategory '';
  412.             /** @var OrderItem $OrderItem */
  413.             if ($OrderItem->getClassCategoryName1()) {
  414.                 $classCategory .= ' [ '.$OrderItem->getClassCategoryName1();
  415.                 if ($OrderItem->getClassCategoryName2() == '') {
  416.                     $classCategory .= ' ]';
  417.                 } else {
  418.                     $classCategory .= ' * '.$OrderItem->getClassCategoryName2().' ]';
  419.                 }
  420.             }
  421.             // product
  422.             $productName $OrderItem->getProductName();
  423.             if (null !== $OrderItem->getProductCode()) {
  424.                 $productName .= ' / '.$OrderItem->getProductCode();
  425.             }
  426.             if ($classCategory) {
  427.                 $productName .= ' / '.$classCategory;
  428.             }
  429.             if ($this->taxExtension->isReducedTaxRate($OrderItem)) {
  430.                 $productName .= ' ※';
  431.                 $isShowReducedTaxMess true;
  432.             }
  433.             $arrOrder[$i][0] = $productName;
  434.             // 購入数量
  435.             $arrOrder[$i][1] = number_format($OrderItem->getQuantity());
  436.             // 税込金額(単価)
  437.             $arrOrder[$i][2] = $this->eccubeExtension->getPriceFilter($OrderItem->getPrice());
  438.             // 小計(商品毎)
  439.             $arrOrder[$i][3] = $this->eccubeExtension->getPriceFilter($OrderItem->getTotalPrice());
  440.             ++$i;
  441.         }
  442.         if (!$Order->isMultiple()) {
  443.             // =========================================
  444.             // 小計
  445.             // =========================================
  446.             $arrOrder[$i][0] = '';
  447.             $arrOrder[$i][1] = '';
  448.             $arrOrder[$i][2] = '';
  449.             $arrOrder[$i][3] = '';
  450.             ++$i;
  451.             $arrOrder[$i][0] = '';
  452.             $arrOrder[$i][1] = '';
  453.             $arrOrder[$i][2] = '商品合計';
  454.             $arrOrder[$i][3] = $this->eccubeExtension->getPriceFilter($Order->getSubtotal());
  455.             ++$i;
  456.             $arrOrder[$i][0] = '';
  457.             $arrOrder[$i][1] = '';
  458.             $arrOrder[$i][2] = '送料';
  459.             $arrOrder[$i][3] = $this->eccubeExtension->getPriceFilter($Order->getDeliveryFeeTotal());
  460.             ++$i;
  461.             $arrOrder[$i][0] = '';
  462.             $arrOrder[$i][1] = '';
  463.             $arrOrder[$i][2] = '手数料';
  464.             $arrOrder[$i][3] = $this->eccubeExtension->getPriceFilter($Order->getCharge());
  465.             ++$i;
  466.             $arrOrder[$i][0] = '';
  467.             $arrOrder[$i][1] = '';
  468.             $arrOrder[$i][2] = '値引き';
  469.             $arrOrder[$i][3] = $this->eccubeExtension->getPriceFilter($Order->getTaxableDiscount());
  470.             ++$i;
  471.             $arrOrder[$i][0] = '';
  472.             $arrOrder[$i][1] = '';
  473.             $arrOrder[$i][2] = '';
  474.             $arrOrder[$i][3] = '';
  475.             ++$i;
  476.             $arrOrder[$i][0] = '';
  477.             $arrOrder[$i][1] = '';
  478.             $arrOrder[$i][2] = '合計';
  479.             $arrOrder[$i][3] = $this->eccubeExtension->getPriceFilter($Order->getTaxableTotal());
  480.             ++$i;
  481.             $arrOrder[$i][0] = '';
  482.             $arrOrder[$i][1] = '';
  483.             $arrOrder[$i][2] = '';
  484.             $arrOrder[$i][3] = '';
  485.             foreach ($Order->getTaxFreeDiscountItems() as $Item) {
  486.                 ++$i;
  487.                 $arrOrder[$i][0] = '';
  488.                 $arrOrder[$i][1] = '';
  489.                 $arrOrder[$i][2] = $Item->getProductName();
  490.                 $arrOrder[$i][3] = $this->eccubeExtension->getPriceFilter($Item->getTotalPrice());
  491.             }
  492.             ++$i;
  493.             $arrOrder[$i][0] = '';
  494.             $arrOrder[$i][1] = '';
  495.             $arrOrder[$i][2] = '請求金額';
  496.             $arrOrder[$i][3] = $this->eccubeExtension->getPriceFilter($Order->getPaymentTotal());
  497.             if ($isShowReducedTaxMess) {
  498.                 ++$i;
  499.                 $arrOrder[$i][0] = '※は軽減税率対象商品です。';
  500.                 $arrOrder[$i][1] = '';
  501.                 $arrOrder[$i][2] = '';
  502.                 $arrOrder[$i][3] = '';
  503.             }
  504.         }
  505.         // PDFに設定する
  506.         $this->setFancyTable($this->labelCell$arrOrder$this->widthCell);
  507.         // インボイス対応
  508.         $this->backupFont();
  509.         $this->SetLineWidth(.3);
  510.         $this->SetFont(self::FONT_SJIS''6);
  511.         $this->Cell(00''01'C'0'');
  512.         // 行頭近くの場合、表示崩れがあるためもう一個字下げする
  513.         if (270 <= $this->GetY()) {
  514.             $this->Cell(00''01'C'0'');
  515.         }
  516.         $width array_reduce($this->widthCell, function ($n$w) {
  517.             return $n $w;
  518.         });
  519.         $this->SetX(20);
  520.         $message '';
  521.         foreach ($Order->getTotalByTaxRate() as $rate => $total) {
  522.             $message .= '('.$rate.'%対象: ';
  523.             $message .= $this->eccubeExtension->getPriceFilter($total);
  524.             $message .= ' 内消費税: '.$this->eccubeExtension->getPriceFilter($Order->getTaxByTaxRate()[$rate]).')'.PHP_EOL;
  525.         }
  526.         $this->MultiCell($width4$message0'R'0'');
  527.         $this->restoreFont();
  528.     }
  529.     /**
  530.      * PDFへのテキスト書き込み
  531.      *
  532.      * @param int    $x     X座標
  533.      * @param int    $y     Y座標
  534.      * @param string $text  テキスト
  535.      * @param int    $size  フォントサイズ
  536.      * @param string $style フォントスタイル
  537.      */
  538.     protected function lfText($x$y$text$size 0$style '')
  539.     {
  540.         // 退避
  541.         $bakFontStyle $this->FontStyle;
  542.         $bakFontSize $this->FontSizePt;
  543.         $this->SetFont(''$style$size);
  544.         $this->Text($x $this->baseOffsetX$y $this->baseOffsetY$text);
  545.         // 復元
  546.         $this->SetFont(''$bakFontStyle$bakFontSize);
  547.     }
  548.     /**
  549.      * Colored table.
  550.      *
  551.      * @param array $header 出力するラベル名一覧
  552.      * @param array $data   出力するデータ
  553.      * @param array $w      出力するセル幅一覧
  554.      */
  555.     protected function setFancyTable($header$data$w)
  556.     {
  557.         // フォント情報のバックアップ
  558.         $this->backupFont();
  559.         // 開始座標の設定
  560.         $this->setBasePosition(0149);
  561.         // Colors, line width and bold font
  562.         $this->SetFillColor(216216216);
  563.         $this->SetTextColor(0);
  564.         $this->SetDrawColor(000);
  565.         $this->SetLineWidth(.3);
  566.         $this->SetFont(self::FONT_SJIS'B'8);
  567.         $this->SetFont('''B');
  568.         // Header
  569.         $this->Cell(57''00''0'');
  570.         $count count($header);
  571.         for ($i 0$i $count; ++$i) {
  572.             $this->Cell($w[$i], 7$header[$i], 10'C'1);
  573.         }
  574.         $this->Ln();
  575.         // Color and font restoration
  576.         $this->SetFillColor(235235235);
  577.         $this->SetTextColor(0);
  578.         $this->SetFont('');
  579.         // Data
  580.         $fill 0;
  581.         $writeRow = function($row$cellHeight$fill$isBorder) use($w) {
  582.             $i 0;
  583.             $h 0;
  584.             foreach ($row as $col) {
  585.                 // 列の処理
  586.                 // TODO: 汎用的ではない処理。この指定は呼び出し元で行うようにしたい。
  587.                 // テキストの整列を指定する
  588.                 $align = ($i == 0) ? 'L' 'R';
  589.                 // セル高さが最大値を保持する
  590.                 if ($h >= $cellHeight) {
  591.                     $cellHeight $h;
  592.                 }
  593.                 // 最終列の場合は次の行へ移動
  594.                 // (0: 右へ移動(既定)/1: 次の行へ移動/2: 下へ移動)
  595.                 $ln = ($i == (count($row) - 1)) ? 0;
  596.                 $this->MultiCell(
  597.                     $w[$i], // セル幅
  598.                     $cellHeight// セルの最小の高さ
  599.                     !$isBorder $col ''// 文字列
  600.                     $isBorder 0// 境界線の描画方法を指定
  601.                     $align// テキストの整列
  602.                     $fill// 背景の塗つぶし指定
  603.                     $ln // 出力後のカーソルの移動方法
  604.                 );
  605.                 $h $this->getLastH();
  606.                 $i++;
  607.             }
  608.             return $cellHeight;
  609.         };
  610.         foreach ($data as $row) {
  611.             // 行の処理
  612.             $h 4;
  613.             $this->Cell(5$h''00''0'');
  614.             if ((277 $this->getY()) < ($h 4)) {
  615.                 $this->checkPageBreak($this->PageBreakTrigger 1);
  616.             }
  617.             $x $this->getX();
  618.             $y $this->getY();
  619.             // 1度目は文字だけ出力し、行の高さ最大を取得
  620.             $h $writeRow($row$h$fillfalse);
  621.             $this->setXY($x$y);
  622.             // 2度目に最大の高さに合わせて、境界線を描画
  623.             $writeRow($row$h$filltrue);
  624.             $fill = !$fill;
  625.         }
  626.         $h 4;
  627.         $this->Cell(5$h''00''0'');
  628.         $this->Cell(array_sum($w), 0'''T');
  629.         $this->SetFillColor(255);
  630.         // フォント情報の復元
  631.         $this->restoreFont();
  632.     }
  633.     /**
  634.      * 基準座標を設定する.
  635.      *
  636.      * @param int $x
  637.      * @param int $y
  638.      */
  639.     protected function setBasePosition($x null$y null)
  640.     {
  641.         // 現在のマージンを取得する
  642.         $result $this->getMargins();
  643.         // 基準座標を指定する
  644.         $actualX is_null($x) ? $result['left'] : $x;
  645.         $this->SetX($actualX);
  646.         $actualY is_null($y) ? $result['top'] : $y;
  647.         $this->SetY($actualY);
  648.     }
  649.     /**
  650.      * Font情報のバックアップ.
  651.      */
  652.     protected function backupFont()
  653.     {
  654.         // フォント情報のバックアップ
  655.         $this->bakFontFamily $this->FontFamily;
  656.         $this->bakFontStyle $this->FontStyle;
  657.         $this->bakFontSize $this->FontSizePt;
  658.     }
  659.     /**
  660.      * Font情報の復元.
  661.      */
  662.     protected function restoreFont()
  663.     {
  664.         $this->SetFont($this->bakFontFamily$this->bakFontStyle$this->bakFontSize);
  665.     }
  666. }