+#!/usr/bin/env python
import wx
import wx.lib.scrolledpanel as scrolled
import os
return "pixel at (%d,%d)" % (self.x,self.y)
def right(self):
return ""
+ def verifies(self, model):
+ return True
class PixelColorCheck(Check):
def __init__(self, x,y, color):
self.color = color
def right(self):
return "is of color 0x%06x" % self.color
+ def verifies(self, model):
+ p = model.getPixel(self.x,self.y)
+ val = p[0]<<16 | p[1]<<8 | p[2]
+ return val == self.color
class TwoPixelCheck(Check):
def __init__(self, x,y, x2,y2):
return "pixel at (%d,%d)" % (self.x2,self.y2)
class PixelBrighterThan(TwoPixelCheck):
- pass
+ def verifies(self, model):
+ p1 = model.getPixel(self.x,self.y)
+ p2 = model.getPixel(self.x2,self.y2)
+ val1 = p1[0] + p1[1] + p1[2]
+ val2 = p2[0] + p2[1] + p2[2]
+ return val1 > val2
class PixelDarkerThan(TwoPixelCheck):
pass
class AreaNotPlain(AreaCheck):
pass
-checktypes = [PixelColorCheck,PixelBrighterThan,PixelDarkerThan,PixelEqualTo,AreaPlain,AreaNotPlain]
+class AreaText(AreaCheck):
+ def __init__(self, x,y, x2, y2, text=""):
+ AreaCheck.__init__(self,x,y,x2,y2)
+ self.text = text
+
+checktypes = [PixelColorCheck,PixelBrighterThan,PixelDarkerThan,PixelEqualTo,AreaPlain,AreaNotPlain,AreaText]
+
+global TESTMODE
def convert_to_ppm(pdf):
print pdf
f.close()
width,heigth = re.compile(r"Page size:\s*([0-9]+) x ([0-9]+) pts").findall(info)[0]
dpi = int(72.0 * 612 / int(width))
- os.system("pdftoppm -r "+str(dpi)+" -f 1 -l 1 "+pdf+" test")
+ if TESTMODE:
+ os.system("pdf2swf -s zoom="+str(dpi)+" -p1 "+pdf+" -o test.swf")
+ os.system("swfrender --legacy test.swf -o test.png")
+ os.unlink("test.swf")
+ return "test.png"
+ else:
+ os.system("pdftoppm -r "+str(dpi)+" -f 1 -l 1 "+pdf+" test")
return "test-000001.ppm"
+
class Model:
- def __init__(self, filename, checks):
- self.filename = filename
- self.imgfilename = convert_to_ppm(filename)
+ def __init__(self, specfile, docfile, checks):
+ self.specfile = specfile
+ self.docfile = docfile
+ self.imgfilename = convert_to_ppm(self.docfile)
self.bitmap = wx.Bitmap(self.imgfilename)
self.image = wx.ImageFromBitmap(self.bitmap)
self.width = self.bitmap.GetWidth()
self.appendListeners = []
self.drawModeListeners = []
self.drawmode = PixelColorCheck
+
+ def close(self):
+ try: os.unlink(self.imgfilename)
+ except: pass
+
+ def getPixel(self,x,y):
+ return (self.image.GetRed(x,y), self.image.GetGreen(x,y), self.image.GetBlue(x,y))
def setdrawmode(self, mode):
self.drawmode = mode
@staticmethod
def load(filename):
- path = os.path.splitext(filename)[0]+".rb"
+ # convenience, allow to do "edit_spec.py file.pdf"
+ p,ext = os.path.splitext(filename)
+ if ext!=".rb":
+ path = p+".rb"
+ if not os.path.isfile(path):
+ path = p+".spec.rb"
+ if not os.path.isfile(path):
+ print "No file %s found, creating new..." % path
+ return Model(path, filename, [])
+ else:
+ path = filename
+
fi = open(path, "rb")
r_file = re.compile(r"^convert_file \"([^\"]*)\"")
r_pixelcolor = re.compile(r"^pixel_at\(([0-9]+),([0-9]+)\).should_be_of_color (0x[0-9a-fA-F]+)")
r_pixelequalto = re.compile(r"^pixel_at\(([0-9]+),([0-9]+)\).should_be_the_same_as pixel_at\(([0-9]+),([0-9]+)\)")
r_areaplain = re.compile(r"^area_at\(([0-9]+),([0-9]+),([0-9]+),([0-9]+)\).should_be_plain_colored")
r_areanotplain = re.compile(r"^area_at\(([0-9]+),([0-9]+),([0-9]+),([0-9]+)\).should_not_be_plain_colored")
+ r_areatext = re.compile(r"^area_at\(([0-9]+),([0-9]+),([0-9]+),([0-9]+)\).should_contain_text '(.*)'")
r_width = re.compile(r"^width.should be ([0-9]+)")
r_height = re.compile(r"^height.should be ([0-9]+)")
r_describe = re.compile(r"^describe \"pdf conversion\"")
if filename:
raise Exception("can't load multi-file specs (in line %d)" % (nr+1))
filename = m.group(1);
- model = Model(filename, [])
+ model = Model(path, filename, [])
continue
m = r_pixelcolor.match(line)
if m: model.append(PixelColorCheck(int(m.group(1)),int(m.group(2)),int(m.group(3),16)));continue
if m: model.append(AreaPlain(int(m.group(1)),int(m.group(2)),int(m.group(3)),int(m.group(4))));continue
m = r_areanotplain.match(line)
if m: model.append(AreaNotPlain(int(m.group(1)),int(m.group(2)),int(m.group(3)),int(m.group(4))));continue
+ m = r_areatext.match(line)
+ if m: model.append(AreaText(int(m.group(1)),int(m.group(2)),int(m.group(3)),int(m.group(4)),m.group(5)));continue
if r_width.match(line) or r_height.match(line):
continue # compatibility
if r_describe.match(line) or r_end.match(line) or r_header.match(line):
return model
def save(self):
- path = os.path.splitext(self.filename)[0]+".rb"
+ path = self.specfile
fi = open(path, "wb")
fi.write("require File.dirname(__FILE__) + '/spec_helper'\n\ndescribe \"pdf conversion\" do\n")
- fi.write(" convert_file \"%s\" do\n" % self.filename)
+ fi.write(" convert_file \"%s\" do\n" % self.docfile)
for check in self.checks:
c = check.__class__
if c == PixelColorCheck:
fi.write(" area_at(%d,%d,%d,%d).should_be_plain_colored\n" % (check.x,check.y,check.x2,check.y2))
elif c == AreaNotPlain:
fi.write(" area_at(%d,%d,%d,%d).should_not_be_plain_colored\n" % (check.x,check.y,check.x2,check.y2))
+ elif c == AreaText:
+ fi.write(" area_at(%d,%d,%d,%d).should_contain_text '%s'\n" % (check.x,check.y,check.x2,check.y2,check.text))
fi.write(" end\n")
fi.write("end\n")
fi.close()
for xx in range(15):
x = self.x+xx-8
if 0<=x<self.model.width and 0<=y<self.model.height:
- color = (self.model.image.GetRed(x,y), self.model.image.GetGreen(x,y), self.model.image.GetBlue(x,y))
+ color = self.model.getPixel(x,y)
else:
color = (0,0,0)
dc.SetPen(wx.Pen(color))
if check:
self.model.delete(check)
else:
- color = self.model.image.GetRed(x,y)<<16 | self.model.image.GetGreen(x,y)<<8 | self.model.image.GetBlue(x,y)
+ p = self.model.getPixel(x,y)
+ color = p[0]<<16|p[1]<<8|p[2]
self.model.append(PixelColorCheck(x,y,color))
else:
if not self.firstclick:
dc.SetBackground(wx.Brush((0,0,0)))
dc.DrawBitmap(self.model.bitmap, 0, 0, False)
+ red = wx.Pen((192,0,0),2)
+
if self.firstclick:
x,y = self.firstclick
if AreaCheck in self.model.drawmode.__bases__:
dc.DrawLine(x,y,self.x,self.y)
for check in self.model.checks:
+ if TESTMODE and not check.verifies(model):
+ dc.SetPen(red)
+ else:
+ dc.SetPen(wx.BLACK_PEN)
if AreaCheck in check.__class__.__bases__:
dc.SetBrush(wx.TRANSPARENT_BRUSH)
dc.DrawRectangle(check.x,check.y,check.x2-check.x,check.y2-check.y)
dc.DrawLine(x,y,x+1,y)
if TwoPixelCheck in check.__class__.__bases__:
dc.DrawLine(x,y,check.x2,check.y2)
+ dc.SetPen(wx.BLACK_PEN)
class EntryPanel(scrolled.ScrolledPanel):
def __init__(self, parent, model):
def delete(self, event):
self.model.delete(self.id2check[event.Id])
+ def text(self, event):
+ check = self.id2check[event.GetEventObject().Id]
+ check.text = event.GetString()
+
def append(self, check):
self.vbox = wx.BoxSizer(wx.VERTICAL)
self.vbox.Add(wx.StaticLine(self, -1, size=(500,-1)), 0, wx.ALL, 5)
self.id2check[button.Id] = check
self.Bind(wx.EVT_BUTTON, self.delete, button)
+ def setdefault(lb,nr):
+ lb.Select(nr);self.Bind(wx.EVT_CHOICE, lambda lb:lb.EventObject.Select(nr), lb)
+
desc = wx.StaticText(self, -1, check.left())
+
hbox.Add(desc, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)
if isinstance(check,AreaCheck):
- choices = ["is plain","is not plain"]
+ choices = ["is plain","is not plain","contains text"]
lb = wx.Choice(self, -1, (100, 50), choices = choices)
hbox.Add(lb, 0, wx.ALIGN_LEFT|wx.ALL, 5)
+ if isinstance(check, AreaPlain):
+ setdefault(lb,0)
+ elif isinstance(check, AreaNotPlain):
+ setdefault(lb,1)
+ else:
+ setdefault(lb,2)
+ tb = wx.TextCtrl(self, -1, check.text, size=(100, 25))
+ self.id2check[tb.Id] = check
+ self.Bind(wx.EVT_TEXT, self.text, tb)
+
+ hbox.Add(tb, 0, wx.ALIGN_LEFT|wx.ALL, 5)
elif isinstance(check,TwoPixelCheck):
choices = ["is the same as","is brighter than","is darker than"]
lb = wx.Choice(self, -1, (100, 50), choices = choices)
hbox.Add(lb, 0, wx.ALIGN_LEFT|wx.ALL, 5)
+ if isinstance(check, PixelEqualTo):
+ setdefault(lb,0)
+ elif isinstance(check, PixelBrighterThan):
+ setdefault(lb,1)
+ elif isinstance(check, PixelDarkerThan):
+ setdefault(lb,2)
elif isinstance(check,PixelColorCheck):
# TODO: color control
pass
- desc = wx.StaticText(self, -1, check.right())
- hbox.Add(desc, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)
-
+ desc2 = wx.StaticText(self, -1, check.right())
+ hbox.Add(desc2, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)
+
self.vbox.Add(hbox)
self.vbox.Add(wx.StaticLine(self, -1, size=(500,-1)), 0, wx.ALL, 5)
self.end = wx.Window(self, -1, size=(1,1))
self.toolbar.Realize()
-#class ScrollFrame(wx.Frame):
+if __name__ == "__main__":
+ from optparse import OptionParser
+ global TESTMODE
+ parser = OptionParser()
+ parser.add_option("-t", "--test", dest="test", help="Test checks against swf", action="store_true")
+ (options, args) = parser.parse_args()
+
+ TESTMODE = options.test
-def getpixels(filename, p):
- model = Model(filename, p)
app = wx.PySimpleApp()
- main = MainFrame(app, model)
- main.Show()
- app.MainLoop()
- return model.pixels
+ model = Model.load(args[0])
-if __name__ == "__main__":
- app = wx.PySimpleApp()
- model = Model.load(sys.argv[1])
main = MainFrame(app, model)
main.Show()
app.MainLoop()
model.save()
+ model.close()