Fix style parsing with better style.

SkFontStyle needs to be updated to take scalar weight and width, and
separate slant and italic into separate scalar values. Since there are
currently issues with serialization of SkFontStyle, start with the
serialization and deserialization before moving these changes up into
SkFontStyle and its users itself.

Bug: oss-fuzz:24962,skia:6844
Change-Id: Ic121d95fe1309ee04dbd6b1fe72d9b1d01b52258
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/313476
Reviewed-by: Herb Derby <herb@google.com>
Commit-Queue: Ben Wagner <bungeman@google.com>
diff --git a/src/core/SkFontDescriptor.cpp b/src/core/SkFontDescriptor.cpp
index 7445202..c8e94c60 100644
--- a/src/core/SkFontDescriptor.cpp
+++ b/src/core/SkFontDescriptor.cpp
@@ -12,16 +12,21 @@
 enum {
     kInvalid        = 0x00,
 
-    // these must match the sfnt 'name' enums
-    kFontFamilyName = 0x01,
-    kFullName       = 0x04,
-    kPostscriptName = 0x06,
+    // Related to a font request.
+    kFontFamilyName = 0x01, // int length, data[length]
+    kFullName       = 0x04, // int length, data[length]
+    kPostscriptName = 0x06, // int length, data[length]
+    kWeight         = 0x10, // scalar (1 - 1000)
+    kWidth          = 0x11, // scalar (percentage, 100 is 'normal')
+    kSlant          = 0x12, // scalar (cw angle, 14 is a normal right leaning oblique)
+    kItalic         = 0x13, // scalar (0 is Roman, 1 is fully Italic)
 
-    // These count backwards from 0xFF, so as not to collide with the SFNT
-    // defines for names in its 'name' table.
-    kFontVariation  = 0xFA,
-    kFontIndex      = 0xFD,
-    kSentinel       = 0xFF,
+    // Related to font data. Can also be used with a requested font.
+    kFontVariation  = 0xFA, // int count, (u32, scalar)[count]
+
+    // Related to font data.
+    kFontIndex      = 0xFD, // int
+    kSentinel       = 0xFF, // no data
 };
 
 SkFontDescriptor::SkFontDescriptor() { }
@@ -48,25 +53,45 @@
            stream->writePackedUInt(n);
 }
 
+static bool write_scalar(SkWStream* stream, SkScalar n, uint32_t id) {
+    return stream->writePackedUInt(id) &&
+           stream->writeScalar(n);
+}
+
 static size_t SK_WARN_UNUSED_RESULT read_id(SkStream* stream) {
     size_t i;
     if (!stream->readPackedUInt(&i)) { return kInvalid; }
     return i;
 }
 
-bool SkFontDescriptor::Deserialize(SkStream* stream, SkFontDescriptor* result) {
-    size_t styleBits;
-    if (!stream->readPackedUInt(&styleBits)) { return false; }
-    result->fStyle = SkFontStyle((styleBits >> 16) & 0xFFFF,
-                                 (styleBits >> 8 ) & 0xFF,
-                                 static_cast<SkFontStyle::Slant>(styleBits & 0xFF));
+static constexpr SkScalar usWidths[9] {
+    1, 2, 3, 4, 5, 6, 7, 8, 9
+};
+static constexpr SkScalar width_for_usWidth[0x10] = {
+    50,
+    50, 62.5, 75, 87.5, 100, 112.5, 125, 150, 200,
+    200, 200, 200, 200, 200, 200
+};
 
+bool SkFontDescriptor::Deserialize(SkStream* stream, SkFontDescriptor* result) {
     size_t coordinateCount;
     using CoordinateCountType = decltype(result->fCoordinateCount);
 
     size_t index;
     using CollectionIndexType = decltype(result->fCollectionIndex);
 
+    SkScalar weight = SkFontStyle::kNormal_Weight;
+    SkScalar width = SkFontStyle::kNormal_Width;
+    SkScalar slant = 0;
+    SkScalar italic = 0;
+
+    size_t styleBits;
+    if (!stream->readPackedUInt(&styleBits)) { return false; }
+    weight = ((styleBits >> 16) & 0xFFFF);
+    width  = ((styleBits >>  8) & 0x000F)[width_for_usWidth];
+    slant  = ((styleBits >>  0) & 0x000F) != SkFontStyle::kUpright_Slant ? 14 : 0;
+    italic = ((styleBits >>  0) & 0x000F) == SkFontStyle::kItalic_Slant ? 1 : 0;
+
     for (size_t id; (id = read_id(stream)) != kSentinel;) {
         switch (id) {
             case kFontFamilyName:
@@ -78,6 +103,18 @@
             case kPostscriptName:
                 if (!read_string(stream, &result->fPostscriptName)) { return false; }
                 break;
+            case kWeight:
+                if (!stream->readScalar(&weight)) { return false; }
+                break;
+            case kWidth:
+                if (!stream->readScalar(&width)) { return false; }
+                break;
+            case kSlant:
+                if (!stream->readScalar(&slant)) { return false; }
+                break;
+            case kItalic:
+                if (!stream->readScalar(&italic)) { return false; }
+                break;
             case kFontVariation:
                 if (!stream->readPackedUInt(&coordinateCount)) { return false; }
                 if (!SkTFitsIn<CoordinateCountType>(coordinateCount)) { return false; }
@@ -100,6 +137,12 @@
         }
     }
 
+    SkFontStyle::Slant slantEnum = SkFontStyle::kUpright_Slant;
+    if (slant != 0) { slantEnum = SkFontStyle::kOblique_Slant; }
+    if (0 < italic) { slantEnum = SkFontStyle::kItalic_Slant; }
+    int usWidth = SkScalarRoundToInt(SkScalarInterpFunc(width, &width_for_usWidth[1], usWidths, 9));
+    result->fStyle = SkFontStyle(SkScalarRoundToInt(weight), usWidth, slantEnum);
+
     size_t length;
     if (!stream->readPackedUInt(&length)) { return false; }
     if (length > 0) {
@@ -121,6 +164,11 @@
     write_string(stream, fFullName, kFullName);
     write_string(stream, fPostscriptName, kPostscriptName);
 
+    write_scalar(stream, fStyle.weight(), kWeight);
+    write_scalar(stream, fStyle.width()[width_for_usWidth], kWidth);
+    write_scalar(stream, fStyle.slant() == SkFontStyle::kUpright_Slant ? 0 : 14, kSlant);
+    write_scalar(stream, fStyle.slant() == SkFontStyle::kItalic_Slant ? 1 : 0, kItalic);
+
     if (fCollectionIndex) {
         write_uint(stream, fCollectionIndex, kFontIndex);
     }
diff --git a/tests/TypefaceTest.cpp b/tests/TypefaceTest.cpp
index 53d6412..f1be7b4 100644
--- a/tests/TypefaceTest.cpp
+++ b/tests/TypefaceTest.cpp
@@ -133,6 +133,10 @@
 
 DEF_TEST(FontDescriptorNegativeVariationSerialize, reporter) {
     SkFontDescriptor desc;
+    SkFontStyle style(2, 9, SkFontStyle::kOblique_Slant);
+    desc.setStyle(style);
+    const char postscriptName[] = "postscript";
+    desc.setPostscriptName(postscriptName);
     SkFontArguments::VariationPosition::Coordinate* variation = desc.setVariationCoordinates(1);
     variation[0] = { 0, -1.0f };
 
@@ -141,6 +145,8 @@
     SkFontDescriptor descD;
     SkFontDescriptor::Deserialize(stream.detachAsStream().get(), &descD);
 
+    REPORTER_ASSERT(reporter, descD.getStyle() == style);
+    REPORTER_ASSERT(reporter, 0 == strcmp(desc.getPostscriptName(), postscriptName));
     if (descD.getVariationCoordinateCount() != 1) {
         REPORT_FAILURE(reporter, "descD.getVariationCoordinateCount() != 1", SkString());
         return;