diff options
| -rw-r--r-- | Lib/defconQt/__main__.py | 4 | ||||
| -rw-r--r-- | Lib/defconQt/fontView.py | 10 | ||||
| -rw-r--r-- | Lib/defconQt/glyphView.py | 58 | ||||
| -rw-r--r-- | Lib/defconQt/objects/defcon.py | 162 | ||||
| -rw-r--r-- | Lib/defconQt/pens/__init__.py | 0 | ||||
| -rw-r--r-- | Lib/defconQt/pens/copySelectionPen.py | 59 | ||||
| -rw-r--r-- | setup.py | 1 | 
7 files changed, 272 insertions, 22 deletions
| diff --git a/Lib/defconQt/__main__.py b/Lib/defconQt/__main__.py index 07f273c..c1e548c 100644 --- a/Lib/defconQt/__main__.py +++ b/Lib/defconQt/__main__.py @@ -1,4 +1,4 @@ -from defcon import Font +from defconQt.objects.defcon import TFont  from defconQt.fontView import MainWindow  import sys @@ -20,6 +20,6 @@ representationFactories.registerAllFactories()  app = QApplication(sys.argv)  # TODO: http://stackoverflow.com/a/21330349/2037879  app.setWindowIcon(QIcon("defconQt/resources/icon.png")) -window = MainWindow(Font(ufoFile)) +window = MainWindow(TFont(ufoFile))  window.show()  sys.exit(app.exec_()) diff --git a/Lib/defconQt/fontView.py b/Lib/defconQt/fontView.py index ba773c3..6a603a4 100644 --- a/Lib/defconQt/fontView.py +++ b/Lib/defconQt/fontView.py @@ -1,10 +1,9 @@ -from defcon import Font  from defconQt.featureTextEditor import MainEditWindow  from defconQt.fontInfo import TabDialog  from defconQt.glyphCollectionView import GlyphCollectionWidget  from defconQt.glyphView import MainGfxWindow  from defconQt.groupsView import GroupsWindow -from defconQt.objects.defcon import CharacterSet +from defconQt.objects.defcon import CharacterSet, TFont  from defconQt.spaceCenter import MainSpaceWindow  from fontTools.agl import AGL2UV  # TODO: remove globs when things start to stabilize @@ -80,7 +79,7 @@ class AddGlyphDialog(QDialog):          self.setLayout(layout)      @staticmethod -    def getNewGlyphNames(parent=None, currentGlyphs=None): +    def getNewGlyphNames(parent, currentGlyphs=None):          dialog = AddGlyphDialog(currentGlyphs, parent)          result = dialog.exec_()          sortFont = False @@ -229,7 +228,7 @@ class SortDialog(QDialog):          return 0      @staticmethod -    def getDescriptor(parent=None, sortDescriptor=None): +    def getDescriptor(parent, sortDescriptor=None):          dialog = SortDialog(sortDescriptor, parent)          result = dialog.exec_()          if dialog.characterSetBox.isChecked(): @@ -336,7 +335,7 @@ class MainWindow(QMainWindow):      def newFile(self):          ok = self.maybeSaveBeforeExit()          if not ok: return -        self.font = Font() +        self.font = TFont()          self.font.info.unitsPerEm = 1000          self.font.info.ascender = 750          self.font.info.descender = -250 @@ -476,6 +475,7 @@ class MainWindow(QMainWindow):              else:                  glyph.lib["public.markColor"] = ",".join(str(c) for c in color.getRgbF()) +    # TODO: maybe store this in TFont      def newStandardGlyph(self, name):          self.font.newGlyph(name)          self.font[name].width = 500 diff --git a/Lib/defconQt/glyphView.py b/Lib/defconQt/glyphView.py index c42e4b7..cfeff3b 100644 --- a/Lib/defconQt/glyphView.py +++ b/Lib/defconQt/glyphView.py @@ -1,7 +1,7 @@  from enum import Enum  from math import copysign -from defcon.objects.contour import Contour -from defcon.objects.point import Point +from defconQt.objects.defcon import TContour, TGlyph +from defconQt.pens.copySelectionPen import CopySelectionPen  from fontTools.misc import bezierTools  from PyQt5.QtCore import *#QFile, QLineF, QObject, QPointF, QRectF, QSize, Qt  from PyQt5.QtGui import *#QBrush, QColor, QImage, QKeySequence, QPainter, QPainterPath, QPixmap, QPen @@ -11,13 +11,13 @@ from PyQt5.QtWidgets import *#(QAction, QActionGroup, QApplication, QFileDialog,  from PyQt5.QtOpenGL import QGL, QGLFormat, QGLWidget -class GotoWindow(QDialog): +class GotoDialog(QDialog):      alphabetical = [          dict(type="alphabetical", allowPseudoUnicode=True)      ]      def __init__(self, font, parent=None): -        super(GotoWindow, self).__init__(parent) +        super(GotoDialog, self).__init__(parent)          self.setWindowModality(Qt.WindowModal)          self.setWindowTitle("Go to…")          self.font = font @@ -79,14 +79,18 @@ class GotoWindow(QDialog):          self.glyphList.addItems(glyphs)          if select: self.glyphList.setCurrentRow(0) -    def accept(self): -        # TODO: zap going thru the view, here and above -        currentItem = self.glyphList.currentItem() +    @staticmethod +    def getNewGlyph(parent, font): +        dialog = GotoDialog(font, parent) +        result = dialog.exec_() +        currentItem = dialog.glyphList.currentItem() +        newGlyph = None          if currentItem is not None: -            targetGlyph = currentItem.text() -            if not targetGlyph in self.font: return -            self._view.setGlyph(self.font[targetGlyph]) -        super(GotoWindow, self).accept() +            newGlyphName = currentItem.text() +            if newGlyphName in dialog.font: +                newGlyph = dialog.font[newGlyphName] +        return (newGlyph, result) +  class MainGfxWindow(QMainWindow):      def __init__(self, glyph, parent=None): @@ -263,6 +267,9 @@ class OffCurvePointItem(QGraphicsEllipseItem):                      value.setY(0)          elif change == QGraphicsItem.ItemPositionHasChanged:              self.parentItem()._CPMoved(value) +        # TODO: consider what to do w offCurves +        #elif change == QGraphicsItem.ItemSelectedHasChanged: +        #    pass#self.parentItem()._CPSelChanged(value)          return value      def mousePressEvent(self, event): @@ -432,6 +439,8 @@ class OnCurvePointItem(QGraphicsPathItem):                  self._contour[pointIndex+1].x = self.pos().x()+nextPos.x()                  self._contour[pointIndex+1].y = self.pos().y()+nextPos.y()              self.setShallowDirty() +        elif change == QGraphicsItem.ItemSelectedHasChanged: +            self._point.selected = value          return value      def setShallowDirty(self): @@ -667,9 +676,11 @@ class GlyphScene(QGraphicsScene):              event.accept()              return          elif key == Qt.Key_J: -            glyph = self.views()[0]._glyph -            dialog = GotoWindow(glyph.getParent(), self.parent()) -            dialog.exec_() +            view = self.views()[0] +            glyph = view._glyph +            newGlyph, ok = GotoDialog.getNewGlyph(self.parent(), glyph.getParent()) +            if ok and newGlyph is not None: +                view.setGlyph(newGlyph)              return          elif event.matches(QKeySequence.Undo):              if len(self._dataForUndo) > 0: @@ -699,6 +710,23 @@ class GlyphScene(QGraphicsScene):              self.setSelectionArea(QPainterPath(), view.transform())              event.accept()              return +        elif event.matches(QKeySequence.Copy): +            clipboard = QApplication.clipboard() +            mimeData = clipboard.mimeData() +            pen = CopySelectionPen() +            self._glyphObject.drawPoints(pen) +            # XXX: clipboard should outlive the widget window! +            self._clipboardObject = pen.getGlyph().serializeForUndo() +            event.accept() +            return +        elif event.matches(QKeySequence.Paste): +            if self._clipboardObject is None: return +            pen = self._glyphObject.getPointPen() +            pasteGlyph = TGlyph() +            pasteGlyph.deserializeFromUndo(self._clipboardObject) +            pasteGlyph.drawPoints(pen) +            event.accept() +            return          else:              sel = self.selectedItems()              if len(sel) == 1 and isinstance(sel[0], OffCurvePointItem) and \ @@ -798,7 +826,7 @@ class GlyphScene(QGraphicsScene):              lastContour.dirty = True              self._editing = True          elif not (touched and isinstance(touched, OnCurvePointItem)): -            nextC = Contour() +            nextC = TContour()              self._glyphObject.appendContour(nextC)              nextC.addPoint((x,y), "move") diff --git a/Lib/defconQt/objects/defcon.py b/Lib/defconQt/objects/defcon.py index 487865a..5de0297 100644 --- a/Lib/defconQt/objects/defcon.py +++ b/Lib/defconQt/objects/defcon.py @@ -1,3 +1,165 @@ +from defcon import Font, Contour, Glyph, Point +from defcon.objects.base import BaseObject + +class TFont(Font): +    def __init__(self, *args, **kwargs): +        if not "glyphClass" in kwargs: +            kwargs["glyphClass"] = TGlyph +        if not "glyphContourClass" in kwargs: +            kwargs["glyphContourClass"] = TContour +        if not "glyphPointClass" in kwargs: +            kwargs["glyphPointClass"] = TPoint +        super(TFont, self).__init__(*args, **kwargs) + +    # XXX: had to copy all of this so as to exclude template glyphs from +    # save. It pains me that's the only thing to be done w current upstream +    def save(self, path=None, formatVersion=None): +        saveAs = False +        if path is not None and path != self._path: +            saveAs = True +        else: +            path = self._path +        ## work out the format version +        # if None is given, fallback to the one that +        # came in when the UFO was loaded +        if formatVersion is None and self._ufoFormatVersion is not None: +            formatVersion = self._ufoFormatVersion +        # otherwise fallback to 2 +        elif self._ufoFormatVersion is None: +            formatVersion = 2 +        ## make a UFOWriter +        ufoWriter = ufoLib.UFOWriter(path, formatVersion=formatVersion) +        ## save objects +        saveInfo = False +        saveKerning = False +        saveGroups = False +        saveFeatures = False +        ## lib should always be saved +        saveLib = True +        # if in a save as, save all objects +        if saveAs: +            saveInfo = True +            saveKerning = True +            saveGroups = True +            saveFeatures = True +        ## if changing ufo format versions, save all objects +        if self._ufoFormatVersion != formatVersion: +            saveInfo = True +            saveKerning = True +            saveGroups = True +            saveFeatures = True +        # save info, kerning and features if they are dirty +        if self._info is not None and self._info.dirty: +            saveInfo = True +        if self._kerning is not None and self._kerning.dirty: +            saveKerning = True +        if self._features is not None and self._features.dirty: +            saveFeatures = True +        # always save groups and lib if they are loaded +        # as they contain sub-objects that may not notify +        # the main object about changes. +        if self._groups is not None: +            saveGroups = True +        if self._lib is not None: +            saveLib = True +        # save objects as needed +        if saveInfo: +            ufoWriter.writeInfo(self.info) +            self._stampInfoDataState() +            self.info.dirty = False +        if saveKerning: +            ufoWriter.writeKerning(self.kerning) +            self._stampKerningDataState() +            self.kerning.dirty = False +        if saveGroups: +            ufoWriter.writeGroups(self.groups) +            self._stampGroupsDataState() +        if saveFeatures and formatVersion > 1: +            ufoWriter.writeFeatures(self.features.text) +            self._stampFeaturesDataState() +        if saveLib: +            # if making format version 1, do some +            # temporary down conversion before +            # passing the lib to the writer +            libCopy = dict(self.lib) +            if formatVersion == 1: +                self._convertToFormatVersion1RoboFabData(libCopy) +            ufoWriter.writeLib(libCopy) +            self._stampLibDataState() +        ## save glyphs +        # for a save as operation, load all the glyphs +        # and mark them as dirty. +        if saveAs: +            for glyph in self: +                glyph.dirty = True +        glyphSet = ufoWriter.getGlyphSet() +        for glyphName, glyphObject in self._glyphs.items(): +            if glyphObject.template: continue +            if glyphObject.dirty: +                glyphSet.writeGlyph(glyphName, glyphObject, glyphObject.drawPoints) +                self._stampGlyphDataState(glyphObject) +        # remove deleted glyphs +        if not saveAs and self._scheduledForDeletion: +            for glyphName in self._scheduledForDeletion: +                if glyphName in glyphSet: +                    glyphSet.deleteGlyph(glyphName) +        glyphSet.writeContents() +        self._glyphSet = glyphSet +        self._scheduledForDeletion = [] +        self._path = path +        self._ufoFormatVersion = formatVersion +        self.dirty = False + +class TGlyph(Glyph): +    def __init__(self, *args, **kwargs): +        super(TGlyph, self).__init__(*args, **kwargs) +        self._template = False + +    def _get_template(self): +        return self._template + +    def _set_template(self, value): +        self._template = value + +    template = property(_get_template, _set_template, doc="A boolean indicating whether the glyph is a template glyph.") + +    def _set_dirty(self, value): +        BaseObject._set_dirty(self, value) +        if value: +            self.template = False + +    dirty = property(BaseObject._get_dirty, _set_dirty) + +class TContour(Contour): +    def __init__(self, pointClass=None): +        if pointClass is None: +            pointClass = TPoint +        super(TContour, self).__init__(pointClass) + +    def drawPoints(self, pointPen): +        """ +        Draw the contour with **pointPen**. +        """ +        pointPen.beginPath() +        for point in self._points: +            pointPen.addPoint((point.x, point.y), segmentType=point.segmentType, smooth=point.smooth, name=point.name, selected=point.selected) +        pointPen.endPath() + +class TPoint(Point): +    __slots__ = ["_selected"] + +    def __init__(self, pt, segmentType=None, smooth=False, name=None, selected=False): +        super(TPoint, self).__init__(pt, segmentType, smooth, name) +        self._selected = selected + +    def _get_selected(self): +        return self._selected + +    def _set_selected(self, value): +        self._selected = value + +    selected = property(_get_selected, _set_selected, doc="A boolean indicating the selected state of the point.") +  class CharacterSet(object):      __slots__ = ["_name", "_glyphNames"] diff --git a/Lib/defconQt/pens/__init__.py b/Lib/defconQt/pens/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Lib/defconQt/pens/__init__.py diff --git a/Lib/defconQt/pens/copySelectionPen.py b/Lib/defconQt/pens/copySelectionPen.py new file mode 100644 index 0000000..53d917b --- /dev/null +++ b/Lib/defconQt/pens/copySelectionPen.py @@ -0,0 +1,59 @@ +from defcon import Glyph +from robofab.pens.pointPen import AbstractPointPen + +class CopySelectionPen(AbstractPointPen): +    def __init__(self, glyph=None): +        if glyph is None: +            glyph = Glyph() +        self._glyph = glyph +        self._contour = None +        self._havePoint = False +        self._originalContourIsOpen = False + +    def beginPath(self): +        self._contour = self._glyph.contourClass(pointClass=self._glyph.pointClass) + +    def endPath(self, keepOpened=False): +        if self._havePoint: +            self.elideOrphanOffCurves(True) +            if self._originalContourIsOpen or keepOpened: +                self._contour[0].segmentType = "move" +            self._contour.dirty = False +            self._glyph.appendContour(self._contour) +        self._contour = None +        self._havePoint = False +        self._originalContourIsOpen = False + +    def addPoint(self, pt, segmentType=None, smooth=False, name=None, selected=False, **kwargs): +        if segmentType == "move": +            self._originalContourIsOpen = True +        if segmentType is None or selected: +            if selected: +                self._havePoint = True +            self._contour.addPoint(pt, segmentType, smooth, name) +        else: +            self.elideOrphanOffCurves(False) +            if self._havePoint: +                # We started drawing a path and we have a gap in it. Create +                # a new contour (and leave this one opened). +                self.endPath(True) +                self.beginPath() + +    def addComponent(self, baseGlyphName, transformation): +        pass # XXX + +    def elideOrphanOffCurves(self, arrivedAtBoundary): +        # onCurves that aren't selected and preceding offCurves if any are +        # elided +        for _ in range(2): +            if len(self._contour): +                if len(self._contour) > 1 and arrivedAtBoundary and \ +                      self._contour[0].segmentType == "curve": +                    # We're at the end of drawing and the offCurves lead to begin +                    # onCurve. Let them in. +                    pass +                elif self._contour[-1].segmentType is None: +                    self._contour.removePoint(self._contour[-1]) + +    def getGlyph(self): +        return self._glyph @@ -42,6 +42,7 @@ setup(name="defconQt",      packages=[          "defconQt",          "defconQt.objects", +        "defconQt.pens",          "defconQt.representationFactories",      ],      package_dir={"":"Lib"} | 
