/* * Copyright 2019 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "modules/skottie/src/text/TextValue.h" #include "modules/skottie/src/SkottieJson.h" #include "modules/skottie/src/SkottiePriv.h" #include "modules/skottie/src/SkottieValue.h" namespace skottie::internal { bool Parse(const skjson::Value& jv, const internal::AnimationBuilder& abuilder, TextValue* v) { const skjson::ObjectValue* jtxt = jv; if (!jtxt) { return false; } const skjson::StringValue* font_name = (*jtxt)["f"]; const skjson::StringValue* text = (*jtxt)["t"]; const skjson::NumberValue* text_size = (*jtxt)["s"]; const skjson::NumberValue* line_height = (*jtxt)["lh"]; if (!font_name || !text || !text_size || !line_height) { return false; } const auto* font = abuilder.findFont(SkString(font_name->begin(), font_name->size())); if (!font) { abuilder.log(Logger::Level::kError, nullptr, "Unknown font: \"%s\".", font_name->begin()); return false; } v->fText.set(text->begin(), text->size()); v->fTextSize = **text_size; v->fLineHeight = **line_height; v->fTypeface = font->fTypeface; v->fAscent = font->fAscentPct * -0.01f * v->fTextSize; // negative ascent per SkFontMetrics v->fLineShift = ParseDefault((*jtxt)["ls"], 0.0f); static constexpr SkTextUtils::Align gAlignMap[] = { SkTextUtils::kLeft_Align, // 'j': 0 SkTextUtils::kRight_Align, // 'j': 1 SkTextUtils::kCenter_Align // 'j': 2 }; v->fHAlign = gAlignMap[std::min(ParseDefault((*jtxt)["j"], 0), SK_ARRAY_COUNT(gAlignMap) - 1)]; // Optional text box size. if (const skjson::ArrayValue* jsz = (*jtxt)["sz"]) { if (jsz->size() == 2) { v->fBox.setWH(ParseDefault((*jsz)[0], 0), ParseDefault((*jsz)[1], 0)); } } // Optional text box position. if (const skjson::ArrayValue* jps = (*jtxt)["ps"]) { if (jps->size() == 2) { v->fBox.offset(ParseDefault((*jps)[0], 0), ParseDefault((*jps)[1], 0)); } } static constexpr Shaper::ResizePolicy gResizeMap[] = { Shaper::ResizePolicy::kNone, // 'rs': 0 Shaper::ResizePolicy::kScaleToFit, // 'rs': 1 Shaper::ResizePolicy::kDownscaleToFit, // 'rs': 2 }; // TODO: remove "sk_rs" support after migrating clients. v->fResize = gResizeMap[std::min(std::max(ParseDefault((*jtxt)[ "rs"], 0), ParseDefault((*jtxt)["sk_rs"], 0)), SK_ARRAY_COUNT(gResizeMap) - 1)]; // Optional min/max font size (used when aute-resizing) v->fMinTextSize = ParseDefault((*jtxt)["mf"], 0.0f); v->fMaxTextSize = ParseDefault((*jtxt)["xf"], std::numeric_limits::max()); // At the moment, BM uses the paragraph box to discriminate point mode vs. paragraph mode. v->fLineBreak = v->fBox.isEmpty() ? Shaper::LinebreakPolicy::kExplicit : Shaper::LinebreakPolicy::kParagraph; // Optional explicit text mode. // N.b.: this is not being exported by BM, only used for testing. auto text_mode = ParseDefault((*jtxt)["m"], -1); if (text_mode >= 0) { // Explicit text mode. v->fLineBreak = (text_mode == 0) ? Shaper::LinebreakPolicy::kExplicit // 'm': 0 -> point text : Shaper::LinebreakPolicy::kParagraph; // 'm': 1 -> paragraph text } // Optional capitalization. static constexpr Shaper::Capitalization gCapMap[] = { Shaper::Capitalization::kNone, // 'ca': 0 Shaper::Capitalization::kUpperCase, // 'ca': 1 }; v->fCapitalization = gCapMap[std::min(ParseDefault((*jtxt)["ca"], 0), SK_ARRAY_COUNT(gCapMap) - 1)]; // In point mode, the text is baseline-aligned. v->fVAlign = v->fBox.isEmpty() ? Shaper::VAlign::kTopBaseline : Shaper::VAlign::kTop; static constexpr Shaper::VAlign gVAlignMap[] = { Shaper::VAlign::kVisualTop, // 'vj': 0 Shaper::VAlign::kVisualCenter, // 'vj': 1 Shaper::VAlign::kVisualBottom, // 'vj': 2 }; size_t vj; if (skottie::Parse((*jtxt)[ "vj"], &vj) || skottie::Parse((*jtxt)["sk_vj"], &vj)) { // TODO: remove after migrating clients. if (vj < SK_ARRAY_COUNT(gVAlignMap)) { v->fVAlign = gVAlignMap[vj]; } else { // Legacy sk_vj values. // TODO: remove after clients update. switch (vj) { case 3: // 'sk_vj': 3 -> kVisualCenter/kScaleToFit v->fVAlign = Shaper::VAlign::kVisualCenter; v->fResize = Shaper::ResizePolicy::kScaleToFit; break; case 4: // 'sk_vj': 4 -> kVisualCenter/kDownscaleToFit v->fVAlign = Shaper::VAlign::kVisualCenter; v->fResize = Shaper::ResizePolicy::kDownscaleToFit; break; default: abuilder.log(Logger::Level::kWarning, nullptr, "Ignoring unknown 'vj' value: %zu", vj); break; } } } const auto& parse_color = [] (const skjson::ArrayValue* jcolor, SkColor* c) { if (!jcolor) { return false; } VectorValue color_vec; if (!skottie::Parse(*jcolor, &color_vec)) { return false; } *c = color_vec; return true; }; v->fHasFill = parse_color((*jtxt)["fc"], &v->fFillColor); v->fHasStroke = parse_color((*jtxt)["sc"], &v->fStrokeColor); if (v->fHasStroke) { v->fStrokeWidth = ParseDefault((*jtxt)["sw"], 1.0f); v->fPaintOrder = ParseDefault((*jtxt)["of"], true) ? TextPaintOrder::kFillStroke : TextPaintOrder::kStrokeFill; } return true; } } // namespace skottie::internal