Introduction
This is the fourth entry highlighting primary use cases of the Google Slides API with Python; check back in the archives to access the first three. Today, we're focused on some of the basics, like adding text to slides. We'll also cover adding shapes, and as a bonus, adding text into shapes!Using the Google Slides API
The demo script requires creating a new slide deck (and adding a new slide) so you need the read-write scope for Slides:'https://www.googleapis.com/auth/presentations'
— Read-write access to Slides and Slides presentation properties
SLIDES
variable.Create new presentation & get its objects' IDs
A new slide deck can be created withSLIDES.presentations().create()
—or alternatively with the Google Drive API which we won't do here. From the API response, we save the new deck's ID along with the IDs of the title and subtitle textboxes on the default title slide:rsp = SLIDES.presentations().create( body={'title': 'Adding text formatting DEMO'}).execute() deckID = rsp['presentationId'] titleSlide = rsp['slides'][0] # title slide object IDs titleID = titleSlide['pageElements'][0]['objectId'] subtitleID = titleSlide['pageElements'][1]['objectId']The title slide only has two elements on it, the title and subtitle textboxes, returned in that order, hence why we grab them at indexes 0 and 1 respectively.
Generating our own unique object IDs
In the next steps, we generate our own unique object IDs. We'll first explain what those objects are followed by why you'd want to create your own object IDs rather than letting the API create default IDs for the same objects.As we've done in previous posts on the Slides API, we create one new slide with the "main point" layout. It has one notable object, a "large-ish" textbox and nothing else. We'll create IDs for the slide itself and another for its textbox. Next, we'll (use the API to) "draw" 3 shapes on this slide, so we'll create IDs for each of those. That's 5 (document-unique) IDs total. Now let's discuss why you'd "roll your own" IDs.
Why and how to generate our own IDs
It's advantageous for all developers to minimize the overall number of calls to Google APIs. While most of services provided through the APIs are free, they'll have some quota to prevent abuse (Slides API quotas page FYI). So how does creating our own IDs help reduce API calls?Passing in object IDs is optional for "create" calls. Providing your own ID lets you create an object and modify it using additional requests within the same API call to
SLIDES.presentations().batchUpdate()
. If you don't provide your own object IDs, the API will generate a unique one for you.Unfortunately, this means that instead of one API call, you'll need one to create the object, likely another to get that object to determine its ID, and yet another to update that object using the ID you just fetched. Separate API calls to create, get, and update means (at least) 3x more than if you provided your own IDs (where you can do create & update with a single API call; no get necessary).
Here are a few things to know when rolling your own IDs:
- IDs must start with an alphanumeric character or underscore (matches regex
[a-zA-Z0-9_]
) - Any remaining characters can also include a hyphen or colon (matches regex
[a-zA-Z0-9_-:]
) - The length of the ID must conform to: 5 ≤
len(ID)
≤ 50. - Object IDs must be unique across all objects in a presentation.
You'll somehow need to ensure your IDs are unique or use UUIDs (universally unique identifiers) for which most languages have libraries for. Examples: Java developers can use
java.util.UUID.randomUUID().toString()
while Python users can import the uuid
module plus any extra work to get UUID string values:import uuid gen_uuid = lambda : str(uuid.uuid4()) # get random UUID stringFinally, be aware that if an object is modified in the UI, its ID may change. For more information, review the "Working with object IDs" section in the Slides API Overview page.
Back to sample app
All that said, let's go back to the code and generate those 5 random object IDs we promised earlier:mpSlideID = gen_uuid() # mainpoint IDs mpTextboxID = gen_uuid() smileID = gen_uuid() # shape IDs str24ID = gen_uuid() arwbxID = gen_uuid()With that, we're ready to create the requests array (
reqs
) to send to the API.Create "main point" slide
The first request creates the "main point" slide...reqs = [ {'createSlide': 'objectId': mpSlideID, 'slideLayoutReference': {'predefinedLayout': 'MAIN_POINT'}, 'placeholderIdMappings': [{ 'objectId': mpTextboxID, 'layoutPlaceholder': {'type': 'TITLE', 'index': 0} }], }},...where...
objectID
—our generated ID we're assigning to the newly-created slideslideLayoutReference
—new slide layout type ("main point")placeholderIdMappings
—array of IDs ([inner]objectId
) for each of the page elements and which object (layoutPlaceholder
) they should map or be assigned to
placeholderIdMappings
only has one element.Add title slide and main point textbox text
The next requests fill in the title & subtitle in the default title slide and also the textbox on the main point slide.{'insertText': {'objectId': titleID, 'text': 'Adding text and shapes'}}, {'insertText': {'objectId': subtitleID, 'text': 'via the Google Slides API'}}, {'insertText': {'objectId': mpTextboxID, 'text': 'text & shapes'}},The first pair use IDs that were generated by the Slides API when the presentation was created while the main point textbox ID was generated by us.
Create three shapes
Above, we created IDs for three shapes, a "smiley face," a 24-point star, and a double arrow box (smileID
, str24ID
, arwbxID
). The request for the first looks like this:{'createShape': { 'objectId': smileID, 'shapeType': 'SMILEY_FACE', 'elementProperties': { "pageObjectId": mpSlideID, 'size': { 'height': {'magnitude': 3000000, 'unit': 'EMU'}, 'width': {'magnitude': 3000000, 'unit': 'EMU'} }, 'transform': { 'unit': 'EMU', 'scaleX': 1.3449, 'scaleY': 1.3031, 'translateX': 4671925, 'translateY': 450150, }, }, }}The JSON for the other two shapes are similar, with differences being: the object ID, the
shapeType
, and the transform. You can see the corresponding requests for the other shapes in the full source code at the bottom of this post, so we won't display them here as the descriptions will be nearly identical.Size & transform for slide objects
When placing or manipulating objects on slides, key element properties you must provide are the sizes and transforms. These are components you must either use some math to create or derive from pre-existing objects. Resizing, rotating, and similar operations require some basic knowledge of matrix math. Take a look at the Page Elements page in the official docs as well as the Transforms concept guide for more details.Deriving from pre-existing objects: if you're short on time, don't want to deal with the math, or perhaps thinking something like, "Geez, I just want to draw a smiley face on a slide." One common pattern then, is to bring up the Slides UI, create a blank slide & place your image or draw your shape the way you want, with the size you want, & putting it exactly where you want. For example:
Once you have that desired shape (and size and location), you can use the API (either presentations.get or presentations.pages.get) to read that object's size and transform then drop both of those into your application so the API creates a new shape in the exact way, mirroring what you created in the UI. For the smiley face above, the JSON payload we got back from one of the "get" calls could look something like:
createShape
request, you'll see we used those exact values. Note: because the 3 shapes are all in different locations and sizes, expect the corresponding values for each shape to be different.Bonus: adding text to shapes
Now that you know how to add text and shapes, it's only fitting that we show you how to add text into shapes. The good news is that the technique is no different than adding text to textboxes or even tables. So with the shape IDs, our final set of requests along with thebatchUpdate()
call looks like this:
{'insertText': {'objectId': smileID, 'text': 'Put the nose somewhere here!'}}, {'insertText': {'objectId': str24ID, 'text': 'Count 24 points on this star!'}}, {'insertText': {'objectId': arwbxID, 'text': "An uber bizarre arrow box!"}}, ] # end of 'reqs' SLIDES.presentations().batchUpdate(body={'requests': reqs}, presentationId=deckID).execute()
Conclusion
If you run the script, you should get output that looks something like this, with eachprint()
representing each API call:$ python3 slides_shapes_text.py ** Create new slide deck & set up object IDs ** Create "main point" slide, add text & interesting shapes DONEWhen the script has completed, you should have a new presentation with a title slide and a main point slide with shapes which should look something like this:
Below is the entire script for your convenience which runs on both Python 2 and Python 3 (unmodified!)—by using, copying, and/or modifying this code or any other piece of source from this blog, you implicitly agree to its Apache2 license:
from __future__ import print_function
import uuid
from apiclient import discovery
from httplib2 import Http
from oauth2client import file, client, tools
gen_uuid = lambda : str(uuid.uuid4()) # get random UUID string
SCOPES = 'https://www.googleapis.com/auth/presentations',
store = file.Storage('storage.json')
creds = store.get()
if not creds or creds.invalid:
flow = client.flow_from_clientsecrets('client_secret.json', SCOPES)
creds = tools.run_flow(flow, store)
SLIDES = discovery.build('slides', 'v1', http=creds.authorize(Http()))
print('** Create new slide deck & set up object IDs')
rsp = SLIDES.presentations().create(
body={'title': 'Adding text & shapes DEMO'}).execute()
deckID = rsp['presentationId']
titleSlide = rsp['slides'][0] # title slide object IDs
titleID = titleSlide['pageElements'][0]['objectId']
subtitleID = titleSlide['pageElements'][1]['objectId']
mpSlideID = gen_uuid() # mainpoint IDs
mpTextboxID = gen_uuid()
smileID = gen_uuid() # shape IDs
str24ID = gen_uuid()
arwbxID = gen_uuid()
print('** Create "main point" slide, add text & interesting shapes')
reqs = [
# create new "main point" layout slide, giving slide & textbox IDs
{'createSlide': {
'objectId': mpSlideID,
'slideLayoutReference': {'predefinedLayout': 'MAIN_POINT'},
'placeholderIdMappings': [{
'objectId': mpTextboxID,
'layoutPlaceholder': {'type': 'TITLE', 'index': 0}
}],
}},
# add title & subtitle to title slide; add text to main point slide textbox
{'insertText': {'objectId': titleID, 'text': 'Adding text and shapes'}},
{'insertText': {'objectId': subtitleID, 'text': 'via the Google Slides API'}},
{'insertText': {'objectId': mpTextboxID, 'text': 'text & shapes'}},
# create smiley face
{'createShape': {
'objectId': smileID,
'shapeType': 'SMILEY_FACE',
'elementProperties': {
"pageObjectId": mpSlideID,
'size': {
'height': {'magnitude': 3000000, 'unit': 'EMU'},
'width': {'magnitude': 3000000, 'unit': 'EMU'}
},
'transform': {
'unit': 'EMU', 'scaleX': 1.3449, 'scaleY': 1.3031,
'translateX': 4671925, 'translateY': 450150,
},
},
}},
# create 24-point star
{'createShape': {
'objectId': str24ID,
'shapeType': 'STAR_24',
'elementProperties': {
"pageObjectId": mpSlideID,
'size': {
'height': {'magnitude': 3000000, 'unit': 'EMU'},
'width': {'magnitude': 3000000, 'unit': 'EMU'}
},
'transform': {
'unit': 'EMU', 'scaleX': 0.7079, 'scaleY': 0.6204,
'translateX': 2036175, 'translateY': 237350,
},
},
}},
# create double left & right arrow w/textbox
{'createShape': {
'objectId': arwbxID,
'shapeType': 'LEFT_RIGHT_ARROW_CALLOUT',
'elementProperties': {
"pageObjectId": mpSlideID,
'size': {
'height': {'magnitude': 3000000, 'unit': 'EMU'},
'width': {'magnitude': 3000000, 'unit': 'EMU'}
},
'transform': {
'unit': 'EMU', 'scaleX': 1.1451, 'scaleY': 0.4539,
'translateX': 1036825, 'translateY': 3235375,
},
},
}},
# add text to all 3 shapes
{'insertText': {'objectId': smileID, 'text': 'Put the nose somewhere here!'}},
{'insertText': {'objectId': str24ID, 'text': 'Count 24 points on this star!'}},
{'insertText': {'objectId': arwbxID, 'text': "An uber bizarre arrow box!"}},
]
SLIDES.presentations().batchUpdate(body={'requests': reqs},
presentationId=deckID).execute()
print('DONE')
As with our other code samples, you can now customize it to learn more about the API, integrate into other apps for your own needs, for a mobile frontend, sysadmin script, or a server-side backend!Code challenge
Create a 2x3 or 3x4 table on a slide and add text to each "cell." This should be a fairly easy exercise, especially if you look at the Table Operations documentation. HINT: you'll be usinginsertText
with just an extra field, cellLocation
. EXTRA CREDIT: generalize your solution so that you're grabbing cells from a Google Sheet and "import" them into a table on a slide. HINT: look for the earlier post where we describe how to create slides from spreadsheet data.
Awesome!
ReplyDeleteHow do you use presentations.pages.get?
ReplyDeleteAll of the methods have documentation you can review to learn how they work. For the one you brought up, the docs page is https://developers.google.com/slides/reference/rest/v1/presentations.pages/get
Delete