change all fonts in powerpoint without opening the file - python

I wanted change the all fonts in about 100 powerpoint files, without opening the files. There are several shape types in each slide and each might have a different font. I used python-pptx package and wrote the following code to change the fonts of all texts in a powerpoint presentation. Although it does not give any error, it does not work, and the fonts in the file are still whatever they were, for example Arial. I also added print(shape.text) to make sure that it has found all texts, and it seems that there is no issue there. Is it a bug? Or am I missing anything?
prs = Presentation('f10.pptx')
for i, slide in enumerate(prs.slides):
for shape in slide.shapes:
print (shape.has_text_frame)
if shape.has_text_frame:
print(shape.text)
for p in shape.text_frame.paragraphs:
for r in p.runs:
print(r.font.name)
r.font.name = 'Tahoma'
print(r.font.name)
prs.save('f10_tahoma.pptx')
Besides, it seems that the package does not work for utf-8 characters. I added a text-box on the last slide by adding:
text_frame = shape.text_frame
text_frame.clear() # not necessary for newly-created shape
p = text_frame.paragraphs[0]
run = p.add_run()
run.text = 'سلام '
font = run.font
font.name = 'Andalus'
font.size = Pt(18)
before saving the file to add a textbox with utf-8 characters. It adds it there, and when I check the font it shows that it is set to Andalus, but actually it is not Andalus.

With Aspose.Slides for Python via .NET, you can easily change all fonts for all texts in your presentations. The following code example shows you how to do this:
import aspose.slides as slides
with slides.Presentation('example.pptx') as presentation:
for slide in presentation.slides:
for shape in slide.shapes:
if isinstance(shape, slides.AutoShape):
for paragraph in shape.text_frame.paragraphs:
for portion in paragraph.portions:
portion.portion_format.latin_font = slides.FontData('Tahoma')
You can also evaluate Aspose.Slides Cloud SDK for Python for presentation manipulating. This REST-based API allows you to make 150 free API calls per month for API learning and presentation processing.
Aspose Slides Online Viewer can be used to view presentations without PowerPoint installed.
I work at Aspose.

What language is the text of the file? Run.font properties work fine for UTF-8, but there is a separate font for cursive scripts like Arabic. Access to that secondary font is not implemented in python-pptx unfortunately, but that could explain at least part of the behavior you're seeing.
For roman character text (like that we're using here), there are a couple things to check.
The font in question needs to be installed on the machine PowerPoint is running on when the document is opened. Otherwise PowerPoint will substitute a font.
The font (typeface) name used in the XML will not always exactly match what appears in the PowerPoint drop-down selection box. You need to give that name to python-pptx in the exact form it should appear in the XML. You may need to make an example file that works by hand, perhaps containing a single slide with a single textbox for simplicity, and then inspect the XML of that file to find the "spelling" used for that typeface by PowerPoint.
You could do that with code like this:
prs = Presentation("example.pptx")
shape = prs.slides[0].shapes[0]
print(shape._element.xml)
You should be able to locate the typeface name somewhere in an element like <p:rPr> or <p:defRPr>.

Related

Text aside and below an image in pyqt5

I am preparing a catalog for a small library. All books with main data are displayed. Next to each title there is a button that must show the cover image and a short description of the content. Something like this.
Until now I have not found anything that could help me, all the options present in designer do not allow to have the text arranged as I would like
You cannot achieve that layout using Designer, because the layout management of Qt always considers objects (widgets) having a rectangular shape, even if they contain text: text based widgets are just like other "rectangular widgets" and their content is not considered nor changed based on the "shape" of other elements in the layout: Qt layout managers always consider widgets as rectangles.
It can be done, though, using rich-text based widgets like QLabel (discouraged for this purpose[1]) or QTextEdit, by embedding the image in the document[2], since the (limited) HTML subset provided by the Qt rich text engine supports the basic float property[3] for images and tables.
The following basic HTML can be used with QTextEdit.setHtml():
<img src="path/to/image.png" style="float: right;">
Some very long text...
...
...
...
Here is the result:
The only issue is the path of the image.
If the image is stored with the Qt resource system, use that path (src=":path/in/resources")[4].
If the image is a local file, then just use python formatting with the path to that file, and ensure that that path is correct (consider that relative paths always use the working directory, which is the path from which the python interpreter is launched).
If the image is loaded internally (for instance, from the network) and not stored as a physical file, you can use the base64 encoding to show the image:
import base64
# ...
# assuming "imageData" is a Python "bytes" object
base64data = base64.b64encode(imageData).decode()
html = '''
<img src="data:image/whatever;base64,{}" style="float: right;">
'''.format(base64data)
# to avoid escaping of curly brackets for format() or f-string
html += '''
Some very long text...
'''
textEdit = QTextEdit(readOnly=True)
textEdit.setHtml(html)
Note that the value of data: in <img src=""> is actually pointless: you only need to provide the data: part, the value is actually not that relevant since Qt just uses QImage.loadFromData() and automatically guesses the format from the file using QImageReader. The result is the same that you would get by doing img = QImage('myimage') even if you don't specify the format and the file name has no specific/valid extension. This also obviously means that if the file/data is corrupted, the image might not be shown (and a "broken image" icon will be shown as a placeholder).
If the image was retrieved through Qt (for instance, using QFile.readAll() or downloaded with QNetworkAccessManager), then the the raw data is stored as a QByteArray, and the base64 data used above can be created with the following:
base64data = imageData.toBase64().data().decode()
as in:
# - encode using base64, as another QByteArray:
qtBase64 = imageData.toBase64()
# - convert it to a Python "bytes" object (b"..."):
pyBase64 = qtBase64.data()
# - make it a string:
base64data = pyBase64.decode()
The above is also valid if the image is stored on a database as raw data.
[1] QLabel obviously lacks direct support for scrolling, and it also has some important limitations when using rich text contents (most importantly, word wrapped contents) and the label is part of a complex layout.
[2] QLabel actually uses QTextDocument features internally, which enables rich text features (most importantly, word wrapping; see the above note) whenever it detects what could possibly be HTML formatted content (based on the return value of Qt.mightBeRichText()).
[3] Remember that the current Qt rich-text support is based on HTML4 and CSS2.1, so the only valid values of float are: left, right and none (the default). The CSS inherit value is also not supported.
[4] Be aware that PyQt6 dropped support for the pyrcc utility, but resource files can still be used (since they're implemented on the Qt side, not in Python) and built with the rcc utility provided by Qt: they added the -g python flag, similarly to what done for uic, which creates python compatible resource files. You only need to edit the import statement of the generated file to avoid issues (remember that PyQt and PySide can not be used together, and PySide might not be installed in the target system). See this answer.

How Could I replace Text in a PPP with Python pptx?

I want to replace the text in a textbox in Powerpoint with Python-pptx.
Everything I found online didn't work for me and the documentation isn't that helpful for me.
So I have a Textbox with the Text:
$$Name 1$$
$$Name 2$$
and I want to change the $$Name1 $$ to Tom.
How can I achieve that?
A TextFrame object defined in python-pptx helps in manipulating contents of a textbox. You can do something like:
from python-pptx import Presentation
"""open file"""
prs = Presentaion('pptfile.pptx')
"""get to the required slide"""
slide = prs.slides[0]
"""Find required text box"""
for shape in slide.shapes:
if not shape.has_text_frame:
continue
text_frame = shape.text_frame
if "Name 1" == text_frame.text:
text_frame.text = "Tom"
"""save the file"""
prs.save("pptfile.pptx")
Try this:
import pptx
input_pptx = "Input File Path"
prs = pptx.Presentation((input_pptx))
testString = "$$Name1 $$"
replaceString = 'Tom'
title_slide_layout = prs.slide_layouts[0]
slide = prs.slides.add_slide(title_slide_layout)
for slide in prs.slides:
for shape in slide.shapes:
if shape.has_text_frame:
if(shape.text.find(testString))!=-1:
shape.text = shape.text.replace(testString, replaceString)
if not shape.has_table:
continue
prs.save('C:/test.pptx')
Ok thank you. I just found out, that my PowerPoint example was totaly messed up. No everything works fine with a new PowerPoint blanked
In order to keep original formatting, we need to replace the text at the run level.
from pptx import Presentation
ppt = Presentation(file_path_of_pptx)
search_str = '$$Name1$$'
replace_str = 'Tom'
for slide in ppt.slides:
for shape in slide.shapes:
if shape.has_text_frame:
for paragraph in shape.text_frame.paragraphs:
for run in paragraph.runs:
print(run.text)
if(run.text.find(search_str))!=-1:
run.text = run.text.replace(search_str, replace_str)
ppt.save(file_path_of_new_pptx)
Since PowerPoint splits the text of a paragraph into seemingly random runs (and on top each run carries its own - possibly different - character formatting) you can not just look for the text in every run, because the text may actually be distributed over a couple of runs and in each of those you'll only find part of the text you are looking for.
Doing it at the paragraph level is possible, but you'll lose all character formatting of that paragraph, which might screw up your presentation quite a bit.
Using the text on paragraph level, doing the replacement and assigning that result to the paragraph's first run while removing the other runs from the paragraph is better, but will change the character formatting of all runs to that of the first one, again screwing around in places, where it shouldn't.
Therefore I wrote a rather comprehensive script that can be installed with
python -m pip install python-pptx-text-replacer
and that creates a command python-pptx-text-replacer that you can use to do those replacements from the command line, or you can use the class TextReplacer in that package in your own Python scripts. It is able to change text in tables, charts and wherever else some text might appear, while preserving any character formatting specified for that text.
Read the README.md at https://github.com/fschaeck/python-pptx-text-replacer for more detailed information on usage. And open an issue there if you got any problems with the code!
Also see my answer at python-pptx - How to replace keyword across multiple runs? for an example of how the script deals with character formatting...

Cannot Embed Cover Art To Mp3 in Python 3.5.2

I have this file "image.jp
and this .mp3 file:
"Green Day - When I Come Around [Official Music Video].mp3"
in the directory "test"
I have already successfully set tags as Author, Title, Album and etc using eyeD3 library.
and then I try to set the Cover Art.
I've tried two possibilities, but none of them worked:
First one: Mutagen:
from mutagen.mp3 import MP3
from mutagen.id3 import ID3, APIC, error
complete_file_path = "test\\"+"Green Day - When I Come Around [Official Music Video].mp3"
path_to_thumb_wf = "test\\"+"image.jpg"
audio = MP3(complete_file_path, ID3=ID3)
# add ID3 tag if it doesn't exist
try:
audio.add_tags()
except error:
pass
print(path_to_thumb_wf)
audio.tags.add(
APIC(
encoding=3, # 3 is for utf-8
mime='image/jpg', # image/jpeg or image/png
type=3, # 3 is for the cover image
desc=u'Cover',
data=open(path_to_thumb_wf, 'rb').read()
)
)
audio.save(v2_version=3)
And the solution using eyeD3
audiofile = eyed3.load(complete_file_path)
# read image into memory
imagedata = open(path_to_thumb_wf,"rb").read()
# append image to tags
audiofile.tag.images.set(3,imagedata,"image/jpeg", u"you can put a description here")
audiofile.tag.save()
I'm using python 3.5.2 on Windows 10. And i don't know if it could influence the result but i'll say anyway, the song has already a cover art that I'd like to change.
As explained in the ID3v2.3 section on APIC:
There may be several pictures attached to one file, each in their individual "APIC" frame, but only one with the same content descriptor. There may only be one picture with the picture type declared as picture type $01 and $02 respectively.
In v2.3, IIRC, "content descriptor" isn't actually documented anywhere, so different clients may do slightly different things here, but most tools will treat it either as the picture type plus description string, or as the entire header (text encoding, MIME type, picture type, and encoded description) as a binary blob. (And some tools just ignore it and allow you to store pictures with completely identical frame headers, but I don't think that's relevant with Mutagen.)
At any rate, this means you're probably just adding another Cover (front) picture, named 'Cover', rather than replacing any existing one.
You haven't explained how you're looking at the file. But I'm guessing you're trying to open it in Windows Media Player or iTunes or some other player, or view it in Windows Explorer (which I think just asks WMP to read the tag), or something like that?
Almost all such tools, when faced with multiple images, just show you the first one. (Some of them don't even distinguish on picture type, and show you the first image of any type, even if it's a 32x32 file icon…)
Some do have a way to view the other pictures, however. For example, in iTunes, if you Get Info or Properties on the track, then go to the Cover Art or similar tab (sorry for the vagueness, but the names have changed across versions), you can see all of the pictures in the tag.
At any rate, if you want to replace the APIC with a different one, you either need to exactly match the descriptor (and, again, that can mean different things to different libraries…), or, more simply, just delete the old one as well as adding the new one.
One more thing to watch out for: both iTunes and WMP cache cover art, and assume that it's never going to change once the file has been imported. And WMP also has various things that can override the image in the file, such as a properly-UUID'd folder cover art image in the same directory.

Creating a lightweight fallback font with fontforge and fonttools

For a webapp I need a way to prevent that a browser falls back to another font if my web font doesn't include a character. It seems the only way to do this is to add another font to the fontstack which includes "all" possible characters 1.
There are already existing fallback fonts, but those are more debug helpers as they show the codepoint as number, therefore they are much to heavy (>2MB).
The fallback font for my usecase should just show something like a box to signal a missing character.
My idea was to generate a simple font with only one glyph and apply a feature file which will replace all glyphs with this one.
My script for fontforge:
import fontforge
import fontTools.feaLib.builder as feaLibBuilder
from fontTools.ttLib import TTFont
font_name = 'maeh.ttf'
font = fontforge.font()
glyph = font.createChar(33, "theone")
pen = glyph.glyphPen()
pen.moveTo((100,100))
pen.lineTo((100,500))
pen.lineTo((500,500))
pen.lineTo((500,100))
pen.closePath()
for i in range(34, 99):
glyph = font.createChar(i)
glyph.width=10
font.cidConvertTo('Adobe', 'Identity', 0) # doesn't make a difference
font.generate(font_name)
font = TTFont(font_name)
feaLibBuilder.addOpenTypeFeatures(font, 'fallback.fea')
font.save("fea_"+font_name)
My feature file:
languagesystem DFLT dflt;
#all=[\00035-\00039];
##all=[A-Z] this works
feature liga {
sub #all by theone;
} liga;
But the above results in a
KeyError: ('cid00037', 'SingleSubst[0]', 'Lookup[0]', 'LookupList')
with changing numbers for cid00037.
If I use the out commented A-Z from the Feature file it works, so this approach doesn't seem to be completely wrong.
Why can't fonttools find the glyphs if I specify the range in CID notation?
Is there another way to crate a class for the OpenType feature file which includes all glyphs?
While working on the above problem, somebody hinted me to the Adobe NotDef font, which is pretty much what I was looking for. For some reason I wasn't able to convert the .otf of the Adobe NotDef to woff or woff2 with fontforge. Also all the online tools to create the web font files like fontsquirrel failed. To create the woff file I used sfnt2woff from the woff-tools package. For the woff2 file I used https://github.com/google/woff2.

Convert SVG to PDF (svglib + reportlab not good enough)

I'm creating some SVGs in batches and need to convert those to a PDF document for printing. I've been trying to use svglib and its svg2rlg method but I've just discovered that it's absolutely appalling at preserving the vector graphics in my document. It can barely position text correctly.
My dynamically-generated SVG is well formed and I've tested svglib on the raw input to make sure it's not a problem I'm introducing.
So what are my options past svglib and ReportLab? It either has to be free or very cheap as we're already out of budget on the project this is part of. We can't afford the 1k/year fee for ReportLab Plus.
I'm using Python but at this stage, I'm happy as long as it runs on our Ubuntu server.
Edit: Tested Prince. Better but it's still ignoring half the document.
I use inkscape for this. In your django view do like:
from subprocess import Popen
x = Popen(['/usr/bin/inkscape', your_svg_input, \
'--export-pdf=%s' % your_pdf_output])
try:
waitForResponse(x)
except OSError, e:
return False
def waitForResponse(x):
out, err = x.communicate()
if x.returncode < 0:
r = "Popen returncode: " + str(x.returncode)
raise OSError(r)
You may need to pass as parameters to inkscape all the font files you refer to in your .svg, so keep that in mind if your text does not appear correctly on the .pdf output.
CairoSVG is the one I am using:
import cairosvg
cairosvg.svg2pdf(url='image.svg', write_to='image.pdf')
rst2pdf uses reportlab for generating PDFs. It can use inkscape and pdfrw for reading PDFs.
pdfrw itself has some examples that show reading PDFs and using reportlab to output.
Addressing the comment by Martin below (I can edit this answer, but do not have the reputation to comment on a comment on it...):
reportlab knows nothing about SVG files. Some tools, like svg2rlg, attempt to recreate an SVG image into a PDF by drawing them into the reportlab canvas. But you can do this a different way with pdfrw -- if you can use another tool to convert the SVG file into a PDF image, then pdfrw can take that converted PDF, and add it as a form XObject into the PDF that you are generating with reportlab. As far as reportlab is concerned, it is really no different than placing a JPEG image.
Some tools will do terrible things to your SVG files (rasterizing them, for example). In my experience, inkscape usually does a pretty good job, and leaves them in a vector format. You can even do this headless, e.g. "inkscape my.svg -A my.pdf".
The entire reason I wrote pdfrw in the first place was for this exact use-case -- being able to reuse vector images in new PDFs created by reportlab.
Just to let you know and for the future issue, I find a solution for this problem:
# I only install svg2rlg, not svglib (svg2rlg is inside svglib as well)
import svg2rlg
# Import of the canvas
from reportlab.pdfgen import canvas
# Import of the renderer (image part)
from reportlab.graphics import renderPDF
rlg = svg2rlg.svg2rlg("your_img.svg")
c = canvas.Canvas("example.pdf")
c.setTitle("my_title_we_dont_care")
# Generation of the first page
# You have a last option on this function,
# about the boundary but you can leave it as default.
renderPDF.draw(rlg, c, 80, 740 - rlg.height)
renderPDF.draw(rlg, c, 60, 540 - rlg.height)
c.showPage()
# Generation of the second page
renderPDF.draw(rlg, c, 50, 740 - rlg.height)
c.showPage()
# Save
c.save()
Enjoy a bit with the position (80, 740 - h), it is only the position.
If the code doesn't work, you can look at in the render's reportlab library.
You have a function in reportlab to create directly a pdf from your image:
renderPDF.drawToFile(rlg, "example.pdf", "title")
You can open it and read it. It is not very complicated. This code come from this function.

Categories

Resources