ダメプログラマの技術メモ

人生に疲れたダメプログラマが、PHP・Java・C#の技術メモや備忘録を適当に書いていくブログです。

pdf

EC-CUBEの納品書(PDF)のロゴ画像を削除(変更)する

またまたEC-CUBEの納品書ネタです。

今回は、納品書をカスタマイズして、右上にあるロゴ画像を削除してみたいと思います。

ソースコード

EC-CUBEのバージョンは2.11.5です。

修正するファイルは、/data/class/SC_Fpdf.phpです。
SC_Fpdf.phpで納品書に書き出すテキストやその位置を規定しています。

修正前
//ロゴ画像
$logo_file = PDF_TEMPLATE_REALDIR . 'logo.png';
$this->Image($logo_file, 124, 46, 40);

修正後
//ロゴ画像
// $logo_file = PDF_TEMPLATE_REALDIR . 'logo.png';
// $this->Image($logo_file, 124, 46, 40);

納品書

修正前の納品書
EC-CUBE_納品書_修正前


修正後の納品書
EC-CUBE_納品書_修正後

補足

ロゴ画像を変更したい場合は、/data/Smarty/templates/admin/pdf/log.pngを差し替えるだけでOKです。

EC-CUBEの納品書(PDF)に電話番号を追加する

EC-CUBEの受注管理画面には、デフォルトで納品書(PDF)の出力機能があります。

今回は、この納品書をカスタマイズして、電話番号を追加してみたいと思います。

ソースコード

EC-CUBEのバージョンは2.11.5です。

修正するファイルは、/data/class/SC_Fpdf.phpです。
SC_Fpdf.phpの中で、納品書に書き出すテキストやその位置を指定しています。

修正前
// 購入者情報
$text = "〒 ".$this->arrDisp['order_zip01']." - ".$this->arrDisp['order_zip02'];
$this->lfText(23, 43, $text, 10); //購入者郵便番号
$text = $this->arrPref[$this->arrDisp['order_pref']] . $this->arrDisp['order_addr01'];
$this->lfText(27, 47, $text, 10); //購入者都道府県+住所1
$this->lfText(27, 51, $this->arrDisp['order_addr02'], 10); //購入者住所2
$text = $this->arrDisp['order_name01']." ".$this->arrDisp['order_name02']." 様";
$this->lfText(27, 59, $text, 11); //購入者氏名

修正後
// 購入者情報
$text = "〒 ".$this->arrDisp['order_zip01']." - ".$this->arrDisp['order_zip02'];
$this->lfText(23, 43, $text, 10); //購入者郵便番号
$text = $this->arrPref[$this->arrDisp['order_pref']] . $this->arrDisp['order_addr01'];
$this->lfText(27, 47, $text, 10); //購入者都道府県+住所1
$this->lfText(27, 51, $this->arrDisp['order_addr02'], 10); //購入者住所2
$text = "TEL ".$this->arrDisp['order_tel01']." - ".$this->arrDisp['order_tel02']." - ".$this->arrDisp['order_tel03'];
$this->lfText(27, 55, $text, 10); //電話番号
$text = $this->arrDisp['order_name01']." ".$this->arrDisp['order_name02']." 様";
$this->lfText(27, 63, $text, 11); //購入者氏名

納品書

修正前の納品書
EC-CUBE_納品書_修正前

修正後の納品書
EC-CUBE_納品書_修正後

C#でWordやExcelからPDFを作成する(その3)

C#でWordやExcelからPDFを作成する(その2)の続きです。

WordやExcelからPDFを作成した後、サーバにPDFを送信するサンプルプログラムです。

クライアント側のソースコード(C#)

コーディング前にVisual Studioの「参照設定」から、以下のDLLをプロジェクトに追加します。

C:\Program Files\Common Files\Bullzip\PDF Printer\API\Microsoft.NET\Framework\v4.0\Bullzip.PDFWriter.dll

try {
    // PDF作成
    DateTime now = DateTime.Now;
    PdfSettings pdfSettings = new PdfSettings();
    pdfSettings.PrinterName = "Bullzip PDF Printer";
    string outputDir = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + "\\data\\";
    string outputFile = string.Format("temp_{0,0:00}-{1,0:00}-{2,0:00}.pdf", now.Hour, now.Minute, now.Second);
    pdfSettings.SetValue("Output", outputDir + outputFile);
    pdfSettings.SetValue("ShowPDF", "no");
    pdfSettings.SetValue("ShowSettings", "never");
    pdfSettings.SetValue("ShowSaveAS", "never");
    pdfSettings.SetValue("ShowProgress", "no");
    pdfSettings.SetValue("ShowProgressFinished", "no");
    pdfSettings.SetValue("ConfirmOverwrite", "yes");
    pdfSettings.WriteSettings(PdfSettingsFileType.RunOnce);
    PdfUtil.PrintFile(fileName, "Bullzip PDF Printer");
    // PDF作成完了まで待機
    Stream st = null;
    int sleepTime = 0;
    while (sleepTime < 10000)
    {
        try
        {
            if ((st = File.Open(
                  outputDir + outputFile,
                  FileMode.Open,
                  FileAccess.Read,
                  FileShare.None)) != null)
            {
                break;
            }
        }
        catch
        {
            System.Threading.Thread.Sleep(500);
            sleepTime += 500;
        }
    }
    if (st != null)
    {
        st.Close();
    }
    else
    {
        throw new ApplicationException("タイムアウトエラーが発生しました。");
    }
    // PDF送信
    WebClient wc = new WebClient();
    byte[] resData = wc.UploadFile("http://localhost/receive.php", outputDir + outputFile);
    string resText = System.Text.Encoding.UTF8.GetString(resData);
    if (String.Empty.Equals(resText))
    {
        throw new ApplicationException("PDFファイルのアップロードに失敗しました。");
    }
catch (Exception ex)
{
    MessageBox.Show("PDF Send Error.\n\n" + ex.Message);
}

変数fileNameにWordやExcelのファイルパスが入ります。

PDFを作成する部分は以下のサイトの丸写しですが、少し注意点もあるのでご説明します。
【参考サイト】
Printing Microsoft Word Documents to PDF using C#

「PDF作成」について

上記の参考サイトのサンプルプログラムは、
pdfSettings.SetValue("ShowPDF", "yes");
となっています。

「ShowPDF」はPDF作成後にPDFを表示するかどうかの設定です。

今回は必要ない動きなので、
pdfSettings.SetValue("ShowPDF", "no");
としました。

【参考サイト】
PDF Writer - Settings

「PDF作成完了まで待機」について

PdfUtil.PrintFile(fileName, "Bullzip PDF Printer");
を実行しても、すぐ次のステップに処理が移ってしまうようです。
(PrintFileメソッドは非同期処理?)

PDFを送信するのは、作成が完了した後でなければならなく、それまで処理を待機する必要があります。

当初、FileSystemWatcherクラスを使用していましたが、このクラスはファイル作成開始は検知できても、作成完了は検知できないようです。
そのため、Openメソッドで例外が発生しなくなる(どのプロセスもファイルを使用していない)まで、Sleepメソッドで待機するようにしています。

なお、タイムアウト時間(10,000ミリ秒)と待機間隔(500ミリ秒)は適当なので、環境に応じて適切な値を設定するべきです。

【参考サイト】
@IT:.NET TIPS 監視により作成/変更が通知されたファイルを開くには? - C# VB.NET

「PDF送信」について

PDFのアップロードは、WebClient.UploadFileメソッドを使用するのが最も簡単だと思います。
<input type="file">を使用した場合と同じような挙動になります。
ちなみに、フィールド名は「file」で固定されます。

サーバ側のソースコード(PHP)

<?php
move_uploaded_file($_FILES["file"]["tmp_name"], "./" . $_FILES["file"]["name"]);
echo $_FILES["file"]["name"];

一時ファイル(PDF)を公開ディレクトリに移動して、ファイル名をクライアント側に返却しているだけです。

クライアント側でWebClient.UploadFileメソッドを使用しているため、$_FILESの1次元目は「file」になっています。


サンプルプログラムは実用レベルではありませんが、Bullzip PDF Printerを使用してWordやExcelからPDFを作成し、それをサーバに送信するところまで確認できました。

追記 2012/04/24

当記事のコメント欄においてご指摘を頂きました。

(1)プリンタ名
決め打ちだけど、プリンタ名は変更できる。
従って、レジストリからプリンタドライバ(これは変らない)で探すようにしないと正確なプリンタ名を得られない。

WMIのWin32_Printerクラスを使用すれば、プリンタドライバ名が取得できそうです。

プリンタドライバ名とプリンタ名を取得するサンプルプログラム
using System.Management;
// 参照設定にSystem.Managementを追加する必要があります。

private void button1_Click(object sender, EventArgs e)
{
    ObjectQuery oq = new ObjectQuery("SELECT * FROM Win32_Printer");
    ManagementObjectSearcher mos = new ManagementObjectSearcher(oq);
    ManagementObjectCollection moc = mos.Get();
    foreach (ManagementObject mo in moc)
    {
        // プリンタドライバ名
        Console.WriteLine(mo["DriverName"]);
        // プリンタ名
        Console.WriteLine(mo["Name"]);
    }
}

【参考サイト】
取得する方法は一つではない - 新日々此何有哉

(2)PDF化の完了を知る
処理が終わるとログを出力するので、名前付きパイプを作り、SetValueを使って"StatusFile"にその名前付を指定する。
名前付きパイプは相手側がファイルを開くまで待機するAPIがある。
更に、こちらでパイプを読み出していると、ある時点からエラーになる。
これは相手がパイプを閉じた結果である。
つまり、処理完了を知ることができる。
パイプはディスク上にファイルは作らないので後始末もいらない。
もし、PDFのディスク上のファイルが不要なら、出力先にこのパイプを使い、こちらは読み出したデータを転送先に送れば、もっと速く処理することができる。

処理の流れは以下のようになるのかなぁ?
  1. パイプ名(StatusFileに設定する値)を定義する。
  2. パイプを作成する。
  3. StatusFileにパイプ名を指定する。
  4. PDF印刷を開始する。
  5. Bullzip PDF Printerがパイプ(StatusFile)を開くまで待機する。
  6. Bullzip PDF Printerがパイプを開いたら、エラーが発生するまでStatusFileの内容を読み込む。
  7. エラーが発生したら、PDFの作成が完了したと見做す。
【参考サイト】
VBA 複数Word→PDF変換→Zip圧縮 | OKWave

う~ん、うまくいかない。。。

「5. Bullzip PDF Printerがパイプ(StatusFile)を開くまで待機する。」は、NamedPipeServerStream.WaitForConnectionメソッドを使うことで実現しているのですが、StatusFileが作成されても待機されたままで、次に処理が移りませんでした。

NamedPipeClientStream.Connectメソッドをコールしていないせいかなぁと思って、PDF印刷処理を別スレッドにして、印刷を開始した後にNamedPipeClientStream.Connectメソッドをコールしましたが、それでも駄目でした。

数時間ほど悩んでいたところ、参考サイトに、
'★パイプはプロセス間継承するので記述子を設定する
継承有無.サイズ = Len(継承)
継承有無.継承 = 1
という気になる文が。。。

確かにNamedPipeServerStreamのコンストラクタには、HandleInheritability列挙体(ハンドルを子プロセスが継承できるかどうか指定する)を引数に持つものがあります。
これだ!と思って、HandleInheritability.Inheritableを指定してみたところ、、、

やはり、駄目でした。

そもそも、NamedPipeServerStreamクラスやNamedPipeClientStreamクラスを使用すること自体が間違っているのかも。。。

せっかくご指摘して頂いたのに、自分にはかなり難しい内容で対応できませんでした(´;ω;`)

C#でWordやExcelからPDFを作成する(その2)

C#でWordやExcelからPDFを作成する(その1)で、Bullzip PDF Printerを使用するということを述べました。

開発するに当たり、Visual StudioとBullzip PDF Printerのインストールが必要になります。

ダウンロードサイト

Visual C# 2010 Express
http://www.microsoft.com/japan/msdn/vstudio/express/

Bullzip PDF Printer
http://www.bullzip.com/products/pdf/info.php#download

インストール方法

Visual StudioもBullzip PDF PrinterもインストールオプションはデフォルトのままでOKです。

注意点

Bullzip PDF Printerに関しては、少しだけ注意することがあります。

Bullzip PDF Printerは原則フリーなのですが、再配布時と1会社につき利用者が10人以上の場合はライセンス料が発生するらしいです。

You need a redistribution license if you want to redistribute the PDF Printer on your own redistribution media. This could be a DVD or your web site. A redistribution license offers a royalty free redistribution model, which is what most software companies want. The alternative to a redistribution license is to send your users to this web page to download the program themselves. In this model your users are subject to the normal license conditions and must buy a license if they have more than 10 users per company.

英語が苦手なので、読み間違っていたらスミマセン。

まぁ、個人レベルで使用する分には問題ないでしょう。


インストールが終われば、いよいよコーディングなのですが、ちょっと時間が、、、
(;><)

次回の更新時に、ソースコードを提示します。

C#でWordやExcelからPDFを作成する(その1)

またまた厄介な問題に直面してしまいました。

表題どおりのことをやりたいのですが、一つ問題があります。
それは、PDFを作成した後、ある処理を行ってからPDFをサーバに送信したいのです。
しかも、それら全ての処理をシームレスに行う必要があります。

PrimoPDF等の仮想プリンタを使用すればPDFは簡単に作成できますが、PDFを作成した後の「ある処理→サーバ送信」の連携の部分が問題になってきます。

かと言って、ダメプログラマの私が仮想プリンタを一から作れるわけもなく、どうしたらよいか迷っていたところ、、、

以下のサイトに答えがありました。
(管理人様に感謝です)

C#というか.NETでExcelやWordをプログラマブルにPDF化する(BullZip)

Bullzip PDF Printerという仮想プリンタにはCOMが提供されているみたいです。
C#側からCOMを参照すれば、PDF作成からサーバ送信まで一連の流れがシームレスに実現できそうです。

具体的な方法については、また日を改めてご説明します。
(というか、上記のサイトの内容を見れば、全部わかりますね。。。
記事検索
管理人が利用しているレンタルサーバ
管理人が利用しているドメイン会社
  • ライブドアブログ