Vertical alignment in SVG via svgwrite - python

I'm having issues with vertical alignment of texts when using the svgwrite python library.
For example, here's a simple code that generates a red-filled circle with a black text saying "Text" on top of the circle:
import svgwrite
d = svgwrite.Drawing(filename='alignment_test.svg',
size=(60,60))
circle = d.circle((30,30), 30, fill='red')
text = d.text('Test', (30,30),
style='text-anchor:middle',
font_size='17px')
d.add(circle)
d.add(text)
d.save()
The result is this expected image:
However, I want the text to also be vertically aligned.
I tried using the alignment-baseline attribute of SVG, i.e.
style='text-anchor:middle;\
alignment-baseline:middle'
However, it doesn't work - neither in FireFox (where the vertical alignment just doesn't work), not in Inkscape, which complains Unimplemented style property 363.
I would appreciate any suggestions on how to solve the matter.

...so while writing this question I searched around a bit more, and actually read the second answer here (by toutankh):
vertical alignment of text element in SVG,
which suggests using the attribute dominant-baseline instead of alignment-baseline for text objects. And it works perfectly.
Lesson learned: never read only the first answer to a SO question.

Related

Can I get the formatted text size using ReportLab in Python?

Is it possible to get the size/extents of rendered text in reportLab?
I need to calculate some positions relative to other elements on the page.
The flowable system isn't want I'm looking for. I don't mind using it to get the sizes if necessary, but it's not appropriate for my overall layout. I tried creating a Paragraph an calling wrap but it returns the available width always, not the measured extends.
You may get the width of a rendered text with stringWidth:
from reportlab.pdfbase.pdfmetrics import stringWidth
width = stringWidth(text, font_name, font_size)
Line spacing gives you the height of a line of text.

How to Invert SVG text with appropriate kerning?

How can I invert (rotate 180 degrees) a text object so that the text is kerned appropriately?
My example uses Python and the svgwrite package, but my question seems about any SVG.
Suppose I use the following code:
dwg = svgwrite.Drawing()
dwg.add(dwg.text(fullName, (int(width/2.),gnameHeight),
font_size=gnameFontSize, text_anchor="middle"))
The above code generates text looking like this:
dwg.text() objects accept a rotate parameter that is applied to all characters in a text string, so I've used the following code to reverse the string first:
pcRotate = [180]
ngap = 1
revFullName = fullName
rcl = []
for c in revFullName:
rcl.append(c)
for i in range(ngap):
rcl.append(' ')
rcl.reverse()
revFullName = ''.join(rcl)
dwg.add(dwg.text(revFullName, (int(width/2.),pcnameHeight),
font_size=gnameFontSize, text_anchor="middle", rotate=pcRotate))
But, this produces the very ugly version below:
and this is using an artificial space gap between characters to make it slightly less unreadable.
What's the best way to tap into whatever kerning is being used by standard text in this inverted situation?
The rotate attribute of a <text> element is intended for situations where you want to rotate individual characters. If you want to rotate the whole text object then you should be using a transform instead.
http://pythonhosted.org/svgwrite/classes/mixins.html#transform-mixin
I'm posting this as a self-answer, only to make formatting more clear. Two useful hints from #paul-lebeau happily acknowledged.
While the svgwrite package seems solid, its documentation is a bit thin. The two things I wish it had said:
The rotate attribute of a <text> element is intended for situations where you want to rotate individual characters. If you want to rotate the whole text object, then you should be using a transform mixin instead.
If you need to center the transformed text with respect to some center (other that the default current user coordinate system), add two additional parameters xctr,yctr. This differs from the doc which calls for a single center argument that is a (2-tuple).
The correct code is:
pcRotate = 'rotate(180,%s,%s)' % (int(width/2.),pcnameHeight)
textGroup = svgwrite.container.Group(transform=pcRotate)
textGroup.add(dwg.text(fullName, (int(width/2.),pcnameHeight),
font_size=gnameFontSize, text_anchor="middle"))
dwg.add(textGroup)

How do I numerically position a barcode in reportlab?

I have a bit of a specific case that I can't seem to figure out a solution to. I'm writing a shipping label template object in ReportLab for Python. I have the following code that creates a barcode as a drawing.
uspsBarcode = createBarcodeDrawing('USPS_4State', value=self.imbval, routing=self.zip4.replace('-',''))
print uspsBarcode.getBounds() # print out of position and scale
Using that code, I later add it to a shape group, and that shape group gets returned. So I need the barcode to be positioned relative to the object. I can't seem to find any way to pass positioning to this, even though I've dug through the inheritance. Though as you can see from the print, positioning is set somwhere.
Just for anyone else that runs across this issue. It turns out that if you put the barcode drawing in a shape group, the shape group container can be moved around numerically with the shift function.
uspsBarcode = shapes.Group()
bc = createBarcodeDrawing('USPS_4State', value=self.imbVal, routing=self.zip4.replace('-',''))
uspsBarcode.add(bc)
uspsBarcode.shift(self.x+(s*0.2), self.y)

Insert images to powerpoint slide using python (win32com.client)

I have been tasked with inserting and re-sizing several hundred images to a powerpoint.
I need to use a particular source format that is similar to other power points used by our company.
I have been playing around with activepython's win32com API's and I have figured out how to open up a file and create a blank slide.
My question is how would I go about inserting an image and resizing it to whatever size I need(the images will be the only thing on each page). Also I am trying to use my company's theme for the background and title page but this is not as important as getting the images on page and re-sized.
any help would be greatly appreciated, thanks!
I got this from the page Xavier referred to:
Pict1 = Slide1.Shapes.AddPicture(FileName=pictName, LinkToFile=False, SaveWithDocument=True, Left=100, Top=100, Width=200, Height=200)
That will work if your original images are square; otherwise, it will distort them.
Better to specify -1 for the width and height. Then PPT will insert them at their "natural" size (whatever PPT might decide that is ... not important for present purposes). Then you can read the shape's size to determine its aspect ratio and make sure that stays constant if you change the size or you can set the shape's .LockAspectRatio property to true and adjust either height or width and the other will auto adjust to maintain the aspect ratio.
You should use Shapes.AddPicture as described here (archived version of this broken link):
from tkinter import *
import tkinter.filedialog as tkFileDialog
import win32com.client # middleman/translator/messanger between windows and python
import win32com.gen_py.MSO as MSO # contains constants refering to Microsoft Office Objects
import win32com.gen_py.MSPPT as MSPPT # contains constants refering to Microsoft Office Power Point Objects
g = globals() # a dictonary of global vlaues, that will be the constants of the two previous imports
for c in dir(MSO.constants): g[c] = getattr(MSO.constants, c) # globally define these
for c in dir(MSPPT.constants): g[c] = getattr(MSPPT.constants, c)
Application = win32com.client.Dispatch("PowerPoint.Application")
Application.Visible = True # shows what's happening, not required, but helpful for now
Presentation = Application.Presentations.Add() # adds a new presentation
Slide1 = Presentation.Slides.Add(1, ppLayoutBlank) # new slide, at beginning
TenptStr = Slide1.Shapes.AddShape(msoShape10pointStar, 100, 100, 200, 200)
pictName = tkFileDialog.askopenfilename(title="Please Select the Image you wish to load")
print(pictName)
Pict1 = Slide1.Shapes.AddPicture(FileName=pictName, LinkToFile=False, SaveWithDocument=True, Left=100, Top=100, Width=200, Height=200)
Note that the "choose file" option produces a file path with '/" rather than '' and you may need to replace the former by the latter.
Firstly I would resize them first before insertion using PIL python imaging library. In general mucking about with any office image processing has been uniformly awful for me so I would not recommend it.
secondly the object model examples in VBA found in PowerPoint help or online are usually all you need to get a long way
from memory the insert was pretty easy -
ppt.slide.ImageFromFile("path")
however exact positioning I do not remember and I am afraid I don't have ppt here to try out. If I get achance later I will post some references
Good luck with the docs
I agree with the accepted answer. I would like to point out that I needed to prefix the FileName string with an 'r' to treat the backslashes as literal characters. Escaping the backslashes by replacing single backslashes with double also works. #vestland this may be helpful

Suggestions on manipulating an SVG map

I'm working on a map of the native languages of California for Wikipedia. The map contains areas that each correspond to a language. The original looks like this (click it to see the SVG):
I want to make "locator maps" for each of those individual languages by hand (in Inkscape), like this one, for a language called Cahuilla (which has the language code cah):
Needless to say, doing this would be a pain in the neck if I generated all 60-some by hand in Inkscape.
Worse, whenever I find a mistake in the original, I'd have to redo the whole set of locator maps. (And in fact I recently realized that my original is missing one language entirely. Sorry Cupeño.)
So my goal is to automate this process. I don't have much experience processing SVG or even XML, so I'm looking for recommendations as to which libraries would be most helpful. I'd prefer solutions in Python, sincely I'm not hopeless in that language.
To summarize, I need to:
Get a list of references to all the language shapes in the original SVG file.
For each language in this list:
change the background color of the shape for the current language
put a box around the language
duplicate the box and scale it to a given size (I recognize that in this step it might be difficult to actually "crop" the surrounding shapes as I did in my example -- just creating a box with the language shape on a white background would be sufficient.)
place the duplicate in the upper right corner
save all this stuff in code.svg
The final product will then be 60 SVG files named cah.svg, etc. Better yet, it would be possible to re-generate the whole shebang if it became necessary to edit the original map (which is quite likely).
I would recommend using Python and specifically creating extensions for Inkscape. I don't think you really need 60 SVG unless you really want to because the source map will have everything you need.
What I would do is use Inkscape to rename the various regions to the same language code you will be using. For example, Cahuilla or cah is currently path13882 in your SVG file. I would rename it to cah or Cahuilla and repeat the process for each of the language regions. Use the Edit\XML Editor to help make sure you update all of the paths.
After you have updated the names/IDs, then you might look into SVG scripting. I would just create a javascript/ecmascript map or dictionary that has the relevant information for the language:
var langaugeMap = {};
languageMap["cah"] = { name: "Cahuilla", color: "rgb(255, 0, 0)" };
languageMap["cup"] = { name: "Cupeño", color: "rgb(255, 64, 0)" };
// and so on -- this could even be generated from a CSV file or Excel, etc.
// if the highlighted color is always the same, then you don't need it in the map
// or use style sheets for an activeshape and inactiveshape
// Put any information that is specific to a language in the map
Then you just need to add a mouseover function that would add and position the bounding box and change the path color. Here is one example of events and scripting although it is quite dated. Carto.net has an interactive map example as well.
The SVG would look something like:
<path
style="fill:#800000;fill-opacity:1;display:inline"
d="m 422.43078,517.40746 c 0.52151,0.006 1.10755,0.0374 1.75925,0.0825 3.82011,0.26462 5.01088,0.75501 5.75001,2.37491 0.51312,1.12355 2.4121,3.0097 4.22213,4.1946 3.906,2.55656 7.38824,2.07964 9.61517,-1.3194 2.12996,-3.25075 9.13451,-3.19196 13.61739,0.11545 1.77185,1.30707 4.04994,2.38037 5.06319,2.38041 1.01325,0 3.34593,0.92548 5.18421,2.06155 2.52816,1.56236 4.9918,2.09869 10.09889,2.19902 3.71359,0.0729 7.68145,0.64349 8.82374,1.26442 2.81717,1.53202 5.67633,1.42382 10.7693,-0.40133 4.97461,-1.78261 6.31161,-1.36525 17.10267,5.31063 3.39862,2.10239 6.90491,4.08094 7.7956,4.39801 2.46593,0.8776 4.55428,4.66976 3.95259,7.17971 -0.29359,1.22605 -0.75898,3.51121 -1.03349,5.07968 -0.27411,1.56855 -0.88382,3.33952 -1.35761,3.93621 -1.50842,1.89871 -20.98501,7.77151 -27.8945,8.41122 -3.66014,0.33879 -8.3091,1.04337 -10.32987,1.56676 -3.50666,0.90799 -3.81743,0.79746 -6.78388,-2.44089 -3.3486,-3.65594 -6.11308,-4.2716 -8.48815,-1.89661 -2.14408,2.14401 -1.85126,3.96434 1.0667,6.66846 1.40725,1.30409 1.85699,2.10446 1.00027,1.77571 -0.85672,-0.32883 -6.3937,-0.12213 -12.3033,0.46176 -5.9096,0.58386 -12.56062,1.27336 -14.78297,1.53381 -4.17058,0.4888 -5.09869,-0.37014 -2.61673,-2.42989 2.1563,-1.78956 1.74245,-2.63318 -1.65999,-3.36449 -1.69931,-0.36525 -4.94789,-1.90738 -7.213,-3.42496 -2.26473,-1.51754 -5.89662,-3.66823 -8.07583,-4.77731 -2.17921,-1.10923 -6.21922,-3.94186 -8.97721,-6.29463 -4.75318,-4.05478 -4.93682,-4.36681 -3.43604,-6.02527 0.96935,-1.07117 2.36209,-1.56397 3.5899,-1.26992 1.62639,0.38937 2.49494,-0.41237 4.59588,-4.24958 1.42481,-2.60257 2.23686,-4.95457 1.80316,-5.22266 -0.4337,-0.26805 -1.06784,-3.14557 -1.40725,-6.39358 -0.33978,-3.24797 -1.19001,-6.79064 -1.89134,-7.87242 -1.74322,-2.68957 -1.2114,-3.65437 2.44111,-3.61188 l 0,0 z"
id="cah"
inkscape:label="#cah"
onmouseover="highlightRegion(evt);"
onmouseout="restoreRegion(evt);" />
For any automation with the SVG file, I would use Inkscape extensions. Look at the Inkscape wiki under the Developer Tutorials / Extensions. Otherwise, SVG is still XML so if you have named your language regions in the source file with a distinguishing name, you could use Python to parse the XML and save each path/language region to a separate file.
Here's an example using your map. You can click any element to get the boundingbox, this can be used for the miniview viewBox (with some tweaking). As you see it's not adding much code to the map, just a couple of elements. When you have all the viewBoxes you can update the miniview 'viewBox' attribute on the fly using javascript.
For the coloring of the selected shape, I'd suggest adding a <use> element inside the miniview <svg> that has a fill that overrides any fill used in the main map (you may have to use a stylesheet rule for this to make sure it's got high specificity, e.g adding #miniview .activeshape { fill: red !important }. Let the <use> point to the selected shape, and make sure you add class="activeshape" on the shape you point to.
This should be a start at least, hope it helps :)

Categories

Resources