Google 簡報 API 的一個實用應用程式,是將一個或多個資料來源的資訊合併至範本簡報。
本頁面將概略說明如何從外部來源取得資料,並將資料插入現有的範本簡報。這個概念與使用文書處理程式和試算表進行合併列印類似。
這種做法有幾個好處:
設計人員可以輕鬆使用 Google 簡報編輯器微調簡報設計。這比在應用程式中調整參數來設定算繪投影片設計更容易。
將內容與呈現方式分開是眾所皆知的設計原則,且有許多好處。
基本食譜
以下是如何使用 Slides API 將資料合併至簡報的範例:
使用預留位置內容建立簡報,以便設計。
針對要插入的每個內容元素,請使用標記取代預留位置內容。標記是包含不重複字串的文字方塊或圖形。請務必使用平常不太可能出現的字串。例如,
{{account-holder-name}}
可能是個不錯的標記。在程式碼中使用 Google 雲端硬碟 API 建立簡報副本。
在程式碼中,使用 Slides API 的
batchUpdate
方法,搭配一組replaceAllText
要求,執行整個簡報中的所有文字替換作業。使用replaceAllShapesWithImage
要求,在整個簡報中執行圖片替換作業。
建立含有標記的投影片後,請務必複製一份,然後使用 Slides API 操作副本。請勿使用簡報 API 操作主要的「範本」副本!
以下各節包含程式碼片段,說明此程序的部分內容。您也可以觀看上述影片,瞭解完整的範例 (Python),其中結合了下方各個部分的幾個概念。
合併文字
您可以使用 replaceAllText
要求,將簡報中指定文字字串的所有例項替換為新文字。對於合併作業而言,這比個別找出及取代每個文字例項更簡單。這項方法最複雜的原因之一,是頁面元素 ID 難以預測,尤其是當協作者精進及維護範本呈現時。
範例
這個範例會使用 Drive API 複製範本簡報,建立簡報的新例項。接著,它會使用 Google 試算表 API 讀取試算表試算表的資料,最後使用簡報 API 更新新簡報。
這個範例會從試算表中命名範圍的一列中,擷取 3 個儲存格的資料。接著,系統會將該資料替換為字串 {{customer-name}}
、{{case-description}}
或 {{total-portfolio}}
出現的任何位置。
Apps Script
/** * Use the Sheets API to load data, one record per row. * @param {string} templatePresentationId * @param {string} dataSpreadsheetId * @returns {*[]} */ function textMerging(templatePresentationId, dataSpreadsheetId) { let responses = []; const dataRangeNotation = 'Customers!A2:M6'; try { let values = SpreadsheetApp.openById(dataSpreadsheetId).getRange(dataRangeNotation).getValues(); // For each record, create a new merged presentation. for (let i = 0; i < values.length; ++i) { const row = values[i]; const customerName = row[2]; // name in column 3 const caseDescription = row[5]; // case description in column 6 const totalPortfolio = row[11]; // total portfolio in column 12 // Duplicate the template presentation using the Drive API. const copyTitle = customerName + ' presentation'; let copyFile = { title: copyTitle, parents: [{id: 'root'}] }; copyFile = Drive.Files.copy(copyFile, templatePresentationId); const presentationCopyId = copyFile.id; // Create the text merge (replaceAllText) requests for this presentation. const requests = [{ replaceAllText: { containsText: { text: '{{customer-name}}', matchCase: true }, replaceText: customerName } }, { replaceAllText: { containsText: { text: '{{case-description}}', matchCase: true }, replaceText: caseDescription } }, { replaceAllText: { containsText: { text: '{{total-portfolio}}', matchCase: true }, replaceText: totalPortfolio + '' } }]; // Execute the requests for this presentation. const result = Slides.Presentations.batchUpdate({ requests: requests }, presentationCopyId); // Count the total number of replacements made. let numReplacements = 0; result.replies.forEach(function(reply) { numReplacements += reply.replaceAllText.occurrencesChanged; }); console.log('Created presentation for %s with ID: %s', customerName, presentationCopyId); console.log('Replaced %s text instances', numReplacements); } } catch (err) { // TODO (Developer) - Handle exception console.log('Failed with error: %s', err.error); } };
Go
// Use the Sheets API to load data, one record per row. dataRangeNotation := "Customers!A2:M6" sheetsResponse, _ := sheetsService.Spreadsheets.Values.Get(dataSpreadsheetId, dataRangeNotation).Do() values := sheetsResponse.Values // For each record, create a new merged presentation. for _, row := range values { customerName := row[2].(string) caseDescription := row[5].(string) totalPortfolio := row[11].(string) // Duplicate the template presentation using the Drive API. copyTitle := customerName + " presentation" file := drive.File{ Title: copyTitle, } presentationFile, _ := driveService.Files.Copy(templatePresentationId, &file).Do() presentationId := presentationFile.Id // Create the text merge (replaceAllText) requests for this presentation. requests := []*slides.Request{{ ReplaceAllText: &slides.ReplaceAllTextRequest{ ContainsText: &slides.SubstringMatchCriteria{ Text: "{{customer-name}}", MatchCase: true, }, ReplaceText: customerName, }, }, { ReplaceAllText: &slides.ReplaceAllTextRequest{ ContainsText: &slides.SubstringMatchCriteria{ Text: "{{case-description}}", MatchCase: true, }, ReplaceText: caseDescription, }, }, { ReplaceAllText: &slides.ReplaceAllTextRequest{ ContainsText: &slides.SubstringMatchCriteria{ Text: "{{total-portfolio}}", MatchCase: true, }, ReplaceText: totalPortfolio, }, }} // Execute the requests for this presentation. body := &slides.BatchUpdatePresentationRequest{ Requests: requests, } response, _ := slidesService.Presentations.BatchUpdate(presentationId, body).Do()
Java
import com.google.api.client.googleapis.json.GoogleJsonError; import com.google.api.client.googleapis.json.GoogleJsonResponseException; import com.google.api.client.http.HttpRequestInitializer; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.json.gson.GsonFactory; import com.google.api.services.drive.Drive; import com.google.api.services.drive.model.File; import com.google.api.services.sheets.v4.Sheets; import com.google.api.services.sheets.v4.model.ValueRange; import com.google.api.services.slides.v1.Slides; import com.google.api.services.slides.v1.SlidesScopes; import com.google.api.services.slides.v1.model.BatchUpdatePresentationRequest; import com.google.api.services.slides.v1.model.BatchUpdatePresentationResponse; import com.google.api.services.slides.v1.model.ReplaceAllTextRequest; import com.google.api.services.slides.v1.model.Request; import com.google.api.services.slides.v1.model.Response; import com.google.api.services.slides.v1.model.SubstringMatchCriteria; import com.google.auth.http.HttpCredentialsAdapter; import com.google.auth.oauth2.GoogleCredentials; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /* Class to demonstrate the use of Slides Text Merging API */ public class TextMerging { /** * Changes specified texts with data from spreadsheet. * * @param templatePresentationId - id of the presentation. * @param dataSpreadsheetId - id of the spreadsheet containing data. * @return merged presentation id * @throws IOException - if credentials file not found. */ public static List<BatchUpdatePresentationResponse> textMerging( String templatePresentationId, String dataSpreadsheetId) throws IOException { /* Load pre-authorized user credentials from the environment. TODO(developer) - See https://developers.google.com/identity for guides on implementing OAuth2 for your application. */ GoogleCredentials credentials = GoogleCredentials.getApplicationDefault() .createScoped(Arrays.asList(SlidesScopes.PRESENTATIONS, SlidesScopes.DRIVE, SlidesScopes.SPREADSHEETS)); HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter( credentials); // Create the slides API client Slides service = new Slides.Builder(new NetHttpTransport(), GsonFactory.getDefaultInstance(), requestInitializer) .setApplicationName("Slides samples") .build(); // Create the drive API client Drive driveService = new Drive.Builder(new NetHttpTransport(), GsonFactory.getDefaultInstance(), requestInitializer) .setApplicationName("Slides samples") .build(); // Create the sheets API client Sheets sheetsService = new Sheets.Builder(new NetHttpTransport(), GsonFactory.getDefaultInstance(), requestInitializer) .setApplicationName("Slides samples") .build(); List<BatchUpdatePresentationResponse> responses = new ArrayList<>(5); // Use the Sheets API to load data, one record per row. String dataRangeNotation = "Customers!A2:M6"; ValueRange sheetsResponse = sheetsService.spreadsheets().values() .get(dataSpreadsheetId, dataRangeNotation).execute(); List<List<Object>> values = sheetsResponse.getValues(); try { // For each record, create a new merged presentation. for (List<Object> row : values) { String customerName = row.get(2).toString(); // name in column 3 String caseDescription = row.get(5).toString(); // case description in column 6 String totalPortfolio = row.get(11).toString(); // total portfolio in column 12 // Duplicate the template presentation using the Drive API. String copyTitle = customerName + " presentation"; File content = new File().setName(copyTitle); File presentationFile = driveService.files().copy(templatePresentationId, content).execute(); String presentationId = presentationFile.getId(); // Create the text merge (replaceAllText) requests for this presentation. List<Request> requests = new ArrayList<>(); requests.add(new Request() .setReplaceAllText(new ReplaceAllTextRequest() .setContainsText(new SubstringMatchCriteria() .setText("{{customer-name}}") .setMatchCase(true)) .setReplaceText(customerName))); requests.add(new Request() .setReplaceAllText(new ReplaceAllTextRequest() .setContainsText(new SubstringMatchCriteria() .setText("{{case-description}}") .setMatchCase(true)) .setReplaceText(caseDescription))); requests.add(new Request() .setReplaceAllText(new ReplaceAllTextRequest() .setContainsText(new SubstringMatchCriteria() .setText("{{total-portfolio}}") .setMatchCase(true)) .setReplaceText(totalPortfolio))); // Execute the requests for this presentation. BatchUpdatePresentationRequest body = new BatchUpdatePresentationRequest().setRequests(requests); BatchUpdatePresentationResponse response = service.presentations().batchUpdate(presentationId, body).execute(); // Count total number of replacements made. int numReplacements = 0; for (Response resp : response.getReplies()) { numReplacements += resp.getReplaceAllText().getOccurrencesChanged(); } // Prints the merged presentation id and count of replacements. System.out.println("Created merged presentation for " + customerName + " with ID: " + presentationId); System.out.println("Replaced " + numReplacements + " text instances."); } } catch (NullPointerException ne) { System.out.println("Text not found to replace with image."); } catch (GoogleJsonResponseException e) { // TODO(developer) - handle error appropriately GoogleJsonError error = e.getDetails(); if (error.getCode() == 404) { System.out.printf("Presentation not found with id '%s'.\n", templatePresentationId); } else { throw e; } } return responses; } }
JavaScript
function textMerging(templatePresentationId, dataSpreadsheetId, callback) { // Use the Sheets API to load data, one record per row. const responses = []; const dataRangeNotation = 'Customers!A2:M6'; try { gapi.client.sheets.spreadsheets.values.get({ spreadsheetId: dataSpreadsheetId, range: dataRangeNotation, }).then((sheetsResponse) => { const values = sheetsResponse.result.values; // For each record, create a new merged presentation. for (let i = 0; i < values.length; ++i) { const row = values[i]; const customerName = row[2]; // name in column 3 const caseDescription = row[5]; // case description in column 6 const totalPortfolio = row[11]; // total portfolio in column 12 // Duplicate the template presentation using the Drive API. const copyTitle = customerName + ' presentation'; const request = { name: copyTitle, }; gapi.client.drive.files.copy({ fileId: templatePresentationId, requests: request, }).then((driveResponse) => { const presentationCopyId = driveResponse.result.id; // Create the text merge (replaceAllText) requests for this presentation. const requests = [{ replaceAllText: { containsText: { text: '{{customer-name}}', matchCase: true, }, replaceText: customerName, }, }, { replaceAllText: { containsText: { text: '{{case-description}}', matchCase: true, }, replaceText: caseDescription, }, }, { replaceAllText: { containsText: { text: '{{total-portfolio}}', matchCase: true, }, replaceText: totalPortfolio, }, }]; // Execute the requests for this presentation. gapi.client.slides.presentations.batchUpdate({ presentationId: presentationCopyId, requests: requests, }).then((batchUpdateResponse) => { const result = batchUpdateResponse.result; responses.push(result.replies); // Count the total number of replacements made. let numReplacements = 0; for (let i = 0; i < result.replies.length; ++i) { numReplacements += result.replies[i].replaceAllText.occurrencesChanged; } console.log(`Created presentation for ${customerName} with ID: ${presentationCopyId}`); console.log(`Replaced ${numReplacements} text instances`); if (responses.length === values.length) { // callback for the last value if (callback) callback(responses); } }); }); } }); } catch (err) { document.getElementById('content').innerText = err.message; return; } }
Node.js
/** * Adds data from a spreadsheet to a template presentation. * @param {string} templatePresentationId The template presentation ID. * @param {string} dataSpreadsheetId The data spreadsheet ID. */ async function textMerging(templatePresentationId, dataSpreadsheetId) { const {GoogleAuth} = require('google-auth-library'); const {google} = require('googleapis'); const auth = new GoogleAuth({ scopes: [ 'https://www.googleapis.com/auth/presentations', 'https://www.googleapis.com/auth/drive', 'https://www.googleapis.com/auth/spreadsheets', ], }); const slidesService = google.slides({version: 'v1', auth}); const sheetsService = google.sheets({version: 'v4', auth}); const driveService = google.drive({version: 'v2', auth}); // Use the Sheets API to load data, one record per row. const responses = []; const dataRangeNotation = 'A2:M6'; try { const sheetsResponse = await sheetsService.spreadsheets.values.get({ spreadsheetId: dataSpreadsheetId, range: dataRangeNotation, }); const values = sheetsResponse.data.values; // For each record, create a new merged presentation. for (let i = 0; i < values.length; ++i) { const row = values[i]; const customerName = row[2]; // name in column 3 const caseDescription = row[5]; // case description in column 6 const totalPortfolio = row[11]; // total portfolio in column 12 // Duplicate the template presentation using the Drive API. const copyTitle = customerName + ' presentation'; let requests = { name: copyTitle, }; const driveResponse = await driveService.files.copy({ fileId: templatePresentationId, requests, }); const presentationCopyId = driveResponse.data.id; // Create the text merge (replaceAllText) requests for this presentation. requests = [ { replaceAllText: { containsText: { text: '{{customer-name}}', matchCase: true, }, replaceText: customerName, }, }, { replaceAllText: { containsText: { text: '{{case-description}}', matchCase: true, }, replaceText: caseDescription, }, }, { replaceAllText: { containsText: { text: '{{total-portfolio}}', matchCase: true, }, replaceText: totalPortfolio, }, }, ]; // Execute the requests for this presentation. const batchUpdateResponse = await slidesService.presentations.batchUpdate( { presentationId: presentationCopyId, resource: { requests, }, }, ); const result = batchUpdateResponse.data; // Count the total number of replacements made. let numReplacements = 0; for (let i = 0; i < result.replies.length; ++i) { numReplacements += result.replies[i].replaceAllText.occurrencesChanged; } console.log( `Created presentation for ${customerName} with ID: ` + presentationCopyId, ); console.log(`Replaced ${numReplacements} text instances`); return result; } } catch (err) { // TODO (developer) - Handle exception throw err; } }
PHP
use Google\Client; use Google\Service\Drive; use Google\Service\Slides; use Google\Service\Slides\Request; function textMerging($templatePresentationId, $dataSpreadsheetId) { /* Load pre-authorized user credentials from the environment. TODO(developer) - See https://developers.google.com/identity for guides on implementing OAuth2 for your application. */ $client = new Google\Client(); $client->useApplicationDefaultCredentials(); $client->addScope(Google\Service\Drive::DRIVE); $slidesService = new Google_Service_Slides($client); $driveService = new Google_Service_Drive($client); $sheetsService = new Google_Service_Sheets($client); try { $responses = array(); // Use the Sheets API to load data, one record per row. $dataRangeNotation = 'Customers!A2:M6'; $sheetsResponse = $sheetsService->spreadsheets_values->get($dataSpreadsheetId, $dataRangeNotation); $values = $sheetsResponse['values']; // For each record, create a new merged presentation. foreach ($values as $row) { $customerName = $row[2]; // name in column 3 $caseDescription = $row[5]; // case description in column 6 $totalPortfolio = $row[11]; // total portfolio in column 12 // Duplicate the template presentation using the Drive API. $copy = new Google_Service_Drive_DriveFile(array( 'name' => $customerName . ' presentation' )); $driveResponse = $driveService->files->copy($templatePresentationId, $copy); $presentationCopyId = $driveResponse->id; // Create the text merge (replaceAllText) requests for this presentation. $requests = array(); $requests[] = new Google_Service_Slides_Request(array( 'replaceAllText' => array( 'containsText' => array( 'text' => '{{customer-name}}', 'matchCase' => true ), 'replaceText' => $customerName ) )); $requests[] = new Google_Service_Slides_Request(array( 'replaceAllText' => array( 'containsText' => array( 'text' => '{{case-description}}', 'matchCase' => true ), 'replaceText' => $caseDescription ) )); $requests[] = new Google_Service_Slides_Request(array( 'replaceAllText' => array( 'containsText' => array( 'text' => '{{total-portfolio}}', 'matchCase' => true ), 'replaceText' => $totalPortfolio ) )); // Execute the requests for this presentation. $batchUpdateRequest = new Google_Service_Slides_BatchUpdatePresentationRequest(array( 'requests' => $requests )); $response = $slidesService->presentations->batchUpdate($presentationCopyId, $batchUpdateRequest); $responses[] = $response; // Count the total number of replacements made. $numReplacements = 0; foreach ($response->getReplies() as $reply) { $numReplacements += $reply->getReplaceAllText()->getOccurrencesChanged(); } printf("Created presentation for %s with ID: %s\n", $customerName, $presentationCopyId); printf("Replaced %d text instances.\n", $numReplacements); } return $responses; } catch (Exception $e) { echo 'Message: ' . $e->getMessage(); } }
Python
import google.auth from googleapiclient.discovery import build from googleapiclient.errors import HttpError def text_merging(template_presentation_id, data_spreadsheet_id): """ Run Text merging the user has access to. Load pre-authorized user credentials from the environment. TODO(developer) - See https://developers.google.com/identity for guides on implementing OAuth2 for the application. """ creds, _ = google.auth.default() # pylint: disable=maybe-no-member try: service = build("slides", "v1", credentials=creds) sheets_service = build("sheets", "v4", credentials=creds) drive_service = build("drive", "v3", credentials=creds) # Use the Sheets API to load data, one record per row. data_range_notation = "Customers!A2:M6" sheets_response = ( sheets_service.spreadsheets() .values() .get(spreadsheetId=data_spreadsheet_id, range=data_range_notation) .execute() ) values = sheets_response.get("values") # For each record, create a new merged presentation. for row in values: customer_name = row[2] # name in column 3 case_description = row[5] # case description in column 6 total_portfolio = row[11] # total portfolio in column 12 # Duplicate the template presentation using the Drive API. copy_title = customer_name + " presentation" body = {"name": copy_title} drive_response = ( drive_service.files() .copy(fileId=template_presentation_id, body=body) .execute() ) presentation_copy_id = drive_response.get("id") # Create the text merge (replaceAllText) requests # for this presentation. requests = [ { "replaceAllText": { "containsText": { "text": "{{customer-name}}", "matchCase": True, }, "replaceText": customer_name, } }, { "replaceAllText": { "containsText": { "text": "{{case-description}}", "matchCase": True, }, "replaceText": case_description, } }, { "replaceAllText": { "containsText": { "text": "{{total-portfolio}}", "matchCase": True, }, "replaceText": total_portfolio, } }, ] # Execute the requests for this presentation. body = {"requests": requests} response = ( service.presentations() .batchUpdate(presentationId=presentation_copy_id, body=body) .execute() ) # Count the total number of replacements made. num_replacements = 0 for reply in response.get("replies"): if reply.get("occurrencesChanged") is not None: num_replacements += reply.get("replaceAllText").get( "occurrencesChanged" ) print( "Created presentation for " f"{customer_name} with ID: {presentation_copy_id}" ) print(f"Replaced {num_replacements} text instances") except HttpError as error: print(f"An error occurred: {error}") return error if __name__ == "__main__": # Put the template_presentation_id, data_spreadsheet_id # of slides text_merging( "10QnVUx1X2qHsL17WUidGpPh_SQhXYx40CgIxaKk8jU4", "17eqFZl_WK4WVixX8PjvjfLD77DraoFwMDXeiHB3dvuM", )
小茹
# Use the Sheets API to load data, one record per row. data_range_notation = 'Customers!A2:M6' sheets_response = sheets_service.get_spreadsheet_values( data_spreadsheet_id, data_range_notation ) values = sheets_response.values # For each record, create a new merged presentation. values.each do |row| customer_name = row[2] # name in column 3 case_description = row[5] # case description in column 6 total_portfolio = row[11] # total portfolio in column 12 # Duplicate the template presentation using the Drive API. copy_title = customer_name + ' presentation' body = Google::Apis::SlidesV1::Presentation.new body.title = copy_title drive_response = drive_service.copy_file(template_presentation_id, body) presentation_copy_id = drive_response.id # Create the text merge (replace_all_text) requests for this presentation. requests = [] << { replace_all_text: { contains_text: { text: '{{customer-name}}', match_case: true }, replace_text: customer_name } } << { replace_all_text: { contains_text: { text: '{{case-description}}', match_case: true }, replace_text: case_description } } << { replace_all_text: { contains_text: { text: '{{total-portfolio}}', match_case: true }, replace_text: total_portfolio } } # Execute the requests for this presentation. req = Google::Apis::SlidesV1::BatchUpdatePresentationRequest.new(requests: requests) response = slides_service.batch_update_presentation( presentation_copy_id, req )
合併圖片
您也可以使用 replaceAllShapesWithImage
要求,將圖片合併至簡報中。這項要求會將所有包含提供文字字串的形狀,替換為提供的圖片。要求會自動調整圖片位置和縮放比例,讓圖片符合標記形狀的邊界,同時保留圖片的顯示比例。
範例
這個範例會使用 Google 雲端硬碟 API 複製範本簡報,建立簡報的新例項。接著,它會使用 Slides API 找出任何含有文字 {{company-logo}}
的形狀,並將其替換為公司標誌圖片。這項要求也會將任何含有文字 {{customer-graphic}}
的圖形替換為其他圖片。
Apps Script
/** * Duplicate the template presentation using the Drive API. * @param {string} templatePresentationId * @param {string} imageUrl * @param {string} customerName * @returns {*} */ function imageMerging(templatePresentationId, imageUrl, customerName) { const logoUrl = imageUrl; const customerGraphicUrl = imageUrl; const copyTitle = customerName + ' presentation'; let copyFile = { title: copyTitle, parents: [{id: 'root'}] }; try { copyFile = Drive.Files.copy(copyFile, templatePresentationId); const presentationCopyId = copyFile.id; // Create the image merge (replaceAllShapesWithImage) requests. const requests = [{ replaceAllShapesWithImage: { imageUrl: logoUrl, imageReplaceMethod: 'CENTER_INSIDE', containsText: { text: '{{company-logo}}', matchCase: true } } }, { replaceAllShapesWithImage: { imageUrl: customerGraphicUrl, imageReplaceMethod: 'CENTER_INSIDE', containsText: { text: '{{customer-graphic}}', matchCase: true } } }]; // Execute the requests for this presentation. let batchUpdateResponse = Slides.Presentations.batchUpdate({ requests: requests }, presentationCopyId); let numReplacements = 0; batchUpdateResponse.replies.forEach(function(reply) { numReplacements += reply.replaceAllShapesWithImage.occurrencesChanged; }); console.log('Created merged presentation with ID: %s', presentationCopyId); console.log('Replaced %s shapes with images.', numReplacements); return batchUpdateResponse; } catch (err) { // TODO (Developer) - Handle exception console.log('Failed with error: %s', err.error); } };
Go
// Duplicate the template presentation using the Drive API. copyTitle := customerName + " presentation" file := drive.File{ Title: copyTitle, } presentationFile, _ := driveService.Files.Copy(templatePresentationId, &file).Do() presentationId := presentationFile.Id // Create the image merge (replaceAllShapesWithImage) requests. requests := []*slides.Request{{ ReplaceAllShapesWithImage: &slides.ReplaceAllShapesWithImageRequest{ ImageUrl: logoURL, ReplaceMethod: "CENTER_INSIDE", ContainsText: &slides.SubstringMatchCriteria{ Text: "{{company-logo}}", MatchCase: true, }, }, }, { ReplaceAllShapesWithImage: &slides.ReplaceAllShapesWithImageRequest{ ImageUrl: customerGraphicURL, ReplaceMethod: "CENTER_INSIDE", ContainsText: &slides.SubstringMatchCriteria{ Text: "{{customer-graphic}}", MatchCase: true, }, }, }} // Execute the requests for this presentation. body := &slides.BatchUpdatePresentationRequest{Requests: requests} response, _ := slidesService.Presentations.BatchUpdate(presentationId, body).Do() // Count total number of replacements made. var numReplacements int64 = 0 for _, resp := range response.Replies { numReplacements += resp.ReplaceAllShapesWithImage.OccurrencesChanged } fmt.Printf("Created merged presentation with ID %s\n", presentationId) fmt.Printf("Replaced %d shapes instances with images.\n", numReplacements)
Java
import com.google.api.client.http.HttpRequestInitializer; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.json.gson.GsonFactory; import com.google.api.services.drive.Drive; import com.google.api.services.drive.model.File; import com.google.api.services.slides.v1.Slides; import com.google.api.services.slides.v1.SlidesScopes; import com.google.api.services.slides.v1.model.BatchUpdatePresentationRequest; import com.google.api.services.slides.v1.model.BatchUpdatePresentationResponse; import com.google.api.services.slides.v1.model.Request; import com.google.api.services.slides.v1.model.Response; import com.google.api.services.slides.v1.model.ReplaceAllShapesWithImageRequest; import com.google.api.services.slides.v1.model.SubstringMatchCriteria; import com.google.auth.http.HttpCredentialsAdapter; import com.google.auth.oauth2.GoogleCredentials; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /* Class to demonstrate the use of Slides Image Merging API */ public class ImageMerging { /** * Changes specified texts into images. * * @param templatePresentationId - id of the presentation. * @param imageUrl - Url of the image. * @param customerName - Name of the customer. * @return merged presentation id * @throws IOException - if credentials file not found. */ public static BatchUpdatePresentationResponse imageMerging(String templatePresentationId, String imageUrl, String customerName) throws IOException { /* Load pre-authorized user credentials from the environment. TODO(developer) - See https://developers.google.com/identity for guides on implementing OAuth2 for your application. */ GoogleCredentials credentials = GoogleCredentials.getApplicationDefault() .createScoped(Arrays.asList(SlidesScopes.PRESENTATIONS, SlidesScopes.DRIVE)); HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter( credentials); // Create the slides API client Slides service = new Slides.Builder(new NetHttpTransport(), GsonFactory.getDefaultInstance(), requestInitializer) .setApplicationName("Slides samples") .build(); // Create the drive API client Drive driveService = new Drive.Builder(new NetHttpTransport(), GsonFactory.getDefaultInstance(), requestInitializer) .setApplicationName("Slides samples") .build(); // Duplicate the template presentation using the Drive API. String copyTitle = customerName + " presentation"; File content = new File().setName(copyTitle); File presentationFile = driveService.files().copy(templatePresentationId, content).execute(); String presentationId = presentationFile.getId(); // Create the image merge (replaceAllShapesWithImage) requests. List<Request> requests = new ArrayList<>(); requests.add(new Request() .setReplaceAllShapesWithImage(new ReplaceAllShapesWithImageRequest() .setImageUrl(imageUrl) .setImageReplaceMethod("CENTER_INSIDE") .setContainsText(new SubstringMatchCriteria() .setText("{{company-logo}}") .setMatchCase(true)))); // Execute the requests. BatchUpdatePresentationRequest body = new BatchUpdatePresentationRequest().setRequests(requests); BatchUpdatePresentationResponse response = service.presentations().batchUpdate(presentationId, body).execute(); int numReplacements = 0; try { // Count total number of replacements made. for (Response resp : response.getReplies()) { numReplacements += resp.getReplaceAllShapesWithImage().getOccurrencesChanged(); } // Prints the merged presentation id and count of replacements. System.out.println("Created merged presentation with ID: " + presentationId); System.out.println("Replaced " + numReplacements + " shapes instances with images."); } catch (NullPointerException ne) { System.out.println("Text not found to replace with image."); } return response; } }
JavaScript
function imageMerging( templatePresentationId, imageUrl, customerName, callback, ) { const logoUrl = imageUrl; const customerGraphicUrl = imageUrl; // Duplicate the template presentation using the Drive API. const copyTitle = customerName + ' presentation'; try { gapi.client.drive.files .copy({ fileId: templatePresentationId, resource: { name: copyTitle, }, }) .then((driveResponse) => { const presentationCopyId = driveResponse.result.id; // Create the image merge (replaceAllShapesWithImage) requests. const requests = [ { replaceAllShapesWithImage: { imageUrl: logoUrl, replaceMethod: 'CENTER_INSIDE', containsText: { text: '{{company-logo}}', matchCase: true, }, }, }, { replaceAllShapesWithImage: { imageUrl: customerGraphicUrl, replaceMethod: 'CENTER_INSIDE', containsText: { text: '{{customer-graphic}}', matchCase: true, }, }, }, ]; // Execute the requests for this presentation. gapi.client.slides.presentations .batchUpdate({ presentationId: presentationCopyId, requests: requests, }) .then((batchUpdateResponse) => { let numReplacements = 0; for ( let i = 0; i < batchUpdateResponse.result.replies.length; ++i ) { numReplacements += batchUpdateResponse.result.replies[i].replaceAllShapesWithImage .occurrencesChanged; } console.log( `Created merged presentation with ID: ${presentationCopyId}`, ); console.log(`Replaced ${numReplacements} shapes with images.`); if (callback) callback(batchUpdateResponse.result); }); }); } catch (err) { document.getElementById('content').innerText = err.message; return; } }
Node.js
/** * Add an image to a template presentation. * @param {string} templatePresentationId The template presentation ID. * @param {string} imageUrl The image URL * @param {string} customerName A customer name used for the title */ async function imageMerging(templatePresentationId, imageUrl, customerName) { const {GoogleAuth} = require('google-auth-library'); const {google} = require('googleapis'); const auth = new GoogleAuth({ scopes: [ 'https://www.googleapis.com/auth/presentations', 'https://www.googleapis.com/auth/drive', ], }); const slidesService = google.slides({version: 'v1', auth}); const driveService = google.drive({version: 'v2', auth}); const logoUrl = imageUrl; const customerGraphicUrl = imageUrl; // Duplicate the template presentation using the Drive API. const copyTitle = customerName + ' presentation'; try { const driveResponse = await driveService.files.copy({ fileId: templatePresentationId, resource: { name: copyTitle, }, }); const presentationCopyId = driveResponse.data.id; // Create the image merge (replaceAllShapesWithImage) requests. const requests = [ { replaceAllShapesWithImage: { imageUrl: logoUrl, replaceMethod: 'CENTER_INSIDE', containsText: { text: '{{company-logo}}', matchCase: true, }, }, }, { replaceAllShapesWithImage: { imageUrl: customerGraphicUrl, replaceMethod: 'CENTER_INSIDE', containsText: { text: '{{customer-graphic}}', matchCase: true, }, }, }, ]; // Execute the requests for this presentation. const batchUpdateResponse = await slidesService.presentations.batchUpdate({ presentationId: presentationCopyId, resource: { requests, }, }); let numReplacements = 0; for (let i = 0; i < batchUpdateResponse.data.replies.length; ++i) { numReplacements += batchUpdateResponse.data.replies[i].replaceAllShapesWithImage .occurrencesChanged; } console.log(`Created merged presentation with ID: ${presentationCopyId}`); console.log(`Replaced ${numReplacements} shapes with images.`); return batchUpdateResponse.data; } catch (err) { // TODO (developer) - Handle exception throw err; } }
PHP
use Google\Client; use Google\Service\Drive; use Google\Service\Slides; use Google\Service\DriveFile; use Google\Service\Slides\Request; function imageMerging($templatePresentationId, $imageUrl, $customerName) { /* Load pre-authorized user credentials from the environment. TODO(developer) - See https://developers.google.com/identity for guides on implementing OAuth2 for your application. */ $client = new Google\Client(); $client->useApplicationDefaultCredentials(); $client->addScope(Google\Service\Drive::DRIVE); $slidesService = new Google_Service_Slides($client); $driveService = new Google_Service_Drive($client); // Duplicate the template presentation using the Drive API. $copy = new Google_Service_Drive_DriveFile([ 'name' => $customerName . ' presentation' ]); $driveResponse = $driveService->files->copy($templatePresentationId, $copy); $presentationCopyId = $driveResponse->id; // Create the image merge (replaceAllShapesWithImage) requests. $requests[] = new Google_Service_Slides_Request([ 'replaceAllShapesWithImage' => [ 'imageUrl' => $imageUrl, 'replaceMethod' => 'CENTER_INSIDE', 'containsText' => [ 'text' => '{{company-logo}}', 'matchCase' => true ] ] ]); $requests[] = new Google_Service_Slides_Request([ 'replaceAllShapesWithImage' => [ 'imageUrl' => $imageUrl, 'replaceMethod' => 'CENTER_INSIDE', 'containsText' => [ 'text' => '{{customer-graphic}}', 'matchCase' => true ] ] ]); // Execute the requests. $batchUpdateRequest = new Google_Service_Slides_BatchUpdatePresentationRequest([ 'requests' => $requests ]); $response = $slidesService->presentations->batchUpdate($presentationCopyId, $batchUpdateRequest); // Count the total number of replacements made. $numReplacements = 0; foreach ($response->getReplies() as $reply) { $numReplacements += $reply->getReplaceAllShapesWithImage()->getOccurrencesChanged(); } printf("Created presentation for %s with ID: %s\n", $customerName, $presentationCopyId); printf("Replaced %d shapes with images.\n", $numReplacements); return $response; }
Python
import google.auth from googleapiclient.discovery import build from googleapiclient.errors import HttpError def image_merging(template_presentation_id, image_url, customer_name): """image_merging require template_presentation_id, image_url and customer_name Load pre-authorized user credentials from the environment. TODO(developer) - See https://developers.google.com/identity for guides on implementing OAuth2 for the application. """ creds, _ = google.auth.default() # pylint: disable=maybe-no-member try: slides_service = build("slides", "v1", credentials=creds) drive_service = build("drive", "v3", credentials=creds) logo_url = image_url customer_graphic_url = image_url # Duplicate the template presentation using the Drive API. copy_title = customer_name + " presentation" drive_response = ( drive_service.files() .copy(fileId=template_presentation_id, body={"name": copy_title}) .execute() ) presentation_copy_id = drive_response.get("id") # Create the image merge (replaceAllShapesWithImage) requests. requests = [] requests.append( { "replaceAllShapesWithImage": { "imageUrl": logo_url, "replaceMethod": "CENTER_INSIDE", "containsText": { "text": "{{company-logo}}", "matchCase": True, }, } } ) requests.append( { "replaceAllShapesWithImage": { "imageUrl": customer_graphic_url, "replaceMethod": "CENTER_INSIDE", "containsText": { "text": "{{customer-graphic}}", "matchCase": True, }, } } ) # Execute the requests. body = {"requests": requests} response = ( slides_service.presentations() .batchUpdate(presentationId=presentation_copy_id, body=body) .execute() ) # Count the number of replacements made. num_replacements = 0 for reply in response.get("replies"): # add below line if reply.get("occurrencesChanged") is not None: # end tag num_replacements += reply.get("replaceAllShapesWithImage").get( "occurrencesChanged" ) print(f"Created merged presentation with ID:{presentation_copy_id}") print(f"Replaced {num_replacements} shapes with images") except HttpError as error: print(f"An error occurred: {error}") print("Images is not merged") return error return response if __name__ == "__main__": # Put the template_presentation_id, image_url and customer_name image_merging( "10QnVUx1X2qHsL17WUidGpPh_SQhXYx40CgIxaKk8jU4", "https://www.google.com/images/branding/" "googlelogo/2x/googlelogo_color_272x92dp.png", "Fake Customer", )
小茹
# Duplicate the template presentation using the Drive API. copy_title = customer_name + ' presentation' body = Google::Apis::SlidesV1::Presentation.new body.title = copy_title drive_response = drive_service.copy_file(template_presentation_id, body) presentation_copy_id = drive_response.id # Create the image merge (replace_all_shapes_with_image) requests. requests = [] << { replace_all_shapes_with_image: { image_url: logo_url, replace_method: 'CENTER_INSIDE', contains_text: { text: '{{company-logo}}', match_case: true } } } << { replace_all_shapes_with_image: { image_url: customer_graphic_url, replace_method: 'CENTER_INSIDE', contains_text: { text: '{{customer-graphic}}', match_case: true } } } # Execute the requests. req = Google::Apis::SlidesV1::BatchUpdatePresentationRequest.new(requests: requests) response = slides_service.batch_update_presentation( presentation_copy_id, req ) # Count the number of replacements made. num_replacements = 0 response.replies.each do |reply| num_replacements += reply.replace_all_shapes_with_image.occurrences_changed end puts "Created presentation for #{customer_name} with ID: #{presentation_copy_id}" puts "Replaced #{num_replacements} shapes with images"
取代特定文字方塊或圖片例項
replaceAllText
和 replaceAllShapesWithImage
要求可用於在整個簡報中替換代碼,但有時您只需要根據其他條件 (例如位於特定投影片) 替換元素。
在這種情況下,您必須擷取要取代的代碼形狀 ID。如要替換文字,請刪除這些形狀中的現有文字,然後插入新文字 (請參閱「在指定形狀中編輯文字」範例)。
圖片替換作業較為複雜。如要合併圖片,您必須:
- 取得標記形狀的 ID。
- 從標記複製大小和轉換資訊。
- 使用大小和轉換資訊,將圖片新增至網頁。
- 刪除標籤形狀。
在縮放圖片至所需大小的同時,請務必注意保留圖片的顯示比例,詳情請見下節。另請參閱這個範例:使用圖片取代形狀標記。
保留顯示比例
使用 Slides API 建立圖片時,顯示比例只會根據圖片大小,而非大小和轉換資料。您在 createImage
要求中提供的大小資料,會視為圖片的理想大小。API 會將圖片的顯示比例調整為所需大小,然後套用提供的轉換。
將標記替換為圖片時,請按照下列方式設定圖片的大小和縮放比例,以保留圖片的顯示比例:
- width:設為標記的
width
和scaleX
乘積 - height:設為標記的
height
和scaleY
的乘積 - scale_x:設為
1
- scale_y:設為
1
這會導致 Slides API 根據標記的視覺大小,而非未縮放的大小,將圖片調整為適當的顯示比例 (請參閱「以圖片取代形狀標記」)。將縮放參數設為 1
可避免圖片縮放兩次。
這項安排可確保圖片的顯示比例得以保留,並防止圖片超出標記形狀的大小。圖片的中心點與標記形狀相同。
管理範本
針對應用程式定義及擁有的範本簡報,請使用代表應用程式的專屬帳戶建立範本。服務帳戶是個不錯的選擇,可避免因 Google Workspace 限制共用政策而造成複雜情況。
使用範本建立簡報執行個體時,請務必使用終端使用者憑證。這樣一來,使用者就能完全控管產生的簡報,並避免 Google 雲端硬碟中與每位使用者限制相關的縮放問題。
如要使用服務帳戶建立範本,請使用應用程式憑證執行下列步驟:
- 使用 Google 簡報 API 中的 presentations.create 建立簡報。
- 更新權限,讓簡報收件者可透過 Drive API 中的 permissions.create 讀取簡報。
- 更新權限,允許範本作者使用 Drive API 中的 permissions.create 寫入範本。
- 視需要編輯範本。
如要建立簡報的例項,請使用使用者憑證執行下列步驟:
- 使用 Drive API 中的 files.copy 建立範本副本。
- 使用 Slides API 中的 presentation.batchUpdate 取代值。