將資料合併至簡報

Google 簡報 API 的其中一項實用功能,是將一或多個資料來源的資訊合併到範本簡報中。

本頁說明如何從外部來源取得資料,並插入現有的範本簡報。這個概念與使用文書處理軟體和試算表進行合併列印類似。

這種做法有以下幾個優點:

  • 設計人員可以輕鬆使用 Google 簡報編輯器微調簡報設計。這比在應用程式中調整參數來設定算繪的投影片設計簡單許多。

  • 將內容與呈現方式分開是眾所皆知的設計原則,好處多多。

合併的概念圖。

基本配方

以下範例說明如何使用 Slides API 將資料合併至簡報:

  1. 使用預留位置內容建立簡報,方便設計簡報的呈現方式。

  2. 針對要插入的每個內容元素,請將預留位置內容換成標記。標記是含有不重複字串的文字方塊或形狀。請務必使用正常情況下不太可能出現的字串。舉例來說,{{account-holder-name}} 可能是不錯的標記。

  3. 在程式碼中,使用 Google Drive API 複製簡報。

  4. 在程式碼中,使用 Slides API 的 batchUpdate 方法和一組 replaceAllText 要求,在整個簡報中執行所有文字替換作業。使用 replaceAllShapesWithImage 要求在整個簡報中替換圖片。

建立含有標記的投影片組後,請務必複製一份,並使用 Slides API 操作副本。請勿使用 Google Slides API 操控主要「範本」副本!

以下各節包含的程式碼片段,說明瞭這個程序的部分內容。您也可以觀看上方影片,查看完整的範例 (Python),其中結合了下方各節的幾個概念。

合併文字

您可以使用 replaceAllText 要求,將簡報中特定文字字串的所有例項替換為新文字。與個別尋找及取代每個文字執行個體相比,這項功能更簡單。這個方法之所以最為複雜,是因為網頁元素 ID 難以預測,尤其是在協作者調整及維護範本簡報時。

範例

這個範例會使用 Google 雲端硬碟 API 複製範本簡報,建立簡報的新執行個體。接著使用 Google Sheets API 從 Google 試算表讀取資料,最後使用 Slides API 更新新簡報。

這個範例會從試算表具名範圍中,某列的 3 個儲存格擷取資料。然後,在出現字串 {{customer-name}}{{case-description}}{{total-portfolio}} 的位置,將該資料代入簡報。

Apps Script

slides/api/Snippets.gs
/**  * 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

slides/snippets/presentations.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

slides/snippets/src/main/java/TextMerging.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

slides/snippets/slides_text_merging.js
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

slides/snippets/slides_text_merging.js
import {GoogleAuth} from 'google-auth-library'; import {google} from 'googleapis';  /**  * Merges text from a spreadsheet into a template presentation.  * @param {string} templatePresentationId The ID of the template presentation.  * @param {string} dataSpreadsheetId The ID of the spreadsheet containing the data.  */ async function textMerging(templatePresentationId, dataSpreadsheetId) {   // Authenticate with Google and get an authorized client.   const auth = new GoogleAuth({     scopes: [       'https://www.googleapis.com/auth/presentations',       'https://www.googleapis.com/auth/drive',       'https://www.googleapis.com/auth/spreadsheets',     ],   });    // Create new clients for Slides, Sheets, and Drive APIs.   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 from the spreadsheet.   const dataRangeNotation = 'A2:M6';   const sheetsResponse = await sheetsService.spreadsheets.values.get({     spreadsheetId: dataSpreadsheetId,     range: dataRangeNotation,   });   const values = sheetsResponse.data.values;    // For each row of data, create a new presentation by copying the template   // and replacing the placeholder text with the data.   for (let i = 0; i < values.length; ++i) {     const row = values[i];     const customerName = row[2]; // Column 3     const caseDescription = row[5]; // Column 6     const totalPortfolio = row[11]; // Column 12      // Duplicate the template presentation.     const title = `${customerName} presentation`;     const driveResponse = await driveService.files.copy({       fileId: templatePresentationId,       requestBody: {         title,       },     });     const presentationCopyId = driveResponse.data.id;      // Create the text merge 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 to replace the placeholder text.     const batchUpdateResponse = await slidesService.presentations.batchUpdate({       presentationId: presentationCopyId,       requestBody: {         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.`);   } }

PHP

slides/snippets/src/SlidesTextMerging.php
<?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

slides/snippets/slides_text_merging.py
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",   )

Ruby

slides/snippets/lib/file_snippets.rb
# 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 Drive API 複製範本簡報,建立簡報的新執行個體。然後使用 Slides API 找出含有「{{company-logo}}」文字的任何形狀,並替換成公司標誌圖片。要求也會將所有形狀換成文字 {{customer-graphic}},並換成其他圖片。

Apps Script

slides/api/Snippets.gs
/**  * 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

slides/snippets/presentations.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

slides/snippets/src/main/java/ImageMerging.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

slides/snippets/slides_image_merging.js
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

slides/snippets/slides_image_merging.js
import {GoogleAuth} from 'google-auth-library'; import {google} from 'googleapis';  /**  * Replaces shapes in a presentation with images.  * @param {string} templatePresentationId The ID of the template presentation.  * @param {string} imageUrl The URL of the image to use.  * @param {string} customerName The name of the customer for the new presentation title.  * @return {Promise<object>} The response from the batch update.  */ async function imageMerging(templatePresentationId, imageUrl, customerName) {   // Authenticate with Google and get an authorized client.   const auth = new GoogleAuth({     scopes: [       'https://www.googleapis.com/auth/presentations',       'https://www.googleapis.com/auth/drive',     ],   });    // Create new clients for Slides and Drive APIs.   const slidesService = google.slides({version: 'v1', auth});   const driveService = google.drive({version: 'v2', auth});    const logoUrl = imageUrl;   const customerGraphicUrl = imageUrl;    // Duplicate the template presentation.   const copyTitle = `${customerName} presentation`;   const driveResponse = await driveService.files.copy({     fileId: templatePresentationId,     requestBody: {       name: copyTitle,     },   });   const presentationCopyId = driveResponse.data.id;    // Create the image merge 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 to replace the shapes with images.   const batchUpdateResponse = await slidesService.presentations.batchUpdate({     presentationId: presentationCopyId,     requestBody: {       requests,     },   });    // Count the total number of replacements made.   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; }

PHP

slides/snippets/src/SlidesImageMerging.php
<?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

slides/snippets/slides_image_merging.py
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",   )

Ruby

slides/snippets/lib/file_snippets.rb
# 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"

取代特定文字方塊或圖片例項

replaceAllTextreplaceAllShapesWithImage 要求可用於取代整個簡報中的標記,但有時您只需要根據其他條件 (例如位於特定投影片上) 取代元素。

在這些情況下,您必須擷取要取代的標記形狀 ID。如要替換文字,請刪除這些圖案中的現有文字,然後插入新文字 (請參閱「編輯特定圖案中的文字」範例)。

圖片替換作業較為複雜。如要合併圖片,請按照下列步驟操作:

  1. 取得標記形狀的 ID。
  2. 從標記複製大小和轉換資訊。
  3. 使用大小和轉換資訊,將圖片新增至網頁。
  4. 刪除標籤形狀。

如要將圖片縮放至所需大小,同時保留長寬比,可能需要多加留意,詳情請參閱下一節。另請參閱這個範例:以圖片取代形狀標記

保留長寬比

使用 Slides API 建立圖片時,系統只會根據圖片大小調整圖片比例,不會根據大小和轉換資料調整。系統會將您在createImage要求中提供的大小資料,視為圖片的所需大小。API 會將圖片的顯示比例調整為所需大小,然後套用提供的轉換。

以圖片取代標記時,請按照下列方式設定圖片大小和縮放比例,保留圖片的長寬比:

  • width:設為標記的 widthscaleX 的乘積
  • height:設為標記的 heightscaleY 的乘積
  • scale_x:設為 1
  • scale_y:設為 1

這會導致 Slides API 根據標記的視覺大小,而非未縮放的大小,調整圖片的長寬比 (請參閱「以圖片取代形狀標記」)。將縮放參數設為 1,可避免圖片縮放兩次。

這樣可確保圖片的顯示比例維持不變,並防止圖片超出標籤形狀的大小。圖片的中心點與標記形狀相同。

管理範本

如果是應用程式定義及擁有的範本簡報,請使用代表應用程式的專屬帳戶建立範本。服務帳戶是不錯的選擇,可避免 Google Workspace 政策限制分享而造成複雜情況。

從範本建立簡報執行個體時,請務必使用使用者憑證。這樣一來,使用者就能完全掌控產生的簡報,並避免 Google 雲端硬碟中與使用者限制相關的縮放問題。

如要使用服務帳戶建立範本,請使用應用程式憑證執行下列步驟:

  1. 使用 Google 簡報 API 中的 presentations.create 建立簡報。
  2. 更新權限,允許簡報收件者使用 Drive API 中的 permissions.create 讀取簡報。
  3. 更新權限,允許範本作者使用 Drive API 中的 permissions.create 寫入範本。
  4. 視需要編輯範本。

如要建立簡報的執行個體,請使用使用者憑證執行下列步驟:

  1. 使用 Drive API 中的 files.copy 建立範本副本。
  2. 使用 Slides API 中的 presentation.batchUpdate 替換值。