Apply basic ligatures to a document

From Scribus Wiki
Jump to navigation Jump to search
This article is part of the Scripts series.

This script is a search and replace tool "specialized" in ligatures. The specific code is mostly meant for displaying the report at the end. And to make sure it doesn't try to apply some ligatures with fonts that doesn't have them.

Note that since Scribus doesn't support OpenType features nor direct glyph access yet, these ligatures need to be mapped as characters in the font.

How to use it

Ligatures are presented in a python dictionary. Since not all fonts have the same ligatures, you need to specify the font name. So the first level in this dictionary is the font name according to scribus. Then you have to put the ligatures you want as a sub-dictionary.

liga = {
	'Miama Italic': {
		u'oos':u'\ue0f9',
		u'os':u'\ue0fa',
		u'Th':u'\ue0fc',
		u'tt':u'\ue0fd',
		u'tz':u'\ue0fe',
		u'oo':u'\ue0ff',
		u'ff':u'\ufb00',
		u'fl':u'\ufb02',
		u'ffl':u'\ufb04',
		u'ſt':u'\ufb05',
		u'st':u'\ufb06'
	},
	'DejaVu Sans Book': {
		u'ff':u'\ufb00',
		u'fi':u'\ufb01',
		u'fl':u'\ufb02',
		u'ffi':u'\ufb03',
		u'ffl':u'\ufb04',
		u'ſt':u'\ufb05',
		u'st':u'\ufb06'
	}
}

In my example, DejaVu Sans has all the standard (unicode) ligatures while Miama doesn't (missing fi and ffi) but it has other ligatures mapped in the private use area.

The script

#!/usr/bin/env python
# coding: utf-8
#
#       Copyright 2012 Olivier Berten <olivier.berten@gmail.com>
#       
#       This program is free software; you can redistribute it and/or modify
#       it under the terms of the GNU General Public License as published by
#       the Free Software Foundation; either version 3 of the License, or
#       (at your option) any later version.
#       
#       This program is distributed in the hope that it will be useful,
#       but WITHOUT ANY WARRANTY; without even the implied warranty of
#       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#       GNU General Public License for more details.
#       
#       You should have received a copy of the GNU General Public License
#       along with this program; if not, write to the Free Software
#       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#       MA 02110-1301, USA.
#

import scribus
import re

punctuation = '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'

liga = {
	'Miama Italic': {
		u'oos':u'\ue0f9',
		u'os':u'\ue0fa',
		u'Th':u'\ue0fc',
		u'tt':u'\ue0fd',
		u'tz':u'\ue0fe',
		u'oo':u'\ue0ff',
		u'ff':u'\ufb00',
		u'fl':u'\ufb02',
		u'ffl':u'\ufb04',
		u'ſt':u'\ufb05',
		u'st':u'\ufb06'
	},
	'DejaVu Sans Book': {
		u'ff':u'\ufb00',
		u'fi':u'\ufb01',
		u'fl':u'\ufb02',
		u'ffi':u'\ufb03',
		u'ffl':u'\ufb04',
		u'ſt':u'\ufb05',
		u'st':u'\ufb06'
	}
}

def applyLigatures():
	page = 1
	pagenum = scribus.pageCount()
	report = ''
	while (page <= pagenum):
		scribus.gotoPage(page)
		d = scribus.getPageItems()
		rep_page = ['Page ' + str(page) + '\n', True]
		for item in d:
			if (item[1] == 4):
				contents = unicode(scribus.getAllText(item[0]))
				rep_frame = ['    ' + item[0] + '\n', True]
				words = re.compile(r'[%s\s]+' % re.escape(punctuation)).split(contents)
				p = 0
				d = 0
				for w in words:
					wo = w
					d2 = 0
					p = contents.find(w,p)
					lig = False
					rep_lig = []
					for font, lpairs in liga.iteritems():
						for l in sorted(lpairs.keys(), key=len, reverse=True):
							f = [m.start() for m in re.finditer(l, w)]
							if len(f) > 0:
								for e in f:
									scribus.selectText(p+e-d+d2, len(l), item[0])
									Font = scribus.getFont(item[0])
									FontSize = scribus.getFontSize(item[0])
									if Font == font:
										lig = True
										scribus.deleteText(item[0])
										scribus.insertText(lpairs[l], p+e-d+d2, item[0])
										scribus.selectText(p+e-d+d2, len(lpairs[l]), item[0])
										scribus.setFont(Font, item[0])
										scribus.selectText(p+e-d+d2, len(lpairs[l]), item[0])
										scribus.setFontSize(FontSize, item[0])
										d += len(l)-len(lpairs[l])
										d2 += len(l)-len(lpairs[l])
										w = w.replace(l, lpairs[l], 1)
										rep_lig.append(l)
					if lig:
						if rep_page[1]:
							report += rep_page[0]
							rep_page[1] = False
						if rep_frame[1]:
							report += rep_frame[0]
							rep_frame[1] = False
						report += '        ' + wo + ' (' + ', '.join(rep_lig) + ')\n'
		page += 1
	scribus.docChanged(True)
	if report > '':
		report = 'Ligatures made:\n\n' + report
	else:
		report = 'No ligature made'
	scribus.messageBox("Finished", report)

if scribus.haveDoc():
	try:
		applyLigatures()
	except Exception, e:
		print e
else:
	scribus.messageBox('Error', 'This script needs an opened document!')