Google Slides - central master template - google-apps-script

Does anyone know of a way to have a central master template for google slide presentations that automatically cascades changes down to presentations using it ?
If not automatic then maybe there is something that can be done with google apps script to pull any changes to the master template down to the associated presentations ?
Here is a simple example of what I am trying to do:
Create master template/theme (M1) with layout (L1) with two placeholders and a company logo
Create new presentation (P1) importing theme M1 above using Layout L1
Amend master theme M1 Layout A with new company logo or new placeholder
How do i get this change to propagate to P1 without manually importing the template/theme again ? It would be ideal if P1 could subscribe to changes in M1 but i can't see any option for this so was wondering if I could script something ?
Thanks in advance
Greg

This is not possible in Apps Script right now
There is a feature request for this in the Issue Tracker, go give it a ☆!
https://issuetracker.google.com/issues/129457735
Maybe go and explain your use case for it too.
Possible avenue for workaround
The best workaround I can think of is something along the lines of this script:
function copyStyling() {
// This is a standalone script
let masterID = "1107dQEIAbZ8ipBi0wvU6cdy4OV7N2hURT5fjgOwm_vY";
let childID = "1XvGARRBzXofsjrFJkl8SCmt3tQJ2nkw1n9MG3tr9fhU";
// Master Slide Variables
let masterPresentation = SlidesApp.openById(masterID);
let masterSlide = masterPresentation.getSlides()[0];
let masterElements = masterSlide.getPageElements();
// Get style elements
let masterBackground = masterSlide.getBackground();
let masterSolidFill = masterBackground.getSolidFill().getColor();
// etc
// ...
// Child Slide Variables
let childPresentation = SlidesApp.openById(childID);
let childSlide = childPresentation.getSlides()[0];
let childElements = childSlide.getPageElements();
// Updating the stylings for the page
let childBackground = childSlide.getBackground();
childBackground.setSolidFill(masterSolidFill);
// etc
// ...
// Updating the stylings for each element on the page
masterElements.forEach((element, i) => {
childElements[i].setLeft(element.getLeft());
childElements[i].setTop(element.getTop());
// etc
// ...
});
}
This script works if both Master and Child presentations use the same theme (i.e. the master style sheets)
It works by having a single slide in a "Master presentation" which you modify and the Child presentations also have only a single slide.
It gets style info. This script gets the background of the slide (if its a solid fill) and the top left position of each element.
It then updates the child with this information.
It really depends on how many changes are going to happen to the child presentations. If no elements are going to change, and only limited style characteristics are going to change, then it shouldn't take too long to get a working script together. It would just involve going through the documentation and picking out the attributes you want to update.
If the number of elements are going to change, their positions going to be rearranged, with very different content from the placeholders, then it can get considerably more complex. Then it becomes a function of how many hours you can invest into it! Though hopefully this serves as a good starting point for that.
Ideally to this script would be added the width and height of each element to go along with the top and left position, their rotation, transformation, font, font color, font font style, direction, and minimal support for shapes. With these things I believe you could have quite a powerful tool.
Reference
Apps Script Slides Service

Related

Multiple mxGraph on the same page

I have a tabbed web page and I would like to place two different instances of mxGraph, one on the 1st tab and the other one on the 2nd tab:
var editor1 = new mxEditor();
var editor2 = new mxEditor();
I would like to configure each instance in its own way, so the two editors should have different behaviours and properties. Unfortunately, mxGraph is based on lots of static constructs:
mxConstants.EDGE_SELECTION_COLOR = '#a8d6e1';
mxGraphView.prototype.updateFloatingTerminalPoint = function (...) { ... };
mxConnectionHandler.prototype.movePreviewAway = false;
...
It seems that placing two graphs on the same page isn't the right thing to do, since the latest configurations would override others and, maybe, one event can conflict with other ones.
What are you suggesting me?
I'm thinking at completely redrawing graph every time a tab gets focused, but: 1. performances? 2. does mxGraph have a global destroy or reset function? 3. any side effect?...

How to change shape type in Google Slides?

I can see that Slides have getShapeType but don't have setShapeType method.
How may we udpate the shape type in Google Slides?
This is possible from the Google Slides context menu, but does not appear to be possible using Google App Script.
Any ideas? Thanks!
From Class Layout you can use getShapes to goto Class Shape where there are a lot methods for working with shapes.
Currently you cannot change the type of an existing Shape with Apps Script. This is not possible even with Slides API (you can change the shape properties in presentations.batchUpdate, but this doesn't include the shape type).
Once a Shape is created, the type is set, and if you want to have a Shape that has another type, you have to create a new one.
There are ways to achieve this, though, even if not so direct and fast:
Workaround #1 (Apps Script methods):
Using Apps Script built-in methods, you could do the following:
Get the properties of the shape you want to update (including position, rotation, scaling, color, etc.), with the corresponding methods (getRotation, getHeight, etc.).
Delete this existing shape via Shape.remove.
Create a new shape with the same properties as the old one, but with a different shape, via Slide.insertShape.
See for example, this sample, in which the position, rotation and dimensions of an existing shape is retrieved, then the shape is removed, and finally a new one is created with the same position, rotation and dimensions of the old one, and a triangular shape:
var left = shape.getLeft();
var top = shape.getTop();
var rotation = shape.getRotation();
var height = shape.getHeight();
var width = shape.getWidth();
shape.remove();
var shapeType = SlidesApp.ShapeType.TRIANGLE;
var newShape = slide.insertShape(shapeType, left, top, width, height);
newShape.setRotation(rotation);
Workaround #2 (Advanced Slides Service):
You could also activate Advanced Slides Service and use Slides API to accomplish the same process. You would have to do the following:
Retrieve the shape properties via presentations.get
Remove the old shape via presentations.batchUpdate, providing a DeleteObjectRequest.
Create a new shape with the properties of the old one and the new shape type via presentations.batchUpdate, providing a CreateShapeRequest.
Note:
I'd suggest you consider filing a Feature Request in this Issue Tracker component explaining the potential usefulness of this desired functionality.
Reference:
presentations.batchUpdate
UpdateShapePropertiesRequest
Advanced Slides Service
Slide.insertShape(shapeType, left, top, width, height)

Cloning a viewer material

I want to override the color of a component in the viewer, in order to conserve the same rendering effect than other components I would like to clone an existing material and simply modify the color of the clone.
I can change the color of an existing material as follow:
var renderProxy =
viewer.impl.getRenderProxy(
model, fragIds[0])
renderProxy.material.setHex(0xFF0000)
This affects all other components in the model which are using that material, which is not the desired result.
For that purpose I would like to clone material, modify it and affect the new material to a specific component. Invoking the material.clone() method is working:
var newMat = renderProxy.material.clone()
newMat.setHex(0xFF0000)
But the new material will loose all the specific properties that makes it look nice by the renderer.
So my question "is there a way to -easily- clone a viewer material without writing the cloning code for each property"?
You will need to clone the 'prism' material, rather than the 3js phong-material.
Start with this repo: https://github.com/wallabyway/fusion-chair-configurator
as an example, to create a 'metal' material, use these two lines of code (and copy the initPaint() function).
https://github.com/wallabyway/fusion-chair-configurator/blob/c6d5bd575cdf40194c9fbdd1c5f9bb27c70b356e/docs/js/app.js#L107-L108
The szPrism json string, contains lots of parameters. Prism materials are rather complex, but you can find out more about what these parameters do by understanding this article for the Autodesk Cloud Render ART help page (the real-time renderer built inside Revit2019, Fusion360, etc)...
http://help.autodesk.com/view/ARENDERING/ENU/?guid=GUID-49345267-CE6A-4006-BB58-5BEAFD8B0D0E
Try experimenting with Fusion360's 'render' mode. Start by opening 'appearance' and creating some custom materials. You can modify their parameters in real-time to better understand what they do and get the effect you are looking for.
Here is a tutorial video on Fusion360 custom materials: https://www.youtube.com/watch?v=D9AS5rQhtPo
Let me know if that helps.

html div nesting? using google fetchurl

I'm trying to grab a table from the following webpage
http://www.bloomberg.com/markets/companies/country/hong-kong/
I have some sample code which was kindly provided by Phil Bozak here:
grabbing table from html using Google script
which grabs the table for this website:
http://www.airchina.com.cn/www/en/html/index/ir/traffic/
As you can see from Phil's code, there is alot of "getElement()" in the code. If i look at the html code for the Air China website. It looks like it's nested four times? that's why the string of .getElement?
Now I look at the source code for the Bloomberg page and its is load with "div"...
the question is can someone show me how to grab the table from this the Bloomberg page?
and just a brief explanation of the theory also would be useful. Thanks a bunch.
Let's flip your question upside down, and start with the theory. Methodology might be a better word for it.
You want to get at something specific in a structured page. To do that, you either need a way to zap right to the element (which can be done if it's labeled in a unique way that we can access), OR you need to navigate the structure more-or-less manually. You already know how to look at the source of a page, so you're familiar with this step. Here's a screenshot of Firefox Inspector, highlighting the element we're interested in.
We can see the hierarchy of elements that lead to the table: html, body, div, div, div.ticker, table.ticker_data. We can also see the source:
<table class="ticker_data">
Neat! It's labeled! Unfortunately, that class info gets dropped when we process the HTML in our script. Bummer. If it was id="ticker_data" instead, we could use the getElementByVal() utility from this answer to reach it, and give ourselves some immunity from future restructuring of the page. Put a pin in that - we'll come back to it.
It can help to visualize this in the debugger. Here's a utility script for that - run it in debug mode, and you'll have your HTML document laid out to explore:
/**
* Debug-run this in the editor to be able to explore the structure of web pages.
*
* Set target to the page you're interested in.
*/
function pageExplorer() {
var target = "http://www.bloomberg.com/markets/companies/country/hong-kong/";
var pageTxt = UrlFetchApp.fetch(target).getContentText();
var pageDoc = Xml.parse(pageTxt,true);
debugger; // Pause in debugger - explore pageDoc
}
This is what our page looks like in the debugger:
You might be wondering what the numbered elements are, since you don't see them in the source. When there are multiples of an element type at the same level in an XML document, the parser presents them as an array, numbered 0..n. Thus, when we see 0 under a div in the debugger, that's telling us that there are multiple <div> tags in the HTML source at that level, and we can access them as an array, for example .div[0].
Ok, theory behind us, let's go ahead and see how we can access the table by brute-force.
Knowing the hierarchy, including the div arrays shown in the debugger, we could do this, ala Phil's previous answer. I'll do some weird indenting to illustrate the document structure:
...
var target = "http://www.bloomberg.com/markets/companies/country/hong-kong/";
var pageTxt = UrlFetchApp.fetch(target).getContentText();
var pageDoc = Xml.parse(pageTxt,true);
var table = pageDoc.getElement()
.getElement("body")
.getElements("div")[0] // 0-th div under body, shown in debugger
.getElements("div")[5] // 5-th div under there
.getElement("div") // another div
.getElement("table"); // finally, our table
As a much more compact alternative to all those .getElement() calls, we can navigate using dot notation.
var table = pageDoc.getElement().body.div[0].div[5].div.table;
And that's that.
Let's go back to that pinned idea. In the debugger, we can see that there are various attributes attached to elements. In particular, there's an "id" on that div[5] that contains the div that contains the table. Remember, in the source we saw "class" attributes, but note that they don't make it this far.
Still, the fact that a kindly programmer put this "id" in place means we can do this, with getDivById() from that earlier question:
var contentDiv = getDivById( pageDoc.getElement().body, 'content' );
var table = contentDiv.div.table;
If they move things around, we might still be able to find that table, without changing our code.
You already know what to do once you have the table element, so we're done here!

Retrieving all address information along Google Maps route

I am developing an Windows Forms application using VB.NET that offers the user to lookup addresses on Google Maps through a Web Browser. I can also successfully show the directions between two points to the user, as well as allow the user to drag the route as he/she pleases. My question now is - is it possible for me to get the lattitude/longitude information of the route, i.e. the overview_polyline array of encoded lattitude/longitude points and save it to e.g. a text file on my computer? Or is it possible to get a list of all the addresses located both sides of the route over the entire length of the route, and then save the data to a file on my computer? I'm using HTML files to access and display the Google Maps data in the Web Browser item.
Thank you
This is actually pretty simple if your just looking for the screen coordinates.
// this probably should be in your form initialization
this.MouseClick += new MouseEventHandler(MouseClickEvent);
void MouseClickEvent(object sender, MouseEventArgs e)
{
// do whatever you need with e.Location
}
if your strictly looking for the point in the browser, you need to consider the functions
browser.PointToClient();
browser.PointToScreen();
So, this method is usable if you know exactly where your form is (easy to get its coords) and where you webbrowser control is (easy to get coords of this as well since it's just a control in your form) and then, as long as you know how many pixels from the left or right, and from the top or bottom the image will be displayed, once you get the global mouse click coords (which is easy) you can predict where it was clicked on the image.
Alternatively, there are some scarier or uglier ways to do it here...
You can use the ObjectForScripting property to embed code to do this in the webbrowser. It's ugly to say the least. MSDN has some documentation on the process here: http://msdn.microsoft.com/en-us/library/system.windows.forms.webbrowser.objectforscripting.aspx
Because its really ugly, maybe a better solution is to use AxWebBrowser - it's ugly too but not so scary.
In addition, I found this post of someone wanting to do it on a pdf document, and a MSFT person saying its not possible, but really what he is trying to say is that it isn't built in, even with a pdf document its still possible to predict with high to certain accuracy where it was clicked if you use the first method i described. Here is the post anyway: http://social.msdn.microsoft.com/Forums/en/csharpgeneral/thread/2c41b74a-d140-4533-9009-9fcb382dcb60
However, it is possible, and there are a few ways to do it, so don't get scared from that last link I gave ya.
Also, this post may help if you want to do it in javascript:
http://www.devx.com/tips/Tip/29285
Basically, you can add an attribute on the image through methods available in the webbrowser control, you can add something like onclick="GetCoords();" so when it is clicked, the JavaScript function will get the coords, and then you can use javascript to place the values in a hidden input field (input type="hidden") which you can add through the webbrowser control, or if there is one already on the page, you can use that. So, once you place the coords using javacript into that input field, you can easily grab the value in that using the webbrowser control, eg:
webbrowser1.document.getElementById("myHiddenInputField").value
That will get the value in that field, which you've set through JavaScript. Also, the "GetCoords()" function i mentioned is called SetValues() in the javascript method link i provided above (in the devx.com site) but I named it GetCoords because it makes more sense and didn't want to confuse you with the actual name they used, you can change this to any name you want of course. Here is the javascript they were using, this only gets the coords into a variable, doesn't put it into a hidden input field, we will need to do that in addition (at the end of the javascript SetValues/GetCoords function).
function SetValues()
{
var s = 'X=' + window.event.clientX + ' Y=' + window.event.clientY ;
document.getElementById('divCoord').innerText = s;
}
These guys are just saving it inside a div element, which is visible to users, but you can make the div invisible if you want to use a div field, there is no advantage or disadvantage in doing that, you would just need to set the visible property to false using javascript or css, but still, it is easier to use a hidden input field so you don't need to mess with any of that.
Let me know how you get along.