#!/usr/bin/env python # -*- coding: utf-8 -*- ######################################################################## # version 1.0.0 # # GimpPythonFuVerticalWriting-2-8.py # Copyright (C) 2013 かんら・から http://www.pixiv.net/member.php?id=3098715 # # GimpPythonFuVerticalWriting-2-8.py is Python-fu plugin for GIMP 2.8 # # GimpPythonFuVerticalWriting-2-8.py 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. # # GimpPythonFuVerticalWriting-2-8.py 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, see . # # GPLv3 ライセンス # かんら・から http://www.pixiv.net/member.php?id=3098715 # バグレポート・アイデアなどは pixiv メッセージでお願いします。 # # ダウンロード # http://www.magic-object.mydns.jp/ # # このスクリプトを使用して発生した問題に関し、作成者は如何なる保証などを行う事はありません。 # 自己責任でのみの使用を許可します。 ######################################################################## from gimpfu import * import gimpcolor import gimpui import gtk import gobject import re import math import xml.dom.minidom # import chardet import os import ConfigParser import pygtk pygtk.require('2.0') ######################################################################## ######################################################################## # Pangoクラス # # Pango を操作する # ######################################################################## class PangoAccess: ######################################################################## document = None fontList = [] lines = [] ######################################################################## defaultFont = 'Sans' ######################################################################## def __init__(self, markUp, defaultText=None): if markUp: markUp = markUp.rstrip() try: self.document = xml.dom.minidom.parseString(markUp) except: self.document = xml.dom.minidom.parseString('') if defaultText: defaultText = defaultText.rstrip() textNode = self.document.createTextNode(defaultText) self.document.firstChild.appendChild(textNode) elif markUp: textNode = self.document.createTextNode(markUp) self.document.firstChild.appendChild(textNode) else: self.document = xml.dom.minidom.parseString('') if defaultText: textNode = self.document.createTextNode(defaultText) self.document.firstChild.appendChild(textNode) self.parse() ######################################################################## def __del__(self): if self.document: self.document.unlink() ######################################################################## def getAllText(self): if not self.document: return '' ######################################################################## return self.getTextNodeValue(self.document.firstChild) ######################################################################## def toUnicode(self, text): if isinstance(text, unicode): return text if not isinstance(text, str): text = str(text) # codecInfo = chardet.detect( text ) # return unicode( text, codecInfo['encoding'] ) try: return unicode(text, 'utf-8') except: pass try: return unicode(text, 'euc-jp') except: pass try: return unicode(text, 'cp932') except: return text ######################################################################## def getTextNodeValue(self, element): if not element: return '' elif element.nodeType == element.TEXT_NODE: return element.nodeValue elif element.nodeType != element.ELEMENT_NODE: return '' ######################################################################## text = '' for node in element.childNodes: if node.hasChildNodes(): text += self.getTextNodeValue(node) elif node.nodeType == node.TEXT_NODE: text += node.nodeValue ######################################################################## return text ######################################################################## def setFontList(self, fontList): self.fontList = sorted(fontList) ######################################################################## def getFontName(self, name, bold=False, italic=False): if name not in self.fontList: return name ######################################################################## regExpBoldItalic = re.compile('( bold italic$)|( italic bold$)|(-P?BI?$)', re.IGNORECASE) regExpBold = re.compile('( bold$)|(-P?B$)', re.IGNORECASE) regExpItalic = re.compile('( italic$)|(-I$)', re.IGNORECASE) if bold and italic: if regExpBoldItalic.search(name): return name elif bold: if regExpBold.search(name): return name elif italic: if regExpItalic.search(name): return name ######################################################################## orgFontName = re.sub('-[PBSI]+$', '', name) regExpBoldItalic = re.compile('^(' + re.escape(orgFontName) + '\\s*)(' + regExpBoldItalic.pattern + ')', regExpBoldItalic.flags) regExpBold = re.compile('^(' + re.escape(orgFontName) + '\\s*)(' + regExpBold.pattern + ')', regExpBold.flags) regExpItalic = re.compile('^(' + re.escape(orgFontName) + '\\s*)(' + regExpItalic.pattern + ')', regExpItalic.flags) ######################################################################## for fontName in self.fontList: if bold and italic: if regExpBoldItalic.search(fontName): return fontName elif bold: if regExpBold.search(fontName): return fontName elif italic: if regExpItalic.search(fontName): return fontName ######################################################################## return name ######################################################################## def setDefaultFont(self, font): self.defaultFont = font ######################################################################## def parse(self): self.lines = [] if not self.document: return self.lines ######################################################################## nodeList = self.document.firstChild.childNodes for node in nodeList: self.parseNode(node, self.defaultFont, False, False, {}) ######################################################################## def parseNode(self, node, font=None, bold=False, italic=False, attribute={}): if not node: return if not isinstance(node, xml.dom.minidom.Node): return if node.nodeType == node.TEXT_NODE: self.wordAdd(node.nodeValue, font, bold, italic, attribute) return elif node.nodeType == node.ELEMENT_NODE: if not node.hasChildNodes(): return elif node.tagName.lower() == 'b': bold = True elif node.tagName.lower() == 'i': italic = True elif node.tagName.lower() == 's': attribute['strikethrough'] = True elif node.tagName.lower() == 'u': attribute['underline'] = 'single' elif node.tagName.lower() == 'span': if node.attributes: for index in range(node.attributes.length): attrNode = node.attributes.item(index) if attrNode.name.lower() in ('font', 'font_desc'): font = attrNode.nodeValue elif attrNode.name.lower() in ('size', 'font_size'): if attrNode.nodeValue.isdigit(): attribute['point'] = int(attrNode.nodeValue) / 1024.0 elif attrNode.name.lower() == 'xx-large': attribute['point'] = 32 elif attrNode.name.lower() == 'x-large': attribute['point'] = 18 elif attrNode.name.lower() == 'large': attribute['point'] = 19.2 elif attrNode.name.lower() == 'midium': attribute['point'] = 16 elif attrNode.name.lower() == 'small': attribute['point'] = 14.2 elif attrNode.name.lower() == 'x-small': attribute['point'] = 12 elif attrNode.name.lower() == 'xx-small': attribute['point'] = 9.6 else: attribute['size'] = attrNode.nodeValue elif attrNode.name.lower() in ('foreground', 'fgcolor', 'color'): color = {'red': 0.0, 'green': 0.0, 'blue': 0.0} if attrNode.nodeValue.startswith('#'): match = re.match('^#([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})$', attrNode.nodeValue) if match: color['red'] = int(match.group(1), 16) / 255.0 color['green'] = int(match.group(2), 16) / 255.0 color['blue'] = int(match.group(3), 16) / 255.0 elif re.match('^#([0-9A-Fa-f])([0-9A-Fa-f])([0-9A-Fa-f])$', attrNode.nodeValue): match = re.match('^#([0-9A-Fa-f])([0-9A-Fa-f])([0-9A-Fa-f])$', attrNode.nodeValue) color['red'] = int(match.group(1), 16) / 15.0 color['green'] = int(match.group(2), 16) / 15.0 color['blue'] = int(match.group(3), 16) / 15.0 elif attrNode.nodeValue.lower() == 'white': color['red'] = 1.0 color['green'] = 1.0 color['blue'] = 1.0 elif attrNode.nodeValue.lower() == 'black': color['red'] = 0.0 color['green'] = 0.0 color['blue'] = 0.0 elif attrNode.nodeValue.lower() == 'gray': color['red'] = 0.5 color['green'] = 0.5 color['blue'] = 0.5 elif attrNode.nodeValue.lower() == 'red': color['red'] = 1.0 color['green'] = 0.0 color['blue'] = 0.0 elif attrNode.nodeValue.lower() == 'green': color['red'] = 0.0 color['green'] = 1.0 color['blue'] = 0.0 elif attrNode.nodeValue.lower() == 'blue': color['red'] = 0.0 color['green'] = 0.0 color['blue'] = 1.0 elif attrNode.nodeValue.lower() == 'cyan': color['red'] = 0.0 color['green'] = 1.0 color['blue'] = 1.0 elif attrNode.nodeValue.lower() == 'magenta': color['red'] = 1.0 color['green'] = 0.0 color['blue'] = 1.0 elif attrNode.nodeValue.lower() == 'yellow': color['red'] = 1.0 color['green'] = 1.0 color['blue'] = 0.0 attribute['color'] = color else: attribute[attrNode.name.lower()] = attrNode.nodeValue.lower() for child in node.childNodes: self.parseNode(child, font, bold, italic, attribute) return ######################################################################## ######################################################################## def wordAdd(self, text, font=None, bold=False, italic=False, attribute={}): ######################################################################## if re.search('\\r?\\n', text): lfCount = len(re.findall('\\r?\\n', text)) restLf = lfCount for line in re.split('\\r?\\n', text): self.wordAdd(line, font, bold, italic, attribute) if restLf > 0: self.lines.append([]) restLf -= 1 return ######################################################################## if self.lines is None or not isinstance(self.lines, list): self.lines = [[]] ######################################################################## lineIndex = len(self.lines) - 1 if lineIndex < 0: self.lines.append([]) lineIndex = 0 elif self.lines[lineIndex] is None or not isinstance(self.lines[lineIndex], list): self.lines[lineIndex] = [] ######################################################################## wordData = { 'font': self.getFontName(font, bold, italic), 'text': self.toUnicode(text) } if isinstance(attribute, dict): for key in attribute.keys(): if key not in wordData.keys(): wordData[key] = attribute[key] self.lines[lineIndex].append(wordData) ######################################################################## ######################################################################## # Preference クラス # # GIMP の初期設定ファイルを操作する # ######################################################################## class GimpPreference: ######################################################################## gimpPreferenceDirPath = None preferenceDirPath = None preferenceFilePath = None ######################################################################## config = None ######################################################################## def __init__(self, majorVersion, minorVersion, dirName, fileName): homePath = os.path.expanduser('~') gimpPreferenceDirName = '.gimp-' + str(majorVersion) + '.' + str(minorVersion) self.gimpPreferenceDirPath = os.path.join(homePath, gimpPreferenceDirName) if not os.path.exists(self.gimpPreferenceDirPath): return self.preferenceDirPath = os.path.join(self.gimpPreferenceDirPath, dirName) if not os.path.exists(self.preferenceDirPath): os.mkdir(self.preferenceDirPath) self.preferenceFilePath = os.path.join(self.preferenceDirPath, fileName) ######################################################################## def __del__(self): pass ######################################################################## def load(self): if not os.path.exists(self.preferenceFilePath): return False self.config = ConfigParser.SafeConfigParser(allow_no_value=True) try: self.config.read(self.preferenceFilePath) return True except: return False ######################################################################## def save(self): if not self.preferenceFilePath: return False if not self.config: return False try: with open(self.preferenceFilePath, 'wb') as configfile: self.config.write(configfile) return True except: return False ######################################################################## def getAll(self, section): if not section: return {} if not self.config: return {} try: results = {} pairList = self.config.items(section) for pair in pairList: results[pair[0]] = pair[1] return pair except: return {} ######################################################################## def getOne(self, section, option, defaultValue=None, isString=False): if not section: return defaultValue if not option: return defaultValue if not self.config: return defaultValue try: value = self.config.get(section, option, isString) if isString: return value elif value.lower() == 'true': return True elif value.lower() == 'false': return False else: try: if int(value) == float(value): return int(value) else: return float(value) except: return value except: return defaultValue ######################################################################## def setOne(self, section, option, value): if not section: return False if not option: return False if not self.config: self.config = ConfigParser.SafeConfigParser(allow_no_value=True) if not self.config: return False if value is None: value = '' elif isinstance(value, bool): if value: value = '1' else: value = '0' elif not isinstance(value, unicode) and not isinstance(value, str): value = str(value) try: self.config.add_section(section) except: pass try: self.config.set(section, option, value) return True except: return False ######################################################################## ######################################################################## # 縦書クラス情報取得 # # テキストレイヤーから縦書きレイヤー用の情報を入手するダイアログ # ######################################################################## class VerticalTextAccess: ######################################################################## ( _LineNumber, _ColumnNumber, _RubyTargetNumcer, _RubyNumber ) = range(4) ######################################################################## image = None targetTextLayer = None doPutMessage = True lastMessage = '' usableVersion = (2, 8, 0) canUseLayerGroup = False pango = None ######################################################################## preferenceDirName = 'VerticalWriting' preferenceFileName = 'preference.conf' ######################################################################## lineSpaceRate = 1.0 charSpaceRate = 0.0 rubySizeRate = 1.0 / 3.0 rightShiftRate = 0.5 upShiftRate = 0.5 forceSmallerScaleRate = 0.8 ######################################################################## useSizeBool = True useColorBool = True useRubyBool = True ######################################################################## defaultRotateRightTartget = u'[]{}()<>|-~_|「」『』(){}【】<>《》〔〕〘〙〚〛‥…ー−―〜~' defaultRightShiftTarget = u'\'"、。’”' defaultUpShiftTarget = u'、。' defaultJoinBeforeTarget = u'゜゛' defaultConsiderOneCharTarget = u'!?' defaultForceSmallerTarget = u'ぁぃぅぇぉゃゅょっゎァィゥェォヵヶャュョッヮ' ######################################################################## rotateRightTartget = defaultRotateRightTartget rightShiftTarget = defaultRightShiftTarget upShiftTarget = defaultUpShiftTarget joinBeforeTarget = defaultJoinBeforeTarget considerOneCharTarget = defaultConsiderOneCharTarget forceSmallerTarget = defaultForceSmallerTarget flags = {} ######################################################################## def __init__(self, image): self.results = gtk.RESPONSE_CANCEL self.image = image versionLength = len(self.usableVersion) for index in range(0, versionLength): if self.usableVersion[index] < gimp.version[index]: self.canUseLayerGroup = True break elif self.usableVersion[index] == gimp.version[index]: if index == (versionLength - 1): self.canUseLayerGroup = True break continue else: self.canUseLayerGroup = False break return ######################################################################## def checkUsable(self): if not self.canUseLayerGroup: self.errorMessage('GIMP的版本太旧了。') return False else: layer = pdb.gimp_image_get_active_layer(self.image) if not pdb.gimp_item_is_text_layer(layer): self.errorMessage('文本图层未被选中。') return False else: return True ######################################################################## # エラーメッセージ機能 # def errorMessage(self, message=None): if self.doPutMessage: if message is None: message = self.lastMessage else: self.lastMessage = message print message pdb.gimp_message_set_handler(MESSAGE_BOX) pdb.gimp_message(message) # MESSAGE_BOX CONSOLE ERROR_CONSOLE # gimp.message( message ) return ######################################################################## def getLayerText(self): if not self.image: return '' layer = pdb.gimp_image_get_active_layer(self.image) if not pdb.gimp_item_is_text_layer(layer): return '' self.targetTextLayer = layer markUp = pdb.gimp_text_layer_get_markup(layer) if markUp: self.pango = PangoAccess(markUp) return self.pango.getAllText() text = pdb.gimp_text_layer_get_text(layer) if text: self.pango = PangoAccess(text) return text return '' ######################################################################## def dialogMake(self, proc_name=None, defaultText=None): self.loadPreference() self.dialog = gimpui.Dialog(proc_name, "python-fu", None, 0, None, proc_name, (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK) ) self.dialog.set_modal(True) self.dialog.set_alternative_button_order((gtk.RESPONSE_OK, gtk.RESPONSE_CANCEL)) # self.dialog.se_transient() self.tooltips = gtk.Tooltips() hBox = gtk.HBox() label = gtk.Label('行间距(pt):') hBox.pack_start(label, expand=False) self.lineSpacePointSpin = gtk.SpinButton() self.lineSpacePointSpin.set_digits(1) self.lineSpacePointSpin.set_range(1, 72) self.lineSpacePointSpin.set_increments(1, 10) self.setDafultLineSpace() hBox.pack_start(self.lineSpacePointSpin, expand=False) self.tooltips.set_tip(self.lineSpacePointSpin, '请指定行之间的间距。') label = gtk.Label(' 文字间距(pt):') hBox.pack_start(label, expand=False) self.charSpacePointSpin = gtk.SpinButton() self.charSpacePointSpin.set_digits(1) self.charSpacePointSpin.set_range(0, 72) self.charSpacePointSpin.set_increments(1, 10) self.setDafultCharSpace() hBox.pack_start(self.charSpacePointSpin, expand=False) self.tooltips.set_tip(self.charSpacePointSpin, '请指定文字之间的间距。') self.dialog.vbox.pack_start(hBox, expand=False) label = gtk.Label('') hBox.pack_start(label, expand=True) self.resetButton = gtk.Button('重置') self.resetButton.connect('clicked', self.clickResetButton, None) hBox.pack_start(self.resetButton, expand=False) self.tooltips.set_tip(self.resetButton, '重置除ruby项目之外的所有项目。') self.useSizeCheckButton = gtk.CheckButton('使用单独字体大小信息') self.useSizeCheckButton.set_active(self.useSizeBool) self.useSizeCheckButton.show() self.dialog.vbox.pack_start(self.useSizeCheckButton, expand=False) self.tooltips.set_tip(self.useSizeCheckButton, '每个字符的大小不同。') self.useColorCheckButton = gtk.CheckButton('使用单独的字体颜色信息') self.useColorCheckButton.set_active(self.useColorBool) self.useColorCheckButton.show() self.dialog.vbox.pack_start(self.useColorCheckButton, expand=False) self.tooltips.set_tip(self.useColorCheckButton, '每个字符的颜色不同。') self.useRubyCheckButton = gtk.CheckButton('使用ruby') self.useRubyCheckButton.set_active(self.useRubyBool) self.useRubyCheckButton.show() self.useRubyCheckButton.connect('toggled', self.tggleUseRubyCheckButton, None) self.dialog.vbox.pack_start(self.useRubyCheckButton, expand=False) self.tooltips.set_tip(self.useRubyCheckButton, '请确定是否使用ruby。') self.rubyVBox = gtk.VBox() self.rubyVBox.show() self.rubyHBox = gtk.HBox() self.rubyHBox.show() self.rubyHBox.pack_start(self.rubyVBox, expand=True, fill=True, padding=0) ### テキスト表示エリア ### self.textView = gtk.TextView() self.textView.set_editable(False) textBuffer = self.textView.get_buffer() if defaultText: textBuffer.set_text(defaultText) else: layerText = self.getLayerText() if layerText: textBuffer.set_text(layerText) textBuffer.connect('notify::has-selection', self.notify_has_selection) textBuffer.connect_after('notify::cursor-position', self.notify_cursor_position) self.textView.connect('button-release-event', self.button_release_event) self.textView.connect('button-press-event', self.button_press_event) # self.textView.connect( 'move-cursor', self.textSelectChange, None ) self.textView.show() self.tooltips.set_tip(self.textView, '选择想要添加ruby的文字,然后按“添加ruby”按扭。') self.textScrollArea = gtk.ScrolledWindow() self.textScrollArea.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) self.textScrollArea.add(self.textView) self.textScrollArea.show() hSeparator = gtk.HSeparator() hSeparator.show() # self.dialog.vbox.pack_start( self.textScrollArea ) # self.dialog.vbox.pack_start( hSeparator ) self.rubyVBox.pack_start(self.textScrollArea) self.rubyVBox.pack_start(hSeparator) ### ルビリストエリア ### self.rubyListStore = gtk.ListStore(gobject.TYPE_INT, gobject.TYPE_INT, gobject.TYPE_STRING, gobject.TYPE_STRING) self.listBox = gtk.TreeView(self.rubyListStore) self.listSelection = self.listBox.get_selection() self.listSelection.connect('changed', self.changedRubySlection, self.listBox) renderer = gtk.CellRendererText() column = gtk.TreeViewColumn('行数', renderer, text=self._LineNumber) column.set_sort_column_id(self._LineNumber) self.listBox.append_column(column) renderer = gtk.CellRendererText() column = gtk.TreeViewColumn('起始位置', renderer, text=self._ColumnNumber) column.set_sort_column_id(self._ColumnNumber) self.listBox.append_column(column) renderer = gtk.CellRendererText() column = gtk.TreeViewColumn('对象', renderer, text=self._RubyTargetNumcer) column.set_sort_column_id(self._RubyTargetNumcer) self.listBox.append_column(column) renderer = gtk.CellRendererText() renderer.set_property('editable', True) renderer.connect('edited', self.rubyChanged, self.rubyListStore, self._RubyNumber) column = gtk.TreeViewColumn('ruby', renderer, text=self._RubyNumber) column.set_sort_column_id(self._RubyNumber) self.listBox.append_column(column) self.listBox.show() self.tooltips.set_tip(self.listBox, '如果要更改ruby字符,请双击“对象”并更改它。') self.rubyScrollArea = gtk.ScrolledWindow() self.rubyScrollArea.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) self.rubyScrollArea.add(self.listBox) self.rubyScrollArea.show() hBox = gtk.HBox() hBox.show() hBox.pack_start(self.rubyScrollArea) vBox = gtk.VBox() vBox.show() self.rubyAddButton = gtk.Button('添加ruby') self.rubyAddButton.set_sensitive(False) self.rubyAddButton.connect('clicked', self.clickAddRubyButton, None) self.rubyAddButton.show() vBox.pack_start(self.rubyAddButton, expand=False) self.tooltips.set_tip(self.rubyAddButton, '请从上方区域选择需要添加ruby的文字,然后按下此按扭。') self.rubyRemoveButton = gtk.Button('删除ruby') self.rubyRemoveButton.set_sensitive(False) self.rubyRemoveButton.connect('clicked', self.clickRemoveRubyButton, None) self.rubyRemoveButton.show() vBox.pack_start(self.rubyRemoveButton, expand=False) self.tooltips.set_tip(self.rubyRemoveButton, '请选择左档并按下此按扭。') hSeparator = gtk.HSeparator() hSeparator.show() vBox.pack_start(hSeparator, expand=True) ######################################################################## label = gtk.Label('ruby大小(pt)') vBox.pack_start(label, expand=False) self.rubyPointSpin = gtk.SpinButton() self.rubyPointSpin.set_digits(1) self.rubyPointSpin.set_range(1, 100) self.rubyPointSpin.set_increments(1, 10) self.setDefaultRubySize() vBox.pack_start(self.rubyPointSpin, expand=False) self.tooltips.set_tip(self.rubyPointSpin, '请输入ruby字体大小。') hBox.pack_start(vBox, expand=False) ######################################################################## self.rubyVBox.pack_start(hBox) self.rubyFrame = gtk.Frame('ruby') self.rubyFrame.show() self.rubyFrame.add(self.rubyHBox) self.dialog.vbox.pack_start(self.rubyFrame, padding=20) hBox = gtk.HBox() label = gtk.Label('向右旋转90°的字符') hBox.pack_start(label, expand=False) self.rotateRightLineEdit = gtk.Entry() self.rotateRightLineEdit.set_text(self.rotateRightTartget) hBox.pack_start(self.rotateRightLineEdit) self.dialog.vbox.pack_start(hBox) self.tooltips.set_tip(self.rotateRightLineEdit, '请指定在竖直文本下需要向右旋转90°的字符。') hBox = gtk.HBox() label = gtk.Label('向右侧移动的字符') hBox.pack_start(label, expand=False) self.rightShiftLineEdit = gtk.Entry() self.rightShiftLineEdit.set_text(self.rightShiftTarget) hBox.pack_start(self.rightShiftLineEdit) self.tooltips.set_tip(self.rightShiftLineEdit, '请指定在竖直文本下需要向右侧移动的字符。') label = gtk.Label(' 移动比率:') hBox.pack_start(label, expand=False) self.rightShiftScaleSpin = gtk.SpinButton() self.rightShiftScaleSpin.set_digits(2) self.rightShiftScaleSpin.set_range(0.05, 1) self.rightShiftScaleSpin.set_increments(0.05, 0.1) self.rightShiftScaleSpin.set_value(self.rightShiftRate) hBox.pack_start(self.rightShiftScaleSpin, expand=False) self.tooltips.set_tip(self.rightShiftScaleSpin, '请指定相对于字符宽度的移动比率。(0~1)') self.dialog.vbox.pack_start(hBox) hBox = gtk.HBox() label = gtk.Label('向上移动的字符') hBox.pack_start(label, expand=False) self.upShiftLineEdit = gtk.Entry() self.upShiftLineEdit.set_text(self.upShiftTarget) hBox.pack_start(self.upShiftLineEdit) self.tooltips.set_tip(self.upShiftLineEdit, '请指定在竖直文本下需要向上移动的字符。') label = gtk.Label(' 移动比率:') hBox.pack_start(label, expand=False) self.upShiftScaleSpin = gtk.SpinButton() self.upShiftScaleSpin.set_digits(2) self.upShiftScaleSpin.set_range(0.05, 1) self.upShiftScaleSpin.set_increments(0.05, 0.1) self.upShiftScaleSpin.set_value(self.upShiftRate) hBox.pack_start(self.upShiftScaleSpin, expand=False) self.tooltips.set_tip(self.upShiftScaleSpin, '请指定相对于字符高度的移动比率。(0~1)') self.dialog.vbox.pack_start(hBox) hBox = gtk.HBox() label = gtk.Label('与前一个字符结合的字符') hBox.pack_start(label, expand=False) self.joinBeforeLineEdit = gtk.Entry() self.joinBeforeLineEdit.set_text(self.joinBeforeTarget) hBox.pack_start(self.joinBeforeLineEdit) self.dialog.vbox.pack_start(hBox) self.tooltips.set_tip(self.joinBeforeLineEdit, '请指定您需要与前一个字符结合使用的内容,例如“゛”或“゜”。') hBox = gtk.HBox() label = gtk.Label('连续字符被识别为一个字符') hBox.pack_start(label, expand=False) self.considerOneCharLineEdit = gtk.Entry() self.considerOneCharLineEdit.set_text(self.considerOneCharTarget) hBox.pack_start(self.considerOneCharLineEdit) self.tooltips.set_tip(self.considerOneCharLineEdit, '请指定在竖直文本下想要识别的连续字符,如“!!?”。') self.dialog.vbox.pack_start(hBox) hBox = gtk.HBox() label = gtk.Label('被强制缩小的字符') hBox.pack_start(label, expand=False) self.forceSmallerTargetLineEdit = gtk.Entry() self.forceSmallerTargetLineEdit.set_text(self.forceSmallerTarget) hBox.pack_start(self.forceSmallerTargetLineEdit) self.tooltips.set_tip(self.forceSmallerTargetLineEdit, '请指定在竖直文本下略微缩小的字符,例如“っ”或“ぁ”。') label = gtk.Label(' 缩小比率:') hBox.pack_start(label, expand=False) self.forceSmallerScaleSpin = gtk.SpinButton() self.forceSmallerScaleSpin.set_digits(2) self.forceSmallerScaleSpin.set_range(0.05, 1) self.forceSmallerScaleSpin.set_increments(0.05, 0.1) self.forceSmallerScaleSpin.set_value(self.forceSmallerScaleRate) hBox.pack_start(self.forceSmallerScaleSpin, expand=False) self.tooltips.set_tip(self.forceSmallerScaleSpin, '请指定相对于字符大小的缩小比率。(0~1)') self.dialog.vbox.pack_start(hBox) self.dialog.set_default_size(700, 600) self.dialog.show_all() ######################################################################## def notify_has_selection(self, buffer, *args): self.textViewSelectionCheck() ######################################################################## def notify_cursor_position(self, buffer, position): self.textViewSelectionCheck() ######################################################################## def button_release_event(self, textview, event): self.textViewSelectionCheck() ######################################################################## def button_press_event(self, *args): self.textViewSelectionCheck() ######################################################################## def textViewSelectionCheck(self): textbuffer = self.textView.get_buffer() selection = textbuffer.get_selection_bounds() if len(selection) < 2: self.rubyAddButton.set_sensitive(False) return else: text = textbuffer.get_text(selection[0], selection[1], include_hidden_chars=True) if re.match('^[^\\s]+[\\r]?[\\n].', text): self.rubyAddButton.set_sensitive(False) else: self.rubyAddButton.set_sensitive(True) ######################################################################## def tggleUseRubyCheckButton(self, checkBox, data=None): if self.useRubyCheckButton.get_active(): self.rubyFrame.show() else: self.rubyFrame.hide() ######################################################################## def clickAddRubyButton(self, button, data=None): ######################################################################## textbuffer = self.textView.get_buffer() selection = textbuffer.get_selection_bounds() if len(selection) < 2: return text = textbuffer.get_text(selection[0], selection[1], include_hidden_chars=True) if re.match('^[^\\s]+[\\r]?[\\n].', text): return ######################################################################## lineNo = selection[0].get_line() + 1 columnNo = selection[0].get_line_offset() + 1 self.rubyListStore.append([lineNo, columnNo, text, '请在此输入ruby文本']) ######################################################################## def rubyChanged(self, cellrenderertext, path, new_text, listStore, column): new_text = new_text.strip() if new_text: listStore[path][column] = new_text ######################################################################## def changedRubySlection(self, treeselection, listBox): if treeselection.count_selected_rows() < 1: self.rubyRemoveButton.set_sensitive(False) else: self.rubyRemoveButton.set_sensitive(True) ######################################################################## def clickRemoveRubyButton(self, button, data=None): if self.listSelection.count_selected_rows() < 1: return ######################################################################## selected = self.listSelection.get_selected() if len(selected) < 2: return ######################################################################## self.rubyListStore.remove(selected[1]) ######################################################################## def clickResetButton(self, button, data=None): self.resetValues() self.resetWidgetsValue() ######################################################################## def resetValues(self): self.lineSpaceRate = 1.0 self.charSpaceRate = 0.0 self.rubySizeRate = 1.0 / 3.0 self.rightShiftRate = 0.5 self.upShiftRate = 0.5 self.forceSmallerScaleRate = 0.8 self.useSizeBool = True self.useColorBool = True self.useRubyBool = True self.rotateRightTartget = self.defaultRotateRightTartget self.rightShiftTarget = self.defaultRightShiftTarget self.upShiftTarget = self.defaultUpShiftTarget self.joinBeforeTarget = self.defaultJoinBeforeTarget self.considerOneCharTarget = self.defaultConsiderOneCharTarget self.forceSmallerTarget = self.defaultForceSmallerTarget ######################################################################## def resetWidgetsValue(self): self.setDafultLineSpace() self.setDafultCharSpace() self.useSizeCheckButton.set_active(self.useSizeBool) self.useColorCheckButton.set_active(self.useColorBool) self.useRubyCheckButton.set_active(self.useRubyBool) self.setDefaultRubySize() self.rotateRightLineEdit.set_text(self.rotateRightTartget) self.rightShiftLineEdit.set_text(self.rightShiftTarget) self.rightShiftScaleSpin.set_value(self.rightShiftRate) self.upShiftLineEdit.set_text(self.upShiftTarget) self.upShiftScaleSpin.set_value(self.upShiftRate) self.joinBeforeLineEdit.set_text(self.joinBeforeTarget) self.considerOneCharLineEdit.set_text(self.considerOneCharTarget) self.forceSmallerTargetLineEdit.set_text(self.forceSmallerTarget) self.forceSmallerScaleSpin.set_value(self.forceSmallerScaleRate) ######################################################################## def loadPreference(self): self.resetValues() pref = GimpPreference(gimp.version[0], gimp.version[1], self.preferenceDirName, self.preferenceFileName) if not pref.load(): return self.lineSpaceRate = float(pref.getOne('space', 'lineSpaceRate', self.lineSpaceRate)) self.charSpaceRate = float(pref.getOne('space', 'charSpaceRate', self.charSpaceRate)) self.useSizeBool = bool(pref.getOne('bool', 'useSizeBool', self.useSizeBool)) self.useColorBool = bool(pref.getOne('bool', 'useColorBool', self.useColorBool)) self.useRubyBool = bool(pref.getOne('bool', 'useRubyBool', self.useRubyBool)) self.rubySizeRate = float(pref.getOne('ruby', 'rubySizeRate', self.rubySizeRate)) self.rightShiftRate = float(pref.getOne('shift', 'rightShiftRate', self.rightShiftRate)) self.upShiftRate = float(pref.getOne('shift', 'upShiftRate', self.upShiftRate)) self.forceSmallerScaleRate = float(pref.getOne('scale', 'forceSmallerScaleRate', self.forceSmallerScaleRate)) dummy = PangoAccess(None) self.rotateRightTartget = dummy.toUnicode( pref.getOne('text', 'rotateRightTartget', self.rotateRightTartget, True)) self.rightShiftTarget = dummy.toUnicode(pref.getOne('text', 'rightShiftTarget', self.rightShiftTarget, True)) self.upShiftTarget = dummy.toUnicode(pref.getOne('text', 'upShiftTarget', self.upShiftTarget, True)) self.joinBeforeTarget = dummy.toUnicode(pref.getOne('text', 'joinBeforeTarget', self.joinBeforeTarget, True)) self.considerOneCharTarget = dummy.toUnicode( pref.getOne('text', 'considerOneCharTarget', self.considerOneCharTarget, True)) self.forceSmallerTarget = dummy.toUnicode( pref.getOne('text', 'forceSmallerTarget', self.forceSmallerTarget, True)) ######################################################################## def savePreference(self): pointSize = self.getDefaultFontSizeInPoint() if pointSize <= 0.0: self.lineSpaceRate = 1.0 self.charSpaceRate = 0.0 self.rubySizeRate = 1.0 / 3.0 else: self.lineSpaceRate = self.lineSpacePointSpin.get_value() / float(pointSize) self.charSpaceRate = self.charSpacePointSpin.get_value() / float(pointSize) self.rubySizeRate = self.rubyPointSpin.get_value() / float(pointSize) self.rightShiftRate = float(self.rightShiftScaleSpin.get_value()) self.upShiftRate = float(self.upShiftScaleSpin.get_value()) self.forceSmallerScaleRate = float(self.forceSmallerScaleSpin.get_value()) pref = GimpPreference(gimp.version[0], gimp.version[1], self.preferenceDirName, self.preferenceFileName) if not pref.setOne('space', 'lineSpaceRate', self.lineSpaceRate): return False if not pref.setOne('space', 'charSpaceRate', self.charSpaceRate): return False if not pref.setOne('bool', 'useSizeBool', self.useSizeBool): return False if not pref.setOne('bool', 'useColorBool', self.useColorBool): return False if not pref.setOne('bool', 'useRubyBool', self.useRubyBool): return False if not pref.setOne('ruby', 'rubySizeRate', self.rubySizeRate): return False if not pref.setOne('shift', 'rightShiftRate', self.rightShiftRate): return False if not pref.setOne('shift', 'upShiftRate', self.upShiftRate): return False if not pref.setOne('scale', 'forceSmallerScaleRate', self.forceSmallerScaleRate): return False if not pref.setOne('text', 'rotateRightTartget', self.rotateRightTartget): return False if not pref.setOne('text', 'rightShiftTarget', self.rightShiftTarget): return False if not pref.setOne('text', 'upShiftTarget', self.upShiftTarget): return False if not pref.setOne('text', 'joinBeforeTarget', self.joinBeforeTarget): return False if not pref.setOne('text', 'considerOneCharTarget', self.considerOneCharTarget): return False if not pref.setOne('text', 'forceSmallerTarget', self.forceSmallerTarget): return False return pref.save() ######################################################################## def setDafultLineSpace(self, defaultValue=12): try: pointSize = self.getDefaultFontSizeInPoint() if pointSize < 0: self.lineSpacePointSpin.set_value(defaultValue) return # 行間は文字のポイントサイズで self.lineSpacePointSpin.set_value(pointSize * self.lineSpaceRate) except: return ######################################################################## def setDafultCharSpace(self, defaultValue=0): try: pointSize = self.getDefaultFontSizeInPoint() if pointSize < 0: self.charSpacePointSpin.set_value(defaultValue) return # 文字の間隔はポイントサイズで self.charSpacePointSpin.set_value(pointSize * self.charSpaceRate) except: return ######################################################################## def getDefaultFontSizeInPoint(self): if not self.image: return -1.0 try: layer = pdb.gimp_image_get_active_layer(self.image) if not pdb.gimp_item_is_text_layer(layer): return -1.0 sizeInInch = 0 (fontSize, fontUnit) = pdb.gimp_text_layer_get_font_size(layer) if pdb.gimp_unit_get_identifier(fontUnit).lower() in ['px', 'pix', 'pixel', 'pixels']: (xResolution, yResoLution) = pdb.gimp_image_get_resolution(self.image) sizeInInch = fontSize / xResolution else: sizeInInch = fontSize / pdb.gimp_unit_get_factor(fontUnit) point = sizeInInch * 72 return point except: return -1.0 ######################################################################## def setDefaultRubySize(self, defaultValue=6): try: pointSize = self.getDefaultFontSizeInPoint() if pointSize < 0: self.rubyPointSpin.set_value(defaultValue) return # ルビは 1/3 サイズで self.rubyPointSpin.set_value(pointSize * self.rubySizeRate) except: return ######################################################################## def setResults(self): self.flags['lineSpacePoint'] = self.lineSpacePointSpin.get_value() self.flags['charSpacePoint'] = self.charSpacePointSpin.get_value() self.flags['useFontSize'] = self.useSizeCheckButton.get_active() self.flags['useFontColor'] = self.useColorCheckButton.get_active() self.flags['useRuby'] = self.useRubyCheckButton.get_active() self.flags['rubyPointSize'] = self.rubyPointSpin.get_value() self.rotateRightTartget = self.pango.toUnicode(self.rotateRightLineEdit.get_text().strip()) self.rightShiftTarget = self.pango.toUnicode(self.rightShiftLineEdit.get_text().strip()) self.rightShiftScale = float(self.rightShiftScaleSpin.get_value()) self.upShiftTarget = self.pango.toUnicode(self.upShiftLineEdit.get_text().strip()) self.upShiftScale = float(self.upShiftScaleSpin.get_value()) self.joinBeforeTarget = self.pango.toUnicode(self.joinBeforeLineEdit.get_text().strip()) self.considerOneCharTarget = self.pango.toUnicode(self.considerOneCharLineEdit.get_text().strip()) self.forceSmallerTarget = self.pango.toUnicode(self.forceSmallerTargetLineEdit.get_text().strip()) self.forceSmallerScale = float(self.forceSmallerScaleSpin.get_value()) self.rubyInfo = [] # 配列としてアクセス for row in self.rubyListStore: rowDictionary = {} rowDictionary['lineNum'] = row[0] rowDictionary['columnNum'] = row[1] rowDictionary['targetText'] = self.pango.toUnicode(row[2]) rowDictionary['rubyText'] = self.pango.toUnicode(row[3]) rowDictionary['lineIndex'] = rowDictionary['lineNum'] - 1 rowDictionary['columnIndex'] = rowDictionary['columnNum'] - 1 self.rubyInfo.append(rowDictionary) self.savePreference() ######################################################################## def run(self): if self.dialog: self.results = self.dialog.run() self.setResults() self.dialog.destroy() self.dialog = None return self.results else: return gtk.RESPONSE_CANCEL ######################################################################## ######################################################################## # 縦書クラス情報取得 # # テキストレイヤーから縦書きレイヤーを生成 # ######################################################################## class VerticalTextLayer: groupNameFormat = '竖排文本组 #' lineGroupNameFormat = '行 #' rubyGroupNameFormat = 'Ruby组 #' ######################################################################## def __init__(self, verticalTextAccess): self.verticalTextAccess = verticalTextAccess ######################################################################## def __del__(self): pass ######################################################################## def create(self): if not self.verticalTextAccess: return if not self.verticalTextAccess.image: return if not self.verticalTextAccess.targetTextLayer: return if not self.verticalTextAccess.pango: return (fontCount, fontList) = pdb.gimp_fonts_get_list(None) self.verticalTextAccess.pango.setFontList(fontList) self.verticalTextAccess.pango.parse() if not self.verticalTextAccess.pango.lines: return if len(self.verticalTextAccess.pango.lines) < 1: return self.image = self.verticalTextAccess.image self.targetTextLayer = self.verticalTextAccess.targetTextLayer rubyInfo = {} if self.verticalTextAccess.rubyInfo and len(self.verticalTextAccess.rubyInfo) > 0: for info in self.verticalTextAccess.rubyInfo: if 'lineNum' in info.keys(): if info['lineNum'] in rubyInfo.keys(): rubyInfo[info['lineNum']].append(info) else: rubyInfo[info['lineNum']] = [info] self.useRubyFlag = self.verticalTextAccess.flags['useRuby'] self.rubyPointSize = self.verticalTextAccess.flags['rubyPointSize'] pdb.gimp_layer_set_visible(self.targetTextLayer, False) (xResolution, yResolution) = pdb.gimp_image_get_resolution(self.image) pointId = 1 unitList = [] for unitId in range(pdb.gimp_unit_get_number_of_units()): name = pdb.gimp_unit_get_identifier(unitId).lower() unitList.append(name) if name in ('pt', 'point', 'points'): pointId = unitId (targetLayerOffsetX, targetLayerOffsetY) = pdb.gimp_drawable_offsets(self.targetTextLayer) targetLayerWidth = pdb.gimp_drawable_width(self.targetTextLayer) targetLayerHeight = pdb.gimp_drawable_height(self.targetTextLayer) rightX = targetLayerOffsetX + targetLayerWidth; topY = targetLayerOffsetY defaultFont = pdb.gimp_text_layer_get_font(self.targetTextLayer) (defaultFontSize, defaultFontUnit) = pdb.gimp_text_layer_get_font_size(self.targetTextLayer) self.verticalTextAccess.pango.setDefaultFont(defaultFont) self.verticalTextAccess.pango.parse() self.lines = self.verticalTextAccess.pango.lines defaultColor = pdb.gimp_text_layer_get_color(self.targetTextLayer) antialias = pdb.gimp_text_layer_get_antialias(self.targetTextLayer) (hinting, autoHint) = pdb.gimp_text_layer_get_hinting(self.targetTextLayer) # '縦書グループ' index = 0 while pdb.gimp_image_get_layer_by_name(self.image, self.groupNameFormat + str(index)): index += 1 topLayerGroup = pdb.gimp_layer_group_new(self.image) pdb.gimp_item_set_name(topLayerGroup, self.groupNameFormat + str(index)) position = pdb.gimp_image_get_layer_position(self.image, self.targetTextLayer) parent = pdb.gimp_item_get_parent(self.targetTextLayer) pdb.gimp_image_insert_layer(self.image, topLayerGroup, parent, position + 1) lineNumber = 0 self.useRubyFlag = self.verticalTextAccess.flags['useRuby'] self.rubyPointSize = self.verticalTextAccess.flags['rubyPointSize'] self.rubyPixcelSize = float(self.rubyPointSize / 72.0) * xResolution lineSpacePoint = self.verticalTextAccess.flags['lineSpacePoint'] lineSpacePixcel = float(lineSpacePoint / 72.0) * xResolution charSpacePoint = self.verticalTextAccess.flags['charSpacePoint'] charSpacePixcel = float(charSpacePoint / 72.0) * yResolution ######################################################################## for line in self.lines: progressVule = float(lineNumber) / float(len(self.lines)) pdb.gimp_progress_update(progressVule) lineNumber += 1 if not line: continue if len(line) < 1: continue (count, idList) = pdb.gimp_item_get_children(topLayerGroup) lineLayerGroup = pdb.gimp_layer_group_new(self.image) index = 0 while pdb.gimp_image_get_layer_by_name(self.image, self.lineGroupNameFormat + str(lineNumber) + ' #' + str(index)): index += 1 pdb.gimp_item_set_name(lineLayerGroup, self.lineGroupNameFormat + str(lineNumber) + ' #' + str(index)) parent = topLayerGroup pdb.gimp_image_insert_layer(self.image, lineLayerGroup, parent, count) maxWidth = 0 for word in line: fontName = defaultFont fontSize = defaultFontSize unit = defaultFontUnit if 'point' in word.keys(): fontSize = float(word['point']) unit = pointId if unit == 0: if fontSize > maxWidth: maxWidth = fontSize else: width = (fontSize / pdb.gimp_unit_get_factor(unit)) * xResolution if width > maxWidth: maxWidth = width offsetY = topY charsIndex = 0; ######################################################################## for wordIndex in range(len(line)): word = line[wordIndex] progressWordVule = float(float(wordIndex) / float(len(line))) / 10.0 pdb.gimp_progress_update(progressVule + progressWordVule) fontName = defaultFont fontSize = defaultFontSize unit = defaultFontUnit color = defaultColor # width = fontSize if 'font' in word.keys(): fontName = word['font'] if 'point' in word.keys() and self.verticalTextAccess.flags['useFontSize']: fontSize = float(word['point']) unit = pointId if 'color' in word.keys() and self.verticalTextAccess.flags['useFontColor']: color = gimpcolor.RGB(float(word['color']['red']), float(word['color']['green']), float(word['color']['blue']), 1.0) fontPixcelSize = fontSize if unit == 0: width = fontSize fontPixcelSize = fontSize else: width = (fontSize / pdb.gimp_unit_get_factor(unit)) * xResolution fontPixcelSize = (fontSize / pdb.gimp_unit_get_factor(unit)) * xResolution wordTextCheck = word['text'] if wordTextCheck.isspace(): wordTextCheck = 'DummyText' textFontExtents = pdb.gimp_text_get_extents_fontname( wordTextCheck, fontPixcelSize, PIXELS, fontName) textFontWidth = textFontExtents[0] textFontHeight = textFontExtents[1] textFontAscent = textFontExtents[2] textFontDescent = textFontExtents[3] charsList = [] chaceText = u'' ########################################################################## # 1文字ごとに分解 for oneChar in word['text']: if self.useRubyFlag and lineNumber in rubyInfo.keys(): # 行に対応するルビの準備 for info in rubyInfo[lineNumber]: targetText = info['targetText'] rubyText = info['rubyText'] if info['columnIndex'] == charsIndex and len(targetText) > 0 and len(rubyText) > 0: listIndex = len(charsList) info['startIndex'] = len(charsList) joinBeforeTarget = re.escape(self.verticalTextAccess.joinBeforeTarget) considerOneCharTarget = self.verticalTextAccess.considerOneCharTarget pattern = u'(.[' + joinBeforeTarget + '])' pattern += u'|(' + considerOneCharTarget + ']+)' dummyTarget = re.sub(pattern, u'X', targetText) info['endIndex'] = info['startIndex'] + len(dummyTarget) - 1 if 'ruby' in word.keys(): word['ruby'][listIndex] = info else: word['ruby'] = {listIndex: info} if oneChar in self.verticalTextAccess.joinBeforeTarget: # 直前の文字に結合させる文字(濁点など) if len(chaceText) > 0: charsList.append(chaceText) chaceText = u'' index = len(charsList) - 1 if index < 0: charsList.append(oneChar) else: charsList[index] += oneChar elif oneChar in self.verticalTextAccess.considerOneCharTarget: # 連続しても1文字と認識させる文字(!? など) chaceText += oneChar else: if len(chaceText) > 0: charsList.append(chaceText) chaceText = u'' charsList.append(oneChar) charsIndex += 1 if len(chaceText) > 0: charsList.append(chaceText) chaceText = u'' ########################################################################## ruby = None for listIndex in range(len(charsList)): progressCharVule = float(float(listIndex) / float(len(charsList))) / 100.0 pdb.gimp_progress_update(progressVule + progressWordVule + progressCharVule) oneChar = charsList[listIndex] if 'ruby' in word.keys() and listIndex in word['ruby'].keys(): # ルビの開始位置設定 ruby = word['ruby'][listIndex] ruby['top'] = offsetY ruby['left'] = rightX + (self.rubyPixcelSize / 2.0) fontSizeRate = 1.0 feedSizePoint = fontSize * fontSizeRate feedSizePixcel = feedSizePoint if unit == 0: feedSizePixcel = feedSizePoint else: feedSizePixcel = (feedSizePoint / pdb.gimp_unit_get_factor(unit)) * yResolution if oneChar in self.verticalTextAccess.forceSmallerTarget: fontSizeRate = self.verticalTextAccess.forceSmallerScale if oneChar.isspace(): offsetY += (feedSizePixcel * fontSizeRate) + charSpacePixcel continue layer = pdb.gimp_text_layer_new(self.image, oneChar, fontName, fontSize * fontSizeRate, unit) index = 0 while pdb.gimp_image_get_layer_by_name(self.image, oneChar + ' #' + str(index)): index += 1 width = pdb.gimp_drawable_width(layer) if width > maxWidth: width = maxWidth offsetX = rightX - maxWidth + ((maxWidth - width) / 2.0) pdb.gimp_item_set_name(layer, oneChar + ' #' + str(index)) pdb.gimp_layer_set_offsets(layer, offsetX, offsetY) (count, idList) = pdb.gimp_item_get_children(lineLayerGroup) pdb.gimp_image_insert_layer(self.image, layer, lineLayerGroup, count) pdb.gimp_text_layer_set_color(layer, color) pdb.gimp_text_layer_set_antialias(layer, antialias) pdb.gimp_text_layer_set_hinting(layer, hinting, autoHint) pdb.gimp_text_layer_set_hint_style(layer, TEXT_HINT_STYLE_FULL) if oneChar in self.verticalTextAccess.rotateRightTartget: centerX = float(offsetX) + (pdb.gimp_drawable_width(layer) / 2.0) centerY = float(offsetY) + (pdb.gimp_drawable_height(layer) / 2.0) pdb.gimp_item_transform_rotate_simple(layer, ROTATE_90, False, centerX, centerY) if oneChar in self.verticalTextAccess.rightShiftTarget or oneChar in self.verticalTextAccess.upShiftTarget: newOffsetX = offsetX newOffsetY = offsetY if oneChar in self.verticalTextAccess.rightShiftTarget: newOffsetX += pdb.gimp_drawable_width(layer) * self.verticalTextAccess.rightShiftScale if oneChar in self.verticalTextAccess.upShiftTarget: newOffsetY -= pdb.gimp_drawable_height(layer) * self.verticalTextAccess.upShiftScale pdb.gimp_layer_set_offsets(layer, int(newOffsetX), int(newOffsetY)) ######################################################################## # 次の文字の開始位置 nextOffsetY = offsetY + (feedSizePixcel * fontSizeRate) + charSpacePixcel ######################################################################## if ruby and ruby['endIndex'] <= listIndex and len(ruby['rubyText']) > 0: # ルビの埋め込み rubyGroup = pdb.gimp_layer_group_new(self.image) index = 0 while True: rubyGroupName = self.rubyGroupNameFormat + str(index) if not pdb.gimp_image_get_layer_by_name(self.image, rubyGroupName): break index += 1 pdb.gimp_item_set_name(rubyGroup, rubyGroupName) (count, idList) = pdb.gimp_item_get_children(lineLayerGroup) pdb.gimp_image_insert_layer(self.image, rubyGroup, lineLayerGroup, count) rubyFontExtents = pdb.gimp_text_get_extents_fontname( ruby['rubyText'], self.rubyPixcelSize, PIXELS, defaultFont) rubyFontWidth = rubyFontExtents[0] rubyFontHeight = rubyFontExtents[1] rubyFontAscent = rubyFontExtents[2] rubyFontDescent = rubyFontExtents[3] rubyTop = ruby['top'] + math.ceil(textFontDescent * (-1) * fontSizeRate / 2.0) rubyLeft = ruby['left'] rubyBottom = nextOffsetY - charSpacePixcel rubyCharCount = len(ruby['rubyText']) restRubyPixcel = (rubyBottom - rubyTop) - (rubyFontHeight * rubyCharCount) rubyFeed = 0 if restRubyPixcel > 0: rubyFeed = restRubyPixcel / (rubyCharCount + 1) rubyTop += rubyFeed ######################################################################## for rubyChar in ruby['rubyText']: if rubyChar.isspace(): rubyTop += self.rubyPixcelSize + rubyFeed rubyLayer = pdb.gimp_text_layer_new(self.image, rubyChar, defaultFont, self.rubyPointSize, pointId) index = 0 while True: rubyName = rubyChar + ' #' + str(index) if not pdb.gimp_image_get_layer_by_name(self.image, rubyName): break index += 1 pdb.gimp_item_set_name(rubyLayer, rubyName) pdb.gimp_layer_set_offsets(rubyLayer, rubyLeft, rubyTop) (count, idList) = pdb.gimp_item_get_children(rubyGroup) pdb.gimp_image_insert_layer(self.image, rubyLayer, rubyGroup, count) pdb.gimp_text_layer_set_hinting(rubyLayer, True, True) pdb.gimp_text_layer_set_hint_style(rubyLayer, TEXT_HINT_STYLE_FULL) rubyTop += pdb.gimp_drawable_height(rubyLayer) + rubyFeed ######################################################################## ruby = None ######################################################################## # 開始位置を次の行へ offsetY = nextOffsetY ######################################################################## ######################################################################## rightX -= maxWidth + lineSpacePixcel ######################################################################## pdb.gimp_image_set_active_layer(self.image, topLayerGroup) pdb.gimp_progress_update(1.0) ######################################################################## ######################################################################## # テキストレイヤーから縦書きレイヤーを作成する # def python_fu_vertical_writing_2_8(image, drawable): proc_name = 'python-fu-vertical-writing-2-8' verticalWriting = VerticalTextAccess(image) if not verticalWriting.checkUsable(): # 使用不可 return # 縦書き処理を実行 verticalWriting.dialogMake(proc_name) results = verticalWriting.run() if results == gtk.RESPONSE_CANCEL: return layerCreator = VerticalTextLayer(verticalWriting) pdb.gimp_image_undo_group_start(image) pdb.gimp_progress_init(u'「縦書き写植」を処理中です...', None) try: layerCreator.create() except: pdb.gimp_message('処理中にエラーが発生しました。') pdb.gimp_progress_end() pdb.gimp_image_undo_group_end(image) return ######################################################################## register( 'python-fu-vertical-writing-2-8', # プロシジャの名前 'テキストレイヤーから縦書きレイヤーを作成する。', # プロシジャの説明文 '文本竖写脚本,根据文本图层创建竖排文本图层,适用于GIMP 2.8版以上。', # PDBに登録する追加情報 'かんら・から', # 作者名 'GPLv3', # ライセンス情報 '2013.08.30', # 作成日 '文本竖书(Python)', # メニューアイテム '*', # 対応する画像タイプ [ (PF_IMAGE, 'image', 'Input image', None), (PF_DRAWABLE, 'drawable', 'Input drawable', None) ], # プロシジャの引数 [], # 戻り値の定義 python_fu_vertical_writing_2_8, # 処理を受け持つ関数名 menu='/Layer/图层操作(Python-fu)' # メニュー表示場所 ) main() # プラグインを駆動させるための関数