Bullets and numbered lists

Improved bullets simple script using regular expressions Python module. Build bulleted lists; numbered lists starting with any number plus separator; upper case or lower case lettered list; and delete list marks. Create styles -- if it doesn't exists -- with proper 2 digits indentation, but is necessary set tabulation "by hand", as this can't be done by script.

Install

 * 1) Copy the code and paste in a text file named "Bullets.py" or whatever "*.py".
 * 2) Run it by "Scripts > Execute script..."
 * 3) In order to scripts appear in "Scripts > Scribus scripts..." menu, put the file in:
 * Windows folder "x:\Program files\Scribus 1.3.9\share\scripts\" or in
 * Linux directory (copy as root) "/usr/share/scribus-ng/scripts/". But these folders are for official scripts and could be overwritten when Scribus reinstalls.

Usage
Select a text frame or some paragraphs and run the script by choosing "Scripts" menu, options "Execute script..." or "Scribus scripts". Asked, input the operation type:


 * "*" will create bulleted list.
 * A number (up 3 digit) will create a numbered list begining with such number. Ex.: "1)". Range: 0 to 999.
 * One or two letters, upper or lower case, will create a lettered list begining with such letters with same case. Ex.: "a)". Range: "a" to "zz".
 * "/" will clear all marks.
 * An optional character after numbers or letters will be used as separator. Default: dot.

Options
if os.name == 'posix': bulletString = u'\u006F\t' #  lower right drop-shadowed white square U+274F else: bulletString = u'\uF06F\t'
 * You can change bullets size putting any value but zero in bulletedListCharSize variable.
 * You can change bullets font putting it Scribus name in bulletedListCharFont variable.
 * This works for numbers and letters too, with its propers variables.
 * Get character hex code from menu: "Insert > Special character > [magnifying glass button]".
 * Change bullet character putting it code in bulletString variable, in place "006F" and "F06F":

Tested and working in Scribus 1.3.9 running in Windows XP, Wine and Ubuntu 10.10.

Caveats

 * The script will clear any character formating in selected paragraphs (fonts, bold, superscripts, soft hyphens etc), as scribus.getText Python function get just plain text, not formating.
 * Some fonts can have different encodings depending operational system. Bullets glyphs may not appear if a publication made in Linux is opened in Windows, and vice-versa. This is true for "Dingbats" font from URW foundry.

Code
#!/usr/bin/env python """ Insert marks, numbers or letters in front selected paragraphs. Author: prof. MS. José Antonio Meira da Rocha mailto:joseantoniomeira@gmail.com http://meiradarocha.jor.br License GPL 2.0 2011-01-17a Usage:
 * 1) -*- coding: utf-8 -*-

=
Select paragraphs at any points. Dialog will ask mark type. Input: Caveats: Tabulation need to be defined by user to identical indentation value. Bullets glyphs can't appear if a publication made in Linux is opened in Windows, and vice-versa. This is true for "Dingbats" font from URW foundry. """ import sys import re import os 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) from scribus import BUTTON_OK, ICON_WARNING numberedListParaStyle = 'Numbered list' numberedListCharFont =   numberedListCharSize = 0  # In points. Zero don't apply font size letteredListParaStyle = 'Lettered list' letteredListCharFont =  #  letteredListCharSize = 0 #  bulletedListParaStyle = 'Bullet' bulletedListCharFont = 'Dingbats Regular' # 'Wingdings Regular' #  bulletedListCharSize = 0 if os.name == 'posix':    bulletString = u'\u006F\t'  #  lower right drop-shadowed white square U+274F else:    bulletString = u'\uF06F\t' defaultParaStyle = 'Default Paragraph Style' defaultCharStyle = 'Default Character Style' separator = '.' indent = 22 # points
 * "*" will create bulleted list.
 * A number (up 3 digit) will create a numbered list begining with such number. Ex.: "1)"
 * One or two letters uppper or lower case will create a lettered list begining with such letters. Ex.: "a)"
 * An optional character after numbers or letters will be used as separator. Default: dot.
 * "/" will clear all marks.
 * Cleans all text format of text selection (font, bold, subscript, etc).
 * Creates paragraph style "Numbered List", "Lettered list" or "Bullet", if it doesn't exist, with proper indentation.
 * If there is no selection, script will apply marks to all text in current frame.
 * Some fonts can have different encodings depending operational system.
 * 1) Change this variables
 * 2) to match your needs
 * 1) to match your needs
 * 1) Font name in Scribus style. Empty string don't apply font.
 * 2) Get font list with 'getFontNames'  in Scripter console
 * 1) Put your favorite unicode char and tab: '\u2022\t' # u'•\t' # bullet U+2022
 * 2) Get hex code from menu: "Insert > Special character > [magnifying button]".
 * 3) Modern dingbat fonts, as Zapf Dingbats or Wingdings, need special hex codes.
 * 4) URW "Dingbats Regular" Type 1 font have different code in Linux and in Windows,
 * 5) none of them Unicode.
 * 6) "ITC Zapf Dingbats Regular" have same code in both OS, but not Unicode encoding.
 * 1) Locales
 * 1) Locales

scriptWindowTitle = "Format list" askSelectText = " Select a text \n" \ +"Select a story text where you want insert numbers."
 * 1) Message to ask frame selection

askOpenDoc = " Open a document " \ +"Open a document before run this script."
 * 1) Message to open a document

askSelectMoreText = " Select more text " \ +"Not unique text selected. Select more text before run this script."
 * 1) Message asking select more text

wrongFrameTypeErrorMsg = " Wrong object selected " \ +"You must select a text frame." markOperation = '' bulletOperation = '*' numberOperation = '1' cleanOperation = '/' alphaOperation = 'a' defaultOperation = bulletOperation alphabet = 'abcdefghijklmnopqrstuvwxyz' alphabetLen = len(alphabet)

askOperationMsg = ' What mark operation? ' \ +'"'+bulletOperation+'" input will create bullet marks.' \ +'and apply +bulletedListParaStyle+ style.' \ +'A number input will create numbered list starting ' \ +'with such number and apply +numberedListParaStyle+ style.' \ +'One or two letters input, upper or lower case, ' \ +'will create lettered list starting with such letters, ' \ +'and apply +letteredListParaStyle+ style.' \ +'Optional char after number or letter ' \ +'will be used as separator. Default: dot. Ex.: "a)" ' \ +'"'+cleanOperation.lower+'" input will delete marks ' \ +'and apply +defaultParaStyle+ style.' \ +'Any other input will cancel all operations.' \ reAnyMark  = r'(\r[^\t|^\r]{,4}\t)'  def applyStyle(style,frame):    Apply a style in selected text.      If style doesn't exist, create it.    try:        scribus.setStyle(style,frame)    except:        scribus.createParagraphStyle(name=style,leftmargin=indent,firstindent=(indent*-1))        scribus.setStyle(style,frame)    return frame def getTextCursor(story):    Find position of text cursor in text frame.    Text need to be selected.    selectedFrame = scribus.getSelectedObject    try:        selectedText = scribus.getText    except:        scribus.messageBox(scriptWindowTitle,wrongFrameTypeErrorMsg,ICON_WARNING,BUTTON_OK)        scribus.selectObject(story) sys.exit scribus.deselectAll allText = scribus.getAllText(selectedFrame) if allText.count(unicode(selectedText)) == 1: cur = allText.find(unicode(selectedText)) else: scribus.messageBox(scriptWindowTitle,askSelectMoreText,ICON_WARNING,BUTTON_OK) scribus.selectObject(story) sys.exit(1) return cur,allText,selectedText num = 0 def numbering(matchObj): Function to re.sub use. global num # Define limit to list limit = 999 if num > limit: num = limit s = u'\r'+str(num)+separator+'\t' num += 1 return s def lettering(matchObj): Function to re.sub use. global num # Define limit to list (zz) limit = ((alphabetLen+1) * alphabetLen) if num > limit: num = limit d1 = ((num-1) % alphabetLen) s1 = alphabet[d1] d2 = ((num-1) / alphabetLen)-1 if d2 > -1: s2 = alphabet[d2] s1 = s2+s1 s = u'\r'+s1+separator+'\t' if markOperation.isupper: s = s.upper num += 1 return s def processMarks(t): Delete old marks and insert new ones. # Clear old numbers splitedText = [] s = re.sub(reAnyMark,u'\r',t) if markOperation == numberOperation: # Insert numbers s = re.sub(u'\r',numbering,s) splitedText = re.split(reAnyMark,s) print 'Split:\n',splitedText elif markOperation.isalpha: # Insert letters s = re.sub(u'\r',lettering,s) splitedText = re.split(reAnyMark,s) print 'Split:\n',splitedText elif markOperation == bulletOperation: # Insert bullets b = u'\r'+bulletString s = re.sub(u'\r',b,s) splitedText = re.split(reAnyMark,s) print 'Split:\n',splitedText return s,splitedText def formatMarks(begin,splitedText,story): '''Format marks inserted by processMarks. Use splitedText array as map to point text in story frame.''' for m in range (1,len(splitedText),2): # element 0 is '' text = splitedText[m] length = len(splitedText[m])-2 # don't count '\r' and '\t' print repr(text[1:len(text)-1]) if markOperation == bulletOperation: if bulletedListCharFont: scribus.selectText(begin,length,story) try: scribus.setFont(bulletedListCharFont,story) except: print "Can't find font:",bulletedListCharFont if bulletedListCharSize: scribus.selectText(begin,length,story) try: scribus.setFontSize(bulletedListCharSize,story) except: print "Couldn't apply list char size" if markOperation == numberOperation: if numberedListCharFont: scribus.selectText(begin,length,story) try: scribus.setFont(numberedListCharFont,story) except: print "Can't find font:",numberedListCharFont if numberedListCharSize: scribus.selectText(begin,length,story) try: scribus.setFontSize(numberedListCharSize,story) except: print "Couldn't apply list char size" if markOperation.isalpha: if letteredListCharFont: scribus.selectText(begin,length,story) try: scribus.setFont(letteredListCharFont,story) except: print "Can't find font:",letteredListCharFont if letteredListCharSize: scribus.selectText(begin,length,story) try: scribus.setFontSize(letteredListCharSize,story) except: print "Couldn't apply list char size" lineAfterBullet = splitedText[m+1] print begin,lineAfterBullet begin = begin + length + 2 + len(unicode(lineAfterBullet)) def doAllOperation(story): Insert marks in front selected paragraphs or clean marks. # Get selection begin cur,allText,selectedText = getTextCursor(story) # Get first selected paragraph begin # (last line break position after selection) begin = allText.rfind(u'\r',0,cur)+1 # Scribus not use '\n'? # Get selection end end = cur+len(unicode(selectedText)) # Get last selected paragraph end end = allText.find(u'\r',end) totalLen = end - begin scribus.selectText(begin,totalLen,story) selectedText = scribus.getText(story) #print '\nSelected:\n',repr(selectedText.encode('utf-8')) # debug # Put temporary carriage return to allow numbering at string begin selectedText = u'\r'+selectedText # Clean old marks and insert new ones processedText,splitedText = processMarks(unicode(selectedText)) # Strip temporary initial CR   processedText = unicode(processedText)[1:] scribus.deleteText(story) scribus.insertText(processedText,begin,story) # Apply style scribus.deselectAll # Get selection new end length = len(unicode(processedText)) scribus.selectText(begin,length,story) if markOperation == bulletOperation: applyStyle(bulletedListParaStyle,story) elif markOperation == numberOperation: applyStyle(numberedListParaStyle,story) elif markOperation == cleanOperation: applyStyle(defaultParaStyle,story) elif markOperation.isalpha: applyStyle(letteredListParaStyle,story) # Apply special formating formatMarks(begin,splitedText,story) scribus.selectObject(story) def decodeInput(res): Parse user input. global num,separator f = res[0] # Test numbered list if f.isdigit: n = re.search('\d{1,3}',res).group num = int(n) # Get separator s = re.search('[^\d]{1,1}',res) if s:           separator = s.group return '1' # Test alphabetic list elif f.isalpha: if len(res) > 1: # if there is second letter or separator f2 = re.search('[a-zA-Z]{1,2}',res).group if len(f2) > 1: digit1 = f2[1].lower num = alphabet.find(digit1)+1 digit2 = f2[0].lower num2 = alphabet.find(digit2)+1 num2 = num2 * len(alphabet) num = num + num2 else: digit1 = f2[0].lower num = alphabet.find(digit1)+1 else: digit1 = f.lower num = alphabet.find(digit1)+1 # Get separator s = re.search('[^a-zA-Z]{1,1}',res) if s:           separator = s.group elif f == bulletOperation: pass elif f == cleanOperation: pass else: f = '' return f def handleSelected: """Handle frame selection.""" global markOperation,separator story = scribus.getSelectedObject(0) if story: res = scribus.valueDialog(scriptWindowTitle,askOperationMsg,defaultOperation) res = res.strip if res: markOperation = decodeInput(res) if markOperation: doAllOperation(story) scribus.docChanged(True) else: scribus.messageBox(scriptWindowTitle,askSelectText,ICON_WARNING,BUTTON_OK) def main(argv): """Main entry point.""" if scribus.haveDoc: scribus.setRedraw(False) handleSelected else: scribus.messageBox(scriptWindowTitle,askOpenDoc,ICON_WARNING,BUTTON_OK) 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)
 * 1) No need to change after this line #############
 * 1) Regular expression to match any mark:
 * 2)  '\r' = carriage return in Python
 * 3)  '\t' = tabulation in Python
 * 4)  [^\t|^\r] = non-tab or non-CR chars
 * {,4} = 0 to 4 currencies from paragraph begin to tab
 * 1) Apply style
 * 1) Apply style
 * 1) Get text cursor position
 * 1) Get text cursor position
 * 1) Numbering
 * 1) Numbering
 * 1) Lettering
 * 1) Lettering
 * 1) Format marks
 * 1) Format marks
 * 1) Do all the job
 * 1) Do all the job