| 
							- /*
 -   ==============================================================================
 - 
 -    This file is part of the JUCE library.
 -    Copyright (c) 2022 - Raw Material Software Limited
 - 
 -    JUCE is an open source library subject to commercial or open-source
 -    licensing.
 - 
 -    By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 -    Agreement and JUCE Privacy Policy.
 - 
 -    End User License Agreement: www.juce.com/juce-7-licence
 -    Privacy Policy: www.juce.com/juce-privacy-policy
 - 
 -    Or: You may also use this code under the terms of the GPL v3 (see
 -    www.gnu.org/licenses).
 - 
 -    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 -    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 -    DISCLAIMED.
 - 
 -   ==============================================================================
 - */
 - 
 - namespace juce
 - {
 - 
 - struct DefaultFontNames
 - {
 -     DefaultFontNames()
 -         : defaultSans  ("sans"),
 -           defaultSerif ("serif"),
 -           defaultFixed ("monospace"),
 -           defaultFallback ("sans")
 -     {
 -     }
 - 
 -     String getRealFontName (const String& faceName) const
 -     {
 -         if (faceName == Font::getDefaultSansSerifFontName())    return defaultSans;
 -         if (faceName == Font::getDefaultSerifFontName())        return defaultSerif;
 -         if (faceName == Font::getDefaultMonospacedFontName())   return defaultFixed;
 - 
 -         return faceName;
 -     }
 - 
 -     String defaultSans, defaultSerif, defaultFixed, defaultFallback;
 - };
 - 
 - Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font)
 - {
 -     static DefaultFontNames defaultNames;
 - 
 -     Font f (font);
 -     f.setTypefaceName (defaultNames.getRealFontName (font.getTypefaceName()));
 -     return Typeface::createSystemTypefaceFor (f);
 - }
 - 
 - //==============================================================================
 - #if JUCE_USE_FREETYPE
 - 
 - StringArray FTTypefaceList::getDefaultFontDirectories()
 - {
 -     return StringArray ("/system/fonts");
 - }
 - 
 - Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font)
 - {
 -     return new FreeTypeTypeface (font);
 - }
 - 
 - void Typeface::scanFolderForFonts (const File& folder)
 - {
 -     FTTypefaceList::getInstance()->scanFontPaths (StringArray (folder.getFullPathName()));
 - }
 - 
 - StringArray Font::findAllTypefaceNames()
 - {
 -     return FTTypefaceList::getInstance()->findAllFamilyNames();
 - }
 - 
 - StringArray Font::findAllTypefaceStyles (const String& family)
 - {
 -     return FTTypefaceList::getInstance()->findAllTypefaceStyles (family);
 - }
 - 
 - bool TextLayout::createNativeLayout (const AttributedString&)
 - {
 -     return false;
 - }
 - 
 - #else
 - 
 - //==============================================================================
 - #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
 -  STATICMETHOD (create,          "create",           "(Ljava/lang/String;I)Landroid/graphics/Typeface;") \
 -  STATICMETHOD (createFromFile,  "createFromFile",   "(Ljava/lang/String;)Landroid/graphics/Typeface;") \
 -  STATICMETHOD (createFromAsset, "createFromAsset",  "(Landroid/content/res/AssetManager;Ljava/lang/String;)Landroid/graphics/Typeface;")
 - 
 -  DECLARE_JNI_CLASS (TypefaceClass, "android/graphics/Typeface")
 - #undef JNI_CLASS_MEMBERS
 - 
 - #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
 -  METHOD (constructor,          "<init>",           "()V") \
 -  METHOD (computeBounds,        "computeBounds",     "(Landroid/graphics/RectF;Z)V")
 - 
 -  DECLARE_JNI_CLASS (AndroidPath, "android/graphics/Path")
 - #undef JNI_CLASS_MEMBERS
 - 
 - #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
 -  METHOD (constructor,   "<init>",   "()V") \
 -  FIELD  (left,           "left",     "F") \
 -  FIELD  (right,          "right",    "F") \
 -  FIELD  (top,            "top",      "F") \
 -  FIELD  (bottom,         "bottom",   "F") \
 -  METHOD (roundOut,       "roundOut", "(Landroid/graphics/Rect;)V")
 - 
 - DECLARE_JNI_CLASS (AndroidRectF, "android/graphics/RectF")
 - #undef JNI_CLASS_MEMBERS
 - 
 - #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
 -  STATICMETHOD (getInstance, "getInstance", "(Ljava/lang/String;)Ljava/security/MessageDigest;") \
 -  METHOD       (update,      "update",      "([B)V") \
 -  METHOD       (digest,      "digest",      "()[B")
 - DECLARE_JNI_CLASS (JavaMessageDigest, "java/security/MessageDigest")
 - #undef JNI_CLASS_MEMBERS
 - 
 - //==============================================================================
 - StringArray Font::findAllTypefaceNames()
 - {
 -     StringArray results;
 - 
 -     for (auto& f : File ("/system/fonts").findChildFiles (File::findFiles, false, "*.ttf"))
 -         results.addIfNotAlreadyThere (f.getFileNameWithoutExtension().upToLastOccurrenceOf ("-", false, false));
 - 
 -     return results;
 - }
 - 
 - StringArray Font::findAllTypefaceStyles (const String& family)
 - {
 -     StringArray results ("Regular");
 - 
 -     for (auto& f : File ("/system/fonts").findChildFiles (File::findFiles, false, family + "-*.ttf"))
 -         results.addIfNotAlreadyThere (f.getFileNameWithoutExtension().fromLastOccurrenceOf ("-", false, false));
 - 
 -     return results;
 - }
 - 
 - const float referenceFontSize = 256.0f;
 - const float referenceFontToUnits = 1.0f / referenceFontSize;
 - 
 - //==============================================================================
 - class AndroidTypeface final : public Typeface
 - {
 - public:
 -     AndroidTypeface (const Font& font)
 -         : Typeface (font.getTypefaceName(), font.getTypefaceStyle()),
 -           ascent (0), descent (0), heightToPointsFactor (1.0f)
 -     {
 -         JNIEnv* const env = getEnv();
 - 
 -         // First check whether there's an embedded asset with this font name:
 -         typeface = GlobalRef (getTypefaceFromAsset (name));
 - 
 -         if (typeface.get() == nullptr)
 -         {
 -             const bool isBold   = style.contains ("Bold");
 -             const bool isItalic = style.contains ("Italic");
 - 
 -             File fontFile (getFontFile (name, style));
 - 
 -             if (! fontFile.exists())
 -                 fontFile = findFontFile (name, isBold, isItalic);
 - 
 -             if (fontFile.exists())
 -                 typeface = GlobalRef (LocalRef<jobject> (env->CallStaticObjectMethod (TypefaceClass, TypefaceClass.createFromFile,
 -                                                                                       javaString (fontFile.getFullPathName()).get())));
 -             else
 -                 typeface = GlobalRef (LocalRef<jobject> (env->CallStaticObjectMethod (TypefaceClass, TypefaceClass.create,
 -                                                                                       javaString (getName()).get(),
 -                                                                                       (isBold ? 1 : 0) + (isItalic ? 2 : 0))));
 -         }
 - 
 -         initialise (env);
 -     }
 - 
 -     AndroidTypeface (const void* data, size_t size)
 -         : Typeface (String (static_cast<uint64> (reinterpret_cast<uintptr_t> (data))), String())
 -     {
 -         auto* env = getEnv();
 -         auto cacheFile = getCacheFileForData (data, size);
 - 
 -         typeface = GlobalRef (LocalRef<jobject> (env->CallStaticObjectMethod (TypefaceClass, TypefaceClass.createFromFile,
 -                                                                               javaString (cacheFile.getFullPathName()).get())));
 - 
 -         initialise (env);
 -     }
 - 
 -     void initialise (JNIEnv* const env)
 -     {
 -         rect = GlobalRef (LocalRef<jobject> (env->NewObject (AndroidRect, AndroidRect.constructor, 0, 0, 0, 0)));
 - 
 -         paint = GlobalRef (GraphicsHelpers::createPaint (Graphics::highResamplingQuality));
 -         const LocalRef<jobject> ignored (paint.callObjectMethod (AndroidPaint.setTypeface, typeface.get()));
 - 
 -         charArray = GlobalRef (LocalRef<jobject> ((jobject) env->NewCharArray (2)));
 - 
 -         paint.callVoidMethod (AndroidPaint.setTextSize, referenceFontSize);
 - 
 -         const float fullAscent = std::abs (paint.callFloatMethod (AndroidPaint.ascent));
 -         const float fullDescent = paint.callFloatMethod (AndroidPaint.descent);
 -         const float totalHeight = fullAscent + fullDescent;
 - 
 -         ascent  = fullAscent / totalHeight;
 -         descent = fullDescent / totalHeight;
 -         heightToPointsFactor = referenceFontSize / totalHeight;
 -     }
 - 
 -     float getAscent() const override                 { return ascent; }
 -     float getDescent() const override                { return descent; }
 -     float getHeightToPointsFactor() const override   { return heightToPointsFactor; }
 - 
 -     float getStringWidth (const String& text) override
 -     {
 -         JNIEnv* env = getEnv();
 -         auto numChars = CharPointer_UTF16::getBytesRequiredFor (text.getCharPointer());
 -         jfloatArray widths = env->NewFloatArray ((int) numChars);
 - 
 -         const int numDone = paint.callIntMethod (AndroidPaint.getTextWidths, javaString (text).get(), widths);
 - 
 -         HeapBlock<jfloat> localWidths (static_cast<size_t> (numDone));
 -         env->GetFloatArrayRegion (widths, 0, numDone, localWidths);
 -         env->DeleteLocalRef (widths);
 - 
 -         float x = 0;
 - 
 -         for (int i = 0; i < numDone; ++i)
 -             x += localWidths[i];
 - 
 -         return x * referenceFontToUnits;
 -     }
 - 
 -     void getGlyphPositions (const String& text, Array<int>& glyphs, Array<float>& xOffsets) override
 -     {
 -         JNIEnv* env = getEnv();
 -         auto numChars = CharPointer_UTF16::getBytesRequiredFor (text.getCharPointer());
 -         jfloatArray widths = env->NewFloatArray ((int) numChars);
 - 
 -         const int numDone = paint.callIntMethod (AndroidPaint.getTextWidths, javaString (text).get(), widths);
 - 
 -         HeapBlock<jfloat> localWidths (static_cast<size_t> (numDone));
 -         env->GetFloatArrayRegion (widths, 0, numDone, localWidths);
 -         env->DeleteLocalRef (widths);
 - 
 -         auto s = text.getCharPointer();
 - 
 -         xOffsets.add (0);
 -         float x = 0;
 - 
 -         for (int i = 0; i < numDone; ++i)
 -         {
 -             const float local = localWidths[i];
 - 
 -             // Android uses jchar (UTF-16) characters
 -             jchar ch = (jchar) s.getAndAdvance();
 - 
 -             // Android has no proper glyph support, so we have to do
 -             // a hacky workaround for ligature detection
 - 
 -            #if JUCE_STRING_UTF_TYPE <= 16
 -             static_assert (sizeof (int) >= (sizeof (jchar) * 2), "Unable store two java chars in one glyph");
 - 
 -             // if the width of this glyph is zero inside the string but has
 -             // a width on it's own, then it's probably due to ligature
 -             if (local == 0.0f && glyphs.size() > 0 && getStringWidth (String (ch)) > 0.0f)
 -             {
 -                 // modify the previous glyph
 -                 int& glyphNumber = glyphs.getReference (glyphs.size() - 1);
 - 
 -                 // make sure this is not a three character ligature
 -                 if (glyphNumber < std::numeric_limits<jchar>::max())
 -                 {
 -                     const unsigned int previousGlyph
 -                         = static_cast<unsigned int> (glyphNumber) & ((1U << (sizeof (jchar) * 8U)) - 1U);
 -                     const unsigned int thisGlyph
 -                         = static_cast<unsigned int> (ch)          & ((1U << (sizeof (jchar) * 8U)) - 1U);
 - 
 -                     glyphNumber = static_cast<int> ((thisGlyph << (sizeof (jchar) * 8U)) | previousGlyph);
 -                     ch = 0;
 -                 }
 -             }
 -            #endif
 - 
 -             glyphs.add ((int) ch);
 -             x += local;
 -             xOffsets.add (x * referenceFontToUnits);
 -         }
 -     }
 - 
 -     bool getOutlineForGlyph (int /*glyphNumber*/, Path& /*destPath*/) override
 -     {
 -         return false;
 -     }
 - 
 -     EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform& t, float /*fontHeight*/) override
 -     {
 -        #if JUCE_STRING_UTF_TYPE <= 16
 -         static_assert (sizeof (int) >= (sizeof (jchar) * 2), "Unable store two jni chars in one int");
 - 
 -         // glyphNumber of zero is used to indicate that the last character was a ligature
 -         if (glyphNumber == 0) return nullptr;
 - 
 -         jchar ch1 = (static_cast<unsigned int> (glyphNumber) >> 0)                     & ((1U << (sizeof (jchar) * 8U)) - 1U);
 -         jchar ch2 = (static_cast<unsigned int> (glyphNumber) >> (sizeof (jchar) * 8U)) & ((1U << (sizeof (jchar) * 8U)) - 1U);
 -        #else
 -         jchar ch1 = glyphNumber, ch2 = 0;
 -        #endif
 -         Rectangle<int> bounds;
 -         auto* env = getEnv();
 - 
 -         {
 -             LocalRef<jobject> matrix (GraphicsHelpers::createMatrix (env, AffineTransform::scale (referenceFontToUnits).followedBy (t)));
 - 
 -             jboolean isCopy;
 -             auto* buffer = env->GetCharArrayElements ((jcharArray) charArray.get(), &isCopy);
 - 
 -             buffer[0] = ch1; buffer[1] = ch2;
 -             env->ReleaseCharArrayElements ((jcharArray) charArray.get(), buffer, 0);
 - 
 -             LocalRef<jobject> path (env->NewObject (AndroidPath, AndroidPath.constructor));
 -             LocalRef<jobject> boundsF (env->NewObject (AndroidRectF, AndroidRectF.constructor));
 - 
 - 
 -             env->CallVoidMethod (paint.get(), AndroidPaint.getCharsPath, charArray.get(), 0, (ch2 != 0 ? 2 : 1), 0.0f, 0.0f, path.get());
 - 
 -             env->CallVoidMethod (path.get(), AndroidPath.computeBounds, boundsF.get(), 1);
 - 
 -             env->CallBooleanMethod (matrix.get(), AndroidMatrix.mapRect, boundsF.get());
 - 
 -             env->CallVoidMethod (boundsF.get(), AndroidRectF.roundOut, rect.get());
 - 
 -             bounds = Rectangle<int>::leftTopRightBottom (env->GetIntField (rect.get(), AndroidRect.left) - 1,
 -                                                          env->GetIntField (rect.get(), AndroidRect.top),
 -                                                          env->GetIntField (rect.get(), AndroidRect.right) + 1,
 -                                                          env->GetIntField (rect.get(), AndroidRect.bottom));
 - 
 -             auto w = bounds.getWidth();
 -             auto h = jmax (1, bounds.getHeight());
 - 
 -             LocalRef<jobject> bitmapConfig (env->CallStaticObjectMethod (AndroidBitmapConfig, AndroidBitmapConfig.valueOf, javaString ("ARGB_8888").get()));
 -             LocalRef<jobject> bitmap (env->CallStaticObjectMethod (AndroidBitmap, AndroidBitmap.createBitmap, w, h, bitmapConfig.get()));
 -             LocalRef<jobject> canvas (env->NewObject (AndroidCanvas, AndroidCanvas.create, bitmap.get()));
 - 
 -             env->CallBooleanMethod (matrix.get(), AndroidMatrix.postTranslate, (float) -bounds.getX(), (float) -bounds.getY());
 -             env->CallVoidMethod (canvas.get(), AndroidCanvas.setMatrix, matrix.get());
 -             env->CallVoidMethod (canvas.get(), AndroidCanvas.drawPath, path.get(), paint.get());
 - 
 -             int requiredRenderArraySize = w * h;
 -             if (requiredRenderArraySize > lastCachedRenderArraySize)
 -             {
 -                 cachedRenderArray = GlobalRef (LocalRef<jobject> ((jobject) env->NewIntArray (requiredRenderArraySize)));
 -                 lastCachedRenderArraySize = requiredRenderArraySize;
 -             }
 - 
 -             env->CallVoidMethod (bitmap.get(), AndroidBitmap.getPixels, cachedRenderArray.get(), 0, w, 0, 0, w, h);
 -             env->CallVoidMethod (bitmap.get(), AndroidBitmap.recycle);
 -         }
 - 
 -         EdgeTable* et = nullptr;
 - 
 -         if (! bounds.isEmpty())
 -         {
 -             et = new EdgeTable (bounds);
 - 
 -             jint* const maskDataElements = env->GetIntArrayElements ((jintArray) cachedRenderArray.get(), nullptr);
 -             const jint* mask = maskDataElements;
 - 
 -             for (int y = bounds.getY(); y < bounds.getBottom(); ++y)
 -             {
 -                #if JUCE_LITTLE_ENDIAN
 -                 const uint8* const lineBytes = ((const uint8*) mask) + 3;
 -                #else
 -                 const uint8* const lineBytes = (const uint8*) mask;
 -                #endif
 - 
 -                 et->clipLineToMask (bounds.getX(), y, lineBytes, 4, bounds.getWidth());
 -                 mask += bounds.getWidth();
 -             }
 - 
 -             env->ReleaseIntArrayElements ((jintArray) cachedRenderArray.get(), maskDataElements, 0);
 -         }
 - 
 -         return et;
 -     }
 - 
 -     GlobalRef typeface, paint, rect, charArray, cachedRenderArray;
 -     float ascent, descent, heightToPointsFactor;
 -     int lastCachedRenderArraySize = -1;
 - 
 - private:
 -     static File findFontFile (const String& family,
 -                               const bool bold, const bool italic)
 -     {
 -         File file;
 - 
 -         if (bold || italic)
 -         {
 -             String suffix;
 -             if (bold)   suffix = "Bold";
 -             if (italic) suffix << "Italic";
 - 
 -             file = getFontFile (family, suffix);
 - 
 -             if (file.exists())
 -                 return file;
 -         }
 - 
 -         file = getFontFile (family, "Regular");
 - 
 -         if (! file.exists())
 -             file = getFontFile (family, String());
 - 
 -         return file;
 -     }
 - 
 -     static File getFontFile (const String& family, const String& fontStyle)
 -     {
 -         String path ("/system/fonts/" + family);
 - 
 -         if (fontStyle.isNotEmpty())
 -             path << '-' << fontStyle;
 - 
 -         return File (path + ".ttf");
 -     }
 - 
 -     static LocalRef<jobject> getTypefaceFromAsset (const String& typefaceName)
 -     {
 -         auto* env = getEnv();
 - 
 -         LocalRef<jobject> assetManager (env->CallObjectMethod (getAppContext().get(), AndroidContext.getAssets));
 - 
 -         if (assetManager == nullptr)
 -             return LocalRef<jobject>();
 - 
 -         auto assetTypeface = env->CallStaticObjectMethod (TypefaceClass, TypefaceClass.createFromAsset, assetManager.get(),
 -                                                           javaString ("fonts/" + typefaceName).get());
 - 
 -         // this may throw
 -         if (env->ExceptionCheck() != 0)
 -         {
 -             env->ExceptionClear();
 -             return LocalRef<jobject>();
 -         }
 - 
 -         return LocalRef<jobject> (assetTypeface);
 -     }
 - 
 -     static File getCacheDirectory()
 -     {
 -         static File result = []()
 -         {
 -             auto appContext = getAppContext();
 - 
 -             if (appContext != nullptr)
 -             {
 -                 auto* env = getEnv();
 - 
 -                 LocalRef<jobject> cacheFile (env->CallObjectMethod (appContext.get(), AndroidContext.getCacheDir));
 -                 LocalRef<jstring> jPath ((jstring) env->CallObjectMethod (cacheFile.get(), JavaFile.getAbsolutePath));
 - 
 -                 return File (juceString (env, jPath.get()));
 -             }
 - 
 -             jassertfalse;
 -             return File();
 -         }();
 - 
 -         return result;
 -     }
 - 
 -     static HashMap<String, File>& getInMemoryFontCache()
 -     {
 -         static HashMap<String, File> cache;
 -         return cache;
 -     }
 - 
 -     static File getCacheFileForData (const void* data, size_t size)
 -     {
 -         static CriticalSection cs;
 -         JNIEnv* const env = getEnv();
 - 
 -         String key;
 -         {
 -             LocalRef<jobject> digest (env->CallStaticObjectMethod (JavaMessageDigest, JavaMessageDigest.getInstance, javaString ("MD5").get()));
 -             LocalRef<jbyteArray> bytes (env->NewByteArray ((int) size));
 - 
 -             jboolean ignore;
 -             auto* jbytes = env->GetByteArrayElements (bytes.get(), &ignore);
 -             memcpy (jbytes, data, size);
 -             env->ReleaseByteArrayElements (bytes.get(), jbytes, 0);
 - 
 -             env->CallVoidMethod (digest.get(), JavaMessageDigest.update, bytes.get());
 -             LocalRef<jbyteArray> result ((jbyteArray) env->CallObjectMethod (digest.get(), JavaMessageDigest.digest));
 - 
 -             auto* md5Bytes = env->GetByteArrayElements (result.get(), &ignore);
 -             key = String::toHexString (md5Bytes, env->GetArrayLength (result.get()), 0);
 -             env->ReleaseByteArrayElements (result.get(), md5Bytes, 0);
 -         }
 - 
 -         ScopedLock lock (cs);
 -         auto& mapEntry = getInMemoryFontCache().getReference (key);
 - 
 -         if (mapEntry == File())
 -         {
 -             mapEntry = getCacheDirectory().getChildFile ("bindata_" + key);
 -             mapEntry.replaceWithData (data, size);
 -         }
 - 
 -         return mapEntry;
 -     }
 - 
 -     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AndroidTypeface)
 - };
 - 
 - //==============================================================================
 - Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font)
 - {
 -     return new AndroidTypeface (font);
 - }
 - 
 - Typeface::Ptr Typeface::createSystemTypefaceFor (const void* data, size_t size)
 - {
 -     return new AndroidTypeface (data, size);
 - }
 - 
 - void Typeface::scanFolderForFonts (const File&)
 - {
 -     jassertfalse; // not available unless using FreeType
 - }
 - 
 - bool TextLayout::createNativeLayout (const AttributedString&)
 - {
 -     return false;
 - }
 - 
 - #endif
 - 
 - } // namespace juce
 
 
  |