https://code.launchpad.net/~leo-editor-team/+junk/inkcall
now includes inkscape-edit.doc.png which gives some minimal guidance on the minimal inkscape interaction needed. inkscape-edit.doc.png (attached) was generated with the script itself, which amuses me greatly.
Still needs better docs, maybe a special case for one line callouts.
Cheers -Terry
Thanks for this. I'm having trouble installing lxml on Windows 7,
which is a problem because I'd like Windows-centric screen shots.
easy_install lxml says "make sure to install the development packages
of libxml2 and libxslt". I've downloaded the windows binary packages
and put their paths in sitecustomize.py, but no joy importing or using
them.
Edward
> Thanks for this. I'm having trouble installing lxml on Windows 7,
> which is a problem because I'd like Windows-centric screen shots.
Switched to ubuntu, but still having problems. lxml exists with
python 2.6.5, but now I crash in resize_curve_box at the statement:
th = self.get_dim(filename, text_id, 'height') # text height
The filename is /tmp/<temp name>
And the crash is "No such file or directory"
Edward
> The filename is /tmp/<temp name>
>
> And the crash is "No such file or directory"
Actually, the crash was due to not having inkscape installed.
Oh cool. It worked.
Edward
------------------------------------------------------------------------------
Edward K. Ream email: edre...@gmail.com
Leo: http://webpages.charter.net/edreamleo/front.html
------------------------------------------------------------------------------
> > I'm having trouble installing lxml on Windows 7.
>
> Sorry for the noise. I didn't read the installation instructions.
> easy_install lxml==2.2.4 will install for Python 2.6.
Hmm, I tried (just now) installing lxml for Python 2.7, which is what I have on my Win 7 here (virtualbox :-). But didn't succeed with lxml==2.2.2. Will try other random numbers, but not impressed with lxml Windows install.
If I can get that working I'll try and clean up some of the other stuff you pointed out.
Cheers -Terry
> cmd = [
> r"c:\scripts\inkscape.bat",
> "--with-gui",
> # svgfile,]
>
> proc = subprocess.Popen(cmd)
>
> However, making a similar change in three places in inkcall.py crashes
> at these lines at get_dim:
>
> for line in stdout.strip().split('\n'):
> id_,x,y,w,h = line.split(',')
>
> Ah. The first line is the basically the cmd above. Maybe I can
> ignore it. Let's change the second line to:
>
> data = line.split(',')
> if len(data) != 5: continue
> id_,x,y,w,h = data
I think the .bat echoed the command to stdout, tripping up the read of the inkscape output.
I've added a --inkscape parameter, so you can do:
C:\Users\tbrown\inkcall>c:\Python26\python.exe inkcall.py --inkscape "c:\Program
Files (x86)\Inkscape\inkscape.exe"
And like the docs would say, if they were written, parameters can be passed in by setting attributes on an object and using it as the opt parameter of the constructor of the InkCall class, as main() sort of indicates.
Cheers -Terry
> I've added a --inkscape parameter
This works. Thanks.
I pushed this change to edit_svg at rev 4.
svgfile = os.path.abspath(svgfile)
This is a good collaboration we have going here. Many thanks for your work.
Edward
P.S. I eagerly await your docs :-) Sometimes its easier to fix
crashers than to understand how to use a program!
EKR
> I pushed this change to edit_svg at rev 4.
>
> svgfile = os.path.abspath(svgfile)
I don't really see what this does, but if it's fixing something in Windows, great.
> This is a good collaboration we have going here. Many thanks for your work.
>
> Edward
>
> P.S. I eagerly await your docs :-) Sometimes its easier to fix
> crashers than to understand how to use a program!
Docs are in the module docstring as of rev 5 (just pushed).
I'll guess I'll add a scriptlet to extract them to .html (they're Rst)
Cheers -Terry
It's the same as os.path.join(os.getcwd(),svgfile), which is essential
the way I'm using inkcall.py.
> Docs are in the module docstring as of rev 5 (just pushed).
Thanks. I'll look at them immediately.
Edward
>> Docs are in the module docstring as of rev 5 (just pushed).
>
> Thanks. I'll look at them immediately.
Excellent docs! Thanks very much. I added a note about easy_install at rev 7.
EKR
> Excellent docs! Thanks very much. I added a note about easy_install at rev 7.
rev 9 includes using PIL (*if installed*) to get pixel perfect rendering of the screenshot. If it's not installed, either the screenshot may be rescaled (and less sharp) in the rendered png, or you need to edit the template to have the right screenshot size, and align it on a pixel boundary.
Cheers -Terry
> rev 9 includes using PIL (*if installed*) to get pixel perfect rendering of the screenshot. If it's not installed, either the screenshot may be rescaled (and less sharp) in the rendered png, or you need to edit the template to have the right screenshot size, and align it on a pixel boundary.
Just pulled rev 9 and installed PIL. This makes a huge difference.
Edward
> rev 9 includes using PIL (*if installed*) to get pixel perfect rendering of the screenshot. If it's not installed, either the screenshot may be rescaled (and less sharp) in the rendered png, or you need to edit the template to have the right screenshot size, and align it on a pixel boundary.
Gah :-) rev 11 finally gets guaranteed no rescaling of screenshot pixels if PIL is available. Inkscape allocates large bounding boxes for elements with drop shadows, which may have non-integer boundaries, leading to potential blurring of screenshot pixels. This is fixed now (an existing inkscape flag) and PIL is used to trim the transparent boundaries these oversized bboxes may introduce if balloons are near the edge.
Cheers -Terry
> c:\leo.repo\inkcall>c:\python26\python.exe inkcall.py --inkscape "c:
> \Program Files (x86)\Inkscape\inkscape.exe"
> Traceback (most recent call last):
> File "inkcall.py", line 538, in <module>
> main()
> File "inkcall.py", line 189, in main
> inkcall.make_png(filename)
> File "inkcall.py", line 450, in make_png
> img = Image.open(outfilename)
> File "c:\python26\lib\site-packages\PIL\Image.py", line 1952, in
> open
> fp = __builtin__.open(fp, "rb")
> IOError: [Errno 2] No such file or directory:
> 'some_screen_shot.co.png'
Don't see why, when you're running from the directory containing the file, but try
c:\python26\python.exe inkcall.py \
--inkscape "c:\Program Files (x86)\Inkscape\inkscape.exe" \
--png c:\output.png
?
Out to run some errands now.
Cheers -Terry
> c:\python26\python.exe inkcall.py \
> --inkscape "c:\Program Files (x86)\Inkscape\inkscape.exe" \
> --png c:\output.png
Now the message is: No such file or directory: c:\\output.png
EKR
> > c:\python26\python.exe inkcall.py \
> > --inkscape "c:\Program Files (x86)\Inkscape\inkscape.exe" \
> > --png c:\output.png
>
> Now the message is: No such file or directory: c:\\output.png
Oh, didn't see it properly the first time. It needs a screenshot filename as an argument. Try c:\python26\python.exe inkcall.py --help
Really I wasn't expecting you'd use it from the command line (although I want to fix any problems there). This script is working for me from Leo (in Ubuntu, admittedly).
import PyQt4.QtGui
app = g.app.gui.qtApp
pix = PyQt4.QtGui.QPixmap
w = pix.grabWindow(app.activeWindow().winId())
path = 'test.png'
w.save(path,'png')
import inkcall
reload(inkcall) # for dev. env.
ik = inkcall.InkCall()
ik.make_svg(path, ['test this'], [33])
ik.edit_svg(path)
ik.make_png(path)
There does still seem to be something wrong with the --png flag - I'll install leo in win 7 py 2.6 and see what happens.
I never could get lxml installed in win 7 py 2.7, fine in 2.6 with your lxml==2.2.4.
Cheers -Terry
> There does still seem to be something wrong with the --png flag - I'll install leo in win 7 py 2.6 and see what happens.
This script is working for me in windows 7, py 2.6, qt 4.7. Obviously it takes some shortcuts, it's up to the user to work out how to get inkcall.py on PYTHONPATH etc.
path should be absolute, as it could end up anywhere.
import PyQt4.QtGui
app = g.app.gui.qtApp
pix = PyQt4.QtGui.QPixmap
w = pix.grabWindow(app.activeWindow().winId())
path = 'test.png'
w.save(path,'png')
import sys
sys.path.append("c:\\users\\tbrown\\inkcall")
import inkcall
reload(inkcall) # for dev. env.
ik = inkcall.InkCall({
'template': "c:\\users\\tbrown\\inkcall\\template.svg",
'inkscape': r"c:\Program Files (x86)\Inkscape\inkscape.exe" ,
})
g.es('generate SVG')
ik.make_svg(path, ['test this'], [33])
g.es('edit SVG')
ik.edit_svg(path)
g.es('generate PNG')
ik.make_png(path)
I think I'll try and make the fail output more useful, seeing there seems to be lots of ways for this to fail to connect to various files it needs.
Cheers -Terry
> I think I'll try and make the fail output more useful, seeing there seems to be lots of ways for this to fail to connect to various files it needs.
That would be good. Obscure tracebacks aren't exactly user-friendly.
I'll start using this code just as soon as I finish DnD. Maybe today.
Edward
To answer several of my own questions, the overall situation is clear:
svg images are xml files. Thus template.svg, as "complex" as it
looks, is just one or more pictures, with supporting info. Depending
on your point of view, Terry's scripts manipulate either the xml or
the pictures they represent.
The big advantage that lxml claims over ElementTree is that lxml
supports xpath queries of the xml. That is, using lxml it is possible
to isolate sub-parts of the svg images.
So xpath is a nice tool to have. Otoh, can it really be all that hard
to parse xml into whatever parts one wants? And why does lxml need
python-specific DLL's?
Edward
P.S. The Inkscape man page describes the command-line args:
http://inkscape.modevia.com/inkscape-man.html
I've copied the description of the relevant args to my study outline.
EKR
--- On Sat, 9/4/10, Edward K. Ream <edre...@gmail.com> wrote:
Thinking this was a simple enough program, I started it specifically trying to avoid lxml, which I usually use without hesitation.
I could accept making a dictionary of @id to node mappings, because I don't think xml.etree.ElementTree can search on @id content (am I missing something?). But then, given an element 'e', I wanted to access its parent. How do you do that in xml.etree.ElementTree? I could find no way. Googling just suggests lxml :-) So yes, lxml could be eliminated, and given the small size of the file involved dumb searches are practical, it just adds a lot of cruft to compensate for the inadequacies of xml.etree.ElementTree.
Note that for this kind of thing I find it much more natural to code from a DOM perspective. Of course it could be done from a SAX perspective, for this kind of app. that seems an unnecessary chore to me.
But if you want to get rid of lxml you can, there's only dozens of nodes in the file, so even the simplest full recursion searches should work.
Cheers -Terry
I mean there's only a small number of elements in the SVG (XML), so even the simplest...
Cheers -Terry
> I could accept making a dictionary of @id to node mappings, because I don't think xml.etree.ElementTree can search on @id content (am I missing something?). But then, given an element 'e', I wanted to access its parent. How do you do that in xml.etree.ElementTree? I could find no way.
You have got to be kidding me :-) How about finding children? If we
can do that, we can create a tree by hand.
Edward
> For example, template.svg looks like a lot
> of work. Perhaps inkscape created it?
It's from Inkscape, I just drew it. Inkscape lets you edit XML, so I could set the IDs I needed.
Cheers -Terry
I see. Thanks.
EKR
Sure, the first attempt recursed the tree and created a dict of @id->element mapping. That same process could create an @id -> parent mapping. That would probably be most of what you need to remove lxml. It's too bad there's no decent xpath in standard python yet. For this SVG
<flowRoot id="flowRoot18">
<flowRegion id="rect3911">
<rect id="rect3911" width="165." height="93." x="123." y="114."/>
</flowRegion>
<flowPara id="co_text_holder">Some text</flowPara>
</flowRoot>
you need to find the parent of the flowPara to read the size for adjusting the frame. In XPath that's
doc.xpath("//*[@id='co_text_holder']/..")[0] # take first item of one item list
w/o xpath, it's somehow search for the element with @id='co_text_holder', and then somehow find its parent.
But sadly it would be easier to write inkcall without lxml than it would be to fix lxml installation on windows problems. Assuming that just asking on the lxml list wouldn't solve the problem, I assume it wouldn't, seeing we followed the official docs.
Of course, I only use windows occasionally for games :-} so it didn't occur to me that inkcall would be run on it.
And to be fair to lxml it does install relatively easily in Windows for python 2.6
I'll drop a note on their list.
Cheers -Terry
> return e.attrib.get('id')
or just e.get('id') I think.
> for name in names:
> print(name,ids.get(name))
>
> Now how hard was that?
Infinitely (1./0.) harder than not having to write anything (the lxml in linux case), but not bad in the lxml isn't available for windows case. I did email the lxml list, I'll see if they respond. Python's lack of full XML support is unfortunate (schemas as well as xpath), Java's had it all forever :-/ I know XML's not the be all and end all (JSON etc), but it's fairly important.
Cheers -Terry
>> Now how hard was that?
>
> Infinitely (1./0.) harder than not having to write anything (the lxml in linux case), but not bad in the lxml isn't available for windows case.
I'm starting to believe you :-) I spent an hour last night slogging
through the inkcall code.
I'm hoping there is a principle that will simplify the transliteration
from lxml to ElementTree, namely that all methods in common between
lxml and ElementTree work exactly the same way. This is a theorem,
not a given.
It's important, as I found out. inkcall is doing quite a bit. For
example, in make_dom there are these lines:
for i in part.values():
i.xpath('..')[0].remove(i)
The hope is to replace this with something like:
for i in part.values():
# Recompute all parents, as in these posts.
parents_d = self.getParents()
parent = parents_d.get(i)
parent.remove(i)
In other words, remove had better work the same way in both environments!
Edward
> I contend that the new code is actually at least as easy to understand
> than the xpath code. I think it would have been just as easy to write
> the new code as it would have been to write the old code.
>
> In other words, for *this* application at least, lxml appears to add
> exactly nothing of significance to ElementTree.
Of course I disagree. I think a platform which does not provide at least moderate support for XPath does not really support XML, at least from a DOM perspective, which I think is the natural way to handle XML in this context (Inkscape extensions are written in python, using the DOM model, and requiring lxml(*)).
To put it another way, if you're writing DOM based XML code then you're familiar with XPath and expect to be able to use it. Say, for an HTML example, I want the captions of all the tables within the content element with headers which mention "precipitation":
doc.xpath("//*[@id='content']//caption[..//th[contains(., 'precipitation')]/text()")
Could you write code to find them without XPath? Of course. Should you need to? I don't think so. You'd certainly never have to write such code in Java :-/
(*) which makes you wonder how they distribute their extensions with the Windows version of Inkscape. I suspect they distribute python with inkscape in a subdirectory of the inkscape directory... :-)
Cheers -Terry
> The present code at rev 19 is completely independent of lxml and will
> work on Py3k. Alas, PIL has not yet been ported to Py3k, so we are
> still better off using Py2k for this project.
Doh. rev 20 uses Qt instead of PIL for image dimension determination, although PIL is still preferred as with PIL we also remove the excessively large transparent borders Inkscape puts on the image when the callout is near / over the edge of the screenshot. So:
PIL present -> no rescaling and borders trimmed
PIL absent and Qt present -> no rescaling, borders NOT trimmed
PIL absent and Qt absent -> image rescaling may occur unless screen shot size matches template placeholder. Borders not trimmed.
Cheers -Terry
> PIL present -> no rescaling and borders trimmed
rescaling be bad, of course, we want 1:1 pixel mapping on the annotated image from the screenshot.
Cheers -Terry
Appears to be so, my windows PortableInkscape 0.48 folder has a python
sub-tree occupying some 22mb. Running its python.exe, with Inkscape
dir in PATH, sez "Python 2.6.5 (r265:79096, Mar 19 2010, 21:48:26)
[MSC v.1500 32 bit (Intel)] on win32"
-matt
> Doh. rev 20 uses Qt instead of PIL for image dimension determination, although PIL is still preferred...
Thanks for this. As I'll explain in another thread, today I shall
move development of this code into Leo's core.
Edward