Exemplo n.º 1
0
 def updateFamily(self, currentItem, previousItem):
     """Update the family edit box and adjust the style and size options.
     
     Arguments:
         currentItem -- the new list widget family item
         previousItem -- the previous list widget item
     """
     family = currentItem.text()
     self.familyEdit.setText(family)
     if self.familyEdit.hasFocus():
         self.familyEdit.selectAll()
     prevStyle = self.styleEdit.text()
     prevSize = self.sizeEdit.text()
     fontDb = QFontDatabase()
     styles = [style for style in fontDb.styles(family)]
     self.styleList.clear()
     self.styleList.addItems(styles)
     if prevStyle:
         try:
             num = styles.index(prevStyle)
         except ValueError:
             num = 0
         self.styleList.setCurrentRow(num)
         self.styleList.scrollToItem(self.styleList.currentItem())
     sizes = [repr(size) for size in fontDb.pointSizes(family)]
     self.sizeList.clear()
     self.sizeList.addItems(sizes)
     if prevSize:
         try:
             num = sizes.index(prevSize)
         except ValueError:
             num = 0
         self.sizeList.setCurrentRow(num)
         self.sizeList.scrollToItem(self.sizeList.currentItem())
         self.updateSample()
Exemplo n.º 2
0
    def findStyles(self, font):
        fontDatabase = QFontDatabase()
        currentItem = self.styleCombo.currentText()
        self.styleCombo.clear()

        for style in fontDatabase.styles(font.family()):
            self.styleCombo.addItem(style)

        styleIndex = self.styleCombo.findText(currentItem)
        if styleIndex == -1:
            self.styleCombo.setCurrentIndex(0)
        else:
            self.styleCombo.setCurrentIndex(styleIndex)
Exemplo n.º 3
0
    def findStyles(self, font):
        fontDatabase = QFontDatabase()
        currentItem = self.styleCombo.currentText()
        self.styleCombo.clear()

        for style in fontDatabase.styles(font.family()):
            self.styleCombo.addItem(style)

        styleIndex = self.styleCombo.findText(currentItem)
        if styleIndex == -1:
            self.styleCombo.setCurrentIndex(0)
        else:
            self.styleCombo.setCurrentIndex(styleIndex)
Exemplo n.º 4
0
    def __init__(self):
        super(MainWindow, self).__init__()

        font_id = QFontDatabase.addApplicationFont("fontawesome-webfont.ttf")

        if font_id is not -1:
            font_db = QFontDatabase()
            self.font_styles = font_db.styles('FontAwesome')
            self.font_families = QFontDatabase.applicationFontFamilies(font_id)
            print(self.font_styles, self.font_families)
            for font_family in self.font_families:
                self.font = font_db.font(font_family, self.font_styles[0], 18)
        self.home()
Exemplo n.º 5
0
class TextFonts(QObject):
    """Provide information about available text fonts. These are exactly the
    fonts that can be seen by LilyPond.
    This is only produced upon request but then stored permanently during the
    program's runtime.
    load_fonts() will run LilyPond to determine the list of fonts, optionally
    reporting to a log.Log widget if given.
    Since this is an asynchronous process GUI elements that want to use the
    results have to connect to the 'loaded' signal which is emitted after
    LilyPond has completed and the results been parsed.

    A Fonts() object is immediately available as fonts.available_fonts, and
    its is_loaded member can be requested to test if fonts have already been
    loaded.
    """

    loaded = signals.Signal()

    def __init__(self, lilypond_info):
        super(TextFonts, self).__init__()
        self.lilypond_info = lilypond_info
        self._tree_model = FontTreeModel(self)
        self._misc_model = MiscTreeModel(self)
        self.job = None
        self.load_fonts()

    def reset(self, log_widget=None):
        self._log = []
        self._tree_model.reset()
        self._misc_model.reset()
        # needs to be reset for the LilyPond-dependent fonts
        self.font_db = QFontDatabase()

        self._is_loaded = False

    def log(self):
        return self._log

    def acknowledge_lily_fonts(self):
        """Add the OpenType fonts in LilyPond's font directory
        to Qt's font database. This should be relevant (untested)
        when the fonts are not additionally installed as system fonts."""
        # TODO: Move the the filtering here.
        # It's not correct to first add the notation fonts to the font debug
        # only to filter them again later. Besides, there might be other valid
        # fonts caught by the filter.
        font_dir = os.path.join(self.lilypond_info.datadir(), 'fonts', 'otf')
        for lily_font in os.listdir(font_dir):
            self.font_db.addApplicationFont(os.path.join(font_dir, lily_font))

    def add_style_to_family(self, families, family_name, input):
        """Parse a font face definition provided by LilyPond.
        There is some guesswork involved since there may be
        discrepancies between the fonts/styles reported by
        LilyPond and those available in QFontDatabase.
        To discuss this the function is heavily commented.
        See also
        http://lists.gnu.org/archive/html/lilypond-user/2018-07/msg00338.html
        """
        def un_camel(style):
            """
            Try to 'fix' a class of errors when LilyPond reports
            e.g. 'BoldItalic' instead of 'Bold Italic'.
            It is unclear if this actually fixes the source of the
            issue or just one arbitrary example.
            """
            # The following regular expression would be a 'proper'
            # un-camel-ing, but this seems not to be relevant.
            #un_cameled = re.sub('([^ ])([A-Z][a-z])', r'\1 \2', style)
            #return re.sub('([A-Za-z])([0-9])', r'\1 \2', un_cameled)
            return ("Bold Italic" if style == "BoldItalic" else style)

        if not family_name in families.keys():
            families[family_name] = {}
        family = families[family_name]
        input = input.strip().split(':')
        # This is a safeguard against improper entries
        if len(input) == 2:
            # The first segment has always one or two entries:
            # - The font family name
            # - The font subfamily name if it differs from the base name.
            # Therefore the series is always the *last* entry in the list.
            # We "unescape" hyphens because this escape is not necessary
            # for our purposes.
            sub_family = input[0].split(',')[-1].replace('\\-', '-')
            if not sub_family in family.keys():
                family[sub_family] = []
            qt_styles = self.font_db.styles(sub_family)
            lily_styles = input[1][6:].split(',')
            match = ''
            if not qt_styles:
                # In some cases Qt does *not* report available styles.
                # In these cases it seems correct to use the style reported
                # by LilyPond. In very rare cases it seems possible that
                # LilyPond reports multiple styles for such fonts, and for now
                # we have to simply ignore these cases so we take the first
                # or single style.
                match = un_camel(lily_styles[0])
            else:
                # Match LilyPond's reported styles with those reported by Qt.
                # We need to un-camel the LilyPond-reported style name, but
                # this may not be a final fix (see comment to un_camel()).
                # If no match is found we simply hope that the style
                # reported by LilyPond will do.
                for style in lily_styles:
                    style = un_camel(style)
                    if style in qt_styles:
                        match = style
                        break
                if not match:
                    match = un_camel(lily_styles[0])
            if not match in family[sub_family]:
                family[sub_family].append(match)
        else:
            pass
            # TODO: issue a warning?
            # In my examples *some* fonts were parsed improperly
            # and therefore skipped.
            # I *think* this happens at the stage of splitting the
            # LilyPond log into individual lines.
            #print("Error when parsing font entry:")
            #print(name)
            #print(input)

    def flatten_log(self):
        """Flatten job history into flat string list."""
        for line in self.job.history():
            # lines in Job.history() are tuples of text and type,
            # we're only interested in the text.
            lines = line[0].split('\n')
            for l in lines:
                self._log.append(l)

    def is_loaded(self):
        return self._is_loaded

    def load_fonts(self, log_widget=None):
        """Run LilyPond to retrieve a list of available fonts.
        Afterwards process_results() will parse the output and build
        info structures to be used later.
        If a log.Log widget is passed as second argument this will
        be connected to the Job to provide realtime feedback on the process.
        Any caller should connect to the 'loaded' signal because this
        is an asynchronous task that takes long to complete."""
        self.reset()
        self.acknowledge_lily_fonts()
        self.run_lilypond(log_widget)

    def misc_model(self):
        return self._misc_model

    def model(self):
        return self._tree_model

    def parse_entries(self):
        """Parse the LilyPond log and push entries to the various
        lists and dictionaries. Parsing the actual font style
        definition is deferred to add_style_to_family()."""
        regexp = re.compile('(.*)\\-\d+')
        families = {}
        config_files = []
        config_dirs = []
        font_dirs = []
        last_family = None
        for e in self._log:
            if e.startswith('family'):
                # NOTE: output of this process is always English,
                # so we can hardcode the splice indices
                original_family = e[7:]
                # filter size-indexed font families
                basename = regexp.match(original_family)
                last_family = basename.groups(
                )[0] if basename else original_family
            elif last_family:
                # We're in the second line of a style definition
                if not last_family.endswith('-brace'):
                    self.add_style_to_family(families, last_family, e)
                last_family = None
            elif e.startswith('Config files:'):
                config_files.append(e[14:])
            elif e.startswith('Font dir:'):
                font_dirs.append(e[10:])
            elif e.startswith('Config dir:'):
                config_dirs.append(e[12:])

        return families, config_files, config_dirs, font_dirs

    def process_results(self):
        """Parse the job history list to dictionaries."""

        self.flatten_log()
        families, config_files, config_dirs, font_dirs = self.parse_entries()

        self._tree_model.populate(families)
        self._misc_model.populate(config_files, config_dirs, font_dirs)

        self._is_loaded = True
        self.job = None
        self.loaded.emit()

    def run_lilypond(self, log_widget=None):
        """Run lilypond from info with the args list, and a job title."""
        # TODO: Use the global JobQueue
        info = self.lilypond_info
        j = self.job = job.Job([info.abscommand() or info.command] +
                               ['-dshow-available-fonts'])
        j.set_title(_("Available Fonts"))
        j.done.connect(self.process_results)
        if log_widget:
            log_widget.connectJob(j)
        j.start()
Exemplo n.º 6
0
class GFonts(QtWidgets.QMainWindow):
    def __init__(self):
        super(GFonts, self).__init__()
        uic.loadUi('gfonts.ui', self)
        self.fTool = gFontsTool()
        self.threadPool = QtCore.QThreadPool()

        self.DDFamily.currentIndexChanged.connect(self.familySelected)
        self.DDWeight.currentIndexChanged.connect(self.weightSelected)

        self.show()

        worker = bgProc(self.fTool.getMetadata)
        worker.signals.finished.connect(self.mdLoaded)
        self.threadPool.start(worker)

        self.statusBar().showMessage('Loading metadata...')

    def mdLoaded(self):
        # metadata loaded handler
        self.DDFamily.addItems(self.fTool.getFamilies())
        self.statusBar().showMessage('Loading metadata... Done')

    def familySelected(self, index):
        selName = self.DDFamily.currentText()
        self.DDWeight.clear()
        self.DDWeight.addItems(self.fTool.getWeights(selName))

    def weightSelected(self, index):
        selName = self.DDFamily.currentText()
        selWeight = self.DDWeight.currentText()
        worker = bgProc(self.fTool.getCSS, selName, selWeight)
        worker.signals.result.connect(self.cssLoaded)
        self.threadPool.start(worker)

        self.statusBar().showMessage('Retrieving CSS')

    def cssLoaded(self, css):
        print('CSS', css)
        pcss = self.fTool.getParsedCSS(css)
        au = self.fTool.getFontAllURIs(pcss)
        fancyName = self.fTool.getFontFullNames(pcss.rules)
        sName = self.fTool.selectSimplestName(fancyName)

        worker = bgProc(self.fTool.getFontBitStreams, au)
        worker.signals.result.connect(self.bsLoaded)
        self.threadPool.start(worker)

        self.statusBar().showMessage('Retrieving font')

    def bsLoaded(self, bs):
        self.statusBar().showMessage('Font loaded')
        key = list(bs.keys())[0]
        self.ttf = QtCore.QByteArray()
        bio = QtCore.QBuffer(self.ttf)
        bio.open(QtCore.QIODevice.WriteOnly)
        self.fTool.mergeBitStreams(bs[key], bio)
        self.statusBar().showMessage('Font merged')
        self.fdb = QFontDatabase()
        insertion = self.fdb.addApplicationFontFromData(self.ttf)
        print('Insertion:', insertion)
        if insertion == -1:
            print('Failed to insert font into database')
        fName = key[0]
        fWeight = key[1]
        print('Converted bitstream to ttf for:', fName, fWeight)
        print('Bitstream keys:', bs.keys())
        if type(fWeight) == str and fWeight[-1] == 'i':
            fWeight = fWeight[:-1]
            italic = True
        else:
            italic = False

        styles = self.fdb.styles(fName)
        print('styles', styles)
        print(fWeight, italic)
        for s in styles:
            print('s:', s)
            print('s weight:', self.fdb.weight(fName, s))
            print('s italic:', self.fdb.italic(fName, s))
            if self.fdb.weight(fName, s) == self.fTool.CSS2QtFontWeight(
                    fWeight) and self.fdb.italic(fName, s) == italic:
                thisStyle = s
                break
            else:
                thisStyle = None
        print(thisStyle)
        font = self.fdb.font(fName, thisStyle, 40)
        self.SampleText.setFont(font)