Google 文件:用 Apps Script 玩轉 google 文件

 


如果你常在 Google 文件裡重複貼資料、套格式、輸出 PDF,八成會想過:「這些步驟能不能自動來?」答案是可以。

本篇文章要介紹的 Google Document Service(在 Apps Script 裡叫 DocumentApp),就是幫你用幾段 JavaScript 把文件生產線自動化的工具。

你可以用範本、放變數、一次灌表格與段落、插入圖片與頁首頁尾,最後還能轉成 PDF 或寄出給同事。

文中會先講它是什麼、能做什麼,再把常用的類別與方法梳理清楚,像 Document、Body、Paragraph、Table 等,搭配實作範例與踩雷整理。看完後,你應該就能把「每月報表、合約、會議紀錄、SOP」這類重複文件,改成按一鍵就產出的流程,省時、穩定,也比較不怕手滑。希望本篇文章能夠幫助到需要的您。


目錄

{tocify} $title={目錄} 


Document Service  是什麼?

Google Apps Script 的 Document Service(在程式裡用 DocumentApp)是一套內建服務,用 JavaScript 直接讀寫 Google 文件(Docs) 的內容與格式:你可以新建/開啟文件、插入段落、圖片、表格、頁首頁尾,甚至批次改樣式、做文件合併(mail merge)與報表輸出。

它是 Apps Script 內建服務的一員,和 Spreadsheet、Gmail 等服務並列。官方定義與 API 索引見此:Document Service 線上參考。


關鍵點 :

    入口是 DocumentApp(文件服務主類別)。

    你可以「建立或開啟文件」、「取得 Body 之後操作段落/文字/表格/清單」等。

    還能為 Google 文件加上自訂選單、對話框與側邊欄(屬於 Docs 的 UI 擴充範圍)。

如果你的需求更進階(例如低層級批量樣式、精細篩選與批次操作),也能在 Apps Script 內啟用 Advanced Docs Service,直接呼叫 Google Docs API。通常內建的 DocumentApp 已足夠,除非你需要 Docs API 的進一步能力。


核心概念與文件結構


在 DocumentApp 的世界,文件由一層層「元素」組成。最外層拿到 Document,再取得 Body(文件本文),接著操作裡面的 Paragraph / Text / Table / ListItem / InlineImage… 等元素。

DocumentApp:服務入口(開/新建/存取文件、UI 功能…)。

Document:一份文件的物件(你可取得 Body、游標/選取、ID、儲存等)。

Body:本文容器(加段落、插入表格/圖片、找文字、套樣式)。

Paragraph / Text:段落、文字(設定 Alignment、Heading、Bold/Italic/Color 等)。

ListItem:清單項目(項目符號/編號,支援巢狀清單)。

Table / TableRow / TableCell:表格階層(建表、調整列欄、插入元素)。

InlineImage / HorizontalRule / PageBreak:行內圖片、分隔線、分頁。

HeaderSection / FooterSection:頁首與頁尾。

Range/RangeElement/Position/Element:游標、選取區與元素抽象層(進行精準編輯)。

Enums(列舉):ElementType、ParagraphHeading、HorizontalAlignment 等。


延伸:

近來 Google Docs 新增「Tabs(分頁頁籤)」結構,Apps Script 也提供操作導引;若你的檔案有多個 Tabs,請留意 API 對「作用中的/第一個」分頁行為與 DocumentTab 的使用方式。

想理解更底層的「文件樹狀結構」與元素關係,可看 Docs API 的結構說明


常見任務清單

建立或開啟文件

// 新建文件
const doc = DocumentApp.create('每月營運報表');
const body = doc.getBody();
body.appendParagraph('報表起始');
const url = doc.getUrl();

// 用 ID 開啟既有文件
const opened = DocumentApp.openById('YOUR_DOC_ID');


DocumentApp.create/openById/openByUrl 是基本功。


插入段落、文字、樣式

const body = DocumentApp.getActiveDocument().getBody();
const title = body.appendParagraph('一、營收摘要');
title.setHeading(DocumentApp.ParagraphHeading.HEADING1);

const p = body.appendParagraph('本月重點:');
p.setBold(true).setAlignment(DocumentApp.HorizontalAlignment.LEFT);
body.appendParagraph('1. 營收較上月成長 12%。');
body.appendParagraph('2. 三大產品線皆有提升。');


段落與文字的 Heading、粗斜體、對齊都能直接設定。


建立表格(報表最常用)

const body = DocumentApp.getActiveDocument().getBody();
const table = body.appendTable([
  ['品項', '本月', '上月', 'MoM'],
  ['A 產品', '2,300', '2,050', '12%'],
  ['B 產品', '1,800', '1,700', '6%']
]);
table.getRow(0).editAsText().setBold(true);


表格由 Table → TableRow → TableCell 組成,細部再放段落/文字。


清單(項目符號/編號)

const body = DocumentApp.getActiveDocument().getBody();
const item1 = body.appendListItem('北區:+8%');
const item2 = body.appendListItem('中區:+5%');
const item3 = body.appendListItem('南區:+10%');
[item1, item2, item3].forEach(li => li.setGlyphType(DocumentApp.GlyphType.BULLET));


ListItem 能巢狀、能切換符號型態。


插入圖片、分隔線、分頁

const imgBlob = DriveApp.getFileById('IMAGE_FILE_ID').getBlob();
body.appendInlineImage(imgBlob);
body.appendHorizontalRule();
body.appendPageBreak();


(注意圖片來源通常從 Drive 取 Blob。)


搜尋與取代(報表/合併列印最實用)

const body = DocumentApp.getActiveDocument().getBody();
body.replaceText('{{公司}}', '星海股份有限公司');
body.replaceText('{{月份}}', '2025/09');


若需求更進階(例如一次性對大量元素與樣式做複雜替換),可考慮 Advanced Docs Service。


頁首頁尾

const doc = DocumentApp.getActiveDocument();
doc.getHeader().appendParagraph('星海股份有限公司 — 內部文件');
doc.getFooter().appendParagraph('Confidential — Do not distribute');


頁首/頁尾也是獨立區塊,可加段落與文字。


自訂選單、對話框與側邊欄(讓同事一鍵產報)

function onOpen() {
  DocumentApp.getUi()
    .createMenu('報表工具')
    .addItem('產生本月報表', 'generateMonthlyReport')
    .addToUi();
}

function generateMonthlyReport() {
  // 產生內容...
  DocumentApp.getUi().alert('已完成!');
}


自訂 UI 能把腳本「產品化」,降低同事學習門檻。


完整情境範例:月報自動產生器(從資料到 PDF)

目標:

        把後台資料(假設已放在 Sheet 或外部系統)整併到一份 Docs 模版,生成當月月報,最後輸出 PDF 給主管。

步驟總覽 : 

    1.    準備一份 Docs 模版,把可變內容標上占位符(如 {{月份}}、{{總營收}})。

    2.    用 Apps Script 取資料 → 開啟模版 → 複製成新文件 → 文字替換 → 插入表格/圖片 → 輸出 PDF → 寄出或存 Drive。

function buildMonthlyReport(monthStr) {
  const tmplId = 'DOC_TEMPLATE_ID';
  const copy = DriveApp.getFileById(tmplId).makeCopy(`營運月報_${monthStr}`);
  const doc = DocumentApp.openById(copy.getId());
  const body = doc.getBody();

  // 1) 取數據(此處以假資料代替)
  const summary = { 總營收: '12,300,000', MoM: '+8%', 北區: '+10%', 中區: '+6%', 南區: '+7%' };
  const topProducts = [
    { name: 'A 產品', revenue: 2300000, mom: '12%' },
    { name: 'B 產品', revenue: 1800000, mom: '6%' },
  ];

  // 2) 標籤替換
  body.replaceText('{{月份}}', monthStr);
  body.replaceText('{{總營收}}', summary.總營收);
  body.replaceText('{{MoM}}', summary.MoM);

  // 3) 區域表現(清單)
  body.appendParagraph('— 區域表現 —').setHeading(DocumentApp.ParagraphHeading.HEADING2);
  ['北區','中區','南區'].forEach(k => body.appendListItem(`${k}:${summary[k]}`));

  // 4) Top 產品表(表格)
  body.appendParagraph('— Top 產品 —').setHeading(DocumentApp.ParagraphHeading.HEADING2);
  const table = body.appendTable([['品項','營收','MoM']]);
  table.getRow(0).editAsText().setBold(true);
  topProducts.forEach(p =>
    table.appendTableRow([p.name, p.revenue.toLocaleString(), p.mom])
  );

  // 5) 美化與分頁
  body.appendHorizontalRule();
  body.appendPageBreak();

  doc.saveAndClose();

  // 6) 輸出 PDF
  const pdf = DriveApp.getFileById(copy.getId()).getAs('application/pdf');
  DriveApp.createFile(pdf).setName(`營運月報_${monthStr}.pdf`);
}


上面涵蓋了 DocumentApp 的核心操作:開/複製文件、替換文字、段落/清單/表格、輸出 PDF。DocumentApp 的建立與開啟方式與方法命名可參考官方說明。


常用 Class 與用途

DocumentApp(入口)

        整個 Document Service 的大門。用它來新建或開啟文件,以及在容器型腳本中建立自訂選單與對話框。

        最常用的方法有 create(name)、openById(id)、openByUrl(url),另外 getUi() 能做出「報表工具」這類自訂選單。

        實務上,你的腳本大多從這裡起手,先拿到 Document,再往內層走。


Document(單一文件本體)

        代表一份 Google 文件。

        拿到 Document 後,第一步通常是 getBody() 取得本文容器;也常用 getHeader()、getFooter() 操作頁首與頁尾。

        處理流程收尾時記得 saveAndClose(),避免之後從 Drive 取檔時還是舊版本。另外 getId()、getUrl() 方便你後續記錄或輸出。


Body(本文容器)

        所有段落、清單、表格、圖片…都塞在 Body 裡。

        加入內容時會用到 appendParagraph()、appendListItem()、appendTable()、appendInlineImage();做範本替換時會用 replaceText(find, replace),搜尋則用 findText(pattern)。

        排版上也能直接對段落/文字設定樣式(見下一段)。Body 是你「渲染內容」的主要工作區。


Paragraph / Text(段落與文字)

        排版與可讀性關鍵在這裡。

        標題層級用 setHeading(DocumentApp.ParagraphHeading.HEADING1..6);字型樣式常見的有 setBold(true)、setItalic(true)、setForegroundColor('#333333')、setAlignment(DocumentApp.HorizontalAlignment.LEFT|CENTER|RIGHT)。

        一般會先 appendParagraph('小節標題'),再把它設成 HEADING2,接著補充說明用普通段落。


ListItem(項目清單)

        用來做條列整理或重點清單。

        appendListItem(text) 會回傳 ListItem,可接著設 setGlyphType(DocumentApp.GlyphType.BULLET|NUMBER)。

        如果有巢狀層級,配合 setNestingLevel(level) 就能做出多階層的大綱式內容。

        報表的「本月重點」「待辦清單」都很適合用它。


Table / TableRow / TableCell(表格家族)

        需要結構化呈現資料,就用表格。

        最省事的做法是把二維陣列丟給 appendTable(rows) 一次建好,再取回 Table 做細部調整。

        例如表頭可以 table.getRow(0).editAsText().setBold(true)。

        若要插入或刪除列,用 insertTableRow()、removeRow();儲存格內其實也是元素容器,可以再放段落或文字。


InlineImage / HorizontalRule / PageBreak(圖片與分隔元素)

        appendInlineImage(blob) 能把 Drive 或外部來源(先轉成 Blob)的圖片嵌進文件;appendHorizontalRule() 拉出分隔線;appendPageBreak() 讓不同章節分到新頁。

        做「封面—目錄—內文」結構時相當好用。

        注意圖片權限與來源,常見問題是檔案沒權限導致讀不到 Blob。


HeaderSection / FooterSection(頁首與頁尾)

        公司抬頭、機密聲明、頁碼等都在這裡處理。

        呼叫 doc.getHeader()、doc.getFooter() 後用法與 Body 類似,也能 appendParagraph() 或調整文字樣式。

        統一在模板中編好基礎樣式,再用程式補關鍵字或日期,維護成本最低。


Element / Range / RangeElement / Position(元素與選取抽象層)

        當你需要「更精準地在某處插入內容」或「針對一段選取範圍做操作」時會用到。

        Range 代表一段選取區,RangeElement 是其中一個元素的包裝,Position 表示在某個元素的特定插入點。

        這組 API 偏進階,用在例如:把游標放在段落中間插入圖片、或對搜尋到的多個區塊逐一套樣式。


Enums(常見列舉型別)

        排版與判斷時會經常遇到幾個列舉:

        ParagraphHeading:標題階層(NORMAL、HEADING1..6)。

        HorizontalAlignment:對齊方式(LEFT、CENTER、RIGHT、JUSTIFY)。

        ElementType:用來辨認元素型別(PARAGRAPH、TEXT、TABLE…),寫工具函式時很好用。

        GlyphType:清單前導符號(項目符號、編號等)。


DocumentTab 與多分頁文件

        若你的文件使用 Docs 的「分頁頁籤(Tabs)」功能,請留意 API 目前的導引與預設行為。

        一般情況下 Body 針對的是「作用中的分頁」,要處理非預設分頁時,務必先定位正確目標再寫入,以免內容跑錯地方。

        這一塊屬於較新的結構,實作前先確認你使用的環境支援度與預期差異。


小提醒與實務節奏

1.    由外而內:DocumentApp → Document → Body →(Paragraph / ListItem / Table …)。寫程式時維持這個心智模型,閱讀性會好很多。

2.    樣式一次定調:標題層級、段落對齊、色彩在模板先定,再用程式補資料。程式只做「資料灌入與必要微調」。

3.    替換優先:能用 replaceText() 解決的,就不要用手動拼字串;穩定又好維護。

4.    收尾要存:大量寫入後記得 saveAndClose(),再去轉 PDF 或複製檔案,避免版本不同步。

5.    權限先確認:圖片、模板、輸出目的地都牽涉 Drive 權限。專案落地前先跑一輪「最壞情境」測試(例如用服務帳號或不同同事帳號)。


和 Google Docs API(Advanced Docs Service)的差異

學習曲線:

        DocumentApp 較直覺,像在組積木;Docs API 偏向操作「請求物件」與 JSON 結構,威力更大、也更繁瑣。

啟用方式:

        DocumentApp 內建即用;Docs API 需在 Apps Script 中啟用 Advanced Docs Service。

適用情境:

        一般報表、模版替換 → DocumentApp 就夠了;需要極細節的批次樣式或結構控制 → 考慮 Docs API。

文件結構認知:

        若要走 Docs API,先讀「文件結構」章節,能少走很多冤枉路。


操作流程整理

定位文件:create/openById/openByUrl。

拿容器:getBody() → 進入本文世界。

塞內容:appendParagraph / appendTable / appendListItem / appendInlineImage...。

套樣式:setHeading / setBold / setAlignment / setForegroundColor…(針對 Paragraph/Text)。

取代/搜尋:replaceText/findText。(模板化最省時)

輸出/流轉:saveAndClose 後,由 Drive 取檔、轉成 PDF 或寄出。

產品化:用 getUi() 做選單/對話框;搭配觸發器定時跑(例如每月一號)。


常見踩雷與避坑清單

1.    存檔時機:

       大量寫入後記得 saveAndClose(),避免狀態未刷新導致後續用 Drive 取檔出現舊版本。(Document 層級方法)

2.    表格中的分頁/分隔線限制:

        某些元素(例如 PageBreak)在特定容器(如表格儲存格)內有限制,請先確認元素可否放入。

3.    搜尋取代的正規表達式:

       replaceText 支援 regex,別忘了跳脫特殊字元,否則一改全改。

4.    圖片來源權限:

       appendInlineImage 需要可讀取的 Blob,從 Drive 取檔需確認檔案權限與帳號。

5.    UI 元件權限:

       自訂選單/對話框在「容器型腳本」最好用;若是獨立腳本,就沒有 Docs 的 UI 可以掛。

6.    觸發器限制:

        onOpen 等簡單觸發器有權限限制;若要寄信/存檔,改用可安裝觸發器。

7.    Tabs 行為:

       有多個 Tabs 的檔案,注意 API 預設操作的是哪個分頁,必要時指名處理。


問題集

Q1:我該選 DocumentApp 還是 Advanced Docs Service?

一般報表、批次替換、表格/清單/圖片 → DocumentApp。需要更細粒度的格式與批次操作、或要整合既有 Docs API 工作流 → Advanced Docs Service。

Q2:可以做互動工具列嗎?

可以,加自訂選單、對話框與側邊欄,但請用容器型腳本(綁在該份 Docs)。

Q3:文件有多分頁(Tabs)怎麼辦?

確認目前 API 操作的是哪個 Tab;需要時以 DocumentTab(與相關導引)定位目標。



張貼留言 (0)
較新的 較舊