見出し画像

パワーポイントファイルの発表者ノートからGO言語のプログラムでテキストを抜き出す小技

ワールドカップ決勝を観たので今朝は開発お休みしました。
素晴らしい試合でした。3時に寝たので8時に起きました。

さて、機能リリースしたVOICEVOXでパワーポイントのナレーションを作るソフト

の中で使ったパワーポイントファイルの中にある発表者のノートからGO言語のプログラムでテキストを抜き出す小技をお教えします。
真面目に考えるとMS Officeのファイルにアクセスできるパッケージを使ってパワーポイントのファイルを読み込む方法になると思います。
私も最初、そう考えていました。

とかです。しかし、ライセンスやパッケージの仕様をよく読むと使うのを躊躇するような感じでした。
そこで、違う方法を探していると

という記事を見つけました。ありがたいことです。ソースコードは

にありました。
解説とソースコードをよく読むとポイントは、

  • パワーポイントのPPTXファイルはZIPファイル

  • ZIPの中にXMLのファイルがある

  • スライド単位のXMLファイルの中にテキストがある

という感じでした。というわけでZIPを解凍してみると、発表者のノートも同じようにXMLファイルでした。この中のテキストを見てみると取り出せそうです。
XMLを真面目にパースして処理しようかと思いましたが、手抜きして正規表現で取り出すことにしました。
試しに作ったテストプログラムは

package main

import (
	"archive/zip"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"regexp"
	"sort"
	"strconv"
	"strings"
)

var noteReg = regexp.MustCompile(`ppt/notesSlides/notesSlide(\d+).xml`)
var textReg = regexp.MustCompile(`<a:t>([^<]+)</a:t>`)
var numOnlyReg = regexp.MustCompile(`^(/d+)$`)

func isPpt(path string) bool {
	e := filepath.Ext(path)
	return e == ".pptx"
}

func extractNote(pptx string) map[int][]string {
	ret := make(map[int][]string)
	reader, err := zip.OpenReader(pptx)
	if err != nil {
		return ret
	}

	for _, file := range reader.File {
		fm := noteReg.FindAllStringSubmatch(file.Name, 1)
		if len(fm) > 0 && len(fm[0]) > 1 {
			slide, err := strconv.Atoi(fm[0][1])
			if err != nil {
				continue
			}
			fmt.Printf("%s:%d\n", file.Name, slide)
			r, err := file.Open()
			if err != nil {
				return ret
			}
			defer r.Close()
			b, err := io.ReadAll(r)
			if err != nil {
				return ret
			}
			m := textReg.FindAllStringSubmatch(string(b), -1)
			for _, e := range m {
				if len(e) > 1 && !numOnlyReg.MatchString(e[1]) {
					ret[slide] = append(ret[slide], e[1])
				}
			}
		}
	}
	return ret
}

func main() {
	file := os.Args[1]
	if isPpt(file) {
		r := extractNote(file)
		keys := []int{}
		for i := range r {
			keys = append(keys, i)
		}
		sort.Ints(keys)
		for i, k := range keys {
			if i != 0 {
				fmt.Println("$")
			}
			fmt.Println(strings.Join(r[k], "\n"))
		}
	}
}

手抜きしてあるのでテキストに<があるとうまくいかないとかもしれませんが、私の目的には十分な感じです。でも真面目な良い子は、真似しないほうがよいです。

ワールドカップも終わってしまったので明日から通常の開発時間に戻ります。正月は高校サッカーを観ようと思います。

明日に続く

開発のための諸経費(機材、Appleの開発者、サーバー運用)に利用します。 ソフトウェアのマニュアルをnoteの記事で提供しています。 サポートによりnoteの運営にも貢献できるのでよろしくお願います。