Move SSD display text predicates to typed Kotlin

ober

fab8904ce71e1544c2b32658dd8a752f8225afd7

diff --git a/templates/ssd-review.ss b/templates/ssd-review.ss
index 5d8e24f..a5477f7 100644
--- a/templates/ssd-review.ss
+++ b/templates/ssd-review.ss
@@ -2297,7 +2297,7 @@
       (typed-library (com sfb ssdreview)
         (export normalizeOcr compact ocrTokenMatches
                 bankCompatible isPaPanelExample isShieldLabel
-                suppressedByRace shouldReplaceOcrLabel)
+                suppressedByRace shouldReplaceOcrLabel ocrTextPresent)
         (def (normalizeOcr (value : String)) : String
           (string-replace-regex
             (string-trim
@@ -2352,7 +2352,9 @@
                 (if (string-matches-regex? text "^\\d+\\s+boxes$")
                   #t
                   (and (equal? status "candidate")
-                       (string-starts-with? notes "Guess:")))))))))
+                       (string-starts-with? notes "Guess:")))))))
+        (def (ocrTextPresent (text : String)) : Bool
+          (not (string-blank? text)))))
 
     (typed-kotlin-file "com/sfb/ssdreview/OcrCandidateSelect.kt"
       (kotlin-imports (android content Context))
@@ -3869,6 +3871,7 @@
                 dialogCountValue editedGroupCountText
                 selectionIgnoredLoadingStatus
                 shouldShowFiringArc shouldFocusFiringArc
+                canvasLabelPresent
                 hasReplacementLabel hasResolvedLabel
                 packageUriText mediaStorePdfSelection
                 mediaStoreDisplayNameSort
@@ -4254,6 +4257,8 @@
                                    (boxTypeIsWeapon : Bool)) : Bool
           (and (shouldShowFiringArc firingArcText boxTypeIsWeapon)
                (string-blank? firingArcText)))
+        (def (canvasLabelPresent (label : String)) : Bool
+          (not (string-blank? label)))
         (def (hasReplacementLabel (replacement : String)) : Bool
           (not (string-blank? replacement)))
         (def (hasResolvedLabel (resolved : String)) : Bool
@@ -7125,7 +7130,7 @@
        "                    line.elements.forEach { element ->"
        "                        val box = element.boundingBox ?: line.boundingBox ?: block.boundingBox ?: return@forEach"
        "                        val text = element.text.trim()"
-       "                        if (text.isNotBlank()) {"
+       "                        if (ocrTextPresent(text)) {"
        "                            words.add(ocrWordFromBounds(text, box.left, box.top, box.right, box.bottom))"
        "                        }"
        "                    }"
@@ -7445,7 +7450,7 @@
        "        for (group in sess.groups) {"
        "            val selected = group.id == selectedGroupId"
        "            val label = displayGroupCanvasLabel(group, selected)"
-       "            if (label.isBlank()) continue"
+       "            if (!canvasLabelPresent(label)) continue"
        "            val center = floatArrayOf("
        "                (group.bbox[0] + group.bbox[2]) / 2f,"
        "                (group.bbox[1] + group.bbox[3]) / 2f"