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를 사용하여 값을 바꿉니다.