const TARGET_HEADING_LEVEL = 3;
const SCRIPT_NAME = 'カスタム スクリプト';
const SCRIPT_ERROR_MENU_NAME = `${SCRIPT_NAME} (設定エラー)`;
function onOpen() {
try {
if (!isValidHeadingLevel(TARGET_HEADING_LEVEL)) {
Logger.log(`onOpen: TARGET_HEADING_LEVEL (${TARGET_HEADING_LEVEL}) が無効です。エラーメニューを表示します。`);
showConfigurationErrorMenu("設定値が無効 (1-6の整数)");
return;
}
DocumentApp.getUi()
.createMenu(SCRIPT_NAME)
.addItem(`「見出し ${TARGET_HEADING_LEVEL}」の前に改ページ挿入`, 'insertPageBreakBeforeHeading')
.addToUi();
Logger.log(`onOpen: メニューを正常に追加 (対象: 見出し ${TARGET_HEADING_LEVEL})。`);
} catch (e) {
Logger.log(`onOpen: メニュー追加中にエラー: ${e}\n${e.stack}`);
showConfigurationErrorMenu("メニュー追加中にエラー発生");
}
}
function showConfigurationErrorMenu(reason) {
try {
DocumentApp.getUi()
.createMenu(SCRIPT_ERROR_MENU_NAME)
.addItem(`設定を確認 (${reason})`, 'showConfigurationError')
.addToUi();
} catch (e) {
Logger.log(`showConfigurationErrorMenu: エラーメニュー表示中にさらにエラー: ${e}`);
}
}
function showConfigurationError() {
let currentSetting = "未定義またはアクセス不能";
try {
currentSetting = (typeof TARGET_HEADING_LEVEL !== 'undefined') ? String(TARGET_HEADING_LEVEL) : "未定義";
} catch(e) { }
const message = `スクリプトの設定に問題があります。\n\n`
+ `現在の設定値: ${currentSetting}\n\n`
+ `スクリプトエディタを開き、ファイルの先頭にある「TARGET_HEADING_LEVEL」の値 (1~6の整数) を確認・修正し、**ファイルを保存**してください。\n\n`
+ `(例: const TARGET_HEADING_LEVEL = 3;)`
DocumentApp.getUi().alert("スクリプト設定エラー", message, DocumentApp.getUi().ButtonSet.OK);
}
function isValidHeadingLevel(level) {
return typeof level === 'number' && Number.isInteger(level) && level >= 1 && level <= 6;
}
function getHeadingConstant(level) {
if (!isValidHeadingLevel(level)) {
Logger.log(`getHeadingConstant: 無効なレベル: ${level}`);
return null;
}
const headingMap = {
1: DocumentApp.ParagraphHeading.HEADING1,
2: DocumentApp.ParagraphHeading.HEADING2,
3: DocumentApp.ParagraphHeading.HEADING3,
4: DocumentApp.ParagraphHeading.HEADING4,
5: DocumentApp.ParagraphHeading.HEADING5,
6: DocumentApp.ParagraphHeading.HEADING6,
};
return headingMap[level] || null;
}
function getHeadingEnumName(enumValue) {
if (enumValue === null || typeof enumValue === 'undefined') {
return 'NULL_OR_UNDEFINED';
}
if (!this.headingEnumNameCache) {
this.headingEnumNameCache = {};
for (const key in DocumentApp.ParagraphHeading) {
if (Object.prototype.hasOwnProperty.call(DocumentApp.ParagraphHeading, key)) {
this.headingEnumNameCache[DocumentApp.ParagraphHeading[key]] = key;
}
}
}
return this.headingEnumNameCache[enumValue] || `UNKNOWN_STYLE (${String(enumValue)})`;
}
function insertPageBreakBeforeHeading() {
const startTime = new Date();
let doc;
try {
doc = DocumentApp.getActiveDocument();
if (!doc) throw new Error("アクティブなドキュメントを取得できませんでした。");
doc.getName();
} catch (e) {
handleExecutionError("ドキュメントアクセスエラー", e);
return;
}
if (!isValidHeadingLevel(TARGET_HEADING_LEVEL)) {
Logger.log(`insertPageBreakBeforeHeading: 設定値が無効 (${TARGET_HEADING_LEVEL})。`);
showConfigurationError();
return;
}
const targetHeadingConstant = getHeadingConstant(TARGET_HEADING_LEVEL);
if (!targetHeadingConstant) {
Logger.log(`insertPageBreakBeforeHeading: 見出し定数取得失敗 (レベル: ${TARGET_HEADING_LEVEL})。`);
DocumentApp.getUi().alert(`スクリプト内部エラー: 設定値 (${TARGET_HEADING_LEVEL}) に対応する見出しスタイル定数を取得できませんでした。`);
return;
}
const targetHeadingName = getHeadingEnumName(targetHeadingConstant);
Logger.log(`--- 改ページ挿入処理 開始 ---`);
Logger.log(`ドキュメント: ${doc.getName()}, 対象見出し: ${TARGET_HEADING_LEVEL} (${targetHeadingName})`);
const body = doc.getBody();
const numChildren = body.getNumChildren();
let pageBreakInsertedCount = 0;
let foundTargetHeadings = 0;
Logger.log(`要素数: ${numChildren}。逆順に走査します...`);
for (let i = numChildren - 1; i >= 0; i--) {
const element = body.getChild(i);
const elementType = element.getType();
let headingStyle = null;
try {
if (elementType === DocumentApp.ElementType.PARAGRAPH) {
headingStyle = element.asParagraph().getHeading();
} else if (elementType === DocumentApp.ElementType.LIST_ITEM) {
headingStyle = element.asListItem().getHeading();
} else {
continue;
}
if (headingStyle === targetHeadingConstant) {
foundTargetHeadings++;
if (i > 0 && body.getChild(i - 1).getType() !== DocumentApp.ElementType.PAGE_BREAK) {
try {
body.insertPageBreak(i);
pageBreakInsertedCount++;
} catch (insertError) {
Logger.log(`! Index ${i} への改ページ挿入中にエラー: ${insertError}`);
}
} else if (i > 0) {
} else {
}
}
} catch (elementError) {
Logger.log(`! Index ${i} (Type: ${elementType}) の処理中にエラー: ${elementError}. スキップします。`);
}
}
const endTime = new Date();
const duration = (endTime.getTime() - startTime.getTime()) / 1000;
Logger.log(`--- 走査終了 ---`);
Logger.log(`処理時間: ${duration.toFixed(2)} 秒`);
Logger.log(`検出された対象見出し (${targetHeadingName}): ${foundTargetHeadings} 個`);
Logger.log(`挿入された改ページ: ${pageBreakInsertedCount} 個`);
const resultMessage = buildResultMessage(pageBreakInsertedCount, foundTargetHeadings, targetHeadingName, duration);
DocumentApp.getUi().alert("処理完了", resultMessage, DocumentApp.getUi().ButtonSet.OK);
Logger.log(`--- 改ページ挿入処理 終了 ---`);
}
function handleExecutionError(context, error) {
Logger.log(`実行時エラー (${context}): ${error}\n${error.stack}`);
let userMessage = `スクリプトの実行中にエラーが発生しました。\n\n状況: ${context}`;
if (error.message) {
if (error.message.includes("Authorization required") || error.message.includes("権限が必要")) {
userMessage = "スクリプトの実行に必要な権限が承認されていません。\n\nドキュメントを再読み込みするか、スクリプトを再度実行し、承認画面で許可してください。";
} else {
userMessage += `\n詳細: ${error.message}`;
}
}
DocumentApp.getUi().alert("スクリプト エラー", userMessage, DocumentApp.getUi().ButtonSet.OK);
}
function buildResultMessage(insertedCount, foundCount, headingName, duration) {
const headingLevelName = `「${headingName}」スタイル (見出し ${TARGET_HEADING_LEVEL})`;
if (insertedCount > 0) {
return `${insertedCount} 箇所の ${headingLevelName} の前に改ページを挿入しました。\n\n処理時間: ${duration.toFixed(2)} 秒`;
} else if (foundCount > 0) {
return `${headingLevelName} は ${foundCount} 箇所で見つかりましたが、改ページは挿入されませんでした。\n(見出しが先頭にあるか、既に改ページが存在するため)\n\n処理時間: ${duration.toFixed(2)} 秒`;
} else {
return `${headingLevelName} が適用された要素が見つかりませんでした。\n\n以下の点を確認してください:\n`
+ `1. 設定値 (${TARGET_HEADING_LEVEL}) は正しいですか?\n`
+ `2. 対象テキストに [表示形式] > [段落スタイル] から正しい見出しスタイルが適用されていますか?\n`
+ `3. スクリプトの権限は承認されていますか?`;
}
}