【改訂】【C#】PDFを操作するプログラム(ページ数の多いPDFファイルに目次を作って目的のページを瞬時に開く)

公開日:2022年5月6日
最終更新日:2022年10月3日

はじめに

以前書いたコードの修正です。
touch-sp.hatenablog.com


タイトルにあるようにページ数の多いPDFファイルに目次を作ることが目的です。


実際に非常に役に立っていて今でも使っています。


一つ問題を抱えていてwebBrowserをNavigateした後Refreshしてもうまく動作しませんでした。


メッセージボックスを表示する行程をはさむとうまくいくのでそのように対応していました。地味に煩わしかったです(笑)。


このたび解決方法が分かったので備忘録として記事に残しておきます。

修正前

private void mokuzi_Click(object sender, EventArgs e)
{
      int currentpage = int.Parse(((MenuItem)sender).Name);
      webBrowser1.Navigate(select_filename + "#page=" + currentpage.ToString());
      //次の1文がないとうまくいかない(キャッシュのクリア?)
      MessageBox.Show("jump to page" + currentpage.ToString());
      webBrowser1.Refresh();
}

修正後

private void mokuzi_Click(object sender, EventArgs e)
{
    int currentpage = int.Parse(((MenuItem)sender).Name);
    webBrowser1.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(refreshPage);
    webBrowser1.Navigate(select_filename + "#page=" + currentpage.ToString());            
}
private void refreshPage(object sender, WebBrowserDocumentCompletedEventArgs e)
{
    webBrowser1.Refresh();
}


webBrowser1.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(refreshPage);

この1行が変なところに記述されていると感じるかもしれませんがここでないと正常に動作しませんでした。目次をクリックするたびにイベントを追加しているので違和感ありありですが。

全体のコード

全体のコードは以下のようになります。

using System;
using System.Drawing;
using System.Windows.Forms;

namespace PDFviewer
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            Load += mainForm_Load;
        }
        MainMenu mainmenu = new MainMenu();
        MenuItem top = new MenuItem();
        string select_filename;
        readonly string[] _kugiri = { ".pdf", ".PDF" };
        readonly string[] _dot = { "," };
        WebBrowser webbrowser = new WebBrowser();

        private void mainForm_Load(object sender, EventArgs e)
        {
            Size = new Size(1000, 750);
            webbrowser.Dock = DockStyle.Fill;
            
            MenuItem file_choose = new MenuItem();
            file_choose.Text = "PDFファイル選択";
            file_choose.Click += new EventHandler(file_choose_Click);
            mainmenu.MenuItems.Add(file_choose);
            top.Text = "目次";
            mainmenu.MenuItems.Add(top);
            Menu = mainmenu;
            Controls.Add(webbrowser);
        }
        private void file_choose_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.InitialDirectory = Environment.CurrentDirectory;
            ofd.Filter = "PDFファイル|*.pdf;*.PDF";
            if (ofd.ShowDialog(this) == DialogResult.OK)
            {
                top.MenuItems.Clear();
                String folder = System.IO.Path.GetDirectoryName(ofd.FileName);
                String filename = System.IO.Path.GetFileName(ofd.FileName);
                select_filename = ofd.FileName;
                string[] filename2 = filename.Split(_kugiri, StringSplitOptions.RemoveEmptyEntries);
                webbrowser.Navigate(select_filename);

                //同名のテキストファイルを探す
                if (System.IO.File.Exists(folder + "/" + filename2[0] + ".txt"))
                {
                    MenuItem[] mokuzi;
                    //内容を一行ずつ読み込む
                    string[] lines = System.IO.File.ReadAllLines(folder + "/" + filename2[0] + ".txt", System.Text.Encoding.GetEncoding("UTF-8"));
                    //空白を削除 
                    string[] lines2 = Array.FindAll(lines, line => line != "");
                    mokuzi = new MenuItem[lines2.Length];
                    string[] page = new string[lines2.Length];
                    string[] title = new string[lines2.Length];
                    for (int i = 0; i < lines2.Length; i++)
                    {
                        page[i] = lines2[i].Split(_dot, StringSplitOptions.RemoveEmptyEntries)[0];
                        title[i] = lines2[i].Split(_dot, StringSplitOptions.RemoveEmptyEntries)[1];
                        mokuzi[i] = new MenuItem(); mokuzi[i].Text = title[i];
                        mokuzi[i].Name = page[i];
                        top.MenuItems.Add(mokuzi[i]);
                        mokuzi[i].Click += new EventHandler(mokuzi_Click);
                    }
                }
            }
        }
        private void mokuzi_Click(object sender, EventArgs e)
        {
            int currentpage = int.Parse(((MenuItem)sender).Name);
            webbrowser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(refreshPage);
            webbrowser.Navigate(select_filename + "#page=" + currentpage.ToString());
        }
        private void refreshPage(object sender, WebBrowserDocumentCompletedEventArgs e)
        {
            ((WebBrowser)sender).Refresh();
        }
    }
}

使い方

PDFファイルと同名のテキストファイルを用意して同じフォルダに置いておくだけです。

たとえば「test.pdf」というファイルがあれば「test.txt」ファイルを作成して中身をこのようにします。

9,第1章
18,第2章
66,第3章
82,第4章

左側がページ数、右側が目次のタイトルです。


(注意)Visual Studio 2022 Communityで作っていますがテキストファイルはUTF-8で保存する必要があります。

2022年5月7日追記

PyQt6でも同じものを作りました。
touch-sp.hatenablog.com