将数据合并到演示文稿中

Google Slides API 的一个实用应用是将一个或多个数据源中的信息合并到模板化的幻灯片组中。

本页概述了如何从外部来源获取数据并将其插入到现有模板演示中。此概念类似于使用文字处理器和电子表格进行邮件合并

这种方法之所以有用,有以下几个原因:

  • 设计人员可以轻松使用 Google 幻灯片编辑器微调演示文稿的设计。这比在应用中调整参数来设置渲染的幻灯片设计要简单得多。

  • 将内容与展示分离是一种众所周知的设计原则,具有诸多优势。

合并的概念图。

基本配方

以下示例展示了如何使用 Slides API 将数据合并到演示中:

  1. 使用占位内容创建演示文稿,以便您进行设计。

  2. 对于您要插入的每个内容元素,请将占位内容替换为标记。标记是包含唯一字符串的文本框或形状。请务必使用不太可能正常出现的字符串。例如,{{account-holder-name}} 可能是一个不错的标记。

  3. 在代码中,使用 Google Drive API 复制演示文稿。

  4. 在代码中,使用 Slides API 的 batchUpdate 方法(包含一组 replaceAllText 请求)在整个演示中执行所有文本替换操作。 使用 replaceAllShapesWithImage 请求在整个演示文稿中执行图片替换。

创建包含标记的演示文稿后,请务必制作副本,并使用 Slides API 来操作该副本。请勿使用 Slides API 来操作您的主要“模板”副本!

以下部分包含的代码段展示了此过程的某些方面。您还可以观看上面的视频,了解一个完整的示例 (Python),其中结合了下面各个部分中的多个概念。

合并文本

您可以使用 replaceAllText 请求将演示文稿中指定文本字符串的所有实例替换为新文本。对于合并,这比单独查找和替换每个文本实例要简单得多。之所以说这是最复杂的方法,是因为网页元素 ID 很难预测,尤其是在协作者改进和维护模板演示时。

示例

此示例使用 Drive API 复制演示模板,从而创建演示的新实例。然后,它使用 Google Sheets API 从 Google 表格中读取数据,最后使用 Slides API 更新新演示文稿。

此示例从电子表格中指定范围的一行中的 3 个单元格获取数据。然后,它会将这些数据替换到演示中出现字符串 {{customer-name}}{{case-description}}{{total-portfolio}} 的任何位置。

Apps 脚本

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
/**  * 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

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 脚本

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
/**  * 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

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 会将图片的宽高比调整为所需大小,然后应用所提供的转换。

将标记替换为图片时,您可以通过按如下方式设置图片的大小和缩放比例来保持图片的宽高比:

  • 宽度:设置为标记的 widthscaleX 的乘积
  • 高度:设置为标记的 heightscaleY 的乘积
  • scale_x:设置为 1
  • scale_y:设置为 1

这会导致 Slides API 根据标记的视觉大小(而非其未缩放的大小)对图片进行宽高比调整(请参阅将形状标记替换为图片)。将缩放参数设置为 1 可防止图片被缩放两次。

这种排列方式可确保图片宽高比保持不变,并防止图片超出标记形状的大小。图片与标记形状具有相同的中心点。

管理模板

对于应用定义和拥有的模板演示,请使用代表应用的专用账号创建模板。服务账号是不错的选择,可避免因 Google Workspace 政策限制共享而导致的问题。

从模板创建演示实例时,请始终使用最终用户凭据。这样一来,用户就可以完全掌控生成的演示,并避免与 Google 云端硬盘中与用户相关的限制相关的扩缩问题。

如需使用服务账号创建模板,请使用应用凭据执行以下步骤:

  1. 使用 Slides API 中的 presentations.create 创建演示文稿。
  2. 更新权限,以允许演示文稿接收者使用 Drive API 中的 permissions.create 读取演示文稿。
  3. 更新权限,以允许模板作者使用云端硬盘 API 中的 permissions.create 向其写入内容。
  4. 根据需要修改模板。

如需创建演示实例,请使用用户凭据执行以下步骤:

  1. 使用 Drive API 中的 files.copy 创建模板的副本。
  2. 使用 Google 幻灯片 API 中的 presentation.batchUpdate 替换值。