Commits:
-
7cf77639
by Charlie Jiang
at 2022-09-12T01:37:18+08:00
[ftinspect] Add transformation and scaling info to the composite glyph view.
Note: Untested since no font with non-1.0 scale subglyphs is found.
* src/ftinspect/engine/fontinfo.cpp, src/ftinspect/engine/fontinfo.hpp:
Fetch transformation and scaling info from the font.
* src/ftinspect/models/fontinfomodels.cpp,
src/ftinspect/models/fontinfomodels.hpp:
Change the "Position" column to "Position and Transformation".
Display the transformation info.
4 changed files:
Changes:
src/ftinspect/engine/fontinfo.cpp
... |
... |
@@ -571,6 +571,21 @@ SFNTTableInfo::getForAll(Engine* engine, |
571
|
571
|
}
|
572
|
572
|
|
573
|
573
|
|
|
574
|
+
|
|
575
|
+FT_UInt16
|
|
576
|
+readUInt16(void* ptr)
|
|
577
|
+{
|
|
578
|
+ return bigEndianToNative(*static_cast<uint16_t*>(ptr));
|
|
579
|
+}
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+double
|
|
583
|
+readF2Dot14(void* ptr)
|
|
584
|
+{
|
|
585
|
+ return static_cast<int16_t>(readUInt16(ptr)) / 16384.0;
|
|
586
|
+}
|
|
587
|
+
|
|
588
|
+
|
574
|
589
|
void
|
575
|
590
|
CompositeGlyphInfo::get(Engine* engine,
|
576
|
591
|
std::vector<CompositeGlyphInfo>& list)
|
... |
... |
@@ -641,7 +656,7 @@ CompositeGlyphInfo::get(Engine* engine, |
641
|
656
|
if (loc + 16 > end)
|
642
|
657
|
continue;
|
643
|
658
|
|
644
|
|
- auto len = static_cast<FT_Int16>(buffer[loc] << 8 | buffer[loc + 1]);
|
|
659
|
+ auto len = static_cast<FT_Int16>(readUInt16(buffer + loc));
|
645
|
660
|
loc += 10; // skip header
|
646
|
661
|
if (len >= 0) // not a composite one
|
647
|
662
|
continue;
|
... |
... |
@@ -652,18 +667,18 @@ CompositeGlyphInfo::get(Engine* engine, |
652
|
667
|
{
|
653
|
668
|
if (loc + 6 > end)
|
654
|
669
|
break;
|
655
|
|
- auto flags = static_cast<FT_UInt16>(buffer[loc] << 8 | buffer[loc + 1]);
|
|
670
|
+ auto flags = readUInt16(buffer + loc);
|
656
|
671
|
loc += 2;
|
657
|
|
- auto index = static_cast<FT_UInt16>(buffer[loc] << 8 | buffer[loc + 1]);
|
|
672
|
+ auto index = readUInt16(buffer + loc);
|
658
|
673
|
loc += 2;
|
659
|
674
|
FT_Int16 arg1, arg2;
|
660
|
675
|
|
661
|
676
|
// https://docs.microsoft.com/en-us/typography/opentype/spec/glyf#composite-glyph-description
|
662
|
677
|
if (flags & 0x0001)
|
663
|
678
|
{
|
664
|
|
- arg1 = static_cast<FT_Int16>(buffer[loc] << 8 | buffer[loc + 1]);
|
|
679
|
+ arg1 = static_cast<FT_Int16>(readUInt16(buffer + loc));
|
665
|
680
|
loc += 2;
|
666
|
|
- arg2 = static_cast<FT_Int16>(buffer[loc] << 8 | buffer[loc + 1]);
|
|
681
|
+ arg2 = static_cast<FT_Int16>(readUInt16(buffer + loc));
|
667
|
682
|
loc += 2;
|
668
|
683
|
}
|
669
|
684
|
else
|
... |
... |
@@ -672,17 +687,43 @@ CompositeGlyphInfo::get(Engine* engine, |
672
|
687
|
arg2 = buffer[loc + 1];
|
673
|
688
|
loc += 2;
|
674
|
689
|
}
|
|
690
|
+
|
|
691
|
+ subglyphs.emplace_back(index, flags,
|
|
692
|
+ flags & 0x0002 ? SubGlyph::PT_Offset
|
|
693
|
+ : SubGlyph::PT_Align,
|
|
694
|
+ std::pair<short, short>(arg1, arg2),
|
|
695
|
+ (flags & 0x0800) != 0);
|
|
696
|
+ // TODO: Use "Default behavior" when neither SCALED_COMPONENT_OFFSET
|
|
697
|
+ // and UNSCALED_COMPONENT_OFFSET are set.
|
|
698
|
+
|
|
699
|
+ auto& glyph = subglyphs.back();
|
675
|
700
|
if (flags & 0x0008)
|
|
701
|
+ {
|
|
702
|
+ glyph.transformationType = SubGlyph::TT_UniformScale;
|
|
703
|
+ glyph.transformation[0] = readF2Dot14(buffer + loc);
|
676
|
704
|
loc += 2;
|
|
705
|
+ }
|
677
|
706
|
else if (flags & 0x0040)
|
|
707
|
+ {
|
|
708
|
+ glyph.transformationType = SubGlyph::TT_XYScale;
|
|
709
|
+ glyph.transformation[0] = readF2Dot14(buffer + loc);
|
|
710
|
+ glyph.transformation[1] = readF2Dot14(buffer + loc + 2);
|
678
|
711
|
loc += 4;
|
|
712
|
+ }
|
679
|
713
|
else if (flags & 0x0080)
|
|
714
|
+ {
|
|
715
|
+ glyph.transformationType = SubGlyph::TT_Matrix;
|
|
716
|
+ glyph.transformation[0] = readF2Dot14(buffer + loc);
|
|
717
|
+ glyph.transformation[1] = readF2Dot14(buffer + loc + 2);
|
|
718
|
+ glyph.transformation[2] = readF2Dot14(buffer + loc + 4);
|
|
719
|
+ glyph.transformation[3] = readF2Dot14(buffer + loc + 6);
|
680
|
720
|
loc += 8;
|
681
|
|
-
|
682
|
|
- subglyphs.emplace_back(index, flags,
|
683
|
|
- flags & 0x0002 ? SubGlyph::PT_Offset
|
684
|
|
- : SubGlyph::PT_Align,
|
685
|
|
- std::pair<short, short>(arg1, arg2));
|
|
721
|
+ }
|
|
722
|
+ else
|
|
723
|
+ {
|
|
724
|
+ glyph.transformationType = SubGlyph::TT_UniformScale;
|
|
725
|
+ glyph.transformation[0] = 1.0;
|
|
726
|
+ }
|
686
|
727
|
|
687
|
728
|
if (!(flags & 0x0020))
|
688
|
729
|
break;
|
src/ftinspect/engine/fontinfo.hpp
... |
... |
@@ -5,6 +5,7 @@ |
5
|
5
|
#pragma once
|
6
|
6
|
|
7
|
7
|
#include <set>
|
|
8
|
+#include <cstring>
|
8
|
9
|
#include <QDateTime>
|
9
|
10
|
#include <QByteArray>
|
10
|
11
|
#include <QString>
|
... |
... |
@@ -248,27 +249,44 @@ struct CompositeGlyphInfo |
248
|
249
|
{
|
249
|
250
|
struct SubGlyph
|
250
|
251
|
{
|
251
|
|
- enum PositionType
|
|
252
|
+ enum PositionType : uint8_t
|
252
|
253
|
{
|
253
|
254
|
PT_Offset, // Child's points are added with a xy-offset
|
254
|
255
|
PT_Align // One point of the child is aligned with one point of the parent
|
255
|
256
|
};
|
|
257
|
+ enum TransformationType : uint8_t
|
|
258
|
+ {
|
|
259
|
+ TT_UniformScale, // uniform scale for x- and y-axis
|
|
260
|
+ TT_XYScale, // separate scale for x- and y-axis
|
|
261
|
+ TT_Matrix // 2x2 matrix
|
|
262
|
+ };
|
256
|
263
|
unsigned short index;
|
257
|
264
|
unsigned short flag;
|
258
|
265
|
PositionType positionType;
|
259
|
266
|
// For PT_Offset: <deltaX, deltaY>
|
260
|
267
|
// For PT_Align: <childPoint, parentPoint>
|
261
|
268
|
std::pair<short, short> position;
|
|
269
|
+ bool positionScaled;
|
|
270
|
+ TransformationType transformationType;
|
|
271
|
+ // For TT_UniformScale: transformation[0] is the scale
|
|
272
|
+ // For TT_XYScale: transformation[0]: x-scale; transformation[1]: y-scale
|
|
273
|
+ // For TT_Matrix: transformation is layouted as
|
|
274
|
+ // [xscale, scale01, scale10, yscale]
|
|
275
|
+ double transformation[4];
|
|
276
|
+
|
262
|
277
|
|
263
|
278
|
SubGlyph(unsigned short index,
|
264
|
279
|
unsigned short flag,
|
265
|
280
|
PositionType positionType,
|
266
|
|
- std::pair<short, short> position)
|
|
281
|
+ std::pair<short, short> position,
|
|
282
|
+ bool positionScaled)
|
267
|
283
|
: index(index),
|
268
|
284
|
flag(flag),
|
269
|
285
|
positionType(positionType),
|
270
|
|
- position(std::move(position))
|
271
|
|
- { }
|
|
286
|
+ position(std::move(position)),
|
|
287
|
+ positionScaled(positionScaled)
|
|
288
|
+ {
|
|
289
|
+ }
|
272
|
290
|
|
273
|
291
|
|
274
|
292
|
friend bool
|
... |
... |
@@ -278,7 +296,11 @@ struct CompositeGlyphInfo |
278
|
296
|
return lhs.index == rhs.index
|
279
|
297
|
&& lhs.flag == rhs.flag
|
280
|
298
|
&& lhs.positionType == rhs.positionType
|
281
|
|
- && lhs.position == rhs.position;
|
|
299
|
+ && lhs.position == rhs.position
|
|
300
|
+ && lhs.positionScaled == rhs.positionScaled
|
|
301
|
+ && lhs.transformationType == rhs.transformationType
|
|
302
|
+ && !std::memcmp(lhs.transformation, rhs.transformation,
|
|
303
|
+ 4 * sizeof(double));
|
282
|
304
|
}
|
283
|
305
|
|
284
|
306
|
|
src/ftinspect/models/fontinfomodels.cpp
... |
... |
@@ -571,6 +571,53 @@ CompositeGlyphsInfoModel::parent(const QModelIndex& child) const |
571
|
571
|
}
|
572
|
572
|
|
573
|
573
|
|
|
574
|
+QString
|
|
575
|
+generatePositionTransformationText(CompositeGlyphInfo::SubGlyph const& info)
|
|
576
|
+{
|
|
577
|
+ QString result;
|
|
578
|
+ switch (info.transformationType)
|
|
579
|
+ {
|
|
580
|
+ case CompositeGlyphInfo::SubGlyph::TT_UniformScale:
|
|
581
|
+ result += QString("scale: %1, ")
|
|
582
|
+ .arg(QString::number(info.transformation[0]));
|
|
583
|
+ break;
|
|
584
|
+ case CompositeGlyphInfo::SubGlyph::TT_XYScale:
|
|
585
|
+ result += QString("xy scale: (%1, %2), ")
|
|
586
|
+ .arg(QString::number(info.transformation[0]),
|
|
587
|
+ QString::number(info.transformation[1]));
|
|
588
|
+ break;
|
|
589
|
+ case CompositeGlyphInfo::SubGlyph::TT_Matrix:
|
|
590
|
+ result += QString("2x2 scale: [%1, %2; %3, %4], ")
|
|
591
|
+ .arg(QString::number(info.transformation[0]),
|
|
592
|
+ QString::number(info.transformation[1]),
|
|
593
|
+ QString::number(info.transformation[2]),
|
|
594
|
+ QString::number(info.transformation[3]));
|
|
595
|
+ break;
|
|
596
|
+ }
|
|
597
|
+
|
|
598
|
+ auto pos = info.position;
|
|
599
|
+ switch (info.positionType)
|
|
600
|
+ {
|
|
601
|
+ case CompositeGlyphInfo::SubGlyph::PT_Offset:
|
|
602
|
+ if (info.positionScaled)
|
|
603
|
+ result += QString("scaled offset: (%1, %2)")
|
|
604
|
+ .arg(QString::number(info.position.first),
|
|
605
|
+ QString::number(info.position.second));
|
|
606
|
+ else
|
|
607
|
+ result += QString("offset: (%1, %2)")
|
|
608
|
+ .arg(QString::number(info.position.first),
|
|
609
|
+ QString::number(info.position.second));
|
|
610
|
+ break;
|
|
611
|
+ case CompositeGlyphInfo::SubGlyph::PT_Align:
|
|
612
|
+ result += QString("anchor points: %1 (parent) <- %2 (this glyph)")
|
|
613
|
+ .arg(QString::number(info.position.first),
|
|
614
|
+ QString::number(info.position.second));
|
|
615
|
+ break;
|
|
616
|
+ }
|
|
617
|
+ return result;
|
|
618
|
+}
|
|
619
|
+
|
|
620
|
+
|
574
|
621
|
QVariant
|
575
|
622
|
CompositeGlyphsInfoModel::data(const QModelIndex& index,
|
576
|
623
|
int role) const
|
... |
... |
@@ -584,25 +631,6 @@ CompositeGlyphsInfoModel::data(const QModelIndex& index, |
584
|
631
|
auto& n = nodes_[id];
|
585
|
632
|
auto glyphIdx = n.glyphIndex;
|
586
|
633
|
|
587
|
|
- if (role == Qt::ToolTipRole && index.column() == CGIM_Position)
|
588
|
|
- {
|
589
|
|
- if (!n.subGlyphInfo)
|
590
|
|
- return {};
|
591
|
|
- auto pos = n.subGlyphInfo->position;
|
592
|
|
- switch (n.subGlyphInfo->positionType)
|
593
|
|
- {
|
594
|
|
- case CompositeGlyphInfo::SubGlyph::PT_Offset:
|
595
|
|
- return QString("Add a offset (%1, %2) to the subglyph's points")
|
596
|
|
- .arg(pos.first)
|
597
|
|
- .arg(pos.second);
|
598
|
|
- case CompositeGlyphInfo::SubGlyph::PT_Align:
|
599
|
|
- return QString("Align parent's point %1 to subglyph's point %2")
|
600
|
|
- .arg(pos.first)
|
601
|
|
- .arg(pos.second);
|
602
|
|
- }
|
603
|
|
- return {};
|
604
|
|
- }
|
605
|
|
-
|
606
|
634
|
if (role == Qt::DecorationRole && index.column() == CGIM_Glyph)
|
607
|
635
|
{
|
608
|
636
|
auto glyphIndex = n.glyphIndex;
|
... |
... |
@@ -628,19 +656,12 @@ CompositeGlyphsInfoModel::data(const QModelIndex& index, |
628
|
656
|
case CGIM_Flag:
|
629
|
657
|
if (!n.subGlyphInfo)
|
630
|
658
|
return {};
|
631
|
|
- return QString::number(n.subGlyphInfo->flag, 16).rightJustified(4, '0');
|
632
|
|
- case CGIM_Position:
|
|
659
|
+ return QString("0x%1").arg(n.subGlyphInfo->flag, 4, 16, QLatin1Char('0'));
|
|
660
|
+ case CGIM_PositionTransformation:
|
633
|
661
|
{
|
634
|
662
|
if (!n.subGlyphInfo)
|
635
|
663
|
return {};
|
636
|
|
- auto pos = n.subGlyphInfo->position;
|
637
|
|
- switch (n.subGlyphInfo->positionType)
|
638
|
|
- {
|
639
|
|
- case CompositeGlyphInfo::SubGlyph::PT_Offset:
|
640
|
|
- return QString("Offset (%1, %2)").arg(pos.first).arg(pos.second);
|
641
|
|
- case CompositeGlyphInfo::SubGlyph::PT_Align:
|
642
|
|
- return QString("Align %1 -> %2").arg(pos.first).arg(pos.second);
|
643
|
|
- }
|
|
664
|
+ return generatePositionTransformationText(*n.subGlyphInfo);
|
644
|
665
|
}
|
645
|
666
|
default:;
|
646
|
667
|
}
|
... |
... |
@@ -665,8 +686,8 @@ CompositeGlyphsInfoModel::headerData(int section, |
665
|
686
|
return tr("Glyph");
|
666
|
687
|
case CGIM_Flag:
|
667
|
688
|
return tr("Flags");
|
668
|
|
- case CGIM_Position:
|
669
|
|
- return tr("Position");
|
|
689
|
+ case CGIM_PositionTransformation:
|
|
690
|
+ return tr("Position and Transformation");
|
670
|
691
|
default:;
|
671
|
692
|
}
|
672
|
693
|
return {};
|
src/ftinspect/models/fontinfomodels.hpp
... |
... |
@@ -259,9 +259,9 @@ public: |
259
|
259
|
|
260
|
260
|
enum Columns : int
|
261
|
261
|
{
|
262
|
|
- CGIM_Glyph = 0, // TODO: transformation, scale? consider more flags?
|
|
262
|
+ CGIM_Glyph = 0,
|
263
|
263
|
CGIM_Flag = 1,
|
264
|
|
- CGIM_Position = 2,
|
|
264
|
+ CGIM_PositionTransformation = 2,
|
265
|
265
|
CGIM_Max
|
266
|
266
|
};
|
267
|
267
|
|
|