com.airbnb.lottie.LottieComposition Java Examples

The following examples show how to use com.airbnb.lottie.LottieComposition. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: AnimatableTextPropertiesParser.java    From lottie-android with Apache License 2.0 6 votes vote down vote up
public static AnimatableTextProperties parse(
    JsonReader reader, LottieComposition composition) throws IOException {
  AnimatableTextProperties anim = null;

  reader.beginObject();
  while (reader.hasNext()) {
    switch (reader.selectName(PROPERTIES_NAMES)) {
      case 0:
        anim = parseAnimatableTextProperties(reader, composition);
        break;
      default:
        reader.skipName();
        reader.skipValue();
    }
  }
  reader.endObject();
  if (anim == null) {
    // Not sure if this is possible.
    return new AnimatableTextProperties(null, null, null, null);
  }
  return anim;
}
 
Example #2
Source File: LottieValueAnimator.java    From lottie-android with Apache License 2.0 6 votes vote down vote up
public void setComposition(LottieComposition composition) {
  // Because the initial composition is loaded async, the first min/max frame may be set
  boolean keepMinAndMaxFrames = this.composition == null;
  this.composition = composition;

  if (keepMinAndMaxFrames) {
    setMinAndMaxFrames(
            (int) Math.max(this.minFrame, composition.getStartFrame()),
            (int) Math.min(this.maxFrame, composition.getEndFrame())
    );
  } else {
    setMinAndMaxFrames((int) composition.getStartFrame(), (int) composition.getEndFrame());
  }
  float frame = this.frame;
  this.frame = 0f;
  setFrame((int) frame);
  notifyUpdate();
}
 
Example #3
Source File: NetworkFetcher.java    From lottie-android with Apache License 2.0 6 votes vote down vote up
@WorkerThread
private LottieResult<LottieComposition> fetchFromNetworkInternal() throws IOException {
  Logger.debug("Fetching " + url);


  HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
  connection.setRequestMethod("GET");

  try {
    connection.connect();

    if (connection.getErrorStream() != null || connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
      String error = getErrorFromConnection(connection);
      return new LottieResult<LottieComposition>(new IllegalArgumentException("Unable to fetch " + url + ". Failed with " + connection.getResponseCode() + "\n" + error));
    }

    LottieResult<LottieComposition> result = getResultFromConnection(connection);
    Logger.debug("Completed fetch from network. Success: " + (result.getValue() != null));
    return result;
  } catch (Exception e) {
    return new LottieResult<>(e);
  } finally {
    connection.disconnect();
  }
}
 
Example #4
Source File: NetworkFetcher.java    From lottie-android with Apache License 2.0 6 votes vote down vote up
/**
 * Returns null if the animation doesn't exist in the cache.
 */
@Nullable
@WorkerThread
private LottieComposition fetchFromCache() {
  if (networkCache == null) {
    return null;
  }
  Pair<FileExtension, InputStream> cacheResult = networkCache.fetch(url);
  if (cacheResult == null) {
    return null;
  }

  FileExtension extension = cacheResult.first;
  InputStream inputStream = cacheResult.second;
  LottieResult<LottieComposition> result;
  if (extension == FileExtension.ZIP) {
    result = LottieCompositionFactory.fromZipStreamSync(new ZipInputStream(inputStream), url);
  } else {
    result = LottieCompositionFactory.fromJsonInputStreamSync(inputStream, url);
  }
  if (result.getValue() != null) {
    return result.getValue();
  }
  return null;
}
 
Example #5
Source File: LottieCompositionParser.java    From lottie-android with Apache License 2.0 6 votes vote down vote up
private static void parseLayers(JsonReader reader, LottieComposition composition,
                                List<Layer> layers, LongSparseArray<Layer> layerMap) throws IOException {
  int imageCount = 0;
  reader.beginArray();
  while (reader.hasNext()) {
    Layer layer = LayerParser.parse(reader, composition);
    if (layer.getLayerType() == Layer.LayerType.IMAGE) {
      imageCount++;
    }
    layers.add(layer);
    layerMap.put(layer.getId(), layer);

    if (imageCount > 4) {
      Logger.warning("You have " + imageCount + " images. Lottie should primarily be " +
          "used with shapes. If you are using Adobe Illustrator, convert the Illustrator layers" +
          " to shape layers.");
    }
  }
  reader.endArray();
}
 
Example #6
Source File: BaseLayer.java    From lottie-android with Apache License 2.0 6 votes vote down vote up
@Nullable
static BaseLayer forModel(
    Layer layerModel, LottieDrawable drawable, LottieComposition composition) {
  switch (layerModel.getLayerType()) {
    case SHAPE:
      return new ShapeLayer(drawable, layerModel);
    case PRE_COMP:
      return new CompositionLayer(drawable, layerModel,
          composition.getPrecomps(layerModel.getRefId()), composition);
    case SOLID:
      return new SolidLayer(drawable, layerModel);
    case IMAGE:
      return new ImageLayer(drawable, layerModel);
    case NULL:
      return new NullLayer(drawable, layerModel);
    case TEXT:
      return new TextLayer(drawable, layerModel);
    case UNKNOWN:
    default:
      // Do nothing
      Logger.warning("Unknown layer type " + layerModel.getLayerType());
      return null;
  }
}
 
Example #7
Source File: LottieCompositionMoshiParser.java    From lottie-android with Apache License 2.0 6 votes vote down vote up
private static void parseLayers(JsonReader reader, LottieComposition composition,
                                List<Layer> layers, LongSparseArray<Layer> layerMap) throws IOException {
  int imageCount = 0;
  reader.beginArray();
  while (reader.hasNext()) {
    Layer layer = LayerParser.parse(reader, composition);
    if (layer.getLayerType() == Layer.LayerType.IMAGE) {
      imageCount++;
    }
    layers.add(layer);
    layerMap.put(layer.getId(), layer);

    if (imageCount > 4) {
      Logger.warning("You have " + imageCount + " images. Lottie should primarily be " +
          "used with shapes. If you are using Adobe Illustrator, convert the Illustrator layers" +
          " to shape layers.");
    }
  }
  reader.endArray();
}
 
Example #8
Source File: LayerParser.java    From lottie-android with Apache License 2.0 5 votes vote down vote up
public static Layer parse(LottieComposition composition) {
  Rect bounds = composition.getBounds();
  return new Layer(
      Collections.<ContentModel>emptyList(), composition, "__container", -1,
      Layer.LayerType.PRE_COMP, -1, null, Collections.<Mask>emptyList(),
      new AnimatableTransform(), 0, 0, 0, 0, 0,
      bounds.width(), bounds.height(), null, null, Collections.<Keyframe<Float>>emptyList(),
      Layer.MatteType.NONE, null, false);
}
 
Example #9
Source File: PathKeyframeParser.java    From lottie-android with Apache License 2.0 5 votes vote down vote up
static PathKeyframe parse(
    JsonReader reader, LottieComposition composition) throws IOException {
  boolean animated = reader.peek() == JsonReader.Token.BEGIN_OBJECT;
  Keyframe<PointF> keyframe = KeyframeParser.parse(
      reader, composition, Utils.dpScale(), PathParser.INSTANCE, animated);

  return new PathKeyframe(composition, keyframe);
}
 
Example #10
Source File: CircleShapeParser.java    From lottie-android with Apache License 2.0 5 votes vote down vote up
static CircleShape parse(
    JsonReader reader, LottieComposition composition, int d) throws IOException {
  String name = null;
  AnimatableValue<PointF, PointF> position = null;
  AnimatablePointValue size = null;
  boolean reversed = d == 3;
  boolean hidden = false;

  while (reader.hasNext()) {
    switch (reader.selectName(NAMES)) {
      case 0:
        name = reader.nextString();
        break;
      case 1:
        position = AnimatablePathValueParser.parseSplitPath(reader, composition);
        break;
      case 2:
        size = AnimatableValueParser.parsePoint(reader, composition);
        break;
      case 3:
        hidden = reader.nextBoolean();
        break;
      case 4:
        // "d" is 2 for normal and 3 for reversed.
        reversed = reader.nextInt() == 3;
        break;
      default:
        reader.skipName();
        reader.skipValue();
    }
  }

  return new CircleShape(name, position, size, reversed, hidden);
}
 
Example #11
Source File: RepeaterParser.java    From lottie-android with Apache License 2.0 5 votes vote down vote up
static Repeater parse(
    JsonReader reader, LottieComposition composition) throws IOException {
  String name = null;
  AnimatableFloatValue copies = null;
  AnimatableFloatValue offset = null;
  AnimatableTransform transform = null;
  boolean hidden = false;

  while (reader.hasNext()) {
    switch (reader.selectName(NAMES)) {
      case 0:
        name = reader.nextString();
        break;
      case 1:
        copies = AnimatableValueParser.parseFloat(reader, composition, false);
        break;
      case 2:
        offset = AnimatableValueParser.parseFloat(reader, composition, false);
        break;
      case 3:
        transform = AnimatableTransformParser.parse(reader, composition);
        break;
      case 4:
        hidden = reader.nextBoolean();
        break;
      default:
        reader.skipValue();
    }
  }

  return new Repeater(name, copies, offset, transform, hidden);
}
 
Example #12
Source File: RectangleShapeParser.java    From lottie-android with Apache License 2.0 5 votes vote down vote up
static RectangleShape parse(
    JsonReader reader, LottieComposition composition) throws IOException {
  String name = null;
  AnimatableValue<PointF, PointF> position = null;
  AnimatablePointValue size = null;
  AnimatableFloatValue roundedness = null;
  boolean hidden = false;

  while (reader.hasNext()) {
    switch (reader.selectName(NAMES)) {
      case 0:
        name = reader.nextString();
        break;
      case 1:
        position =
            AnimatablePathValueParser.parseSplitPath(reader, composition);
        break;
      case 2:
        size = AnimatableValueParser.parsePoint(reader, composition);
        break;
      case 3:
        roundedness = AnimatableValueParser.parseFloat(reader, composition);
        break;
      case 4:
        hidden = reader.nextBoolean();
        break;
      default:
        reader.skipValue();
    }
  }

  return new RectangleShape(name, position, size, roundedness, hidden);
}
 
Example #13
Source File: NetworkFetcher.java    From lottie-android with Apache License 2.0 5 votes vote down vote up
@WorkerThread
public LottieResult<LottieComposition> fetchSync() {
  LottieComposition result = fetchFromCache();
  if (result != null) {
    return new LottieResult<>(result);
  }

  Logger.debug("Animation for " + url + " not found in cache. Fetching from network.");
  return fetchFromNetwork();
}
 
Example #14
Source File: ShapeTrimPathParser.java    From lottie-android with Apache License 2.0 5 votes vote down vote up
static ShapeTrimPath parse(
    JsonReader reader, LottieComposition composition) throws IOException {
  String name = null;
  ShapeTrimPath.Type type = null;
  AnimatableFloatValue start = null;
  AnimatableFloatValue end = null;
  AnimatableFloatValue offset = null;
  boolean hidden = false;

  while (reader.hasNext()) {
    switch (reader.selectName(NAMES)) {
      case 0:
        start = AnimatableValueParser.parseFloat(reader, composition, false);
        break;
      case 1:
        end = AnimatableValueParser.parseFloat(reader, composition, false);
        break;
      case 2:
        offset = AnimatableValueParser.parseFloat(reader, composition, false);
        break;
      case 3:
        name = reader.nextString();
        break;
      case 4:
        type = ShapeTrimPath.Type.forId(reader.nextInt());
        break;
      case 5:
        hidden = reader.nextBoolean();
        break;
      default:
        reader.skipValue();
    }
  }

  return new ShapeTrimPath(name, type, start, end, offset, hidden);
}
 
Example #15
Source File: ShapeGroupParser.java    From lottie-android with Apache License 2.0 5 votes vote down vote up
static ShapeGroup parse(
    JsonReader reader, LottieComposition composition) throws IOException {
  String name = null;
  boolean hidden = false;
  List<ContentModel> items = new ArrayList<>();

  while (reader.hasNext()) {
    switch (reader.selectName(NAMES)) {
      case 0:
        name = reader.nextString();
        break;
      case 1:
        hidden = reader.nextBoolean();
        break;
      case 2:
        reader.beginArray();
        while (reader.hasNext()) {
          ContentModel newItem = ContentModelParser.parse(reader, composition);
          if (newItem != null) {
            items.add(newItem);
          }
        }
        reader.endArray();
        break;
      default:
        reader.skipValue();
    }
  }

  return new ShapeGroup(name, items, hidden);
}
 
Example #16
Source File: NetworkFetcher.java    From lottie-android with Apache License 2.0 5 votes vote down vote up
@WorkerThread
private LottieResult<LottieComposition> fetchFromNetwork() {
  try {
    return fetchFromNetworkInternal();
  } catch (IOException e) {
    return new LottieResult<>(e);
  }
}
 
Example #17
Source File: NetworkFetcher.java    From lottie-android with Apache License 2.0 5 votes vote down vote up
@Nullable
private LottieResult<LottieComposition> getResultFromConnection(HttpURLConnection connection) throws IOException {
  File file;
  FileExtension extension;
  LottieResult<LottieComposition> result;
  String contentType = connection.getContentType();
  if (contentType == null) {
    // Assume JSON for best effort parsing. If it fails, it will just deliver the parse exception
    // in the result which is more useful than failing here.
    contentType = "application/json";
  }
  if (contentType.contains("application/zip")) {
    Logger.debug("Handling zip response.");
    extension = FileExtension.ZIP;
    if (networkCache == null) {
      result = LottieCompositionFactory.fromZipStreamSync(new ZipInputStream(connection.getInputStream()), null);
    } else {
      file = networkCache.writeTempCacheFile(url, connection.getInputStream(), extension);
      result = LottieCompositionFactory.fromZipStreamSync(new ZipInputStream(new FileInputStream(file)), url);
    }
  } else {
    Logger.debug("Received json response.");
    extension = FileExtension.JSON;
    if (networkCache == null) {
      result = LottieCompositionFactory.fromJsonInputStreamSync(connection.getInputStream(), null);
    } else {
      file = networkCache.writeTempCacheFile(url, connection.getInputStream(), extension);
      result = LottieCompositionFactory.fromJsonInputStreamSync(new FileInputStream(new File(file.getAbsolutePath())), url);
    }
  }

  if (networkCache != null && result.getValue() != null) {
    networkCache.renameTempFile(url, extension);
  }
  return result;
}
 
Example #18
Source File: AnimatablePathValueParser.java    From lottie-android with Apache License 2.0 5 votes vote down vote up
public static AnimatablePathValue parse(
    JsonReader reader, LottieComposition composition) throws IOException {
  List<Keyframe<PointF>> keyframes = new ArrayList<>();
  if (reader.peek() == JsonReader.Token.BEGIN_ARRAY) {
    reader.beginArray();
    while (reader.hasNext()) {
      keyframes.add(PathKeyframeParser.parse(reader, composition));
    }
    reader.endArray();
    KeyframesParser.setEndFrames(keyframes);
  } else {
    keyframes.add(new Keyframe<>(JsonUtils.jsonToPoint(reader, Utils.dpScale())));
  }
  return new AnimatablePathValue(keyframes);
}
 
Example #19
Source File: LottieCompositionCache.java    From lottie-android with Apache License 2.0 5 votes vote down vote up
@Nullable
public LottieComposition get(@Nullable String cacheKey) {
  if (cacheKey == null) {
    return null;
  }
  return cache.get(cacheKey);
}
 
Example #20
Source File: ShapeFillParser.java    From lottie-android with Apache License 2.0 5 votes vote down vote up
static ShapeFill parse(
    JsonReader reader, LottieComposition composition) throws IOException {
  AnimatableColorValue color = null;
  boolean fillEnabled = false;
  AnimatableIntegerValue opacity = null;
  String name = null;
  int fillTypeInt = 1;
  boolean hidden = false;

  while (reader.hasNext()) {
    switch (reader.selectName(NAMES)) {
      case 0:
        name = reader.nextString();
        break;
      case 1:
        color = AnimatableValueParser.parseColor(reader, composition);
        break;
      case 2:
        opacity = AnimatableValueParser.parseInteger(reader, composition);
        break;
      case 3:
        fillEnabled = reader.nextBoolean();
        break;
      case 4:
        fillTypeInt = reader.nextInt();
        break;
      case 5:
        hidden = reader.nextBoolean();
        break;
      default:
        reader.skipName();
        reader.skipValue();
    }
  }

  Path.FillType fillType = fillTypeInt == 1 ? Path.FillType.WINDING : Path.FillType.EVEN_ODD;
  return new ShapeFill(name, fillEnabled, fillType, color, opacity, hidden);
}
 
Example #21
Source File: LottieCompositionMoshiParser.java    From lottie-android with Apache License 2.0 5 votes vote down vote up
private static void parseChars(
    JsonReader reader, LottieComposition composition,
    SparseArrayCompat<FontCharacter> characters) throws IOException {
  reader.beginArray();
  while (reader.hasNext()) {
    FontCharacter character = FontCharacterParser.parse(reader, composition);
    characters.put(character.hashCode(), character);
  }
  reader.endArray();
}
 
Example #22
Source File: LottieCompositionMoshiParser.java    From lottie-android with Apache License 2.0 5 votes vote down vote up
private static void parseMarkers(
    JsonReader reader, LottieComposition composition, List<Marker> markers) throws IOException{
  reader.beginArray();
  while (reader.hasNext()) {
    String comment = null;
    float frame = 0f;
    float durationFrames = 0f;
    reader.beginObject();
    while (reader.hasNext()) {
      switch (reader.selectName(MARKER_NAMES)) {
        case 0:
          comment = reader.nextString();
          break;
        case 1:
          frame = (float) reader.nextDouble();
          break;
        case 2:
          durationFrames = (float) reader.nextDouble();
          break;
        default:
          reader.skipName();
          reader.skipValue();
      }
    }
    reader.endObject();
    markers.add(new Marker(comment, frame, durationFrames));
  }
  reader.endArray();
}
 
Example #23
Source File: KeyframeParser.java    From lottie-android with Apache License 2.0 5 votes vote down vote up
static <T> Keyframe<T> parse(JsonReader reader, LottieComposition composition,
                             float scale, ValueParser<T> valueParser, boolean animated) throws IOException {

  if (animated) {
    return parseKeyframe(composition, reader, scale, valueParser);
  } else {
    return parseStaticValue(reader, scale, valueParser);
  }
}
 
Example #24
Source File: KeyframesParser.java    From lottie-android with Apache License 2.0 5 votes vote down vote up
static <T> List<Keyframe<T>> parse(JsonReader reader,
                                   LottieComposition composition, float scale, ValueParser<T> valueParser)
    throws IOException {
  List<Keyframe<T>> keyframes = new ArrayList<>();

  if (reader.peek() == JsonReader.Token.STRING) {
    composition.addWarning("Lottie doesn't support expressions.");
    return keyframes;
  }

  reader.beginObject();
  while (reader.hasNext()) {
    switch (reader.selectName(NAMES)) {
      case 0:
        if (reader.peek() == JsonReader.Token.BEGIN_ARRAY) {
          reader.beginArray();

          if (reader.peek() == JsonReader.Token.NUMBER) {
            // For properties in which the static value is an array of numbers.
            keyframes.add(KeyframeParser.parse(reader, composition, scale, valueParser, false));
          } else {
            while (reader.hasNext()) {
              keyframes.add(KeyframeParser.parse(reader, composition, scale, valueParser, true));
            }
          }
          reader.endArray();
        } else {
          keyframes.add(KeyframeParser.parse(reader, composition, scale, valueParser, false));
        }
        break;
      default:
        reader.skipValue();
    }
  }
  reader.endObject();

  setEndFrames(keyframes);
  return keyframes;
}
 
Example #25
Source File: AnimatableTextPropertiesParser.java    From lottie-android with Apache License 2.0 5 votes vote down vote up
private static AnimatableTextProperties parseAnimatableTextProperties(
    JsonReader reader, LottieComposition composition) throws IOException {
  AnimatableColorValue color = null;
  AnimatableColorValue stroke = null;
  AnimatableFloatValue strokeWidth = null;
  AnimatableFloatValue tracking = null;

  reader.beginObject();
  while (reader.hasNext()) {
    switch (reader.selectName(ANIMATABLE_PROPERTIES_NAMES)) {
      case 0:
        color = AnimatableValueParser.parseColor(reader, composition);
        break;
      case 1:
        stroke = AnimatableValueParser.parseColor(reader, composition);
        break;
      case 2:
        strokeWidth = AnimatableValueParser.parseFloat(reader, composition);
        break;
      case 3:
        tracking = AnimatableValueParser.parseFloat(reader, composition);
        break;
      default:
        reader.skipName();
        reader.skipValue();
    }
  }
  reader.endObject();

  return new AnimatableTextProperties(color, stroke, strokeWidth, tracking);
}
 
Example #26
Source File: ShapePathParser.java    From lottie-android with Apache License 2.0 5 votes vote down vote up
static ShapePath parse(
    JsonReader reader, LottieComposition composition) throws IOException {
  String name = null;
  int ind = 0;
  AnimatableShapeValue shape = null;
  boolean hidden = false;

  while (reader.hasNext()) {
    switch (reader.selectName(NAMES)) {
      case 0:
        name = reader.nextString();
        break;
      case 1:
        ind = reader.nextInt();
        break;
      case 2:
        shape = AnimatableValueParser.parseShapeData(reader, composition);
        break;
      case 3:
        hidden = reader.nextBoolean();
        break;
      default:
        reader.skipValue();
    }
  }

  return new ShapePath(name, ind, shape, hidden);
}
 
Example #27
Source File: Layer.java    From lottie-android with Apache License 2.0 5 votes vote down vote up
public Layer(List<ContentModel> shapes, LottieComposition composition, String layerName, long layerId,
             LayerType layerType, long parentId, @Nullable String refId, List<Mask> masks,
             AnimatableTransform transform, int solidWidth, int solidHeight, int solidColor,
             float timeStretch, float startFrame, int preCompWidth, int preCompHeight,
             @Nullable AnimatableTextFrame text, @Nullable AnimatableTextProperties textProperties,
             List<Keyframe<Float>> inOutKeyframes, MatteType matteType,
             @Nullable AnimatableFloatValue timeRemapping, boolean hidden) {
  this.shapes = shapes;
  this.composition = composition;
  this.layerName = layerName;
  this.layerId = layerId;
  this.layerType = layerType;
  this.parentId = parentId;
  this.refId = refId;
  this.masks = masks;
  this.transform = transform;
  this.solidWidth = solidWidth;
  this.solidHeight = solidHeight;
  this.solidColor = solidColor;
  this.timeStretch = timeStretch;
  this.startFrame = startFrame;
  this.preCompWidth = preCompWidth;
  this.preCompHeight = preCompHeight;
  this.text = text;
  this.textProperties = textProperties;
  this.inOutKeyframes = inOutKeyframes;
  this.matteType = matteType;
  this.timeRemapping = timeRemapping;
  this.hidden = hidden;
}
 
Example #28
Source File: Keyframe.java    From lottie-android with Apache License 2.0 5 votes vote down vote up
public Keyframe(@SuppressWarnings("NullableProblems") LottieComposition composition,
    @Nullable T startValue, @Nullable T endValue,
    @Nullable Interpolator interpolator, float startFrame, @Nullable Float endFrame) {
  this.composition = composition;
  this.startValue = startValue;
  this.endValue = endValue;
  this.interpolator = interpolator;
  this.startFrame = startFrame;
  this.endFrame = endFrame;
}
 
Example #29
Source File: PathKeyframe.java    From lottie-android with Apache License 2.0 4 votes vote down vote up
public PathKeyframe(LottieComposition composition, Keyframe<PointF> keyframe) {
  super(composition, keyframe.startValue, keyframe.endValue, keyframe.interpolator,
      keyframe.startFrame, keyframe.endFrame);
  this.pointKeyFrame = keyframe;
  createPath();
}
 
Example #30
Source File: FontCharacterParser.java    From lottie-android with Apache License 2.0 4 votes vote down vote up
static FontCharacter parse(
    JsonReader reader, LottieComposition composition) throws IOException {
  char character = '\0';
  double size = 0;
  double width = 0;
  String style = null;
  String fontFamily = null;
  List<ShapeGroup> shapes = new ArrayList<>();

  reader.beginObject();
  while (reader.hasNext()) {
    switch (reader.selectName(NAMES)) {
      case 0:
        character = reader.nextString().charAt(0);
        break;
      case 1:
        size = reader.nextDouble();
        break;
      case 2:
        width = reader.nextDouble();
        break;
      case 3:
        style = reader.nextString();
        break;
      case 4:
        fontFamily = reader.nextString();
        break;
      case 5:
        reader.beginObject();
        while (reader.hasNext()) {
          switch (reader.selectName(DATA_NAMES)) {
            case 0:
              reader.beginArray();
              while (reader.hasNext()) {
                shapes.add((ShapeGroup) ContentModelParser.parse(reader, composition));
              }
              reader.endArray();
              break;
            default:
              reader.skipName();
              reader.skipValue();
          }
        }
        reader.endObject();
        break;
      default:
        reader.skipName();
        reader.skipValue();
    }
  }
  reader.endObject();

  return new FontCharacter(shapes, character, size, width, style, fontFamily);
}