20190522のC#に関する記事は7件です。

[C++/C#]C#をC++/CLIでラップしてC++アプリから呼ぶ

やりたいこと

すでにC#で作ってあるライブラリ(dll)を、C++のアプリから呼びたい。
方法として、C#のdllをC++/CLIでラップして、C++から呼ぶということを試してみる。

C#dllを作る

クラスライブラリ(.NET Framework)のプロジェクトを新規追加し、下記のコードを追加する。

DllCs.cs
namespace DllCs
{
    public class DllCsClass
    {
        public static int Add(int a, int b) => a + b;
    }
}

C++/CLIのラッパーを作る

VisualC++ > CLR > CLRクラスライブラリ を選択し、プロジェクトを作成し、下記のコードを追加する。

CsWrapperCppCLI.cpp
// C#をC++/CLIでラップするラッパー関数群
#include "stdafx.h"
#include "CsWrapperCppCLI.h"

// C#(.net)のクラスのnamespace
using namespace DllCs;

// C++/CLIラッパー関数
double __cdecl Add(int a, int b)
{
    // ここで、C#(.net)のメソッドを呼ぶ
    int ret = DllCsClass::Add(a, b);

    return ret;
}

CsWrapperCppCLI.h
#pragma once

// エクスポートとインポートの切り替え
#ifdef VC_DLL_EXPORTS
#undef VC_DLL_EXPORTS
#define VC_DLL_EXPORTS extern "C" __declspec(dllexport)
#else
#define VC_DLL_EXPORTS extern "C" __declspec(dllimport)
#endif

VC_DLL_EXPORTS double __cdecl Add(int a, int b);

※参照に、DllCsのプロジェクトを追加。
※このあたりの書き方は、通常のC++からC++のDLLを呼ぶ方法と同じ。
 →こちら参照

ラッパーを呼ぶC++アプリを作る

C++のコンソールアプリのプロジェクトを作成し、下記コードを追加する。

ConsoleApplication1.cpp
#include "pch.h"
#include <iostream>
#include <Windows.h>

using namespace std; 

typedef int(*Add)(int p1, int p2);

int main()
{
    auto dll = ::LoadLibrary(L"CsWrapperCppCLI.dll");
    auto add = reinterpret_cast<Add>(::GetProcAddress(dll, "Add"));
    wcout << add(10, 20) << endl;

    system("pause");
}

※ここも、通常のC++からC++のDLLを呼ぶ方法(動的)と同じ。
 →こちら参照

参考

C#のメソッドをC++から呼ぶ方法
https://qiita.com/tetsurom/items/a0ad9bd24dbe513afdc4

コード

https://github.com/tera1707/WPF-/tree/master/026_CallCsDllViaCppCLI

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[C++/C#]C#をCOM参照可能にしてC++アプリから呼ぶ2

やりたいこと

前回の続きで、C#のdllを、COM参照可能にしたC#dllでラップして、C++アプリから呼ぶことができるのは分かったが、そのC#のdllCOM参照可能にしたC#dllC++アプリのそれぞれのプラットフォーム(x64/x86/ANYCPU)の組み合わせで、動く動かないをはっきりさせたい。

関連項目目次

実験内容

前回のプロジェクト一式を使用して、C#のdllCOM参照可能にしたC#dllC++アプリについて、それぞれx64x86ANYCPUのdllを作成する。(C++アプリについてはANYCPUはなし)

それを組み合わせて実行し、うまく動作するかどうかを確認する。

動かすときは、下のようなexerun_64.batexerun_86.batを作成し、COM参照可能にしたC#dllを登録してからC++アプリを実行する。

exerun_64.bat
@echo off
cd %~dp0
regasm_64 /u DllCsComWrapper.dll
regasm_86 /u DllCsComWrapper.dll

regasm_64 /codebase DllCsComWrapper.dll
pause
start /wait ConsoleApplication1.exe
echo exeからの戻り値は %ERRORLEVEL% です

regasm_64 /u DllCsComWrapper.dll
regasm_86 /u DllCsComWrapper.dll
pause
exerun_86.bat
@echo off
cd %~dp0
regasm_64 /u DllCsComWrapper.dll
regasm_86 /u DllCsComWrapper.dll

regasm_86 /codebase DllCsComWrapper.dll
pause
start /wait ConsoleApplication1.exe
echo exeからの戻り値は %ERRORLEVEL% です

regasm_64 /u DllCsComWrapper.dll
regasm_86 /u DllCsComWrapper.dll
pause

前提

x64,86それぞれのregasm.exeをとってきてそれぞれregasm_64.exe、regasm_86.exeに改名し、同じ階層に置いている。

ポイント

x64、x86のラッパーdllは、x64.x86それぞれのregasmでしか登録できない。
anycpuのラッパーdllは、x64.x86どちらのregasmでも登録できる。
その際、x64のregasmで登録すればx64向けのdll、x86のregasmで登録すればx86向けのdllとなる。

便利bat

各dll、exeのターゲットが何か(x86/x64)を出力する。
※AnyCPUはx86と出る様子。

CheckBinaryTarget.bat
@echo off
echo process start...

rem 開発用コマンドプロンプト(環境によってはパス違うかも)
call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\Tools\VsDevCmd.bat"

cd %~dp0

SET TARGET1="ConsoleApplication1.exe"
SET TARGET2="DllCsComWrapper.dll"
SET TARGET3="DllTestCs.dll"

echo %TARGET1%
dumpbin /headers %TARGET1% | findstr machine

echo %TARGET2%
dumpbin /headers %TARGET2% | findstr machine

echo %TARGET3%
dumpbin /headers %TARGET3% | findstr machine

pause

結果

下記のようになった。※C#dllのAnyCPUは試してない
image.png

結果としては、

  • C#dllがx64であれば、ラッパーもC++アプリもx64でないといけない
  • C#dllがx86であれば、ラッパーもC++アプリもx86でないといけない

という普通な結果となった。

しかし

COMは、もうちょっと便利な、x64ともx86とも連携できる便利な奴、ではなかった?
イメージが間違えていた??

このHPにあるように、なにかやり方があるのかも。(未検証)
ここにあるのは(32bitDLLを64bitプロセスから呼ぶ方法だが...)
https://blog.mattmags.com/2007/06/30/accessing-32-bit-dlls-from-64-bit-code/

参考

64bitアプリと32bit DLLの混在
https://www.backyrd.net/entry/20130704/1372920343

コード

https://github.com/tera1707/WPF-/tree/master/025_CallCsDllFromCpp

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Windows FormsのMSChartをユーザー側の操作で編集する&ボーダーラインを設定する

まずはサンプルプログラムの動きから
 20190522.gif

そしてソース

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;

namespace ChartTest
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            List<ChartData> data = new List<ChartData>
                                       {
                                           new ChartData {X = 0, Y = 3, Max = 7},
                                           new ChartData {X = 1, Y = 4, Max = 5},
                                           new ChartData {X = 2, Y = 8, Max = 9},
                                           new ChartData {X = 3, Y = 6, Max = 11},
                                           new ChartData {X = 4, Y = 7, Max = 8},
                                           new ChartData {X = 5, Y = 3, Max = 4},
                                           new ChartData {X = 6, Y = 1, Max = 7},
                                           new ChartData {X = 7, Y = 0, Max = 5},
                                           new ChartData {X = 8, Y = 5, Max = 6},
                                           new ChartData {X = 9, Y = 2, Max = 5}
                                       };

            chart1.DataSource = data;

            Series series = chart1.Series.Add("Sample");
            series.MarkerStyle = MarkerStyle.Circle;
            series.MarkerSize = 8;
            series.XValueMember = "X";
            series.YValueMembers = "Y";
            series.ChartType = SeriesChartType.Line;
            chart1.ChartAreas[0].AxisX.Minimum = 0;
            chart1.ChartAreas[0].AxisX.Maximum = 9;

            Series border = chart1.Series.Add("Border");
            border.IsVisibleInLegend = false;
            border.XValueMember = "X";
            border.YValueMembers = "Max";
            border.ChartType = SeriesChartType.Line;
            border.Color = Color.Red;
            border.BorderWidth = 2;

        }

        //指定した座標のグラフ要素を保持するオブジェクト
        private HitTestResult hit = null;

        //直線状態のBorderをクリックしたか
        private bool IsHitBorderLine(string seriesName)
        {
            return checkBox1.Checked && seriesName == "Border";
        }

        private void chart1_MouseDown(object sender, MouseEventArgs e)
        {
            if (!e.Button.HasFlag(MouseButtons.Left))
            {
                return;
            }

            //マウス座標上にヒットしたグラフ要素を取得し、それがDataPointかつ存在するものかチェックする
            HitTestResult test = chart1.HitTest(e.X, e.Y);
            if (test.PointIndex < 0 || test.ChartElementType != ChartElementType.DataPoint)
            {
                return;
            }
            hit = test;
        }

        private void chart1_MouseMove(object sender, MouseEventArgs e)
        {
            if (!e.Button.HasFlag(MouseButtons.Left) || hit == null)
            {
                return;
            }

            ChartArea ca = chart1.ChartAreas[0];
            //NOTE:マウスの座標の範囲チェックをしないと例外が出る
            double dx = ca.AxisX.PixelPositionToValue(e.X);
            double dy = ca.AxisY.PixelPositionToValue(e.Y);

            var curPoint = hit.Series.Points[hit.PointIndex];
            if (IsHitBorderLine(hit.Series.Name))
            {
                //全ての線を移動させる
                var points = hit.Series.Points;
                foreach (var point in points)
                {
                    point.YValues[0] = dy;
                }
            }
            else
            {
                curPoint.YValues[0] = dy;
            }
            chart1.Refresh();
        }

        private void chart1_MouseUp(object sender, MouseEventArgs e)
        {
            if (hit == null)
            {
                return;
            }

            var curPoint = hit.Series.Points[hit.PointIndex];
            var seriesName = hit.Series.Name;
            //取得したグラフ要素を捨てる
            hit = null;

            var points = chart1.Series["Sample"].Points;
            var boderPoints = chart1.Series["Border"].Points;

            if (IsHitBorderLine(seriesName))
            {
                //Borderの最大Y軸に全データを合わせる
                foreach (var point in points)
                {
                    if (point.YValues[0] <= curPoint.YValues[0])
                    {
                        continue;
                    }
                    point.YValues[0] = curPoint.YValues[0];
                }
            }
            else
            {
                //クリックしたX軸のデータをBorder以下のY軸にする
                var point = points.Where(v => v.XValue == curPoint.XValue).FirstOrDefault();
                var borderPoint = boderPoints.Where(v => v.XValue == curPoint.XValue).FirstOrDefault();
                if (point == null || point.YValues[0] <= borderPoint.YValues[0])
                {
                    return;
                }

                point.YValues[0] = borderPoint.YValues[0];
            }

            chart1.Refresh();
        }

        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            if (checkBox1.Checked)
            {
                //Borderの全Pointを最大値に合わせる(直線にする)
                var points = chart1.Series["Border"].Points;
                var maxYValue = points.Max(v => v.YValues[0]);
                foreach(var point in points)
                {
                    point.YValues[0] = maxYValue;
                }
                chart1.Refresh();
            }
        }
    }

    public class ChartData
    {
        public int X { get; set; }

        public int Y { get; set; }

        public int Max { get; set; }
    }
}

(ネストが複雑で汚いっす…)

ポイント

  • HitTestResultオブジェクトを利用すると指定したXY座標のグラフ要素が取得できる
  • ボーダーラインはシリーズに含めて、グラフが変化したときにボーダーラインを超えたか確認する

最後に

参考にしたコードはこちらのQ&Aからです。
MSChartでグラフをユーザー側で編集したい場合の作り方や、ボーダーラインを作りたいときの参考記事が見つからなかったので備忘録がてら作りました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Chartをユーザー操作で編集&ボーダーラインを設定

サンプルプログラム

 20190522.gif

ソース

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;

namespace ChartTest
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            List<ChartData> data = new List<ChartData>
                                       {
                                           new ChartData {X = 0, Y = 3, Max = 7},
                                           new ChartData {X = 1, Y = 4, Max = 5},
                                           new ChartData {X = 2, Y = 8, Max = 9},
                                           new ChartData {X = 3, Y = 6, Max = 11},
                                           new ChartData {X = 4, Y = 7, Max = 8},
                                           new ChartData {X = 5, Y = 3, Max = 4},
                                           new ChartData {X = 6, Y = 1, Max = 7},
                                           new ChartData {X = 7, Y = 0, Max = 5},
                                           new ChartData {X = 8, Y = 5, Max = 6},
                                           new ChartData {X = 9, Y = 2, Max = 5}
                                       };

            chart1.DataSource = data;

            Series series = chart1.Series.Add("Sample");
            series.MarkerStyle = MarkerStyle.Circle;
            series.MarkerSize = 8;
            series.XValueMember = "X";
            series.YValueMembers = "Y";
            series.ChartType = SeriesChartType.Line;
            chart1.ChartAreas[0].AxisX.Minimum = 0;
            chart1.ChartAreas[0].AxisX.Maximum = 9;

            Series border = chart1.Series.Add("Border");
            border.IsVisibleInLegend = false;
            border.XValueMember = "X";
            border.YValueMembers = "Max";
            border.ChartType = SeriesChartType.Line;
            border.Color = Color.Red;
            border.BorderWidth = 2;

        }

        //指定した座標のグラフ要素を保持するオブジェクト
        private HitTestResult hit = null;

        //直線状態のBorderをクリックしたか
        private bool IsHitBorderLine(string seriesName)
        {
            return checkBox1.Checked && seriesName == "Border";
        }

        private void chart1_MouseDown(object sender, MouseEventArgs e)
        {
            if (!e.Button.HasFlag(MouseButtons.Left))
            {
                return;
            }

            //マウス座標上にヒットしたグラフ要素を取得し、それがDataPointかつ存在するものかチェックする
            HitTestResult test = chart1.HitTest(e.X, e.Y);
            if (test.PointIndex < 0 || test.ChartElementType != ChartElementType.DataPoint)
            {
                return;
            }
            hit = test;
        }

        private void chart1_MouseMove(object sender, MouseEventArgs e)
        {
            if (!e.Button.HasFlag(MouseButtons.Left) || hit == null)
            {
                return;
            }

            ChartArea ca = chart1.ChartAreas[0];
            //NOTE:マウスの座標の範囲チェックをしないと例外が出る
            double dx = ca.AxisX.PixelPositionToValue(e.X);
            double dy = ca.AxisY.PixelPositionToValue(e.Y);

            var curPoint = hit.Series.Points[hit.PointIndex];
            if (IsHitBorderLine(hit.Series.Name))
            {
                //全ての線を移動させる
                var points = hit.Series.Points;
                foreach (var point in points)
                {
                    point.YValues[0] = dy;
                }
            }
            else
            {
                curPoint.YValues[0] = dy;
            }
            chart1.Refresh();
        }

        private void chart1_MouseUp(object sender, MouseEventArgs e)
        {
            if (hit == null)
            {
                return;
            }

            var curPoint = hit.Series.Points[hit.PointIndex];
            var seriesName = hit.Series.Name;
            //取得したグラフ要素を捨てる
            hit = null;

            var points = chart1.Series["Sample"].Points;
            var boderPoints = chart1.Series["Border"].Points;

            if (IsHitBorderLine(seriesName))
            {
                //Borderの最大Y軸に全データを合わせる
                foreach (var point in points)
                {
                    if (point.YValues[0] <= curPoint.YValues[0])
                    {
                        continue;
                    }
                    point.YValues[0] = curPoint.YValues[0];
                }
            }
            else
            {
                //クリックしたX軸のデータをBorder以下のY軸にする
                var point = points.Where(v => v.XValue == curPoint.XValue).FirstOrDefault();
                var borderPoint = boderPoints.Where(v => v.XValue == curPoint.XValue).FirstOrDefault();
                if (point == null || point.YValues[0] <= borderPoint.YValues[0])
                {
                    return;
                }

                point.YValues[0] = borderPoint.YValues[0];
            }

            chart1.Refresh();
        }

        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            if (checkBox1.Checked)
            {
                //Borderの全Pointを最大値に合わせる(直線にする)
                var points = chart1.Series["Border"].Points;
                var maxYValue = points.Max(v => v.YValues[0]);
                foreach(var point in points)
                {
                    point.YValues[0] = maxYValue;
                }
                chart1.Refresh();
            }
        }
    }

    public class ChartData
    {
        public int X { get; set; }

        public int Y { get; set; }

        public int Max { get; set; }
    }
}

(ネストが複雑で汚いっす…)

ポイント

  • HitTestResultオブジェクトを利用すると指定したXY座標のグラフ要素が取得できる
  • ボーダーラインはシリーズに含めて、グラフが変化したときにボーダーラインを超えたか確認する

最後に

参考にしたコードはこちらのQ&Aからです。
MSChartでグラフをユーザー側で編集したい場合の作り方や、ボーダーラインを作りたいときの参考記事が見つからなかったので備忘録がてら作りました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ChartのDataPointをドラッグ&ドロップで編集し、ボーダーラインを設定する

サンプルプログラム

 20190522.gif

ソース

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;

namespace ChartTest
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            List<ChartData> data = new List<ChartData>
                                       {
                                           new ChartData {X = 0, Y = 3, Max = 7},
                                           new ChartData {X = 1, Y = 4, Max = 5},
                                           new ChartData {X = 2, Y = 8, Max = 9},
                                           new ChartData {X = 3, Y = 6, Max = 11},
                                           new ChartData {X = 4, Y = 7, Max = 8},
                                           new ChartData {X = 5, Y = 3, Max = 4},
                                           new ChartData {X = 6, Y = 1, Max = 7},
                                           new ChartData {X = 7, Y = 0, Max = 5},
                                           new ChartData {X = 8, Y = 5, Max = 6},
                                           new ChartData {X = 9, Y = 2, Max = 5}
                                       };

            chart1.DataSource = data;

            Series series = chart1.Series.Add("Sample");
            series.MarkerStyle = MarkerStyle.Circle;
            series.MarkerSize = 8;
            series.XValueMember = "X";
            series.YValueMembers = "Y";
            series.ChartType = SeriesChartType.Line;
            chart1.ChartAreas[0].AxisX.Minimum = 0;
            chart1.ChartAreas[0].AxisX.Maximum = 9;

            Series border = chart1.Series.Add("Border");
            border.IsVisibleInLegend = false;
            border.XValueMember = "X";
            border.YValueMembers = "Max";
            border.ChartType = SeriesChartType.Line;
            border.Color = Color.Red;
            border.BorderWidth = 2;

        }

        //指定した座標のグラフ要素を保持するオブジェクト
        private HitTestResult hit = null;

        //直線状態のBorderをクリックしたか
        private bool IsHitBorderLine(string seriesName)
        {
            return checkBox1.Checked && seriesName == "Border";
        }

        private void chart1_MouseDown(object sender, MouseEventArgs e)
        {
            if (!e.Button.HasFlag(MouseButtons.Left))
            {
                return;
            }

            //マウス座標上にヒットしたグラフ要素を取得し、それがDataPointかつ存在するものかチェックする
            HitTestResult test = chart1.HitTest(e.X, e.Y);
            if (test.PointIndex < 0 || test.ChartElementType != ChartElementType.DataPoint)
            {
                return;
            }
            hit = test;
        }

        private void chart1_MouseMove(object sender, MouseEventArgs e)
        {
            if (!e.Button.HasFlag(MouseButtons.Left) || hit == null)
            {
                return;
            }

            ChartArea ca = chart1.ChartAreas[0];
            //NOTE:マウスの座標の範囲チェックをしないと例外が出る
            double dx = ca.AxisX.PixelPositionToValue(e.X);
            double dy = ca.AxisY.PixelPositionToValue(e.Y);

            var curPoint = hit.Series.Points[hit.PointIndex];
            if (IsHitBorderLine(hit.Series.Name))
            {
                //全ての線を移動させる
                var points = hit.Series.Points;
                foreach (var point in points)
                {
                    point.YValues[0] = dy;
                }
            }
            else
            {
                curPoint.YValues[0] = dy;
            }
            chart1.Refresh();
        }

        private void chart1_MouseUp(object sender, MouseEventArgs e)
        {
            if (hit == null)
            {
                return;
            }

            var curPoint = hit.Series.Points[hit.PointIndex];
            var seriesName = hit.Series.Name;
            //取得したグラフ要素を捨てる
            hit = null;

            var points = chart1.Series["Sample"].Points;
            var boderPoints = chart1.Series["Border"].Points;

            if (IsHitBorderLine(seriesName))
            {
                //Borderの最大Y軸に全データを合わせる
                foreach (var point in points)
                {
                    if (point.YValues[0] <= curPoint.YValues[0])
                    {
                        continue;
                    }
                    point.YValues[0] = curPoint.YValues[0];
                }
            }
            else
            {
                //クリックしたX軸のデータをBorder以下のY軸にする
                var point = points.Where(v => v.XValue == curPoint.XValue).FirstOrDefault();
                var borderPoint = boderPoints.Where(v => v.XValue == curPoint.XValue).FirstOrDefault();
                if (point == null || point.YValues[0] <= borderPoint.YValues[0])
                {
                    return;
                }

                point.YValues[0] = borderPoint.YValues[0];
            }

            chart1.Refresh();
        }

        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            if (checkBox1.Checked)
            {
                //Borderの全Pointを最大値に合わせる(直線にする)
                var points = chart1.Series["Border"].Points;
                var maxYValue = points.Max(v => v.YValues[0]);
                foreach(var point in points)
                {
                    point.YValues[0] = maxYValue;
                }
                chart1.Refresh();
            }
        }
    }

    public class ChartData
    {
        public int X { get; set; }

        public int Y { get; set; }

        public int Max { get; set; }
    }
}

(ネストが複雑で汚いっす…)

ポイント

  • HitTestResultオブジェクトを利用すると指定したXY座標のグラフ要素が取得できる
  • ボーダーラインはシリーズに含めて、グラフが変化したときにボーダーラインを超えたか確認する

最後に

参考にしたコードはこちらのQ&Aからです。
MSChartでグラフをユーザー側で編集したい場合の作り方や、ボーダーラインを作りたいときの参考記事が見つからなかったので備忘録がてら作りました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PDF SharpをVS2015で使う

久し振りにPDF Sharpを使う機会があったので、メモ。

(1)NuGetにあった。

Visual Studio 2015 を使っているのだけど、NuGetで「PDF Sharp」を検索すると、取得できた。

(2)使えるフォントが減っていた。

前は、日本語としては「游明朝」、「游ゴシック」が使えたのだけど、今回「Error while parsing an OpenType font.」エラーが発生して、「游ゴシック」が使えなかった。

それではと思い立って、以下を書いてテストしたところ、日本語フォントは、ぱっと見たところ「游明朝」以外は全滅。
また、「HG丸ゴシックM - PRO」などは、エラーにはならないけど日本語は文字化けで使えず。

c#
        protected void Button1_Command(object sender, CommandEventArgs e)
        {
            using(PdfDocument document = new PdfDocument())
            {
                PdfPage page = document.AddPage();
                page.Size = PageSize.A4; //用紙の大きさ
                page.Orientation = PageOrientation.Landscape; //用紙の向き
                XGraphics graphics = XGraphics.FromPdfPage(page);

                var sb = new StringBuilder();
                System.Drawing.Text.InstalledFontCollection ifc =
                    new System.Drawing.Text.InstalledFontCollection();
                FontFamily[] ffs = ifc.Families;
                foreach(FontFamily ff in ffs)
                {
                    string buf = "";
                    try
                    {
                        using(Font fnt = new Font(ff, 8))
                        {
                            buf = fnt.Name;
                            var pdf_ja_font_options = new XPdfFontOptions(PdfFontEncoding.Unicode, PdfFontEmbedding.Always);
                            var pdf_ja_font = new XFont(buf, 12, XFontStyle.Regular, pdf_ja_font_options);

                            graphics.DrawString("こんにちわ世界", pdf_ja_font, XBrushes.Black,
                            new XRect(0, 0, page.Width, page.Height), XStringFormats.Center);
                        }
                    }
                    catch(Exception ex)
                    {
                        buf += ":" + ex.Message;
                    }
                    sb.AppendFormat("{0}\n", buf);
                }
                string file = @"C:\work\フォント調査.txt";
                using(StreamWriter sw = new StreamWriter(file, true, Encoding.Default))
                {
                    sw.Write(sb.ToString());
                }
            }
        }

IPAexフォントは、明朝、ゴシック共に正しく表示されたっぽいので、これを埋め込むことにする。
https://ja.osdn.net/projects/ipafonts/

Noto Fontsも使えそうなのだけど、埋め込むとPDFのサイズがすごいことになるので
(やり方が悪いのかも知れないのだけれど600倍以上になった)、今回はパス。

使えるフォントがとても限られるので、ちょっと不安。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PDFsharp

PDFsharpという、PDF編集ライブラリがある。
.NETからPDFを作成する事が出来る。命令で、文字も書けるし、線も引ける。
MITライセンスとの事で、同ライセンスは、Wikiによると、
“このソフトウェアを誰でも無償で無制限に扱って良い。ただし、著作権表示および本許諾表示をソフトウェアのすべての複製または重要な部分に記載しなければならない。”
“作者または著作権者は、ソフトウェアに関してなんら責任を負わない。”
との事なので、コピーライトを書けば、商用利用も可能なんじゃないかな。
多分。

ASP.NETからでも、参照設定にPdfSharp.dllを追加すれば使えたよ。
游明朝か游ゴシックにフォントを限れば、日本語の利用も可能。
以下、ASP.NET(C#)での、簡単な例。

c#
using PdfSharp;
using PdfSharp.Drawing;
using PdfSharp.Pdf;
using PdfSharp.Pdf.IO;

// 中略...

protected void Button1_Click( object sender, EventArgs e)
{
  PdfDocument document = new PdfDocument();
  PdfPage page = document.AddPage();
  page.Size = PageSize .A4; //用紙の大きさ
  page.Orientation = PageOrientation .Landscape; //用紙の向き

  XGraphics graphics = XGraphics .FromPdfPage(page);

  var pdf_ja_font_options = new XPdfFontOptions (PdfFontEncoding .Unicode, PdfFontEmbedding.Always);
  var pdf_ja_font = new XFont( "游明朝" , 12, XFontStyle.Regular, pdf_ja_font_options);

  graphics.DrawString( "こんにちわ世界" , pdf_ja_font, XBrushes.Black,
new XRect (0, 0, page.Width, page.Height), XStringFormats.Center);

  XPen pen = new XPen( XColors .Red, 4); //線を引く
  pen.DashStyle = XDashStyle .Dash;
  graphics.DrawLine(pen, 10, 20, page.Width - 10, 20);
  graphics.DrawLine(pen, 10, page.Height - 20, page.Width - 10, page.Height - 20);

  document.PageLayout = PdfPageLayout.OneColumn; //幅一杯に表示

  string filepath = @"C:\Users\..(中略)..\asp_pdf\asp_pdf\pdf" ;
  string filename = filepath + @"\HelloWorld.pdf" ;
  document.Save(filename);

  string openfilename = "./pdf/HelloWorld.pdf" ;
  Response.Redirect(openfilename); //PDFに遷移
}

Creator of PDFsharp is empira Software GmbH
Kirchstrase 19 53840 Troisdorf Germany
www.empira.de
PDFsharp (R) is a registered trademark of empira Software GmbH


昔別のブログに書いた記事なのだけど、珍しくアクセス数が多かったのでこちらにお引越し。
初出 2016-04-24

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む