Charlie Jiang pushed to branch ftinspect-psname at FreeType / FreeType Demo Programs
Commits:
-
91b961fa
by Charlie Jiang at 2023-10-03T21:04:12+08:00
-
3228489a
by Charlie Jiang at 2023-10-05T15:21:59+08:00
-
ada48b53
by Charlie Jiang at 2023-10-05T15:21:59+08:00
6 changed files:
- src/ftinspect/engine/engine.cpp
- src/ftinspect/engine/engine.hpp
- src/ftinspect/engine/fontinfo.cpp
- src/ftinspect/panels/settingpanelmmgx.cpp
- src/ftinspect/panels/settingpanelmmgx.hpp
- src/ftmulti.c
Changes:
... | ... | @@ -79,18 +79,14 @@ faceRequester(FTC_FaceID ftcFaceID, |
79 | 79 | FT_Pointer requestData,
|
80 | 80 | FT_Face* faceP)
|
81 | 81 | {
|
82 | - Engine* engine = static_cast<Engine*>(requestData);
|
|
83 | - // `ftcFaceID` is actually an integer
|
|
84 | - // -> First convert pointer to same-width integer, then discard superfluous
|
|
85 | - // bits (e.g., on x86_64 where pointers are wider than `int`).
|
|
86 | - int val = static_cast<int>(reinterpret_cast<intptr_t>(ftcFaceID));
|
|
87 | - // Make sure this does not cause information loss.
|
|
88 | - Q_ASSERT_X(sizeof(void*) >= sizeof(int),
|
|
89 | - "faceRequester",
|
|
90 | - "Pointer size must be at least the size of int"
|
|
91 | - " in order to treat FTC_FaceID correctly");
|
|
92 | - |
|
93 | - const FaceID& faceID = engine->faceIDMap_.key(val);
|
|
82 | + auto* engine = static_cast<Engine*>(requestData);
|
|
83 | + // The highest bit of ftcFaceID:
|
|
84 | + // 0 - fallback face (for information retrieval),
|
|
85 | + // 1 - rendering face/size
|
|
86 | + // Trim the highest bit
|
|
87 | + auto faceIdInt = reinterpret_cast<uintptr_t>(ftcFaceID) & (UINTPTR_MAX >> 1);
|
|
88 | + |
|
89 | + const FaceID& faceID = engine->faceIDMap_.key(faceIdInt);
|
|
94 | 90 | |
95 | 91 | // This is the only place where we have to check the validity of the font
|
96 | 92 | // index; note that the validity of both the face and named instance index
|
... | ... | @@ -179,6 +175,8 @@ Engine::withFace(FaceID id, |
179 | 175 | {
|
180 | 176 | FT_Face face;
|
181 | 177 | // Search triplet (fontIndex, faceIndex, namedInstanceIndex).
|
178 | + // numId doesn't have the highest bit set, so the face is looked up for
|
|
179 | + // retrieving info, thus no rendering / writing should be done in the callback
|
|
182 | 180 | auto numId = reinterpret_cast<FTC_FaceID>(faceIDMap_.value(id));
|
183 | 181 | if (numId)
|
184 | 182 | {
|
... | ... | @@ -292,6 +290,47 @@ Engine::currentFontTricky() |
292 | 290 | }
|
293 | 291 | |
294 | 292 | |
293 | +FTC_FaceID getFTCIdWithHighestBitSet(FTC_FaceID id)
|
|
294 | +{
|
|
295 | + Q_ASSERT_X(sizeof(FTC_FaceID) >= sizeof(uintptr_t),
|
|
296 | + "getFTCIdWithHighestBitSet",
|
|
297 | + "FTC_FaceID must be longer than uintptr_t");
|
|
298 | + return reinterpret_cast<FTC_FaceID>(
|
|
299 | + reinterpret_cast<uintptr_t>(id)
|
|
300 | + | (UINTPTR_MAX & ~INTPTR_MAX)
|
|
301 | + );
|
|
302 | +}
|
|
303 | + |
|
304 | + |
|
305 | +FTC_FaceID getFTCIdWithHighestBitClear(FTC_FaceID id)
|
|
306 | +{
|
|
307 | + Q_ASSERT_X(sizeof(FTC_FaceID) >= sizeof(uintptr_t),
|
|
308 | + "getFTCIdWithHighestBitClear",
|
|
309 | + "FTC_FaceID must be longer than uintptr_t");
|
|
310 | + return reinterpret_cast<FTC_FaceID>(
|
|
311 | + reinterpret_cast<uintptr_t>(id) & INTPTR_MAX
|
|
312 | + );
|
|
313 | +}
|
|
314 | + |
|
315 | + |
|
316 | +void
|
|
317 | +Engine::loadFontWithFTCId(FTC_FaceID ftcId)
|
|
318 | +{
|
|
319 | + if (!FTC_Manager_LookupFace(cacheManager_, ftcId,
|
|
320 | + &ftFallbackFace_))
|
|
321 | + {
|
|
322 | + scaler_.face_id = getFTCIdWithHighestBitSet(ftcId);
|
|
323 | + if (FTC_Manager_LookupSize(cacheManager_, &scaler_, &ftSize_))
|
|
324 | + ftSize_ = NULL; // Good font, bad size.
|
|
325 | + }
|
|
326 | + else
|
|
327 | + {
|
|
328 | + ftFallbackFace_ = NULL;
|
|
329 | + ftSize_ = NULL;
|
|
330 | + }
|
|
331 | +}
|
|
332 | + |
|
333 | + |
|
295 | 334 | int
|
296 | 335 | Engine::loadFont(int fontIndex,
|
297 | 336 | long faceIndex,
|
... | ... | @@ -307,50 +346,36 @@ Engine::loadFont(int fontIndex, |
307 | 346 | auto id = FaceID(fontIndex, faceIndex, namedInstanceIndex);
|
308 | 347 | |
309 | 348 | // Search triplet (fontIndex, faceIndex, namedInstanceIndex).
|
310 | - scaler_.face_id = reinterpret_cast<FTC_FaceID>(faceIDMap_.value(id));
|
|
311 | - if (scaler_.face_id)
|
|
349 | + auto face_id = reinterpret_cast<FTC_FaceID>(faceIDMap_.value(id));
|
|
350 | + if (face_id)
|
|
312 | 351 | {
|
313 | 352 | // Found.
|
314 | - if (!FTC_Manager_LookupFace(cacheManager_, scaler_.face_id,
|
|
315 | - &ftFallbackFace_))
|
|
316 | - {
|
|
353 | + loadFontWithFTCId(face_id);
|
|
354 | + if (ftFallbackFace_)
|
|
317 | 355 | numGlyphs = ftFallbackFace_->num_glyphs;
|
318 | - if (FTC_Manager_LookupSize(cacheManager_, &scaler_, &ftSize_))
|
|
319 | - ftSize_ = NULL; // Good font, bad size.
|
|
320 | - }
|
|
321 | - else
|
|
322 | - {
|
|
323 | - ftFallbackFace_ = NULL;
|
|
324 | - ftSize_ = NULL;
|
|
325 | - }
|
|
326 | 356 | }
|
327 | 357 | else if (fontIndex >= 0)
|
328 | 358 | {
|
329 | - if (faceCounter_ >= INT_MAX) // Prevent overflow.
|
|
359 | + if (faceCounter_ >= INTPTR_MAX) // Prevent overflow.
|
|
330 | 360 | return -1;
|
331 | 361 | |
332 | 362 | // Not found; try to load triplet
|
333 | 363 | // (fontIndex, faceIndex, namedInstanceIndex).
|
334 | - scaler_.face_id = reinterpret_cast<FTC_FaceID>(faceCounter_);
|
|
364 | + face_id = reinterpret_cast<FTC_FaceID>(faceCounter_);
|
|
335 | 365 | faceIDMap_.insert(id, faceCounter_++);
|
336 | - |
|
337 | - if (!FTC_Manager_LookupFace(cacheManager_, scaler_.face_id,
|
|
338 | - &ftFallbackFace_))
|
|
339 | - {
|
|
366 | + loadFontWithFTCId(face_id);
|
|
367 | + if (ftFallbackFace_)
|
|
340 | 368 | numGlyphs = ftFallbackFace_->num_glyphs;
|
341 | - if (FTC_Manager_LookupSize(cacheManager_, &scaler_, &ftSize_))
|
|
342 | - ftSize_ = NULL; // Good font, bad size.
|
|
343 | - }
|
|
344 | 369 | else
|
345 | 370 | {
|
346 | 371 | faceIDMap_.remove(id);
|
347 | 372 | faceCounter_--;
|
348 | - ftFallbackFace_ = NULL;
|
|
349 | - ftSize_ = NULL;
|
|
373 | + scaler_.face_id = 0;
|
|
374 | + face_id = 0;
|
|
350 | 375 | }
|
351 | 376 | }
|
352 | 377 | |
353 | - imageType_.face_id = scaler_.face_id;
|
|
378 | + imageType_.face_id = face_id ? getFTCIdWithHighestBitSet(face_id) : 0;
|
|
354 | 379 | |
355 | 380 | if (numGlyphs < 0)
|
356 | 381 | {
|
... | ... | @@ -367,6 +392,12 @@ Engine::loadFont(int fontIndex, |
367 | 392 | {
|
368 | 393 | curFamilyName_ = QString(ftFallbackFace_->family_name);
|
369 | 394 | curStyleName_ = QString(ftFallbackFace_->style_name);
|
395 | + auto* psName = FT_Get_Postscript_Name(ftFallbackFace_);
|
|
396 | + if (psName)
|
|
397 | + curPostScriptNameWithoutCoords_ = psName;
|
|
398 | + else
|
|
399 | + curPostScriptNameWithoutCoords_ = QString();
|
|
400 | + curPostScriptNameWithCoords_ = curPostScriptNameWithoutCoords_;
|
|
370 | 401 | |
371 | 402 | const char* moduleName = FT_FACE_DRIVER_NAME(ftFallbackFace_);
|
372 | 403 | |
... | ... | @@ -403,16 +434,17 @@ Engine::reloadFont() |
403 | 434 | palette_ = NULL;
|
404 | 435 | if (!scaler_.face_id)
|
405 | 436 | return;
|
406 | - imageType_.face_id = scaler_.face_id;
|
|
437 | + imageType_.face_id = getFTCIdWithHighestBitSet(scaler_.face_id);
|
|
407 | 438 | |
408 | 439 | if (FTC_Manager_LookupFace(cacheManager_,
|
409 | - scaler_.face_id,
|
|
440 | + getFTCIdWithHighestBitClear(scaler_.face_id),
|
|
410 | 441 | &ftFallbackFace_))
|
411 | 442 | {
|
412 | 443 | ftFallbackFace_ = NULL;
|
413 | 444 | ftSize_ = NULL;
|
414 | 445 | return;
|
415 | 446 | }
|
447 | + scaler_.face_id = getFTCIdWithHighestBitSet(scaler_.face_id);
|
|
416 | 448 | if (FTC_Manager_LookupSize(cacheManager_, &scaler_, &ftSize_))
|
417 | 449 | ftSize_ = NULL; // Good font, bad size.
|
418 | 450 | }
|
... | ... | @@ -847,6 +879,9 @@ Engine::applyMMGXDesignCoords(FT_Fixed* coords, |
847 | 879 | FT_Set_Var_Design_Coordinates(ftSize_->face,
|
848 | 880 | static_cast<unsigned>(count),
|
849 | 881 | coords);
|
882 | + auto* psName = FT_Get_Postscript_Name(ftSize_->face);
|
|
883 | + if (psName)
|
|
884 | + curPostScriptNameWithCoords_ = psName;
|
|
850 | 885 | }
|
851 | 886 | |
852 | 887 | |
... | ... | @@ -1062,6 +1097,9 @@ Engine::switchNamedInstance(int index) |
1062 | 1097 | {
|
1063 | 1098 | if (!ftFallbackFace_ || !FT_HAS_MULTIPLE_MASTERS(ftFallbackFace_))
|
1064 | 1099 | return;
|
1100 | + // Writing to ftFallbackFace: since the faces are indexed by named instance
|
|
1101 | + // id, it's safe to set the NI of fallback face
|
|
1102 | + // This helps the PostScript Name label show the correct name.
|
|
1065 | 1103 | auto err = FT_Set_Named_Instance(ftFallbackFace_, index);
|
1066 | 1104 | if (err)
|
1067 | 1105 | {
|
... | ... | @@ -1070,12 +1108,12 @@ Engine::switchNamedInstance(int index) |
1070 | 1108 | if (ftSize_)
|
1071 | 1109 | {
|
1072 | 1110 | err = FT_Set_Named_Instance(ftSize_->face, index);
|
1073 | - if (err)
|
|
1074 | - {
|
|
1075 | - // XXX error handling
|
|
1076 | - }
|
|
1111 | + if (err)
|
|
1112 | + {
|
|
1113 | + // XXX error handling
|
|
1077 | 1114 | }
|
1078 | 1115 | }
|
1116 | +}
|
|
1079 | 1117 | |
1080 | 1118 | |
1081 | 1119 | // end of engine.cpp |
... | ... | @@ -76,6 +76,7 @@ public: |
76 | 76 | |
77 | 77 | //////// Actions
|
78 | 78 | |
79 | + void loadFontWithFTCId(FTC_FaceID ftcId);
|
|
79 | 80 | int loadFont(int fontIndex,
|
80 | 81 | long faceIndex,
|
81 | 82 | int namedInstanceIndex); // Return number of glyphs.
|
... | ... | @@ -128,6 +129,14 @@ public: |
128 | 129 | int currentFontType() const { return fontType_; }
|
129 | 130 | const QString& currentFamilyName() { return curFamilyName_; }
|
130 | 131 | const QString& currentStyleName() { return curStyleName_; }
|
132 | + const QString& currentPostScriptNameWithoutCoords()
|
|
133 | + {
|
|
134 | + return curPostScriptNameWithoutCoords_;
|
|
135 | + }
|
|
136 | + const QString& currentPostScriptNameWithCoords()
|
|
137 | + {
|
|
138 | + return curPostScriptNameWithCoords_;
|
|
139 | + }
|
|
131 | 140 | int currentFontNumberOfGlyphs() { return curNumGlyphs_; }
|
132 | 141 | |
133 | 142 | std::vector<PaletteInfo>& currentFontPalettes() { return curPaletteInfos_; }
|
... | ... | @@ -234,6 +243,8 @@ private: |
234 | 243 | int fontType_ = FontType_Other;
|
235 | 244 | QString curFamilyName_;
|
236 | 245 | QString curStyleName_;
|
246 | + QString curPostScriptNameWithoutCoords_;
|
|
247 | + QString curPostScriptNameWithCoords_;
|
|
237 | 248 | int curNumGlyphs_ = -1;
|
238 | 249 | std::vector<CharMapInfo> curCharMaps_;
|
239 | 250 | std::vector<PaletteInfo> curPaletteInfos_;
|
... | ... | @@ -291,6 +302,7 @@ private: |
291 | 302 | void switchNamedInstance(int index);
|
292 | 303 | |
293 | 304 | // It is safe to put the implementation into the corresponding cpp file.
|
305 | + // Note: no rendering / writing should be done in the callback
|
|
294 | 306 | template <class Func>
|
295 | 307 | void withFace(FaceID id,
|
296 | 308 | Func func);
|
... | ... | @@ -202,9 +202,7 @@ FontBasicInfo::get(Engine* engine) |
202 | 202 | if (face->style_name)
|
203 | 203 | result.styleName = QString(face->style_name);
|
204 | 204 | |
205 | - auto psName = FT_Get_Postscript_Name(face);
|
|
206 | - if (psName)
|
|
207 | - result.postscriptName = QString(psName);
|
|
205 | + result.postscriptName = engine->currentPostScriptNameWithoutCoords();
|
|
208 | 206 | |
209 | 207 | auto head = static_cast<TT_Header*>(FT_Get_Sfnt_Table(face, FT_SFNT_HEAD));
|
210 | 208 | if (head)
|
... | ... | @@ -74,6 +74,10 @@ SettingPanelMMGX::applySettings() |
74 | 74 | engine_->reloadFont();
|
75 | 75 | engine_->applyMMGXDesignCoords(currentValues_.data(),
|
76 | 76 | currentValues_.size());
|
77 | + if (checkDirty())
|
|
78 | + psNameText_->setPlainText(engine_->currentPostScriptNameWithCoords());
|
|
79 | + else
|
|
80 | + psNameText_->setPlainText(engine_->currentPostScriptNameWithoutCoords());
|
|
77 | 81 | }
|
78 | 82 | |
79 | 83 | |
... | ... | @@ -88,6 +92,15 @@ SettingPanelMMGX::checkHidden() |
88 | 92 | }
|
89 | 93 | |
90 | 94 | |
95 | +bool
|
|
96 | +SettingPanelMMGX::checkDirty() const
|
|
97 | +{
|
|
98 | + for (auto w : itemWidgets_)
|
|
99 | + if (w->dirty()) return true;
|
|
100 | + return false;
|
|
101 | +}
|
|
102 | + |
|
103 | + |
|
91 | 104 | void
|
92 | 105 | SettingPanelMMGX::createLayout()
|
93 | 106 | {
|
... | ... | @@ -96,15 +109,19 @@ SettingPanelMMGX::createLayout() |
96 | 109 | resetDefaultButton_ = new QPushButton(tr("Reset Default"), this);
|
97 | 110 | itemsListWidget_ = new QWidget(this);
|
98 | 111 | scrollArea_ = new UnboundScrollArea(this);
|
112 | + psNameBox_ = new QGroupBox("PostScript Name", this);
|
|
113 | + psNameText_ = new QPlainTextEdit(psNameBox_);
|
|
99 | 114 | |
100 | 115 | scrollArea_->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Ignored);
|
101 | 116 | scrollArea_->setWidget(itemsListWidget_);
|
102 | 117 | scrollArea_->setWidgetResizable(true);
|
103 | 118 | itemsListWidget_->setAutoFillBackground(false);
|
119 | + psNameText_->setReadOnly(true);
|
|
104 | 120 | |
105 | 121 | mainLayout_ = new QVBoxLayout;
|
106 | 122 | listLayout_ = new QVBoxLayout;
|
107 | 123 | listWrapperLayout_ = new QVBoxLayout;
|
124 | + psNameBoxLayout_ = new QVBoxLayout;
|
|
108 | 125 | |
109 | 126 | listLayout_->setSpacing(0);
|
110 | 127 | listLayout_->setContentsMargins(0, 0, 0, 0);
|
... | ... | @@ -115,10 +132,16 @@ SettingPanelMMGX::createLayout() |
115 | 132 | listWrapperLayout_->addLayout(listLayout_);
|
116 | 133 | listWrapperLayout_->addStretch(1);
|
117 | 134 | |
135 | + psNameText_->setMaximumHeight(50);
|
|
136 | + psNameText_->setMinimumHeight(50);
|
|
137 | + psNameBox_->setLayout(psNameBoxLayout_);
|
|
138 | + psNameBoxLayout_->addWidget(psNameText_);
|
|
139 | + |
|
118 | 140 | mainLayout_->addWidget(showHiddenCheckBox_);
|
119 | 141 | mainLayout_->addWidget(groupingCheckBox_);
|
120 | 142 | mainLayout_->addWidget(resetDefaultButton_);
|
121 | - mainLayout_->addWidget(scrollArea_, 1);
|
|
143 | + mainLayout_->addWidget(scrollArea_, 2);
|
|
144 | + mainLayout_->addWidget(psNameBox_, 0);
|
|
122 | 145 | |
123 | 146 | setLayout(mainLayout_);
|
124 | 147 | }
|
... | ... | @@ -169,7 +192,7 @@ SettingPanelMMGX::resetDefaultClicked() |
169 | 192 | {
|
170 | 193 | for (auto w : itemWidgets_)
|
171 | 194 | w->resetDefault();
|
172 | - |
|
195 | +
|
|
173 | 196 | retrieveValues();
|
174 | 197 | emit mmgxCoordsChanged();
|
175 | 198 | }
|
... | ... | @@ -234,6 +257,7 @@ void |
234 | 257 | MMGXSettingItem::setValue(FT_Fixed value)
|
235 | 258 | {
|
236 | 259 | actualValue_ = value;
|
260 | + dirty_ = true;
|
|
237 | 261 | updateSlider();
|
238 | 262 | updateLineEdit();
|
239 | 263 | }
|
... | ... | @@ -243,6 +267,7 @@ void |
243 | 267 | MMGXSettingItem::resetDefault()
|
244 | 268 | {
|
245 | 269 | setValue(static_cast<FT_Fixed>(axisInfo_.def * 65536.0));
|
270 | + dirty_ = false;
|
|
246 | 271 | }
|
247 | 272 | |
248 | 273 | |
... | ... | @@ -305,6 +330,7 @@ MMGXSettingItem::sliderValueChanged() |
305 | 330 | * (axisInfo_.maximum - axisInfo_.minimum)
|
306 | 331 | + axisInfo_.minimum;
|
307 | 332 | actualValue_ = static_cast<FT_Fixed>(value * 65536.0);
|
333 | + dirty_ = true;
|
|
308 | 334 | |
309 | 335 | if (axisInfo_.isMM)
|
310 | 336 | actualValue_ = FT_RoundFix(actualValue_);
|
... | ... | @@ -326,6 +352,7 @@ MMGXSettingItem::lineEditChanged() |
326 | 352 | }
|
327 | 353 | |
328 | 354 | actualValue_ = static_cast<FT_Fixed>(newValue / 65536.0);
|
355 | + dirty_ = true;
|
|
329 | 356 | |
330 | 357 | updateSlider();
|
331 | 358 | emit valueChanged();
|
... | ... | @@ -12,11 +12,13 @@ |
12 | 12 | #include <QCheckBox>
|
13 | 13 | #include <QDoubleValidator>
|
14 | 14 | #include <QFrame>
|
15 | +#include <QGroupBox>
|
|
15 | 16 | #include <QGridLayout>
|
16 | 17 | #include <QLabel>
|
17 | 18 | #include <QLineEdit>
|
18 | 19 | #include <QPushButton>
|
19 | 20 | #include <QScrollArea>
|
21 | +#include <QPlainTextEdit>
|
|
20 | 22 | #include <QSlider>
|
21 | 23 | #include <QToolButton>
|
22 | 24 | #include <QWidget>
|
... | ... | @@ -40,6 +42,7 @@ public: |
40 | 42 | void reloadFont();
|
41 | 43 | void applySettings();
|
42 | 44 | void checkHidden();
|
45 | + bool checkDirty() const;
|
|
43 | 46 | std::vector<FT_Fixed>& mmgxCoords() { return currentValues_; }
|
44 | 47 | |
45 | 48 | signals:
|
... | ... | @@ -53,11 +56,14 @@ private: |
53 | 56 | QPushButton* resetDefaultButton_;
|
54 | 57 | QWidget* itemsListWidget_;
|
55 | 58 | UnboundScrollArea* scrollArea_;
|
59 | + QGroupBox* psNameBox_;
|
|
60 | + QPlainTextEdit* psNameText_;
|
|
56 | 61 | std::vector<MMGXSettingItem*> itemWidgets_;
|
57 | 62 | |
58 | 63 | QVBoxLayout* mainLayout_;
|
59 | 64 | QVBoxLayout* listLayout_;
|
60 | 65 | QVBoxLayout* listWrapperLayout_;
|
66 | + QVBoxLayout* psNameBoxLayout_;
|
|
61 | 67 | |
62 | 68 | std::vector<FT_Fixed> currentValues_;
|
63 | 69 | std::vector<MMGXAxisInfo> currentAxes_;
|
... | ... | @@ -83,6 +89,7 @@ public: |
83 | 89 | |
84 | 90 | void updateInfo(MMGXAxisInfo& info);
|
85 | 91 | FT_Fixed value() { return actualValue_; }
|
92 | + bool dirty() { return dirty_; }
|
|
86 | 93 | void setValue(FT_Fixed value);
|
87 | 94 | void resetDefault();
|
88 | 95 | |
... | ... | @@ -100,6 +107,7 @@ private: |
100 | 107 | |
101 | 108 | FT_Fixed actualValue_;
|
102 | 109 | MMGXAxisInfo axisInfo_;
|
110 | + bool dirty_ = false;
|
|
103 | 111 | |
104 | 112 | void createLayout();
|
105 | 113 | void createConnections();
|
... | ... | @@ -1262,7 +1262,7 @@ |
1262 | 1262 | grDoneSurface( surface );
|
1263 | 1263 | grDoneDevices();
|
1264 | 1264 | |
1265 | - free ( multimaster );
|
|
1265 | + FT_Done_MM_Var ( library, multimaster );
|
|
1266 | 1266 | FT_Done_Face ( face );
|
1267 | 1267 | FT_Done_FreeType( library );
|
1268 | 1268 |