Example #1
0
 // @Override
 setEditableValue(model: any, propertyName: string, value: string) {
   if (!value) {
     model[propertyName] = undefined;
     return;
   }
   let processedValue = ColorUtil.parseAndroidColor(value);
   if (!processedValue) {
     processedValue = ColorUtil.parseAndroidColor(ColorUtil.svgToAndroidColor(value));
   }
   model[propertyName] = ColorUtil.toAndroidString(processedValue);
 }
Example #2
0
 // @Override
 interpolateValue(start: string, end: string, f: number) {
   if (!start || !end) {
     return undefined;
   }
   const s = ColorUtil.parseAndroidColor(start);
   const e = ColorUtil.parseAndroidColor(end);
   return ColorUtil.toAndroidString({
     r: _.clamp(Math.round(MathUtil.lerp(s.r, e.r, f)), 0, 0xff),
     g: _.clamp(Math.round(MathUtil.lerp(s.g, e.g, f)), 0, 0xff),
     b: _.clamp(Math.round(MathUtil.lerp(s.b, e.b, f)), 0, 0xff),
     a: _.clamp(Math.round(MathUtil.lerp(s.a, e.a, f)), 0, 0xff),
   });
 }
function getColor(obj: HTMLElement, attr: string, def = '') {
  const androidAttr = `android:${attr}`;
  const color = obj.hasAttribute(androidAttr) ? obj.getAttribute(androidAttr) : def;
  return !!ColorUtil.parseAndroidColor(color) ? color : def;
}
Example #4
0
  const nodeToLayerFn = (node: Element, transforms: ReadonlyArray<Matrix>): Layer => {
    if (
      !node ||
      node.nodeType === Node.TEXT_NODE ||
      node.nodeType === Node.COMMENT_NODE ||
      node instanceof SVGDefsElement ||
      node instanceof SVGUseElement
    ) {
      return undefined;
    }

    const nodeTransforms = getNodeTransforms(node as SVGGraphicsElement);
    transforms = [...transforms, ...nodeTransforms];
    const flattenedTransforms = Matrix.flatten(transforms);

    // Get the referenced clip-path ID, if one exists.
    const refClipPathId = getReferencedClipPathId(node);

    const maybeWrapClipPathInGroupFn = (layer: Layer) => {
      if (!refClipPathId) {
        return layer;
      }
      const paths = (clipPathMap[refClipPathId] || []).map(p => {
        return new Path(
          p
            .mutate()
            .transform(flattenedTransforms)
            .build()
            .getPathString(),
        );
      });
      if (!paths.length) {
        // If the clipPath has no children, then clip the entire layer.
        paths.push(new Path('M 0 0 Z'));
      }
      const groupChildren: Layer[] = paths.map(p => {
        return new ClipPathLayer({
          name: makeFinalNodeIdFn(refClipPathId, 'mask'),
          pathData: p,
          children: [],
        });
      });
      groupChildren.push(layer);
      return new GroupLayer({
        name: makeFinalNodeIdFn('wrapper', 'group'),
        children: groupChildren,
      });
    };

    if (node instanceof SVGPathElement && node.getAttribute('d')) {
      const path = node.getAttribute('d');
      const attrMap: Dictionary<any> = {};
      const simpleAttrFn = (nodeAttr: string, contextAttr: string) => {
        if (node.hasAttribute(nodeAttr)) {
          attrMap[contextAttr] = node.getAttribute(nodeAttr);
        }
      };

      simpleAttrFn('stroke', 'strokeColor');
      simpleAttrFn('stroke-width', 'strokeWidth');
      simpleAttrFn('stroke-linecap', 'strokeLinecap');
      simpleAttrFn('stroke-linejoin', 'strokeLinejoin');
      simpleAttrFn('stroke-miterlimit', 'strokeMiterLimit');
      simpleAttrFn('stroke-opacity', 'strokeAlpha');
      simpleAttrFn('fill', 'fillColor');
      simpleAttrFn('fill-opacity', 'fillAlpha');
      simpleAttrFn('fill-rule', 'fillType');

      // Set the default values as specified by the SVG spec. Note that some of these default
      // values are different than the default values used by VectorDrawables.
      const fillColor =
        'fillColor' in attrMap ? ColorUtil.svgToAndroidColor(attrMap['fillColor']) : '#000';
      const strokeColor =
        'strokeColor' in attrMap ? ColorUtil.svgToAndroidColor(attrMap['strokeColor']) : undefined;
      const fillAlpha = 'fillAlpha' in attrMap ? Number(attrMap['fillAlpha']) : 1;
      let strokeWidth = 'strokeWidth' in attrMap ? Number(attrMap['strokeWidth']) : 1;
      const strokeAlpha = 'strokeAlpha' in attrMap ? Number(attrMap['strokeAlpha']) : 1;
      const strokeLinecap: StrokeLineCap =
        'strokeLinecap' in attrMap ? attrMap['strokeLinecap'] : 'butt';
      const strokeLinejoin: StrokeLineJoin =
        'strokeLinejoin' in attrMap ? attrMap['strokeLinecap'] : 'miter';
      const strokeMiterLimit =
        'strokeMiterLimit' in attrMap ? Number(attrMap['strokeMiterLimit']) : 4;
      const fillRuleToFillTypeFn = (fillRule: string) => {
        return fillRule === 'evenodd' ? 'evenOdd' : 'nonZero';
      };
      const fillType: FillType =
        'fillType' in attrMap ? fillRuleToFillTypeFn(attrMap['fillType']) : 'nonZero';

      let pathData = new Path(path);
      if (transforms.length) {
        pathData = new Path(
          pathData
            .mutate()
            .transform(flattenedTransforms)
            .build()
            .getPathString(),
        );
        strokeWidth = MathUtil.round(strokeWidth * flattenedTransforms.getScaleFactor());
      }
      // TODO: make best effort attempt to restore trimPath{Start,End,Offset}
      return maybeWrapClipPathInGroupFn(
        new PathLayer({
          id: _.uniqueId(),
          name: makeFinalNodeIdFn(node.getAttribute('id'), 'path'),
          children: [],
          pathData,
          fillColor,
          fillAlpha,
          strokeColor,
          strokeAlpha,
          strokeWidth,
          strokeLinecap,
          strokeLinejoin,
          strokeMiterLimit,
          fillType,
        }),
      );
    }

    if (node.childNodes) {
      const children: Layer[] = [];
      for (let i = 0; i < node.childNodes.length; i++) {
        const child = node.childNodes.item(i) as Element;
        const layer = nodeToLayerFn(child, transforms);
        if (layer) {
          children.push(layer);
        }
      }
      return maybeWrapClipPathInGroupFn(
        new GroupLayer({
          id: _.uniqueId(),
          name: makeFinalNodeIdFn(node.getAttribute('id'), 'group'),
          children,
        }),
      );
    }
    return undefined;
  };
Example #5
0
    (layer: VectorLayer | GroupLayer | PathLayer, parentNode: Node) => {
      if (layer instanceof VectorLayer) {
        if (withIds) {
          conditionalAttr(destinationNode, 'id', vl.name, '');
        }
        conditionalAttr(destinationNode, 'opacity', vl.alpha, 1);
        return parentNode;
      }
      if (layer instanceof PathLayer) {
        const { pathData } = layer;
        if (!pathData.getPathString()) {
          return undefined;
        }
        const node = xmlDoc.createElement('path');
        if (withIds) {
          conditionalAttr(node, 'id', layer.name);
        }
        maybeSetClipPathForLayerFn(node, layer.id);
        conditionalAttr(node, 'd', pathData.getPathString());
        if (layer.fillColor) {
          conditionalAttr(node, 'fill', ColorUtil.androidToCssHexColor(layer.fillColor), '');
        } else {
          conditionalAttr(node, 'fill', 'none');
        }
        conditionalAttr(node, 'fill-opacity', layer.fillAlpha, 1);
        if (layer.strokeColor) {
          conditionalAttr(node, 'stroke', ColorUtil.androidToCssHexColor(layer.strokeColor), '');
        }
        conditionalAttr(node, 'stroke-opacity', layer.strokeAlpha, 1);
        conditionalAttr(node, 'stroke-width', layer.strokeWidth, 0);

        if (layer.trimPathStart !== 0 || layer.trimPathEnd !== 1 || layer.trimPathOffset !== 0) {
          const flattenedTransform = LayerUtil.getCanvasTransformForLayer(vl, layer.id);
          const { a, d } = flattenedTransform;
          // Note that we only return the length of the first sub path due to
          // https://code.google.com/p/android/issues/detail?id=172547
          let pathLength: number;
          if (Math.abs(a) !== 1 || Math.abs(d) !== 1) {
            // Then recompute the scaled path length.
            pathLength = pathData
              .mutate()
              .transform(flattenedTransform)
              .build()
              .getSubPathLength(0);
          } else {
            pathLength = pathData.getSubPathLength(0);
          }
          const strokeDashArray = PathUtil.toStrokeDashArray(
            layer.trimPathStart,
            layer.trimPathEnd,
            layer.trimPathOffset,
            pathLength,
          ).join(',');
          const strokeDashOffset = PathUtil.toStrokeDashOffset(
            layer.trimPathStart,
            layer.trimPathEnd,
            layer.trimPathOffset,
            pathLength,
          ).toString();
          conditionalAttr(node, 'stroke-dasharray', strokeDashArray);
          conditionalAttr(node, 'stroke-dashoffset', strokeDashOffset);
        }

        conditionalAttr(node, 'stroke-linecap', layer.strokeLinecap, 'butt');
        conditionalAttr(node, 'stroke-linejoin', layer.strokeLinejoin, 'miter');
        conditionalAttr(node, 'stroke-miterlimit', layer.strokeMiterLimit, 4);
        const fillRule = !layer.fillType || layer.fillType === 'nonZero' ? 'nonzero' : 'evenodd';
        conditionalAttr(node, 'fill-rule', fillRule, 'nonzero');
        parentNode.appendChild(node);
        return parentNode;
      }
      if (layer instanceof GroupLayer) {
        const node = xmlDoc.createElement('g');
        if (withIds) {
          conditionalAttr(node, 'id', layer.name);
        }
        const transformValues: string[] = [];
        if (layer.translateX || layer.translateY) {
          transformValues.push(`translate(${layer.translateX} ${layer.translateY})`);
        }
        if (layer.rotation) {
          transformValues.push(`rotate(${layer.rotation} ${layer.pivotX} ${layer.pivotY})`);
        }
        if (layer.scaleX !== 1 || layer.scaleY !== 1) {
          if (layer.pivotX || layer.pivotY) {
            transformValues.push(`translate(${layer.pivotX} ${layer.pivotY})`);
          }
          transformValues.push(`scale(${layer.scaleX} ${layer.scaleY})`);
          if (layer.pivotX || layer.pivotY) {
            transformValues.push(`translate(${-layer.pivotX} ${-layer.pivotY})`);
          }
        }
        let nodeToAttachToParent = node;
        if (transformValues.length) {
          node.setAttributeNS(undefined, 'transform', transformValues.join(' '));
          if (isLayerBeingClippedFn(layer.id)) {
            // Create a wrapper node so that the clip-path is applied before the transformations.
            const wrapperNode = xmlDoc.createElement('g');
            wrapperNode.appendChild(node);
            nodeToAttachToParent = wrapperNode;
          }
        }
        maybeSetClipPathForLayerFn(nodeToAttachToParent, layer.id);
        parentNode.appendChild(nodeToAttachToParent);
        return node;
      }
      return undefined;
    },