Google Slides API의 한 가지 유용한 용도는 하나 이상의 데이터 소스의 정보를 템플릿 기반 슬라이드 덱에 병합하는 것입니다.
이 페이지에서는 외부 소스에서 데이터를 가져와서 기존 템플릿 프레젠테이션에 삽입하는 방법을 간략히 설명합니다. 이 개념은 워드 프로세서와 스프레드시트를 사용하는 메일 병합의 개념과 유사합니다.
이 접근 방식은 다음과 같은 여러 가지 이유로 유용합니다.
디자이너가 Google Slides 편집기를 사용하여 프레젠테이션 디자인을 쉽게 미세조정할 수 있습니다. 이 방법은 앱에서 매개변수를 조정하여 렌더링된 슬라이드 디자인을 설정하는 것보다 훨씬 쉽습니다.
프레젠테이션에서 콘텐츠를 분리하는 것은 많은 이점을 가진 잘 알려진 디자인 원칙입니다.
기본 레시피
다음은 Slides API를 사용하여 데이터를 프레젠테이션에 병합하는 방법의 예입니다.
디자인을 돕기 위해 자리표시자 콘텐츠를 사용하여 원하는 대로 프레젠테이션을 만듭니다.
삽입할 각 콘텐츠 요소에 대해 자리표시자 콘텐츠를 태그로 대체합니다. 태그는 고유한 문자열이 있는 텍스트 상자 또는 도형입니다. 일반적으로 발생할 가능성이 낮은 문자열을 사용해야 합니다. 예를 들어
{{account-holder-name}}
은 좋은 태그에 해당할 수 있습니다.코드에서 Google Drive API를 사용하여 프레젠테이션의 사본을 만듭니다.
코드에서 Slides API의
batchUpdate
메서드와replaceAllText
요청 집합을 사용하여 프레젠테이션 전체에서 모든 텍스트 대체를 실행합니다.replaceAllShapesWithImage
요청을 사용하여 프레젠테이션 전체에서 이미지 대체를 수행합니다.
태그가 포함된 덱을 만든 후에는 복사본을 만들고 Slides API를 사용하여 복사본을 조작해야 합니다. Slides API를 사용하여 기본 '템플릿' 사본을 조작하지 마세요.
다음 섹션에 이 과정의 일부를 보여주는 코드 스니펫이 포함되어 있습니다. 위 동영상을 통해 아래의 개별 섹션에서 설명하는 여러 가지 개념을 종합한 완전한 예시(Python)를 볼 수도 있습니다.
텍스트 병합
replaceAllText
요청을 사용하여 프레젠테이션에서 지정된 텍스트 문자열의 모든 인스턴스를 새 텍스트로 대체할 수 있습니다. 병합의 경우 이는 텍스트의 각 인스턴스를 개별적으로 찾아 바꾸는 것보다 간단합니다. 이 접근 방식이 가장 정교한 한 가지 이유는 특히 공동작업자가 템플릿 프레젠테이션을 미세조정하고 유지 관리할 때 페이지 요소 ID를 예측하기 어렵기 때문입니다.
예
이 예에서는 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()
자바
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; } }
자바스크립트
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)
자바
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; } }
자바스크립트
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를 사용하여 값을 바꿉니다.