How to align and set color at the same time inside a cell with python-docx - python-docx

I am trying to make a table with python-docx.
This is my desire output:
¡--OK(bold)--¡--MIDDLE(in red)--¡----RIGHT¡
And this is what I get:
¡--OK(bold)--¡MIDDLE(in red)----¡RIGHT----¡
The code that I use is:
from docx import Document
from docx.shared import RGBColor
from docx.enum.text import WD_ALIGN_PARAGRAPH
document = Document()
table = document.add_table(rows=1, cols=3, style='Table Grid')
fila = table.rows[0].cells
# First cell: OK in bold
texto = 'OK'
dentro = fila[0].paragraphs[0]
dentro.add_run(texto).bold = True
dentro.alignment = WD_ALIGN_PARAGRAPH.CENTER
# Second cell: Middle in red
texto = 'MIDDLE'
dentro = fila[1].paragraphs[0].add_run(texto)
dentro.alignment = WD_ALIGN_PARAGRAPH.CENTER
font = dentro.font
font.color.rgb = RGBColor(255,0,0) # Red
# Third cell : Right
texto = 'RIGHT'
dentro = fila[2].paragraphs[0]
dentro.add_run(texto)
dentro.aligment = WD_ALIGN_PARAGRAPH.RIGHT
document.save('demo.docx')
I have two issues: First one is that I do not get the correct alignment in the middle cell when I add the color issue. Second one is that in cells after the wrong output of middle one, the alignment does not seem to work. How can I fix it? Do I have to wait until next version (actual 0.8.10)? Thanks,

Your code for the middle cell is different. You assign the new run to dentro rather than the paragraph. This causes the alignment value to be assigned to the run where it does nothing.
Change:
dentro = fila[1].paragraphs[0].add_run(texto)
to:
dentro = fila[1].paragraphs[0]
run = dentro.add_run(texto)
font = run.font
font.color.rgb = RGBColor(255, 0, 0)
I'm not sure how to account for the RIGHT alignment not "taking" on the third cell; I would make this fix and then see how you go.

Related

appendParagraph - Adding Blank Space on the First Line

I am new to Google Apps Script and trying to generate a document using Excel Data.
I am able to successfully create the document and add tables and paragraphs.
I see some odd behavior with appendParagraph.
It adds a space (blank like) when adding the first paragraph. The the paragraphs that follow are fine.
I tried replacing the new line (\n) with '', but did not work.
Any suggestion how to get rid of the line or add a paragraph without the blank line for the first paragraph (Sample Code below).
titleStyle[DocumentApp.Attribute.HORIZONTAL_ALIGNMENT] = DocumentApp.HorizontalAlignment.CENTER
titleStyle[DocumentApp.Attribute.FONT_SIZE] = 19
titleStyle[DocumentApp.Attribute.FONT_FAMILY] = 'Arial'
titleStyle[DocumentApp.Attribute.BOLD] = true
const compTitle = cell.appendParagraph('TITLE PARAGRAPH')
compTitle.setAttributes(titleStyle)
const contentStyle = {}
contentStyle[DocumentApp.Attribute.HORIZONTAL_ALIGNMENT] = DocumentApp.HorizontalAlignment.CENTER
contentStyle[DocumentApp.Attribute.FONT_SIZE] = 11
contentStyle[DocumentApp.Attribute.FONT_FAMILY] = 'Arial'
contentStyle[DocumentApp.Attribute.BOLD] = false
const content = cell.appendParagraph('CONTENT PARAGRAPH')
content.setAttributes(contentStyle)
As Cooper mentioned, your documents start off with a blank paragraph. If you want to remove it programmatically, run the following function:
function removeEmptyPara() {
const paras = DocumentApp.getActiveDocument().getBody().getParagraphs();
const firstPara = paras[0];
if (paras.length > 1 && !firstPara.getText()) firstPara.removeFromParent()
}
Let me know if this helps.

Centering in a gDoc after a replace

I am looking to replace a string within a Google Doc via an app script. The string will exist on a line, but after the replace, I want it to have a specific font, size and justification.
I've created a style to address all these attributes (I included both Horiz. and Vert. alignment) and most of it works fine. When the string is replaced, the replacement has the right font, size and bold attributes. For some reason, I cannot get the justification to get changed.
// Define the style for the replacement string.
var hdrStyle = {};
hdrStyle[DocumentApp.Attribute.HORIZONTAL_ALIGNMENT] =
DocumentApp.HorizontalAlignment.CENTER;
hdrStyle[DocumentApp.Attribute.VERTICAL_ALIGNMENT] =
DocumentApp.VerticalAlignment.CENTER;
hdrStyle[DocumentApp.Attribute.FONT_FAMILY] = 'Calibri';
hdrStyle[DocumentApp.Attribute.FONT_SIZE] = 24;
hdrStyle[DocumentApp.Attribute.BOLD] = true;
{ then later }
documentBody = DocumentApp.openById(fileId).getBody();
hdrElem = documentBody.findText("old string").getElement();
hdrText = hdrElem.asText().setText("new string");
// Force our 'header style':
hdrElem.setAttributes(hdrStyle);
I've tried setting the style after the findText and (as here) after, but no change in centering.
I see there is a paragraph centering, but I am not clear how to 'get' the paragraph associated with the element that is returned on the find.
I'm hoping this is some simple set of calls - but have run out of ideas (and patience)..!
Any help would be appreciated!
You can use getParent() on hdrElem to get the parent paragraph to apply the styling to.
https://developers.google.com/apps-script/reference/document/text#getParent()
documentBody = DocumentApp.openById(fileId).getBody();
hdrElem = documentBody.findText("old string").getElement();
hdrText = hdrElem.asText().setText("new string");
var hdrParent = hdrElem.getParent()
// Force our 'header style':
hdrParent.setAttributes(hdrStyle);

Masked images not displaying in AS3

I am trying to mask an image on another so that I only view the specific portion of the unmasked image through the masked one. My problem is that I cannot see anything on the screen.. no Image, no effect at all
cardMask = new Image(Root.assets.getTexture("card_mask"));
cardMask.y = Constants.STAGE_HEIGHT*0.40;
cardMask.x = Constants.STAGE_WIDTH *0.48;
trace("it's add mask");
cardLight = new Image(Root.assets.getTexture("card_light_mask"));
cardLight.y = Constants.STAGE_HEIGHT*0.46;
cardLight.x = Constants.STAGE_WIDTH *0.48;
cardLight.mask=cardMask;
maskedDisplayObject = new PixelMaskDisplayObject(-1,false);
maskedDisplayObject.addChild(cardLight);
maskedDisplayObject.x=cardLight.x;
maskedDisplayObject.y=cardLight.y;
maskedDisplayObject.mask=cardMask;
maskedDisplayObject.blendMode = BlendMode.SCREEN;
addChild(maskedDisplayObject);
First, for masking an object the mask object should also be added to the display list. Your code does not add cardMask to display list anywhere. Second, if your maskedDisplayObject should be visible at all times, the mask should be assigned not to it, but to some other object which displayed part you desire to control. And third, it is also possible that this.stage is null, therefore the entire tree (this -> maskedDisplayObject -> cardLight) is plain not rendered. You need to check all three of these conditions to get something displayed.
Also, if you desire cardLight as an object to move independently of maskedDisplayObject, you should add it to this instead, and check that it's displayed on top of maskedDisplayObject (call addChild(cardLight) after addChild(maskedDisplayObject)).
This all totals to this code:
trace("Stage is null:", (this.stage==null)); // if this outputs true, you're out of display
cardMask = new Image(Root.assets.getTexture("card_mask"));
cardMask.y = Constants.STAGE_HEIGHT*0.40;
cardMask.x = Constants.STAGE_WIDTH *0.48; // mask creation unaltered
trace("it's add mask");
cardLight = new Image(Root.assets.getTexture("card_light_mask"));
cardLight.y = Constants.STAGE_HEIGHT*0.46;
cardLight.x = Constants.STAGE_WIDTH *0.48;
cardLight.mask=cardMask; // this is right
maskedDisplayObject = new PixelMaskDisplayObject(-1,false);
// maskedDisplayObject.addChild(cardLight); this is moved to main part of display list
maskedDisplayObject.x=cardLight.x;
maskedDisplayObject.y=cardLight.y;
// maskedDisplayObject.mask=cardMask; NO masking of this, you're only masking cardLight
cardLight.blendMode = BlendMode.SCREEN; // display mode is also changed
addChild(maskedDisplayObject);
addChild(cardLight);

AS3 Array Display button not working properly

I'm working on a program to learn how to use arrays in my computer course and my display button doesn't work properly after the first press. The first time I click it, it works properly and displays everything but the 2nd time it stop showing the first value and starts showing the last value twice, the 3rd time cuts off the 2nd value and displays the last value three times and so on. And when I press the button to find the sum of all values it gives me the sum of all of the values that will show up after I hit the display button. Here's my code, and sorry about the french commentary, it's for school.
function afficherFunction(event:MouseEvent):void
{
// Compose cette fonction visant à afficher tous les éléments du tableau.
txtSortie.text = "";
var entier:int;
entier = -1
for (var i:int=entier; i < mesEntiers.length; i++)
{
if (i+1 < mesEntiers.length)
{
mesEntiers[i] = mesEntiers[i+1];
affichage = affichage + mesEntiers[i] + "\n"
}
}
txtSortie.text = affichage;
affichage = "";
i = -1;
} //Fin fonction afficher.
mesEntiers[i] = mesEntiers[i+1];
This line is your problem. Not sure what you meant for that line to be doing, but it's setting the value at index i to the value at the next index--essentially shifting all the values down one (and losing the value at index 0).

How to get the size (in pixels) of a jpeg image I get with UrlFetchApp.fetch(photoLink)?

In a script that sends email in HTML format I add an image that is stored in a public shared folder.
I get the blob using UrlFetchApp.fetch(photoLink) but the image has not necessarily the right size so in the html code I use width and height attributes (for now with fixed values, see code below) but I'd like it to be automatically resized with the right ratio.
To achieve that I need to know how to get the original size of the image (height and width) but I just don't know how to get it without inserting the image in an intermediate document (which would work but I find this approach a bit weird and unnecessarily complicated... moreover I don't feel like having a useless doc appearing each time I change the image file).
Here is the relevant part of the code that creates the email message :
function sendMail(test,rowData,genTitle,day,title,stHour,endHour){
var photoLink = sh.getRange('H1').getValue();
var image = UrlFetchApp.fetch(photoLink);
//************* find the pixel size of the image to get its ratio
var msgTemplate = '<IMG SRC="'+photoLink+'" BORDER=0 ALT="logo" HEIGHT=200 WIDTH=300><BR><BR>'+
'Résumé de vos réservations au nom de <NOM><BR><BR><BR><table style="background-color:lightblue;border-collapse:collapse;" border = 1 cellpadding = 5><th></th><th><TABLEHEADER></th><EVENTS></table><BR><CONCLUSION><BR>Cordialement,<BR><BR>';
var mailTitle = 'Confirmation de réservation - '+getTextFromHtml(genTitle);
var descr = '';
for(var d = 0;d<day.length;++d){
Logger.log(Number(rowData[(d+5)]));
var content = '<tr bgcolor="#ffffbb" width="100%"><td><NUMBER> </td><td > <DESCRIPTION></td></tr>'
if(Number(rowData[(d+5)])>1){var pl = ' places'}else{var pl = ' place'};
content = content.replace('<NUMBER>',rowData[(d+5)]+pl);
content = content.replace('<DESCRIPTION>',title[d]+' de '+stHour[d]+' heures à '+endHour[d]+' heures');
if(Number(rowData[(d+5)])>0){
descr += content;
}
}
msgTemplate = msgTemplate.replace('<NOM>',rowData[1]).replace('<EVENTS>',descr).replace('<TABLEHEADER>',genTitle);
var textVersion = getTextFromHtml(msgTemplate.replace(/<br>/gi,'\n').replace(/<td>/gi,'\n'));
// Logger.log(textVersion)
if(test){
MailApp.sendEmail(Session.getEffectiveUser().getEmail(),mailTitle, textVersion,{'htmlBody':msgTemplate,"replyTo" : retour});
}
else
{
MailApp.sendEmail(rowData[2],mailTitle, textVersion,{'htmlBody':msgTemplate,"replyTo" : retour});
}
}
There is no easy way within Apps Script to figure out what an image size would be. There are some other projects that might be able to analyze the bitmap data and give you dimensions.
The last time I had to solve this problem. I just wrote a simple App Engine app to do the image math for me -
import webapp2
from google.appengine.api import urlfetch
from google.appengine.api import images
from django.utils import simplejson
class MainHandler(webapp2.RequestHandler):
def get(self):
url = self.request.get('url')
imgResp = urlfetch.fetch(url) #eg. querystring - url=http://xyz.com/img.jpg
if imgResp.status_code == 200:
img = images.Image(imgResp.content);
jsonResp = {"url":url, "h":img.height, "w":img.width, "format":img.format}
self.response.headers['Content-Type'] = 'application/json'
self.response.out.write(simplejson.dumps(jsonResp))
app = webapp2.WSGIApplication([('/imageinfo', MainHandler)], debug=True)
And then I call it from Apps Script like this -
function checkImageSizes() {
var imageUrls = ['http://developers.google.com/apps-script/images/carousel0.png','http://www.w3.org/MarkUp/Test/xhtml-print/20050519/tests/jpeg420exif.jpg'];
for(var i in imageUrls){
var resp = JSON.parse(UrlFetchApp.fetch('http://arunimageinfo.appspot.com/imageinfo?url='+imageUrls[i]).getContentText());
Logger.log('Image at %s is %s x %s',resp.url,resp.w,resp.h);
}
}
You are welcome to use my App Engine instance if your volume is a couple of times a week :)
I doubt you can do this in apps script. Certainly not natively but you might be able to find or adapt a jpg library that looks at the binary blob header and extracts the image size.