diff options
author | Lars Knoll <lars.knoll@nokia.com> | 2009-03-23 09:18:55 (GMT) |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2009-03-23 09:18:55 (GMT) |
commit | e5fcad302d86d316390c6b0f62759a067313e8a9 (patch) | |
tree | c2afbf6f1066b6ce261f14341cf6d310e5595bc1 /tools/makeqpf | |
download | Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.zip Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.gz Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.bz2 |
Long live Qt 4.5!
Diffstat (limited to 'tools/makeqpf')
-rw-r--r-- | tools/makeqpf/Blocks.txt | 185 | ||||
-rw-r--r-- | tools/makeqpf/README | 1 | ||||
-rw-r--r-- | tools/makeqpf/main.cpp | 183 | ||||
-rw-r--r-- | tools/makeqpf/mainwindow.cpp | 322 | ||||
-rw-r--r-- | tools/makeqpf/mainwindow.h | 80 | ||||
-rw-r--r-- | tools/makeqpf/mainwindow.ui | 502 | ||||
-rw-r--r-- | tools/makeqpf/makeqpf.pro | 20 | ||||
-rw-r--r-- | tools/makeqpf/makeqpf.qrc | 5 | ||||
-rw-r--r-- | tools/makeqpf/qpf2.cpp | 767 | ||||
-rw-r--r-- | tools/makeqpf/qpf2.h | 119 |
10 files changed, 2184 insertions, 0 deletions
diff --git a/tools/makeqpf/Blocks.txt b/tools/makeqpf/Blocks.txt new file mode 100644 index 0000000..9cc8754 --- /dev/null +++ b/tools/makeqpf/Blocks.txt @@ -0,0 +1,185 @@ +# Blocks-5.0.0.txt +# Date: 2006-02-15, 15:40:00 [KW] +# +# Unicode Character Database +# Copyright (c) 1991-2006 Unicode, Inc. +# For terms of use, see http://www.unicode.org/terms_of_use.html +# For documentation, see UCD.html +# +# Note: The casing of block names is not normative. +# For example, "Basic Latin" and "BASIC LATIN" are equivalent. +# +# Format: +# Start Code..End Code; Block Name + +# ================================================ + +# Note: When comparing block names, casing, whitespace, hyphens, +# and underbars are ignored. +# For example, "Latin Extended-A" and "latin extended a" are equivalent. +# For more information on the comparison of property values, +# see UCD.html. +# +# All code points not explicitly listed for Block +# have the value No_Block. + +# Property: Block +# +# @missing: 0000..10FFFF; No_Block + +0000..007F; Basic Latin +0080..00FF; Latin-1 Supplement +0100..017F; Latin Extended-A +0180..024F; Latin Extended-B +0250..02AF; IPA Extensions +02B0..02FF; Spacing Modifier Letters +0300..036F; Combining Diacritical Marks +0370..03FF; Greek and Coptic +0400..04FF; Cyrillic +0500..052F; Cyrillic Supplement +0530..058F; Armenian +0590..05FF; Hebrew +0600..06FF; Arabic +0700..074F; Syriac +0750..077F; Arabic Supplement +0780..07BF; Thaana +07C0..07FF; NKo +0900..097F; Devanagari +0980..09FF; Bengali +0A00..0A7F; Gurmukhi +0A80..0AFF; Gujarati +0B00..0B7F; Oriya +0B80..0BFF; Tamil +0C00..0C7F; Telugu +0C80..0CFF; Kannada +0D00..0D7F; Malayalam +0D80..0DFF; Sinhala +0E00..0E7F; Thai +0E80..0EFF; Lao +0F00..0FFF; Tibetan +1000..109F; Myanmar +10A0..10FF; Georgian +1100..11FF; Hangul Jamo +1200..137F; Ethiopic +1380..139F; Ethiopic Supplement +13A0..13FF; Cherokee +1400..167F; Unified Canadian Aboriginal Syllabics +1680..169F; Ogham +16A0..16FF; Runic +1700..171F; Tagalog +1720..173F; Hanunoo +1740..175F; Buhid +1760..177F; Tagbanwa +1780..17FF; Khmer +1800..18AF; Mongolian +1900..194F; Limbu +1950..197F; Tai Le +1980..19DF; New Tai Lue +19E0..19FF; Khmer Symbols +1A00..1A1F; Buginese +1B00..1B7F; Balinese +1D00..1D7F; Phonetic Extensions +1D80..1DBF; Phonetic Extensions Supplement +1DC0..1DFF; Combining Diacritical Marks Supplement +1E00..1EFF; Latin Extended Additional +1F00..1FFF; Greek Extended +2000..206F; General Punctuation +2070..209F; Superscripts and Subscripts +20A0..20CF; Currency Symbols +20D0..20FF; Combining Diacritical Marks for Symbols +2100..214F; Letterlike Symbols +2150..218F; Number Forms +2190..21FF; Arrows +2200..22FF; Mathematical Operators +2300..23FF; Miscellaneous Technical +2400..243F; Control Pictures +2440..245F; Optical Character Recognition +2460..24FF; Enclosed Alphanumerics +2500..257F; Box Drawing +2580..259F; Block Elements +25A0..25FF; Geometric Shapes +2600..26FF; Miscellaneous Symbols +2700..27BF; Dingbats +27C0..27EF; Miscellaneous Mathematical Symbols-A +27F0..27FF; Supplemental Arrows-A +2800..28FF; Braille Patterns +2900..297F; Supplemental Arrows-B +2980..29FF; Miscellaneous Mathematical Symbols-B +2A00..2AFF; Supplemental Mathematical Operators +2B00..2BFF; Miscellaneous Symbols and Arrows +2C00..2C5F; Glagolitic +2C60..2C7F; Latin Extended-C +2C80..2CFF; Coptic +2D00..2D2F; Georgian Supplement +2D30..2D7F; Tifinagh +2D80..2DDF; Ethiopic Extended +2E00..2E7F; Supplemental Punctuation +2E80..2EFF; CJK Radicals Supplement +2F00..2FDF; Kangxi Radicals +2FF0..2FFF; Ideographic Description Characters +3000..303F; CJK Symbols and Punctuation +3040..309F; Hiragana +30A0..30FF; Katakana +3100..312F; Bopomofo +3130..318F; Hangul Compatibility Jamo +3190..319F; Kanbun +31A0..31BF; Bopomofo Extended +31C0..31EF; CJK Strokes +31F0..31FF; Katakana Phonetic Extensions +3200..32FF; Enclosed CJK Letters and Months +3300..33FF; CJK Compatibility +3400..4DBF; CJK Unified Ideographs Extension A +4DC0..4DFF; Yijing Hexagram Symbols +4E00..9FFF; CJK Unified Ideographs +A000..A48F; Yi Syllables +A490..A4CF; Yi Radicals +A700..A71F; Modifier Tone Letters +A720..A7FF; Latin Extended-D +A800..A82F; Syloti Nagri +A840..A87F; Phags-pa +AC00..D7AF; Hangul Syllables +D800..DB7F; High Surrogates +DB80..DBFF; High Private Use Surrogates +DC00..DFFF; Low Surrogates +E000..F8FF; Private Use Area +F900..FAFF; CJK Compatibility Ideographs +FB00..FB4F; Alphabetic Presentation Forms +FB50..FDFF; Arabic Presentation Forms-A +FE00..FE0F; Variation Selectors +FE10..FE1F; Vertical Forms +FE20..FE2F; Combining Half Marks +FE30..FE4F; CJK Compatibility Forms +FE50..FE6F; Small Form Variants +FE70..FEFF; Arabic Presentation Forms-B +FF00..FFEF; Halfwidth and Fullwidth Forms +FFF0..FFFF; Specials +10000..1007F; Linear B Syllabary +10080..100FF; Linear B Ideograms +10100..1013F; Aegean Numbers +10140..1018F; Ancient Greek Numbers +10300..1032F; Old Italic +10330..1034F; Gothic +10380..1039F; Ugaritic +103A0..103DF; Old Persian +10400..1044F; Deseret +10450..1047F; Shavian +10480..104AF; Osmanya +10800..1083F; Cypriot Syllabary +10900..1091F; Phoenician +10A00..10A5F; Kharoshthi +12000..123FF; Cuneiform +12400..1247F; Cuneiform Numbers and Punctuation +1D000..1D0FF; Byzantine Musical Symbols +1D100..1D1FF; Musical Symbols +1D200..1D24F; Ancient Greek Musical Notation +1D300..1D35F; Tai Xuan Jing Symbols +1D360..1D37F; Counting Rod Numerals +1D400..1D7FF; Mathematical Alphanumeric Symbols +20000..2A6DF; CJK Unified Ideographs Extension B +2F800..2FA1F; CJK Compatibility Ideographs Supplement +E0000..E007F; Tags +E0100..E01EF; Variation Selectors Supplement +F0000..FFFFF; Supplementary Private Use Area-A +100000..10FFFF; Supplementary Private Use Area-B + +# EOF
\ No newline at end of file diff --git a/tools/makeqpf/README b/tools/makeqpf/README new file mode 100644 index 0000000..6656355 --- /dev/null +++ b/tools/makeqpf/README @@ -0,0 +1 @@ +This tool is used to create pre-rendered fonts for use with Qtopia Core. diff --git a/tools/makeqpf/main.cpp b/tools/makeqpf/main.cpp new file mode 100644 index 0000000..919841c --- /dev/null +++ b/tools/makeqpf/main.cpp @@ -0,0 +1,183 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui> + +#include "qpf2.h" +#include "mainwindow.h" + +#include <private/qfontengine_p.h> + +QT_BEGIN_NAMESPACE + +static void help() +{ + printf("usage:\n"); + printf("makeqpf fontname pixelsize [italic] [bold] [--exclude-cmap] [-v]\n"); + printf("makeqpf -dump [-v] file.qpf2\n"); + exit(0); +} + +static int gui(const QString &customFont = QString()) +{ + MainWindow mw(customFont); + mw.show(); + return qApp->exec(); +} + +QT_END_NAMESPACE + +int main(int argc, char **argv) +{ + QT_USE_NAMESPACE + + QApplication app(argc, argv); + app.setOrganizationName(QLatin1String("Trolltech")); + app.setApplicationName(QLatin1String("MakeQPF")); + + const QStringList arguments = app.arguments(); + + if (arguments.count() <= 1) { + return gui(); + } else if (arguments.count() == 2 + && QFile::exists(arguments.at(1))) { + return gui(arguments.at(1)); + } + + const QString &firstArg = arguments.at(1); + if (firstArg == QLatin1String("-h") || firstArg == QLatin1String("--help")) + help(); + if (firstArg == QLatin1String("-dump")) { + QString file; + for (int i = 2; i < arguments.count(); ++i) { + if (arguments.at(i).startsWith(QLatin1String("-v"))) + QPF::debugVerbosity += arguments.at(i).length() - 1; + else if (file.isEmpty()) + file = arguments.at(i); + else + help(); + } + + if (file.isEmpty()) + help(); + + QFile f(file); + if (!f.open(QIODevice::ReadOnly)) { + printf("cannot open %s\n", qPrintable(file)); + exit(1); + } + + QByteArray qpf = f.readAll(); + f.close(); + + QPF::dump(qpf); + return 0; + } + + if (arguments.count() < 3) help(); + + QFont font; + + QString fontName = firstArg; + if (QFile::exists(fontName)) { + int id = QFontDatabase::addApplicationFont(fontName); + if (id == -1) { + printf("cannot open font %s", qPrintable(fontName)); + help(); + } + QStringList families = QFontDatabase::applicationFontFamilies(id); + if (families.isEmpty()) { + printf("cannot find any font families in %s", qPrintable(fontName)); + help(); + } + fontName = families.first(); + } + font.setFamily(fontName); + + bool ok = false; + int pixelSize = arguments.at(2).toInt(&ok); + if (!ok) help(); + font.setPixelSize(pixelSize); + + int generationOptions = QPF::IncludeCMap | QPF::RenderGlyphs; + + for (int i = 3; i < arguments.count(); ++i) { + const QString &arg = arguments.at(i); + if (arg == QLatin1String("italic")) { + font.setItalic(true); + } else if (arg == QLatin1String("bold")) { + font.setBold(true); + } else if (arg == QLatin1String("--exclude-cmap")) { + generationOptions &= ~QPF::IncludeCMap; + } else if (arg == QLatin1String("--exclude-glyphs")) { + generationOptions &= ~QPF::RenderGlyphs; + } else if (arg == QLatin1String("-v")) { + ++QPF::debugVerbosity; + } else { + printf("unknown option %s\n", qPrintable(arg)); + help(); + } + } + + font.setStyleStrategy(QFont::NoFontMerging); + + QList<QPF::CharacterRange> ranges; + ranges.append(QPF::CharacterRange()); // default range from 0 to 0xffff + + QString origFont; + QByteArray qpf = QPF::generate(font, generationOptions, ranges, &origFont); + + QString fileName = QPF::fileNameForFont(font); + QFile f(fileName); + f.open(QIODevice::WriteOnly | QIODevice::Truncate); + f.write(qpf); + f.close(); + + if (generationOptions & QPF::IncludeCMap) { + printf("Created %s from %s\n", qPrintable(fileName), qPrintable(origFont)); + } else { + printf("Created %s from %s excluding the character-map\n", qPrintable(fileName), qPrintable(origFont)); + printf("The TrueType font file is therefore required for the font to work\n"); + } + + return 0; +} + diff --git a/tools/makeqpf/mainwindow.cpp b/tools/makeqpf/mainwindow.cpp new file mode 100644 index 0000000..8a4da8c --- /dev/null +++ b/tools/makeqpf/mainwindow.cpp @@ -0,0 +1,322 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mainwindow.h" + +#include <QFontComboBox> +#include <QFontDatabase> +#include <QFileDialog> +#include <QMessageBox> +#include <QListWidget> +#include <QDebug> +#include <QShortcut> +#include <QCompleter> +#include <QDirModel> +#include <QTextCodec> + +QT_BEGIN_NAMESPACE + +MainWindow::MainWindow(const QString &customFont) +{ + setupUi(this); + pixelSize->setValue(QFontInfo(QFont()).pixelSize()); + populateCharacterRanges(); + + { + weightCombo->addItem(QLatin1String("Light"), QVariant(int(QFont::Light))); + const int normalIdx = weightCombo->count(); + weightCombo->addItem(QLatin1String("Normal"), QVariant(int(QFont::Normal))); + weightCombo->addItem(QLatin1String("DemiBold"), QVariant(int(QFont::DemiBold))); + weightCombo->addItem(QLatin1String("Bold"), QVariant(int(QFont::Bold))); + weightCombo->addItem(QLatin1String("Black"), QVariant(int(QFont::Black))); + + weightCombo->setCurrentIndex(normalIdx); + } + + QShortcut *sc = new QShortcut(Qt::ControlModifier + Qt::Key_A, this); + connect(sc, SIGNAL(activated()), + this, SLOT(on_selectAll_clicked())); + sc = new QShortcut(Qt::ControlModifier + Qt::Key_D, this); + connect(sc, SIGNAL(activated()), + this, SLOT(on_deselectAll_clicked())); + sc = new QShortcut(Qt::ControlModifier + Qt::Key_I, this); + connect(sc, SIGNAL(activated()), + this, SLOT(on_invertSelection_clicked())); + + QCompleter *completer = new QCompleter(this); + completer->setModel(new QDirModel(this)); + path->setCompleter(completer); + path->setText(QDir::currentPath()); + + completer = new QCompleter(this); + completer->setModel(new QDirModel(this)); + sampleFile->setCompleter(completer); + charCount->setText(QString()); + + if (!customFont.isEmpty()) + addCustomFont(customFont); + + fontChanged(); + + connect(fontComboBox, SIGNAL(currentFontChanged(QFont)), + this, SLOT(fontChanged())); + connect(pixelSize, SIGNAL(valueChanged(int)), + this, SLOT(fontChanged())); + connect(italic, SIGNAL(stateChanged(int)), + this, SLOT(fontChanged())); + connect(weightCombo, SIGNAL(currentIndexChanged(int)), + this, SLOT(fontChanged())); +} + +void MainWindow::on_actionAdd_Custom_Font_triggered() +{ + QString fontFile = QFileDialog::getOpenFileName(this, tr("Add Custom Font")); + if (fontFile.isEmpty()) + return; + addCustomFont(fontFile); +} + +void MainWindow::on_selectAll_clicked() +{ + for (int i = 0; i < characterRangeView->count(); ++i) + characterRangeView->item(i)->setCheckState(Qt::Checked); +} + +void MainWindow::on_deselectAll_clicked() +{ + for (int i = 0; i < characterRangeView->count(); ++i) + characterRangeView->item(i)->setCheckState(Qt::Unchecked); +} + +void MainWindow::on_invertSelection_clicked() +{ + for (int i = 0; i < characterRangeView->count(); ++i) { + QListWidgetItem *item = characterRangeView->item(i); + if (item->checkState() == Qt::Checked) + item->setCheckState(Qt::Unchecked); + else + item->setCheckState(Qt::Checked); + } +} + +void MainWindow::fontChanged() +{ + QFont f = preview->font(); + f.setStyleStrategy(QFont::NoFontMerging); + f.setPixelSize(pixelSize->value()); + f.setFamily(fontComboBox->currentFont().family()); + f.setItalic(italic->isChecked()); + f.setWeight(weightCombo->itemData(weightCombo->currentIndex()).toInt()); + + if (!preview->isModified()) { + QFontDatabase db; + QFontDatabase::WritingSystem ws = db.writingSystems(f.family()).value(0, QFontDatabase::Any); + QString sample = db.writingSystemSample(ws); + preview->setText(sample); + preview->setModified(false); + } + + fileName->setText(QPF::fileNameForFont(f)); + + preview->setFont(f); +} + +void MainWindow::on_browsePath_clicked() +{ + QString dir = QFileDialog::getExistingDirectory(this, tr("Select Directory")); + if (!dir.isEmpty()) + path->setText(dir); +} + +void MainWindow::on_browseSampleFile_clicked() +{ + QString dir = QFileDialog::getOpenFileName(this, tr("Select Sample File")); + if (!dir.isEmpty()) { + sampleFile->setText(dir); + on_sampleFile_editingFinished(); + } +} + +void MainWindow::on_generate_clicked() +{ + QFile f(path->text() + QDir::separator() + fileName->text()); + if (f.exists()) { + if (QMessageBox::warning(this, QString(), + tr("%1 already exists.\nDo you want to replace it?").arg(f.fileName()), + QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + != QMessageBox::Yes) { + statusBar()->showMessage(tr("Pre-rendering aborted...")); + return; + } + } + + QList<QPF::CharacterRange> ranges; + + if (chooseFromSampleFile->isChecked()) { + ranges = sampleFileRanges; + } else if (chooseFromCodePoints->isChecked()) { + ranges.clear(); + for (int i = 0; i < characterRangeView->count(); ++i) { + QListWidgetItem *item = characterRangeView->item(i); + if (item->checkState() != Qt::Checked) + continue; + + QPF::CharacterRange range = qVariantValue<QPF::CharacterRange>(item->data(Qt::UserRole)); + ranges.append(range); + } + } + + const int generationOptions = QPF::IncludeCMap | QPF::RenderGlyphs; + QByteArray qpf = QPF::generate(preview->font(), generationOptions, ranges); + f.open(QIODevice::WriteOnly | QIODevice::Truncate); + f.write(qpf); + f.close(); + + statusBar()->showMessage(tr("Font successfully pre-rendered to %1").arg(fileName->text())); +} + +void MainWindow::on_sampleFile_editingFinished() +{ + sampleFileRanges.clear(); + QFile f(sampleFile->text()); + if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) { + sampleFileRanges.append(QPF::CharacterRange()); // default = all + return; + } + QTextStream stream(&f); + stream.setCodec(QTextCodec::codecForName("utf-8")); + stream.setAutoDetectUnicode(true); + QString text = stream.readAll(); + + QSet<QChar> coverage; + for (int i = 0; i < text.length(); ++i) + coverage.insert(text.at(i)); + + QList<QChar> sortedCoverage = QList<QChar>::fromSet(coverage); + qSort(sortedCoverage); + // play simple :) + foreach (QChar ch, sortedCoverage) { + QPF::CharacterRange r; + r.start = ch.unicode(); + r.end = r.start + 1; + sampleFileRanges.append(r); + } + + charCount->setText(tr("(%1 unique characters found)").arg(sortedCoverage.count())); +} + +void MainWindow::populateCharacterRanges() +{ + QFile f(":/Blocks.txt"); + if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) + return; + + QRegExp rangeExpr("([0-9a-f]+)\\.\\.([0-9a-f]+); (.+)"); + rangeExpr.setCaseSensitivity(Qt::CaseInsensitive); + + QString ellipsis(QChar(0x2026)); + if (!characterRangeView->fontMetrics().inFont(ellipsis.at(0))) + ellipsis = QLatin1String("..."); + + while (!f.atEnd()) { + QString line = QString::fromAscii(f.readLine()); + + if (line.endsWith(QLatin1Char('\n'))) + line.chop(1); + if (line.endsWith(QLatin1Char('\r'))) + line.chop(1); + + line = line.trimmed(); + + if (line.isEmpty() || line.startsWith(QLatin1Char('#'))) + continue; + + if (!rangeExpr.exactMatch(line) || rangeExpr.numCaptures() != 3) + continue; + + QPF::CharacterRange range; + + bool ok = false; + range.start = rangeExpr.cap(1).toUInt(&ok, /*base*/16); + if (!ok) + continue; + range.end = rangeExpr.cap(2).toUInt(&ok, /*base*/16); + if (!ok) + continue; + + if (range.start >= 0xffff || range.end >= 0xffff) + continue; + + QString description = rangeExpr.cap(3); + + QListWidgetItem *item = new QListWidgetItem(characterRangeView); + QString text = description; + text.append(QLatin1String(" (")); + text.append(rangeExpr.cap(1)); + text.append(ellipsis); + text.append(rangeExpr.cap(2)); + text.append(QLatin1String(")")); + item->setText(text); + item->setCheckState(Qt::Checked); + + item->setData(Qt::UserRole, qVariantFromValue(range)); + } +} + +void MainWindow::addCustomFont(const QString &fontFile) +{ + int id = QFontDatabase::addApplicationFont(fontFile); + if (id < 0) { + QMessageBox::warning(this, tr("Error Adding Custom Font"), + tr("The custom font %s could not be loaded.").arg(fontFile)); + return; + } + const QStringList families = QFontDatabase::applicationFontFamilies(id); + if (families.isEmpty()) { + QMessageBox::warning(this, tr("Error Adding Custom Font"), + tr("The custom font %s provides no font families.").arg(fontFile)); + return; + } + QFont f(families.first()); + fontComboBox->setCurrentFont(f); +} + +QT_END_NAMESPACE diff --git a/tools/makeqpf/mainwindow.h b/tools/makeqpf/mainwindow.h new file mode 100644 index 0000000..dad684b --- /dev/null +++ b/tools/makeqpf/mainwindow.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include <QMainWindow> + +#include "ui_mainwindow.h" +#include "qpf2.h" + +QT_BEGIN_NAMESPACE + +class QListWidgetItem; + +class MainWindow : public QMainWindow, Ui::MainWindow +{ + Q_OBJECT +public: + MainWindow(const QString &customFont); + +private Q_SLOTS: + void on_actionAdd_Custom_Font_triggered(); + void on_selectAll_clicked(); + void on_deselectAll_clicked(); + void on_invertSelection_clicked(); + void fontChanged(); + void on_browsePath_clicked(); + void on_browseSampleFile_clicked(); + void on_generate_clicked(); + void on_sampleFile_editingFinished(); + +private: + void populateCharacterRanges(); + void addCustomFont(const QString &fontFile); + +private: + QList<QPF::CharacterRange> sampleFileRanges; +}; + +QT_END_NAMESPACE + +#endif // MAINWINDOW_H diff --git a/tools/makeqpf/mainwindow.ui b/tools/makeqpf/mainwindow.ui new file mode 100644 index 0000000..f6eda4f --- /dev/null +++ b/tools/makeqpf/mainwindow.ui @@ -0,0 +1,502 @@ +<ui version="4.0" > + <class>MainWindow</class> + <widget class="QMainWindow" name="MainWindow" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>829</width> + <height>813</height> + </rect> + </property> + <property name="windowTitle" > + <string>MakeQPF</string> + </property> + <widget class="QWidget" name="centralwidget" > + <layout class="QVBoxLayout" > + <property name="margin" > + <number>9</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QGroupBox" name="groupBox" > + <property name="title" > + <string>Font Properties</string> + </property> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>9</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QLabel" name="label" > + <property name="text" > + <string>Family:</string> + </property> + </widget> + </item> + <item> + <widget class="QFontComboBox" name="fontComboBox" /> + </item> + <item> + <widget class="QLabel" name="label_2" > + <property name="text" > + <string>Pixel Size:</string> + </property> + </widget> + </item> + <item> + <widget class="QSpinBox" name="pixelSize" > + <property name="minimum" > + <number>1</number> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_7" > + <property name="text" > + <string>Weight:</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="weightCombo" /> + </item> + <item> + <widget class="QCheckBox" name="italic" > + <property name="text" > + <string>Italic</string> + </property> + </widget> + </item> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_2" > + <property name="title" > + <string>Glyph Coverage</string> + </property> + <layout class="QVBoxLayout" > + <property name="margin" > + <number>9</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QRadioButton" name="chooseFromCodePoints" > + <property name="text" > + <string>Choose from Unicode Codepoints:</string> + </property> + <property name="checked" > + <bool>true</bool> + </property> + </widget> + </item> + <item> + <layout class="QVBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QListWidget" name="characterRangeView" /> + </item> + <item> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QPushButton" name="selectAll" > + <property name="text" > + <string>Select &All</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="deselectAll" > + <property name="text" > + <string>&Deselect All</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="invertSelection" > + <property name="text" > + <string>&Invert Selection</string> + </property> + </widget> + </item> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </item> + <item> + <widget class="QRadioButton" name="chooseFromSampleFile" > + <property name="text" > + <string>Choose from Sample Text File (UTF-8 Encoded):</string> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QLabel" name="label_5" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="text" > + <string>Path:</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="sampleFile" > + <property name="enabled" > + <bool>false</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="browseSampleFile" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="text" > + <string>Browse...</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="charCount" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="text" > + <string>TextLabel</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_3" > + <property name="title" > + <string>Preview</string> + </property> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>9</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QLineEdit" name="preview" /> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_4" > + <property name="title" > + <string>Output Options</string> + </property> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>9</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QLabel" name="label_3" > + <property name="text" > + <string>Path:</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="path" /> + </item> + <item> + <widget class="QPushButton" name="browsePath" > + <property name="text" > + <string>Browse...</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_4" > + <property name="text" > + <string>Filename:</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="fileName" > + <property name="enabled" > + <bool>false</bool> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QPushButton" name="generate" > + <property name="text" > + <string>Generate Pre-Rendered Font...</string> + </property> + </widget> + </item> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </widget> + <widget class="QMenuBar" name="menubar" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>829</width> + <height>29</height> + </rect> + </property> + <widget class="QMenu" name="menuFile" > + <property name="title" > + <string>File</string> + </property> + <addaction name="actionAdd_Custom_Font" /> + <addaction name="separator" /> + <addaction name="action_Exit" /> + </widget> + <addaction name="menuFile" /> + </widget> + <widget class="QStatusBar" name="statusbar" /> + <action name="actionAdd_Custom_Font" > + <property name="text" > + <string>&Add Custom Font...</string> + </property> + </action> + <action name="action_Exit" > + <property name="text" > + <string>&Exit</string> + </property> + </action> + </widget> + <resources/> + <connections> + <connection> + <sender>action_Exit</sender> + <signal>triggered()</signal> + <receiver>MainWindow</receiver> + <slot>close()</slot> + <hints> + <hint type="sourcelabel" > + <x>-1</x> + <y>-1</y> + </hint> + <hint type="destinationlabel" > + <x>383</x> + <y>215</y> + </hint> + </hints> + </connection> + <connection> + <sender>chooseFromCodePoints</sender> + <signal>toggled(bool)</signal> + <receiver>characterRangeView</receiver> + <slot>setEnabled(bool)</slot> + <hints> + <hint type="sourcelabel" > + <x>183</x> + <y>144</y> + </hint> + <hint type="destinationlabel" > + <x>146</x> + <y>295</y> + </hint> + </hints> + </connection> + <connection> + <sender>chooseFromCodePoints</sender> + <signal>toggled(bool)</signal> + <receiver>selectAll</receiver> + <slot>setEnabled(bool)</slot> + <hints> + <hint type="sourcelabel" > + <x>236</x> + <y>146</y> + </hint> + <hint type="destinationlabel" > + <x>46</x> + <y>508</y> + </hint> + </hints> + </connection> + <connection> + <sender>chooseFromCodePoints</sender> + <signal>toggled(bool)</signal> + <receiver>deselectAll</receiver> + <slot>setEnabled(bool)</slot> + <hints> + <hint type="sourcelabel" > + <x>280</x> + <y>147</y> + </hint> + <hint type="destinationlabel" > + <x>158</x> + <y>502</y> + </hint> + </hints> + </connection> + <connection> + <sender>chooseFromCodePoints</sender> + <signal>toggled(bool)</signal> + <receiver>invertSelection</receiver> + <slot>setEnabled(bool)</slot> + <hints> + <hint type="sourcelabel" > + <x>364</x> + <y>143</y> + </hint> + <hint type="destinationlabel" > + <x>281</x> + <y>509</y> + </hint> + </hints> + </connection> + <connection> + <sender>chooseFromSampleFile</sender> + <signal>toggled(bool)</signal> + <receiver>sampleFile</receiver> + <slot>setEnabled(bool)</slot> + <hints> + <hint type="sourcelabel" > + <x>134</x> + <y>544</y> + </hint> + <hint type="destinationlabel" > + <x>64</x> + <y>569</y> + </hint> + </hints> + </connection> + <connection> + <sender>chooseFromSampleFile</sender> + <signal>toggled(bool)</signal> + <receiver>browseSampleFile</receiver> + <slot>setEnabled(bool)</slot> + <hints> + <hint type="sourcelabel" > + <x>79</x> + <y>545</y> + </hint> + <hint type="destinationlabel" > + <x>710</x> + <y>582</y> + </hint> + </hints> + </connection> + <connection> + <sender>chooseFromSampleFile</sender> + <signal>toggled(bool)</signal> + <receiver>charCount</receiver> + <slot>setEnabled(bool)</slot> + <hints> + <hint type="sourcelabel" > + <x>274</x> + <y>544</y> + </hint> + <hint type="destinationlabel" > + <x>790</x> + <y>569</y> + </hint> + </hints> + </connection> + <connection> + <sender>chooseFromSampleFile</sender> + <signal>toggled(bool)</signal> + <receiver>label_5</receiver> + <slot>setEnabled(bool)</slot> + <hints> + <hint type="sourcelabel" > + <x>112</x> + <y>541</y> + </hint> + <hint type="destinationlabel" > + <x>37</x> + <y>579</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/tools/makeqpf/makeqpf.pro b/tools/makeqpf/makeqpf.pro new file mode 100644 index 0000000..c2dc9cc --- /dev/null +++ b/tools/makeqpf/makeqpf.pro @@ -0,0 +1,20 @@ +###################################################################### +# Automatically generated by qmake (2.01a) Wed Nov 29 16:21:49 2006 +###################################################################### + +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . ../../src/3rdparty/harfbuzz/src +CONFIG += console +DESTDIR = ../../bin + +target.path=$$[QT_INSTALL_BINS] +INSTALLS += target + +# Input +HEADERS += qpf2.h mainwindow.h +SOURCES += main.cpp qpf2.cpp mainwindow.cpp +DEFINES += QT_NO_FREETYPE +FORMS += mainwindow.ui +RESOURCES += makeqpf.qrc diff --git a/tools/makeqpf/makeqpf.qrc b/tools/makeqpf/makeqpf.qrc new file mode 100644 index 0000000..b1d73b0 --- /dev/null +++ b/tools/makeqpf/makeqpf.qrc @@ -0,0 +1,5 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource prefix="/"> + <file>Blocks.txt</file> +</qresource> +</RCC> diff --git a/tools/makeqpf/qpf2.cpp b/tools/makeqpf/qpf2.cpp new file mode 100644 index 0000000..b97b9d4 --- /dev/null +++ b/tools/makeqpf/qpf2.cpp @@ -0,0 +1,767 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qpf2.h" + +#include <math.h> +#include <private/qfontengine_p.h> +#include <QFile> +#include <qendian.h> + +QT_BEGIN_NAMESPACE + +#include "../../src/gui/text/qpfutil.cpp" + +int QPF::debugVerbosity = 0; + +// ### copied from qfontdatabase.cpp + +// see the Unicode subset bitfields in the MSDN docs +static int requiredUnicodeBits[QFontDatabase::WritingSystemsCount][2] = { + // Any, + { 127, 127 }, + // Latin, + { 0, 127 }, + // Greek, + { 7, 127 }, + // Cyrillic, + { 9, 127 }, + // Armenian, + { 10, 127 }, + // Hebrew, + { 11, 127 }, + // Arabic, + { 13, 127 }, + // Syriac, + { 71, 127 }, + //Thaana, + { 72, 127 }, + //Devanagari, + { 15, 127 }, + //Bengali, + { 16, 127 }, + //Gurmukhi, + { 17, 127 }, + //Gujarati, + { 18, 127 }, + //Oriya, + { 19, 127 }, + //Tamil, + { 20, 127 }, + //Telugu, + { 21, 127 }, + //Kannada, + { 22, 127 }, + //Malayalam, + { 23, 127 }, + //Sinhala, + { 73, 127 }, + //Thai, + { 24, 127 }, + //Lao, + { 25, 127 }, + //Tibetan, + { 70, 127 }, + //Myanmar, + { 74, 127 }, + // Georgian, + { 26, 127 }, + // Khmer, + { 80, 127 }, + // SimplifiedChinese, + { 126, 127 }, + // TraditionalChinese, + { 126, 127 }, + // Japanese, + { 126, 127 }, + // Korean, + { 56, 127 }, + // Vietnamese, + { 0, 127 }, // same as latin1 + // Other, + { 126, 127 } +}; + +#define SimplifiedChineseCsbBit 18 +#define TraditionalChineseCsbBit 20 +#define JapaneseCsbBit 17 +#define KoreanCsbBit 21 + +static QList<QFontDatabase::WritingSystem> determineWritingSystemsFromTrueTypeBits(quint32 unicodeRange[4], quint32 codePageRange[2]) +{ + QList<QFontDatabase::WritingSystem> writingSystems; + bool hasScript = false; + + int i; + for(i = 0; i < QFontDatabase::WritingSystemsCount; i++) { + int bit = requiredUnicodeBits[i][0]; + int index = bit/32; + int flag = 1 << (bit&31); + if (bit != 126 && unicodeRange[index] & flag) { + bit = requiredUnicodeBits[i][1]; + index = bit/32; + + flag = 1 << (bit&31); + if (bit == 127 || unicodeRange[index] & flag) { + writingSystems.append(QFontDatabase::WritingSystem(i)); + hasScript = true; + // qDebug("font %s: index=%d, flag=%8x supports script %d", familyName.latin1(), index, flag, i); + } + } + } + if(codePageRange[0] & (1 << SimplifiedChineseCsbBit)) { + writingSystems.append(QFontDatabase::SimplifiedChinese); + hasScript = true; + //qDebug("font %s supports Simplified Chinese", familyName.latin1()); + } + if(codePageRange[0] & (1 << TraditionalChineseCsbBit)) { + writingSystems.append(QFontDatabase::TraditionalChinese); + hasScript = true; + //qDebug("font %s supports Traditional Chinese", familyName.latin1()); + } + if(codePageRange[0] & (1 << JapaneseCsbBit)) { + writingSystems.append(QFontDatabase::Japanese); + hasScript = true; + //qDebug("font %s supports Japanese", familyName.latin1()); + } + if(codePageRange[0] & (1 << KoreanCsbBit)) { + writingSystems.append(QFontDatabase::Korean); + hasScript = true; + //qDebug("font %s supports Korean", familyName.latin1()); + } + if (!hasScript) + writingSystems.append(QFontDatabase::Symbol); + + return writingSystems; +} + +static QByteArray getWritingSystems(QFontEngine *fontEngine) +{ + QByteArray os2Table = fontEngine->getSfntTable(MAKE_TAG('O', 'S', '/', '2')); + if (os2Table.isEmpty()) + return QByteArray(); + + const uchar *data = reinterpret_cast<const uchar *>(os2Table.constData()); + + quint32 unicodeRange[4] = { + qFromBigEndian<quint32>(data + 42), + qFromBigEndian<quint32>(data + 46), + qFromBigEndian<quint32>(data + 50), + qFromBigEndian<quint32>(data + 54) + }; + quint32 codePageRange[2] = { qFromBigEndian<quint32>(data + 78), qFromBigEndian<quint32>(data + 82) }; + QList<QFontDatabase::WritingSystem> systems = determineWritingSystemsFromTrueTypeBits(unicodeRange, codePageRange); + + QByteArray bitField((QFontDatabase::WritingSystemsCount + 7) / 8, 0); + + for (int i = 0; i < systems.count(); ++i) { + int bitPos = systems.at(i); + bitField[bitPos / 8] = bitField.at(bitPos / 8) | (1 << (bitPos % 8)); + } + + return bitField; +} + +static QString stringify(const QByteArray &bits) +{ + QString result; + for (int i = 0; i < bits.count(); ++i) { + uchar currentByte = bits.at(i); + for (int j = 0; j < 8; ++j) { + if (currentByte & 1) + result += '1'; + else + result += '0'; + currentByte >>= 1; + } + } + return result; +} + +static void dumpWritingSystems(const QByteArray &bits) +{ + QStringList writingSystems; + + QString bitString = stringify(bits); + for (int i = 0; i < qMin(int(QFontDatabase::WritingSystemsCount), bitString.length()); ++i) { + if (bitString.at(i) == QLatin1Char('1')) + writingSystems << QFontDatabase::writingSystemName(QFontDatabase::WritingSystem(i)); + } + + qDebug() << "Supported writing systems:" << writingSystems; +} + +static const char *headerTagNames[QFontEngineQPF::NumTags] = { + "FontName", + "FileName", + "FileIndex", + "FontRevision", + "FreeText", + "Ascent", + "Descent", + "Leading", + "XHeight", + "AverageCharWidth", + "MaxCharWidth", + "LineThickness", + "MinLeftBearing", + "MinRightBearing", + "UnderlinePosition", + "GlyphFormat", + "PixelSize", + "Weight", + "Style", + "EndOfHeader", + "WritingSystems" +}; + +QString QPF::fileNameForFont(const QFont &f) +{ + QString fileName = f.family().toLower() + "_" + QString::number(f.pixelSize()) + + "_" + QString::number(f.weight()) + + (f.italic() ? "_italic" : "") + + ".qpf2"; + fileName.replace(QLatin1Char(' '), QLatin1Char('_')); + return fileName; +} + +QByteArray QPF::generate(const QFont &font, int options, const QList<CharacterRange> &ranges, QString *originalFontFile) +{ + QTextEngine engine("Test", font); + engine.itemize(); + engine.shape(0); + QFontEngine *fontEngine = engine.fontEngine(engine.layoutData->items[0]); + if (fontEngine->type() == QFontEngine::Multi) + fontEngine = static_cast<QFontEngineMulti *>(fontEngine)->engine(0); + + if (originalFontFile) + *originalFontFile = QFile::decodeName(fontEngine->faceId().filename); + + return generate(fontEngine, options, ranges); +} + +QByteArray QPF::generate(QFontEngine *fontEngine, int options, const QList<CharacterRange> &ranges) +{ + QPF font; + + font.options = options; + font.addHeader(fontEngine); + if (options & IncludeCMap) + font.addCMap(fontEngine); + font.addGlyphs(fontEngine, ranges); + + return font.qpf; +} + +void QPF::addHeader(QFontEngine *fontEngine) +{ + QFontEngineQPF::Header *header = reinterpret_cast<QFontEngineQPF::Header *>(addBytes(sizeof(QFontEngineQPF::Header))); + + header->magic[0] = 'Q'; + header->magic[1] = 'P'; + header->magic[2] = 'F'; + header->magic[3] = '2'; + if (options & RenderGlyphs) + header->lock = 0xffffffff; + else + header->lock = 0; + header->majorVersion = QFontEngineQPF::CurrentMajorVersion; + header->minorVersion = QFontEngineQPF::CurrentMinorVersion; + header->dataSize = 0; + int oldSize = qpf.size(); + + addTaggedString(QFontEngineQPF::Tag_FontName, fontEngine->fontDef.family.toUtf8()); + + QFontEngine::FaceId face = fontEngine->faceId(); + addTaggedString(QFontEngineQPF::Tag_FileName, face.filename); + addTaggedUInt32(QFontEngineQPF::Tag_FileIndex, face.index); + + { + const QByteArray head = fontEngine->getSfntTable(MAKE_TAG('h', 'e', 'a', 'd')); + const quint32 revision = qFromBigEndian<quint32>(reinterpret_cast<const uchar *>(head.constData()) + 4); + addTaggedUInt32(QFontEngineQPF::Tag_FontRevision, revision); + } + + addTaggedQFixed(QFontEngineQPF::Tag_Ascent, fontEngine->ascent()); + addTaggedQFixed(QFontEngineQPF::Tag_Descent, fontEngine->descent()); + addTaggedQFixed(QFontEngineQPF::Tag_Leading, fontEngine->leading()); + addTaggedQFixed(QFontEngineQPF::Tag_XHeight, fontEngine->xHeight()); + addTaggedQFixed(QFontEngineQPF::Tag_AverageCharWidth, fontEngine->averageCharWidth()); + addTaggedQFixed(QFontEngineQPF::Tag_MaxCharWidth, QFixed::fromReal(fontEngine->maxCharWidth())); + addTaggedQFixed(QFontEngineQPF::Tag_LineThickness, fontEngine->lineThickness()); + addTaggedQFixed(QFontEngineQPF::Tag_MinLeftBearing, QFixed::fromReal(fontEngine->minLeftBearing())); + addTaggedQFixed(QFontEngineQPF::Tag_MinRightBearing, QFixed::fromReal(fontEngine->minRightBearing())); + addTaggedQFixed(QFontEngineQPF::Tag_UnderlinePosition, fontEngine->underlinePosition()); + addTaggedUInt8(QFontEngineQPF::Tag_PixelSize, fontEngine->fontDef.pixelSize); + addTaggedUInt8(QFontEngineQPF::Tag_Weight, fontEngine->fontDef.weight); + addTaggedUInt8(QFontEngineQPF::Tag_Style, fontEngine->fontDef.style); + + QByteArray writingSystemBitField = getWritingSystems(fontEngine); + if (!writingSystemBitField.isEmpty()) + addTaggedString(QFontEngineQPF::Tag_WritingSystems, writingSystemBitField); + + addTaggedUInt8(QFontEngineQPF::Tag_GlyphFormat, QFontEngineQPF::AlphamapGlyphs); + + addTaggedString(QFontEngineQPF::Tag_EndOfHeader, QByteArray()); + align4(); + header = reinterpret_cast<QFontEngineQPF::Header *>(qpf.data()); + header->dataSize = qToBigEndian<quint16>(qpf.size() - oldSize); +} + +static uchar *appendBytes(QByteArray &array, int size) +{ + int oldSize = array.size(); + array.resize(array.size() + size); + return reinterpret_cast<uchar *>(array.data() + oldSize); +} + +#define APPEND(type, value) \ + qToBigEndian<type>(value, appendBytes(cmap, sizeof(type))) + +struct CMapSegment +{ + int start; // codepoints + int end; + int startGlyphIndex; +}; + +static QByteArray generateTrueTypeCMap(QFontEngine *fe) +{ + QByteArray cmap; + const int glyphCount = fe->glyphCount(); + if (!glyphCount) + return cmap; + + // cmap header + APPEND(quint16, 0); // table version number + APPEND(quint16, 1); // number of tables + + // encoding record + APPEND(quint16, 3); // platform-id + APPEND(quint16, 10); // encoding-id (ucs-4) + const int cmapOffset = cmap.size() + sizeof(quint32); + APPEND(quint32, cmapOffset); // offset to sub-table + + APPEND(quint16, 4); // subtable format + const int cmapTableLengthOffset = cmap.size(); + APPEND(quint16, 0); // length in bytes, will fill in later + APPEND(quint16, 0); // language field + + QList<CMapSegment> segments; + CMapSegment currentSegment; + currentSegment.start = 0xffff; + currentSegment.end = 0; + currentSegment.startGlyphIndex = 0; + quint32 previousGlyphIndex = 0xfffffffe; + bool inSegment = false; + + QGlyphLayoutArray<10> layout; + for (uint uc = 0; uc < 0x10000; ++uc) { + QChar ch(uc); + int nglyphs = 10; + + bool validGlyph = fe->stringToCMap(&ch, 1, &layout, &nglyphs, /*flags*/ 0) + && nglyphs == 1 && layout.glyphs[0]; + + // leaving a segment? + if (inSegment && (!validGlyph || layout.glyphs[0] != previousGlyphIndex + 1)) { + Q_ASSERT(currentSegment.start != 0xffff); + // store the current segment + currentSegment.end = uc - 1; + segments.append(currentSegment); + currentSegment.start = 0xffff; + inSegment = false; + } + // entering a new segment? + if (validGlyph && (!inSegment || layout.glyphs[0] != previousGlyphIndex + 1)) { + currentSegment.start = uc; + currentSegment.startGlyphIndex = layout.glyphs[0]; + inSegment = true; + } + + if (validGlyph) + previousGlyphIndex = layout.glyphs[0]; + else + previousGlyphIndex = 0xfffffffe; + } + + currentSegment.start = 0xffff; + currentSegment.end = 0xffff; + currentSegment.startGlyphIndex = 0; + segments.append(currentSegment); + + if (QPF::debugVerbosity > 3) + qDebug() << "segments:" << segments.count(); + + Q_ASSERT(!inSegment); + + const quint16 entrySelector = int(log2(segments.count())); + const quint16 searchRange = 2 * (1 << entrySelector); + const quint16 rangeShift = segments.count() * 2 - searchRange; + + if (QPF::debugVerbosity > 3) + qDebug() << "entrySelector" << entrySelector << "searchRange" << searchRange + << "rangeShift" << rangeShift; + + APPEND(quint16, segments.count() * 2); // segCountX2 + APPEND(quint16, searchRange); + APPEND(quint16, entrySelector); + APPEND(quint16, rangeShift); + + // end character codes + for (int i = 0; i < segments.count(); ++i) + APPEND(quint16, segments.at(i).end); + + APPEND(quint16, 0); // pad + + // start character codes + for (int i = 0; i < segments.count(); ++i) + APPEND(quint16, segments.at(i).start); + + // id deltas + for (int i = 0; i < segments.count(); ++i) + APPEND(quint16, segments.at(i).startGlyphIndex - segments.at(i).start); + + // id range offsets + for (int i = 0; i < segments.count(); ++i) + APPEND(quint16, 0); + + uchar *lengthPtr = reinterpret_cast<uchar *>(cmap.data()) + cmapTableLengthOffset; + qToBigEndian<quint16>(cmap.size() - cmapOffset, lengthPtr); + + return cmap; +} + +void QPF::addCMap(QFontEngine *fontEngine) +{ + QByteArray cmapTable = fontEngine->getSfntTable(MAKE_TAG('c', 'm', 'a', 'p')); + if (cmapTable.isEmpty()) + cmapTable = generateTrueTypeCMap(fontEngine); + addBlock(QFontEngineQPF::CMapBlock, cmapTable); +} + +void QPF::addGlyphs(QFontEngine *fe, const QList<CharacterRange> &ranges) +{ + const quint16 glyphCount = fe->glyphCount(); + + QByteArray gmap; + gmap.resize(glyphCount * sizeof(quint32)); + gmap.fill(char(0xff)); + //qDebug() << "glyphCount" << glyphCount; + + QByteArray glyphs; + if (options & RenderGlyphs) { + // this is only a rough estimation + glyphs.reserve(glyphCount + * (sizeof(QFontEngineQPF::Glyph) + + qRound(fe->maxCharWidth() * (fe->ascent() + fe->descent()).toReal()))); + + QGlyphLayoutArray<10> layout; + + foreach (CharacterRange range, ranges) { + if (debugVerbosity > 2) + qDebug() << "rendering range from" << range.start << "to" << range.end; + for (uint uc = range.start; uc < range.end; ++uc) { + QChar ch(uc); + int nglyphs = 10; + if (!fe->stringToCMap(&ch, 1, &layout, &nglyphs, /*flags*/ 0)) + continue; + + if (nglyphs != 1) + continue; + + const quint32 glyphIndex = layout.glyphs[0]; + + if (!glyphIndex) + continue; + + Q_ASSERT(glyphIndex < glyphCount); + + QImage img = fe->alphaMapForGlyph(glyphIndex).convertToFormat(QImage::Format_Indexed8); + glyph_metrics_t metrics = fe->boundingBox(glyphIndex); + + const quint32 oldSize = glyphs.size(); + glyphs.resize(glyphs.size() + sizeof(QFontEngineQPF::Glyph) + img.numBytes()); + uchar *data = reinterpret_cast<uchar *>(glyphs.data() + oldSize); + + uchar *gmapPtr = reinterpret_cast<uchar *>(gmap.data() + glyphIndex * sizeof(quint32)); + qToBigEndian(oldSize, gmapPtr); + + QFontEngineQPF::Glyph *glyph = reinterpret_cast<QFontEngineQPF::Glyph *>(data); + glyph->width = img.width(); + glyph->height = img.height(); + glyph->bytesPerLine = img.bytesPerLine(); + glyph->x = qRound(metrics.x); + glyph->y = qRound(metrics.y); + glyph->advance = qRound(metrics.xoff); + data += sizeof(QFontEngineQPF::Glyph); + + if (debugVerbosity && uc >= 'A' && uc <= 'z' || debugVerbosity > 1) { + qDebug() << "adding glyph with index" << glyphIndex << " uc =" << char(uc) << ":\n" + << " glyph->x =" << glyph->x << "rounded from" << metrics.x << "\n" + << " glyph->y =" << glyph->y << "rounded from" << metrics.y << "\n" + << " width =" << glyph->width << "height =" << glyph->height + << " advance =" << glyph->advance << "rounded from" << metrics.xoff + ; + } + + qMemCopy(data, img.bits(), img.numBytes()); + } + } + } + + addBlock(QFontEngineQPF::GMapBlock, gmap); + addBlock(QFontEngineQPF::GlyphBlock, glyphs); +} + +void QPF::addBlock(QFontEngineQPF::BlockTag tag, const QByteArray &blockData) +{ + addUInt16(tag); + addUInt16(0); // padding + const int padSize = ((blockData.size() + 3) / 4) * 4 - blockData.size(); + addUInt32(blockData.size() + padSize); + addByteArray(blockData); + for (int i = 0; i < padSize; ++i) + addUInt8(0); +} + +#define ADD_TAGGED_DATA(tag, qtype, type, value) \ + addUInt16(tag); \ + addUInt16(sizeof(qtype)); \ + add##type(value) + +void QPF::addTaggedString(QFontEngineQPF::HeaderTag tag, const QByteArray &string) +{ + addUInt16(tag); + addUInt16(string.length()); + addByteArray(string); +} + +void QPF::addTaggedQFixed(QFontEngineQPF::HeaderTag tag, QFixed value) +{ + ADD_TAGGED_DATA(tag, quint32, UInt32, value.value()); +} + +void QPF::addTaggedUInt8(QFontEngineQPF::HeaderTag tag, quint8 value) +{ + ADD_TAGGED_DATA(tag, quint8, UInt8, value); +} + +void QPF::addTaggedInt8(QFontEngineQPF::HeaderTag tag, qint8 value) +{ + ADD_TAGGED_DATA(tag, qint8, Int8, value); +} + +void QPF::addTaggedUInt16(QFontEngineQPF::HeaderTag tag, quint16 value) +{ + ADD_TAGGED_DATA(tag, quint16, UInt16, value); +} + +void QPF::addTaggedUInt32(QFontEngineQPF::HeaderTag tag, quint32 value) +{ + ADD_TAGGED_DATA(tag, quint32, UInt32, value); +} + +void QPF::dump(const QByteArray &qpf) +{ + QPF font; + font.qpf = qpf; + + const uchar *data = reinterpret_cast<const uchar *>(qpf.constData()); + const uchar *endPtr = reinterpret_cast<const uchar *>(qpf.constData() + qpf.size()); + data = font.dumpHeader(data); + + const quint32 *gmap = 0; + quint32 glyphCount = 0; + + while (data < endPtr) { + const QFontEngineQPF::Block *block = reinterpret_cast<const QFontEngineQPF::Block *>(data); + quint32 tag = qFromBigEndian(block->tag); + quint32 blockSize = qFromBigEndian(block->dataSize); + qDebug() << "Block: Tag =" << qFromBigEndian(block->tag) << "; Size =" << blockSize << "; Offset =" << hex << data - reinterpret_cast<const uchar *>(qpf.constData()); + data += sizeof(QFontEngineQPF::Block); + + if (debugVerbosity) { + if (tag == QFontEngineQPF::GMapBlock) { + gmap = reinterpret_cast<const quint32 *>(data); + glyphCount = blockSize / 4; + font.dumpGMapBlock(gmap, glyphCount); + } else if (tag == QFontEngineQPF::GlyphBlock + && gmap && debugVerbosity > 1) { + font.dumpGlyphBlock(gmap, glyphCount, data, data + blockSize); + } + } + + data += blockSize; + } +} + +const uchar *QPF::dumpHeader(const uchar *data) +{ + const QFontEngineQPF::Header *header = reinterpret_cast<const QFontEngineQPF::Header *>(data); + qDebug() << "Header:"; + qDebug() << "magic =" + << header->magic[0] + << header->magic[1] + << header->magic[2] + << header->magic[3]; + qDebug() << "lock =" << qFromBigEndian(header->lock); + qDebug() << "majorVersion =" << header->majorVersion; + qDebug() << "minorVersion =" << header->minorVersion; + qDebug() << "dataSize =" << qFromBigEndian(header->dataSize); + + data += sizeof(QFontEngineQPF::Header); + + const uchar *endPtr = data + qFromBigEndian(header->dataSize); + + while (data && data < endPtr) { + data = dumpHeaderTag(data); + } + + return endPtr; +} + +const uchar *QPF::dumpHeaderTag(const uchar *data) +{ + const QFontEngineQPF::Tag *tagPtr = reinterpret_cast<const QFontEngineQPF::Tag *>(data); + quint16 tag = qFromBigEndian(tagPtr->tag); + quint16 size = qFromBigEndian(tagPtr->size); + + qDebug() << "Tag =" << tag << headerTagNames[tag]; + qDebug() << "Size =" << size; + + if (tag == QFontEngineQPF::Tag_EndOfHeader) + return 0; + + data += sizeof(QFontEngineQPF::Tag); + + Q_ASSERT(tag < QFontEngineQPF::NumTags); + + switch (tagTypes[tag]) { + case QFontEngineQPF::StringType: + qDebug() << "Payload =" << QString::fromUtf8(QByteArray(reinterpret_cast<const char *>(data), size)); + break; + case QFontEngineQPF::FixedType: + Q_ASSERT(size == sizeof(quint32)); + qDebug() << "Payload =" << QFixed::fromFixed(qFromBigEndian<quint32>(data)).toReal(); + break; + case QFontEngineQPF::UInt8Type: + Q_ASSERT(size == sizeof(quint8)); + qDebug() << "Payload =" << *data; + break; + case QFontEngineQPF::UInt32Type: + Q_ASSERT(size == sizeof(quint32)); + qDebug() << "Payload =" << qFromBigEndian<quint32>(data); + break; + case QFontEngineQPF::BitFieldType: { + QByteArray bits(reinterpret_cast<const char *>(data), size); + qDebug() << "Payload =" << stringify(bits); + if (QPF::debugVerbosity > 2 && tag == QFontEngineQPF::Tag_WritingSystems) + dumpWritingSystems(bits); + } break; + } + + data += size; + return data; +} + +void QPF::dumpGMapBlock(const quint32 *gmap, int glyphCount) +{ + qDebug() << "glyphCount =" << glyphCount; + int renderedGlyphs = 0; + for (int i = 0; i < glyphCount; ++i) { + if (gmap[i] != 0xffffffff) { + const quint32 glyphPos = qFromBigEndian(gmap[i]); + qDebug("gmap[%d] = 0x%x / %u", i, glyphPos, glyphPos); + ++renderedGlyphs; + } + } + qDebug() << "Glyphs rendered:" << renderedGlyphs << "; Glyphs missing from the font:" << glyphCount - renderedGlyphs; +} + +void QPF::dumpGlyphBlock(const quint32 *gmap, int glyphCount, const uchar *data, const uchar *endPtr) +{ + // glyphPos -> glyphIndex + QMap<quint32, quint32> reverseGlyphMap; + for (int i = 0; i < glyphCount; ++i) { + if (gmap[i] == 0xffffffff) + continue; + const quint32 glyphPos = qFromBigEndian(gmap[i]); + reverseGlyphMap[glyphPos] = i; + } + + const uchar *glyphBlockBegin = data; + while (data < endPtr) { + const QFontEngineQPF::Glyph *g = reinterpret_cast<const QFontEngineQPF::Glyph *>(data); + + const quint64 glyphOffset = data - glyphBlockBegin; + const quint32 glyphIndex = reverseGlyphMap.value(glyphOffset, 0xffffffff); + + if (glyphIndex == 0xffffffff) + qDebug() << "############: Glyph present in glyph block is not listed in glyph map!"; + qDebug("glyph at offset 0x%x glyphIndex = %u", quint32(glyphOffset), glyphIndex); + qDebug() << " width =" << g->width << "height =" << g->height << "x =" << g->x << "y =" << g->y; + qDebug() << " advance =" << g->advance << "bytesPerLine =" << g->bytesPerLine; + + data += sizeof(*g); + if (glyphIndex == 0xffffffff || debugVerbosity > 4) { + dumpGlyph(data, g); + } + + data += g->height * g->bytesPerLine; + } +} + +void QPF::dumpGlyph(const uchar *data, const QFontEngineQPF::Glyph *glyph) +{ + fprintf(stderr, "---- glyph data:\n"); + const char *alphas = " .o#"; + for (int y = 0; y < glyph->height; ++y) { + for (int x = 0; x < glyph->width; ++x) { + const uchar value = data[y * glyph->bytesPerLine + x]; + fprintf(stderr, "%c", alphas[value >> 6]); + } + fprintf(stderr, "\n"); + } + fprintf(stderr, "----\n"); +} + +QT_END_NAMESPACE diff --git a/tools/makeqpf/qpf2.h b/tools/makeqpf/qpf2.h new file mode 100644 index 0000000..211ac4d --- /dev/null +++ b/tools/makeqpf/qpf2.h @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPF2_H +#define QPF2_H + +#include <private/qfontengine_qpf_p.h> +#include <qmetatype.h> + +QT_BEGIN_NAMESPACE + +class QFontEngine; + +class QPF +{ +public: + static int debugVerbosity; + + enum GenerationOption { + IncludeCMap = 0x1, + RenderGlyphs = 0x2 + }; + + struct CharacterRange + { + inline CharacterRange() : start(0), end(0x10000) {} + uint start; + uint end; + }; + + static QString fileNameForFont(const QFont &f); + + static QByteArray generate(const QFont &font, int options, + const QList<CharacterRange> &ranges, + QString *originalFontFile = 0); + static QByteArray generate(QFontEngine *fontEngine, int options, const QList<CharacterRange> &ranges); + void addHeader(QFontEngine *fontEngine); + void addCMap(QFontEngine *fontEngine); + void addGlyphs(QFontEngine *fontEngine, const QList<CharacterRange> &ranges); + void addBlock(QFontEngineQPF::BlockTag tag, const QByteArray &data); + + void addTaggedString(QFontEngineQPF::HeaderTag tag, const QByteArray &string); + void addTaggedQFixed(QFontEngineQPF::HeaderTag tag, QFixed value); + void addTaggedUInt8(QFontEngineQPF::HeaderTag tag, quint8 value); + void addTaggedInt8(QFontEngineQPF::HeaderTag tag, qint8 value); + void addTaggedUInt16(QFontEngineQPF::HeaderTag tag, quint16 value); + void addTaggedUInt32(QFontEngineQPF::HeaderTag tag, quint32 value); + + static void dump(const QByteArray &qpf); + const uchar *dumpHeader(const uchar *data); + const uchar *dumpHeaderTag(const uchar *data); + void dumpGMapBlock(const quint32 *gmap, int glyphCount); + void dumpGlyphBlock(const quint32 *gmap, int glyphCount, const uchar *data, const uchar *endPtr); + void dumpGlyph(const uchar *data, const QFontEngineQPF::Glyph *glyph); + + void addUInt16(quint16 value) { qToBigEndian(value, addBytes(sizeof(value))); } + void addUInt32(quint32 value) { qToBigEndian(value, addBytes(sizeof(value))); } + void addUInt8(quint8 value) { *addBytes(sizeof(value)) = value; } + void addInt8(qint8 value) { *addBytes(sizeof(value)) = quint8(value); } + void addByteArray(const QByteArray &string) { + uchar *data = addBytes(string.length()); + qMemCopy(data, string.constData(), string.length()); + } + + void align4() { while (qpf.size() & 3) { addUInt8('\0'); } } + + uchar *addBytes(int size) { + const int oldSize = qpf.size(); + qpf.resize(qpf.size() + size); + return reinterpret_cast<uchar *>(qpf.data() + oldSize); + } + + QByteArray qpf; + int options; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QPF::CharacterRange) + +#endif // QPF2_H |