Difference between revisions of "Bullets and numbered lists"

From Scribus Wiki
Jump to: navigation, search
m (Install: list adjustment)
(30 intermediate revisions by 2 users not shown)
Line 1: Line 1:
 
+
{{Scripting Index}}
Improved bullets simple script using regular expressions Python module. Build bulleted lists, numbered lists starting with any number plus separator and delete list marks.  
+
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.
 
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==
 +
 +
# Copy the code and paste in a text file named "Bullets.py" or whatever "*.py".
 +
# Run it by "Scripts > Execute script..."
 +
# 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==
 
==Usage==
  
Select a frame or some paragraphs and run the script. Asked, input the operation type:
+
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:
  
* "b" input will create bullet marks and apply "Bullet" style.
+
* "*" will create bulleted list.
* Any number input will create numbered list, starting with such number, and will apply "Numbered List" style. Optional last char will be used as separator. Default: dot. Example: "1)", "16#", "256-".
+
* A number (up 3 digit) will create a numbered list begining with such number. Ex.: "1)". Range: 0 to 999.
* "x" input will delete marks and apply "Default Paragraph Style" style.
+
* 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".
* Any other response will cancel all operations.
+
* "/" will clear all marks.
 +
* An optional character after numbers or letters will be used as separator. Default: dot.
 +
==Options==
 +
* 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":
 +
<pre>
 +
if os.name == 'posix':
 +
    bulletString = u'\u006F\t'  #  lower right drop-shadowed white square U+274F
 +
else:
 +
    bulletString = u'\uF06F\t'
 +
</pre>
 +
 
 +
'''Tested and working in Scribus 1.3.9 running in Windows XP, Wine and Ubuntu 10.10'''.
  
 
==Caveats==
 
==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.
  
Maybe will FREEZE Scribus in Ubuntu 10.04 and 10.10, as script use Pyhton module ('''re''', regular expressions). This is a Ubuntu +  Scribus Scripter bug.
+
==Code==
  
 
<pre>
 
<pre>
#!/usr/bin/env python
+
#!/usr/bin/env python
 
# -*- coding: utf-8 -*-
 
# -*- coding: utf-8 -*-
 
+
 
"""
 
"""
Insert marks or numbers in front selected paragraphs.
+
Insert marks, numbers or letters in front selected paragraphs.
 
Author: prof. MS. José Antonio Meira da Rocha
 
Author: prof. MS. José Antonio Meira da Rocha
 
mailto:joseantoniomeira@gmail.com
 
mailto:joseantoniomeira@gmail.com
 
http://meiradarocha.jor.br
 
http://meiradarocha.jor.br
 
License GPL 2.0
 
License GPL 2.0
2011-01-14a
+
2011-01-17a
 
+
======
+
 
Usage:
 
Usage:
Select paragraphs at any points. Run script.  
+
======
Response 'yes' will clear all numbers. Any other response
+
Select paragraphs at any points. Dialog will ask mark type.
will create numbered list.
+
Input: 
 
+
* "*" 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.
 +
 
Caveats:
 
Caveats:
 
* Cleans all text format of text selection (font, bold, subscript, etc).
 
* Cleans all text format of text selection (font, bold, subscript, etc).
* Creates paragraph style "Numbered List" or "Bullet", if it doesn't exist, with proper indentation. Tabulation need to be defined by user to identical identation value.
+
* Creates paragraph style "Numbered List", "Lettered list" or "Bullet", if it doesn't exist, with proper indentation.  
* If there is no selection, will apply bullets to all text in current frame.
+
  Tabulation need to be defined by user to identical indentation value.
 +
* If there is no selection, script will apply marks to all text in current frame.
 +
* Some fonts can have different encodings depending operational system.
 +
  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 sys
 
import re
 
import re
 
+
import os
 +
 
try:
 
try:
 
     import scribus
 
     import scribus
Line 50: Line 84:
 
     sys.exit(1)
 
     sys.exit(1)
 
from scribus import BUTTON_OK, ICON_WARNING
 
from scribus import BUTTON_OK, ICON_WARNING
 
+
 
#######################
 
#######################
 
# Change this variables
 
# Change this variables
 
# to match your needs
 
# to match your needs
 
#######################
 
#######################
listStyle = 'Numbered list'
+
numberedListParaStyle = 'Numbered list'
bulletStyle = 'Bullet'
+
# Font name in Scribus style. Empty string don't apply font.
defaultStyle = 'Default Paragraph Style'
+
# Get font list with 'getFontNames()' <F9> in Scripter console
 +
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
 +
# Put your favorite unicode char and tab: '\u2022\t' # u'•\t' # bullet U+2022
 +
# Get hex code from menu: "Insert > Special character > [magnifying button]".
 +
# Modern dingbat fonts, as Zapf Dingbats or Wingdings, need special hex codes.
 +
# URW "Dingbats Regular" Type 1 font have different code in Linux and in Windows,
 +
# none of them Unicode.
 +
# "ITC Zapf Dingbats Regular" have same code in both OS, but not Unicode encoding.
 +
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 = '.'
 
separator = '.'
indent = 18 # points
+
indent = 22 # points
# Put your favorite unicode char and tab: '\u2022\t'
+
# Get hex code from "Insert > Special character..." palette
+
bulletString = u'•\t'
+
 
+
 
###########
 
###########
 
# Locales
 
# Locales
 
###########
 
###########
 +
  
 
# Message to ask frame selection
 
# Message to ask frame selection
 
scriptWindowTitle = "Format list"
 
scriptWindowTitle = "Format list"
 
askSelectText = "<h2>Select a text</h2>\n" \
 
askSelectText = "<h2>Select a text</h2>\n" \
        +"Select a <b>story text</b> where you want insert numbers."
+
+"Select a <b>story text</b> where you want insert numbers."
 +
 
 
# Message to open a document
 
# Message to open a document
 
askOpenDoc = "<h2>Open a document</h2>" \
 
askOpenDoc = "<h2>Open a document</h2>" \
        +"Open a document befora run this script."
+
+"Open a document before run this script."
 +
 
 
# Message asking select more text
 
# Message asking select more text
 
askSelectMoreText = "<h2>Select more text</h2>" \
 
askSelectMoreText = "<h2>Select more text</h2>" \
        +"Not unique text selected. Select more text before run this script."
+
+"Not unique text selected. Select more text before run this script."
 +
 
 
wrongFrameTypeErrorMsg = "<h2>Wrong object selected</h2>" \
 
wrongFrameTypeErrorMsg = "<h2>Wrong object selected</h2>" \
        +"You must select a <b>text frame</b>."
+
+"You must select a <b>text frame</b>."
 
+
 
markOperation = ''
 
markOperation = ''
bulletOperation = 'B'
+
bulletOperation = '*'
 
numberOperation = '1'
 
numberOperation = '1'
cleanOperation = 'X'
+
cleanOperation = '/'
defaultResponse = bulletOperation
+
alphaOperation = 'a'
askOperationMsg = "<h2>What mark operation?</h2>" \
+
defaultOperation = bulletOperation
        +'<ul><li>"<b>'+bulletOperation.lower()+'</b>" input will create <b>bullet</b> marks.</li>' \
+
alphabet = 'abcdefghijklmnopqrstuvwxyz'
        +'and apply <i>'+bulletStyle+'</i>&nbsp;&nbsp;style.' \
+
alphabetLen = len(alphabet)
        +'<li><b>Any number</b> input will create <b>numbered list</b>  <br>starting with such number ' \
+
        +'and apply <i>'+listStyle+'</i>&nbsp;&nbsp;style.' \
+
        +'<br><font color="#0000ff">Optional last char will be used as separator.<br>Default: dot. Example:  "<b>1)</b>"</font>' \
+
        +'<li>"<b>'+cleanOperation.lower()+'</b>" input will <b>delete</b> marks ' \
+
        +'and apply <br><i>'+defaultStyle+'</i>&nbsp;&nbsp;style.</li>' \
+
        +'<li>Any <b>other</b> input will <b>cancel</b> all operations.</li></ul>' \
+
 
+
# No need to change:
+
 
+
justClean = 0
+
  
 +
askOperationMsg = '<h2>What mark operation?</h2>' \
 +
+'<ul><li>"<b>'+bulletOperation+'</b>" input will create <b>bullet</b> marks.' \
 +
+'and apply <i>'+bulletedListParaStyle+'</i>  style.' \
 +
+'<li>A <b>number</b> input will create <b>numbered list</b> starting <br>' \
 +
+'with such number and apply <i>'+numberedListParaStyle+'</i>  style.' \
 +
+'<li>One or two <b>letters</b> input, upper or lower case, <br>' \
 +
+'will create <b>lettered list</b> starting with such letters,<br>' \
 +
+'and apply <i>'+letteredListParaStyle+'</i>  style.' \
 +
+'<li><font color="#0000ff">Optional char after number or letter<br>' \
 +
+'will be used as separator. Default: dot. Ex.: "<b>a)</b>"</font>' \
 +
+'<li>"<b>'+cleanOperation.lower()+'</b>" input will <b>delete</b> marks ' \
 +
+'and apply <br><i>'+defaultParaStyle+'</i>  style.</li>' \
 +
+'<li>Any <b>other</b> input will <b>cancel</b> all operations.</li></ul>' \
 +
 +
###### No need to change after this line #############
 +
 
# Regular expression to match any mark:
 
# Regular expression to match any mark:
 
#  '\r' = carriage return in Python
 
#  '\r' = carriage return in Python
 
#  '\t' = tabulation in Python
 
#  '\t' = tabulation in Python
#  [^\t] = non-tab chars
+
#  [^\t|^\r] = non-tab or non-CR chars
#  {,4} = 0 to 4 ocurrencies from paragraph begin to tab
+
#  {,4} = 0 to 4 currencies from paragraph begin to tab
reAnyMark  = r'\r[^\t]{,4}\t'  
+
reAnyMark  = r'(\r[^\t|^\r]{,4}\t)'
 
+
 
##############
 
##############
 
# Apply style
 
# Apply style
 
def applyStyle(style,frame):
 
def applyStyle(style,frame):
     '''Apply a style in selected text.  
+
     '''Apply a style in selected text.
 
     If style doesn't exist, create it.'''
 
     If style doesn't exist, create it.'''
 
     try:
 
     try:
Line 118: Line 183:
 
         scribus.setStyle(style,frame)
 
         scribus.setStyle(style,frame)
 
     return frame
 
     return frame
 
+
 
##########################
 
##########################
 
# Get text cursor position
 
# Get text cursor position
Line 133: Line 198:
 
     scribus.deselectAll()
 
     scribus.deselectAll()
 
     allText = scribus.getAllText(selectedFrame)
 
     allText = scribus.getAllText(selectedFrame)
   
+
   
 
     if allText.count(unicode(selectedText)) == 1:
 
     if allText.count(unicode(selectedText)) == 1:
 
         cur = allText.find(unicode(selectedText))
 
         cur = allText.find(unicode(selectedText))
Line 140: Line 205:
 
         scribus.selectObject(story)
 
         scribus.selectObject(story)
 
         sys.exit(1)
 
         sys.exit(1)
   
+
   
 
     return cur,allText,selectedText
 
     return cur,allText,selectedText
 
+
 
############
 
############
 
# Numbering
 
# Numbering
Line 149: Line 214:
 
     '''Function to re.sub use.'''
 
     '''Function to re.sub use.'''
 
     global num
 
     global num
 +
    # Define limit to list
 +
    limit = 999
 +
    if num > limit: num = limit
 
     s = u'\r'+str(num)+separator+'\t'
 
     s = u'\r'+str(num)+separator+'\t'
 
     num += 1
 
     num += 1
 
     return s
 
     return s
 
+
 +
####################
 +
# Lettering
 +
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 insertMarks(t):
+
def processMarks(t):
 
     '''Delete old marks and insert new ones.'''
 
     '''Delete old marks and insert new ones.'''
 
     # Clear old numbers
 
     # Clear old numbers
 +
    splitedText = []
 
     s = re.sub(reAnyMark,u'\r',t)
 
     s = re.sub(reAnyMark,u'\r',t)
 
     if markOperation == numberOperation:
 
     if markOperation == numberOperation:
 
         # Insert numbers
 
         # Insert numbers
 
         s = re.sub(u'\r',numbering,s)
 
         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:
 
     elif markOperation == bulletOperation:
 
         # Insert bullets
 
         # Insert bullets
 
         b = u'\r'+bulletString
 
         b = u'\r'+bulletString
 
         s = re.sub(u'\r',b,s)
 
         s = re.sub(u'\r',b,s)
   
+
        splitedText = re.split(reAnyMark,s)
     return s
+
        print 'Split:\n',splitedText
 
+
   
 +
     return s,splitedText
 +
 +
#################
 +
# Format marks
 +
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))
 +
 
#################
 
#################
 
# Do all the job
 
# Do all the job
Line 187: Line 325:
 
     # Put temporary carriage return to allow numbering at string begin
 
     # Put temporary carriage return to allow numbering at string begin
 
     selectedText = u'\r'+selectedText
 
     selectedText = u'\r'+selectedText
     # Clean old numbers and insert new ones
+
     # Clean old marks and insert new ones
     cleanedText = insertMarks(unicode(selectedText))
+
     processedText,splitedText = processMarks(unicode(selectedText))
     # Strip temp initial CR
+
     # Strip temporary initial CR
     cleanedText = unicode(cleanedText)[1:]
+
     processedText = unicode(processedText)[1:]
 
     scribus.deleteText(story)
 
     scribus.deleteText(story)
     scribus.insertText(cleanedText,begin,story)
+
     scribus.insertText(processedText,begin,story)
 
     # Apply style
 
     # Apply style
 
     scribus.deselectAll()
 
     scribus.deselectAll()
 
     # Get selection new end
 
     # Get selection new end
     end = len(unicode(cleanedText))
+
     length = len(unicode(processedText))
     scribus.selectText(begin,end,story)
+
     scribus.selectText(begin,length,story)
 
     if markOperation == bulletOperation:
 
     if markOperation == bulletOperation:
         style = bulletStyle
+
         applyStyle(bulletedListParaStyle,story)
 
     elif markOperation == numberOperation:
 
     elif markOperation == numberOperation:
         style = listStyle
+
         applyStyle(numberedListParaStyle,story)
     else:
+
     elif markOperation == cleanOperation:
         style = defaultStyle
+
         applyStyle(defaultParaStyle,story)
     applyStyle(style,story)
+
     elif markOperation.isalpha():
 +
        applyStyle(letteredListParaStyle,story)
 +
    # Apply special formating
 +
    formatMarks(begin,splitedText,story)
 
     scribus.selectObject(story)
 
     scribus.selectObject(story)
 
+
 +
#####################
 
def decodeInput(res):
 
def decodeInput(res):
 
     '''Parse user input.'''
 
     '''Parse user input.'''
 
     global num,separator
 
     global num,separator
     f = res.upper()[0]
+
     f = res[0]
 +
    # Test numbered list
 
     if f.isdigit():
 
     if f.isdigit():
 
         n = re.search('\d{1,3}',res).group()
 
         n = re.search('\d{1,3}',res).group()
 
         num = int(n)
 
         num = int(n)
 +
        # Get separator
 
         s = re.search('[^\d]{1,1}',res)
 
         s = re.search('[^\d]{1,1}',res)
 
         if s:
 
         if s:
 
             separator = s.group()
 
             separator = s.group()
 
         return '1'
 
         return '1'
     else:
+
     # Test alphabetic list
         return f
+
    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():
 
def handleSelected():
 
     """Handle frame selection."""
 
     """Handle frame selection."""
 
     global markOperation,separator
 
     global markOperation,separator
     story = scribus.getSelectedObject(0)  
+
     story = scribus.getSelectedObject(0)
 
     if story:
 
     if story:
         res = scribus.valueDialog(scriptWindowTitle,askOperationMsg,defaultResponse)
+
         res = scribus.valueDialog(scriptWindowTitle,askOperationMsg,defaultOperation)
 
         res = res.strip()
 
         res = res.strip()
 
         if res:
 
         if res:
 
             markOperation = decodeInput(res)
 
             markOperation = decodeInput(res)
            operation = bulletOperation+numberOperation+cleanOperation
+
             if markOperation:
             if operation.count(markOperation):
+
 
                 doAllOperation(story)
 
                 doAllOperation(story)
 
                 scribus.docChanged(True)
 
                 scribus.docChanged(True)
 
     else:
 
     else:
 
         scribus.messageBox(scriptWindowTitle,askSelectText,ICON_WARNING,BUTTON_OK)
 
         scribus.messageBox(scriptWindowTitle,askSelectText,ICON_WARNING,BUTTON_OK)
 
+
###############  
+
###############
 
def main(argv):
 
def main(argv):
 
     """Main entry point."""
 
     """Main entry point."""
Line 244: Line 410:
 
         scribus.setRedraw(False)
 
         scribus.setRedraw(False)
 
         handleSelected()
 
         handleSelected()
     else:
+
     else:  
 
         scribus.messageBox(scriptWindowTitle,askOpenDoc,ICON_WARNING,BUTTON_OK)
 
         scribus.messageBox(scriptWindowTitle,askOpenDoc,ICON_WARNING,BUTTON_OK)
 
+
 
#######################
 
#######################
 
def main_wrapper(argv):
 
def main_wrapper(argv):
Line 258: Line 424:
 
         scribus.statusMessage("")
 
         scribus.statusMessage("")
 
         scribus.progressReset()
 
         scribus.progressReset()
 
+
 
if __name__ == '__main__':
 
if __name__ == '__main__':
 
     main_wrapper(sys.argv)
 
     main_wrapper(sys.argv)
 
</pre>
 
</pre>

Revision as of 00:12, 20 March 2011

This article is part of the Scripts series.

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

  • 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":
if os.name == 'posix':
    bulletString = u'\u006F\t'  #  lower right drop-shadowed white square U+274F
else:
    bulletString = u'\uF06F\t' 

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
# -*- coding: utf-8 -*-
 
"""
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:
======
Select paragraphs at any points. Dialog will ask mark type.
Input:  
* "*" 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.
 
Caveats:
* 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. 
  Tabulation need to be defined by user to identical indentation value.
* If there is no selection, script will apply marks to all text in current frame.
* Some fonts can have different encodings depending operational system. 
  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
 
#######################
# Change this variables
# to match your needs
#######################
numberedListParaStyle = 'Numbered list'
# Font name in Scribus style. Empty string don't apply font.
# Get font list with 'getFontNames()' <F9> in Scripter console
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
# Put your favorite unicode char and tab: '\u2022\t' # u'•\t' # bullet U+2022
# Get hex code from menu: "Insert > Special character > [magnifying button]".
# Modern dingbat fonts, as Zapf Dingbats or Wingdings, need special hex codes.
# URW "Dingbats Regular" Type 1 font have different code in Linux and in Windows,
# none of them Unicode.
# "ITC Zapf Dingbats Regular" have same code in both OS, but not Unicode encoding.
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
 
 
###########
# Locales
###########
 

# Message to ask frame selection
scriptWindowTitle = "Format list"
askSelectText = "<h2>Select a text</h2>\n" \
+"Select a <b>story text</b> where you want insert numbers."

# Message to open a document
askOpenDoc = "<h2>Open a document</h2>" \
+"Open a document before run this script."

# Message asking select more text
askSelectMoreText = "<h2>Select more text</h2>" \
+"Not unique text selected. Select more text before run this script."

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

askOperationMsg = '<h2>What mark operation?</h2>' \
+'<ul><li>"<b>'+bulletOperation+'</b>" input will create <b>bullet</b> marks.' \
+'and apply <i>'+bulletedListParaStyle+'</i>  style.' \
+'<li>A <b>number</b> input will create <b>numbered list</b> starting <br>' \
+'with such number and apply <i>'+numberedListParaStyle+'</i>  style.' \
+'<li>One or two <b>letters</b> input, upper or lower case, <br>' \
+'will create <b>lettered list</b> starting with such letters,<br>' \
+'and apply <i>'+letteredListParaStyle+'</i>  style.' \
+'<li><font color="#0000ff">Optional char after number or letter<br>' \
+'will be used as separator. Default: dot. Ex.: "<b>a)</b>"</font>' \
+'<li>"<b>'+cleanOperation.lower()+'</b>" input will <b>delete</b> marks ' \
+'and apply <br><i>'+defaultParaStyle+'</i>  style.</li>' \
+'<li>Any <b>other</b> input will <b>cancel</b> all operations.</li></ul>' \
 
###### No need to change after this line #############
 
# Regular expression to match any mark:
#  '\r' = carriage return in Python
#  '\t' = tabulation in Python
#  [^\t|^\r] = non-tab or non-CR chars
#  {,4} = 0 to 4 currencies from paragraph begin to tab
reAnyMark  = r'(\r[^\t|^\r]{,4}\t)'  
 
##############
# Apply style
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
 
##########################
# Get text cursor position
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
 
############
# Numbering
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
 
####################
# Lettering
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
 
#################
# Format marks
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))
 
#################
# Do all the job
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)