content: /*
   * Copyright (C) 2018 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package android.graphics.fonts;
  
  import android.annotation.IntRange;
  import android.annotation.NonNull;
  import android.text.FontConfig;
  
  import com.android.internal.util.Preconditions;
  
  import dalvik.annotation.optimization.CriticalNative;
  
  import libcore.util.NativeAllocationRegistry;
  
  import java.util.ArrayList;
  import java.util.HashSet;
  
  /**
   * A font family class can be used for creating Typeface.
   *
   * <p>
   * A font family is a bundle of fonts for drawing text in various styles.
   * For example, you can bundle regular style font and bold style font into a single font family,
   * then system will select the correct style font from family for drawing.
   *
   * <pre>
   *  FontFamily family = new FontFamily.Builder(new Font.Builder("regular.ttf").build())
   *      .addFont(new Font.Builder("bold.ttf").build()).build();
   *  Typeface typeface = new Typeface.Builder2(family).build();
   *
   *  SpannableStringBuilder ssb = new SpannableStringBuilder("Hello, World.");
   *  ssb.setSpan(new StyleSpan(Typeface.Bold), 6, 12, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
   *
   *  textView.setTypeface(typeface);
   *  textView.setText(ssb);
   * </pre>
   *
   * In this example, "Hello, " is drawn with "regular.ttf", and "World." is drawn with "bold.ttf".
   *
   * If there is no font exactly matches with the text style, the system will select the closest font.
   * </p>
   *
   */
  public final class FontFamily {
      private static final String TAG = "FontFamily";
  
      /**
       * A builder class for creating new FontFamily.
       */
      public static final class Builder {
          private static final NativeAllocationRegistry sFamilyRegistory =
                  NativeAllocationRegistry.createMalloced(FontFamily.class.getClassLoader(),
                      nGetReleaseNativeFamily());
  
          private final ArrayList<Font> mFonts = new ArrayList<>();
          private final HashSet<Integer> mStyleHashSet = new HashSet<>();
  
          /**
           * Constructs a builder.
           *
           * @param font a font
           */
          public Builder(@NonNull Font font) {
              Preconditions.checkNotNull(font, "font can not be null");
              mStyleHashSet.add(makeStyleIdentifier(font));
              mFonts.add(font);
          }
  
          /**
           * Adds different style font to the builder.
           *
           * System will select the font if the text style is closest to the font.
           * If the same style font is already added to the builder, this method will fail with
           * {@link IllegalArgumentException}.
           *
           * Note that system assumes all fonts bundled in FontFamily have the same coverage for the
           * code points. For example, regular style font and bold style font must have the same code
           * point coverage, otherwise some character may be shown as tofu.
           *
           * @param font a font
           * @return this builder
           */
          public @NonNull Builder addFont(@NonNull Font font) {
              Preconditions.checkNotNull(font, "font can not be null");
              if (!mStyleHashSet.add(makeStyleIdentifier(font))) {
                  throw new IllegalArgumentException(font + " has already been added");
              }
              mFonts.add(font);
              return this;
          }
  
          /**
           * Build the font family
           * @return a font family
           */
          public @NonNull FontFamily build() {
              return build("", FontConfig.Family.VARIANT_DEFAULT, true /* isCustomFallback */);
          }
  
          /** @hide */
          public @NonNull FontFamily build(@NonNull String langTags, int variant,
                  boolean isCustomFallback) {
              final long builderPtr = nInitBuilder();
              for (int i = 0; i < mFonts.size(); ++i) {
                  nAddFont(builderPtr, mFonts.get(i).getNativePtr());
              }
              final long ptr = nBuild(builderPtr, langTags, variant, isCustomFallback);
              final FontFamily family = new FontFamily(mFonts, ptr);
              sFamilyRegistory.registerNativeAllocation(family, ptr);
              return family;
          }
  
          private static int makeStyleIdentifier(@NonNull Font font) {
              return font.getStyle().getWeight() | (font.getStyle().getSlant()  << 16);
          }
  
          private static native long nInitBuilder();
          @CriticalNative
          private static native void nAddFont(long builderPtr, long fontPtr);
          private static native long nBuild(long builderPtr, String langTags, int variant,
                  boolean isCustomFallback);
          @CriticalNative
          private static native long nGetReleaseNativeFamily();
      }
  
      private final ArrayList<Font> mFonts;
      private final long mNativePtr;
  
      // Use Builder instead.
      private FontFamily(@NonNull ArrayList<Font> fonts, long ptr) {
          mFonts = fonts;
          mNativePtr = ptr;
      }
  
      /**
       * Returns a font
       *
       * @param index an index of the font
       * @return a registered font
       */
      public @NonNull Font getFont(@IntRange(from = 0) int index) {
          return mFonts.get(index);
      }
  
      /**
       * Returns the number of fonts in this FontFamily.
       *
       * @return the number of fonts registered in this family.
       */
      public @IntRange(from = 1) int getSize() {
          return mFonts.size();
      }
  
      /** @hide */
      public long getNativePtr() {
          return mNativePtr;
      }
  }
  