From: Matthias Kramm Date: Fri, 14 Aug 2009 18:50:03 +0000 (+0200) Subject: added test for text selection functionality X-Git-Tag: version-0-9-1~306 X-Git-Url: http://git.asbjorn.biz/?p=swftools.git;a=commitdiff_plain;h=5c336b4f7017fb121fc2cbe2a02c1fa56986b560 added test for text selection functionality --- diff --git a/lib/devices/polyops.c b/lib/devices/polyops.c index 70fd99b..fea200b 100644 --- a/lib/devices/polyops.c +++ b/lib/devices/polyops.c @@ -320,6 +320,10 @@ void polyops_drawchar(struct _gfxdevice*dev, gfxfont_t*font, int glyphnr, gfxcol /* notable change in character size: character was clipped TODO: how to deal with diagonal cuts? */ + msg(" Character %d was clipped: (%f,%f,%f,%f) -> (%f,%f,%f,%f)", + glyphnr, + bbox.xmin,bbox.ymin,bbox.xmax,bbox.ymax, + bbox2.xmin,bbox2.ymin,bbox2.xmax,bbox2.ymax); polyops_fill(dev, glyph, color); } else { if(i->out) i->out->drawchar(i->out, font, glyphnr, color, matrix); diff --git a/spec/edit_spec.py b/spec/edit_spec.py index 2ab4453..b270d18 100755 --- a/spec/edit_spec.py +++ b/spec/edit_spec.py @@ -60,7 +60,12 @@ class AreaPlain(AreaCheck): 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 @@ -146,6 +151,7 @@ class Model: 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\"") @@ -176,6 +182,8 @@ class Model: 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): @@ -205,6 +213,8 @@ class Model: 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() @@ -362,6 +372,10 @@ class EntryPanel(scrolled.ScrolledPanel): 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) @@ -381,13 +395,20 @@ class EntryPanel(scrolled.ScrolledPanel): 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) - if isinstance(check,AreaPlain): + hbox.Add(lb, 0, wx.ALIGN_LEFT|wx.ALL, 5) + if isinstance(check, AreaPlain): setdefault(lb,0) - else: + elif isinstance(check, AreaNotPlain): setdefault(lb,1) - hbox.Add(lb, 0, wx.ALIGN_LEFT|wx.ALL, 5) + 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) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index d1c1458..1ee0b39 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -41,16 +41,20 @@ class ConversionFailed < Exception end class Area - def initialize(x1,y1,x2,y2,data) - @x1,@y1,@x2,@y2 = x1,y1,x2,y2 - @rgb = Array.new(data.size/3) do |i| data.slice(i*3,3) end + def initialize(x1,y1,x2,y2,file) + @x1,@y1,@x2,@y2,@file = x1,y1,x2,y2,file end def should_be_plain_colored + @rgb = @file.get_area(@x1,@y1,@x2,@y2) unless @rgb @rgb.minmax == [@rgb[0],@rgb[0]] or raise AreaError.new(self,"is not plain colored") end def should_not_be_plain_colored + @rgb = @file.get_area(@x1,@y1,@x2,@y2) unless @rgb @rgb.minmax != [@rgb[0],@rgb[0]] or raise AreaError.new(self,"is plain colored") end + def should_contain_text(text) + @file.get_text(@x1,@y1,@x2,@y2) == text or raise AreaError.new(self, "doesn't contain text #{text}") + end def to_s "(#{@x1},#{@y1},#{@x2},#{@y2})" end @@ -92,15 +96,21 @@ class DocFile @filename = filename @page = page end - def load() + def convert() + return if @swfname @swfname = @filename.gsub(/.pdf$/i,"")+".swf" + `pdfinfo #{@filename}` =~ /Page size:\s*([0-9]+) x ([0-9]+) pts/ + width,height = $1,$2 + dpi = (72.0 * 612 / width.to_i).to_i + output = `pdf2swf --flatten -s zoom=#{dpi} -p #{@page} #{@filename} -o #{@swfname} 2>&1` + #output = `pdf2swf -s zoom=#{dpi} --flatten -p #{@page} #{@filename} -o #{@swfname} 2>&1` + raise ConversionFailed.new(output,@swfname) unless File.exists?(@swfname) + end + def render() + return if @img + convert() @pngname = @filename.gsub(/.pdf$/i,"")+".png" begin - `pdfinfo #{@filename}` =~ /Page size:\s*([0-9]+) x ([0-9]+) pts/ - width,height = $1,$2 - dpi = (72.0 * 612 / width.to_i).to_i - output = `pdf2swf -s zoom=#{dpi} --flatten -p #{@page} #{@filename} -o #{@swfname} 2>&1` - raise ConversionFailed.new(output,@swfname) unless File.exists?(@swfname) output = `swfrender --legacy #{@swfname} -o #{@pngname} 2>&1` raise ConversionFailed.new(output,@pngname) unless File.exists?(@pngname) @img = Magick::Image.read(@pngname).first @@ -109,21 +119,30 @@ class DocFile `rm -f #{@pngname}` end end - def area_at(x1,y1,x2,y2) - self.load() + def get_text(x1,y1,x2,y2) + self.convert() + puts "swfstrings -x #{x1} -y #{y1} -W #{x2-x1} -H #{y2-y1} #{@swfname}" + puts `swfstrings -x #{x1} -y #{y1} -W #{x2-x1} -H #{y2-y1} #{@swfname}` + `swfstrings -x #{x1} -y #{y1} -W #{x2-x1} -H #{y2-y1} #{@swfname}` + end + def get_area(x1,y1,x2,y2) + self.render() data = @img.export_pixels(x1, y1, x2-x1, y2-y1, "RGB") - return Area.new(x1,y1,x2,y2,data) + Array.new(data.size/3) do |i| data.slice(i*3,3) end + end + def area_at(x1,y1,x2,y2) + return Area.new(x1,y1,x2,y2,self) end def width() - self.load() + self.render() return @img.columns end def height() - self.load() + self.render() return @img.rows end def pixel_at(x,y) - self.load() + self.render() data = @img.export_pixels(x, y, 1, 1, "RGB") return Pixel.new(x,y,data) end diff --git a/spec/textarea.pdf b/spec/textarea.pdf new file mode 100644 index 0000000..196a613 Binary files /dev/null and b/spec/textarea.pdf differ diff --git a/spec/textarea.py b/spec/textarea.py new file mode 100644 index 0000000..4a77043 --- /dev/null +++ b/spec/textarea.py @@ -0,0 +1,26 @@ +from sys import * +from pdflib_py import * +import md5 +p = PDF_new() +PDF_open_file(p, "textarea.pdf") + +PDF_set_parameter(p, "usercoordinates", "true") + +PDF_set_info(p, "Creator", "smalltext.py") +PDF_begin_page(p, 612, 3000) +font = PDF_load_font(p, "Courier", "host", "") + +PDF_setfont(p, font, 12.0) +i = 0 +# the idea is to overflow the placetext matrix once, so that +# we have at least two different ty values +for y in range(3000 / 9): + PDF_set_text_pos(p, 0, y*9); + text = "".join([md5.md5(str(i+j*732849)).hexdigest() for j in range(3)]) + print text + PDF_show(p, text) + i = i + 1 + +PDF_end_page(p) +PDF_close(p) +PDF_delete(p); diff --git a/spec/textarea.spec.rb b/spec/textarea.spec.rb new file mode 100644 index 0000000..dcaeea6 --- /dev/null +++ b/spec/textarea.spec.rb @@ -0,0 +1,10 @@ +require File.dirname(__FILE__) + '/spec_helper' + +describe "pdf conversion" do + convert_file "textarea.pdf" do + area_at(460,94,610,106).should_contain_text '97924ff65f9dfc75450ba' + area_at(467,373,525,384).should_contain_text '29cf24e47' + area_at(474,592,543,601).should_contain_text '0afa27099a' + area_at(59,798,131,808).should_contain_text '4c28e489b4' + end +end diff --git a/src/swfdump.c b/src/swfdump.c index a082a5f..12c653d 100644 --- a/src/swfdump.c +++ b/src/swfdump.c @@ -415,10 +415,24 @@ void textcallback(void*self, int*glyphs, int*ypos, int nr, int fontid, int fonts printf("\n"); } -void handleText(TAG*tag) +void handleText(TAG*tag, char*prefix) { printf("\n"); - swf_ParseDefineText(tag,textcallback, 0); + if(placements) { + swf_SetTagPos(tag, 0); + swf_GetU16(tag); + swf_GetRect(tag, 0); + swf_ResetReadBits(tag); + MATRIX m; + swf_GetMatrix(tag, &m); + printf("%s| Matrix\n",prefix); + printf("%s| %5.3f %5.3f %6.2f\n", prefix, m.sx/65536.0, m.r1/65536.0, m.tx/20.0); + printf("%s| %5.3f %5.3f %6.2f\n", prefix, m.r0/65536.0, m.sy/65536.0, m.ty/20.0); + swf_SetTagPos(tag, 0); + } + if(showtext) { + swf_ParseDefineText(tag,textcallback, 0); + } } void handleDefineSound(TAG*tag) @@ -1363,10 +1377,7 @@ int main (int argc,char ** argv) printf(" URL: %s\n", s); } else if(tag->id == ST_DEFINETEXT || tag->id == ST_DEFINETEXT2) { - if(showtext) - handleText(tag); - else - printf("\n"); + handleText(tag, myprefix); } else if(tag->id == ST_DEFINESCALINGGRID) { U16 id = swf_GetU16(tag); diff --git a/src/swfstrings.c b/src/swfstrings.c index bfca4fa..1bbd52f 100644 --- a/src/swfstrings.c +++ b/src/swfstrings.c @@ -131,13 +131,21 @@ void textcallback(void*self, int*glyphs, int*advance, int nr, int fontid, int fo for(t=0;t x+w || starty > y+h) { + if(x|y|w|h) { + if(xx < x || yy < y || xx > x+w || yy > y+h) { /* outside of bounding box */ - continue; + ///printf("(%d+%d,%d) -> (%d,%d)\n", startx, advance[t]/20, starty, xx, yy); + if(t==nr-1) return; + else continue; } } @@ -178,6 +186,7 @@ void fontcallback(void*self,U16 id,U8 * name) swf_FontFree(font); } +TAG**id2tag = 0; int main (int argc,char ** argv) { @@ -199,6 +208,8 @@ int main (int argc,char ** argv) if(!h) h = (swf.movieSize.ymax - swf.movieSize.ymin) / 20; } + id2tag = malloc(sizeof(TAG)*65536); + fontnum = 0; swf_FontEnumerate(&swf,&fontcallback1, 0); fonts = (SWFFONT**)malloc(fontnum*sizeof(SWFFONT*)); @@ -209,7 +220,22 @@ int main (int argc,char ** argv) while (tag) { if(swf_isTextTag(tag)) { - swf_ParseDefineText(tag, textcallback, 0); + id2tag[swf_GetDefineID(tag)] = tag; + } else if(swf_isPlaceTag(tag)) { + SWFPLACEOBJECT po; + swf_SetTagPos(tag, 0); + swf_GetPlaceObject(tag, &po); + if(!po.move && id2tag[po.id]) { + TAG*text = id2tag[po.id]; + swf_SetTagPos(text, 0); + swf_GetU16(text); + swf_GetRect(text, NULL); + swf_ResetReadBits(text); + MATRIX m,tm; + swf_GetMatrix(text, &tm); + swf_MatrixJoin(&m, &po.matrix, &tm); + swf_ParseDefineText(text, textcallback, &m); + } } tag = tag->next; }