Automatic story formatting: Difference between revisions

From Scribus Wiki
Jump to navigation Jump to search
(This script show a dialog with options to be applied to a selected text frame: paragraphs styles, title frame, number of text columns, etc.)
 
mNo edit summary
(5 intermediate revisions by the same user not shown)
Line 14: Line 14:
joseantoniorocha@gmail.com
joseantoniorocha@gmail.com
http://meiradarocha.jor.br
http://meiradarocha.jor.br
Licence: GPL 2
License: GPL 2
2011-01-26
2013-05-02a


USAGE:
USAGE:
Draw a text frame marking the area where you need a story,  
Draw a text frame marking the area where you need a story,  
optionaly putting text inside. Select the frame, run the script  
optionally putting text inside. Select the frame, run the script  
and choose styles to be applied, numbers of columns, gutter
and choose styles to be applied, numbers of columns, gutter
and guide lines.
and guide lines.
Line 25: Line 25:
1. Resize the frame to hold headers.
1. Resize the frame to hold headers.
2. Draw a second linked frame to hold text in columns.
2. Draw a second linked frame to hold text in columns.
3. Clean up the text, ripping off blank paragraphs, double spaces,
3. Clean up the text, ripping off blank paragraphs, double spaces, changing quotes etc.
  changing quotes etc.
4. Apply the chosen styles.
4. Apply the choosen styles.
5. Find and apply style to short paragraphs without dot as sub-headers.
5. Find and apply style to short paragraphs as sub-headers.


NEED:
NEED:
Tcl/Tk 8.5 (or 8.4 with Tile 8.2)
Tcl/Tk 8.5 (or 8.4 with Tile 8.2)
Python 2.7 (or 2.5.4 with pyttk)
Python 2.7 (or 2.5.4 with pyttk)
'''
'''
############################
############################
# Locales to be translated
# Locales to be translated
Line 41: Line 42:
defaultNumCols = '2'
defaultNumCols = '2'


# Default style names - change to match your style sheet
# Style names - change to match your style sheet
# Style names - change to match your style sheet
paragraph1style = 'Standing head 1'
paragraph1style = 'Standing head 1'
Line 48: Line 50:
paragraph5style = 'Sub byline'
paragraph5style = 'Sub byline'
paragraph6style = 'First paragraph 1'
paragraph6style = 'First paragraph 1'
paragraph7style = 'Default Paragraph Style'
bodyTextStyle  = 'Default Paragraph Style'
paragraphLaststyle = 'Default Paragraph Style'
paragraphLastStyle = 'Default Paragraph Style'
defaultParagraphStyle = 'Default Paragraph Style'
defaultParagraphStyle = 'Default Paragraph Style'
subHeaderStyle = 'Subhead'
subHeaderStyle = 'Subhead'
'''
# Style names - change to match your style sheet
paragraph1style = 'Antetitulo'
paragraph2style = 'Titulo 2'
paragraph3style = 'Subtitulo 1'
paragraph4style = 'Assinatura'
paragraph5style = 'Sub byline'
paragraph6style = 'Primeiro paragrafo'
paragraph7style = 'Default Paragraph Style'
paragraphLaststyle = 'Default Paragraph Style'
defaultParagraphStyle = 'Default Paragraph Style'
subHeaderStyle = 'Entretitulo'
'''
# Put in this list the paragraphs to be hyphenated
hyphenated = [paragraph6style,paragraph7style]


# Put in this dictionary the styles with special formating and the functions that does it
###############################
# Bullet special formats
# Put your favorite bullet to lists.
# Some OS have different character codes
bulletedListStyle = 'Bullet'
bulletSize = 0  # In points. Zero means same size as text.
# Some common dingbat fonts:
# 'ITC Zapf Dingbats Regular' # 'Dingbats Regular' # 'Wingdings Regular'
if os.name == 'posix':
    bullet = u'\u25cf'
    bulletFont = 'Dingbats Regular' 
else:
    bullet = u'\u25cf'
    bulletFont = 'Times New Roman Regular'
 
###############################
# Standing head special formats
standingHeadStyle = 'Standing head 1'
headBulletSize = 0
if os.name == 'posix':
    headBullet = u'\u25ba'
    headBulletFont = 'Dingbats Regular'
else:
    headBullet = u'\u25ba'
    headBulletFont = 'Times New Roman Regular'
 
###########################
# Bullet to mark story end.
endBulletSize = 0
if os.name == 'posix':
    endBullet = u'\u25a0'
    endBulletFont = 'Dingbats Regular'
else:
    endBullet = u'\u25a0'
    endBulletFont = 'Times New Roman Regular'
 
#########################################################
# Put in this list the paragraphs styles to be hyphenated
#########################################################
hyphenate = [
    paragraph6style,
    bodyTextStyle,
    paragraphLastStyle,
    bulletedListStyle
]
 
##########################################################
# Put in this dictionary the styles with special formating  
and the functions that does it (without "()").
# Use it to put bullets in front or at end of a paragraph,
#  format first words as bold etc.
##########################################################
specialParagraphs = {
specialParagraphs = {
   paragraph1style: 'putBullet',
   paragraph1style: 'formatBulletedList'
   paragraph4style: 'putBullet'
  #,paragraph4style: 'formatBulletedList'
   #,paragraphLastStyle: 'formatLastParagraph'
}
}


Line 80: Line 119:


# Strings to ask to select frame
# Strings to ask to select frame
storyFrameLabel = "Selecione um quadro"
storyFrameLabel = "Select a frame"
askOpenFrame = "<h2>"+storyFrameLabel+"</h2>\n" \
askOpenFrame = "<h2>"+storyFrameLabel+"</h2>\n" \
     + "Draw or select <b>a text frame</b>\nwhere you want to put a story."  
     + "Draw or select <b>a text frame</b>\nwhere you want to put a story."  
Line 112: Line 151:
paraLines = (1, 2, 3, 4, 5, 6)
paraLines = (1, 2, 3, 4, 5, 6)
numColumnsList = (1, 2, 3, 4, 5, 6, 7, 8 , 9, 10, 11, 12)
numColumnsList = (1, 2, 3, 4, 5, 6, 7, 8 , 9, 10, 11, 12)
################################
# Debugging functions
################################
import inspect
debuggingOn = 0
def whoami():
    return inspect.stack()[2][3]
def whosdaddy():
    return inspect.stack()[3][3]
def debug():
    if debuggingOn:
        here = "I'm %s, son of %s" % (whoami(), whosdaddy())
        scribus.statusMessage(here)
        print here
################################


# IMPORTS
# IMPORTS
import sys
import sys
import re


try:
try:
Line 148: Line 204:


# Constants
# Constants
inch = 0.0138888888889 # mistakenly translated to portuguese 'pol'
inch = 0.0138888888889 # mistakenly translated to 'pol', in Portuguese localization
AROUND_FRAME = 1
AROUND_FRAME = 1
APPEND = -1  # -1 append text to frame


# Measure convertion dictionaries
# Measure conversion dictionaries
unitDict = {
unitDict = {
     UNIT_POINTS:pt,
     UNIT_POINTS:pt,
Line 171: Line 228:
import xml.parsers.expat
import xml.parsers.expat


###################################
################################################
# Code to handle elements of XML Scribus doc
# Convert default points values to current units
################################################
# Dictionaries to hold style sheets
# Dictionaries to hold style sheets
styles = {}
styles = {}
Line 178: Line 238:
pages = {}
pages = {}


# Handle elements of XML Scribus doc
# Convert default points values to current units
def start_element(name, attrs):
def start_element(name, attrs):
     '''Build dictonaries from SLA doc file.'''
     '''Build dictionaries from SLA doc file.'''
     # print 'Start element:', name, attrs  #debug
     # print 'Start element:', name, attrs  #debug
     global styles, charstyles, document, pages
     global styles, charstyles, document, pages
Line 187: Line 245:
         document = attrs
         document = attrs
     if name == 'STYLE':
     if name == 'STYLE':
         styles[attrs[u'NAME']] = attrs
         #print '=====\n',name,attrs,attrs[u'NAME']
        # A bug in style editor:
        # sometimes create a anomale style definition <STYLE LANGUAGE="Portuguese"/>
        # So we need to catch errors and ignore it
        try: styles[attrs[u'NAME']] = attrs
        except: pass
     if name == 'CHARSTYLE':
     if name == 'CHARSTYLE':
         charstyles[attrs[u'CNAME']] = attrs
         try: charstyles[attrs[u'CNAME']] = attrs
        except: pass
     if name == 'PAGE':
     if name == 'PAGE':
         pages[attrs[u'NUM']] = attrs
         try: pages[attrs[u'NUM']] = attrs
        except: pass


def getParagraphStyle(styleName):
def getParagraphStyle(styleName):
Line 207: Line 272:
def getParagraphAttribute(styleName,attrName):
def getParagraphAttribute(styleName,attrName):
     '''Return value of given attribute of given style
     '''Return value of given attribute of given style
     ***in current mesurement units.***'''
     ***in current mesurement units.*** '''
    dic = getParagraphStyle(styleName)
     res = 0
     res = 0
     # Find parameter in parent style, if not in current style.
     # Find parameter in parent style, if not in current style, up to 10 levels.
     if attrName in dic:
     for n in range(10):
        res = dic[attrName]
        dic = getParagraphStyle(styleName)
        # Convert points to current units
        if attrName in dic:
        unit = scribus.getUnit()
            res = dic[attrName]
        res = float(res)*unitDict[unit]
            # Convert points to current units
        return res
            unit = scribus.getUnit()
    else:   
            res = float(res)*unitDict[unit]
        if 'PARENT' in dic:
            print "Find",attrName,"=",res,'in',styleName,"."
            styleName = dic['PARENT']
             break
            res = getParagraphAttribute(styleName,attrName)
             return res
         else:
         else:
             res = 0
             print "\nI don't find",attrName,"in",styleName,"."
            return res
            if 'PARENT' in dic:
                styleName = dic['PARENT']
                print "I will search in his father,",styleName,"."
            else:
                print "I don't find any value. Setting to zero..."
                break
    else:
        res = 0
    return res


def getStyleLineSpacing(styleName):
def getStyleLineSpacing(styleName):
     '''Return line spacing of given style.'''
     '''Return line spacing of given style.'''
    # Need test line spacing mode
    # If line space mode is automatic,
    # line space is equal font size times line space percent
     res = float(getParagraphAttribute(styleName,'LINESP'))
     res = float(getParagraphAttribute(styleName,'LINESP'))
     return res
     return res
Line 303: Line 376:
def parseDoc():
def parseDoc():
     ''' Get attributes from current Scribus doc.'''
     ''' Get attributes from current Scribus doc.'''
    debug()
     global styleList
     global styleList
     parser = xml.parsers.expat.ParserCreate('UTF-8')
     parser = xml.parsers.expat.ParserCreate('UTF-8')
Line 308: Line 382:
     try:  
     try:  
         docName = scribus.saveDoc()
         docName = scribus.saveDoc()
         docName = scribus.getDocName()  # Undocumented Scribus 1.3.9 function
         # Undocumented Scribus 1.3.9 function:
         file = open(unicode(docName),'r')
        docName = unicode(scribus.getDocName()) 
         file = open(docName,'r')
         parser.ParseFile(file)
         parser.ParseFile(file)
         file.close()
         file.close()
     except:
     except:
        print "Error analising doc."
         earlyExit()
         earlyExit()
     styleList = scribus.getAllStyles()
     styleList = scribus.getAllStyles()
     styleList.sort()
     styleList.sort()


##################
# GUI code
##################
def set_Tk_var():
def set_Tk_var():
     # These are Tk variables used passed to Tkinter and must be
     # These are Tk variables used passed to Tkinter and must be
Line 345: Line 424:
     global chk2label
     global chk2label
     chk2label = StringVar()
     chk2label = StringVar()
     chk2label.set('1')
     chk2label.set('\Uffffffff 1')
     global para2style
     global para2style
     para2style = StringVar()
     para2style = StringVar()
Line 359: Line 438:
     global chk3label
     global chk3label
     chk3label = StringVar()
     chk3label = StringVar()
     chk3label.set('2')
     chk3label.set('\Uffffffff 2')
     global para3style
     global para3style
     para3style = StringVar()
     para3style = StringVar()
Line 373: Line 452:
     global chk4label
     global chk4label
     chk4label = StringVar()
     chk4label = StringVar()
     chk4label.set('3')
     chk4label.set('\Uffffffff 3')
     global para4style
     global para4style
     para4style = StringVar()
     para4style = StringVar()
Line 384: Line 463:
     global chk5label
     global chk5label
     chk5label = StringVar()
     chk5label = StringVar()
     chk5label.set('4')
     chk5label.set('\Uffffffff 4')
     global para5style
     global para5style
     para5style = StringVar()
     para5style = StringVar()
Line 395: Line 474:
     global chk6label
     global chk6label
     chk6label = StringVar()
     chk6label = StringVar()
     chk6label.set('5')
     chk6label.set('\Uffffffff 5')
     global para6style
     global para6style
     para6style = StringVar()
     para6style = StringVar()
Line 409: Line 488:
     global para7style
     global para7style
     para7style = StringVar()
     para7style = StringVar()
     para7style.set(paragraph7style)
     para7style.set(bodyTextStyle)
      
      
     # Paragraph Last
     # Paragraph Last
Line 417: Line 496:
     global paraLastStyle
     global paraLastStyle
     paraLastStyle = StringVar()
     paraLastStyle = StringVar()
     paraLastStyle.set(paragraphLaststyle)
     paraLastStyle.set(paragraphLastStyle)
    global chkEndBull
    chkEndBull = StringVar()
    chkEndBull.set('1')
      
      
     # Columns
     # Columns
Line 440: Line 522:
     global isToDrawGuides
     global isToDrawGuides
     isToDrawGuides = StringVar()
     isToDrawGuides = StringVar()
     isToDrawGuides.set('1')
     isToDrawGuides.set('0')
      
      
     # Snap to baseline guides
     # Snap to baseline guides
     global snap
     global snap
     snap = StringVar()
     snap = StringVar()
     snap.set('1')
     snap.set('0')
      
      
class Build_Story_dialog:
class Build_Story_dialog:
Line 470: Line 552:
         self.tLa47_tRa48.configure(text=guiJustApplyStyles,variable=action,value="0")
         self.tLa47_tRa48.configure(text=guiJustApplyStyles,variable=action,value="0")
         self.tLa47_tRa49 = ttk.Radiobutton (self.tLa47)
         self.tLa47_tRa49 = ttk.Radiobutton (self.tLa47)
         self.tLa47_tRa49.place(relx=0.49,rely=0.12,relheight=0.7,relwidth=0.45)
         self.tLa47_tRa49.place(relx=0.49,rely=0.12,relheight=0.7,relwidth=0.5)
         self.tLa47_tRa49.configure(text=guiRebuildFrames,variable=action,value="1")
         self.tLa47_tRa49.configure(text=guiRebuildFrames,variable=action,value="1")
          
          
Line 478: Line 560:
         self.tLa34.configure(text=guiParagr)
         self.tLa34.configure(text=guiParagr)
         self.tLa35 = ttk.Label (master)
         self.tLa35 = ttk.Label (master)
         self.tLa35.place(relx=0.14,rely=0.28)
         self.tLa35.place(relx=0.16,rely=0.28)
         self.tLa35.configure(text=guiStyle)
         self.tLa35.configure(text=guiStyle)
         self.tLa36 = ttk.Label (master)
         self.tLa36 = ttk.Label (master)
Line 487: Line 569:
         self.tCh37.place(relx=0.02,rely=0.34)
         self.tCh37.place(relx=0.02,rely=0.34)
         self.tCh37.configure(textvariable=chk1label,variable=chk1)
         self.tCh37.configure(textvariable=chk1label,variable=chk1)
         self.tCo38 = ttk.Combobox (master)
         self.tCo38 = ttk.Combobox (master)
         self.tCo38.place(relx=0.14,rely=0.34,relwidth=0.34)
         self.tCo38.place(relx=0.16,rely=0.34,relwidth=0.32)
         self.tCo38.configure(textvariable=para1style,values=styleList)
         self.tCo38.configure(textvariable=para1style,values=styleList)
         self.tCo39 = ttk.Combobox (master)
         self.tCo39 = ttk.Combobox (master)
         self.tCo39.place(relx=0.48,rely=0.34,relwidth=0.11)
         self.tCo39.place(relx=0.48,rely=0.34,relwidth=0.11)
         self.tCo39.configure(textvariable=para1lin,values=paraLines,justify="center")
         self.tCo39.configure(textvariable=para1lin,values=paraLines,justify="center")
         self.tCo38.configure(state="disabled")
         self.tCo38.configure(state="disabled")
         self.tCo39.configure(state="disabled")
         self.tCo39.configure(state="disabled")
Line 500: Line 585:
         self.tCh40.configure(textvariable=chk2label,variable=chk2)
         self.tCh40.configure(textvariable=chk2label,variable=chk2)
         self.tCo41 = ttk.Combobox (master)
         self.tCo41 = ttk.Combobox (master)
         self.tCo41.place(relx=0.14,rely=0.42,relwidth=0.34)
         self.tCo41.place(relx=0.16,rely=0.42,relwidth=0.32)
         self.tCo41.configure(textvariable=para2style,values=styleList)
         self.tCo41.configure(textvariable=para2style,values=styleList)
         self.tCo42 = ttk.Combobox (master)
         self.tCo42 = ttk.Combobox (master)
Line 511: Line 596:
         self.tCh43.configure(textvariable=chk3label,variable=chk3)
         self.tCh43.configure(textvariable=chk3label,variable=chk3)
         self.tCo44 = ttk.Combobox (master)
         self.tCo44 = ttk.Combobox (master)
         self.tCo44.place(relx=0.14,rely=0.5,relwidth=0.34)
         self.tCo44.place(relx=0.16,rely=0.5,relwidth=0.32)
         self.tCo44.configure(textvariable=para3style,values=styleList)
         self.tCo44.configure(textvariable=para3style,values=styleList)
         self.tCo45 = ttk.Combobox (master)
         self.tCo45 = ttk.Combobox (master)
Line 522: Line 607:
         self.tCh46.configure(textvariable=chk4label,variable=chk4)
         self.tCh46.configure(textvariable=chk4label,variable=chk4)
         self.tCo47 = ttk.Combobox (master)
         self.tCo47 = ttk.Combobox (master)
         self.tCo47.place(relx=0.14,rely=0.58,relwidth=0.34)
         self.tCo47.place(relx=0.16,rely=0.58,relwidth=0.32)
         self.tCo47.configure(textvariable=para4style,values=styleList)
         self.tCo47.configure(textvariable=para4style,values=styleList)
         # Para #5
         # Para #5
Line 529: Line 614:
         self.tCh49.configure(textvariable=chk5label,variable=chk5)
         self.tCh49.configure(textvariable=chk5label,variable=chk5)
         self.tCo50 = ttk.Combobox (master)
         self.tCo50 = ttk.Combobox (master)
         self.tCo50.place(relx=0.14,rely=0.66,relwidth=0.34)
         self.tCo50.place(relx=0.16,rely=0.66,relwidth=0.32)
         self.tCo50.configure(textvariable=para5style,values=styleList)
         self.tCo50.configure(textvariable=para5style,values=styleList)
         # Para #6
         # Para #6
         self.tCh52 = ttk.Checkbutton (master)
         self.tCh52 = ttk.Checkbutton (master)
         self.tCh52.place(relx=0.02,rely=0.74)
         self.tCh52.place(relx=0.02,rely=0.74)
         self.tCh52.configure(textvariable=chk6label,variable=chk6)
         self.tCh52.configure(textvariable=chk6label,variable=chk6,state="disabled")
         self.tCo53 = ttk.Combobox (master)
         self.tCo53 = ttk.Combobox (master)
         self.tCo53.place(relx=0.14,rely=0.74,relwidth=0.34)
         self.tCo53.place(relx=0.16,rely=0.74,relwidth=0.32)
         self.tCo53.configure(textvariable=para6style,values=styleList)
         self.tCo53.configure(textvariable=para6style,values=styleList)
         # Para #7
         # Para #7
Line 543: Line 628:
         self.tCh54.configure(textvariable=chk7label,variable=chk7)
         self.tCh54.configure(textvariable=chk7label,variable=chk7)
         self.tCo55 = ttk.Combobox (master)
         self.tCo55 = ttk.Combobox (master)
         self.tCo55.place(relx=0.14,rely=0.82,relwidth=0.34)
         self.tCo55.place(relx=0.16,rely=0.82,relwidth=0.32)
         self.tCo55.configure(textvariable=para7style,values=styleList)
         self.tCo55.configure(textvariable=para7style,values=styleList)
         # Para #last
         # Para #last
Line 550: Line 635:
         self.tCh33.configure(text=guiLastPara,variable=chkLast)
         self.tCh33.configure(text=guiLastPara,variable=chkLast)
         self.tCo34 = ttk.Combobox (master)
         self.tCo34 = ttk.Combobox (master)
         self.tCo34.place(relx=0.14,rely=0.9,relwidth=0.34)
         self.tCo34.place(relx=0.16,rely=0.9,relwidth=0.32)
         self.tCo34.configure(textvariable=paraLastStyle,values=styleList)
         self.tCo34.configure(textvariable=paraLastStyle,values=styleList)
        self.tCh34b = ttk.Checkbutton (master)
        self.tCh34b.place(relx=0.49,rely=0.9)
        self.tCh34b.configure(text="Bullet",variable=chkEndBull)
       
          
          
         # Content Frame
         # Content Frame
Line 586: Line 675:
         self.tLa32_cpd34.configure(justify="center",textvariable=numCols,values=numColumnsList)
         self.tLa32_cpd34.configure(justify="center",textvariable=numCols,values=numColumnsList)
         self.tLa32_cpd34.configure(state="disabled")
         self.tLa32_cpd34.configure(state="disabled")
        self.tLa32_cpd34.configure(validate="all",validatecommand="self.validateColumns")
         # Gutter label
         # Gutter label
         self.tLa32_cpd35 = ttk.Label (self.tLa32)
         self.tLa32_cpd35 = ttk.Label (self.tLa32)
Line 634: Line 724:
         self.tCh46.bind("<ButtonRelease-1>", self.__handlePara4)
         self.tCh46.bind("<ButtonRelease-1>", self.__handlePara4)
         self.tCh49.bind("<ButtonRelease-1>", self.__handlePara5)
         self.tCh49.bind("<ButtonRelease-1>", self.__handlePara5)
         self.tCh52.bind("<ButtonRelease-1>", self.__handlePara6)
         #self.tCh52.bind("<ButtonRelease-1>", self.__handlePara6)
         self.tCh54.bind("<ButtonRelease-1>", self.__handlePara7)
         self.tCh54.bind("<ButtonRelease-1>", self.__handlePara7)
         self.tCh33.bind("<ButtonRelease-1>", self.__handlePara8)
         self.tCh33.bind("<ButtonRelease-1>", self.__handlePara8)
       
    def validateColumns():
        pass
          
          
     def __handleRebuild(self,event):
     def __handleRebuild(self,event):
Line 675: Line 768:
         if chk1.get() == '1':
         if chk1.get() == '1':
             num += 1
             num += 1
             chk1label.set(''+str(num))
             chk1label.set('\Uffffffff '+str(num))
         else: chk1label.set('')
         else: chk1label.set('')
         if chk2.get() == '1':
         if chk2.get() == '1':
             num += 1
             num += 1
             chk2label.set(''+str(num))
             chk2label.set('\Uffffffff '+str(num))
         else: chk2label.set('')
         else: chk2label.set('')
         if chk3.get() == '1':
         if chk3.get() == '1':
             num += 1
             num += 1
             chk3label.set(''+str(num))
             chk3label.set('\Uffffffff '+str(num))
         else: chk3label.set('')
         else: chk3label.set('')
         if chk4.get() == '1':
         if chk4.get() == '1':
             num += 1
             num += 1
             chk4label.set(''+str(num))
             chk4label.set('\Uffffffff '+str(num))
         else: chk4label.set('')
         else: chk4label.set('')
         if chk5.get() == '1':
         if chk5.get() == '1':
             num += 1
             num += 1
             chk5label.set(''+str(num))
             chk5label.set('\Uffffffff '+str(num))
         else: chk5label.set('')
         else: chk5label.set('')
         if chk6.get() == '1':
         if chk6.get():
             num += 1
             num += 1
             chk6label.set(''+str(num))
             chk6label.set('\Uffffffff '+str(num))
         else: chk6label.set('')
         else: chk6label.set('')
        
        
Line 801: Line 894:
# Clean text
# Clean text
def normalizeText(t):
def normalizeText(t):
        '''Rip off blank paragraphs, double spaces, html tags, quotes etc.'''
    '''Rip off blank paragraphs, double spaces, html tags, quotes etc.'''
        t = t.strip()
    debug()
        while t.count('\t'):
    t = t.strip()
                t = t.replace('\t','')
    while t.count('\t'):
        while t.count('  '):
        t = t.replace('\t','')
                t = t.replace('  ',' ')
    while t.count('  '):
        while t.count('<br>'):
        t = t.replace('  ',' ')
                t = t.replace('<br>','\n')
    while t.count('<br>'):
        while t.count('\r'):            # Fim de parágrafo tipo DOS e Windows.  
        t = t.replace('<br>','\n')
                t = t.replace('\r','\n') # Troca pelo fim-de-parágrafo padrão
    while t.count('\r'):            # DOS/Windows paragraph end.  
        while t.count(' \n'):
        t = t.replace('\r','\n') # Change by new line
                t = t.replace(' \n','\n')
    while t.count(' \n'):
        while t.count('\n '):
        t = t.replace(' \n','\n')
                t = t.replace('\n ','\n')
    while t.count('\n '):
        while t.count('\n\n'):
        t = t.replace('\n ','\n')
                t = t.replace('\n\n','\n')
    while t.count('\n\n'):
        # Cria travessões
        t = t.replace('\n\n','\n')
        while t.count('\n-'):
    # Create m-dash
                t = t.replace('\n-','\n—')
    while t.count('\n-'):
        # Change computer quotes by typographic quotes
        t = t.replace('\n-','\n')
        while t.count('\n"'):
    # Change computer quotes by typographic quotes
                t = t.replace('\n"','\n“')
    while t.count('\n"'):
        while t.count(' "'):
        t = t.replace('\n"','\n')
                t = t.replace(' "',' ')
    while t.count(' "'):
        while t.count('" '):
        t = t.replace(' "',' ')
                t = t.replace('" ','')
    while t.count('" '):
        while t.count('",'):
        t = t.replace('" ',' ')
                t = t.replace('",',',')
    while t.count('",'):
        while t.count('".'):
        t = t.replace('",',',')
                t = t.replace('".','.')
    while t.count('".'):
        while t.count('."'):
        t = t.replace('".','.')
                t = t.replace('."','.')
    while t.count('."'):
        while t.count('.'):
        t = t.replace('."','.')
                t = t.replace('.','.')
    while t.count('.'):
       
        t = t.replace('.','.')
        return t
   
    return t


####################
#######################
# Insert bullets
# Format bulleted lists
# in some paragraphs
def formatBulletedList(textBegin,text,style,story):
def putBullet(line):
     '''Insert bullet at first paragraph.'''
     '''Insert bullet at first paragraph.'''
     # Remove old bullet
    debug()
     # Clear old marks
    reBullet  = r'['+bullet+'|(^\*)]'
    s = re.sub(reBullet,'',text)
    s = s.strip(" ")
    paraLen = len(s)
    scribus.insertText(s, APPEND, story)
    try:
        scribus.selectText(textBegin, paraLen-1, story)
        applyStyle(style, story)
    except: print "Can't select and apply style",style
    # Hyphenate if style is in hyphenation list
    if hyphenate.count(style) != 0:
        scribus.hyphenateText()
    # Insert bullet
    b = bullet+u'\t'
    scribus.insertText(b, textBegin, story)
    scribus.selectText(textBegin,1,story)
    if bulletFont:
        try: scribus.setFont(bulletFont,story)
        except: print "Can't find font:",bulletFont
    if bulletSize:
        try:
            scribus.selectText(textBegin,1,story)
            scribus.setFontSize(bulletSize,story)
        except: print "Can't size bullet."
    return paraLen+len(b)
 
# Tie above function to style name
specialParagraphs[bulletedListStyle] = 'formatBulletedList'
 
#######################
# Format Standing Head
def formatStandingHead(textBegin,text,style,story):
    '''Insert bullet in standing head.'''
    # Clear old marks
    debug()
    reBullet  = r''+headBullet
    s = re.sub(reBullet,'',text)
    s = s.strip(" ")
    paraLen = len(s)
    scribus.insertText(s, APPEND, story)
    try:
        scribus.selectText(textBegin, paraLen-1, story)
        applyStyle(style, story)
    except: print "Can't select and apply style",style
    # Hyphenate if style is in hyphenation list
    if hyphenate.count(style) != 0:
        scribus.hyphenateText()
    # Insert bullet
    b = headBullet+u'\t'
    scribus.insertText(b, textBegin, story)
    scribus.selectText(textBegin,1,story)
    if headBulletFont:
        try: scribus.setFont(headBulletFont,story)
        except: print "Can't find font:",headBulletFont
    if headBulletSize:
        try:
            scribus.selectText(textBegin,1,story)
            scribus.setFontSize(headBulletSize,story)
        except: print "Can't size bullet."
    return paraLen+len(b)
 
# Tie above function to style name
specialParagraphs[standingHeadStyle] = 'formatStandingHead'
 
######################
# formatLastParagraph.
# exec by formatText
######################
def formatLastParagraph(textBegin,text,style,story):
    '''Do especial formating in last paragraph,
    as put a bullet at end of paragraph.'''
    # Clear old marks
    debug()
    reEndBullet  = r''+endBullet
    s = re.sub(reEndBullet,'',text)
    s = s.strip(" ")
    paraLen = len(s)
    endPara = textBegin+paraLen
    scribus.insertText(s, APPEND, story)
    scribus.selectText(textBegin, paraLen-1, story)
    applyStyle(style, story)
    # Hyphenate if style is in hyphenation list
    if hyphenate.count(style) != 0:
        scribus.hyphenateText()
    # If terminate with bullet
    if (chkEndBull.get() == '1'):
        # Format end bullet
        eb = ' '+endBullet
        scribus.insertText(eb, APPEND, story)
        if endBulletFont:
            scribus.selectText(endPara+1,1,story)
            try: scribus.setFont(endBulletFont,story)
            except: print "Can't find font:",endBulletFont
    if endBulletSize:
        scribus.selectText(endPara+1,1,story)
        try: scribus.setFontSize(endBulletSize,story)
        except: print "Can't size end bullet."
      
      
     # Insert new bullet
     return paraLen
 
###################
# Insert text and
# format paragraph
def formatParagraph(textBegin,text,style,story):
    '''Insert text and format paragraph.'''
    debug()
    paraLen = len(text)
    scribus.insertText(text, APPEND, story)
    scribus.selectText(textBegin, paraLen-1, story)
    applyStyle(style, story)
    # Hyphenate if style is in hyphenation list
    if hyphenate.count(style) != 0:
        scribus.hyphenateText()
      
      
    return paraLen
###################
###################
def applyStyle(style,story):
def applyStyle(style,story):
     '''Try apply style to selected text. If style doesn't exist, create it.'''
     '''Try apply style to selected text. If style doesn't exist, create it.'''
    debug()
     try:
     try:
         scribus.setStyle(style, story)
         scribus.setStyle(style, story)
Line 860: Line 1,068:
def formatText(story):
def formatText(story):
     '''Format story with styles pre-header, header, sub-header etc.'''
     '''Format story with styles pre-header, header, sub-header etc.'''
     # Todo: translate italics, bolds, superscripts etc here
     # To do: translate italics, bolds, superscripts etc here
     scribus.deselectAll()
     debug()
    scribus.selectObject(story)
     paraStyles = []  
    alltext = scribus.getAllText(story)
    scribus.deleteText(story)
    alltext = normalizeText(alltext)
    lines = alltext.splitlines(1)
    textBegin = 0
     paraStyles = []
    paraLen = 0
     # Build a list of choosen styles
     # Build a list of choosen styles
     if chk1.get() == '1':
     if chk1.get() == '1':
Line 881: Line 1,082:
     if chk5.get() == '1':
     if chk5.get() == '1':
         paraStyles.append(para5style.get())
         paraStyles.append(para5style.get())
     if chk6.get() == '1':
     if chk6.get() == '1': # let ever on
         paraStyles.append(para6style.get())
         paraStyles.append(para6style.get())


    ####################################
     # Format headers and first paragraph
     # Format headers and first paragraph
   
    scribus.deselectAll()
    scribus.selectObject(story)
    alltext = scribus.getAllText(story)
    # Do nothing if no text
    if len(alltext) == 0:
        return
    alltext = normalizeText(alltext)
    # Convert text to unicode
    alltext = unicode(alltext)
    scribus.deleteText(story)
    # lines = alltext.splitlines(1)
    # splitlines can't be used because catch line break '\x1C' as paragraph end
    lines = alltext.split('\n')  # avoid separete paragraphs with line breaks (\x1C')
    # print repr(lines)  # debug
    textBegin = 0
    global paraLen # to permit exec string formatSpecial
    paraLen = 0
   
     scribus.deselectAll()
     scribus.deselectAll()
   
    ###################################
    # For each choosen header paragraph
    ###################################
   
     for j in range(len(paraStyles)):
     for j in range(len(paraStyles)):
         paraLen = len(unicode(lines[j]))
         try:
         scribus.insertText(lines[j], -1, story) # -1 append text to frame
            paraLen = len(lines[j])+1  # add new-line to counter
         scribus.selectText(textBegin, paraLen-1, story)
         except IndexError,err:
         applyStyle(paraStyles[j], story)
            print "No more text in frame."
            return
        ######################################
        # If paragraph needs special handling,
        #   execute the function to do that.
         ######################################
          
         if paraStyles[j] in specialParagraphs:
         if paraStyles[j] in specialParagraphs:
             execString = specialParagraphs[paraStyles[j]]+'("'+paraStyles[j]+'")'
             # Ugly, I know. But I'm former Mumps programmer and I like xecute
             print execString # exec execString
            formatSpecial = 'global paraLen \nparaLen = '+specialParagraphs[paraStyles[j]]+ \
         if hyphenated.count(paraStyles[j]) != 0:
            '(textBegin,lines[j]+"\\n",paraStyles[j],story)'
             scribus.hyphenateText()
            # scribus.messageBox("Debug",formatSpecial,ICON_WARNING,BUTTON_OK)
             exec formatSpecial
           
        ############################################
        # If paragraph don't needs special handling,
         #  insert and format.
        ############################################
        else:
            paraLen = formatParagraph(textBegin,lines[j]+'\n',paraStyles[j],story)
              
         textBegin += paraLen
         textBegin += paraLen
         scribus.deselectAll()  # if not desselect all, strange select errors happens
         # if not desselect all, strange select errors happens
        scribus.deselectAll() 
   
    ########################################
    # Continue formating text after headers:
    # format body text and last paragraph.
    ########################################
    bodySty = para7style.get()
   
    ##########################
    # For each body paragraph:
    ##########################
      
      
    # Format body text
     for n in range((j+1),len(lines)):
     for n in range((j+1),len(lines)):
         paraLen = len(unicode(lines[n]))
         paraLen = len(lines[n])+1  # add 'new-line' to counter
         scribus.insertText(lines[n], -1, story) # append text to frame
       
        scribus.selectText(textBegin, paraLen-1, story)
        ###########################
         # Short paragraphs without dots are formated as 'Subheaders'.
        # Format bulleted lists
         if ((paraLen < subHeaderThreshold) and (lines[n].find('.')) == -1):
        # (lines begining with '* '
             applyStyle(subHeaderStyle, story)
        # or with bullet char)
         ###########################
       
        if (lines[n][0:2] == '* ') or (lines[n][0:1] == bullet):
            paraLen = formatBulletedList(textBegin,lines[n]+'\n',bulletedListStyle,story)
       
        ###############################
         # If is short paragraph without
        # dots, format as 'Subheader'.
         ###############################
       
        elif ((paraLen < subHeaderThreshold) and (lines[n].find('.')) == -1):
             paraLen = formatParagraph(textBegin,lines[n]+'\n',subHeaderStyle,story)
           
        #########################
        # If is normal paragraph:
        #########################
       
         else:
         else:
             if n == (len(lines) - 1):  # apply style to last paragraph
           
                 applyStyle(paraLastStyle.get(), story)
            #######################
                 scribus.hyphenateText()
            # If is last paragraph:
             else: # Apply default style to all text
            #######################
                 applyStyle(para7style.get(), story)
           
                 scribus.hyphenateText()
             if (n == (len(lines) - 1)) and (chkLast.get() =='1'):
               
                ######################################
                # If paragraph needs special handling,
                # execute the function to do that.
                ######################################
               
                 lastPara = paraLastStyle.get()
                if lastPara in specialParagraphs:
                    formatSpecial = 'global paraLen\nparaLen = '+specialParagraphs[lastPara]+ \
                    '(textBegin,lines[n],lastPara,story)'
                    exec formatSpecial  # function need handle end bullets
               
                #################################################
                 # If last paragraph don't needs special handling,
                # just insert and format.
                #################################################
               
                else:
                    paraLen = formatLastParagraph(textBegin,lines[n],lastPara,story)
               
            #############################################
            # If is not last paragraph, apply body style:
            #############################################
             else:
                # If style needs special handling
                 if bodySty in specialParagraphs:
                    formatSpecial = 'global paraLen\nparaLen = '+specialParagraphs[bodySty]+ \
                    '(textBegin,lines[n]+"\\n",bodySty,story)'
                    exec formatSpecial
               
                 # If style don't needs special handling, insert and format.
                else:
                    paraLen = formatParagraph(textBegin,lines[n]+'\n',bodySty,story)
       
         textBegin += paraLen
         textBegin += paraLen
         scribus.deselectAll()
         scribus.deselectAll()
       
    return


###################
###################
Line 925: Line 1,225:
     if body text is align to baseline. Put here the rules that fix this.
     if body text is align to baseline. Put here the rules that fix this.
     Rules depends on your particular stylesheet.'''
     Rules depends on your particular stylesheet.'''
    debug()
     hasHeader1 = int(chk1.get())
     hasHeader1 = int(chk1.get())
     hasHeader2 = int(chk2.get())
     hasHeader2 = int(chk2.get())
Line 976: Line 1,277:
def getHeaderHeight():
def getHeaderHeight():
     '''Calculate header frame height using style sheet data.'''
     '''Calculate header frame height using style sheet data.'''
    debug()
     global chk1, para1style, para1lin  
     global chk1, para1style, para1lin  
     global chk2, para2style, para2lin
     global chk2, para2style, para2lin
Line 1,021: Line 1,323:
def roundToNearBaseline(num):
def roundToNearBaseline(num):
     '''Round vertical dimensions to near baseline grid.'''
     '''Round vertical dimensions to near baseline grid.'''
    debug()
     offset = getBaselineOffset()
     offset = getBaselineOffset()
     grid = getBaselineGrid()
     grid = getBaselineGrid()
Line 1,033: Line 1,336:
def roundToNextBaseline(num):
def roundToNextBaseline(num):
     '''Round vertical dimensions to next baseline grid.'''
     '''Round vertical dimensions to next baseline grid.'''
    debug()
     offset = getBaselineOffset()
     offset = getBaselineOffset()
     grid = getBaselineGrid()
     grid = getBaselineGrid()
Line 1,044: Line 1,348:
######################
######################
def buildStory(story):
def buildStory(story):
     '''Build headline frame.'''
     '''Build headline and body text frames.'''
    debug()
     # get selected object dimensions  
     # get selected object dimensions  
     global storyLeft, storyTop, storyWidth, storyHeight, numCols, gutter
     global storyLeft, storyTop, storyWidth, storyHeight, numCols, gutter
Line 1,106: Line 1,411:
# Do all the job
# Do all the job
def ok():
def ok():
     try: root.destroy()
     #try:  
     except: pass
    root.destroy()
     story = scribus.getSelectedObject(0)  
     #except: pass
     story = scribus.getSelectedObject(0)
    # Build title and text frames, if asked
     if action.get() == '1':
     if action.get() == '1':
         buildStory(story)
         buildStory(story)
    # Load text, if asked
     if contentSource.get() == '2':
     if contentSource.get() == '2':
         file = scribus.fileDialog("Open", "*.txt")
         file = scribus.fileDialog("Open", "*.txt")
         if file != '':
         if file != '':
             try:
             try:
                 text = open(file).read()
                 text = unicode(open(file).read())
                text = unicode(text)
             except: print 'Error reading file'
             except: print 'Error reading file'
             scribus.setText(text,story)
             scribus.setText(text,story)
    # Apply styles and format text
     formatText(story)
     formatText(story)
    # Finalize
     scribus.docChanged(True)
     scribus.docChanged(True)
     quit()
     quit()
###########
###########
def quit():
def quit():
Line 1,164: Line 1,474:
def main(argv):
def main(argv):
     if scribus.haveDoc():
     if scribus.haveDoc():
         scribus.setRedraw(False)
         #scribus.setRedraw(False)
         unit = scribus.getUnit()
         unit = scribus.getUnit()
         parseDoc()
         parseDoc()
Line 1,186: Line 1,496:
if __name__ == '__main__':
if __name__ == '__main__':
     main_wrapper(sys.argv)
     main_wrapper(sys.argv)
</nowiki></pre>
</nowiki></pre>

Revision as of 20:04, 2 May 2013

This article is part of the Scripts series.

This script show a dialog with options to be applied to a selected text frame: paragraphs styles, title frame, number of text columns, etc.

Need Tkinter.

#!/usr/bin/python
# -*- coding: utf-8 -*-

'''
BUILD NEWSPAPER STORY.
Author: prof. José Antonio Meira da Rocha
joseantoniorocha@gmail.com
http://meiradarocha.jor.br
License: GPL 2
2013-05-02a

USAGE:
Draw a text frame marking the area where you need a story, 
optionally putting text inside. Select the frame, run the script 
and choose styles to be applied, numbers of columns, gutter
and guide lines.
The script will:
1. Resize the frame to hold headers.
2. Draw a second linked frame to hold text in columns.
3. Clean up the text, ripping off blank paragraphs, double spaces, changing quotes etc.
4. Apply the chosen styles.
5. Find and apply style to short paragraphs without dot as sub-headers.

NEED:
Tcl/Tk 8.5 (or 8.4 with Tile 8.2)
Python 2.7 (or 2.5.4 with pyttk)

'''

############################
# Locales to be translated
# and user variables

subHeaderThreshold = 30 # max chars to a paragraph be styled as subHeader
defaultNumCols = '2'

# Default style names - change to match your style sheet
# Style names - change to match your style sheet
paragraph1style = 'Standing head 1'
paragraph2style = 'Headline 1'
paragraph3style = 'Deck 1'
paragraph4style = 'Byline'
paragraph5style = 'Sub byline'
paragraph6style = 'First paragraph 1'
bodyTextStyle   = 'Default Paragraph Style'
paragraphLastStyle = 'Default Paragraph Style'
defaultParagraphStyle = 'Default Paragraph Style'
subHeaderStyle = 'Subhead'

###############################
# Bullet special formats
# Put your favorite bullet to lists.
# Some OS have different character codes
bulletedListStyle = 'Bullet'
bulletSize = 0  # In points. Zero means same size as text.
# Some common dingbat fonts:
# 'ITC Zapf Dingbats Regular' # 'Dingbats Regular' # 'Wingdings Regular'
if os.name == 'posix': 
    bullet = u'\u25cf' 
    bulletFont = 'Dingbats Regular'  
else: 
    bullet = u'\u25cf'
    bulletFont = 'Times New Roman Regular' 

###############################
# Standing head special formats
standingHeadStyle = 'Standing head 1'
headBulletSize = 0
if os.name == 'posix': 
    headBullet = u'\u25ba'
    headBulletFont = 'Dingbats Regular'
else: 
    headBullet = u'\u25ba'
    headBulletFont = 'Times New Roman Regular'
   
###########################
# Bullet to mark story end. 
endBulletSize = 0
if os.name == 'posix': 
    endBullet = u'\u25a0' 
    endBulletFont = 'Dingbats Regular'
else: 
    endBullet = u'\u25a0'
    endBulletFont = 'Times New Roman Regular'

#########################################################
# Put in this list the paragraphs styles to be hyphenated
#########################################################
hyphenate = [
    paragraph6style,
    bodyTextStyle,
    paragraphLastStyle,
    bulletedListStyle
]

##########################################################
# Put in this dictionary the styles with special formating 
#   and the functions that does it (without "()"). 
# Use it to put bullets in front or at end of a paragraph,
#   format first words as bold etc.
##########################################################
specialParagraphs = {
  paragraph1style: 'formatBulletedList'
  #,paragraph4style: 'formatBulletedList'
  #,paragraphLastStyle: 'formatLastParagraph'
}

# Strings to ask to open a doc
openDocLabel= "Open a document"
askOpenDoc = "<h2>"+openDocLabel+"</h2>" \
    + "<p>Please, open a document and select some text frame before run this script</p>"

# Strings to ask to select frame
storyFrameLabel = "Select a frame"
askOpenFrame = "<h2>"+storyFrameLabel+"</h2>\n" \
    + "Draw or select <b>a text frame</b>\nwhere you want to put a story." 

# GUI strings
guiWindow = "Build story"
guiHeader = "Newspaper Story builder"
guiAction = "Action"
guiJustApplyStyles = "Just apply styles"
guiRebuildFrames = "Rebuild frame too"
guiParagr = "Apply"
guiStyle = "Style"
guiLines = "Lines"
guiBody = 'Body ¶'
guiLastPara = "Last ¶"
guiColumns = "Columns"
guiNumCols = "How many?"
guiGutter = "Gutters"
guiContent = "Content"
guiUseCurrent = "Use current"
guiGetFromClipboard = "Get from clipboard"
guiImportFromFile = "Import from file"
guiOptions = "Options"
guiDrawGuideLines = "Draw guide lines"
guiSnapToBaselineGrid = "Snap to baseline grid"
guiOK = "OK"
guiCancel = "Cancel"

# END locale strings

paraLines = (1, 2, 3, 4, 5, 6)
numColumnsList = (1, 2, 3, 4, 5, 6, 7, 8 , 9, 10, 11, 12)
################################
# Debugging functions
################################
import inspect
debuggingOn = 0
def whoami():
    return inspect.stack()[2][3]
def whosdaddy():
    return inspect.stack()[3][3]
def debug():
    if debuggingOn:
        here = "I'm %s, son of %s" % (whoami(), whosdaddy())
        scribus.statusMessage(here)
        print here

################################

# IMPORTS
import sys
import re

try:
    import scribus
except ImportError,err:
    print "This Python script is written for the Scribus scripting interface."
    print "It can only be run from within Scribus."
    sys.exit(1)
#
py2 = py30 = py31 = False
version = sys.hexversion
if version >= 0x020504F0 and version < 0x03000000 :
    py2 = True    # Python 2.5.4 or 2.6 or 2.7
    from Tkinter import *
    import ttk
elif version >= 0x03000000 and version < 0x03010000 :
    py30 = True
    from tkinter import *
    import ttk
elif version >= 0x03010000:
    py31 = True
    from tkinter import *
    import tkinter.ttk as ttk
else:
    print ("""
    You do not have a version of python supporting ttk widgets..
    You need a version >= 2.6 to execute PAGE modules
    or a version 2.5 patched with pyttk and Tcl with Tile.
    """)
    sys.exit()
#
from scribus import UNIT_POINTS, UNIT_MILLIMETERS, UNIT_CM, UNIT_INCHES, UNIT_PICAS, UNIT_CICERO, LINE_SOLID, JOIN_MITTER, BUTTON_OK, BUTTON_CANCEL, BUTTON_ABORT, BUTTON_NO, BUTTON_YES, ICON_WARNING, ICON_INFORMATION, pt, mm, p, cm, c

# Constants
inch = 0.0138888888889 # mistakenly translated to 'pol', in Portuguese localization
AROUND_FRAME = 1
APPEND = -1  # -1 append text to frame

# Measure conversion dictionaries
unitDict = {
    UNIT_POINTS:pt,
    UNIT_MILLIMETERS:mm,
    UNIT_INCHES:inch,
    UNIT_PICAS:(pt/12),  # Fix bug in Scribus
    UNIT_CM:cm,
    UNIT_CICERO:c 
}
unitStrDict = {
    UNIT_POINTS:'pt',
    UNIT_MILLIMETERS:'mm',
    UNIT_INCHES:'in',
    UNIT_PICAS:'p', 
    UNIT_CM:'cm',
    UNIT_CICERO:'c' 
}

import xml.parsers.expat

################################################
# Code to handle elements of XML Scribus doc 
# Convert default points values to current units
################################################
# Dictionaries to hold style sheets
styles = {}
charstyles = {}
document = {}
pages = {}

def start_element(name, attrs):
    '''Build dictionaries from SLA doc file.'''
    # print 'Start element:', name, attrs  #debug
    global styles, charstyles, document, pages
    if name == 'DOCUMENT':
        document = attrs
    if name == 'STYLE':
        #print '=====\n',name,attrs,attrs[u'NAME']
        # A bug in style editor:
        # sometimes create a anomale style definition <STYLE LANGUAGE="Portuguese"/>
        # So we need to catch errors and ignore it
        try: styles[attrs[u'NAME']] = attrs
        except: pass
    if name == 'CHARSTYLE':
        try: charstyles[attrs[u'CNAME']] = attrs
        except: pass
    if name == 'PAGE':
        try: pages[attrs[u'NUM']] = attrs
        except: pass

def getParagraphStyle(styleName):
    '''Return a dictionary with paragraph style parameters.
    If style don't exist, create it with default paragraph parameters.'''
    try:
        res = styles[styleName]
    except:
        print styleName,'not in style sheet. Creating it.'
        scribus.createParagraphStyle(styleName)
        styles[styleName] = styles[defaultParagraphStyle]
        res = styles[defaultParagraphStyle]
    return res

def getParagraphAttribute(styleName,attrName):
    '''Return value of given attribute of given style
    ***in current mesurement units.*** '''
    res = 0
    # Find parameter in parent style, if not in current style, up to 10 levels.
    for n in range(10):
        dic = getParagraphStyle(styleName)
        if attrName in dic:
            res = dic[attrName]
            # Convert points to current units
            unit = scribus.getUnit()
            res = float(res)*unitDict[unit]
            print "Find",attrName,"=",res,'in',styleName,"."
            break
        else:
            print "\nI don't find",attrName,"in",styleName,"."
            if 'PARENT' in dic:
                styleName = dic['PARENT']
                print "I will search in his father,",styleName,"."
            else:
                print "I don't find any value. Setting to zero..."
                break
    else:
        res = 0
    return res

def getStyleLineSpacing(styleName):
    '''Return line spacing of given style.'''
    # Need test line spacing mode
    # If line space mode is automatic,
    # line space is equal font size times line space percent
    res = float(getParagraphAttribute(styleName,'LINESP'))
    return res

def getStyleLineSpaceMode(styleName):
    '''0=fixed, 1=proportional.'''
    res = int(getParagraphAttribute(styleName,'LINESPMode'))
    return res

def getStyleSpaceAbove(styleName):
    '''Return space above of given paragraph style.'''
    res = float(getParagraphAttribute(styleName,'VOR'))
    return res

def getStyleSpaceBelow(styleName):
    '''Return space below of given paragraph style.'''
    res = float(getParagraphAttribute(styleName,'NACH'))
    return res

def getBaselineGrid():
    '''Get document baseline grid.'''
    global document
    if document == {}:
        res = 0
    else:
        res = float(document['BASEGRID'])
        unit = scribus.getUnit()
        res = res*unitDict[unit]
    return res

def getBaselineOffset():
    '''Get document baseline offset.'''
    global document
    if document == {}:
        res = 0
    else:
        res = float(document['BASEO'])
        unit = scribus.getUnit()
        res = res*unitDict[unit]
    return res

def getAutoLineSpace():
    '''Get automatic line space percentage of current document.'''
    global document
    if document == {}:
        res = 0
    else:
        res = float(document['AUTOL'])
        unit = scribus.getUnit()
        res = res*unitDict[unit]
    return res

def getPageAttributes(page):
    page = page - 1
    try: res = pages[str(unicode(page))]
    except:
        print 'Page',page,'not in document.'
        res = {}
    return res

def getVAutoGap():
    page = scribus.currentPage()
    pdic = getPageAttributes(page)
    if pdic == {}:
        res = 0
    else:
        try: 
            res = float(pdic['AGverticalAutoGap'])
            unit = scribus.getUnit()
            res = res*unitDict[unit]
        except: 
            print 'AGverticalAutoGap','not in page',page
    return res

def parseDoc():
    ''' Get attributes from current Scribus doc.'''
    debug()
    global styleList
    parser = xml.parsers.expat.ParserCreate('UTF-8')
    parser.StartElementHandler = start_element
    try: 
        docName = scribus.saveDoc()
        # Undocumented Scribus 1.3.9 function:
        docName = unicode(scribus.getDocName())  
        file = open(docName,'r')
        parser.ParseFile(file)
        file.close()
    except:
        print "Error analising doc."
        earlyExit()
    styleList = scribus.getAllStyles()
    styleList.sort()

##################
# GUI code
##################
def set_Tk_var():
    # These are Tk variables used passed to Tkinter and must be
    # defined before the widgets using them are created.
    # Paragraph 1
    global action
    action = StringVar()
    action.set("0")
    
    # Paragraph 1
    global chk1
    chk1 = StringVar()
    chk1.set('0')
    global chk1label
    chk1label = StringVar()
    chk1label.set('')
    global para1style
    para1style = StringVar()
    para1style.set(paragraph1style)
    global para1lin
    para1lin = StringVar()
    para1lin.set('1')
    
    # Paragraph 2
    global chk2
    chk2 = StringVar()
    chk2.set('1')
    global chk2label
    chk2label = StringVar()
    chk2label.set('\Uffffffff 1')
    global para2style
    para2style = StringVar()
    para2style.set(paragraph2style)
    global para2lin
    para2lin = StringVar()
    para2lin.set('1')
    
    # Paragraph 3
    global chk3
    chk3 = StringVar()
    chk3.set('1')
    global chk3label
    chk3label = StringVar()
    chk3label.set('\Uffffffff 2')
    global para3style
    para3style = StringVar()
    para3style.set(paragraph3style)
    global para3lin
    para3lin = StringVar()
    para3lin.set('1')
    
    # Paragraph 4
    global chk4
    chk4 = StringVar()
    chk4.set('1')
    global chk4label
    chk4label = StringVar()
    chk4label.set('\Uffffffff 3')
    global para4style
    para4style = StringVar()
    para4style.set(paragraph4style)
    
    # Paragraph 5
    global chk5
    chk5 = StringVar()
    chk5.set('1')
    global chk5label
    chk5label = StringVar()
    chk5label.set('\Uffffffff 4')
    global para5style
    para5style = StringVar()
    para5style.set(paragraph5style)
    
    # Paragraph 6
    global chk6
    chk6 = StringVar()
    chk6.set('1')
    global chk6label
    chk6label = StringVar()
    chk6label.set('\Uffffffff 5')
    global para6style
    para6style = StringVar()
    para6style.set(paragraph6style)
    
    # Paragraph 7
    global chk7
    chk7 = StringVar()
    chk7.set('1')
    global chk7label
    chk7label = StringVar()
    chk7label.set(guiBody)
    global para7style
    para7style = StringVar()
    para7style.set(bodyTextStyle)
    
    # Paragraph Last
    global chkLast
    chkLast = StringVar()
    chkLast.set('1')
    global paraLastStyle
    paraLastStyle = StringVar()
    paraLastStyle.set(paragraphLastStyle)
    global chkEndBull
    chkEndBull = StringVar()
    chkEndBull.set('1')
    
    # Columns
    global numCols
    numCols = StringVar()
    numCols.set(defaultNumCols)
    
    # Columns & Gaps
    global gutter, unitString
    gutter = StringVar()
    gap = getVAutoGap()
    unit = scribus.getUnit()
    unitString = unitStrDict[unit]
    gutter.set(gap)
    
    # Content
    global contentSource
    contentSource = StringVar()
    contentSource.set('0')
    
    # Guides
    global isToDrawGuides
    isToDrawGuides = StringVar()
    isToDrawGuides.set('0')
    
    # Snap to baseline guides
    global snap
    snap = StringVar()
    snap.set('0')
    
class Build_Story_dialog:
    def __init__(self, master=None):
        # Set background of toplevel window to match
        # current style
        style = ttk.Style()
        theme = style.theme_use()
        default = style.lookup(theme, 'background')
        master.configure(background=default)
        # Head label
        self.tLa33 = ttk.Label (master)
        self.tLa33.place(relx=0.02,rely=0.03)
        self.tLa33.configure(font="font12")
        self.tLa33.configure(text=guiHeader)
        
        # Actions frame
        self.tLa47 = ttk.Labelframe (master)
        self.tLa47.place(relx=0.02,rely=0.11,relheight=0.15,relwidth=0.57)
        self.tLa47.configure(text=guiAction,width="295",height="55")
        # Action radiobuttons
        self.tLa47_tRa48 = ttk.Radiobutton (self.tLa47)
        self.tLa47_tRa48.place(relx=0.03,rely=0.12,relheight=0.7,relwidth=0.45)
        self.tLa47_tRa48.configure(text=guiJustApplyStyles,variable=action,value="0")
        self.tLa47_tRa49 = ttk.Radiobutton (self.tLa47)
        self.tLa47_tRa49.place(relx=0.49,rely=0.12,relheight=0.7,relwidth=0.5)
        self.tLa47_tRa49.configure(text=guiRebuildFrames,variable=action,value="1")
        
        # Styles labels
        self.tLa34 = ttk.Label (master)
        self.tLa34.place(relx=0.02,rely=0.28)
        self.tLa34.configure(text=guiParagr)
        self.tLa35 = ttk.Label (master)
        self.tLa35.place(relx=0.16,rely=0.28)
        self.tLa35.configure(text=guiStyle)
        self.tLa36 = ttk.Label (master)
        self.tLa36.place(relx=0.48,rely=0.28)
        self.tLa36.configure(text=guiLines)
        # Para #1
        self.tCh37 = ttk.Checkbutton (master)
        self.tCh37.place(relx=0.02,rely=0.34)
        self.tCh37.configure(textvariable=chk1label,variable=chk1)

        self.tCo38 = ttk.Combobox (master)
        self.tCo38.place(relx=0.16,rely=0.34,relwidth=0.32)
        self.tCo38.configure(textvariable=para1style,values=styleList)

        self.tCo39 = ttk.Combobox (master)
        self.tCo39.place(relx=0.48,rely=0.34,relwidth=0.11)
        self.tCo39.configure(textvariable=para1lin,values=paraLines,justify="center")

        self.tCo38.configure(state="disabled")
        self.tCo39.configure(state="disabled")
        # Para #2
        self.tCh40 = ttk.Checkbutton (master)
        self.tCh40.place(relx=0.02,rely=0.42)
        self.tCh40.configure(textvariable=chk2label,variable=chk2)
        self.tCo41 = ttk.Combobox (master)
        self.tCo41.place(relx=0.16,rely=0.42,relwidth=0.32)
        self.tCo41.configure(textvariable=para2style,values=styleList)
        self.tCo42 = ttk.Combobox (master)
        self.tCo42.place(relx=0.48,rely=0.42,relwidth=0.11)
        self.tCo42.configure(textvariable=para2lin,values=paraLines,justify="center")
        self.tCo42.configure(state="disabled")
        # Para #3
        self.tCh43 = ttk.Checkbutton (master)
        self.tCh43.place(relx=0.02,rely=0.5)
        self.tCh43.configure(textvariable=chk3label,variable=chk3)
        self.tCo44 = ttk.Combobox (master)
        self.tCo44.place(relx=0.16,rely=0.5,relwidth=0.32)
        self.tCo44.configure(textvariable=para3style,values=styleList)
        self.tCo45 = ttk.Combobox (master)
        self.tCo45.place(relx=0.48,rely=0.5,relwidth=0.11)
        self.tCo45.configure(textvariable=para3lin,values=paraLines,justify="center")
        self.tCo45.configure(state="disabled")
        # Para #4
        self.tCh46 = ttk.Checkbutton (master)
        self.tCh46.place(relx=0.02,rely=0.58)
        self.tCh46.configure(textvariable=chk4label,variable=chk4)
        self.tCo47 = ttk.Combobox (master)
        self.tCo47.place(relx=0.16,rely=0.58,relwidth=0.32)
        self.tCo47.configure(textvariable=para4style,values=styleList)
        # Para #5
        self.tCh49 = ttk.Checkbutton (master)
        self.tCh49.place(relx=0.02,rely=0.66)
        self.tCh49.configure(textvariable=chk5label,variable=chk5)
        self.tCo50 = ttk.Combobox (master)
        self.tCo50.place(relx=0.16,rely=0.66,relwidth=0.32)
        self.tCo50.configure(textvariable=para5style,values=styleList)
        # Para #6
        self.tCh52 = ttk.Checkbutton (master)
        self.tCh52.place(relx=0.02,rely=0.74)
        self.tCh52.configure(textvariable=chk6label,variable=chk6,state="disabled")
        self.tCo53 = ttk.Combobox (master)
        self.tCo53.place(relx=0.16,rely=0.74,relwidth=0.32)
        self.tCo53.configure(textvariable=para6style,values=styleList)
        # Para #7
        self.tCh54 = ttk.Checkbutton (master)
        self.tCh54.place(relx=0.02,rely=0.82)
        self.tCh54.configure(textvariable=chk7label,variable=chk7)
        self.tCo55 = ttk.Combobox (master)
        self.tCo55.place(relx=0.16,rely=0.82,relwidth=0.32)
        self.tCo55.configure(textvariable=para7style,values=styleList)
        # Para #last
        self.tCh33 = ttk.Checkbutton (master)
        self.tCh33.place(relx=0.02,rely=0.9)
        self.tCh33.configure(text=guiLastPara,variable=chkLast)
        self.tCo34 = ttk.Combobox (master)
        self.tCo34.place(relx=0.16,rely=0.9,relwidth=0.32)
        self.tCo34.configure(textvariable=paraLastStyle,values=styleList)
        self.tCh34b = ttk.Checkbutton (master)
        self.tCh34b.place(relx=0.49,rely=0.9)
        self.tCh34b.configure(text="Bullet",variable=chkEndBull)
        
        
        # Content Frame
        self.tLa38 = ttk.Labelframe (master)
        self.tLa38.place(relx=0.62,rely=0.11,relheight=0.26,relwidth=0.36)
        self.tLa38.configure(text=guiContent,width="185",height="95")
        # Use current label
        self.tLa38_cpd39 = ttk.Radiobutton (self.tLa38)
        self.tLa38_cpd39.place(relx=0.05,rely=0.08)
        self.tLa38_cpd39.configure(text=guiUseCurrent,variable=contentSource,value="0")
        # Import from file label
        self.tLa38_cpd41 = ttk.Radiobutton (self.tLa38)
        self.tLa38_cpd41.place(relx=0.05,rely=0.36)
        self.tLa38_cpd41.configure(text=guiImportFromFile,variable=contentSource,value="2")
        #self.tLa38_cpd41.configure(state="disabled")
        # Get from clipboard label
        self.tLa38_cpd40 = ttk.Radiobutton (self.tLa38)
        self.tLa38_cpd40.place(relx=0.05,rely=0.63)
        self.tLa38_cpd40.configure(text=guiGetFromClipboard,variable=contentSource,value="1")
        self.tLa38_cpd40.configure(state="disabled")

        # Columns frame
        self.tLa32 = ttk.Labelframe (master)
        self.tLa32.place(relx=0.62,rely=0.37,relheight=0.21,relwidth=0.36)
        self.tLa32.configure(text=guiColumns,width="185",height="70")
        # How many label
        self.tLa32_cpd33 = ttk.Label (self.tLa32)
        self.tLa32_cpd33.place(relx=0.05,rely=0.07)
        self.tLa32_cpd33.configure(text=guiNumCols)
        self.tLa32_cpd33.configure(state="disabled")
        # How many combo
        self.tLa32_cpd34 = ttk.Combobox (self.tLa32)
        self.tLa32_cpd34.place(relx=0.05,rely=0.4,relwidth=0.36)
        self.tLa32_cpd34.configure(justify="center",textvariable=numCols,values=numColumnsList)
        self.tLa32_cpd34.configure(state="disabled")
        self.tLa32_cpd34.configure(validate="all",validatecommand="self.validateColumns")
        # Gutter label
        self.tLa32_cpd35 = ttk.Label (self.tLa32)
        self.tLa32_cpd35.place(relx=0.5,rely=0.07)
        self.tLa32_cpd35.configure(justify="center",text=guiGutter)
        self.tLa32_cpd35.configure(state="disabled")
        # Gutter combo
        self.tLa32_cpd36 = ttk.Combobox (self.tLa32)
        self.tLa32_cpd36.place(relx=0.5,rely=0.4,relwidth=0.33)
        self.tLa32_cpd36.configure(justify="center",values=numColumnsList,textvariable=gutter)
        self.tLa32_cpd36.configure(state="disabled")
        # measure
        self.tLa32_cpd37 = ttk.Label (self.tLa32)
        self.tLa32_cpd37.place(relx=0.84,rely=0.5)
        self.tLa32_cpd37.configure(text=unitString)
        self.tLa32_cpd37.configure(state="disabled")

        # Options frame
        self.tLa42 = ttk.Labelframe (master)
        self.tLa42.place(relx=0.62,rely=0.58,relheight=0.23,relwidth=0.36)
        self.tLa42.configure(text=guiOptions,width="185",height="80")
        # Draw guides check
        self.tLa42_cpd44 = ttk.Checkbutton (self.tLa42)
        self.tLa42_cpd44.place(relx=0.05,rely=0.08)
        self.tLa42_cpd44.configure(text=guiDrawGuideLines,variable=isToDrawGuides)
        self.tLa42_cpd44.configure(state="disabled")
        # Snap guides check
        self.tLa42_cpd46 = ttk.Checkbutton (self.tLa42)
        self.tLa42_cpd46.place(relx=0.05,rely=0.5)
        self.tLa42_cpd46.configure(text=guiSnapToBaselineGrid,variable=snap)
        self.tLa42_cpd46.configure(state="disabled")

        # Button OK
        self.tBu67 = ttk.Button (master)
        self.tBu67.place(relx=0.81,rely=0.89)
        self.tBu67.configure(text=guiOK,command=ok)
        # Button Cancel
        self.tBu68 = ttk.Button (master)
        self.tBu68.place(relx=0.62,rely=0.89)
        self.tBu68.configure(text=guiCancel,command=quit)
    
        # Bind mouse button release to Rebuild check mark
        self.tLa47_tRa48.bind("<ButtonRelease-1>", self.__handleRebuild)
        self.tLa47_tRa49.bind("<ButtonRelease-1>", self.__handleRebuild)
        self.tCh37.bind("<ButtonRelease-1>", self.__handlePara1)
        self.tCh40.bind("<ButtonRelease-1>", self.__handlePara2)
        self.tCh43.bind("<ButtonRelease-1>", self.__handlePara3)
        self.tCh46.bind("<ButtonRelease-1>", self.__handlePara4)
        self.tCh49.bind("<ButtonRelease-1>", self.__handlePara5)
        #self.tCh52.bind("<ButtonRelease-1>", self.__handlePara6)
        self.tCh54.bind("<ButtonRelease-1>", self.__handlePara7)
        self.tCh33.bind("<ButtonRelease-1>", self.__handlePara8)
        
    def validateColumns():
        pass
        
    def __handleRebuild(self,event):
        w = event.widget
        if w.cget('value') == '0':
            self.tLa32_cpd34.configure(state="disabled")  # num cols combo
            self.tLa32_cpd36.configure(state="disabled")  # gutter combo
            self.tLa42_cpd46.configure(state="disabled")  # snap check
            self.tLa42_cpd44.configure(state="disabled")  # guides check
            self.tLa32_cpd33.configure(state="disabled")  # Labels
            self.tLa32_cpd35.configure(state="disabled")
            self.tLa32_cpd37.configure(state="disabled")
            self.tCo39.configure(state="disabled")
            self.tCo42.configure(state="disabled")
            self.tCo45.configure(state="disabled")

        else:
            self.tLa32_cpd34.configure(state="enabled")
            self.tLa32_cpd36.configure(state="enabled")
            self.tLa42_cpd46.configure(state="enabled")
            self.tLa42_cpd44.configure(state="enabled")
            self.tLa32_cpd33.configure(state="enabled")
            self.tLa32_cpd35.configure(state="enabled")
            self.tLa32_cpd37.configure(state="enabled")
            if int(chk1.get()):
                self.tCo39.configure(state="enabled")
            if int(chk2.get()):
                self.tCo42.configure(state="enabled")
            if int(chk3.get()):
                self.tCo45.configure(state="enabled")
            
    def __renumberPara(self,event):
        '''Come to here with variable no set yet'''
        #w = event.widget
        #print w.keys()
        # ['takefocus', 'variable', 'onvalue', 'offvalue', 'command', 'text', 'textvariable', 'underline', 'width', 'image', 'compound', 'padding', 'state', 'takefocus', 'cursor', 'style', 'class']
        num = 0
        if chk1.get() == '1':
            num += 1
            chk1label.set('\Uffffffff '+str(num))
        else: chk1label.set('')
        if chk2.get() == '1':
            num += 1
            chk2label.set('\Uffffffff '+str(num))
        else: chk2label.set('')
        if chk3.get() == '1':
            num += 1
            chk3label.set('\Uffffffff '+str(num))
        else: chk3label.set('')
        if chk4.get() == '1':
            num += 1
            chk4label.set('\Uffffffff '+str(num))
        else: chk4label.set('')
        if chk5.get() == '1':
            num += 1
            chk5label.set('\Uffffffff '+str(num))
        else: chk5label.set('')
        if chk6.get():
            num += 1
            chk6label.set('\Uffffffff '+str(num))
        else: chk6label.set('')
       
    def __handlePara1(self,event):
        if chk1.get() == '0':
            self.tCo38.configure(state="enabled")
            if action.get() == '1':
                self.tCo39.configure(state="enabled")
            chk1.set('1')
            self.__renumberPara(event)
            chk1.set('0')
        else:
            self.tCo38.configure(state="disabled")
            self.tCo39.configure(state="disabled")
            chk1.set('0')
            self.__renumberPara(event)
            chk1.set('1')

    def __handlePara2(self,event):
        if chk2.get() == '0':
            self.tCo41.configure(state="enabled")
            if action.get() == '1':
                self.tCo42.configure(state="enabled")
            chk2.set('1')
            self.__renumberPara(event)
            chk2.set('0')
        else:
            self.tCo41.configure(state="disabled")
            self.tCo42.configure(state="disabled")
            chk2.set('0')
            self.__renumberPara(event)
            chk2.set('1')

    def __handlePara3(self,event):
        if chk3.get() == '0':
            self.tCo44.configure(state="enabled")
            if action.get() == '1':
                self.tCo45.configure(state="enabled")
            chk3.set('1')
            self.__renumberPara(event)
            chk3.set('0')
        else:
            self.tCo44.configure(state="disabled")
            self.tCo45.configure(state="disabled")
            chk3.set('0')
            self.__renumberPara(event)
            chk3.set('1')

    def __handlePara4(self,event):
        if chk4.get() == '0':
            self.tCo47.configure(state="enabled")
            chk4.set('1')
            self.__renumberPara(event)
            chk4.set('0')
        else:
            self.tCo47.configure(state="disabled")
            chk4.set('0')
            self.__renumberPara(event)
            chk4.set('1')

    def __handlePara5(self,event):
        if chk5.get() == '0':
            self.tCo50.configure(state="enabled")
            chk5.set('1')
            self.__renumberPara(event)
            chk5.set('0')
        else:
            self.tCo50.configure(state="disabled")
            chk5.set('0')
            self.__renumberPara(event)
            chk5.set('1')

    def __handlePara6(self,event):
        if chk6.get() == '0':
            self.tCo53.configure(state="enabled")
            chk6.set('1')
            self.__renumberPara(event)
            chk6.set('0')
        else:
            self.tCo53.configure(state="disabled")
            chk6.set('0')
            self.__renumberPara(event)
            chk6.set('1')

    def __handlePara7(self,event):
        if chk7.get() == '0':
            self.tCo55.configure(state="enabled")
        else:
            chk7.set('0')
            self.__renumberPara(event)
            chk7.set('1')
            self.tCo55.configure(state="disabled")

    def __handlePara8(self,event):
        if chkLast.get() == '0':
            self.tCo34.configure(state="enabled")
        else:
            
            self.tCo34.configure(state="disabled")

def init():
    pass

#####################
# Clean text
def normalizeText(t):
    '''Rip off blank paragraphs, double spaces, html tags, quotes etc.'''
    debug()
    t = t.strip()
    while t.count('\t'):
        t = t.replace('\t','')
    while t.count('  '):
        t = t.replace('  ',' ')
    while t.count('<br>'):
        t = t.replace('<br>','\n')
    while t.count('\r'):             # DOS/Windows paragraph end. 
        t = t.replace('\r','\n') # Change by new line
    while t.count(' \n'):
        t = t.replace(' \n','\n')
    while t.count('\n '):
        t = t.replace('\n ','\n')
    while t.count('\n\n'):
        t = t.replace('\n\n','\n')
    # Create m-dash
    while t.count('\n-'):
        t = t.replace('\n-','\n')
    # Change computer quotes by typographic quotes
    while t.count('\n"'):
        t = t.replace('\n"','\n')
    while t.count(' "'):
        t = t.replace(' "',' ')
    while t.count('" '):
        t = t.replace('" ',' ')
    while t.count('",'):
        t = t.replace('",',',')
    while t.count('".'):
        t = t.replace('".','.')
    while t.count('."'):
        t = t.replace('."','.')
    while t.count('.'):
        t = t.replace('.','.')
    
    return t

#######################
# Format bulleted lists 
def formatBulletedList(textBegin,text,style,story):
    '''Insert bullet at first paragraph.'''
    debug()
    # Clear old marks
    reBullet  = r'['+bullet+'|(^\*)]'
    s = re.sub(reBullet,'',text)
    s = s.strip(" ")
    paraLen = len(s)
    scribus.insertText(s, APPEND, story)
    try:
        scribus.selectText(textBegin, paraLen-1, story)
        applyStyle(style, story)
    except: print "Can't select and apply style",style
    # Hyphenate if style is in hyphenation list
    if hyphenate.count(style) != 0:
        scribus.hyphenateText()
    # Insert bullet
    b = bullet+u'\t'
    scribus.insertText(b, textBegin, story)
    scribus.selectText(textBegin,1,story)
    if bulletFont:
        try: scribus.setFont(bulletFont,story)
        except: print "Can't find font:",bulletFont
    if bulletSize:
        try:
            scribus.selectText(textBegin,1,story)
            scribus.setFontSize(bulletSize,story)
        except: print "Can't size bullet."
    return paraLen+len(b)

# Tie above function to style name
specialParagraphs[bulletedListStyle] = 'formatBulletedList'

#######################
# Format Standing Head 
def formatStandingHead(textBegin,text,style,story):
    '''Insert bullet in standing head.'''
    # Clear old marks
    debug()
    reBullet  = r''+headBullet
    s = re.sub(reBullet,'',text)
    s = s.strip(" ")
    paraLen = len(s)
    scribus.insertText(s, APPEND, story)
    try:
        scribus.selectText(textBegin, paraLen-1, story)
        applyStyle(style, story)
    except: print "Can't select and apply style",style
    # Hyphenate if style is in hyphenation list
    if hyphenate.count(style) != 0:
        scribus.hyphenateText()
    # Insert bullet
    b = headBullet+u'\t'
    scribus.insertText(b, textBegin, story)
    scribus.selectText(textBegin,1,story)
    if headBulletFont:
        try: scribus.setFont(headBulletFont,story)
        except: print "Can't find font:",headBulletFont
    if headBulletSize:
        try:
            scribus.selectText(textBegin,1,story)
            scribus.setFontSize(headBulletSize,story)
        except: print "Can't size bullet."
    return paraLen+len(b)

# Tie above function to style name
specialParagraphs[standingHeadStyle] = 'formatStandingHead'

######################
# formatLastParagraph.
# exec by formatText
######################
def formatLastParagraph(textBegin,text,style,story):
    '''Do especial formating in last paragraph,
    as put a bullet at end of paragraph.'''
    # Clear old marks
    debug()
    reEndBullet  = r''+endBullet
    s = re.sub(reEndBullet,'',text)
    s = s.strip(" ")
    paraLen = len(s)
    endPara = textBegin+paraLen
    scribus.insertText(s, APPEND, story)
    scribus.selectText(textBegin, paraLen-1, story)
    applyStyle(style, story)
    # Hyphenate if style is in hyphenation list
    if hyphenate.count(style) != 0:
        scribus.hyphenateText()
    # If terminate with bullet
    if (chkEndBull.get() == '1'):
        # Format end bullet
        eb = ' '+endBullet
        scribus.insertText(eb, APPEND, story)
        if endBulletFont:
            scribus.selectText(endPara+1,1,story)
            try: scribus.setFont(endBulletFont,story)
            except: print "Can't find font:",endBulletFont
    if endBulletSize:
        scribus.selectText(endPara+1,1,story)
        try: scribus.setFontSize(endBulletSize,story)
        except: print "Can't size end bullet."
    
    return paraLen

###################
# Insert text and
# format paragraph
def formatParagraph(textBegin,text,style,story):
    '''Insert text and format paragraph.'''
    debug()
    paraLen = len(text)
    scribus.insertText(text, APPEND, story)
    scribus.selectText(textBegin, paraLen-1, story)
    applyStyle(style, story)
    # Hyphenate if style is in hyphenation list
    if hyphenate.count(style) != 0:
        scribus.hyphenateText()
    
    return paraLen

###################
def applyStyle(style,story):
    '''Try apply style to selected text. If style doesn't exist, create it.'''
    debug()
    try:
        scribus.setStyle(style, story)
    except:
        scribus.createParagraphStyle(style)
        scribus.setStyle(style, story)
    return story

####################
def formatText(story):
    '''Format story with styles pre-header, header, sub-header etc.'''
    # To do: translate italics, bolds, superscripts etc here
    debug()
    paraStyles = [] 
    # Build a list of choosen styles
    if chk1.get() == '1':
        paraStyles.append(para1style.get())
    if chk2.get() == '1':
        paraStyles.append(para2style.get())
    if chk3.get() == '1':
        paraStyles.append(para3style.get())
    if chk4.get() == '1':
        paraStyles.append(para4style.get())
    if chk5.get() == '1':
        paraStyles.append(para5style.get())
    if chk6.get() == '1':  # let ever on
        paraStyles.append(para6style.get())

    ####################################
    # Format headers and first paragraph
    
    scribus.deselectAll()
    scribus.selectObject(story)
    alltext = scribus.getAllText(story)
    # Do nothing if no text
    if len(alltext) == 0:
        return
    alltext = normalizeText(alltext)
    # Convert text to unicode
    alltext = unicode(alltext)
    scribus.deleteText(story)
    # lines = alltext.splitlines(1) 
    # splitlines can't be used because catch line break '\x1C' as paragraph end
    lines = alltext.split('\n')  # avoid separete paragraphs with line breaks (\x1C')
    # print repr(lines)  # debug
    textBegin = 0
    global paraLen # to permit exec string formatSpecial
    paraLen = 0
    
    scribus.deselectAll()
    
    ###################################
    # For each choosen header paragraph
    ###################################
    
    for j in range(len(paraStyles)):
        try:
            paraLen = len(lines[j])+1  # add new-line to counter
        except IndexError,err:
            print "No more text in frame."
            return
        ######################################
        # If paragraph needs special handling,
        #   execute the function to do that.
        ######################################
        
        if paraStyles[j] in specialParagraphs:
            # Ugly, I know. But I'm former Mumps programmer and I like xecute
            formatSpecial = 'global paraLen \nparaLen = '+specialParagraphs[paraStyles[j]]+ \
            '(textBegin,lines[j]+"\\n",paraStyles[j],story)'
            # scribus.messageBox("Debug",formatSpecial,ICON_WARNING,BUTTON_OK)
            exec formatSpecial
            
        ############################################
        # If paragraph don't needs special handling, 
        #  insert and format.
        ############################################
        else:
            paraLen = formatParagraph(textBegin,lines[j]+'\n',paraStyles[j],story)
            
        textBegin += paraLen
        # if not desselect all, strange select errors happens
        scribus.deselectAll()  
    
    ########################################
    # Continue formating text after headers:
    # format body text and last paragraph.
    ########################################
    bodySty = para7style.get()
    
    ##########################
    # For each body paragraph:
    ##########################
    
    for n in range((j+1),len(lines)):
        paraLen = len(lines[n])+1  # add 'new-line' to counter
        
        ###########################
        # Format bulleted lists
        # (lines begining with '* '
        # or with bullet char)
        ###########################
        
        if (lines[n][0:2] == '* ') or (lines[n][0:1] == bullet): 
            paraLen = formatBulletedList(textBegin,lines[n]+'\n',bulletedListStyle,story)
        
        ###############################
        # If is short paragraph without
        # dots, format as 'Subheader'.
        ###############################
        
        elif ((paraLen < subHeaderThreshold) and (lines[n].find('.')) == -1):
            paraLen = formatParagraph(textBegin,lines[n]+'\n',subHeaderStyle,story)
            
        #########################
        # If is normal paragraph:
        #########################
        
        else:
            
            #######################
            # If is last paragraph:
            #######################
            
            if (n == (len(lines) - 1)) and (chkLast.get() =='1'):
                
                ######################################
                # If paragraph needs special handling,
                #  execute the function to do that.
                ######################################
                
                lastPara = paraLastStyle.get()
                if lastPara in specialParagraphs:
                    formatSpecial = 'global paraLen\nparaLen = '+specialParagraphs[lastPara]+ \
                    '(textBegin,lines[n],lastPara,story)'
                    exec formatSpecial  # function need handle end bullets
                
                #################################################
                # If last paragraph don't needs special handling, 
                # just insert and format.
                #################################################
                
                else:
                    paraLen = formatLastParagraph(textBegin,lines[n],lastPara,story)
                
            #############################################
            # If is not last paragraph, apply body style:
            #############################################
            else:
                # If style needs special handling
                if bodySty in specialParagraphs:
                    formatSpecial = 'global paraLen\nparaLen = '+specialParagraphs[bodySty]+ \
                    '(textBegin,lines[n]+"\\n",bodySty,story)'
                    exec formatSpecial
                
                # If style don't needs special handling, insert and format.
                else:
                    paraLen = formatParagraph(textBegin,lines[n]+'\n',bodySty,story)
        
        textBegin += paraLen
        scribus.deselectAll()

###################
def adjustHeaderHeight(storyTop,headerBottom):
    '''Rules for header End adjustments.
    Some headers + lines combinations results in bad height
    if body text is align to baseline. Put here the rules that fix this.
    Rules depends on your particular stylesheet.'''
    debug()
    hasHeader1 = int(chk1.get())
    hasHeader2 = int(chk2.get())
    hasHeader3 = int(chk3.get())
    lines1 = int(para1lin.get())
    lines2 = int(para2lin.get())
    lines3 = int(para3lin.get())
    lineSpace1 = getStyleLineSpacing(para1style.get())
    lineSpace2 = getStyleLineSpacing(para2style.get())
    lineSpace3 = getStyleLineSpacing(para3style.get())
    grid = getBaselineGrid()
    headerHeight = headerBottom - storyTop
    unit = scribus.getUnit()
    threshold = grid*3*unitDict[unit]
    headerHeight = max(headerHeight,threshold)
    headerBottom = storyTop + headerHeight
    columnsTop = headerBottom
    print 'threshold:',threshold,'headerHeight:',headerHeight
    if hasHeader1:
        pass
            
    if hasHeader2:
        if lines2 == 1:
            print 'lineSpace2:',lineSpace2
            if hasHeader3:
                if (lineSpace2 < (52*unitDict[unit])) or (lineSpace2 > (41*unitDict[unit])):
                    columnsTop -= grid
                    return headerBottom,columnsTop
            if not hasHeader3:
                if (lineSpace2 < (41*unitDict[unit])) or (lineSpace2 > (32*unitDict[unit])):
                    columnsTop -= grid
                    return headerBottom,columnsTop
                
        if lines2 == 2:
            if not hasHeader3:
                if (lineSpace2 < (37*unitDict[unit])) or (lineSpace2 > (30*unitDict[unit])):
                    columnsTop -= grid
                    return headerBottom,columnsTop
                
        if lines2 == 3:
            if (lineSpace2 < (52*unitDict[unit])) or (lineSpace2 > (41*unitDict[unit])):
                columnsTop -= grid
                return headerBottom,columnsTop

    if hasHeader3:
        pass
    
    return headerBottom,columnsTop

######################
def getHeaderHeight():
    '''Calculate header frame height using style sheet data.'''
    debug()
    global chk1, para1style, para1lin 
    global chk2, para2style, para2lin
    global chk3, para3style, para3lin
    height = 0
    # Pre-header
    hasHeader1 = int(chk1.get())
    if hasHeader1:
        style = para1style.get()
        lineSpace = getStyleLineSpacing(style)
        lines = int(para1lin.get())
        lineSpace *= lines
        spMode= getStyleLineSpaceMode(style)
        if spMode == 1:
            lineSpace *= ( 1 + ( getAutoLineSpace()/100 ) )
        lineSpace += getStyleSpaceAbove(style) + getStyleSpaceBelow(style)
        height = lineSpace 
    # Header
    hasHeader2 = int(chk2.get())
    if hasHeader2:
        style = para2style.get()
        lineSpace = getStyleLineSpacing(style)
        lines = int(para2lin.get())
        lineSpace *= lines
        spMode= getStyleLineSpaceMode(style)
        if spMode == 1:
            lineSpace *= ( 1 + ( getAutoLineSpace()/100 ) )
        lineSpace += getStyleSpaceAbove(style) + getStyleSpaceBelow(style)
        height += lineSpace
    # Post-header
    hasHeader3 = int(chk3.get())
    if hasHeader3:
        style = para3style.get()
        lineSpace = getStyleLineSpacing(style)
        lines = int(para3lin.get())
        lineSpace *= lines
        spMode= getStyleLineSpaceMode(style)
        if spMode == 1:
            lineSpace *= ( 1 + ( getAutoLineSpace()/100 ) )
        lineSpace += getStyleSpaceAbove(style) + getStyleSpaceBelow(style)
        height += lineSpace
    return height

#############################
def roundToNearBaseline(num):
    '''Round vertical dimensions to near baseline grid.'''
    debug()
    offset = getBaselineOffset()
    grid = getBaselineGrid()
    n = num - offset
    times,mod = divmod(n,grid)
    if (mod < (grid / 2)):
        res = offset + (times * grid)
    else:
        res = offset + (times * grid) + grid
    return res

def roundToNextBaseline(num):
    '''Round vertical dimensions to next baseline grid.'''
    debug()
    offset = getBaselineOffset()
    grid = getBaselineGrid()
    n = num - offset
    times,mod = divmod(n,grid)
    res = offset + (times * grid)
    if res != num:
        res += grid
    return res

######################
def buildStory(story):
    '''Build headline and body text frames.'''
    debug()
    # get selected object dimensions 
    global storyLeft, storyTop, storyWidth, storyHeight, numCols, gutter
    global columnsFrame, headerHeight, columnsTop
    # Get frame dimensions
    storyLeft, storyTop = scribus.getPosition(story)
    storyWidth, storyHeight = scribus.getSize(story)
    
    # BUILD HEADLINES FRAME
    headerHeight = getHeaderHeight()
    headerBottom = storyTop + headerHeight
    # Round header height to baseline grid
    if snap.get() == '1':
        grid = getBaselineGrid()
        storyTop = roundToNearBaseline(storyTop)
        headerBottom = roundToNextBaseline(headerBottom)
        # Adjust some bad headerBottom
        headerBottom,columnsTop = adjustHeaderHeight(storyTop,headerBottom)
        #columnsTop = headerBottom
    else:
        columnsTop = headerBottom
    headerHeight = headerBottom - storyTop
    # Resize header frame
    scribus.moveObjectAbs(storyLeft, storyTop ,story)
    scribus.sizeObject(storyWidth, headerHeight, story)
    scribus.textFlowMode(story, 0) # 
    # Set just one column for header
    scribus.setColumns(1, story)
    
    # BUILD BODY TEXT FRAME
    columnsBottom = storyHeight + storyTop
    if snap.get() == '1':
        columnsBottom = roundToNextBaseline(columnsBottom)
    gap = float(gutter.get())
    columnsHeight = columnsBottom - columnsTop
    columnsFrame = scribus.createText(storyLeft, columnsTop, storyWidth, columnsHeight)
    scribus.textFlowMode(columnsFrame, 0)
    columnNumbers = int(numCols.get())
    scribus.setColumnGap(gap, columnsFrame)
    scribus.setColumns(columnNumbers, columnsFrame)
    
    # Draw guides
    if isToDrawGuides.get() == '1':
        # Append guides to existing guides array
        scribus.setHGuides(scribus.getHGuides() + [storyTop] + [columnsTop] + [columnsBottom])
        scribus.setVGuides(scribus.getVGuides() + [storyLeft-gap])
        scribus.setVGuides(scribus.getVGuides() + [storyLeft,(storyLeft+storyWidth)])
        scribus.setVGuides(scribus.getVGuides() + [(storyLeft+storyWidth+gap)])
        columnWidth = (storyWidth - (gap * (columnNumbers - 1))) / columnNumbers
        nextCol = storyLeft+columnWidth
        columnGuides = []
        for col in range(1,columnNumbers):
            columnGuides = columnGuides + [nextCol,(nextCol+gap)]
            nextCol = nextCol + columnWidth + gap
        scribus.setVGuides(scribus.getVGuides() + columnGuides)
    
    # Link header frame to body text frame
    scribus.linkTextFrames(story,columnsFrame)

##################
# Do all the job
def ok():
    #try: 
    root.destroy()
    #except: pass
    story = scribus.getSelectedObject(0)
    # Build title and text frames, if asked
    if action.get() == '1':
        buildStory(story)
    # Load text, if asked
    if contentSource.get() == '2':
        file = scribus.fileDialog("Open", "*.txt")
        if file != '':
            try:
                text = unicode(open(file).read())
            except: print 'Error reading file'
            scribus.setText(text,story)
    # Apply styles and format text
    formatText(story)
    # Finalize
    scribus.docChanged(True)
    quit()

###########
def quit():
    earlyExit()

def earlyExit():
    if scribus.haveDoc():
        scribus.setRedraw(True)
    scribus.statusMessage("")
    scribus.progressReset()
    try: root.destroy()
    except: pass
    sys.exit()

######################
def handleSelection():
    '''Handle object selection.'''
    global storyFrame
    if (scribus.selectionCount() < 1):
        scribus.messageBox(storyFrameLabel,askOpenFrame,ICON_WARNING,BUTTON_OK)
        earlyExit()
    else:
        pass

def vp_start_gui():
    global val, w, root
    root = Tk()
    # Center the dialog
    wid, hei = root.winfo_screenwidth(), root.winfo_screenheight()
    x = (wid - 517) / 2
    y = (hei - 363) / 2
    geo = '517x363+'+str(x)+'+'+str(y)
    #
    root.title(guiWindow)
    root.geometry(geo)
    set_Tk_var()
    w = Build_Story_dialog (root)
    init()
    handleSelection()
    root.mainloop()

def main(argv):
    if scribus.haveDoc():
        #scribus.setRedraw(False)
        unit = scribus.getUnit()
        parseDoc()
        vp_start_gui()
    else:
        scribus.messageBox(openDocLabel,askOpenDoc,ICON_WARNING,BUTTON_OK)
        earlyExit()

def main_wrapper(argv):
    """"""
    try:
        scribus.statusMessage("Running script...")
        scribus.progressReset()
        main(argv)
    finally:
        if scribus.haveDoc():
            scribus.setRedraw(True)
        scribus.statusMessage("")
        scribus.progressReset()

if __name__ == '__main__':
    main_wrapper(sys.argv)