Google Slides API 的一个实用应用是将一个或多个数据源的信息合并到一个模板化的幻灯片组。
本页概述了如何从外部来源提取数据,并将其插入现有的模板演示文稿。此概念与使用字处理程序和电子表格的邮件合并概念相似。
这种方法有以下几点好处:
方便设计者使用 Google 幻灯片编辑器调整演示文稿的设计。这比在应用中调整参数来设置呈现的幻灯片设计容易得多。
将内容与演示文稿分离是一个众所周知的设计原则,它具有许多优势。
基本食谱
以下示例展示了如何使用 Slides API 将数据合并到演示文稿中:
使用占位符内容根据您需要的显示效果创建演示文稿,以帮助您进行设计。
对于您将插入的每个内容元素,使用标记替换占位符内容。标记是具有唯一字符串的文本框或形状。请务必使用一般情况下不太可能出现的字符串。例如,
{{account-holder-name}}
可能是个不错的标记。在您的代码中,使用 Google Drive API 复制演示文稿。
在您的代码中,使用 Slides API 的
batchUpdate
方法以及一组replaceAllText
请求,以替换整个演示文稿中的所有文本。使用replaceAllShapesWithImage
请求替换整个演示文稿中的图像。
在创建包含标记的幻灯片组后,请务必创建一个副本,并使用 Slides API 处理此副本。切勿使用 Slides API 处理您的主要“模板”副本!
以下部分包含描述此流程部分操作的代码段。您也可以观看上面的视频来查看完整示例 (Python),这个示例组合了以下各部分中的多个概念。
合并文本
您可以使用 replaceAllText
请求将演示文稿中给定文本字符串的所有实例替换为新文本。对于合并,这比逐一查找和替换文本的每个实例更简单。此方法之所以成为最复杂的方法,其原因之一是页面元素 ID 很难预测,特别是在协作者优化和维护模板演示文稿时。
示例
此示例使用 Drive API 复制模板演示文稿,从而生成新的演示文稿实例。然后,它使用 Google 表格 API 从 Google 表格电子表格读取数据,最后使用 Slides API 更新新的演示文稿。
此示例从电子表格中指定范围的某行的 3 个单元格中提取数据。然后,它在演示文稿中出现字符串 {{customer-name}}
、{{case-description}}
或 {{total-portfolio}}
的任何位置使用该数据进行替换。
Apps 脚本
/** * Use the Sheets API to load data, one record per row. * @param {string} templatePresentationId * @param {string} dataSpreadsheetId * @returns {*[]} */ function textMerging(templatePresentationId, dataSpreadsheetId) { let responses = []; const dataRangeNotation = 'Customers!A2:M6'; try { let values = SpreadsheetApp.openById(dataSpreadsheetId).getRange(dataRangeNotation).getValues(); // For each record, create a new merged presentation. for (let i = 0; i < values.length; ++i) { const row = values[i]; const customerName = row[2]; // name in column 3 const caseDescription = row[5]; // case description in column 6 const totalPortfolio = row[11]; // total portfolio in column 12 // Duplicate the template presentation using the Drive API. const copyTitle = customerName + ' presentation'; let copyFile = { title: copyTitle, parents: [{id: 'root'}] }; copyFile = Drive.Files.copy(copyFile, templatePresentationId); const presentationCopyId = copyFile.id; // Create the text merge (replaceAllText) requests for this presentation. const requests = [{ replaceAllText: { containsText: { text: '{{customer-name}}', matchCase: true }, replaceText: customerName } }, { replaceAllText: { containsText: { text: '{{case-description}}', matchCase: true }, replaceText: caseDescription } }, { replaceAllText: { containsText: { text: '{{total-portfolio}}', matchCase: true }, replaceText: totalPortfolio + '' } }]; // Execute the requests for this presentation. const result = Slides.Presentations.batchUpdate({ requests: requests }, presentationCopyId); // Count the total number of replacements made. let numReplacements = 0; result.replies.forEach(function(reply) { numReplacements += reply.replaceAllText.occurrencesChanged; }); console.log('Created presentation for %s with ID: %s', customerName, presentationCopyId); console.log('Replaced %s text instances', numReplacements); } } catch (err) { // TODO (Developer) - Handle exception console.log('Failed with error: %s', err.error); } };
Go
// Use the Sheets API to load data, one record per row. dataRangeNotation := "Customers!A2:M6" sheetsResponse, _ := sheetsService.Spreadsheets.Values.Get(dataSpreadsheetId, dataRangeNotation).Do() values := sheetsResponse.Values // For each record, create a new merged presentation. for _, row := range values { customerName := row[2].(string) caseDescription := row[5].(string) totalPortfolio := row[11].(string) // Duplicate the template presentation using the Drive API. copyTitle := customerName + " presentation" file := drive.File{ Title: copyTitle, } presentationFile, _ := driveService.Files.Copy(templatePresentationId, &file).Do() presentationId := presentationFile.Id // Create the text merge (replaceAllText) requests for this presentation. requests := []*slides.Request{{ ReplaceAllText: &slides.ReplaceAllTextRequest{ ContainsText: &slides.SubstringMatchCriteria{ Text: "{{customer-name}}", MatchCase: true, }, ReplaceText: customerName, }, }, { ReplaceAllText: &slides.ReplaceAllTextRequest{ ContainsText: &slides.SubstringMatchCriteria{ Text: "{{case-description}}", MatchCase: true, }, ReplaceText: caseDescription, }, }, { ReplaceAllText: &slides.ReplaceAllTextRequest{ ContainsText: &slides.SubstringMatchCriteria{ Text: "{{total-portfolio}}", MatchCase: true, }, ReplaceText: totalPortfolio, }, }} // Execute the requests for this presentation. body := &slides.BatchUpdatePresentationRequest{ Requests: requests, } response, _ := slidesService.Presentations.BatchUpdate(presentationId, body).Do()
Java
import com.google.api.client.googleapis.json.GoogleJsonError; import com.google.api.client.googleapis.json.GoogleJsonResponseException; import com.google.api.client.http.HttpRequestInitializer; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.json.gson.GsonFactory; import com.google.api.services.drive.Drive; import com.google.api.services.drive.model.File; import com.google.api.services.sheets.v4.Sheets; import com.google.api.services.sheets.v4.model.ValueRange; import com.google.api.services.slides.v1.Slides; import com.google.api.services.slides.v1.SlidesScopes; import com.google.api.services.slides.v1.model.BatchUpdatePresentationRequest; import com.google.api.services.slides.v1.model.BatchUpdatePresentationResponse; import com.google.api.services.slides.v1.model.ReplaceAllTextRequest; import com.google.api.services.slides.v1.model.Request; import com.google.api.services.slides.v1.model.Response; import com.google.api.services.slides.v1.model.SubstringMatchCriteria; import com.google.auth.http.HttpCredentialsAdapter; import com.google.auth.oauth2.GoogleCredentials; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /* Class to demonstrate the use of Slides Text Merging API */ public class TextMerging { /** * Changes specified texts with data from spreadsheet. * * @param templatePresentationId - id of the presentation. * @param dataSpreadsheetId - id of the spreadsheet containing data. * @return merged presentation id * @throws IOException - if credentials file not found. */ public static List<BatchUpdatePresentationResponse> textMerging( String templatePresentationId, String dataSpreadsheetId) throws IOException { /* Load pre-authorized user credentials from the environment. TODO(developer) - See https://developers.google.com/identity for guides on implementing OAuth2 for your application. */ GoogleCredentials credentials = GoogleCredentials.getApplicationDefault() .createScoped(Arrays.asList(SlidesScopes.PRESENTATIONS, SlidesScopes.DRIVE, SlidesScopes.SPREADSHEETS)); HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter( credentials); // Create the slides API client Slides service = new Slides.Builder(new NetHttpTransport(), GsonFactory.getDefaultInstance(), requestInitializer) .setApplicationName("Slides samples") .build(); // Create the drive API client Drive driveService = new Drive.Builder(new NetHttpTransport(), GsonFactory.getDefaultInstance(), requestInitializer) .setApplicationName("Slides samples") .build(); // Create the sheets API client Sheets sheetsService = new Sheets.Builder(new NetHttpTransport(), GsonFactory.getDefaultInstance(), requestInitializer) .setApplicationName("Slides samples") .build(); List<BatchUpdatePresentationResponse> responses = new ArrayList<>(5); // Use the Sheets API to load data, one record per row. String dataRangeNotation = "Customers!A2:M6"; ValueRange sheetsResponse = sheetsService.spreadsheets().values() .get(dataSpreadsheetId, dataRangeNotation).execute(); List<List<Object>> values = sheetsResponse.getValues(); try { // For each record, create a new merged presentation. for (List<Object> row : values) { String customerName = row.get(2).toString(); // name in column 3 String caseDescription = row.get(5).toString(); // case description in column 6 String totalPortfolio = row.get(11).toString(); // total portfolio in column 12 // Duplicate the template presentation using the Drive API. String copyTitle = customerName + " presentation"; File content = new File().setName(copyTitle); File presentationFile = driveService.files().copy(templatePresentationId, content).execute(); String presentationId = presentationFile.getId(); // Create the text merge (replaceAllText) requests for this presentation. List<Request> requests = new ArrayList<>(); requests.add(new Request() .setReplaceAllText(new ReplaceAllTextRequest() .setContainsText(new SubstringMatchCriteria() .setText("{{customer-name}}") .setMatchCase(true)) .setReplaceText(customerName))); requests.add(new Request() .setReplaceAllText(new ReplaceAllTextRequest() .setContainsText(new SubstringMatchCriteria() .setText("{{case-description}}") .setMatchCase(true)) .setReplaceText(caseDescription))); requests.add(new Request() .setReplaceAllText(new ReplaceAllTextRequest() .setContainsText(new SubstringMatchCriteria() .setText("{{total-portfolio}}") .setMatchCase(true)) .setReplaceText(totalPortfolio))); // Execute the requests for this presentation. BatchUpdatePresentationRequest body = new BatchUpdatePresentationRequest().setRequests(requests); BatchUpdatePresentationResponse response = service.presentations().batchUpdate(presentationId, body).execute(); // Count total number of replacements made. int numReplacements = 0; for (Response resp : response.getReplies()) { numReplacements += resp.getReplaceAllText().getOccurrencesChanged(); } // Prints the merged presentation id and count of replacements. System.out.println("Created merged presentation for " + customerName + " with ID: " + presentationId); System.out.println("Replaced " + numReplacements + " text instances."); } } catch (NullPointerException ne) { System.out.println("Text not found to replace with image."); } catch (GoogleJsonResponseException e) { // TODO(developer) - handle error appropriately GoogleJsonError error = e.getDetails(); if (error.getCode() == 404) { System.out.printf("Presentation not found with id '%s'.\n", templatePresentationId); } else { throw e; } } return responses; } }
JavaScript
function textMerging(templatePresentationId, dataSpreadsheetId, callback) { // Use the Sheets API to load data, one record per row. const responses = []; const dataRangeNotation = 'Customers!A2:M6'; try { gapi.client.sheets.spreadsheets.values.get({ spreadsheetId: dataSpreadsheetId, range: dataRangeNotation, }).then((sheetsResponse) => { const values = sheetsResponse.result.values; // For each record, create a new merged presentation. for (let i = 0; i < values.length; ++i) { const row = values[i]; const customerName = row[2]; // name in column 3 const caseDescription = row[5]; // case description in column 6 const totalPortfolio = row[11]; // total portfolio in column 12 // Duplicate the template presentation using the Drive API. const copyTitle = customerName + ' presentation'; const request = { name: copyTitle, }; gapi.client.drive.files.copy({ fileId: templatePresentationId, requests: request, }).then((driveResponse) => { const presentationCopyId = driveResponse.result.id; // Create the text merge (replaceAllText) requests for this presentation. const requests = [{ replaceAllText: { containsText: { text: '{{customer-name}}', matchCase: true, }, replaceText: customerName, }, }, { replaceAllText: { containsText: { text: '{{case-description}}', matchCase: true, }, replaceText: caseDescription, }, }, { replaceAllText: { containsText: { text: '{{total-portfolio}}', matchCase: true, }, replaceText: totalPortfolio, }, }]; // Execute the requests for this presentation. gapi.client.slides.presentations.batchUpdate({ presentationId: presentationCopyId, requests: requests, }).then((batchUpdateResponse) => { const result = batchUpdateResponse.result; responses.push(result.replies); // Count the total number of replacements made. let numReplacements = 0; for (let i = 0; i < result.replies.length; ++i) { numReplacements += result.replies[i].replaceAllText.occurrencesChanged; } console.log(`Created presentation for ${customerName} with ID: ${presentationCopyId}`); console.log(`Replaced ${numReplacements} text instances`); if (responses.length === values.length) { // callback for the last value if (callback) callback(responses); } }); }); } }); } catch (err) { document.getElementById('content').innerText = err.message; return; } }
Node.js
/** * Adds data from a spreadsheet to a template presentation. * @param {string} templatePresentationId The template presentation ID. * @param {string} dataSpreadsheetId The data spreadsheet ID. */ async function textMerging(templatePresentationId, dataSpreadsheetId) { const {GoogleAuth} = require('google-auth-library'); const {google} = require('googleapis'); const auth = new GoogleAuth({ scopes: [ 'https://www.googleapis.com/auth/presentations', 'https://www.googleapis.com/auth/drive', 'https://www.googleapis.com/auth/spreadsheets', ], }); const slidesService = google.slides({version: 'v1', auth}); const sheetsService = google.sheets({version: 'v4', auth}); const driveService = google.drive({version: 'v2', auth}); // Use the Sheets API to load data, one record per row. const responses = []; const dataRangeNotation = 'A2:M6'; try { const sheetsResponse = await sheetsService.spreadsheets.values.get({ spreadsheetId: dataSpreadsheetId, range: dataRangeNotation, }); const values = sheetsResponse.data.values; // For each record, create a new merged presentation. for (let i = 0; i < values.length; ++i) { const row = values[i]; const customerName = row[2]; // name in column 3 const caseDescription = row[5]; // case description in column 6 const totalPortfolio = row[11]; // total portfolio in column 12 // Duplicate the template presentation using the Drive API. const copyTitle = customerName + ' presentation'; let requests = { name: copyTitle, }; const driveResponse = await driveService.files.copy({ fileId: templatePresentationId, requests, }); const presentationCopyId = driveResponse.data.id; // Create the text merge (replaceAllText) requests for this presentation. requests = [ { replaceAllText: { containsText: { text: '{{customer-name}}', matchCase: true, }, replaceText: customerName, }, }, { replaceAllText: { containsText: { text: '{{case-description}}', matchCase: true, }, replaceText: caseDescription, }, }, { replaceAllText: { containsText: { text: '{{total-portfolio}}', matchCase: true, }, replaceText: totalPortfolio, }, }, ]; // Execute the requests for this presentation. const batchUpdateResponse = await slidesService.presentations.batchUpdate( { presentationId: presentationCopyId, resource: { requests, }, }, ); const result = batchUpdateResponse.data; // Count the total number of replacements made. let numReplacements = 0; for (let i = 0; i < result.replies.length; ++i) { numReplacements += result.replies[i].replaceAllText.occurrencesChanged; } console.log( `Created presentation for ${customerName} with ID: ` + presentationCopyId, ); console.log(`Replaced ${numReplacements} text instances`); return result; } } catch (err) { // TODO (developer) - Handle exception throw err; } }
PHP
use Google\Client; use Google\Service\Drive; use Google\Service\Slides; use Google\Service\Slides\Request; function textMerging($templatePresentationId, $dataSpreadsheetId) { /* Load pre-authorized user credentials from the environment. TODO(developer) - See https://developers.google.com/identity for guides on implementing OAuth2 for your application. */ $client = new Google\Client(); $client->useApplicationDefaultCredentials(); $client->addScope(Google\Service\Drive::DRIVE); $slidesService = new Google_Service_Slides($client); $driveService = new Google_Service_Drive($client); $sheetsService = new Google_Service_Sheets($client); try { $responses = array(); // Use the Sheets API to load data, one record per row. $dataRangeNotation = 'Customers!A2:M6'; $sheetsResponse = $sheetsService->spreadsheets_values->get($dataSpreadsheetId, $dataRangeNotation); $values = $sheetsResponse['values']; // For each record, create a new merged presentation. foreach ($values as $row) { $customerName = $row[2]; // name in column 3 $caseDescription = $row[5]; // case description in column 6 $totalPortfolio = $row[11]; // total portfolio in column 12 // Duplicate the template presentation using the Drive API. $copy = new Google_Service_Drive_DriveFile(array( 'name' => $customerName . ' presentation' )); $driveResponse = $driveService->files->copy($templatePresentationId, $copy); $presentationCopyId = $driveResponse->id; // Create the text merge (replaceAllText) requests for this presentation. $requests = array(); $requests[] = new Google_Service_Slides_Request(array( 'replaceAllText' => array( 'containsText' => array( 'text' => '{{customer-name}}', 'matchCase' => true ), 'replaceText' => $customerName ) )); $requests[] = new Google_Service_Slides_Request(array( 'replaceAllText' => array( 'containsText' => array( 'text' => '{{case-description}}', 'matchCase' => true ), 'replaceText' => $caseDescription ) )); $requests[] = new Google_Service_Slides_Request(array( 'replaceAllText' => array( 'containsText' => array( 'text' => '{{total-portfolio}}', 'matchCase' => true ), 'replaceText' => $totalPortfolio ) )); // Execute the requests for this presentation. $batchUpdateRequest = new Google_Service_Slides_BatchUpdatePresentationRequest(array( 'requests' => $requests )); $response = $slidesService->presentations->batchUpdate($presentationCopyId, $batchUpdateRequest); $responses[] = $response; // Count the total number of replacements made. $numReplacements = 0; foreach ($response->getReplies() as $reply) { $numReplacements += $reply->getReplaceAllText()->getOccurrencesChanged(); } printf("Created presentation for %s with ID: %s\n", $customerName, $presentationCopyId); printf("Replaced %d text instances.\n", $numReplacements); } return $responses; } catch (Exception $e) { echo 'Message: ' . $e->getMessage(); } }
Python
import google.auth from googleapiclient.discovery import build from googleapiclient.errors import HttpError def text_merging(template_presentation_id, data_spreadsheet_id): """ Run Text merging the user has access to. Load pre-authorized user credentials from the environment. TODO(developer) - See https://developers.google.com/identity for guides on implementing OAuth2 for the application. """ creds, _ = google.auth.default() # pylint: disable=maybe-no-member try: service = build("slides", "v1", credentials=creds) sheets_service = build("sheets", "v4", credentials=creds) drive_service = build("drive", "v3", credentials=creds) # Use the Sheets API to load data, one record per row. data_range_notation = "Customers!A2:M6" sheets_response = ( sheets_service.spreadsheets() .values() .get(spreadsheetId=data_spreadsheet_id, range=data_range_notation) .execute() ) values = sheets_response.get("values") # For each record, create a new merged presentation. for row in values: customer_name = row[2] # name in column 3 case_description = row[5] # case description in column 6 total_portfolio = row[11] # total portfolio in column 12 # Duplicate the template presentation using the Drive API. copy_title = customer_name + " presentation" body = {"name": copy_title} drive_response = ( drive_service.files() .copy(fileId=template_presentation_id, body=body) .execute() ) presentation_copy_id = drive_response.get("id") # Create the text merge (replaceAllText) requests # for this presentation. requests = [ { "replaceAllText": { "containsText": { "text": "{{customer-name}}", "matchCase": True, }, "replaceText": customer_name, } }, { "replaceAllText": { "containsText": { "text": "{{case-description}}", "matchCase": True, }, "replaceText": case_description, } }, { "replaceAllText": { "containsText": { "text": "{{total-portfolio}}", "matchCase": True, }, "replaceText": total_portfolio, } }, ] # Execute the requests for this presentation. body = {"requests": requests} response = ( service.presentations() .batchUpdate(presentationId=presentation_copy_id, body=body) .execute() ) # Count the total number of replacements made. num_replacements = 0 for reply in response.get("replies"): if reply.get("occurrencesChanged") is not None: num_replacements += reply.get("replaceAllText").get( "occurrencesChanged" ) print( "Created presentation for " f"{customer_name} with ID: {presentation_copy_id}" ) print(f"Replaced {num_replacements} text instances") except HttpError as error: print(f"An error occurred: {error}") return error if __name__ == "__main__": # Put the template_presentation_id, data_spreadsheet_id # of slides text_merging( "10QnVUx1X2qHsL17WUidGpPh_SQhXYx40CgIxaKk8jU4", "17eqFZl_WK4WVixX8PjvjfLD77DraoFwMDXeiHB3dvuM", )
Ruby
# 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 脚本
/** * Duplicate the template presentation using the Drive API. * @param {string} templatePresentationId * @param {string} imageUrl * @param {string} customerName * @returns {*} */ function imageMerging(templatePresentationId, imageUrl, customerName) { const logoUrl = imageUrl; const customerGraphicUrl = imageUrl; const copyTitle = customerName + ' presentation'; let copyFile = { title: copyTitle, parents: [{id: 'root'}] }; try { copyFile = Drive.Files.copy(copyFile, templatePresentationId); const presentationCopyId = copyFile.id; // Create the image merge (replaceAllShapesWithImage) requests. const requests = [{ replaceAllShapesWithImage: { imageUrl: logoUrl, imageReplaceMethod: 'CENTER_INSIDE', containsText: { text: '{{company-logo}}', matchCase: true } } }, { replaceAllShapesWithImage: { imageUrl: customerGraphicUrl, imageReplaceMethod: 'CENTER_INSIDE', containsText: { text: '{{customer-graphic}}', matchCase: true } } }]; // Execute the requests for this presentation. let batchUpdateResponse = Slides.Presentations.batchUpdate({ requests: requests }, presentationCopyId); let numReplacements = 0; batchUpdateResponse.replies.forEach(function(reply) { numReplacements += reply.replaceAllShapesWithImage.occurrencesChanged; }); console.log('Created merged presentation with ID: %s', presentationCopyId); console.log('Replaced %s shapes with images.', numReplacements); return batchUpdateResponse; } catch (err) { // TODO (Developer) - Handle exception console.log('Failed with error: %s', err.error); } };
Go
// Duplicate the template presentation using the Drive API. copyTitle := customerName + " presentation" file := drive.File{ Title: copyTitle, } presentationFile, _ := driveService.Files.Copy(templatePresentationId, &file).Do() presentationId := presentationFile.Id // Create the image merge (replaceAllShapesWithImage) requests. requests := []*slides.Request{{ ReplaceAllShapesWithImage: &slides.ReplaceAllShapesWithImageRequest{ ImageUrl: logoURL, ReplaceMethod: "CENTER_INSIDE", ContainsText: &slides.SubstringMatchCriteria{ Text: "{{company-logo}}", MatchCase: true, }, }, }, { ReplaceAllShapesWithImage: &slides.ReplaceAllShapesWithImageRequest{ ImageUrl: customerGraphicURL, ReplaceMethod: "CENTER_INSIDE", ContainsText: &slides.SubstringMatchCriteria{ Text: "{{customer-graphic}}", MatchCase: true, }, }, }} // Execute the requests for this presentation. body := &slides.BatchUpdatePresentationRequest{Requests: requests} response, _ := slidesService.Presentations.BatchUpdate(presentationId, body).Do() // Count total number of replacements made. var numReplacements int64 = 0 for _, resp := range response.Replies { numReplacements += resp.ReplaceAllShapesWithImage.OccurrencesChanged } fmt.Printf("Created merged presentation with ID %s\n", presentationId) fmt.Printf("Replaced %d shapes instances with images.\n", numReplacements)
Java
import com.google.api.client.http.HttpRequestInitializer; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.json.gson.GsonFactory; import com.google.api.services.drive.Drive; import com.google.api.services.drive.model.File; import com.google.api.services.slides.v1.Slides; import com.google.api.services.slides.v1.SlidesScopes; import com.google.api.services.slides.v1.model.BatchUpdatePresentationRequest; import com.google.api.services.slides.v1.model.BatchUpdatePresentationResponse; import com.google.api.services.slides.v1.model.Request; import com.google.api.services.slides.v1.model.Response; import com.google.api.services.slides.v1.model.ReplaceAllShapesWithImageRequest; import com.google.api.services.slides.v1.model.SubstringMatchCriteria; import com.google.auth.http.HttpCredentialsAdapter; import com.google.auth.oauth2.GoogleCredentials; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /* Class to demonstrate the use of Slides Image Merging API */ public class ImageMerging { /** * Changes specified texts into images. * * @param templatePresentationId - id of the presentation. * @param imageUrl - Url of the image. * @param customerName - Name of the customer. * @return merged presentation id * @throws IOException - if credentials file not found. */ public static BatchUpdatePresentationResponse imageMerging(String templatePresentationId, String imageUrl, String customerName) throws IOException { /* Load pre-authorized user credentials from the environment. TODO(developer) - See https://developers.google.com/identity for guides on implementing OAuth2 for your application. */ GoogleCredentials credentials = GoogleCredentials.getApplicationDefault() .createScoped(Arrays.asList(SlidesScopes.PRESENTATIONS, SlidesScopes.DRIVE)); HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter( credentials); // Create the slides API client Slides service = new Slides.Builder(new NetHttpTransport(), GsonFactory.getDefaultInstance(), requestInitializer) .setApplicationName("Slides samples") .build(); // Create the drive API client Drive driveService = new Drive.Builder(new NetHttpTransport(), GsonFactory.getDefaultInstance(), requestInitializer) .setApplicationName("Slides samples") .build(); // Duplicate the template presentation using the Drive API. String copyTitle = customerName + " presentation"; File content = new File().setName(copyTitle); File presentationFile = driveService.files().copy(templatePresentationId, content).execute(); String presentationId = presentationFile.getId(); // Create the image merge (replaceAllShapesWithImage) requests. List<Request> requests = new ArrayList<>(); requests.add(new Request() .setReplaceAllShapesWithImage(new ReplaceAllShapesWithImageRequest() .setImageUrl(imageUrl) .setImageReplaceMethod("CENTER_INSIDE") .setContainsText(new SubstringMatchCriteria() .setText("{{company-logo}}") .setMatchCase(true)))); // Execute the requests. BatchUpdatePresentationRequest body = new BatchUpdatePresentationRequest().setRequests(requests); BatchUpdatePresentationResponse response = service.presentations().batchUpdate(presentationId, body).execute(); int numReplacements = 0; try { // Count total number of replacements made. for (Response resp : response.getReplies()) { numReplacements += resp.getReplaceAllShapesWithImage().getOccurrencesChanged(); } // Prints the merged presentation id and count of replacements. System.out.println("Created merged presentation with ID: " + presentationId); System.out.println("Replaced " + numReplacements + " shapes instances with images."); } catch (NullPointerException ne) { System.out.println("Text not found to replace with image."); } return response; } }
JavaScript
function imageMerging( templatePresentationId, imageUrl, customerName, callback, ) { const logoUrl = imageUrl; const customerGraphicUrl = imageUrl; // Duplicate the template presentation using the Drive API. const copyTitle = customerName + ' presentation'; try { gapi.client.drive.files .copy({ fileId: templatePresentationId, resource: { name: copyTitle, }, }) .then((driveResponse) => { const presentationCopyId = driveResponse.result.id; // Create the image merge (replaceAllShapesWithImage) requests. const requests = [ { replaceAllShapesWithImage: { imageUrl: logoUrl, replaceMethod: 'CENTER_INSIDE', containsText: { text: '{{company-logo}}', matchCase: true, }, }, }, { replaceAllShapesWithImage: { imageUrl: customerGraphicUrl, replaceMethod: 'CENTER_INSIDE', containsText: { text: '{{customer-graphic}}', matchCase: true, }, }, }, ]; // Execute the requests for this presentation. gapi.client.slides.presentations .batchUpdate({ presentationId: presentationCopyId, requests: requests, }) .then((batchUpdateResponse) => { let numReplacements = 0; for ( let i = 0; i < batchUpdateResponse.result.replies.length; ++i ) { numReplacements += batchUpdateResponse.result.replies[i].replaceAllShapesWithImage .occurrencesChanged; } console.log( `Created merged presentation with ID: ${presentationCopyId}`, ); console.log(`Replaced ${numReplacements} shapes with images.`); if (callback) callback(batchUpdateResponse.result); }); }); } catch (err) { document.getElementById('content').innerText = err.message; return; } }
Node.js
/** * Add an image to a template presentation. * @param {string} templatePresentationId The template presentation ID. * @param {string} imageUrl The image URL * @param {string} customerName A customer name used for the title */ async function imageMerging(templatePresentationId, imageUrl, customerName) { const {GoogleAuth} = require('google-auth-library'); const {google} = require('googleapis'); const auth = new GoogleAuth({ scopes: [ 'https://www.googleapis.com/auth/presentations', 'https://www.googleapis.com/auth/drive', ], }); const slidesService = google.slides({version: 'v1', auth}); const driveService = google.drive({version: 'v2', auth}); const logoUrl = imageUrl; const customerGraphicUrl = imageUrl; // Duplicate the template presentation using the Drive API. const copyTitle = customerName + ' presentation'; try { const driveResponse = await driveService.files.copy({ fileId: templatePresentationId, resource: { name: copyTitle, }, }); const presentationCopyId = driveResponse.data.id; // Create the image merge (replaceAllShapesWithImage) requests. const requests = [ { replaceAllShapesWithImage: { imageUrl: logoUrl, replaceMethod: 'CENTER_INSIDE', containsText: { text: '{{company-logo}}', matchCase: true, }, }, }, { replaceAllShapesWithImage: { imageUrl: customerGraphicUrl, replaceMethod: 'CENTER_INSIDE', containsText: { text: '{{customer-graphic}}', matchCase: true, }, }, }, ]; // Execute the requests for this presentation. const batchUpdateResponse = await slidesService.presentations.batchUpdate({ presentationId: presentationCopyId, resource: { requests, }, }); let numReplacements = 0; for (let i = 0; i < batchUpdateResponse.data.replies.length; ++i) { numReplacements += batchUpdateResponse.data.replies[i].replaceAllShapesWithImage .occurrencesChanged; } console.log(`Created merged presentation with ID: ${presentationCopyId}`); console.log(`Replaced ${numReplacements} shapes with images.`); return batchUpdateResponse.data; } catch (err) { // TODO (developer) - Handle exception throw err; } }
PHP
use Google\Client; use Google\Service\Drive; use Google\Service\Slides; use Google\Service\DriveFile; use Google\Service\Slides\Request; function imageMerging($templatePresentationId, $imageUrl, $customerName) { /* Load pre-authorized user credentials from the environment. TODO(developer) - See https://developers.google.com/identity for guides on implementing OAuth2 for your application. */ $client = new Google\Client(); $client->useApplicationDefaultCredentials(); $client->addScope(Google\Service\Drive::DRIVE); $slidesService = new Google_Service_Slides($client); $driveService = new Google_Service_Drive($client); // Duplicate the template presentation using the Drive API. $copy = new Google_Service_Drive_DriveFile([ 'name' => $customerName . ' presentation' ]); $driveResponse = $driveService->files->copy($templatePresentationId, $copy); $presentationCopyId = $driveResponse->id; // Create the image merge (replaceAllShapesWithImage) requests. $requests[] = new Google_Service_Slides_Request([ 'replaceAllShapesWithImage' => [ 'imageUrl' => $imageUrl, 'replaceMethod' => 'CENTER_INSIDE', 'containsText' => [ 'text' => '{{company-logo}}', 'matchCase' => true ] ] ]); $requests[] = new Google_Service_Slides_Request([ 'replaceAllShapesWithImage' => [ 'imageUrl' => $imageUrl, 'replaceMethod' => 'CENTER_INSIDE', 'containsText' => [ 'text' => '{{customer-graphic}}', 'matchCase' => true ] ] ]); // Execute the requests. $batchUpdateRequest = new Google_Service_Slides_BatchUpdatePresentationRequest([ 'requests' => $requests ]); $response = $slidesService->presentations->batchUpdate($presentationCopyId, $batchUpdateRequest); // Count the total number of replacements made. $numReplacements = 0; foreach ($response->getReplies() as $reply) { $numReplacements += $reply->getReplaceAllShapesWithImage()->getOccurrencesChanged(); } printf("Created presentation for %s with ID: %s\n", $customerName, $presentationCopyId); printf("Replaced %d shapes with images.\n", $numReplacements); return $response; }
Python
import google.auth from googleapiclient.discovery import build from googleapiclient.errors import HttpError def image_merging(template_presentation_id, image_url, customer_name): """image_merging require template_presentation_id, image_url and customer_name Load pre-authorized user credentials from the environment. TODO(developer) - See https://developers.google.com/identity for guides on implementing OAuth2 for the application. """ creds, _ = google.auth.default() # pylint: disable=maybe-no-member try: slides_service = build("slides", "v1", credentials=creds) drive_service = build("drive", "v3", credentials=creds) logo_url = image_url customer_graphic_url = image_url # Duplicate the template presentation using the Drive API. copy_title = customer_name + " presentation" drive_response = ( drive_service.files() .copy(fileId=template_presentation_id, body={"name": copy_title}) .execute() ) presentation_copy_id = drive_response.get("id") # Create the image merge (replaceAllShapesWithImage) requests. requests = [] requests.append( { "replaceAllShapesWithImage": { "imageUrl": logo_url, "replaceMethod": "CENTER_INSIDE", "containsText": { "text": "{{company-logo}}", "matchCase": True, }, } } ) requests.append( { "replaceAllShapesWithImage": { "imageUrl": customer_graphic_url, "replaceMethod": "CENTER_INSIDE", "containsText": { "text": "{{customer-graphic}}", "matchCase": True, }, } } ) # Execute the requests. body = {"requests": requests} response = ( slides_service.presentations() .batchUpdate(presentationId=presentation_copy_id, body=body) .execute() ) # Count the number of replacements made. num_replacements = 0 for reply in response.get("replies"): # add below line if reply.get("occurrencesChanged") is not None: # end tag num_replacements += reply.get("replaceAllShapesWithImage").get( "occurrencesChanged" ) print(f"Created merged presentation with ID:{presentation_copy_id}") print(f"Replaced {num_replacements} shapes with images") except HttpError as error: print(f"An error occurred: {error}") print("Images is not merged") return error return response if __name__ == "__main__": # Put the template_presentation_id, image_url and customer_name image_merging( "10QnVUx1X2qHsL17WUidGpPh_SQhXYx40CgIxaKk8jU4", "https://www.google.com/images/branding/" "googlelogo/2x/googlelogo_color_272x92dp.png", "Fake Customer", )
Ruby
# Duplicate the template presentation using the Drive API. copy_title = customer_name + ' presentation' body = Google::Apis::SlidesV1::Presentation.new body.title = copy_title drive_response = drive_service.copy_file(template_presentation_id, body) presentation_copy_id = drive_response.id # Create the image merge (replace_all_shapes_with_image) requests. requests = [] << { replace_all_shapes_with_image: { image_url: logo_url, replace_method: 'CENTER_INSIDE', contains_text: { text: '{{company-logo}}', match_case: true } } } << { replace_all_shapes_with_image: { image_url: customer_graphic_url, replace_method: 'CENTER_INSIDE', contains_text: { text: '{{customer-graphic}}', match_case: true } } } # Execute the requests. req = Google::Apis::SlidesV1::BatchUpdatePresentationRequest.new(requests: requests) response = slides_service.batch_update_presentation( presentation_copy_id, req ) # Count the number of replacements made. num_replacements = 0 response.replies.each do |reply| num_replacements += reply.replace_all_shapes_with_image.occurrences_changed end puts "Created presentation for #{customer_name} with ID: #{presentation_copy_id}" puts "Replaced #{num_replacements} shapes with images"
替换特定的文本框或图像实例
replaceAllText
和 replaceAllShapesWithImage
请求可用于替换整个演示文稿中的标记,但有时您只需要替换符合其他条件(例如位于特定幻灯片中)的元素。
在这些情况下,您必须检索您要替换的标记形状的 ID。对于文本替换,您删除这些形状中的现有文本,然后插入新文本(请参阅在指定的形状中编辑文本示例)。
图像替换则较为复杂。如需在图片中进行合并,您需要执行以下操作:
- 获取标记形状的 ID。
- 复制标记的大小和转换信息。
- 使用大小和转换信息将图像添加到页面。
- 删除标记形状。
根据下一部分所述,在保持图像纵横比的同时将图像缩放到所需大小时要格外谨慎。另请参阅以下示例:将形状标记替换为图片。
保持宽高比
使用 Slides API 创建图片时,合适的纵横比仅取决于图片大小,而不是大小和转换数据。您在 createImage
请求中提供的大小数据被视为图像所需的大小。此 API 根据所需的大小调整图像的纵横比,然后应用提供的转换。
将标记替换为图像时,您可通过按如下方式设置图像的大小和缩放比例来保持图像的纵横比:
- width:设为标记的
width
和scaleX
的乘积 - height:设为标记的
height
和scaleY
的乘积 - scale_x:设置为
1
- scale_y:设置为
1
这会导致 Slides API 根据标记的视觉大小(而不是其未缩放前的大小)确定适合图像的纵横比(请参阅将形状标记替换为图像)。将缩放比例参数设为 1
可防止图像被缩放两次。
此安排可确保图像的纵横比得以保持,并防止图像超出标记形状的大小。图片的中心点与标记形状相同。
管理模板
对于由应用定义和拥有的模板演示文稿,可使用表示此应用的专用账号创建模板。服务账号是一个非常好的选择,可避免限制共享的 Google Workspace 政策带来的复杂性。
通过模板创建演示文稿的实例时,请始终使用最终用户凭据。这使用户能够全面控制生成的演示文稿,并防止 Google Drive 中与每位用户限制有关的缩放比例问题。
如需使用服务账号创建模板,请使用应用凭据执行以下步骤:
- 在 Slides API 中使用 presentations.create 创建演示文稿。
- 更新权限,以允许演示文稿接收者使用 Drive API 中的 permissions.create 读取演示文稿。
- 更新权限以允许模板作者在 Drive API 中使用 permissions.create 向该文件写入内容。
- 根据需要修改模板。
如需创建演示文稿的实例,请使用用户凭据执行以下步骤:
- 在 Drive API 中使用 files.copy 创建模板的副本。
- 在 Slides API 中使用 presentation.batchUpdate 替换值。