Automatic story formatting: Difference between revisions

From Scribus Wiki
Jump to navigation Jump to search
No edit summary
(my tailor is rich)
 
(5 intermediate revisions by one other user not shown)
Line 1: Line 1:
[[Category:Scripts]]
[[Category:Scripts]]
{{Scripting Index}}
{{Scripting Index}}
This script show a dialog with options to be applied to a selected text frame: paragraphs styles, title frame, number of text columns, etc.  
This script shows a dialog with options to be applied to a selected text frame: paragraphs styles, title frame, number of text columns, etc.  


Need Tkinter.
Need Tkinter.
Line 11: Line 11:
'''
'''
BUILD NEWSPAPER STORY.
BUILD NEWSPAPER STORY.
Author: prof. Jos\Uffffffffntonio Meira da Rocha
Author: prof. José Antonio Meira da Rocha
joseantoniorocha@gmail.com
joseantoniorocha@gmail.com
http://meiradarocha.jor.br
http://meiradarocha.jor.br
Licence: GPL 2
License: GPL 2
2012-03-10a
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 26: Line 26:
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, changing quotes etc.
3. Clean up the text, ripping off blank paragraphs, double spaces, changing quotes etc.
4. Apply the choosen styles.
4. Apply the chosen styles.
5. Find and apply style to short paragraphs without dot as sub-headers.
5. Find and apply style to short paragraphs without dot as sub-headers.


Line 50: 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'
Line 72: Line 72:
###############################
###############################
# Standing head special formats
# Standing head special formats
standingHeadStyle = 'Standing head '
standingHeadStyle = 'Standing head 1'
headBulletSize = 0
headBulletSize = 0
if os.name == 'posix':  
if os.name == 'posix':  
Line 204: Line 204:


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


# Measure convertion dictionaries
# Measure conversion dictionaries
unitDict = {
unitDict = {
     UNIT_POINTS:pt,
     UNIT_POINTS:pt,
Line 239: Line 239:


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

Latest revision as of 20:29, 23 October 2015

This article is part of the Scripts series.

This script shows 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)