要怎麼抓出文件中的特定文字或段落,直接改字體的大小、顏色、背景、粗細與字型?先來看今天的結果之一,把文章中的「人」字都改成紅字、粗體與放大——
今天,我們會教用 GAS 搭配 Goolge Doc 來設定,這個要動用到的元素叫做 Attribute
。那因為在 Google Slide 中的 Element、Attribute 也很多有重疊,所以這邊就會講細一點,之後就可以一起服用。換句話說,今天會教說怎麼透過 GAS 調整 Google Doc 和 Google Slide 裡面元素的「格式」。我們先複習一下前幾天講過的議題——
那今天,我們會針對上述議題中的「元素」進行。基本上前幾天講了上述元素的操作,今天終於可以進入到「格式調整」了,今天會專注在以下內容——
就讓我們開始吧!
考慮到有些夥伴不一定會都有細讀的文章,就撈叨一點把基本步驟再次附上,如果會的夥伴可以直接跳到 Q5。
我們已經知道大致上,每一個 Google 文件都會有 Element (元件),且每一個 Element 都會有 Attribute (屬性)。今天我們主要會介紹下圖綠色的 Attribute 的部分。
下圖主要是列舉幾個常見用於操作 Element 的 Attribute。
這邊就節錄一本書中的「段落、照片、表格與清單」,來作為今天我們的範例。
好,大致理解基本概念後,就讓我們開始吧。
那這次我們不會用 Google Sheet,而是直接用 Google Doc 進入,借一下 D16 的影片。
一樣第一次會有存取驗證需要大家按一下。這邊仍是借用一下 D2 的影片。
getBody()
我們先用 getActiveDocument()
抓出正在綁定的文件;那假設我們都是針對主要內文(Body)的部分,所以我們先設定好 getbody()
。
let doc_body = DocumentApp.getActiveDocument().getBody();
因為更新有比較複雜的細節,我們就先來講講刪除。
基本上之後的步驟會分成:
getAttributes()
)setAttributes()
)3-1 和 3-2 主要就是 D16 Element 的讀取與創造 的涵蓋內容,這邊就一樣快速帶給大家看。
這邊就先上一段「讀取」的程式碼——
function readParagraph(){
let doc_body = DocumentApp.getActiveDocument().getBody();
let paragraphs = doc_body.getParagraphs();
Logger.log(paragraphs)
}
再來看讀取的結果影片——
那如果要改成讀取其他的 Element,可以參考之前的簡易表單來做更換。
那如果我們今天要新增一段表格,可以怎麼做?我們可以運用 appendTable
的功能來執行。
function addTable(){
let doc_body = DocumentApp.getActiveDocument().getBody();
let cells = [[1,2,3],[4,5,6]];
doc_body.appendTable(cells)
}
跑起來長這樣——
那如果要改成新增其他的 Element,可以參考之前的簡易表單來做操作。
那以上這邊如果覺得太快,或想知道更細部,記得可以回去看 D16 Element 的讀取與創造 ,如果覺得時間OK,那我們就進入到新的部分,也就是「部分讀取」。
比起整段做讀取、更改,我們更常遇到的情況是只要更動部分。像是我想要把文件中的關鍵字抓出來標記顏色等、或是想要動從第三頁到第八頁的表格,詳細要怎麼執行?
基本上第一部,我們都要先跟 GAS 說「我想抓出元素的___部分」。而這邊就要用 findText()
來執行。
findText()
的基本使用架構如下,我們用來示範如何抓到段落中的「人」字。
function testFindText(){
let doc_body = DocumentApp.getActiveDocument().getBody();
let target = '人';
let searchResult = doc_body.findText(target);
while (searchResult !== null) {
Logger.log(searchResult.getElement().asText().getText());
searchResult = doc_body.findText(target, searchResult);
}
}
用法上, findText(searchPattern, from)
前面的 searchPattern 用的是 regex(嚴格說起來是 google 的 re2),我們就簡單輸入要找的文字;後面的 from
指的是當找到多個元素後,從哪一個開始?沒輸入的話預設會回傳第一個,但在第一個之後如果要繼續找,就要搭配 while
回圈並將 from
輸入為前一次的搜尋結果。換句話說,就是把下圖手刻的方式轉成 while 迴圈。
跑起來結果長這樣——
那,從清單的只有回傳兩點發現,它確實有抓到「人」字,但是是回傳了「人」字所在的整個元件(Element),如果是在段落(Paragraph)內,就回傳段落,如果是在清單項目(ListItem)內就回傳整個項目。那我們要怎麼抓出特定範圍?這時就要提到 range
元素了。
range
元素與 offset
的概念Range 是什麼?簡單來說,當我們用滑鼠選取了一段內容後,都會伴隨出range
(當然省略掉 select
的部分,不過在這邊我們先注重在 range
)。
且每個 range 都有被伴隨的 offset(位移),簡單來說可以當成:是在段落中的哪個位置。
以上面的影片來說,「人」這個字即是位在第四個位置(Offset 為 3);更嚴格地說,「人」這個 range
的在段落中的開始位置(StartOffset
) 和結束位置(EndOffset
)都是為在 Offset 為 3的部分。
注意因為是程式語言,第一個位置我們要從 Offset 為 0 開始算。位移的概念可以參考如下
那為什麼這個重要?因為當我們等下要改內容時,這就會派上用場。實際上,對 GAS 來說,「部分選取」就是先選整段,再跟我說段落中的哪個位置(Offset)要改。我們快速示範如果要將 Offset 的文字都改成紅色要怎麼做。
我們先來看看沒上 Offset 的結果,會發現就等於整段改成紅色。
同時看看上了 Offset 的結果,會發現只有我們要的「特定範圍」變色。
實際上,我是透過 range
物件中的 getStartOffset()
和 getEndOffsetInclusive()
達到的。先附上程式碼,可以先專注看「Offset」的部分,我們馬上就會來細講改顏色的部分。
function highlightText() {
let target = '人';
let doc_body = DocumentApp.getActiveDocument().getBody();
let searchResult = doc_body.findText(target);
let textStyle={};
textStyle[DocumentApp.Attribute.FOREGROUND_COLOR] = '#FF0000'
while (searchResult !== null) {
let search_text = searchResult.getElement();
Logger.log(searchResult.getStartOffset() +' '+ searchResult.getEndOffsetInclusive())
search_text.setAttributes(searchResult.getStartOffset(), searchResult.getEndOffsetInclusive(),textStyle)
searchResult = doc_body.findText(target, searchResult);
}
好,那我們能抓到範圍了,接下來就是要改屬性了。但在改屬性前,因為物件的屬性算有點複雜,我個人是會建議先檢查「有沒有這個屬性」。
getAttributes()
來讀取現有屬性通常我會先用 getAttributes()
來看我的目標物件有哪些可以調整的屬性。這也可以幫助我們理解怎麼設定屬性。這邊寫一段簡單的程式,會印出目標「段落」、「清單項目」和「文件」的含有屬性。
function testFindText(){
let doc_body = DocumentApp.getActiveDocument().getBody();
let target = '人';
let searchResult = doc_body.findText(target);
while (searchResult !== null) {
Logger.log(searchResult.getElement().asText().getText());
Logger.log(searchResult.getElement().getAttributes());
searchResult = doc_body.findText(target, searchResult);
}
Logger.log(doc_body.getAttributes())
}
跑起來長這樣——
我這邊把「段落」、「清單項目」與「文件」分開來看。
{STRIKETHROUGH=null, LINK_URL=null, FONT_FAMILY=Source Sans Pro, FONT_SIZE=null, UNDERLINE=null, BACKGROUND_COLOR=null, ITALIC=null, FOREGROUND_COLOR=null, BOLD=null}
{LINK_URL=null, FOREGROUND_COLOR=null, BOLD=null, BACKGROUND_COLOR=null, UNDERLINE=null, STRIKETHROUGH=null, FONT_FAMILY=Source Sans Pro, FONT_SIZE=null, ITALIC=null}
{MARGIN_TOP=21.25984251968504, LINK_URL=null, FONT_SIZE=null, UNDERLINE=null, STRIKETHROUGH=null, FOREGROUND_COLOR=null, FONT_FAMILY=null, BOLD=null, PAGE_HEIGHT=841.68, MARGIN_BOTTOM=72.0, MARGIN_LEFT=72.0, BACKGROUND_COLOR=null, PAGE_WIDTH=595.4399999999999, ITALIC=null, MARGIN_RIGHT=72.0}
這邊要特別提一下我們抓出的資料結構,是一種叫做 dictionary 的結構。這邊的重點會放在,所以如果我們要寫入「屬性」,我們也要套用這結構。
而其中的屬性有三種特性——
特別把第一點拉出來說明,因為待會會用到這概念。
setAttributes()
更改現有屬性好,那到底要怎麼改屬性呢?我們先來一段程式碼。
function setElementAttribute(){
let doc_body = DocumentApp.getActiveDocument().getBody();
let style = {};
style[DocumentApp.Attribute.FOREGROUND_COLOR] = '#FF0000'
let paragraphs = doc_body.getParagraphs();
for(para of paragraphs){
para.setAttributes(style);
}
}
跑起來長這樣——
這邊先設定一個 dictionary 叫做 style
(let style = {}
),並用 style[key]=value
設定期中的數值(精確來說是 Key-value pair),其中 key
的部分就是我們從 Step 4 中抓出來的屬性們,只是前面多加了 DocumentApp.Attribute
;數值就是 Step 4 的數值們。
可以發現,就是將所有「段落」都改成紅色。至於為什麼「清單」也會變色,複習是因為清單內的文字,其實也有包含一個段落的元素。好,那這是整段的更改,基本上就是要全部元素取得後,針對一個個元素抓出來設定。
那如果我們想做部分文字的更改呢?這邊就可以用我們在 Step 3 最後面的段落,先貼上方便大家比較。
function highlightText() {
let target = '人';
let doc_body = DocumentApp.getActiveDocument().getBody();
let searchResult = doc_body.findText(target);
let textStyle={};
textStyle[DocumentApp.Attribute.FOREGROUND_COLOR] = '#FF0000'
while (searchResult !== null) {
let search_text = searchResult.getElement();
Logger.log(searchResult.getStartOffset() +' '+ searchResult.getEndOffsetInclusive())
search_text.setAttributes(searchResult.getStartOffset(), searchResult.getEndOffsetInclusive(),textStyle)
searchResult = doc_body.findText(target, searchResult);
}
基本上就是要在 setAttributes()
前加上「起始」與「結束」的 Offset,跑起來長這樣。
好,那我們介紹了「單純改字體顏色」,如果我們想調整的很多,要怎麼辦?沒問題的,這邊直接上程式碼,用「部分元素」做示範。
function highlightText() {
// target,background
let target = '人';
let doc_body = DocumentApp.getActiveDocument().getBody();
let searchResult = doc_body.findText(target);
let textStyle={};
textStyle[DocumentApp.Attribute.FOREGROUND_COLOR] = '#FF0000'
textStyle[DocumentApp.Attribute.FONT_FAMILY] = 'Calibri';
textStyle[DocumentApp.Attribute.FONT_SIZE] = 18;
textStyle[DocumentApp.Attribute.BOLD] = true;
while (searchResult !== null) {
let search_text = searchResult.getElement();
search_text.setAttributes(searchResult.getStartOffset(), searchResult.getEndOffsetInclusive(),textStyle)
searchResult = doc_body.findText(target, searchResult);
}
}
跑起來長這樣,確定我們有改到「人」字的顏色、字體與粗體——
但如果想設定其他的參數呢?這邊直接幫大家整理了表格。首先,如果是要改「部分文字」,可以用以下的功能們。
如果是要改「整個段落」,可以用以下的功能們。
如果是要改「文字」以外的部分,可以參考官方文件。
好,那這邊就是我們今天的內容。我有參考這篇:Can I color certain words in Google Document using Google Apps Script?,如果有夥伴想將上述功能改成有 UI 的 Add-On,裡面有完整的程式碼。另外,官方也有文件說明 Editing and styling text,也可以搭配參考。
好,那今天就到這邊。今天我們主要交代了 Attribute 的「如何更新」,總算把 Document 的部分告一段落,明天會進入 Slide 的部分,最後會講 Sheet。如果還有問題,透過留言之外,也可以到 Facebook Group,想開很久這次鐵人賽才真的開起來,歡迎來當 Founding Member。如果不想錯過可以訂閱按讚小鈴鐺(?),也歡迎留言跟我說你還想知道什麼做法/主題。我們明天見。