sync docs up with tip of tree

Also check in work in progress for blend modes,
round rects, and a placeholder for pictures.

One minor include change to add a parameter name
for SkBlendMode function.

TBR=reed@google.com
R=caryclark@google.com

Docs-Preview: https://skia.org/?cl=134200
Bug: skia:6898
Change-Id: I5d2a9221d61edb32d9c7edbb3193401605b2b513
Reviewed-on: https://skia-review.googlesource.com/134200
Reviewed-by: Cary Clark <caryclark@google.com>
Reviewed-by: Cary Clark <caryclark@skia.org>
Commit-Queue: Cary Clark <caryclark@skia.org>
Auto-Submit: Cary Clark <caryclark@skia.org>
diff --git a/tools/bookmaker/bookmaker.cpp b/tools/bookmaker/bookmaker.cpp
index 947c077..58c73e6 100644
--- a/tools/bookmaker/bookmaker.cpp
+++ b/tools/bookmaker/bookmaker.cpp
@@ -133,16 +133,15 @@
 , { "Code",         MarkType::kCode,         R_F, E_N, M_CSST | M_E | M_MD | M(Typedef) }
 , { "",             MarkType::kColumn,       R_Y, E_N, M(Row) }
 , { "",             MarkType::kComment,      R_N, E_N, 0 }
-, { "Const",        MarkType::kConst,        R_Y, E_O, M_E | M_ST  }
+, { "Const",        MarkType::kConst,        R_Y, E_O, M_E | M_CSST  }
 , { "Define",       MarkType::kDefine,       R_O, E_Y, M_ST }
-, { "DefinedBy",    MarkType::kDefinedBy,    R_N, E_N, M(Method) }
 , { "Deprecated",   MarkType::kDeprecated,   R_Y, E_N, M_CS | M_MDCM | M_E }
 , { "Description",  MarkType::kDescription,  R_Y, E_N, M(Example) | M(NoExample) }
 , { "Details",      MarkType::kDetails,      R_N, E_N, M(Const) }
 , { "Duration",     MarkType::kDuration,     R_N, E_N, M(Example) | M(NoExample) }
 , { "Enum",         MarkType::kEnum,         R_Y, E_O, M_CSST }
 , { "EnumClass",    MarkType::kEnumClass,    R_Y, E_O, M_CSST }
-, { "Example",      MarkType::kExample,      R_O, E_N, M_CSST | M_E | M_MD }
+, { "Example",      MarkType::kExample,      R_O, E_N, M_CSST | M_E | M_MD | M(Const) }
 , { "Experimental", MarkType::kExperimental, R_Y, E_N, M_CS | M_MDCM | M_E }
 , { "External",     MarkType::kExternal,     R_Y, E_N, 0 }
 , { "File",         MarkType::kFile,         R_Y, E_N, M(Topic) }
@@ -165,12 +164,12 @@
 , { "NoJustify",    MarkType::kNoJustify,    R_N, E_N, M(Const) | M(Member) }
 , { "Outdent",      MarkType::kOutdent,      R_N, E_N, M(Code) }
 , { "Param",        MarkType::kParam,        R_Y, E_N, M(Method) | M(Define) }
-, { "PhraseDef",    MarkType::kPhraseDef,    R_Y, E_N, M(Subtopic) }
+, { "PhraseDef",    MarkType::kPhraseDef,    R_Y, E_N, M_ST }
 , { "",             MarkType::kPhraseParam,  R_Y, E_N, 0 }
 , { "",             MarkType::kPhraseRef,    R_N, E_N, 0 }
 , { "Platform",     MarkType::kPlatform,     R_N, E_N, M(Example) | M(NoExample) }
 , { "Populate",     MarkType::kPopulate,     R_N, E_N, M(Subtopic) }
-, { "Private",      MarkType::kPrivate,      R_Y, E_N, M_CSST | M_MDCM | M_E }
+, { "Private",      MarkType::kPrivate,      R_N, E_N, M_CSST | M_MDCM | M_E }
 , { "Return",       MarkType::kReturn,       R_Y, E_N, M(Method) }
 , { "",             MarkType::kRow,          R_Y, E_N, M(Table) | M(List) }
 , { "SeeAlso",      MarkType::kSeeAlso,      R_C, E_N, M_CSST | M_E | M_MD | M(Typedef) }
@@ -401,29 +400,6 @@
             }
             break;
         // these types are children of parents, but are not in named maps
-        case MarkType::kDefinedBy: {
-            string prefixed(fRoot->fName);
-            const char* start = fChar;
-            string name(start, this->trimmedBracketEnd(fMC) - start);
-            prefixed += "::" + name;
-            this->skipToEndBracket(fMC);
-            const auto leafIter = fRoot->fLeaves.find(prefixed);
-            if (fRoot->fLeaves.end() != leafIter) {
-                this->reportError<bool>("DefinedBy already defined");
-            }
-            definition = &fRoot->fLeaves[prefixed];
-            definition->fParent = fParent;
-            definition->fStart = defStart;
-            definition->fContentStart = start;
-            definition->fName = name;
-            definition->fFiddle = Definition::NormalizedName(name);
-            definition->fContentEnd = fChar;
-            this->skipToEndBracket('\n');
-            definition->fTerminator = fChar;
-            definition->fMarkType = markType;
-            definition->fLineCount = fLineCount;
-            fParent->fChildren.push_back(definition);
-            } break;
         case MarkType::kDescription:
         case MarkType::kStdOut:
         // may be one-liner
@@ -1761,6 +1737,17 @@
     if (!paren) {
         return this->reportError<string>("missing method name and reference");
     }
+    {
+        TextParserSave endCheck(this);
+        while (end < fEnd && !this->strnchr(')', end)) {
+            fChar = end + 1;
+            end = this->lineEnd();
+        }
+        if (end >= fEnd) {
+            return this->reportError<string>("missing method end paren");
+        }
+        endCheck.restore();
+    }
     const char* nameStart = paren;
     char ch;
     bool expectOperator = false;
@@ -1803,10 +1790,13 @@
     }
     const Definition* parent = this->parentSpace();
     if (parent && parent->fName.length() > 0) {
-        if (parent->fName == name) {
+        size_t parentNameIndex = parent->fName.rfind(':');
+        parentNameIndex = string::npos == parentNameIndex ? 0 : parentNameIndex + 1;
+        string parentName = parent->fName.substr(parentNameIndex);
+        if (parentName == name) {
             isConstructor = true;
         } else if ('~' == name[0]) {
-            if (parent->fName != name.substr(1)) {
+            if (parentName != name.substr(1)) {
                  return this->reportError<string>("expected destructor");
             }
             isConstructor = true;
@@ -2220,7 +2210,6 @@
         case MarkType::kAlias:
         case MarkType::kAnchor:
         case MarkType::kBug:  // fixme: expect number
-        case MarkType::kDefinedBy:
         case MarkType::kDeprecated:
         case MarkType::kDetails:
         case MarkType::kDuration:
diff --git a/tools/bookmaker/bookmaker.h b/tools/bookmaker/bookmaker.h
index 3616e20..24d5617 100644
--- a/tools/bookmaker/bookmaker.h
+++ b/tools/bookmaker/bookmaker.h
@@ -94,7 +94,6 @@
     kComment,
     kConst,
     kDefine,
-    kDefinedBy,
     kDeprecated,
     kDescription,
     kDetails,  // used by #Const to specify #Subtopic details with examples and so on
@@ -113,7 +112,7 @@
 	kIn,
     kLegend,
 	kLine,
-    kLink,
+    kLink,     // used internally by #Anchor
     kList,
     kLiteral,  // don't lookup hyperlinks, do substitution, etc
     kMarkChar,
@@ -906,8 +905,7 @@
     const Definition* iRootParent() const {
         const Definition* test = fParent;
         while (test) {
-            if (Type::kKeyWord == test->fType
-                    && (KeyWord::kClass == test->fKeyWord || KeyWord::kStruct == test->fKeyWord)) {
+            if (KeyWord::kClass == test->fKeyWord || KeyWord::kStruct == test->fKeyWord) {
                 return test;
             }
             test = test->fParent;
@@ -1158,9 +1156,6 @@
     }
 
     void indentOut() {
-        if (fIndent < 4) {  // FIXME: hack until I can debug again
-            return;
-        }
         SkASSERT(fIndent >= 4);
         SkASSERT(fIndentStack.back().fIndent == fIndent);
         fIndent -= 4;
@@ -1569,6 +1564,7 @@
     void dumpClassTokens(IClassDefinition& classDef);
     void dumpComment(const Definition& );
     void dumpCommonTail(const Definition& );
+    void dumpConst(const Definition& , string className);
     void dumpDefine(const Definition& );
     void dumpEnum(const Definition& , string name);
     bool dumpGlobals();
@@ -1580,10 +1576,12 @@
     Definition* findIncludeObject(const Definition& includeDef, MarkType markType,
                                   string typeName);
     static KeyWord FindKey(const char* start, const char* end);
+    Bracket grandParentBracket() const;
     bool isClone(const Definition& token);
     bool isConstructor(const Definition& token, string className);
     bool isInternalName(const Definition& token);
     bool isOperator(const Definition& token);
+    Definition* parentBracket(Definition* parent) const;
     bool parseChar();
     bool parseComment(string filename, const char* start, const char* end, int lineCount,
             Definition* markupDef);
@@ -1611,6 +1609,10 @@
     bool parseUnion();
 
     void popBracket() {
+        if (Definition::Type::kKeyWord == fParent->fType
+                && KeyWord::kTypename == fParent->fKeyWord) {
+            this->popObject();
+        }
         SkASSERT(Definition::Type::kBracket == fParent->fType);
         this->popObject();
         Bracket bracket = this->topBracket();
@@ -1657,13 +1659,7 @@
         fInCharCommentString = fInChar || fInComment || fInString;
     }
 
-    Bracket topBracket() const {
-        Definition* parent = fParent;
-        while (parent && Definition::Type::kBracket != parent->fType) {
-            parent = parent->fParent;
-        }
-        return parent ? parent->fBracket : Bracket::kNone;
-    }
+    Bracket topBracket() const;
 
     template <typename T>
     string uniqueName(const unordered_map<string, T>& m, string typeName) {
@@ -1803,6 +1799,16 @@
         this->writeTag(tagType, tagID.c_str());
     }
 
+    void writeTagTable(string tagType, string body) {
+        this->writeTag(tagType.c_str());
+        this->writeSpace(1);
+        this->writeString("#");
+        this->writeSpace(1);
+        this->writeString(body);
+        this->writeSpace(1);
+        this->writeString("##");
+    }
+
 protected:
     static void ValidateKeyWords();
 
diff --git a/tools/bookmaker/includeParser.cpp b/tools/bookmaker/includeParser.cpp
index d6aa447..fdaf0cc 100644
--- a/tools/bookmaker/includeParser.cpp
+++ b/tools/bookmaker/includeParser.cpp
@@ -136,6 +136,12 @@
         return true;
     }
     KeyWord keyWord = FindKey(fIncludeWord, fChar);
+    if (KeyWord::kClass == keyWord || KeyWord::kStruct == keyWord) {
+        Bracket bracket = this->topBracket();
+        if (Bracket::kParen == bracket) {
+            return true;
+        }
+    }
     if (KeyWord::kNone != keyWord) {
         if (KeyProperty::kPreprocessor != kKeyWords[(int) keyWord].fProperty) {
             this->addKeyword(keyWord);
@@ -360,9 +366,6 @@
                     }
                     if (def->crossCheck2(token)) {
                         def->fVisited = true;
-                        if (MarkType::kDefinedBy == def->fMarkType) {
-                            def->fParent->fVisited = true;
-                        }
                         if (token.fDeprecated && !def->fDeprecated) {
                             fFailed = !def->reportError<bool>("expect bmh to be marked deprecated");
                         }
@@ -556,6 +559,9 @@
             this->writeBlockSeparator();
         }
         switch (token.fMarkType) {
+            case MarkType::kConst:
+                this->dumpConst(token, classDef.fName);
+            break;
             case MarkType::kEnum:
             case MarkType::kEnumClass:
                 this->dumpEnum(token, token.fName);
@@ -753,6 +759,15 @@
     this->lf(2);
 }
 
+void IncludeParser::dumpConst(const Definition& token, string className) {
+    this->writeTag("Const");
+    this->writeSpace();
+    this->writeString(token.fName);
+    this->writeTagTable("Line", "incomplete");
+    this->lf(2);
+    this->dumpComment(token);
+}
+
 void IncludeParser::dumpDefine(const Definition& token) {
     this->writeTag("Define", token.fName);
     this->lf(2);
@@ -859,6 +874,10 @@
 }
 
 bool IncludeParser::dumpGlobals() {
+    if (fIDefineMap.empty() && fIFunctionMap.empty() && fIEnumMap.empty() && fITemplateMap.empty()
+            && fITypedefMap.empty() && fIUnionMap.empty()) {
+        return true;
+    }
     size_t lastBSlash = fFileName.rfind('\\');
     size_t lastSlash = fFileName.rfind('/');
     size_t lastDotH = fFileName.rfind(".h");
@@ -881,16 +900,10 @@
     string topicName = globalsName.length() > 2 && isupper(globalsName[2]) &&
         ("Sk" == prefixName || "Gr" == prefixName) ? globalsName.substr(2) : globalsName;
     this->writeTagNoLF("Topic", topicName);
-    this->writeTag("Alias", topicName + "_Reference");
+    this->writeEndTag("Alias", topicName + "_Reference");
     this->lf(2);
     this->writeTag("Subtopic", "Overview");
-    fIndent += 4;
-    this->writeTag("Subtopic", "Subtopic");
-    fIndent += 4;
     this->writeTag("Populate");
-    fIndent -= 4;
-    this->writeEndTag();
-    fIndent -= 4;
     this->writeEndTag();
     this->lf(2);
     if (!fIDefineMap.empty()) {
@@ -1037,7 +1050,7 @@
             name = name.substr(0, constPos) + "_const";
         }
     }
-    this->writeString(name);
+    this->writeBlock((int) name.size(), name.c_str());
     string inType;
     if (this->isConstructor(token, className)) {
         inType = "Constructor";
@@ -1047,13 +1060,7 @@
         inType = "incomplete";
     }
     this->writeTag("In", inType);
-    this->writeTag("Line");
-    this->writeSpace(1);
-    this->writeString("#");
-    this->writeSpace(1);
-    this->writeString("incomplete");
-    this->writeSpace(1);
-    this->writeString("##");
+    this->writeTagTable("Line", "incomplete");
     this->lf(2);
     this->dumpComment(token);
 }
@@ -1097,7 +1104,7 @@
     string topicName = skClassName.length() > 2 && isupper(skClassName[2]) &&
         ("Sk" == prefixName || "Gr" == prefixName) ? skClassName.substr(2) : skClassName;
     this->writeTagNoLF("Topic", topicName);
-    this->writeTag("Alias", topicName + "_Reference");
+    this->writeEndTag("Alias", topicName + "_Reference");
     this->lf(2);
     auto& classMap = fIClassMap[skClassName];
     SkASSERT(KeyWord::kClass == classMap.fKeyWord || KeyWord::kStruct == classMap.fKeyWord);
@@ -1118,6 +1125,7 @@
     bool hasConstructor = false;
     bool hasMember = false;
     bool hasOperator = false;
+    bool hasStruct = false;
     for (const auto& oneClass : fIClassMap) {
         if (skClassName + "::" != oneClass.first.substr(0, skClassName.length() + 2)) {
             continue;
@@ -1125,6 +1133,13 @@
         hasClass = true;
         break;
     }
+    for (const auto& oneStruct : fIStructMap) {
+        if (skClassName + "::" != oneStruct.first.substr(0, skClassName.length() + 2)) {
+            continue;
+        }
+        hasStruct = true;
+        break;
+    }
     for (const auto& token : classMap.fTokens) {
         if (Definition::Type::kMark != token.fType || MarkType::kMethod != token.fMarkType) {
             continue;
@@ -1146,18 +1161,12 @@
         hasMember = true;
     }
     this->writeTag("Subtopic", "Overview");
-    fIndent += 4;
-    this->writeTag("Subtopic", "Subtopic");
-    fIndent += 4;
     this->writeTag("Populate");
-    fIndent -= 4;
-    this->writeEndTag();
-    fIndent -= 4;
     this->writeEndTag();
     this->lf(2);
 
     if (hasClass) {
-        this->writeTag("Subtopic", "Class_or_Struct");
+        this->writeTag("Subtopic", "Class");
         this->writeTag("Populate");
         this->writeEndTag();
         this->lf(2);
@@ -1186,6 +1195,12 @@
         this->writeEndTag();
         this->lf(2);
     }
+    if (hasStruct) {
+        this->writeTag("Subtopic", "Struct");
+        this->writeTag("Populate");
+        this->writeEndTag();
+        this->lf(2);
+    }
     for (auto& oneEnum : fIEnumMap) {
         this->writeBlockSeparator();
         this->dumpEnum(*oneEnum.second, oneEnum.first);
@@ -1210,6 +1225,7 @@
         SkASSERT(KeyWord::kClass == keyword || KeyWord::kStruct == keyword);
         const char* containerType = KeyWord::kClass == keyword ? "Class" : "Struct";
         this->writeTag(containerType, innerName);
+        this->writeTagTable("Line", "incomplete");
         this->lf(2);
         this->writeTag("Code");
         this->writeEndTag("ToDo", "fill this in manually");
@@ -1307,6 +1323,19 @@
     return &markupDef;
 }
 
+Definition* IncludeParser::parentBracket(Definition* parent) const {
+    while (parent && Definition::Type::kBracket != parent->fType) {
+        parent = parent->fParent;
+    }
+    return parent;
+}
+
+Bracket IncludeParser::grandParentBracket() const {
+    Definition* parent = parentBracket(fParent);
+    parent = parentBracket(parent ? parent->fParent : nullptr);
+    return parent ? parent->fBracket : Bracket::kNone;
+}
+
 // caller just returns, so report error here
 bool IncludeParser::parseClass(Definition* includeDef, IsStruct isStruct) {
     SkASSERT(includeDef->fTokens.size() > 0);
@@ -1663,7 +1692,7 @@
         parser.skipToNonName();
         string memberName(memberStart, parser.fChar);
         if (parser.eof() || !parser.skipWhiteSpace()) {
-            return this->reportError<bool>("enum member must end with comma 1");
+            return parser.reportError<bool>("enum member must end with comma 1");
         }
         const char* dataStart = parser.fChar;
         if ('=' == parser.peek()) {
@@ -1674,7 +1703,7 @@
             continue;
         }
         if (parser.eof() || ',' != parser.peek()) {
-            return this->reportError<bool>("enum member must end with comma 2");
+            return parser.reportError<bool>("enum member must end with comma 2");
         }
         dataEnd = parser.fChar;
         const char* start = parser.anyOf("/\n");
@@ -1867,7 +1896,7 @@
         }
         break;
     }
-    tokenIter->fName = nameStr;
+    tokenIter->fName = nameStr;     // simple token stream, OK if name is duplicate
     tokenIter->fMarkType = MarkType::kMethod;
     tokenIter->fPrivate = string::npos != nameStr.find("::");
     auto testIter = child->fParent->fTokens.begin();
@@ -1934,15 +1963,19 @@
     markupDef->fTokens.emplace_back(MarkType::kMethod, start, end, tokenIter->fLineCount,
             markupDef, '\0');
     Definition* markupChild = &markupDef->fTokens.back();
-    // do find instead -- I wonder if there is a way to prevent this in c++
-    IClassDefinition& classDef = fIClassMap[markupDef->fName];
-    SkASSERT(classDef.fStart);
-    string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
-    markupChild->fName = uniqueName;
-    if (!this->findComments(*child, markupChild)) {
-        return false;
+    // TODO: I wonder if there is a way to prevent looking up by operator[] (creating empty) ?
+    {
+        auto mapIter = fIClassMap.find(markupDef->fName);
+        SkASSERT(fIClassMap.end() != mapIter);
+        IClassDefinition& classDef = mapIter->second;
+        SkASSERT(classDef.fStart);
+        string uniqueName = this->uniqueName(classDef.fMethods, nameStr);
+        markupChild->fName = uniqueName;
+        if (!this->findComments(*child, markupChild)) {
+            return false;
+        }
+        classDef.fMethods[uniqueName] = markupChild;
     }
-    classDef.fMethods[uniqueName] = markupChild;
     return true;
 }
 
@@ -2324,6 +2357,8 @@
                 break;
             }
             this->pushBracket(Bracket::kAngle);
+            // this angle bracket may be an operator or may be a bracket
+            // wait for balancing close angle, if any, to decide
             break;
         case ')':
         case ']':
@@ -2340,8 +2375,9 @@
                 }
             }
             bool popBraceParent = fInBrace == fParent;
-            if ((')' == test ? Bracket::kParen :
-                    ']' == test ? Bracket::kSquare : Bracket::kBrace) == this->topBracket()) {
+            Bracket match = ')' == test ? Bracket::kParen :
+                    ']' == test ? Bracket::kSquare : Bracket::kBrace;
+            if (match == this->topBracket()) {
                 this->popBracket();
                 if (!fInFunction) {
                     fInFunction = ')' == test;
@@ -2350,6 +2386,10 @@
                 }
             } else if (')' == test && Bracket::kDebugCode == this->topBracket()) {
                 this->popBracket();
+            } else if (Bracket::kAngle == this->topBracket()
+                    && match == this->grandParentBracket()) {
+                this->popBracket();
+                this->popBracket();
             } else {
                 return reportError<bool>("malformed close bracket");
             }
@@ -2374,6 +2414,7 @@
                 break;
             }
             if (Bracket::kAngle == this->topBracket()) {
+                // looks like angle pair are braces, not operators
                 this->popBracket();
             } else {
                 return reportError<bool>("malformed close angle bracket");
@@ -2707,7 +2748,12 @@
     if (fIStructMap.end() != fIStructMap.find(root)) {
         return true;
     }
-    // TODO incomplete: probably need to look in other places for class-less includes like SkColor.h
+    if (fIEnumMap.end() != fIEnumMap.find(root)) {
+        return true;
+    }
+    if (fIFunctionMap.end() != fIFunctionMap.find(root)) {
+        return true;
+    }
     return false;
 }
 
@@ -2743,3 +2789,8 @@
     SkString fullName = SkOSPath::Join(docs, baseName.c_str());
     remove(fullName.c_str());
 }
+
+Bracket IncludeParser::topBracket() const {
+    Definition* parent = this->parentBracket(fParent);
+    return parent ? parent->fBracket : Bracket::kNone;
+}
diff --git a/tools/bookmaker/includeWriter.cpp b/tools/bookmaker/includeWriter.cpp
index c029421..cd67be0 100644
--- a/tools/bookmaker/includeWriter.cpp
+++ b/tools/bookmaker/includeWriter.cpp
@@ -158,9 +158,6 @@
                 }
                 commentStart = prop->fTerminator;
                 } break;
-            case MarkType::kDefinedBy:
-                commentStart = prop->fTerminator;
-                break;
             case MarkType::kBug: {
                 if (fReturnOnWrite) {
                     return true;
@@ -1437,7 +1434,7 @@
                         }
                         ++alternate;
                         string alternateMethod = methodName + '_' + to_string(alternate);
-                        clonedMethod = this->findMethod(alternateMethod, root);
+                       clonedMethod = this->findMethod(alternateMethod, root);
                     } while (clonedMethod);
                     if (!clonedMethod) {
                         return child.reportError<bool>("cloned method not found");
@@ -1489,9 +1486,6 @@
                 continue;
             }
             method = this->findMethod(methodName + "()", root);
-            if (method && MarkType::kDefinedBy == method->fMarkType) {
-                method = method->fParent;
-            }
             if (method) {
                 if (method->fCloned) {
                     clonedMethod = method;
@@ -1547,7 +1541,9 @@
                 methodName = child.fName;
             } else {
                 methodName = root->fName + "::" + child.fName;
-                inConstructor = root->fName == child.fName;
+                size_t lastName = root->fName.rfind(':');
+                lastName = string::npos == lastName ? 0 : lastName + 1;
+                inConstructor = root->fName.substr(lastName) == child.fName;
                 method = root->find(methodName, RootDefinition::AllowParens::kNo);
             }
             fContinuation = child.fContentEnd;
diff --git a/tools/bookmaker/mdOut.cpp b/tools/bookmaker/mdOut.cpp
index 76df5da..653ca5e 100644
--- a/tools/bookmaker/mdOut.cpp
+++ b/tools/bookmaker/mdOut.cpp
@@ -835,7 +835,8 @@
                 break;
             }
         }
-        SkASSERT(csParent || string::npos == fRoot->fFileName.find("Sk"));
+        SkASSERT(csParent || string::npos == fRoot->fFileName.find("Sk")
+                || string::npos != fRoot->fFileName.find("SkBlendMode_Reference.bmh"));
     }
     return csParent;
 }
@@ -1370,8 +1371,6 @@
             this->htmlOut(anchorDef(def->fFiddle, "Define " + def->fName));
             this->lf(2);
             break;
-        case MarkType::kDefinedBy:
-            break;
         case MarkType::kDeprecated:
             this->writeString("Deprecated.");
             this->lf(2);
@@ -1444,6 +1443,10 @@
             break;
         case MarkType::kIllustration: {
             string illustName = "Illustrations_" + def->fParent->fFiddle;
+            string number = string(def->fContentStart, def->length());
+            if (number.length() && "1" != number) {
+                illustName += "_" + number;
+            }
             auto illustIter = fBmhParser.fTopicMap.find(illustName);
             SkASSERT(fBmhParser.fTopicMap.end() != illustIter);
             Definition* illustDef = illustIter->second;
@@ -2117,9 +2120,16 @@
 }
 
 void MdOut::subtopicOut(string name) {
-    Definition* csParent = this->csParent();
-    SkASSERT(csParent);
     const Definition* topicParent = fSubtopic ? fSubtopic->topicParent() : nullptr;
+    Definition* csParent = this->csParent();
+    if (!csParent) {
+        auto csIter = std::find_if(topicParent->fChildren.begin(), topicParent->fChildren.end(),
+                [](const Definition* def){ return MarkType::kEnum == def->fMarkType
+                || MarkType::kEnumClass == def->fMarkType; } );
+        SkASSERT(topicParent->fChildren.end() != csIter);
+        csParent = *csIter;
+    }
+    SkASSERT(csParent);
     this->lfAlways(1);
     if (fPopulators.end() != fPopulators.find(name)) {
         const SubtopicDescriptions& tableDescriptions = this->populator(name);
@@ -2148,6 +2158,11 @@
             continue;
         }
         size_t start = entry->fName.find_last_of("::");
+        if (MarkType::kConst == entry->fMarkType && entry->fParent
+                && MarkType::kEnumClass == entry->fParent->fMarkType
+                && string::npos != start && start > 1) {
+            start = entry->fName.substr(0, start - 1).rfind("::");
+        }
         string entryName = entry->fName.substr(string::npos == start ? 0 : start + 1);
         items[entryName] = entry;
     }
@@ -2199,5 +2214,5 @@
         }
     }
     FPRINTF("</table>");
-    this->lfAlways(1);
+    this->lf(2);
 }
diff --git a/tools/bookmaker/spellCheck.cpp b/tools/bookmaker/spellCheck.cpp
index cd02178..a65e2c1 100644
--- a/tools/bookmaker/spellCheck.cpp
+++ b/tools/bookmaker/spellCheck.cpp
@@ -172,8 +172,6 @@
         } break;
         case MarkType::kDefine:
             break;
-        case MarkType::kDefinedBy:
-            break;
         case MarkType::kDeprecated:
             break;
         case MarkType::kDescription: