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