merged final cvs changes to git
[swftools.git] / wx / pdf2swf.py
1 #!/usr/bin/env python
2 # -*- coding: ISO-8859-15 -*-
3
4 import sys
5 import wx
6 import os
7 sys.path+=["../lib/python"]
8 import gfx
9 import images
10
11 basedir = os.getcwd()
12
13 gfx.verbose(3)
14
15 #try:
16 #    gfx.setparameter("wxwindowparams", "1")
17 #except:
18 #    gfx.setoption("wxwindowparams", "1")
19
20 class StaticData:
21     def __init__(self):
22         self.simpleviewer_bitmap = wx.BitmapFromImage(wx.ImageFromData(images.simpleviewer_width,images.simpleviewer_height,images.simpleviewer_data))
23         self.raw_bitmap = wx.BitmapFromImage(wx.ImageFromData(images.raw_width,images.raw_height,images.raw_data))
24         self.motionpaper_bitmap = wx.BitmapFromImage(wx.ImageFromData(images.motionpaper_width,images.motionpaper_height,images.motionpaper_data))
25         self.rfxview_bitmap = wx.BitmapFromImage(wx.ImageFromData(images.rfxview_width,images.rfxview_height,images.rfxview_data))
26 staticdata = None
27
28 HTMLTEMPLATE = """<html>
29 <body style="padding: 0px; margin: 0px">
30 <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
31         width="%(width)s"
32         height="%(height)s"
33         codebase="http://active.macromedia.com/flash5/cabs/swflash.cab#version=%(version)d,0,0,0">
34         <param name="MOVIE" value="%(swffilename)s">
35         <param name="PLAY" value="true">
36         <param name="LOOP" value="true">
37         <param name="QUALITY" value="high">
38         <param name="FLASHVARS" value="%(flashvars)s">
39           <embed src="%(swffilename)s" width="%(width)s" height="%(height)s"
40                  play="true" ALIGN="" loop="true" quality="high"
41                  type="application/x-shockwave-flash"
42                  flashvars="%(flashvars)s"
43                  pluginspage="http://www.macromedia.com/go/getflashplayer">
44           </embed>
45 </object>
46 </body>
47 </html>
48 """
49
50 def error(msg):
51     dlg = wx.MessageDialog(None, msg, "Error", style=wx.OK, pos=wx.DefaultPosition)
52     dlg.ShowModal()
53     dlg.Destroy()
54
55 def savefilestatus(msg):
56     dlg = wx.MessageDialog(None, msg, "Save file status", style=wx.OK, pos=wx.DefaultPosition)
57     dlg.ShowModal()
58     dlg.Destroy()
59
60 def swfcombine(params):
61     exe = "swfcombine"
62     if os.path.sep == '/':
63         locations = [os.path.join(basedir, "swfcombine"), 
64                      "/usr/local/bin/swfcombine",
65                      "/usr/bin/swfcombine"
66                     ]
67     else:
68         locations = [os.path.join(basedir, "swfcombine.exe"), 
69                      "c:\\swftools\\swfcombine.exe"]
70     for e in locations:
71         if os.path.isfile(e):
72             exe = e
73             break
74
75     if hasattr(os,"spawnv"):
76         print "spawnv",exe,params
77         ret = os.spawnv(os.P_WAIT, exe, [exe]+params)
78         if not ret:
79             return
80
81     cmd = exe + " " + (" ".join(params))
82     print "system",cmd
83     ret = os.system(cmd)
84     if ret&0xff00:
85         error("Couldn't execute swfcombine.exe- error code "+str(ret))
86
87 ICON_SIZE = 64
88
89 EVENT_PAGE_CHANGE = 1
90 EVENT_FILE_CHANGE = 2
91 EVENT_STATUS_TEXT = 4
92
93 class ProgressFrame(wx.Dialog):
94     def __init__(self, parent, message=""):
95         wx.Dialog.__init__(self, parent, -1, "Progress", size=(350, 150))
96         panel = wx.Panel(self, -1)
97         self.count = 0
98         
99         self.msg = wx.StaticText(panel, -1, message, (20,25))
100         self.gauge = wx.Gauge(panel, -1, 100, (20, 50), (250, 25))
101
102         self.gauge.SetBezelFace(3)
103         self.gauge.SetShadowWidth(3)
104
105         self.Bind(wx.EVT_WINDOW_DESTROY, self.close, id=wx.ID_CLOSE)
106
107     def setProgress(self, num):
108         self.gauge.SetValue(int(num))
109
110     def close(self, event):
111         print "close"
112
113
114 def swapextension(filename,newext):
115     basename,ext = os.path.splitext(filename)
116     return basename + "." + newext
117
118 def has_different_size_pages(doc):
119     width,height = 0,0
120     for i in range(1,doc.pages+1):
121         page = doc.getPage(i)
122         if i==1:
123             width,height = page.width,page.height
124         else:
125             if abs(width-page.width)>2 or \
126                abs(height-page.height)>2:
127                    return 1
128     return 0
129
130
131 options = []
132 gfx_options = {}
133
134 class Option:
135     def __init__(self, parameter, text, options, default, mapping=None):
136         self.parameter = parameter
137         self.text = text
138         self.options = options
139         self.default = default
140         self.mapping = mapping
141         self.control = None
142         self.enabled = 1
143         self.register()
144
145     def generateControl(self, panel):
146         if type(self.options) == type((0,)):
147             control = wx.Choice(panel, -1, choices=self.options)
148             control.SetSelection(self.default)
149         elif self.options == "slider":
150             control = wx.Slider(panel, -1, self.default, 0, 100, size=(100, -1), style=wx.SL_HORIZONTAL|wx.SL_LABELS|wx.SL_TOP)
151         elif self.options == "spinner":
152             control = wx.SpinCtrl(panel, -1, str(self.default))
153         else:
154             control = wx.Choice(panel, -1, choices=["broken"])
155             control.SetSelection(0)
156
157         self.control = control
158         return self.control
159
160     def getSettings(self):
161         value = ""
162         if type(self.options) == type((0,)):
163             value = self.options[self.control.GetCurrentSelection()]
164             if self.mapping and value in self.mapping:
165                 value = str(self.mapping[value])
166             if value == "yes": 
167                 value = "1"
168             elif value == "no":
169                 value = "0"
170             return {self.parameter:value}
171         elif self.options == "slider" or self.options == "spinner":
172             value = str(self.control.GetValue())
173             return {self.parameter:value}
174
175     def register(self):
176         global options
177         options += [self]
178
179 class Option2(Option):
180
181     def __init__(self, parameter, text, options, default, mapping=None):
182         Option.__init__(self, parameter, text, options, default, mapping)
183         self.enabled = 0
184
185     def generateControl(self, panel):
186         p = wx.Panel(panel, -1)
187         #p.SetOwnBackgroundColour('#ff0000')
188         h = wx.BoxSizer(wx.HORIZONTAL)
189         control = wx.Choice(p, -1, choices=self.options)
190         control.SetSelection(self.default)
191         text = wx.StaticText(p, -1, self.text)
192         h.Add(text,1,wx.EXPAND|wx.ALIGN_LEFT|wx.TOP, 5)
193         h.Add(control,1,wx.EXPAND|wx.ALIGN_RIGHT|wx.ALIGN_TOP)
194         self.control = control
195         if self.enabled:
196             control.Enable()
197         else:
198             control.Disable()
199         p.SetSizer(h)
200         p.Fit()
201         return p 
202     
203     def Disable(self):
204         self.enabled=0
205         if self.control:
206             self.control.Disable()
207
208     def Enable(self):
209         self.enabled=1
210         if self.control:
211             self.control.Enable()
212     
213     def getSettings(self):
214         if not self.enabled:
215             return {}
216         return Option.getSettings(self)
217
218 class ChooseAndText(Option):
219     def __init__(self, parameter, text, options, default, editselection, textvalue=""):
220         Option.__init__(self, parameter, text, options, default)
221         self.editselection = editselection
222         self.selection = default
223         self.textvalue = textvalue
224         self.enabled = 0
225         self.choice = None
226
227     def generateControl(self, panel):
228         p = wx.Panel(panel, -1)
229         h = wx.BoxSizer(wx.HORIZONTAL)
230         control = wx.Choice(p, -1, choices=self.options)
231         p.Bind(wx.EVT_CHOICE, self.OnChoice, control)
232         control.SetSelection(self.default)
233         text = wx.StaticText(p, -1, self.text)
234         if self.selection == self.editselection:
235             edittext = wx.TextCtrl(p, -1, self.textvalue)
236             self.textvalue = ""
237         else:
238             edittext = wx.TextCtrl(p, -1, "")
239             edittext.Disable()
240         p.Bind(wx.EVT_TEXT, self.OnText, edittext)
241         h.Add(text,1,wx.EXPAND|wx.ALIGN_LEFT|wx.TOP, 5)
242         h.Add(control,1,wx.EXPAND|wx.ALIGN_RIGHT)
243         h.Add(edittext,1,wx.EXPAND|wx.ALIGN_RIGHT)
244         self.choice = control
245         self.edittext = edittext
246         if self.enabled:
247             control.Enable()
248         else:
249             control.Disable()
250         p.SetSizer(h)
251         p.Fit()
252         return p 
253
254     def OnText(self, event):
255         text = self.edittext.GetValue()
256         text2 = "".join(c for c in text if c.isdigit())
257         if text2!=text:
258             self.edittext.SetValue(text2)
259
260     def OnChoice(self, event):
261         self.selection = self.choice.GetCurrentSelection()
262         if self.selection != self.editselection:
263             if not self.textvalue and self.edittext.GetValue():
264                 self.textvalue = self.edittext.GetValue()
265             self.edittext.SetValue("")
266             self.edittext.Disable()
267         else:
268             if self.textvalue and not self.edittext.GetValue():
269                 self.edittext.SetValue(self.textvalue)
270                 self.textvalue = ""
271             self.edittext.Enable()
272     
273     def Disable(self):
274         self.enabled=0
275         if not self.choice:
276             return
277         self.choice.Disable()
278         self.edittext.Disable()
279
280     def Enable(self):
281         self.enabled=1
282         if not self.choice:
283             return
284         self.choice.Enable()
285         if self.choice.GetCurrentSelection() == self.editselection:
286             if self.textvalue and not self.edittext.GetValue():
287                 self.edittext.SetValue(self.textvalue)
288                 self.textvalue = ""
289             self.edittext.Enable()
290         else:
291             self.edittext.Disable()
292     
293     def getSettings(self):
294         if not self.enabled:
295             return {}
296         if self.choice.GetCurrentSelection() != self.editselection:
297             value = self.options[self.choice.GetCurrentSelection()]
298         else:
299             value = self.edittext.GetValue().strip()
300         return {self.parameter:value}
301
302 class TextOption:
303     def __init__(self, parameter, label, default=""):
304         self.parameter = parameter
305         self.label = label
306         self.default = default
307         self.register()
308
309     def generateControl(self, panel):
310         v = wx.BoxSizer(wx.VERTICAL)
311         self.control = wx.TextCtrl(panel, -1, self.default, size=(250, -1))
312         self.control.Fit()
313         return self.control
314
315     def getSettings(self):
316         settings = {}
317         for items in self.control.GetValue().split(" "):
318             if "=" in items:
319                 l = items.split("=")
320                 if len(l) == 2:
321                     settings[l[0]] = l[1]
322         return settings
323
324     def register(self):
325         global options
326         options += [self]
327
328 class RadioOption(Option):
329     def __init__(self, text, options):
330         self.text = text
331         self.options = options
332         self.selected = "==nothing=="
333         self.radios = []
334         self.register()
335         
336     def generateControl(self, panel):
337         control = wx.Panel(panel, -1)
338         vsplit = wx.BoxSizer(wx.VERTICAL)
339         for i in range(len(self.options)/2):
340             text = self.options[i*2]
341             if i == 0:
342                 c = wx.RadioButton(control, -1, text, style=wx.RB_GROUP)
343             else:
344                 c = wx.RadioButton(control, -1, text)
345             control.Bind(wx.EVT_RADIOBUTTON, self.OnRadio, c)
346             self.radios += [c]
347             vsplit.Add(c)
348         control.SetSizer(vsplit)
349         control.Fit()
350         self.control = control
351         return control
352
353     def OnRadio(self, event):
354         self.selected = event.GetEventObject().GetLabel()
355
356     def getSettings(self):
357         for i in range(len(self.options)/2):
358             if self.options[i*2] == self.selected:
359                 return self.options[i*2+1]
360         return self.options[1]
361
362 class BitmapWindow(wx.Window):
363     def __init__(self, parent, image):
364         wx.Window.__init__(self, parent, -1)
365         self.image = image
366         self.SetMinSize((image.GetWidth()+2, image.GetHeight()+2))
367         self.SetMaxSize((image.GetWidth()+2, image.GetHeight()+2))
368         self.SetSize((image.GetWidth()+2, image.GetHeight()+2))
369         self.Bind(wx.EVT_PAINT, self.OnPaint)
370         self.Update()
371     def OnPaint(self, event):
372         dc = wx.PaintDC(self)
373         self.Draw(dc)
374     def Draw(self,dc=None):
375         if not dc:
376             dc = wx.ClientDC(self)
377         dc.DrawRectangleRect((0, 0, self.image.GetWidth()+2, self.image.GetHeight()+2))
378         dc.DrawBitmap(self.image, 1, 1, False)
379
380 class ImageRadioOption(Option):
381     def __init__(self, text, options):
382         self.text = text
383         self.options = options
384         self.selected = "==nothing=="
385         self.radios = []
386         self.register()
387         self.ids = []
388         
389     def generateControl(self, panel):
390         control = wx.Panel(panel, -1)
391         vsplit = wx.BoxSizer(wx.VERTICAL)
392         first = 1
393         for image,text,params,selected,extraoptions in self.options:
394             hsplit = wx.BoxSizer(wx.HORIZONTAL)
395
396             v = wx.BoxSizer(wx.VERTICAL)
397
398             name,text = text.split("- ")
399
400             c = wx.CheckBox(control, -1, name)
401             control.Bind(wx.EVT_CHECKBOX, self.OnRadio, c)
402
403             # radio buttons crash windows when clicked on- even without event bindings.
404             # This is caused by the subpanel which is created for extra options
405             # (I tried this with a empty Panel(), and even that crashed)
406             #if first:
407             #    c = wx.RadioButton(control, -1, name, style=wx.RB_GROUP)
408             #else:
409             #    c = wx.RadioButton(control, -1, name)
410             #control.Bind(wx.EVT_RADIOBUTTON, self.OnRadio, c)
411
412             self.ids += [c.GetId()]
413
414             first = 0
415
416             if "disable" in text:
417                 c.Enable(False)
418             if selected:
419                 self.selected = c.GetId()
420                 c.SetValue(True)
421             else:
422                 c.SetValue(False)
423             self.radios += [c]
424
425             bitmap = BitmapWindow(control, image)
426             t = wx.StaticText(control, -1, text, size=(400,50))
427             
428             v.Add(c, 0, wx.EXPAND)
429             v.Add(t, 0, wx.EXPAND|wx.LEFT, 20)
430            
431             for o in extraoptions:
432                 cx = o.generateControl(control)
433                 if selected:
434                     o.Enable()
435                 else:
436                     o.Disable()
437                 v.Add(cx, 0, wx.EXPAND|wx.LEFT, 20)
438             
439             v.SetMinSize((330,170))
440             
441             hsplit.Add(bitmap, 0, wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.ALIGN_TOP, 5)
442             hsplit.Add(v, 0, wx.EXPAND)
443             vsplit.Add(hsplit, 0, wx.EXPAND)
444
445         control.SetSizer(vsplit)
446         control.Fit()
447         self.control = control
448         return vsplit
449
450     def OnRadio(self, event):
451         self.selected = event.GetEventObject().GetId()
452         for c in self.radios:
453             if c.GetId() == self.selected:
454                 c.SetValue(1)
455             else:
456                 c.SetValue(0)
457         i = 0
458         for image,text,params,selected,extraoptions in self.options:
459             if self.ids[i] == self.selected:
460                 for xo in extraoptions:
461                     xo.Enable()
462                 pass
463             else:
464                 for xo in extraoptions:
465                     xo.Disable()
466                 pass
467             i = i + 1
468         event.ResumePropagation(0)
469
470     def getSettings(self):
471         i = 0
472         for image,text,params,s,extraoptions in self.options:
473             id = self.ids[i]
474             i = i + 1
475             if id == self.selected:
476                 return params
477         return {}
478
479
480 class OptionFrame(wx.Dialog):
481
482     def __init__(self, parent):
483         wx.Dialog.__init__(self, parent, -1, "Options")
484
485         #self.nb = wx.Notebook(self, -1)#, wx.Point(0,0), wx.Size(0,0), wxNB_FIXEDWIDTH)
486         self.nb = wx.Notebook(self, -1)
487
488         self.needreload = 0
489         
490         options0 = [RadioOption('Rendering mode', 
491                         ["Convert polygons to polygons and fonts to fonts", {},
492                          "Convert fonts to fonts, everything else to bitmaps", {"poly2bitmap":"1"},
493                          "Convert everthing to bitmaps", {"poly2bitmap":"1", "bitmapfonts":"1"}
494                         ])]
495
496         mp_options = []
497         sv_options = [Option2('flashversion', 'Flash version:', ('4','5','6','7','8'), 2),
498                       Option2('transparent', 'Make SWF file transparent:', ('no','yes'), 0),
499                      ]
500
501         raw_options = [Option2('flashversion', 'Flash version:', ('4','5','6','7','8','9'), 2),
502                        Option2('insertstop', 'Insert stop after each frame:', ('no','yes'), 0),
503                        Option2('transparent', 'Make SWF file transparent:', ('no','yes'), 0),
504                       ]
505         rfxview_options = [ChooseAndText('rfxwidth', 'Width:', ('same as PDF','fullscreen','custom'),1,2,"600"),
506                            ChooseAndText('rfxheight', 'Height:', ('same as PDF','fullscreen','custom'),1,2,"800"),
507                            Option2('rfxzoomtype', 'Initial zoom level:', ('Original resolution','Show all','Maximum width/height'),2),
508                           ]
509
510         options4 = [ImageRadioOption('Select Paging GUI', 
511                         [(staticdata.raw_bitmap, "No Viewer- The SWF will be in \"raw\" format, with each page a seperate frame. Use this if you want to add a viewer yourself afterwards.", {}, 0, raw_options),
512                          (staticdata.simpleviewer_bitmap, "SimpleViewer- A tiny viewer, which attaches directly to the SWF, and provides small previous/next buttons in the upper left corner", {"simpleviewer":"1", "insertstop":"1"}, 0, sv_options),
513                          (staticdata.rfxview_bitmap, "rfxView- A more sophisticated viewer with zooming and scrolling.", {"rfxview":"1", "flashversion":"8"}, 1, rfxview_options),
514                          #(staticdata.motionpaper_bitmap, "MotionPaper- A highly sophisticated viewer with page flipping. (disabled in this evaluation version)", {}, 0, mp_options),
515                          #(staticdata.motionpaper_bitmap, "Your advertisement here- Are you are company who developed a viewer for pdf2swf, or who offers commercial PDF hosting service? Place your advertisement or demo viewer here, or allow pdf2swf to upload SWFs directly to your site! contact sales@swftools.org for details.", {}, 0, mp_options),
516                         ])]
517
518         options1 = [Option('zoom', 'Resolution (in dpi):', "spinner", 72),
519                     Option('fontquality', 'Font quality:', "slider", 20),
520                     Option('storeallcharacters', 'Insert full fonts in SWF file:', ('no','yes'), 0),
521                     Option('splinequality', 'Polygon quality:', "slider", 100),
522                     Option('jpegquality', 'JPEG quality:', "slider", 75),
523                     Option('jpegsubpixels', 'JPEG image resolution:', ('same as in PDF', '1x', '2x', '4x'), 0, {"same as in PDF": 0, "1x": 1, "2x": 2, "3x": 3}),
524                     Option('ppmsubpixels', 'non-JPEG image resolution:', ('same as in PDF', '1x', '2x', '4x'), 0, {"same as in PDF": 0, "1x": 1, "2x": 2, "3x": 3}),
525                    ]
526         
527         
528         options3 = [TextOption('_additional_', 'Additional options')]
529
530         panel1 = [('Rendering options', options0,''),
531                   ('Quality',options1,'v')]
532         panel3 = [('Select paging GUI', options4,'')]
533         panel4 = [('Additional options', options3,'')]
534
535         panels = [('Quality', panel1),
536                   ('Viewer', panel3),
537                   ('Advanced', panel4)]
538
539         for name,poptions in panels:
540             panel = wx.Panel(self.nb, -1)
541             self.nb.AddPage(panel, name)
542         
543             vsplit = wx.BoxSizer(wx.VERTICAL)
544       
545             for name,options,align in poptions:
546                 optiongroup = wx.StaticBox(panel, -1, name)
547                 optiongroupsizer= wx.StaticBoxSizer(optiongroup, wx.VERTICAL)
548                 optiongroup.SetSizer(optiongroupsizer)
549
550                 if align == 'v':
551                     grid = wx.GridSizer(rows=len(options), cols=2, hgap=3, vgap=3)
552                     optiongroupsizer.Add(grid, 1, wx.EXPAND, 0)
553                 else:
554                     grid = wx.GridSizer(rows=len(options), cols=1, hgap=3, vgap=3)
555                     optiongroupsizer.Add(grid, 1, wx.EXPAND, 0)
556
557                 for option in options:
558                     if align=='v':
559                         t = wx.StaticText(panel, -1, option.text)
560                         grid.Add(t, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT)
561                     optionbox = option.generateControl(panel)
562                     grid.Add(optionbox, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT)
563
564                 vsplit.Add(optiongroupsizer, 0, wx.EXPAND, 0)
565
566             #hs = wx.BoxSizer(wx.HORIZONTAL)
567             #hs.Add(gobutton, 0, wx.ALIGN_CENTER, 0)
568             gobutton = wx.Button(panel, -1, "Apply")
569             self.Bind(wx.EVT_BUTTON, self.Apply, gobutton)
570
571             vsplit.Add(gobutton, 0, wx.ALIGN_CENTER|wx.ALL, 0)
572             
573             panel.SetSizer(vsplit)
574             panel.Fit()
575
576         self.nb.Fit()
577
578         self.Fit()
579         
580
581     def updateOptions(self):
582         global options,gfx_options
583         a = []
584
585         # FIXME: we clear *our* options- but gfx will still have
586         #        stored the old ones. Critical for options in the "imageradio" section.
587         gfx_options.clear()
588         i = 0
589         print "----- options ------"
590         for option in options:
591             for k,v in option.getSettings().items():
592                 gfx_options[k] = v
593                 gfx.setparameter(k,v)
594                 print k,v
595             i = i + 1
596
597         # TODO: filter out "global" options, and do this only if
598         # pdf layer is affected
599     
600     def Apply(self, event):
601         self.updateOptions()
602         self.Hide()
603         self.needreload = 1
604
605
606 class State:
607     def __init__(self):
608         self.pdf = None
609         self.page = None
610         self.pagenr = 1
611         self.pagebitmap = None
612         self.bitmap_width = 0
613         self.bitmap_height = 0
614         self.bitmap_page = 0
615         self.filename = None
616         self.status_text = None
617         self.lastsavefile = "output.swf"
618         self.lasthtmlfile = "index.html"
619
620         self.listeners = []
621
622     def onEvent(self,event_type, function):
623         self.listeners += [(event_type,function)]
624     def loadPDF(self,filename):
625         self.filename = filename
626         self.lastsavefile = swapextension(filename,"swf")
627         self.lasthtmlfile = swapextension(filename,"html")
628
629         self.pdf = gfx.open("pdf",filename)
630         if(has_different_size_pages(self.pdf)):
631             # just let the user know- for now, we can't handle this properly
632             dlg = wx.MessageDialog(app.frame, """In this PDF, width or height are not the same for each page. This might cause problems if you export pages of different dimensions into the same SWF file.""", "Notice", style=wx.OK, pos=wx.DefaultPosition)
633             dlg.ShowModal()
634             dlg.Destroy()
635
636         self.changePage(1)
637
638         for type,f in self.listeners:
639             if type&EVENT_PAGE_CHANGE or type&EVENT_FILE_CHANGE:
640                 f()
641         self.setStatus("File loaded successfully.")
642
643     def saveSWF(self, filename, progress, pages=None, html=0):
644         if html:
645             basename,ext = os.path.splitext(filename)
646             if not ext:
647                 html = basename + ".html"
648                 filename = basename + ".swf"
649             elif ext.lower() != ".swf":
650                 html = filename
651                 filename = basename + ".swf"
652             else:
653                 html = basename + ".html"
654                 filename = filename
655
656         steps = 100.0 / (self.pdf.pages*2 + 3)
657         pos = [0]
658         
659         self.lastsavefile = filename
660         if html:
661             self.lasthtmlfile = html
662
663         swf = gfx.SWF()
664         for k,v in gfx_options.items():
665             swf.setparameter(k,v)
666         if pages is None:
667             pages = range(1,self.pdf.pages+1)
668         pdfwidth,pdfheight=0,0
669         for pagenr in pages:
670             page = self.pdf.getPage(pagenr)
671             pdfwidth = page.width
672             pdfheight = page.height
673             swf.startpage(page.width, page.height)
674             page.render(swf)
675             swf.endpage()
676         swf.save(filename)
677
678         if gfx_options.get("rfxview",None):
679             rfxview = os.path.join(basedir, "rfxview.swf")
680             if not os.path.isfile(rfxview):
681                 error("File rfxview.swf not found in working directory")
682             else:
683                 swfcombine([rfxview,"viewport="+filename,"-o",filename])
684         
685         if html:
686             version = int(gfx_options.get("flashversion", "8"))
687             swf = gfx.open("swf", filename)
688             page1 = swf.getPage(1)
689
690             width,height = str(page1.width),str(page1.height)
691
692
693             w = gfx_options.get("rfxwidth","")
694             if w == "fullscreen":    width = "100%"
695             elif w == "same as PDF": width = pdfwidth+40
696             elif w.isdigit():        width = w
697             else:                    width = pdfwidth
698             
699             h = gfx_options.get("rfxheight","")
700             if h == "fullscreen":    height = "100%"
701             elif h == "same as PDF": height = pdfheight+70
702             elif h.isdigit():        height = h
703             else:                    height = pdfwidth
704
705             flashvars = ""
706             zoomtype = gfx_options.get("rfxzoomtype","")
707             if zoomtype=="Original resolution":
708                 flashvars = "zoomtype=1"
709             elif zoomtype=="Show all":
710                 flashvars = "zoomtype=2"
711             elif zoomtype=="Maximum width/height":
712                 flashvars = "zoomtype=3"
713
714             swffilename = os.path.basename(filename)
715             fi = open(html, "wb")
716             fi.write(HTMLTEMPLATE % locals())
717             fi.close()
718
719
720     def changePage(self,page):
721         self.pagenr = page
722         self.page = self.pdf.getPage(self.pagenr)
723         for type,f in self.listeners:
724             if type&EVENT_PAGE_CHANGE:
725                 f()
726
727     def getPageIcon(self,pagenr):
728         page = self.pdf.getPage(pagenr)
729         return wx.BitmapFromImage(wx.ImageFromData(ICON_SIZE,ICON_SIZE,page.asImage(ICON_SIZE,ICON_SIZE)))
730         #return wx.BitmapFromImage(wx.ImageFromData(8,8,"0"*(64*3)))
731
732     def getPageImage(self, width, height):
733         if self.bitmap_width == width and self.bitmap_height == height and self.bitmap_page == self.pagenr:
734             return self.pagebitmap
735         else:
736             self.bitmap_width = width
737             self.bitmap_height = height
738             self.bitmap_page = self.pagenr
739             self.pagebitmap = wx.BitmapFromImage(wx.ImageFromData(width,height,self.page.asImage(width,height)))
740             #self.pagebitmap = wx.BitmapFromImage(wx.ImageFromData(8,8,"0"*(64*3)))
741             return self.pagebitmap
742
743     def setStatus(self,text):
744         self.status_text = text
745         for type,f in self.listeners:
746             if type&EVENT_STATUS_TEXT:
747                 f()
748
749 state = State()
750
751 class PageListWidget(wx.ListCtrl):
752     def __init__(self,parent):
753         wx.ListCtrl.__init__(self,parent,style=wx.LC_ICON|wx.LC_AUTOARRANGE)
754         #self.SetMinSize((ICON_SIZE+8,-1))
755         #self.SetMaxSize((ICON_SIZE+8,-1))
756         #self.SetSize((ICON_SIZE+8,-1))
757         self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.SelectItem)
758         state.onEvent(EVENT_FILE_CHANGE, self.reload)
759         state.onEvent(EVENT_PAGE_CHANGE, self.switchPage)
760         self.reload()
761         self.dontcare = 0
762         #self.Bind(wx.EVT_IDLE, self.OnIdle)
763         #print dir(self)
764
765     def processFiles(self):
766         if self.filepos >= 0 and self.filepos < state.pdf.pages:
767             icon = state.getPageIcon(self.filepos+1)
768             self.imglist.Add(icon)
769             self.InsertImageStringItem(self.filepos, str(self.filepos+1), self.filepos)
770             self.filepos = self.filepos + 1
771         self.Update()
772
773     def OnIdle(self,event):
774         self.processFiles()
775         event.ResumePropagation(0)
776
777     def reload(self):
778         self.filepos = -1
779         self.DeleteAllItems()
780         self.imglist = wx.ImageList(ICON_SIZE,ICON_SIZE,mask=False)
781         self.AssignImageList(self.imglist,wx.IMAGE_LIST_NORMAL)
782         self.filepos = 0
783         while state.pdf and self.filepos < state.pdf.pages:
784             self.processFiles()
785
786     def switchPage(self):
787         if self.dontcare:
788             self.dontcare = 0
789             return
790         for i in range(0,self.GetItemCount()):
791             self.Select(i, False)
792         self.Select(state.pagenr-1, True)
793         self.Focus(state.pagenr-1)
794         self.Update()
795
796     def SelectItem(self,event):
797         self.dontcare = 1 #ignore next change event
798         state.changePage(event.GetIndex()+1)
799
800
801 helptxt = """
802 This is the SWF preview window.
803 Here, you will see how the SWF file generated from
804 the PDF file will look like. Changing parameters in
805 the configuration which affect the appeareance of
806 the final SWF will affect this preview, too, so you
807 can always evaluate the final output beforehand.
808 """
809
810         
811 class OnePageWidget(wx.Window):
812     def __init__(self,parent):
813         wx.Window.__init__(self, parent)
814         self.SetSize((160,100))
815         self.SetMinSize((160,100))
816         self.Fit()
817         self.Bind(wx.EVT_PAINT, self.OnPaint)
818         self.Bind(wx.EVT_SIZE, self.OnSize)
819         self.Bind(wx.EVT_KEY_DOWN, self.key_down)
820         state.onEvent(EVENT_PAGE_CHANGE, self.OnPageChange)
821     
822     def key_down(self, event):
823         if state.pdf:
824             if event.GetKeyCode() == 312 and state.pagenr>1:
825                 state.changePage(state.pagenr-1)
826             elif event.GetKeyCode() == 313 and state.pagenr<state.pdf.pages:
827                 state.changePage(state.pagenr+1)
828
829     def OnPageChange(self):
830         self.Refresh()
831
832     def OnSize(self, event):
833         self.Refresh()
834
835     def Draw(self,dc=None):
836         global bitmap
837         if not dc:
838             dc = wx.ClientDC(self)
839         posx = 0 
840         posy = 0
841         window_width,window_height = self.GetSize()
842         dc.Clear()
843
844         if not state.pdf or not state.page:
845             return
846
847         if state.page.width * window_height > state.page.height * window_width:
848             width = window_width
849             height = window_width * state.page.height / state.page.width
850             posy = (window_height - height) / 2
851         else:
852             width = window_height * state.page.width / state.page.height
853             height = window_height
854             posx = (window_width - width) / 2
855
856         dc.DrawBitmap(state.getPageImage(width,height), posx,posy, False)
857         #state.getPageImage(
858
859     def OnPaint(self, event):
860         dc = wx.PaintDC(self)
861         self.Draw(dc)
862
863 class Pdf2swfFrame(wx.Frame):
864     #def __init__(self):
865         #wx.Window.__init__(self, None, id=-1, pos=wx.DefaultPosition, size=wx.DefaultSize)
866     def __init__(self,application):
867         wx.Frame.__init__(self, None, -1, style = wx.DEFAULT_FRAME_STYLE)
868         self.application = application
869         
870         self.SetTitle("pdf2swf")
871         self.createMenu()
872         self.createToolbar()
873         self.createStatusBar()
874         self.createMainFrame()
875         
876         self.SetSize((800,600))
877
878         self.options = OptionFrame(None)
879         self.options.Show(False)
880         self.options.updateOptions()
881
882         state.onEvent(EVENT_STATUS_TEXT, self.status_change)
883         self.html = 0
884        
885         #self.table = wx.AcceleratorTable([(wx.ACCEL_ALT,  ord('X'), 333),])
886         #self.SetAcceleratorTable(self.table)
887
888         self.Bind(wx.EVT_IDLE, self.OnIdle)
889         self.Bind(wx.EVT_CLOSE, self.menu_exit)
890         return
891
892     def menu_open(self,event):
893         global state
894         if state.filename:
895             dlg = wx.FileDialog(self, "Choose PDF File:", style = wx.DD_DEFAULT_STYLE, defaultFile = state.filename, wildcard = "PDF files (*.pdf)|*.pdf|all files (*.*)|*.*")
896         else:
897             dlg = wx.FileDialog(self, "Choose PDF File:", style = wx.DD_DEFAULT_STYLE, wildcard = "PDF files (*.pdf)|*.pdf|all files (*.*)|*.*")
898
899         if dlg.ShowModal() == wx.ID_OK:
900             self.filename = dlg.GetFilename() 
901             state.loadPDF(self.filename)
902
903     def menu_save(self,event,pages=None):
904         html,self.html = self.html,0
905         global state
906         if not state.pdf:
907             return
908         print "html",html
909         if not html:
910             defaultFile = state.lastsavefile
911         else:
912             defaultFile = state.lasthtmlfile
913         dlg = wx.FileDialog(self, "Choose Save Filename:", style = wx.SAVE | wx.OVERWRITE_PROMPT, defaultFile = defaultFile, wildcard = "all files (*.*)|*.*|SWF files (*.swf)|*.swf|HTML template (*.html)|*.html")
914         
915         if dlg.ShowModal() == wx.ID_OK:
916             filename = os.path.join(dlg.GetDirectory(),dlg.GetFilename())
917         
918             #progress = ProgressFrame(self, "Saving %s File '%s'..." % (html and "HTML" or "SWF", filename))
919             #progress.Show(True)
920             progress = None
921             state.saveSWF(filename, progress, pages, html)
922             #progress.Destroy()
923     
924     def menu_save_selected(self,event):
925         if not state.pdf:
926             return
927         p = []
928         for i in range(0,self.pagelist.GetItemCount()):
929             if self.pagelist.IsSelected(i):
930                 p += [i+1]
931         self.menu_save(event, pages=p)
932
933     def menu_save_html(self,event):
934         self.html = 1
935         return self.menu_save(event)
936
937     def menu_save_selected_html(self,event):
938         self.html = 1
939         return self.menu_save_selected(event)
940
941     def menu_exit(self,event):
942         self.application.Exit()
943
944     def menu_selectall(self,event):
945         for i in range(0,self.pagelist.GetItemCount()):
946             self.pagelist.Select(i, True)
947     def menu_options(self,event):
948         self.options.Show(True)
949
950     def status_change(self):
951         self.statusbar.SetStatusText(state.status_text)
952
953     def OnIdle(self,event):
954         if self.options.needreload:
955             self.options.needreload = 0
956             if state.pdf:
957                 # reload
958                 state.loadPDF(state.filename)
959
960     def createMenu(self):
961         menubar = wx.MenuBar()
962
963         menu = wx.Menu();menubar.Append(menu, "&File")
964         menu.Append(wx.ID_OPEN, "Open PDF\tCTRL-O");self.Bind(wx.EVT_MENU, self.menu_open, id=wx.ID_OPEN)
965         menu.AppendSeparator()
966         menu.Append(wx.ID_SAVE, "Save SWF (all pages)\tCTRL-W");self.Bind(wx.EVT_MENU, self.menu_save, id=wx.ID_SAVE)
967         menu.Append(wx.ID_SAVEAS, "Save SWF (selected pages)\tCTRL-S");self.Bind(wx.EVT_MENU, self.menu_save_selected, id=wx.ID_SAVEAS)
968         menu.AppendSeparator()
969         menu.Append(2001, "Save HTML template (all pages)\tCTRL-H");self.Bind(wx.EVT_MENU, self.menu_save_html, id=2001)
970         menu.Append(2002, "Save HTML template (selected pages)");self.Bind(wx.EVT_MENU, self.menu_save_selected_html, id=2002)
971         menu.AppendSeparator()
972         menu.Append(wx.ID_EXIT, "Exit\tCTRL-Q");self.Bind(wx.EVT_MENU, self.menu_exit, id=wx.ID_EXIT)
973
974         menu = wx.Menu();menubar.Append(menu, "&Edit")
975         menu.Append(wx.ID_SELECTALL, "Select All\tCTRL-A");self.Bind(wx.EVT_MENU, self.menu_selectall, id=wx.ID_SELECTALL)
976         menu.AppendSeparator()
977         menu.Append(wx.ID_PREFERENCES, "Options\tCTRL-R");self.Bind(wx.EVT_MENU, self.menu_options, id=wx.ID_PREFERENCES)
978
979         menu = wx.Menu();menubar.Append(menu, "&Help")
980
981         self.SetMenuBar(menubar)
982
983
984     def createToolbar(self):
985
986         tsize = (16,16)
987         self.toolbar = self.CreateToolBar(wx.TB_HORIZONTAL | wx.NO_BORDER | wx.TB_FLAT)
988
989         self.toolbar.AddSimpleTool(wx.ID_OPEN,
990                                    wx.ArtProvider.GetBitmap(wx.ART_FILE_OPEN, wx.ART_TOOLBAR, tsize),
991                                    "Open")
992         self.toolbar.AddSimpleTool(wx.ID_SAVE,
993                                    wx.ArtProvider.GetBitmap(wx.ART_FILE_SAVE, wx.ART_TOOLBAR, tsize),
994                                    "Save selected pages")
995         self.toolbar.AddSimpleTool(wx.ID_PREFERENCES,
996                                    wx.ArtProvider.GetBitmap(wx.ART_LIST_VIEW, wx.ART_TOOLBAR, tsize),
997                                    "Options")
998         #self.toolbar.AddSeparator()
999         self.toolbar.Realize()
1000
1001     def createStatusBar(self):
1002         self.statusbar = self.CreateStatusBar(1)
1003
1004     def createMainFrame(self):
1005         
1006         if 0:
1007             self.pagelist = PageListWidget(self)
1008             self.onepage = OnePageWidget(self)
1009             hsplit = wx.BoxSizer(wx.HORIZONTAL)
1010             pagelistbox = wx.StaticBox(self, -1, "Pages")
1011             pagelistboxsizer= wx.StaticBoxSizer(pagelistbox, wx.VERTICAL)
1012             pagelistboxsizer.Add(self.pagelist, proportion=1, flag=wx.EXPAND)
1013             onepagebox = wx.StaticBox(self, -1, "Page 1")
1014             onepageboxsizer= wx.StaticBoxSizer(onepagebox, wx.VERTICAL)
1015             onepageboxsizer.Add(self.onepage, proportion=1, flag=wx.EXPAND)
1016             hsplit.Add(pagelistboxsizer, 0, wx.EXPAND, 0)
1017             hsplit.Add(onepageboxsizer, 1, wx.EXPAND, 0)
1018             self.SetAutoLayout(True)
1019             self.SetSizer(hsplit)
1020             hsplit.Fit(self)
1021             hsplit.SetSizeHints(self)
1022         else:
1023             hsplit = wx.SplitterWindow(self, style=wx.SP_3D|wx.SP_LIVE_UPDATE)
1024             #p1 = wx.Panel(hsplit,-1, style=wx.SUNKEN_BORDER)
1025             #p2 = wx.Panel(hsplit,-1, style=wx.SUNKEN_BORDER)
1026             self.pagelist = PageListWidget(hsplit)
1027             self.onepage = OnePageWidget(hsplit)
1028             #hsplit.SplitVertically(p1,p2, sashPosition=64)
1029             hsplit.SplitVertically(self.pagelist, self.onepage, sashPosition=ICON_SIZE*3/2)
1030             hsplit.SetMinimumPaneSize(10)
1031
1032 #class TestWindow(wx.SplitterWindow):
1033 #    def __init__(self,parent):
1034 #        wx.SplitterWindow.__init__(self, parent, -1)
1035 #        panel = wx.Panel(self, -1)
1036 #        self.txt = wx.StaticText(panel, -1, "bla bla bla", (100,10), (160,-1), wx.ALIGN_CENTER|wx.ALIGN_CENTER_VERTICAL)
1037 #        self.SetMinSize((320,200))
1038 #        self.txt.SetForegroundColour("blue")
1039 #        self.txt.SetBackgroundColour("green")
1040 #        self.Fit()
1041 #        self.Initialize(panel)
1042 #
1043 #class TestFrame(wx.Frame):
1044 #    def __init__(self,parent):
1045 #        wx.Frame.__init__(self, None, -1)
1046 #        panel = wx.Panel(self, -1)
1047 #        window = TestWindow(panel)
1048 #        window.Show()
1049
1050 class MyApp(wx.App):
1051     def __init__(self):
1052         wx.App.__init__(self, redirect=False, filename=None, useBestVisual=False)
1053         
1054         #state.loadPDF("sitis2007.pdf")
1055         #state.loadPDF("wxPython-Advanced-OSCON2004.pdf")
1056         global staticdata
1057         staticdata = StaticData()
1058
1059         self.frame = Pdf2swfFrame(self)
1060         self.SetTopWindow(self.frame)
1061         self.frame.Show(True)
1062
1063         #self.frame = TestFrame(self)
1064         #self.frame.Show(True)
1065
1066     def OnInit(self):
1067         return True
1068
1069 app = MyApp()
1070 app.MainLoop()
1071