PDFファイルよりテキスト抽出 PdfiumViewerを利用 C#
NuGetより導入する。
PdfiumViewer
PdfiumViewer.Native.x86.v8-xfa (32bitのとき)
PdfiumViewer.Native.x86_64.v8-xfa (64bitのとき)
PDFファイル、JPEGファイルの表示は、WebViwer2を利用。
画面設計等
![](https://ssksvr.com/wp-content/uploads/2024/05/image-29-1024x764.png)
PDF選択ボタンで、ファイル選択します(PDFファイルあるいはJPEGファイル)。JPEG変換ボタンでPDFファイルをJPEGファイルへ変換します。Ocr処理ボタンで、JPEGファイル選択時にテキストを抽出します。テキスト抽出ボタンで、PDFファイル選択時にテキストを抽出します。
NuGetインストール済
![](https://ssksvr.com/wp-content/uploads/2024/05/image-30.png)
![](https://ssksvr.com/wp-content/uploads/2024/05/image-31.png)
Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
using Windows.Graphics.Imaging;
using Windows.Media.Ocr;
using System.Drawing.Imaging;
using System.IO;
using static System.Net.Mime.MediaTypeNames;
using Shell32;
using Windows.Storage.Streams;
using Windows.Storage;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using static System.Net.WebRequestMethods;
using System.Runtime.InteropServices.ComTypes;
using Windows.Foundation;
using Windows.UI.Xaml.Shapes;
using Point = System.Drawing.Point;
using System.Runtime.InteropServices.WindowsRuntime;
using Image = System.Drawing.Image;
using Path = System.IO.Path;
using File = System.IO.File;
using PdfiumViewer;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
//control clsResize
clsResize _form_resize;
//Form1オブジェクトを保持するためのフィールド
private static Form1 _form1Instance;
//
private const string IMG_PATH_SEP = "_";
private const string IMG_PATH_EXT = ".jpg";
private int IMG_DPI = 300;
private long JPEG_QUALITY = 100;
private string pdf_path = "";
public Form1()
{
InitializeComponent();
_form_resize = new clsResize(this); //I put this after the initialize event to be sure that all controls are initialized properly
this.Load += new EventHandler(_Load); //This will be called after the initialization // form_load
this.Resize += new EventHandler(_Resize); //form_resize
}
//clsResize _Load
private void _Load(object sender, EventArgs e)
{
_form_resize._get_initial_size();
textBox3.Text = "300";
textBox4.Text = "100";
}
//clsResize _Resize
private void _Resize(object sender, EventArgs e)
{
_form_resize._resize();
}
//画像選択
private void Open()
{
//OpenFileDialogクラスのインスタンスを作成
OpenFileDialog ofd = new OpenFileDialog();
//はじめのファイル名を指定する
//はじめに「ファイル名」で表示される文字列を指定する
ofd.FileName = "default.pdf";
//はじめに表示されるフォルダを指定する
//指定しない(空の文字列)の時は、現在のディレクトリが表示される
ofd.InitialDirectory = System.Environment.CurrentDirectory + @"\";
//[ファイルの種類]に表示される選択肢を指定する
//指定しないとすべてのファイルが表示される
ofd.Filter = "PDFファイル(*.pdf)|*.pdf|JPEGファイル(*.jpg)|*.jpg|すべてのファイル(*.*)|*.*";
//[ファイルの種類]ではじめに選択されるものを指定する
//2番目の「すべてのファイル」が選択されているようにする
ofd.FilterIndex = 1;
//タイトルを設定する
ofd.Title = "開くファイルを選択してください";
//ダイアログボックスを閉じる前に現在のディレクトリを復元するようにする
ofd.RestoreDirectory = true;
//存在しないファイルの名前が指定されたとき警告を表示する
//デフォルトでTrueなので指定する必要はない
ofd.CheckFileExists = true;
//存在しないパスが指定されたとき警告を表示する
//デフォルトでTrueなので指定する必要はない
ofd.CheckPathExists = true;
//ダイアログを表示する
if (ofd.ShowDialog() == DialogResult.OK)
{
//OKボタンがクリックされたとき、選択されたファイル名を表示する
Console.WriteLine(ofd.FileName);
webView21.Source = new Uri(ofd.FileName);
textBox1.Text = ofd.FileName;
pdf_path = ofd.FileName;
}
}
//PDF選択
private void PDF選択ToolStripMenuItem_Click(object sender, EventArgs e)
{
Open();
}
//JPEG変換
private void JPEG変換ToolStripMenuItem_Click(object sender, EventArgs e)
{
Henkan();
}
//テキスト抽出
private void テキスト抽出ToolStripMenuItem_Click(object sender, EventArgs e)
{
SelText();
}
//テキスト消去
private void テキスト消去ToolStripMenuItem_Click(object sender, EventArgs e)
{
textBox2.Text = "";
}
private void テキスト保存ToolStripMenuItem_Click(object sender, EventArgs e)
{
save();
}
private void 終了ToolStripMenuItem_Click(object sender, EventArgs e)
{
Environment.Exit(0);
}
//画像選択
private void button1_Click(object sender, EventArgs e)
{
Open();
}
//JPEG変換
private void button2_Click(object sender, EventArgs e)
{
Henkan();
}
//テキスト消去
private void button3_Click(object sender, EventArgs e)
{
textBox2.Text = "";
}
//テキスト抽出
private void button4_Click(object sender, EventArgs e)
{
SelText();
}
//テキスト保存
private void button5_Click(object sender, EventArgs e)
{
save();
}
//終了
private void button6_Click(object sender, EventArgs e)
{
Environment.Exit(0);
}
//テキスト抽出
private void SelText()
{
var filename = textBox1.Text;
using (PdfDocument doc = PdfDocument.Load(filename))
{
for (var pageNum = 0; pageNum < doc.PageCount; pageNum++)
{
string text = doc.GetPdfText(pageNum);
Console.WriteLine(text);
textBox2.Text = textBox2.Text + text + "\r\n";
}
}
}
//JPEG変換
private void Henkan()
{
//SaveFileDialogクラスのインスタンスを作成
SaveFileDialog sfd = new SaveFileDialog();
//はじめのファイル名を指定する
//はじめに「ファイル名」で表示される文字列を指定する
sfd.FileName = "新しいファイル.jpg";
//はじめに表示されるフォルダを指定する
sfd.InitialDirectory = System.Environment.CurrentDirectory + @"\";
//[ファイルの種類]に表示される選択肢を指定する
//指定しない(空の文字列)の時は、現在のディレクトリが表示される
sfd.Filter = "JPEGファイル(*.jpg)|*.jpg|すべてのファイル(*.*)|*.*";
//[ファイルの種類]ではじめに選択されるものを指定する
//2番目の「すべてのファイル」が選択されているようにする
sfd.FilterIndex = 1;
//タイトルを設定する
sfd.Title = "保存先のファイルを選択してください";
//ダイアログボックスを閉じる前に現在のディレクトリを復元するようにする
sfd.RestoreDirectory = true;
//既に存在するファイル名を指定したとき警告する
//デフォルトでTrueなので指定する必要はない
sfd.OverwritePrompt = true;
//存在しないパスが指定されたとき警告を表示する
//デフォルトでTrueなので指定する必要はない
sfd.CheckPathExists = true;
//解像度、品質セット
try
{
IMG_DPI = Int32.Parse(textBox3.Text);
JPEG_QUALITY = long.Parse(textBox4.Text);
}
finally
{
}
//ダイアログを表示する
if (sfd.ShowDialog() == DialogResult.OK)
{
//OKボタンがクリックされたとき、選択されたファイル名を表示する
//Console.WriteLine(sfd.FileName);
//int resolution = 300;
//string ReadFileName = textBox1.Text;
//PdfiumViewer.PdfDocument pdfdoc = PdfiumViewer.PdfDocument.Load(ReadFileName);
//SizeF size = pdfdoc.PageSizes[0];
//int w = (int)size.Width * resolution / 72;
//int h = (int)size.Height * resolution / 72;
//Image image = pdfdoc.Render(0, w, h, resolution, resolution, false);
//image.Save(sfd.FileName, System.Drawing.Imaging.ImageFormat.Jpeg);
//PDFのフォルダ名
string img_dir = Path.GetDirectoryName(sfd.FileName);//(pdf_path);
//PDFの拡張子を除いたファイル名+区切り文字
string img_fn_head = Path.GetFileNameWithoutExtension(sfd.FileName) + IMG_PATH_SEP;//(pdf_path) + IMG_PATH_SEP;
//
//保存先画像ファイル名(保存時は、この後ろに連番と拡張子が追加される)
string img_path_head = Path.Combine(img_dir, img_fn_head);
//
//1ページ目の画像ファイル名
string img_path_001 = this.getImgPath(img_path_head, 1);
//
//1ページ目の画像ファイルが存在する?
if (File.Exists(img_path_001))
{
//
//PDFファイルの最終更新日時
DateTime dt_pdf = File.GetLastWriteTime(pdf_path);
//1ページ目画像ファイルの最終更新日時
DateTime dt_img = File.GetLastWriteTime(img_path_001);
//
//画像の方が大きい?(新しい?)
if (dt_pdf < dt_img)
{
return;
}
//画像の方が古い
else
{
//...
}
}
//
//PDFを開く
using (PdfiumViewer.PdfDocument pdf_doc = PdfiumViewer.PdfDocument.Load(pdf_path))
{
//
//ページ毎にループ
for (int page = 0; page < pdf_doc.PageCount; page++)
{
//
SizeF pageSize = pdf_doc.PageSizes[page];
double ppi = IMG_DPI * 0.75;
int width = (int)(pageSize.Width * IMG_DPI / ppi);
int height = (int)(pageSize.Height * IMG_DPI / ppi);
//画像変換
using (Image img_obj = pdf_doc.Render(page, width, height, IMG_DPI, IMG_DPI, false))
{
//保存先ファイル名(連番挿入後)
string img_path_full = this.getImgPath(img_path_head, page + 1);
//保存
this.saveJpeg(img_obj, img_path_full, JPEG_QUALITY);
}
}
}
}
}
//ファイル名先頭部と末尾部からファイル名文字列を生成する
private string getImgPath(string head, int tail_num)
{
return this.getImgPath(head, tail_num.ToString("D3"));
}
private string getImgPath(string head, string tail)
{
return head + tail + IMG_PATH_EXT;
}
//JPEG保存(画質指定)
private void saveJpeg(Image img_obj, string path, long quality)
{
//JPEGエンコーダ取得失敗?
if (this.jpegEncoder == null)
{
//Imageオブジェクトにお任せで保存
img_obj.Save(path, ImageFormat.Jpeg);
}
//JPEGエンコーダあり
else
{
//画質パラメータ生成
EncoderParameter encParam = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, quality);
EncoderParameters encParams = new EncoderParameters(1);
encParams.Param[0] = encParam;
//パラメータを指定して保存
img_obj.Save(path, this.jpegEncoder, encParams);
}
}
//JpegEncoderを取得
private ImageCodecInfo jpegEncoder = null;
private void getImageCodecInfo_Jpeg()
{
this.jpegEncoder = null;
foreach (ImageCodecInfo ici in ImageCodecInfo.GetImageEncoders())
{
if (ici.FormatID == ImageFormat.Jpeg.Guid)
{
this.jpegEncoder = ici;
break;
}
}
}
//テキスト保存
private void save()
{
//SaveFileDialogクラスのインスタンスを作成
SaveFileDialog sfd = new SaveFileDialog();
//はじめのファイル名を指定する
//はじめに「ファイル名」で表示される文字列を指定する
sfd.FileName = "新しいファイル.txt";
//はじめに表示されるフォルダを指定する
sfd.InitialDirectory = System.Environment.CurrentDirectory + @"\";
//[ファイルの種類]に表示される選択肢を指定する
//指定しない(空の文字列)の時は、現在のディレクトリが表示される
sfd.Filter = "Textファイル(*.txt)|*.txt|すべてのファイル(*.*)|*.*";
//[ファイルの種類]ではじめに選択されるものを指定する
//2番目の「すべてのファイル」が選択されているようにする
sfd.FilterIndex = 1;
//タイトルを設定する
sfd.Title = "保存先のファイルを選択してください";
//ダイアログボックスを閉じる前に現在のディレクトリを復元するようにする
sfd.RestoreDirectory = true;
//既に存在するファイル名を指定したとき警告する
//デフォルトでTrueなので指定する必要はない
sfd.OverwritePrompt = true;
//存在しないパスが指定されたとき警告を表示する
//デフォルトでTrueなので指定する必要はない
sfd.CheckPathExists = true;
//ダイアログを表示する
if (sfd.ShowDialog() == DialogResult.OK)
{
//OKボタンがクリックされたとき、選択されたファイル名を表示する
//Console.WriteLine(sfd.FileName);
//UTF-8で書き込む
//書き込むファイルが既に存在している場合は、上書きする
System.IO.Stream stream;
stream = sfd.OpenFile();
if (stream != null)
{
//ファイルに書き込む
System.IO.StreamWriter sw = new System.IO.StreamWriter(stream);
sw.Write(textBox2.Text);
//閉じる
sw.Close();
stream.Close();
}
}
}
public async void OcrAll()
{
string ImagePath = textBox1.Text;
//画像ファイルをOCRを実行
string ocrResult = await PerformOCR(ImagePath);
//結果を表示
textBox2.Text = textBox2.Text + "\r\n" + ocrResult.Replace(" ", "");
}
private async Task<string> PerformOCR(string imagePath)
{
try
{
//画像ファイルをパイト配列として読み込む
byte[] imageBytes = System.IO.File.ReadAllBytes(imagePath);
//パイト配列をIBufferに変換
IBuffer buffer = imageBytes.AsBuffer();
//IBufferからSoftwareBitmapに変換
SoftwareBitmap softwareBitmap;
using (var stream = new InMemoryRandomAccessStream())
{
await stream.WriteAsync(buffer);
stream.Seek(0);
var decoder = await Windows.Graphics.Imaging.BitmapDecoder.CreateAsync(stream);
softwareBitmap = await decoder.GetSoftwareBitmapAsync();
}
//OCR工ンジンを初期化
var language = new Windows.Globalization.Language("ja");
OcrEngine ocrEngine = OcrEngine.TryCreateFromUserProfileLanguages();//OcrEngine.TryCreateFromLanguage(language); //
//画像をOCRにかけ、結果を取碍
OcrResult ocrResult = await ocrEngine.RecognizeAsync(softwareBitmap);
//認識結果をテキストに変換
//string recognizedText = ocrResult.Text;
string recognizedText = ocrResult.Lines.Select(line => line.Text).Aggregate((current, next) => current + Environment.NewLine + next);
return recognizedText;
}
catch (Exception ex)
{
return "OCRエラー" + ex.Message;
}
}
//OCR全体処理
private void button7_Click(object sender, EventArgs e)
{
OcrAll();
}
private void ocr処理ToolStripMenuItem_Click(object sender, EventArgs e)
{
OcrAll();
}
}
}
clsResize.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
public class clsResize
{
List<System.Drawing.Rectangle> _arr_control_storage = new List<System.Drawing.Rectangle>();
private bool showRowHeader = false;
public clsResize(Form _form_)
{
form = _form_; //the calling form
_formSize = _form_.ClientSize; //Save initial form size
_fontsize = _form_.Font.Size; //Font size
//ADD
var _controls = _get_all_controls(form);//call the enumerator
FontTable = new Dictionary<string, float>();
ControlTable = new Dictionary<string, System.Drawing.Rectangle>();
foreach (Control control in _controls) //Loop through the controls
{
FontTable.Add(control.Name, control.Font.Size);
ControlTable.Add(control.Name, control.Bounds);
}
//ADD
}
//ADD
Dictionary<string, float> FontTable;
Dictionary<string, System.Drawing.Rectangle> ControlTable;
//ADD
private float _fontsize { get; set; }
private System.Drawing.SizeF _formSize { get; set; }
private Form form { get; set; }
public void _get_initial_size() //get initial size//
{
var _controls = _get_all_controls(form);//call the enumerator
foreach (Control control in _controls) //Loop through the controls
{
_arr_control_storage.Add(control.Bounds); //saves control bounds/dimension
//If you have datagridview
if (control.GetType() == typeof(DataGridView))
_dgv_Column_Adjust(((DataGridView)control), showRowHeader);
}
}
public void _resize() //Set the resize
{
double _form_ratio_width = (double)form.ClientSize.Width / (double)_formSize.Width; //ratio could be greater or less than 1
double _form_ratio_height = (double)form.ClientSize.Height / (double)_formSize.Height; // this one too
var _controls = _get_all_controls(form); //reenumerate the control collection
int _pos = -1;//do not change this value unless you know what you are doing
foreach (Control control in _controls)
{
//ADD
this._fontsize = FontTable[control.Name]; //<-取得したコントロールのフォントサイズ値で上書きするためにこれを追加
//ADD
// do some math calc
_pos += 1;//increment by 1;
System.Drawing.Size _controlSize = new System.Drawing.Size((int)(_arr_control_storage[_pos].Width * _form_ratio_width),
(int)(_arr_control_storage[_pos].Height * _form_ratio_height)); //use for sizing
System.Drawing.Point _controlposition = new System.Drawing.Point((int)
(_arr_control_storage[_pos].X * _form_ratio_width), (int)(_arr_control_storage[_pos].Y * _form_ratio_height));//use for location
//set bounds
control.Bounds = new System.Drawing.Rectangle(_controlposition, _controlSize); //Put together
//Assuming you have a datagridview inside a form()
//if you want to show the row header, replace the false statement of
//showRowHeader on top/public declaration to true;
if (control.GetType() == typeof(DataGridView))
_dgv_Column_Adjust(((DataGridView)control), showRowHeader);
//Font AutoSize
control.Font = new System.Drawing.Font(form.Font.FontFamily,
(float)(((Convert.ToDouble(_fontsize) * _form_ratio_width) / 2) +
((Convert.ToDouble(_fontsize) * _form_ratio_height) / 2)));
}
}
private void _dgv_Column_Adjust(DataGridView dgv, bool _showRowHeader) //if you have Datagridview
//and want to resize the column base on its dimension.
{
int intRowHeader = 0;
const int Hscrollbarwidth = 5;
if (_showRowHeader)
intRowHeader = dgv.RowHeadersWidth;
else
dgv.RowHeadersVisible = false;
for (int i = 0; i < dgv.ColumnCount; i++)
{
if (dgv.Dock == DockStyle.Fill) //in case the datagridview is docked
dgv.Columns[i].Width = ((dgv.Width - intRowHeader) / dgv.ColumnCount);
else
dgv.Columns[i].Width = ((dgv.Width - intRowHeader - Hscrollbarwidth) / dgv.ColumnCount);
}
}
private static IEnumerable<Control> _get_all_controls(Control c)
{
return c.Controls.Cast<Control>().SelectMany(item =>
_get_all_controls(item)).Concat(c.Controls.Cast<Control>()).Where(control =>
control.Name != string.Empty);
}
}
実行画面
PDFファイルを選択し、テキスト抽出しました。
![](https://ssksvr.com/wp-content/uploads/2024/05/image-32-1024x770.png)
PDFファイルをJPEG変換でJPEGファイルとして保存。PDF選択でJPEGファイルを選択し、Ocr処理を実行しました。
![](https://ssksvr.com/wp-content/uploads/2024/05/image-33-1024x765.png)