Python-docx how to set font spacing? - python

How to set font spacing in python-docx or how to add element to w:rPr?
//<w:rPr> <w:spacing w:val="200"/> </w:rPr>

There is no API support for this setting in python-docx.
Adding a <w:spacing> element will work if that's what Word does, however the sequence in which child elements appear is, in general, significant in WordprocessingML (the XML schema .docx files adhere to). If you don't get the w:spacing element in the right order among the w:rPr child elements or you add one when one is already there, you'll trigger a repair error.
So you need something like this:
def run_set_spacing(run, value: int):
"""Set the font spacing for `run` to `value` in twips.
A twip is a "twentieth of an imperial point", so 1/1440 in.
"""
def get_or_add_spacing(rPr):
# --- check if `w:spacing` child already exists ---
spacings = rPr.xpath("./w:spacing")
# --- return that if so ---
if spacings:
return spacings[0]
# --- otherwise create one ---
spacing = OxmlElement("w:spacing")
rPr.insert_element_before(
spacing,
*(
"w:w",
"w:kern",
"w:position",
"w:sz",
"w:szCs",
"w:highlight",
"w:u",
"w:effect",
"w:bdr",
"w:shd",
"w:fitText",
"w:vertAlign",
"w:rtl",
"w:cs",
"w:em",
"w:lang",
"w:eastAsianLayout",
"w:specVanish",
"w:oMath",
),
)
return spacing
rPr = run._r.get_or_add_rPr()
spacing = get_or_add_spacing(rPr)
spacing.set("val", str(value))
Then you would call this for each run that needs that setting like so:
run_set_spacing(run, 200)

I tried the answer by scanny, It did not work. But the output document XML <w:spacing val="200"> is close to the right format.
The right format is <w:spacing w:val="200">
Try this
from docx.oxml.ns import qn
spacing.set(qn('w:val'), str(value))

Related

reportlab: Smartest way to position page with bookmarkPage

I've been using python and reportlab to auto-generate long documents and want to use the PDF outline tree for easy navigation through the document. According to the docs, canvas.bookmarkPage comes with multiple options to adjust the document view after jumping to the destination page. The standard one is a simple page Fit to the window. From a user perspective, I would prefer FitH (as wide as possible, with destination at the top of the screen) or XYZ (keep user zoom level with destination at the top of the screen). When using any fit option except the basic Fit, the function call must be provided with the coordinates to arrange the view accordingly.
However, I could not find any explanations, examples, code snippets, or anything on how to figure this out, and it took me a good while to come up with a solution. So, I want to share this solution here and ask if this is really the best way to do it or if I overlooked something basic.
The key thing here is SmartParagraph which remembers its position after it was drawn. First, I used flowable.canv.absolutePosition(0,0) in the afterFlowable() method because this is where I needed this information to pass it to bookmarkPage(). However, the position was always reported as 0, 0, so apparently the flowable and/or the canvas have forgotten everything about the position when afterFlowable() is reached. So I thought there has to be some point in time when a Flowable knows its position and after investigating the source code I found out that after draw(), it still knows where it is.
So: SmartParagraph is a subclass of Paragraph that stores its position after it is drawn, so that later in the document building process this can be used by any external element for whatever.
The example will create a dummy pdf with 2 headings that do a nice FitH zoom and two headings that do the basic Fit zoom.
Does anyone have a better idea on how to solve this?
import typing
from reportlab.lib.styles import ParagraphStyle as PS
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.platypus.flowables import Flowable
from reportlab.platypus import PageBreak, Spacer
from reportlab.platypus.paragraph import Paragraph
from reportlab.platypus.doctemplate import SimpleDocTemplate
from reportlab.lib.units import cm
class SmartParagraph(Paragraph):
def __init__(self, text, *args, **kwds):
"""This paragraph remembers its position on the canvas"""
super(SmartParagraph, self).__init__(text, *args, **kwds)
self._pos: typing.Tuple[int, int] = None
def draw(self):
super(SmartParagraph, self).draw()
self._pos = self.canv.absolutePosition(0, 0)
def get_pos(self) -> typing.Tuple[int, int]:
return self._pos
class CustomDocTemplate(SimpleDocTemplate):
def __init__(self, filename, outline_levels: int = 4, **kwargs):
super(CustomDocTemplate, self).__init__(filename, **kwargs)
self._bookmark_keys = list()
if not isinstance(outline_levels, int) and outline_levels < 1:
raise ValueError("Outline levels must be integer and at least 1")
self._outline_levels = {f'Heading{level+1}': level for level in range(outline_levels)}
# Map of kind: Heading1 -> 0
# Heading 1 is level 0, I dont make the rules
def afterFlowable(self, flowable: Flowable):
"""Registers TOC entries."""
if isinstance(flowable, Paragraph):
flowable: Paragraph
text = flowable.getPlainText()
style = flowable.style.name
if style in self._outline_levels:
level = self._outline_levels[style]
else:
return
if text not in self._bookmark_keys:
key = text
self._bookmark_keys.append(key)
else:
# There might headings with identical text, yet they need a different key
# Keys are stored in a list and incremented if a duplicate is found
cnt = 1
while True:
key = text + str(cnt)
if key not in self._bookmark_keys:
self._bookmark_keys.append(key)
break
cnt += 1
if isinstance(flowable, SmartParagraph):
# Only smart paragraphs know their own position
x, y = flowable.get_pos()
y += flowable.style.fontSize + 15
self.canv.bookmarkPage(key, fit="FitH", top=y)
else:
# Dumb paragraphs need to show the whole page
self.canv.bookmarkPage(key)
self.canv.addOutlineEntry(title=text, key=key, level=level)
def _endBuild(self):
"""Override of parent function. Shows outline tree by default when opening PDF."""
super(CustomDocTemplate, self)._endBuild()
self.canv.showOutline()
story = list()
story.append(SmartParagraph('First Smart Heading', getSampleStyleSheet()['h1']))
story.append(Paragraph('Text in first heading'))
story.append(Spacer(1, 0.5 * cm))
story.append(SmartParagraph('First Sub Smart Heading', getSampleStyleSheet()['h2']))
story.append(Paragraph('Text in first sub heading'))
story.append(Spacer(1, 0.5 * cm))
story.append(Paragraph('Second Sub Dumb Heading', getSampleStyleSheet()['h2']))
story.append(Paragraph('Text in second sub heading'))
story.append(PageBreak())
story.append(Paragraph('Last Dumb Heading', getSampleStyleSheet()['h1']))
story.append(Paragraph('Text in last heading', PS('body')))
doc = CustomDocTemplate('mintoc.pdf')
doc.multiBuild(story)

Setting background color of wordx

im trying to set a report default background color but it appears black instead of blue, can anyone help?
Why is that? do i need to use another format that is not hex? iv tryied with other format and still nothing.
docpath="/Users/ricardosimoes/Desktop/DESKTOP/Jira/my_word_file.docx"
mydoc = docx.Document()
section_h = mydoc.sections[0]
header = section_h.header
styles = mydoc.styles
style = styles.add_style('Tahoma',WD_STYLE_TYPE.PARAGRAPH)
style.font.name = 'Tahoma'
style.font.size = Pt(11)
shd = OxmlElement('w:background')
# Add attributes to the xml element
shd.set(qn('w:color'), '#0000FF')
shd.set(qn('w:themeColor'), 'text1')
shd.set(qn('w:themeTint'), 'F2')
# Add background element at the start of Document.xml using below
mydoc.element.insert(0, shd)
# Add displayBackgroundShape element to setting.xml
shd1 = OxmlElement('w:displayBackgroundShape')
mydoc.settings.element.insert(0, shd1)
paragraph_h = header.paragraphs[0]
runheader = paragraph_h.add_run()
runheader.add_picture("client_report/report_img/titulo.png", width=docx.shared.Inches(5), height=docx.shared.Inches(1))
mydoc.add_picture("client_report/report_img/bottom.png", width=docx.shared.Inches(5),
height=docx.shared.Inches(1))
mydoc.save(docpath)
The snippet seems to supply all three of the w:color, w:themeColor and w:themeTint attributes, and the latter two override the first one. The ECMA-376 standard says about the w:color attribute:
If the background specifies the use of a theme color via the themeColor attribute, this value is ignored. [Note: Applications are discouraged from specifying both the color and themeColor attributes on the same parent element. end note]
I haven't tested this, but removing themeColor and themeTint (and dropping the # from 0000FF) should cause the blue color to show up.

Creating a Hierachy in Maya

I've written some code for a tool that duplicates and moves controllers/objects to joints. Its really just code to copy and move one object to a list of other objects. Its very basic but it does work.
The pasted code is a snippet that I've taken out from the rest but will work if run in Maya.
This code in particular will create a nurbsCurve, put it inside a group, then move that group to the first object on the list. I then used a loop to do it again, but then parent the group underneath the nurbsCurve from the previous group to create a parent-hierarchy all the way down.
import maya.cmds as cmds
def setZero(target):
cmds.setAttr("%s.translateX" % target, 0)
cmds.setAttr("%s.translateY" % target, 0)
cmds.setAttr("%s.translateZ" % target, 0)
cmds.setAttr("%s.rotateX" % target, 0)
cmds.setAttr("%s.rotateY" % target, 0)
cmds.setAttr("%s.rotateZ" % target, 0)
selJoint = cmds.ls(selection = True, shortNames = True)
firstCtrl = cmds.circle(normal =( 1,0,0))
firstGrp = cmds.group(firstCtrl)
cmds.parent(firstGrp,selJoint[0])
setZero(firstGrp)
cmds.parent(firstGrp, world = True)
#Use loop for the rest of the joints
for joint in selJoint:
#Skip the first joint since its already done
if joint == selJoint[0]:
continue
circleCtrl = cmds.circle(normal =( 1,0,0))
offsetGrp = cmds.group(circleCtrl)
cmds.parent(offsetGrp, joint)
setZero(offsetGrp)
cmds.parent(offsetGrp, world = True)
cmds.parent(offsetGrp, firstCtrl) #Parent new offset Group to the old controller
firstCtrl = circleCtrl #The new controller is now the target for the next offset/ctrl to be parented under
It works as intended but I get this warning:
Warning: Cannot parent components or objects in the underworld
I've tried looking this issue up, but I haven't found anything on how to fix the problem. But I do know that the "underworld" is another space in Maya, and that it can cause issues down the line.
The warning is being thrown at the end on this line cmds.parent(offsetGrp, firstCtrl), and if you print out firstCtrl it'll output something like this:
[u'nurbsCircle1', u'makeNurbCircle1']
So what it's doing is it's trying to parent firstCtrl and 'nurbsCircle1' to 'makeNurbCircle1'. Since 'makeNurbCircle1' is a node with no transform, it throws that warning, because obviously you can't parent to something with no transform.
Luckily it's very easy to fix. Now that we know that it's a list, we just parent to the first index of it like so: cmds.parent(offsetGrp, firstCtrl[0])
If you want more info on this same warning then you can also check out this question.

canvasObjects; found PartInstance, expecting tuple in Abaqus/CAE

I'm developening an Abaqus/CAE plug-in, in this plug-in i'm using the gui toolkit, and i have a button that uses the PickStep, on click the button i can select a PartInstance in the viewport.
Then i want to export the selected PartInstance to an .obj file but when i try it, abaqus displays an error.
This is an example of my PICK BUTTON:
# PICK BUTTON 1
pickHf = FXHorizontalFrame(p=col2, opts=0, x=0, y=0, w=0, h=0, pl=0, pr=0, pt=0, pb=0, hs=DEFAULT_SPACING,
vs=DEFAULT_SPACING)
# Note: Set the selector to indicate that this widget should not be
# colored differently from its parent when the 'Color layout managers'
# button is checked in the RSG Dialog Builder dialog.
pickHf.setSelector(99)
label1 = FXLabel(p=pickHf, text='' + ' (None)', ic=None, opts=LAYOUT_CENTER_Y | JUSTIFY_LEFT)
pickHandler1 = DBPickHandler(form, form.uper, 'Select a 3D, discrete and dependent meshed instance', INSTANCES,
1, label1)
icon = afxGetIcon('select', AFX_ICON_SMALL)
FXButton(p=pickHf, text='\tPick Items in Viewport', ic=icon, tgt=pickHandler1, sel=AFXMode.ID_ACTIVATE,
opts=BUTTON_NORMAL | LAYOUT_CENTER_Y, x=0, y=0, w=0, h=0, pl=2, pr=2, pt=1, pb=1)
I save the value in an ObjectKeyword:
self.uper = AFXObjectKeyword(self.cmd, 'uper', True, pickedDefault)
This is how i export the PartInstance to .obj:
print 'Uper - ' + uper[0].name
f.write('Uper - '+uper[0].name+'\n')
session.writeOBJFile(fileName='C:/temp/Uper.obj', canvasObjects=(uper[0]))
That displays and error, and i also tried this:
print 'Fixed - ' + fixed[0].name
f.write(fixed[0].name+'\n')
fixedobj = open('Fixed.obj', 'w')
pickle.dump(fixed[0], fixedobj)
fixedobj.close()
But that does not work either.
I get this error:
canvasObjects;found PartInstance, expecting tuple
This answer will help you. On your call to session.writeOBJFile you are trying to create a one element tuple for the canvasObjects argument. Simply wrapping the item in parentheses won't achieve that. You need to add a comma to make it a tuple:
session.writeOBJFile(fileName='C:/temp/Uper.obj', canvasObjects=(uper[0],))
The Abaqus documentation says this about canvasObjects:
canvasObjects
A sequence of canvas objects to export.
I'm not sure if PartInstance is considered a canvas object or not, but you may still have problems even after correcting the argument to be a tuple. If so, make sure the items of the tuple are proper canvas objects.

Problem when getting the content of a listbox with python and ctypes on win32

I would like to get the content of a list box thanks to python and ctypes.
item_count = ctypes.windll.user32.SendMessageA(hwnd, win32con.LB_GETCOUNT, 0, 0)
items = []
for i in xrange(item_count):
text_len = ctypes.windll.user32.SendMessageA(hwnd, win32con.LB_GETTEXTLEN, i, 0)
buffer = ctypes.create_string_buffer("", text_len+1)
ctypes.windll.user32.SendMessageA(hwnd, win32con.LB_GETTEXT, i, buffer)
items.append(buffer.value)
print items
The number of items is correct but the text is wrong. All text_len are 4 and the text values are something like '0\xd9\xee\x02\x90'
I have tried to use a unicode buffer with a similar result.
I don't find my error. Any idea?
If the list box in question is owner-drawn, this passage from the LB_GETTEXT documentation may be relevant:
If you create the list box with an owner-drawn style but without the LBS_HASSTRINGS style, the buffer pointed to by the lParam parameter will receive the value associated with the item (the item data).
The four bytes you received certainly look like they may be a pointer, which is a typical value to store in the per-item data.
It looks like you need to use a packed structure for the result. I found an example online, perhaps this will assist you:
http://www.brunningonline.net/simon/blog/archives/winGuiAuto.py.html
# Programmer : Simon Brunning - simon#brunningonline.net
# Date : 25 June 2003
def _getMultipleWindowValues(hwnd, getCountMessage, getValueMessage):
'''A common pattern in the Win32 API is that in order to retrieve a
series of values, you use one message to get a count of available
items, and another to retrieve them. This internal utility function
performs the common processing for this pattern.
Arguments:
hwnd Window handle for the window for which items should be
retrieved.
getCountMessage Item count message.
getValueMessage Value retrieval message.
Returns: Retrieved items.'''
result = []
VALUE_LENGTH = 256
bufferlength_int = struct.pack('i', VALUE_LENGTH) # This is a C style int.
valuecount = win32gui.SendMessage(hwnd, getCountMessage, 0, 0)
for itemIndex in range(valuecount):
valuebuffer = array.array('c',
bufferlength_int +
" " * (VALUE_LENGTH - len(bufferlength_int)))
valueLength = win32gui.SendMessage(hwnd,
getValueMessage,
itemIndex,
valuebuffer)
result.append(valuebuffer.tostring()[:valueLength])
return result
def getListboxItems(hwnd):
'''Returns the items in a list box control.
Arguments:
hwnd Window handle for the list box.
Returns: List box items.
Usage example: TODO
'''
return _getMultipleWindowValues(hwnd,
getCountMessage=win32con.LB_GETCOUNT,
getValueMessage=win32con.LB_GETTEXT)

Categories

Resources