אחת מהשימושים השימושיים של Google Slides API היא למזג מידע ממקור נתונים אחד או יותר למצגת שקפים לפי תבנית.
בדף הזה נסביר איך אפשר להעביר נתונים ממקור חיצוני ולהוסיף אותם למצגת קיימת בתבנית. הרעיון דומה למיזוג דואר באמצעות מעבד תמלילים וגיליון אלקטרוני.
יש כמה סיבות לכך שגישה כזו שימושית:
מעצבים יכולים לשפר בקלות את עיצוב המצגת באמצעות הכלי לעריכה ב-Google Slides. הדרך הזו קלה הרבה יותר מאשר לשנות את הפרמטרים באפליקציה כדי להגדיר את עיצוב השקופית שעבר רינדור.
הפרדת התוכן מהתצוגה היא עקרון עיצוב ידוע עם יתרונות רבים.
מתכון בסיסי
דוגמה לאופן שבו אפשר להשתמש ב-Slides API כדי למזג נתונים במצגת:
תוכלו ליצור את המצגת כפי שאתם רוצים שהיא תופיע, באמצעות תוכן placeholder שיעזור לכם בעיצוב.
לכל רכיב תוכן שתוסיפו, מחליפים את תוכן placeholder בתג. תגים הם תיבות טקסט או צורות עם מחרוזת ייחודית. חשוב להשתמש במחרוזות שלא סביר שיופיעו באופן טבעי. לדוגמה,
{{account-holder-name}}
יכול להיות תג טוב.בקוד, משתמשים ב-Google Drive API כדי ליצור עותק של המצגת.
בקוד, משתמשים ב-method
batchUpdate
של Slides API, עם קבוצה של בקשותreplaceAllText
, כדי לבצע את כל החלפות הטקסט במצגת. משתמשים בבקשותreplaceAllShapesWithImage
כדי לבצע החלפות של תמונות במהלך המצגת.
אחרי שיוצרים חבילה עם תגים, חשוב ליצור עותק ולהשתמש ב-Slides API כדי לבצע שינויים בעותק. אל תשתמשו ב-Slides API כדי לשנות את העותק הראשי של התבנית!
בקטעים הבאים מופיעים קטעי קוד שממחישים חלק מהתהליך הזה. אפשר גם לצפות בסרטון שלמעלה כדי לראות דוגמה מלאה (ב-Python) שמשלבת כמה מהמושגים מהקטעים הנפרדים שבהמשך.
מיזוג טקסט
אפשר להשתמש בבקשה replaceAllText
כדי להחליף בטקסט חדש את כל המופעים של מחרוזת טקסט מסוימת במצגת. כשממזגים, הפתרון הזה פשוט יותר מאשר למצוא כל מופע של טקסט בנפרד ולהחליף אותו. אחת הסיבות לכך שהגישה הזו היא המתוחכמת ביותר היא שקשה לחזות את מזהי רכיבי הדף, במיוחד כששותפי עריכה משפרים ומטפלים בפריסה של התבנית.
דוגמה
בדוגמה הזו נעשה שימוש ב-Drive API כדי להעתיק מצגת של תבנית, וכך ליצור מופע חדש של המצגת. לאחר מכן, הקוד משתמש ב-Google Sheets API כדי לקרוא נתונים מגיליון אלקטרוני ב-Sheets, ובסוף משתמש ב-Slides API כדי לעדכן את המצגת החדשה.
בדוגמה הזו, הנתונים נלקחים מ-3 תאים בשורה אחת של טווח בעל שם בגיליון האלקטרוני. לאחר מכן הנתונים האלה מוחלפים במצגת בכל מקום שבו מופיעות המחרוזות {{customer-name}}
, {{case-description}}
או {{total-portfolio}}
.
Apps Script
/** * Use the Sheets API to load data, one record per row. * @param {string} templatePresentationId * @param {string} dataSpreadsheetId * @returns {*[]} */ function textMerging(templatePresentationId, dataSpreadsheetId) { let responses = []; const dataRangeNotation = 'Customers!A2:M6'; try { let values = SpreadsheetApp.openById(dataSpreadsheetId).getRange(dataRangeNotation).getValues(); // For each record, create a new merged presentation. for (let i = 0; i < values.length; ++i) { const row = values[i]; const customerName = row[2]; // name in column 3 const caseDescription = row[5]; // case description in column 6 const totalPortfolio = row[11]; // total portfolio in column 12 // Duplicate the template presentation using the Drive API. const copyTitle = customerName + ' presentation'; let copyFile = { title: copyTitle, parents: [{id: 'root'}] }; copyFile = Drive.Files.copy(copyFile, templatePresentationId); const presentationCopyId = copyFile.id; // Create the text merge (replaceAllText) requests for this presentation. const requests = [{ replaceAllText: { containsText: { text: '{{customer-name}}', matchCase: true }, replaceText: customerName } }, { replaceAllText: { containsText: { text: '{{case-description}}', matchCase: true }, replaceText: caseDescription } }, { replaceAllText: { containsText: { text: '{{total-portfolio}}', matchCase: true }, replaceText: totalPortfolio + '' } }]; // Execute the requests for this presentation. const result = Slides.Presentations.batchUpdate({ requests: requests }, presentationCopyId); // Count the total number of replacements made. let numReplacements = 0; result.replies.forEach(function(reply) { numReplacements += reply.replaceAllText.occurrencesChanged; }); console.log('Created presentation for %s with ID: %s', customerName, presentationCopyId); console.log('Replaced %s text instances', numReplacements); } } catch (err) { // TODO (Developer) - Handle exception console.log('Failed with error: %s', err.error); } };
Go
// Use the Sheets API to load data, one record per row. dataRangeNotation := "Customers!A2:M6" sheetsResponse, _ := sheetsService.Spreadsheets.Values.Get(dataSpreadsheetId, dataRangeNotation).Do() values := sheetsResponse.Values // For each record, create a new merged presentation. for _, row := range values { customerName := row[2].(string) caseDescription := row[5].(string) totalPortfolio := row[11].(string) // Duplicate the template presentation using the Drive API. copyTitle := customerName + " presentation" file := drive.File{ Title: copyTitle, } presentationFile, _ := driveService.Files.Copy(templatePresentationId, &file).Do() presentationId := presentationFile.Id // Create the text merge (replaceAllText) requests for this presentation. requests := []*slides.Request{{ ReplaceAllText: &slides.ReplaceAllTextRequest{ ContainsText: &slides.SubstringMatchCriteria{ Text: "{{customer-name}}", MatchCase: true, }, ReplaceText: customerName, }, }, { ReplaceAllText: &slides.ReplaceAllTextRequest{ ContainsText: &slides.SubstringMatchCriteria{ Text: "{{case-description}}", MatchCase: true, }, ReplaceText: caseDescription, }, }, { ReplaceAllText: &slides.ReplaceAllTextRequest{ ContainsText: &slides.SubstringMatchCriteria{ Text: "{{total-portfolio}}", MatchCase: true, }, ReplaceText: totalPortfolio, }, }} // Execute the requests for this presentation. body := &slides.BatchUpdatePresentationRequest{ Requests: requests, } response, _ := slidesService.Presentations.BatchUpdate(presentationId, body).Do()
Java
import com.google.api.client.googleapis.json.GoogleJsonError; import com.google.api.client.googleapis.json.GoogleJsonResponseException; import com.google.api.client.http.HttpRequestInitializer; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.json.gson.GsonFactory; import com.google.api.services.drive.Drive; import com.google.api.services.drive.model.File; import com.google.api.services.sheets.v4.Sheets; import com.google.api.services.sheets.v4.model.ValueRange; import com.google.api.services.slides.v1.Slides; import com.google.api.services.slides.v1.SlidesScopes; import com.google.api.services.slides.v1.model.BatchUpdatePresentationRequest; import com.google.api.services.slides.v1.model.BatchUpdatePresentationResponse; import com.google.api.services.slides.v1.model.ReplaceAllTextRequest; import com.google.api.services.slides.v1.model.Request; import com.google.api.services.slides.v1.model.Response; import com.google.api.services.slides.v1.model.SubstringMatchCriteria; import com.google.auth.http.HttpCredentialsAdapter; import com.google.auth.oauth2.GoogleCredentials; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /* Class to demonstrate the use of Slides Text Merging API */ public class TextMerging { /** * Changes specified texts with data from spreadsheet. * * @param templatePresentationId - id of the presentation. * @param dataSpreadsheetId - id of the spreadsheet containing data. * @return merged presentation id * @throws IOException - if credentials file not found. */ public static List<BatchUpdatePresentationResponse> textMerging( String templatePresentationId, String dataSpreadsheetId) throws IOException { /* Load pre-authorized user credentials from the environment. TODO(developer) - See https://developers.google.com/identity for guides on implementing OAuth2 for your application. */ GoogleCredentials credentials = GoogleCredentials.getApplicationDefault() .createScoped(Arrays.asList(SlidesScopes.PRESENTATIONS, SlidesScopes.DRIVE, SlidesScopes.SPREADSHEETS)); HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter( credentials); // Create the slides API client Slides service = new Slides.Builder(new NetHttpTransport(), GsonFactory.getDefaultInstance(), requestInitializer) .setApplicationName("Slides samples") .build(); // Create the drive API client Drive driveService = new Drive.Builder(new NetHttpTransport(), GsonFactory.getDefaultInstance(), requestInitializer) .setApplicationName("Slides samples") .build(); // Create the sheets API client Sheets sheetsService = new Sheets.Builder(new NetHttpTransport(), GsonFactory.getDefaultInstance(), requestInitializer) .setApplicationName("Slides samples") .build(); List<BatchUpdatePresentationResponse> responses = new ArrayList<>(5); // Use the Sheets API to load data, one record per row. String dataRangeNotation = "Customers!A2:M6"; ValueRange sheetsResponse = sheetsService.spreadsheets().values() .get(dataSpreadsheetId, dataRangeNotation).execute(); List<List<Object>> values = sheetsResponse.getValues(); try { // For each record, create a new merged presentation. for (List<Object> row : values) { String customerName = row.get(2).toString(); // name in column 3 String caseDescription = row.get(5).toString(); // case description in column 6 String totalPortfolio = row.get(11).toString(); // total portfolio in column 12 // Duplicate the template presentation using the Drive API. String copyTitle = customerName + " presentation"; File content = new File().setName(copyTitle); File presentationFile = driveService.files().copy(templatePresentationId, content).execute(); String presentationId = presentationFile.getId(); // Create the text merge (replaceAllText) requests for this presentation. List<Request> requests = new ArrayList<>(); requests.add(new Request() .setReplaceAllText(new ReplaceAllTextRequest() .setContainsText(new SubstringMatchCriteria() .setText("{{customer-name}}") .setMatchCase(true)) .setReplaceText(customerName))); requests.add(new Request() .setReplaceAllText(new ReplaceAllTextRequest() .setContainsText(new SubstringMatchCriteria() .setText("{{case-description}}") .setMatchCase(true)) .setReplaceText(caseDescription))); requests.add(new Request() .setReplaceAllText(new ReplaceAllTextRequest() .setContainsText(new SubstringMatchCriteria() .setText("{{total-portfolio}}") .setMatchCase(true)) .setReplaceText(totalPortfolio))); // Execute the requests for this presentation. BatchUpdatePresentationRequest body = new BatchUpdatePresentationRequest().setRequests(requests); BatchUpdatePresentationResponse response = service.presentations().batchUpdate(presentationId, body).execute(); // Count total number of replacements made. int numReplacements = 0; for (Response resp : response.getReplies()) { numReplacements += resp.getReplaceAllText().getOccurrencesChanged(); } // Prints the merged presentation id and count of replacements. System.out.println("Created merged presentation for " + customerName + " with ID: " + presentationId); System.out.println("Replaced " + numReplacements + " text instances."); } } catch (NullPointerException ne) { System.out.println("Text not found to replace with image."); } catch (GoogleJsonResponseException e) { // TODO(developer) - handle error appropriately GoogleJsonError error = e.getDetails(); if (error.getCode() == 404) { System.out.printf("Presentation not found with id '%s'.\n", templatePresentationId); } else { throw e; } } return responses; } }
JavaScript
function textMerging(templatePresentationId, dataSpreadsheetId, callback) { // Use the Sheets API to load data, one record per row. const responses = []; const dataRangeNotation = 'Customers!A2:M6'; try { gapi.client.sheets.spreadsheets.values.get({ spreadsheetId: dataSpreadsheetId, range: dataRangeNotation, }).then((sheetsResponse) => { const values = sheetsResponse.result.values; // For each record, create a new merged presentation. for (let i = 0; i < values.length; ++i) { const row = values[i]; const customerName = row[2]; // name in column 3 const caseDescription = row[5]; // case description in column 6 const totalPortfolio = row[11]; // total portfolio in column 12 // Duplicate the template presentation using the Drive API. const copyTitle = customerName + ' presentation'; const request = { name: copyTitle, }; gapi.client.drive.files.copy({ fileId: templatePresentationId, requests: request, }).then((driveResponse) => { const presentationCopyId = driveResponse.result.id; // Create the text merge (replaceAllText) requests for this presentation. const requests = [{ replaceAllText: { containsText: { text: '{{customer-name}}', matchCase: true, }, replaceText: customerName, }, }, { replaceAllText: { containsText: { text: '{{case-description}}', matchCase: true, }, replaceText: caseDescription, }, }, { replaceAllText: { containsText: { text: '{{total-portfolio}}', matchCase: true, }, replaceText: totalPortfolio, }, }]; // Execute the requests for this presentation. gapi.client.slides.presentations.batchUpdate({ presentationId: presentationCopyId, requests: requests, }).then((batchUpdateResponse) => { const result = batchUpdateResponse.result; responses.push(result.replies); // Count the total number of replacements made. let numReplacements = 0; for (let i = 0; i < result.replies.length; ++i) { numReplacements += result.replies[i].replaceAllText.occurrencesChanged; } console.log(`Created presentation for ${customerName} with ID: ${presentationCopyId}`); console.log(`Replaced ${numReplacements} text instances`); if (responses.length === values.length) { // callback for the last value if (callback) callback(responses); } }); }); } }); } catch (err) { document.getElementById('content').innerText = err.message; return; } }
Node.js
/** * Adds data from a spreadsheet to a template presentation. * @param {string} templatePresentationId The template presentation ID. * @param {string} dataSpreadsheetId The data spreadsheet ID. */ async function textMerging(templatePresentationId, dataSpreadsheetId) { const {GoogleAuth} = require('google-auth-library'); const {google} = require('googleapis'); const auth = new GoogleAuth({ scopes: [ 'https://www.googleapis.com/auth/presentations', 'https://www.googleapis.com/auth/drive', 'https://www.googleapis.com/auth/spreadsheets', ], }); const slidesService = google.slides({version: 'v1', auth}); const sheetsService = google.sheets({version: 'v4', auth}); const driveService = google.drive({version: 'v2', auth}); // Use the Sheets API to load data, one record per row. const responses = []; const dataRangeNotation = 'A2:M6'; try { const sheetsResponse = await sheetsService.spreadsheets.values.get({ spreadsheetId: dataSpreadsheetId, range: dataRangeNotation, }); const values = sheetsResponse.data.values; // For each record, create a new merged presentation. for (let i = 0; i < values.length; ++i) { const row = values[i]; const customerName = row[2]; // name in column 3 const caseDescription = row[5]; // case description in column 6 const totalPortfolio = row[11]; // total portfolio in column 12 // Duplicate the template presentation using the Drive API. const copyTitle = customerName + ' presentation'; let requests = { name: copyTitle, }; const driveResponse = await driveService.files.copy({ fileId: templatePresentationId, requests, }); const presentationCopyId = driveResponse.data.id; // Create the text merge (replaceAllText) requests for this presentation. requests = [ { replaceAllText: { containsText: { text: '{{customer-name}}', matchCase: true, }, replaceText: customerName, }, }, { replaceAllText: { containsText: { text: '{{case-description}}', matchCase: true, }, replaceText: caseDescription, }, }, { replaceAllText: { containsText: { text: '{{total-portfolio}}', matchCase: true, }, replaceText: totalPortfolio, }, }, ]; // Execute the requests for this presentation. const batchUpdateResponse = await slidesService.presentations.batchUpdate( { presentationId: presentationCopyId, resource: { requests, }, }, ); const result = batchUpdateResponse.data; // Count the total number of replacements made. let numReplacements = 0; for (let i = 0; i < result.replies.length; ++i) { numReplacements += result.replies[i].replaceAllText.occurrencesChanged; } console.log( `Created presentation for ${customerName} with ID: ` + presentationCopyId, ); console.log(`Replaced ${numReplacements} text instances`); return result; } } catch (err) { // TODO (developer) - Handle exception throw err; } }
PHP
use Google\Client; use Google\Service\Drive; use Google\Service\Slides; use Google\Service\Slides\Request; function textMerging($templatePresentationId, $dataSpreadsheetId) { /* Load pre-authorized user credentials from the environment. TODO(developer) - See https://developers.google.com/identity for guides on implementing OAuth2 for your application. */ $client = new Google\Client(); $client->useApplicationDefaultCredentials(); $client->addScope(Google\Service\Drive::DRIVE); $slidesService = new Google_Service_Slides($client); $driveService = new Google_Service_Drive($client); $sheetsService = new Google_Service_Sheets($client); try { $responses = array(); // Use the Sheets API to load data, one record per row. $dataRangeNotation = 'Customers!A2:M6'; $sheetsResponse = $sheetsService->spreadsheets_values->get($dataSpreadsheetId, $dataRangeNotation); $values = $sheetsResponse['values']; // For each record, create a new merged presentation. foreach ($values as $row) { $customerName = $row[2]; // name in column 3 $caseDescription = $row[5]; // case description in column 6 $totalPortfolio = $row[11]; // total portfolio in column 12 // Duplicate the template presentation using the Drive API. $copy = new Google_Service_Drive_DriveFile(array( 'name' => $customerName . ' presentation' )); $driveResponse = $driveService->files->copy($templatePresentationId, $copy); $presentationCopyId = $driveResponse->id; // Create the text merge (replaceAllText) requests for this presentation. $requests = array(); $requests[] = new Google_Service_Slides_Request(array( 'replaceAllText' => array( 'containsText' => array( 'text' => '{{customer-name}}', 'matchCase' => true ), 'replaceText' => $customerName ) )); $requests[] = new Google_Service_Slides_Request(array( 'replaceAllText' => array( 'containsText' => array( 'text' => '{{case-description}}', 'matchCase' => true ), 'replaceText' => $caseDescription ) )); $requests[] = new Google_Service_Slides_Request(array( 'replaceAllText' => array( 'containsText' => array( 'text' => '{{total-portfolio}}', 'matchCase' => true ), 'replaceText' => $totalPortfolio ) )); // Execute the requests for this presentation. $batchUpdateRequest = new Google_Service_Slides_BatchUpdatePresentationRequest(array( 'requests' => $requests )); $response = $slidesService->presentations->batchUpdate($presentationCopyId, $batchUpdateRequest); $responses[] = $response; // Count the total number of replacements made. $numReplacements = 0; foreach ($response->getReplies() as $reply) { $numReplacements += $reply->getReplaceAllText()->getOccurrencesChanged(); } printf("Created presentation for %s with ID: %s\n", $customerName, $presentationCopyId); printf("Replaced %d text instances.\n", $numReplacements); } return $responses; } catch (Exception $e) { echo 'Message: ' . $e->getMessage(); } }
Python
import google.auth from googleapiclient.discovery import build from googleapiclient.errors import HttpError def text_merging(template_presentation_id, data_spreadsheet_id): """ Run Text merging the user has access to. Load pre-authorized user credentials from the environment. TODO(developer) - See https://developers.google.com/identity for guides on implementing OAuth2 for the application. """ creds, _ = google.auth.default() # pylint: disable=maybe-no-member try: service = build("slides", "v1", credentials=creds) sheets_service = build("sheets", "v4", credentials=creds) drive_service = build("drive", "v3", credentials=creds) # Use the Sheets API to load data, one record per row. data_range_notation = "Customers!A2:M6" sheets_response = ( sheets_service.spreadsheets() .values() .get(spreadsheetId=data_spreadsheet_id, range=data_range_notation) .execute() ) values = sheets_response.get("values") # For each record, create a new merged presentation. for row in values: customer_name = row[2] # name in column 3 case_description = row[5] # case description in column 6 total_portfolio = row[11] # total portfolio in column 12 # Duplicate the template presentation using the Drive API. copy_title = customer_name + " presentation" body = {"name": copy_title} drive_response = ( drive_service.files() .copy(fileId=template_presentation_id, body=body) .execute() ) presentation_copy_id = drive_response.get("id") # Create the text merge (replaceAllText) requests # for this presentation. requests = [ { "replaceAllText": { "containsText": { "text": "{{customer-name}}", "matchCase": True, }, "replaceText": customer_name, } }, { "replaceAllText": { "containsText": { "text": "{{case-description}}", "matchCase": True, }, "replaceText": case_description, } }, { "replaceAllText": { "containsText": { "text": "{{total-portfolio}}", "matchCase": True, }, "replaceText": total_portfolio, } }, ] # Execute the requests for this presentation. body = {"requests": requests} response = ( service.presentations() .batchUpdate(presentationId=presentation_copy_id, body=body) .execute() ) # Count the total number of replacements made. num_replacements = 0 for reply in response.get("replies"): if reply.get("occurrencesChanged") is not None: num_replacements += reply.get("replaceAllText").get( "occurrencesChanged" ) print( "Created presentation for " f"{customer_name} with ID: {presentation_copy_id}" ) print(f"Replaced {num_replacements} text instances") except HttpError as error: print(f"An error occurred: {error}") return error if __name__ == "__main__": # Put the template_presentation_id, data_spreadsheet_id # of slides text_merging( "10QnVUx1X2qHsL17WUidGpPh_SQhXYx40CgIxaKk8jU4", "17eqFZl_WK4WVixX8PjvjfLD77DraoFwMDXeiHB3dvuM", )
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 Script
/** * Duplicate the template presentation using the Drive API. * @param {string} templatePresentationId * @param {string} imageUrl * @param {string} customerName * @returns {*} */ function imageMerging(templatePresentationId, imageUrl, customerName) { const logoUrl = imageUrl; const customerGraphicUrl = imageUrl; const copyTitle = customerName + ' presentation'; let copyFile = { title: copyTitle, parents: [{id: 'root'}] }; try { copyFile = Drive.Files.copy(copyFile, templatePresentationId); const presentationCopyId = copyFile.id; // Create the image merge (replaceAllShapesWithImage) requests. const requests = [{ replaceAllShapesWithImage: { imageUrl: logoUrl, imageReplaceMethod: 'CENTER_INSIDE', containsText: { text: '{{company-logo}}', matchCase: true } } }, { replaceAllShapesWithImage: { imageUrl: customerGraphicUrl, imageReplaceMethod: 'CENTER_INSIDE', containsText: { text: '{{customer-graphic}}', matchCase: true } } }]; // Execute the requests for this presentation. let batchUpdateResponse = Slides.Presentations.batchUpdate({ requests: requests }, presentationCopyId); let numReplacements = 0; batchUpdateResponse.replies.forEach(function(reply) { numReplacements += reply.replaceAllShapesWithImage.occurrencesChanged; }); console.log('Created merged presentation with ID: %s', presentationCopyId); console.log('Replaced %s shapes with images.', numReplacements); return batchUpdateResponse; } catch (err) { // TODO (Developer) - Handle exception console.log('Failed with error: %s', err.error); } };
Go
// Duplicate the template presentation using the Drive API. copyTitle := customerName + " presentation" file := drive.File{ Title: copyTitle, } presentationFile, _ := driveService.Files.Copy(templatePresentationId, &file).Do() presentationId := presentationFile.Id // Create the image merge (replaceAllShapesWithImage) requests. requests := []*slides.Request{{ ReplaceAllShapesWithImage: &slides.ReplaceAllShapesWithImageRequest{ ImageUrl: logoURL, ReplaceMethod: "CENTER_INSIDE", ContainsText: &slides.SubstringMatchCriteria{ Text: "{{company-logo}}", MatchCase: true, }, }, }, { ReplaceAllShapesWithImage: &slides.ReplaceAllShapesWithImageRequest{ ImageUrl: customerGraphicURL, ReplaceMethod: "CENTER_INSIDE", ContainsText: &slides.SubstringMatchCriteria{ Text: "{{customer-graphic}}", MatchCase: true, }, }, }} // Execute the requests for this presentation. body := &slides.BatchUpdatePresentationRequest{Requests: requests} response, _ := slidesService.Presentations.BatchUpdate(presentationId, body).Do() // Count total number of replacements made. var numReplacements int64 = 0 for _, resp := range response.Replies { numReplacements += resp.ReplaceAllShapesWithImage.OccurrencesChanged } fmt.Printf("Created merged presentation with ID %s\n", presentationId) fmt.Printf("Replaced %d shapes instances with images.\n", numReplacements)
Java
import com.google.api.client.http.HttpRequestInitializer; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.json.gson.GsonFactory; import com.google.api.services.drive.Drive; import com.google.api.services.drive.model.File; import com.google.api.services.slides.v1.Slides; import com.google.api.services.slides.v1.SlidesScopes; import com.google.api.services.slides.v1.model.BatchUpdatePresentationRequest; import com.google.api.services.slides.v1.model.BatchUpdatePresentationResponse; import com.google.api.services.slides.v1.model.Request; import com.google.api.services.slides.v1.model.Response; import com.google.api.services.slides.v1.model.ReplaceAllShapesWithImageRequest; import com.google.api.services.slides.v1.model.SubstringMatchCriteria; import com.google.auth.http.HttpCredentialsAdapter; import com.google.auth.oauth2.GoogleCredentials; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /* Class to demonstrate the use of Slides Image Merging API */ public class ImageMerging { /** * Changes specified texts into images. * * @param templatePresentationId - id of the presentation. * @param imageUrl - Url of the image. * @param customerName - Name of the customer. * @return merged presentation id * @throws IOException - if credentials file not found. */ public static BatchUpdatePresentationResponse imageMerging(String templatePresentationId, String imageUrl, String customerName) throws IOException { /* Load pre-authorized user credentials from the environment. TODO(developer) - See https://developers.google.com/identity for guides on implementing OAuth2 for your application. */ GoogleCredentials credentials = GoogleCredentials.getApplicationDefault() .createScoped(Arrays.asList(SlidesScopes.PRESENTATIONS, SlidesScopes.DRIVE)); HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter( credentials); // Create the slides API client Slides service = new Slides.Builder(new NetHttpTransport(), GsonFactory.getDefaultInstance(), requestInitializer) .setApplicationName("Slides samples") .build(); // Create the drive API client Drive driveService = new Drive.Builder(new NetHttpTransport(), GsonFactory.getDefaultInstance(), requestInitializer) .setApplicationName("Slides samples") .build(); // Duplicate the template presentation using the Drive API. String copyTitle = customerName + " presentation"; File content = new File().setName(copyTitle); File presentationFile = driveService.files().copy(templatePresentationId, content).execute(); String presentationId = presentationFile.getId(); // Create the image merge (replaceAllShapesWithImage) requests. List<Request> requests = new ArrayList<>(); requests.add(new Request() .setReplaceAllShapesWithImage(new ReplaceAllShapesWithImageRequest() .setImageUrl(imageUrl) .setImageReplaceMethod("CENTER_INSIDE") .setContainsText(new SubstringMatchCriteria() .setText("{{company-logo}}") .setMatchCase(true)))); // Execute the requests. BatchUpdatePresentationRequest body = new BatchUpdatePresentationRequest().setRequests(requests); BatchUpdatePresentationResponse response = service.presentations().batchUpdate(presentationId, body).execute(); int numReplacements = 0; try { // Count total number of replacements made. for (Response resp : response.getReplies()) { numReplacements += resp.getReplaceAllShapesWithImage().getOccurrencesChanged(); } // Prints the merged presentation id and count of replacements. System.out.println("Created merged presentation with ID: " + presentationId); System.out.println("Replaced " + numReplacements + " shapes instances with images."); } catch (NullPointerException ne) { System.out.println("Text not found to replace with image."); } return response; } }
JavaScript
function imageMerging( templatePresentationId, imageUrl, customerName, callback, ) { const logoUrl = imageUrl; const customerGraphicUrl = imageUrl; // Duplicate the template presentation using the Drive API. const copyTitle = customerName + ' presentation'; try { gapi.client.drive.files .copy({ fileId: templatePresentationId, resource: { name: copyTitle, }, }) .then((driveResponse) => { const presentationCopyId = driveResponse.result.id; // Create the image merge (replaceAllShapesWithImage) requests. const requests = [ { replaceAllShapesWithImage: { imageUrl: logoUrl, replaceMethod: 'CENTER_INSIDE', containsText: { text: '{{company-logo}}', matchCase: true, }, }, }, { replaceAllShapesWithImage: { imageUrl: customerGraphicUrl, replaceMethod: 'CENTER_INSIDE', containsText: { text: '{{customer-graphic}}', matchCase: true, }, }, }, ]; // Execute the requests for this presentation. gapi.client.slides.presentations .batchUpdate({ presentationId: presentationCopyId, requests: requests, }) .then((batchUpdateResponse) => { let numReplacements = 0; for ( let i = 0; i < batchUpdateResponse.result.replies.length; ++i ) { numReplacements += batchUpdateResponse.result.replies[i].replaceAllShapesWithImage .occurrencesChanged; } console.log( `Created merged presentation with ID: ${presentationCopyId}`, ); console.log(`Replaced ${numReplacements} shapes with images.`); if (callback) callback(batchUpdateResponse.result); }); }); } catch (err) { document.getElementById('content').innerText = err.message; return; } }
Node.js
/** * Add an image to a template presentation. * @param {string} templatePresentationId The template presentation ID. * @param {string} imageUrl The image URL * @param {string} customerName A customer name used for the title */ async function imageMerging(templatePresentationId, imageUrl, customerName) { const {GoogleAuth} = require('google-auth-library'); const {google} = require('googleapis'); const auth = new GoogleAuth({ scopes: [ 'https://www.googleapis.com/auth/presentations', 'https://www.googleapis.com/auth/drive', ], }); const slidesService = google.slides({version: 'v1', auth}); const driveService = google.drive({version: 'v2', auth}); const logoUrl = imageUrl; const customerGraphicUrl = imageUrl; // Duplicate the template presentation using the Drive API. const copyTitle = customerName + ' presentation'; try { const driveResponse = await driveService.files.copy({ fileId: templatePresentationId, resource: { name: copyTitle, }, }); const presentationCopyId = driveResponse.data.id; // Create the image merge (replaceAllShapesWithImage) requests. const requests = [ { replaceAllShapesWithImage: { imageUrl: logoUrl, replaceMethod: 'CENTER_INSIDE', containsText: { text: '{{company-logo}}', matchCase: true, }, }, }, { replaceAllShapesWithImage: { imageUrl: customerGraphicUrl, replaceMethod: 'CENTER_INSIDE', containsText: { text: '{{customer-graphic}}', matchCase: true, }, }, }, ]; // Execute the requests for this presentation. const batchUpdateResponse = await slidesService.presentations.batchUpdate({ presentationId: presentationCopyId, resource: { requests, }, }); let numReplacements = 0; for (let i = 0; i < batchUpdateResponse.data.replies.length; ++i) { numReplacements += batchUpdateResponse.data.replies[i].replaceAllShapesWithImage .occurrencesChanged; } console.log(`Created merged presentation with ID: ${presentationCopyId}`); console.log(`Replaced ${numReplacements} shapes with images.`); return batchUpdateResponse.data; } catch (err) { // TODO (developer) - Handle exception throw err; } }
PHP
use Google\Client; use Google\Service\Drive; use Google\Service\Slides; use Google\Service\DriveFile; use Google\Service\Slides\Request; function imageMerging($templatePresentationId, $imageUrl, $customerName) { /* Load pre-authorized user credentials from the environment. TODO(developer) - See https://developers.google.com/identity for guides on implementing OAuth2 for your application. */ $client = new Google\Client(); $client->useApplicationDefaultCredentials(); $client->addScope(Google\Service\Drive::DRIVE); $slidesService = new Google_Service_Slides($client); $driveService = new Google_Service_Drive($client); // Duplicate the template presentation using the Drive API. $copy = new Google_Service_Drive_DriveFile([ 'name' => $customerName . ' presentation' ]); $driveResponse = $driveService->files->copy($templatePresentationId, $copy); $presentationCopyId = $driveResponse->id; // Create the image merge (replaceAllShapesWithImage) requests. $requests[] = new Google_Service_Slides_Request([ 'replaceAllShapesWithImage' => [ 'imageUrl' => $imageUrl, 'replaceMethod' => 'CENTER_INSIDE', 'containsText' => [ 'text' => '{{company-logo}}', 'matchCase' => true ] ] ]); $requests[] = new Google_Service_Slides_Request([ 'replaceAllShapesWithImage' => [ 'imageUrl' => $imageUrl, 'replaceMethod' => 'CENTER_INSIDE', 'containsText' => [ 'text' => '{{customer-graphic}}', 'matchCase' => true ] ] ]); // Execute the requests. $batchUpdateRequest = new Google_Service_Slides_BatchUpdatePresentationRequest([ 'requests' => $requests ]); $response = $slidesService->presentations->batchUpdate($presentationCopyId, $batchUpdateRequest); // Count the total number of replacements made. $numReplacements = 0; foreach ($response->getReplies() as $reply) { $numReplacements += $reply->getReplaceAllShapesWithImage()->getOccurrencesChanged(); } printf("Created presentation for %s with ID: %s\n", $customerName, $presentationCopyId); printf("Replaced %d shapes with images.\n", $numReplacements); return $response; }
Python
import google.auth from googleapiclient.discovery import build from googleapiclient.errors import HttpError def image_merging(template_presentation_id, image_url, customer_name): """image_merging require template_presentation_id, image_url and customer_name Load pre-authorized user credentials from the environment. TODO(developer) - See https://developers.google.com/identity for guides on implementing OAuth2 for the application. """ creds, _ = google.auth.default() # pylint: disable=maybe-no-member try: slides_service = build("slides", "v1", credentials=creds) drive_service = build("drive", "v3", credentials=creds) logo_url = image_url customer_graphic_url = image_url # Duplicate the template presentation using the Drive API. copy_title = customer_name + " presentation" drive_response = ( drive_service.files() .copy(fileId=template_presentation_id, body={"name": copy_title}) .execute() ) presentation_copy_id = drive_response.get("id") # Create the image merge (replaceAllShapesWithImage) requests. requests = [] requests.append( { "replaceAllShapesWithImage": { "imageUrl": logo_url, "replaceMethod": "CENTER_INSIDE", "containsText": { "text": "{{company-logo}}", "matchCase": True, }, } } ) requests.append( { "replaceAllShapesWithImage": { "imageUrl": customer_graphic_url, "replaceMethod": "CENTER_INSIDE", "containsText": { "text": "{{customer-graphic}}", "matchCase": True, }, } } ) # Execute the requests. body = {"requests": requests} response = ( slides_service.presentations() .batchUpdate(presentationId=presentation_copy_id, body=body) .execute() ) # Count the number of replacements made. num_replacements = 0 for reply in response.get("replies"): # add below line if reply.get("occurrencesChanged") is not None: # end tag num_replacements += reply.get("replaceAllShapesWithImage").get( "occurrencesChanged" ) print(f"Created merged presentation with ID:{presentation_copy_id}") print(f"Replaced {num_replacements} shapes with images") except HttpError as error: print(f"An error occurred: {error}") print("Images is not merged") return error return response if __name__ == "__main__": # Put the template_presentation_id, image_url and customer_name image_merging( "10QnVUx1X2qHsL17WUidGpPh_SQhXYx40CgIxaKk8jU4", "https://www.google.com/images/branding/" "googlelogo/2x/googlelogo_color_272x92dp.png", "Fake Customer", )
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
הן שימושיות להחלפת תגים במהלך מצגת, אבל לפעמים צריך להחליף רכיבים רק לפי קריטריונים אחרים, כמו מיקום בשקופית ספציפית.
במקרים כאלה, צריך לאחזר את המזהים של צורות התגים שרוצים להחליף. כדי להחליף טקסט, מוחקים את הטקסט הקיים בצורות האלה ומזינים את הטקסט החדש (ראו את הדוגמה עריכת טקסט בצורה מסוימת).
החלפת תמונות היא תהליך מורכב יותר. כדי למזג תמונה, צריך:
- מוצאים את המזהה של צורת התג.
- מעתיקים את הגודל ומבצעים טרנספורמציה של המידע מהתג.
- מוסיפים את התמונה לדף לפי פרטי הגודל והטרנספורמציה.
- מוחקים את צורת התג.
כדי לשמור על יחס הגובה-רוחב של התמונה בזמן שמגדילים אותה לגודל הרצוי, יכול להיות שתצטרכו להפעיל שיקול דעת, כפי שמתואר בקטע הבא. כדאי לעיין גם בדוגמה הבאה: החלפת תג של צורה בתמונה.
שמירה על יחס הגובה-רוחב
כשאתם יוצרים תמונות באמצעות Slides API, התאמות הגובה-רוחב מבוססות רק על גודל התמונה, ולא על הנתונים של הגודל והטרנספורמציה. נתוני הגודל שסיפקתם בבקשה createImage
נחשבים לגודל הרצוי של התמונה. ה-API מתאים את יחס הגובה-רוחב של התמונה לגודל הרצוי, ולאחר מכן מחיל את הטרנספורמציה שצוינה.
כשמחליפים תג בתמונה, צריך לשמור על יחס הגובה-רוחב של התמונה על ידי הגדרת הגודל והשינוי של התמונה באופן הבא:
- width: מוגדר כמכפלה של
width
ו-scaleX
בתג - height: מוגדר כמכפלה של
height
ו-scaleY
בתג - scale_x: מוגדר לערך
1
- scale_y: מוגדר לערך
1
כך המערכת של Slides API תתאים את התמונה לפי הגודל החזותי של התג, ולא לפי הגודל שלו ללא שינוי קנה המידה (ראו החלפת תג צורה בתמונה).
הגדרת פרמטרים של שינוי קנה מידה ל-1
מונעת שינוי קנה המידה של התמונה פעמיים.
כך אפשר לוודא שיחס הגובה-רוחב של התמונה נשמר, ומונעים מהתמונה לחרוג מהגודל של צורת התג. מרכז התמונה זהה למרכז של צורת התג.
ניהול התבניות
כדי ליצור מצגות של תבניות שהאפליקציה מגדירה ובבעלותה, צריך ליצור את התבנית באמצעות חשבון ייעודי שמייצג את האפליקציה. חשבונות שירות הם בחירה טובה, והם מונעים סיבוכים עם כללי המדיניות של Google Workspace שמגבילים את השיתוף.
כשאתם יוצרים מופעים של מצגות מתבניות, תמיד צריך להשתמש בפרטי הכניסה של משתמשי הקצה. כך המשתמשים יכולים לשלוט באופן מלא במצגת שנוצרת, ומונעים בעיות שקשורות להתאמה לעומס (scaling) בגלל המגבלות לכל משתמש ב-Google Drive.
כדי ליצור תבנית באמצעות חשבון שירות, מבצעים את השלבים הבאים עם פרטי הכניסה של האפליקציה:
- יוצרים מצגת באמצעות presentations.create ב-Slides API.
- מעדכנים את ההרשאות כדי לאפשר לנמעני המצגת לקרוא אותה באמצעות permissions.create ב-Drive API.
- מעדכנים את ההרשאות כדי לאפשר לכותבי התבנית לכתוב בה באמצעות permissions.create ב-Drive API.
- עורכים את התבנית לפי הצורך.
כדי ליצור מכונה של המצגת, מבצעים את השלבים הבאים עם פרטי הכניסה של המשתמש:
- יוצרים עותק של התבנית באמצעות files.copy ב-Drive API.
- מחליפים ערכים באמצעות presentation.batchUpdate ב-Slides API.