NPlot にPDF出力機能を追加しました

イメージ 1

先日紹介したグラフ描画ライブラリNPlotに、PDF出力機能を追加してみました。
オリジナルのNPlotは出力先として、ベクタ系の画像をサポートしていないようだったので、
PDFを作成できるライブラリiTextSharpを使って、PDF出力機能を追加しました。
LaTeX用のグラフを出力できるプログラムを作りたいというのが開発動機です。

使い方は↓のような感じです。

FileStream fs = new FileStream("nplot_demo.pdf", FileMode.Create);
iTextSharp.text.Document document = new iTextSharp.text.Document(new iTextSharp.text.Rectangle(600.0f, 500.0f), 0,0,0,0);
iTextSharp.text.pdf.PdfWriter writer = iTextSharp.text.pdf.PdfWriter.GetInstance(document, fs);

document.Open();

iTextSharp.text.pdf.BaseFont bf = iTextSharp.text.pdf.BaseFont.CreateFont(
@"sazanami-gothic.ttf", iTextSharp.text.pdf.BaseFont.IDENTITY_H,
iTextSharp.text.pdf.BaseFont.EMBEDDED);
NPlot.PDF.PlotSurface2D s2d = new NPlot.PDF.PlotSurface2D(writer.DirectContent, bf);
s2d.Legend = new NPlot.Legend();
s2d.Legend.AttachTo(NPlot.PlotSurface2D.XAxisPosition.Top, NPlot.PlotSurface2D.YAxisPosition.Right);
s2d.Legend.VerticalEdgePlacement = NPlot.Legend.Placement.Inside;
s2d.Legend.HorizontalEdgePlacement = NPlot.Legend.Placement.Inside;
s2d.Legend.XOffset = -5;
s2d.Legend.YOffset = 0;
s2d.Legend.Font = new Font(FontFamily.GenericMonospace, 30.0f);

s2d.Add(barPlot1);
s2d.Add(line1);
s2d.Add(pointPlot1);
s2d.Refresh();
document.Close();

NPlotでは、グラフを作成するクラスに直接Graphicsオブジェクトを渡して描画を行うような構造になっています。GDI+のGraphicsオブジェクトはPDFをサポートしてませんし、継承などを利用してGraphicsのインタフェースを残して中身を変えられるようにもできていないので、NPlot本体の変更なしではPDFに対応できません。
そこで、NPlotが使っているGraphicsの機能をIGraphicsインタフェースに書き出して、グラフを作成するクラスにGraphicsを渡す代わりに、必要な描画機能をメンバとして持っているIGraphicsインタフェースを渡します。IGraphicsの実装として、Graphicsをほぼそのままラップしたものと、PdfContentByteをラップしたものの2つを用意し、PDF用ではPdfContentByteをラップしたものを使って、それ以外(Windowやビットマップ用、つまり従来からあるもの)ではGraphicsをラップしたものを使うようにしてあります。

NPlot.PDF.PlotSurface2D オブジェクトに数箇所、System.Drawing.Pen や System.Drawing.Brushを指定できるプロパティがあります。それらのオブジェクトを直接利用する代わりに、それらの属性を見て同じように描画するようにしています。なのでBitmapなどに書き出したときと完璧には一致しませんが、基本的な属性はそこそこ同じになるようにしてあります。
Penの属性として、Color、Width、Dash(あと確認してませんがEndCapとLineJoinも一応対応させてみました)が使えます。
BrushはSolidBrushのみ使えます。それ以外のBrushをいれても多分認識しません。

フォントは、System.Drawing.Fontのフォントサイズを参照して、NPlot.PDF.PlotSurface2Dに渡したBaseFontで描画します。なので、1つのグラフ内で複数のフォントサイズを使うことはできますが、複数の種類のフォントは使えません。

さらに新たな出力先(svg、ps、OpenGL、emf…他にもありそうですね)を追加したいときは、IGraphicsインタフェースを実装すればいいと思いますので、場合によってはNPlotの修正なしに追加できる可能性があります。

- http://www.geocities.jp/mocchi_2003/soft/NPlot_with_self_patch_051.zip
PDF機能追加済み NPlotバイナリ+ソース (2006 8/8現在の最新版へリンクしています)
- http://www.geocities.jp/mocchi_2003/soft_readme/nplot_with_pdf/nplot_demo.pdf
- http://www.nplot.com/
NPlot
- http://itextsharp.sourceforge.net/
iTextSharp

当記事に関する感想、疑問などありましたら、どしどし書き込んで下さい。


関連
- NPlot で値の表示範囲を設定する方法
- NPlot でマーカー付き折れ線グラフを使いたい!
- NPlot にPDF出力機能を追加しました
- グラフ作成ライブラリNPlot


もっちーの小部屋 自作のソフトを公開してます。
http://www.geocities.jp/mocchi_2003/index.html

グラフ作成ライブラリNPlot

イメージ 1

グラフが作成できるBSDライクなライセンスのライブラリを発見しました!

ずっと探してたのに見つからなかったんですが,関係ないものを探してたらひょっこりと
現れてくれました。


.Net Framework用のライブラリです。
| ライセンスは独自のものですが,下の文字列をクレジットやドキュメントに
| 入れることを条件にバイナリのみの配布を認めていますので、
| ほぼBSDライセンスと同じ扱いで問題なさそうです。
| "This product includes software developed as
| part of the NPlot library project available
| from: http://www.nplot.com/";
| (This product の部分は適宜変更してOKだそうです)
[追記]現在、バージョンは0.9.10.0です。0.9.9.2 => 0.9.10.0 の変更の際に、
ライセンスがBSDライセンスに変更された模様です。


とりあえずちょっと試してみたのでスクリーンショットを載せておきます。
|バージョンは現在の最新版と思われる 0.9.9.2 で試しました。
[追記]現在、最新バージョンは0.9.10.0です。

ソースは↓(VC# 2005 Expressで作成しました。)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace nplot_practice {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
NPlot.PointPlot pointPlot1 = new NPlot.PointPlot();
pointPlot1.DataSource = new int { 3, 1, 2, 3, 4, 3, 5, 2, 1, 1 };
this.plotSurface2D1.Add(pointPlot1);

NPlot.LinePlot linePlot1 = new NPlot.LinePlot();
// Excelの散布図のような使い方もできる
// ☆を描いてみました
linePlot1.AbscissaData = new int
{ 1, 5, 2, 3, 4, 1}; // 横軸
linePlot1.OrdinateData = new int[] { 3, 3, 1, 4, 1, 3}; // 縦軸
this.plotSurface2D1.Add(linePlot1);
this.plotSurface2D1.Refresh();
}
}
}




もっちーの小部屋 自作のソフトを公開してます。
http://www.geocities.jp/mocchi_2003/index.html

前回作ったライブラリを実際に使ってみた

イメージ 1

前回作った、ネイティブAPIを使ってテキストを画像データにするライブラリとOpenGLを使って、
簡単なサンプルを作ってみました。
↓ライブラリ紹介エントリ
http://blogs.yahoo.co.jp/mocchi_2003/38911375.html


OpenGLのためのウィンドウ周りのライブラリとしてfreeglutを使ってみました。
テクスチャのサイズが2のn乗になっていないので、OpenGLのバージョンが古い環境では
動かないかもしれません。

↓サンプルのソースはこちら
#include <cstdio>
#include <cmath>
#include <GL/freeglut.h>
#include "font_renderer.h"

#define WIDTH 700
#define HEIGHT 300

int window;
int width, height;
double rad = 0.0, scale = 1.0;
font_renderer fl;
GLuint fonttex;

void display_2d(void){
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, (GLdouble)width, (GLdouble)height, 0.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

glEnable(GL_TEXTURE_2D);
glPushMatrix();
glColor3d(1.0, 1.0, 1.0);
glTranslated*1;
glEnable(GL_TEXTURE_2D);
glGenTextures(1, &fonttex);
glBindTexture(GL_TEXTURE_2D, fonttex);
gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB8, fl.get_width(), fl.get_height(), GL_RGB, GL_UNSIGNED_BYTE, fl.get_image());
}

int main(int argc, char **argv){
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_GLUTMAINLOOP_RETURNS);
window = glutCreateWindow("Hello" );
init();
glutMainLoop();
glDeleteTextures(1 , &fonttex);
}


関連
- font_rendererバージョンアップ
- 前回作ったライブラリを実際に使ってみた
- ネイティブAPIを用いたマルチプラットフォームフォントレンダラーライブラリ



もっちーの小部屋 自作のソフトを公開してます。
http://www.geocities.jp/mocchi_2003/index.html

*1:GLdouble)(width/2), (GLdouble)(height/2), 0.0);
glScaled(scale, scale, 0.0);
glBindTexture(GL_TEXTURE_2D, fonttex);
glBegin(GL_POLYGON);
glTexCoord2d(0.0, 0.0); glVertex2i(-fl.get_width()/4, -fl.get_height()/4);
glTexCoord2d(0.0, 1.0); glVertex2i(-fl.get_width()/4, fl.get_height()/4);
glTexCoord2d(1.0, 1.0); glVertex2i( fl.get_width()/4, fl.get_height()/4);
glTexCoord2d(1.0, 0.0); glVertex2i( fl.get_width()/4, -fl.get_height()/4);
glEnd();
glPopMatrix();

glDisable(GL_TEXTURE_2D);
glPushMatrix();
glColor3d(1.0, 1.0, 0.6);
glTranslated((GLdouble)WIDTH/2.0,(GLdouble)HEIGHT/2.0,0.0);
glRotated(rad, 0.0, 0.0, 1.0);
glTranslated(-(GLdouble)WIDTH/2.0,-(GLdouble)HEIGHT/2.0,0.0);
glBegin(GL_TRIANGLES);
glVertex2d(150.0, 0.0);
glVertex2d(0.0, 280.0);
glVertex2d(280.0, 280.0);
glEnd();
glPopMatrix();


}

void display_3d(void){
}

void display(void){
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
display_3d();
display_2d();
glutSwapBuffers();
}

void reshape(int w , int h) {
width = w, height = h;
glViewport(0, 0, width, height);
display();
}

void timer(int id){
glMatrixMode(GL_MODELVIEW);
rad += 5.0;
scale = 2.0 + std::sin(rad/180.0 * 3.14159265358979);
display();
glutTimerFunc(10, timer, 0);
}

void imagesize(int &width, int &height){
int texsize = width, scale = 0;
if (texsize < height) texsize = height;
if (texsize > 0) --texsize;
while(texsize){
texsize /= 2;
++scale;
}
width = height = (1 << scale);
}

void init(){
glutReshapeWindow(WIDTH, HEIGHT);
glEnable(GL_DEPTH_TEST);
glEnable(GL_ALPHA_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutTimerFunc(10, timer, 0);

fl.render("☆OpenGLテスト★", 80, false, false, false,
font_renderer::rgb_type(128,128,128),font_renderer::rgb_type(0,255,255

ネイティブAPIを用いたマルチプラットフォームフォントレンダラーライブラリ

3Dを扱うプログラムや、画像ファイルに色々レンダリングを行うプログラムを作成する際に、
文字列(特に日本語などの多バイト文字列)を扱うのがちょっと面倒です。
そのようなことができるライブラリとして有名なものにFreeTypeなどがありますが、
これを利用するためには、適切にフォントを設定する必要があるため、プログラムに
組み込む際にフォントを組み込むか、フォントがある位置を指定する必要があったりする
ため、フォントの設定はどうでもいいからとにかく文字列を描画したい、というときには
面倒だったり実行ファイルのサイズが大きくなりすぎたりしてちょっとオーバースペックになってしまいます。

そこで、ネイティブAPIの機能(WindowsならTextOut、X11ならXmbDrawString)を利用して、
文字列のレンダリングを行うC++用ライブラリを作ってみました。
レンダリングした結果はRGB配列画像データとして取得できるようにしてありますので、
画像ファイルとして扱ったり、3Dプログラミング用テクスチャとして扱ったり、
(ちょっと冗長ですが)ウィンドウに描画したりと色々な用途に使えます。

レンダリングする文字列の特性として
文字高さ(ピクセル)、ボールド、イタリック、プロポーショナル/固定長、文字色、背景色をそれぞれ
指定できるようにしてあります。

サポートしているプラットフォームは Win32 と X11 です。

フォント種類にはさほどこだわらなくて、とにかく文字列を描画したいという方はぜひ!
zlib/libpngライセンスです。

↓ソースはこちら
http://osdn.net/downloads/users/23/23657/font_renderer_v1_20.zip

(または http://osdn.net/downloads/users/23/23656/nativefont_2_00.zip)

(07/08/17更新 バージョンアップのため)
-----
関連
- [http://blogs.yahoo.co.jp/mocchi_2003/50806312.html font_rendererバージョンアップ]
- [http://blogs.yahoo.co.jp/mocchi_2003/38991447.html 前回作ったライブラリを実際に使ってみた]
- [http://blogs.yahoo.co.jp/mocchi_2003/38911375.html ネイティブAPIを用いたマルチプラットフォームフォントレンダラーライブラリ]

------
もっちーの小部屋 自作のソフトを公開してます。
http://www.geocities.jp/mocchi_2003/index.html

WinMagnetバージョンアップ

自作ソフト WinMagnet をバージョンアップしました。
http://www.geocities.jp/mocchi_2003/soft.html

このソフトは、ウィンドウに貼り付けると、そのウィンドウを常に最前面状態にしたり、本来動かないウィンドウ(最大化状態のウィンドウ、スプラッシュウィンドウなど)を動かすことができたり、半透明化する機能を持っています。

今回のバージョンアップによって、サイズやボタン位置をカスタマイズできるようになりました。

どうぞ、お試しください^^。

LRU コンテナ

一定のサイズを保ち、新たに要素が加えられたときに
LRU (Least Recently Used) な要素を削除する、キャッシュのような働きをする
C++のコンテナを作ってみました。
内部で list と set を保持していて、要素の追加の他に、begin、end、rbegin、rendを
持っています。(といっても、内部で持っているlistのイテレータをそのまま丸投げしている
だけですが)

要素の追加は add メンバ関数で行います。
add メンバ関数は、2引数をとり、boolを返します。
1番目の引数は追加したい要素で、2番目の引数は削除された要素を返すための参照です。
戻り値が true のときに2番目の参照元に削除された要素が入ります。

サンプルの域を出ていない程度の規模のものですが、一応zlib/libpngライセンス
という形で公開します。

https://osdn.net/downloads/users/23/23651/lru.zip

メンバ関数ポインタ同士の比較

二つのメンバ関数ポインタ型変数が等しいかどうかを比較する処理を作ってたんですが、g++ と Visual C++ で一部違った処理になることを発見しました。
下に書いたプログラムを実行すると…

#include <cstdio>

class Test{
public:
  void aaa(void){ printf("a");}
  void bbb(void){ printf("a");}
};

using namespace std;
int main(void){
  typedef void (Test::*func_type)(void);
  func_type a = &Test::aaa, b = &Test::bbb;
  printf("%d\n", a == b);
}

g++ では 0 が、 VC++ (13.10.3077 と 14.00.50727.42 で試しました) では 1が帰ってきます。
VC++では Test::aaa と Test::bbb の内容が一緒のためにメンバ関数の実体を一つしか用意してない
のかもしれません。しかし、下の例のように普通の関数の場合は、

void gaaa(void){ printf("a");}
void gbbb(void){ printf("a");}
using namespace std;
int main(void){
  typedef void (*func_type)(void);
  func_type a = &gaaa, b = &gbbb;
  printf("%d\n", a == b);
}

どちらも 0 になるみたいです。