private static bool CheckPagesCounterPresence(CssDeclaration declaration)
        {
            bool pagesCounterPresent = false;

            // MDN: The counters() function can be used with any CSS property, but support for properties other
            // than content is experimental, and support for the type-or-unit parameter is sparse.
            // iText also does not support counter(pages) anywhere else for now
            if (CssConstants.CONTENT.Equals(declaration.GetProperty()))
            {
                CssDeclarationValueTokenizer       tokenizer = new CssDeclarationValueTokenizer(declaration.GetExpression());
                CssDeclarationValueTokenizer.Token token;
                while ((token = tokenizer.GetNextValidToken()) != null)
                {
                    if (token.IsString())
                    {
                        continue;
                    }
                    if (token.GetValue().StartsWith(CssConstants.COUNTERS + "("))
                    {
                        String paramsStr = token.GetValue().JSubstring(CssConstants.COUNTERS.Length + 1, token.GetValue().Length -
                                                                       1);
                        String[] @params = iText.IO.Util.StringUtil.Split(paramsStr, ",");
                        pagesCounterPresent = pagesCounterPresent || CheckCounterFunctionParamsForPagesReferencePresence(@params);
                    }
                    else
                    {
                        if (token.GetValue().StartsWith(CssConstants.COUNTER + "("))
                        {
                            String paramsStr = token.GetValue().JSubstring(CssConstants.COUNTER.Length + 1, token.GetValue().Length -
                                                                           1);
                            String[] @params = iText.IO.Util.StringUtil.Split(paramsStr, ",");
                            pagesCounterPresent = pagesCounterPresent || CheckCounterFunctionParamsForPagesReferencePresence(@params);
                        }
                        else
                        {
                            if (token.GetValue().StartsWith(CssConstants.TARGET_COUNTER + "("))
                            {
                                String paramsStr = token.GetValue().JSubstring(CssConstants.TARGET_COUNTER.Length + 1, token.GetValue().Length
                                                                               - 1);
                                String[] @params = iText.IO.Util.StringUtil.Split(paramsStr, ",");
                                pagesCounterPresent = pagesCounterPresent || (@params.Length >= TARGET_COUNTER_MIN_PARAMS_SIZE && CheckTargetCounterParamsForPageOrPagesReferencePresence
                                                                                  (@params));
                            }
                            else
                            {
                                if (token.GetValue().StartsWith(CssConstants.TARGET_COUNTERS + "("))
                                {
                                    String paramsStr = token.GetValue().JSubstring(CssConstants.TARGET_COUNTERS.Length + 1, token.GetValue().Length
                                                                                   - 1);
                                    String[] @params = iText.IO.Util.StringUtil.Split(paramsStr, ",");
                                    pagesCounterPresent = pagesCounterPresent || (@params.Length >= TARGET_COUNTERS_MIN_PARAMS_SIZE && CheckTargetCounterParamsForPageOrPagesReferencePresence
                                                                                      (@params));
                                }
                            }
                        }
                    }
                }
            }
            return(pagesCounterPresent);
        }
Ejemplo n.º 2
0
 /// <summary>Parses the provided linear gradient or repeating linear gradient function</summary>
 /// <param name="cssGradientValue">the value to parse</param>
 /// <param name="emValue">the current element's em value</param>
 /// <param name="remValue">the current element's rem value</param>
 /// <returns>
 /// the
 /// <see cref="iText.Kernel.Colors.Gradients.StrategyBasedLinearGradientBuilder"/>
 /// constructed from the parsed linear gradient
 /// or
 /// <see langword="null"/>
 /// if the argument value is not a linear gradient or repeating linear gradient
 /// function
 /// </returns>
 public static StrategyBasedLinearGradientBuilder ParseCssLinearGradient(String cssGradientValue, float emValue
                                                                         , float remValue)
 {
     if (IsCssLinearGradientValue(cssGradientValue))
     {
         cssGradientValue = cssGradientValue.ToLowerInvariant().Trim();
         bool   isRepeating   = false;
         String argumentsPart = null;
         if (cssGradientValue.StartsWith(LINEAR_GRADIENT_FUNCTION_SUFFIX))
         {
             argumentsPart = cssGradientValue.JSubstring(LINEAR_GRADIENT_FUNCTION_SUFFIX.Length, cssGradientValue.Length
                                                         - 1);
             isRepeating = false;
         }
         else
         {
             if (cssGradientValue.StartsWith(REPEATING_LINEAR_GRADIENT_FUNCTION_SUFFIX))
             {
                 argumentsPart = cssGradientValue.JSubstring(REPEATING_LINEAR_GRADIENT_FUNCTION_SUFFIX.Length, cssGradientValue
                                                             .Length - 1);
                 isRepeating = true;
             }
         }
         if (argumentsPart != null)
         {
             IList <String> argumentsList = new List <String>();
             StringBuilder  buff          = new StringBuilder();
             CssDeclarationValueTokenizer       tokenizer = new CssDeclarationValueTokenizer(argumentsPart);
             CssDeclarationValueTokenizer.Token nextToken;
             while ((nextToken = tokenizer.GetNextValidToken()) != null)
             {
                 if (nextToken.GetType() == CssDeclarationValueTokenizer.TokenType.COMMA)
                 {
                     if (buff.Length != 0)
                     {
                         argumentsList.Add(buff.ToString().Trim());
                         buff = new StringBuilder();
                     }
                 }
                 else
                 {
                     buff.Append(" ").Append(nextToken.GetValue());
                 }
             }
             if (buff.Length != 0)
             {
                 argumentsList.Add(buff.ToString().Trim());
             }
             if (argumentsList.IsEmpty())
             {
                 throw new StyledXMLParserException(MessageFormatUtil.Format(StyledXMLParserException.INVALID_GRADIENT_FUNCTION_ARGUMENTS_LIST
                                                                             , cssGradientValue));
             }
             return(ParseCssLinearGradient(argumentsList, isRepeating, emValue, remValue));
         }
     }
     return(null);
 }
 /// <summary>Normalizes the declaration URIs.</summary>
 /// <param name="declarations">the declarations</param>
 private void NormalizeDeclarationURIs(IList <CssDeclaration> declarations)
 {
     // This is the case when css has no location and thus urls should not be resolved against base css location
     if (this.uriResolver == null)
     {
         return;
     }
     foreach (CssDeclaration declaration in declarations)
     {
         if (declaration.GetExpression().Contains("url("))
         {
             CssDeclarationValueTokenizer       tokenizer = new CssDeclarationValueTokenizer(declaration.GetExpression());
             CssDeclarationValueTokenizer.Token token;
             StringBuilder normalizedDeclaration = new StringBuilder();
             while ((token = tokenizer.GetNextValidToken()) != null)
             {
                 String strToAppend;
                 if (token.GetType() == CssDeclarationValueTokenizer.TokenType.FUNCTION && token.GetValue().StartsWith("url("
                                                                                                                       ))
                 {
                     String url = token.GetValue().Trim();
                     url = url.JSubstring(4, url.Length - 1).Trim();
                     if (CssUtils.IsBase64Data(url))
                     {
                         strToAppend = token.GetValue().Trim();
                     }
                     else
                     {
                         if (url.StartsWith("'") && url.EndsWith("'") || url.StartsWith("\"") && url.EndsWith("\""))
                         {
                             url = url.JSubstring(1, url.Length - 1);
                         }
                         url = url.Trim();
                         String finalUrl = url;
                         try {
                             finalUrl = uriResolver.ResolveAgainstBaseUri(url).ToExternalForm();
                         }
                         catch (UriFormatException) {
                         }
                         strToAppend = MessageFormatUtil.Format("url({0})", finalUrl);
                     }
                 }
                 else
                 {
                     strToAppend = token.GetValue();
                 }
                 if (normalizedDeclaration.Length > 0)
                 {
                     normalizedDeclaration.Append(' ');
                 }
                 normalizedDeclaration.Append(strToAppend);
             }
             declaration.SetExpression(normalizedDeclaration.ToString());
         }
     }
 }
        private void RunTest(String src, IList <String> tokenValues, IList <CssDeclarationValueTokenizer.TokenType>
                             tokenTypes)
        {
            CssDeclarationValueTokenizer tokenizer = new CssDeclarationValueTokenizer(src);

            CssDeclarationValueTokenizer.Token token = null;
            NUnit.Framework.Assert.IsTrue(tokenValues.Count == tokenTypes.Count, "Value and type arrays size should be equal"
                                          );
            int index = 0;

            while ((token = tokenizer.GetNextValidToken()) != null)
            {
                NUnit.Framework.Assert.AreEqual(tokenValues[index], token.GetValue());
                NUnit.Framework.Assert.AreEqual(tokenTypes[index], token.GetType());
                ++index;
            }
            NUnit.Framework.Assert.IsTrue(index == tokenValues.Count);
        }
        private TransparentColor GetColorFromAttributeValue(SvgDrawContext context, String rawColorValue, float objectBoundingBoxMargin
                                                            , float parentOpacity)
        {
            if (rawColorValue == null)
            {
                return(null);
            }
            CssDeclarationValueTokenizer tokenizer = new CssDeclarationValueTokenizer(rawColorValue);

            CssDeclarationValueTokenizer.Token token = tokenizer.GetNextValidToken();
            if (token == null)
            {
                return(null);
            }
            String tokenValue = token.GetValue();

            if (tokenValue.StartsWith("url(#") && tokenValue.EndsWith(")"))
            {
                Color            resolvedColor   = null;
                float            resolvedOpacity = 1;
                String           normalizedName  = tokenValue.JSubstring(5, tokenValue.Length - 1).Trim();
                ISvgNodeRenderer colorRenderer   = context.GetNamedObject(normalizedName);
                if (colorRenderer is AbstractGradientSvgNodeRenderer)
                {
                    resolvedColor = ((AbstractGradientSvgNodeRenderer)colorRenderer).CreateColor(context, GetObjectBoundingBox
                                                                                                     (context), objectBoundingBoxMargin, parentOpacity);
                }
                if (resolvedColor != null)
                {
                    return(new TransparentColor(resolvedColor, resolvedOpacity));
                }
                token = tokenizer.GetNextValidToken();
            }
            // may become null after function parsing and reading the 2nd token
            if (token != null)
            {
                String value = token.GetValue();
                if (!SvgConstants.Values.NONE.EqualsIgnoreCase(value))
                {
                    return(new TransparentColor(WebColors.GetRGBColor(value), parentOpacity * GetAlphaFromRGBA(value)));
                }
            }
            return(null);
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Creates a
        /// <see cref="CssQuotes"/>
        /// instance.
        /// </summary>
        /// <param name="quotesString">the quotes string</param>
        /// <param name="fallbackToDefault">indicates whether it's OK to fall back to the default</param>
        /// <returns>
        /// the resulting
        /// <see cref="CssQuotes"/>
        /// instance
        /// </returns>
        public static iText.StyledXmlParser.Css.Resolve.CssQuotes CreateQuotes(String quotesString, bool fallbackToDefault
                                                                               )
        {
            bool error = false;
            List <List <String> > quotes = new List <List <String> >(2);

            quotes.Add(new List <String>());
            quotes.Add(new List <String>());
            if (quotesString != null)
            {
                if (quotesString.Equals(CommonCssConstants.NONE))
                {
                    quotes[0].Add(EMPTY_QUOTE);
                    quotes[1].Add(EMPTY_QUOTE);
                    return(new iText.StyledXmlParser.Css.Resolve.CssQuotes(quotes[0], quotes[1]));
                }
                CssDeclarationValueTokenizer       tokenizer = new CssDeclarationValueTokenizer(quotesString);
                CssDeclarationValueTokenizer.Token token;
                for (int i = 0; ((token = tokenizer.GetNextValidToken()) != null); ++i)
                {
                    if (token.IsString())
                    {
                        quotes[i % 2].Add(token.GetValue());
                    }
                    else
                    {
                        error = true;
                        break;
                    }
                }
                if (quotes[0].Count == quotes[1].Count && !quotes[0].IsEmpty() && !error)
                {
                    return(new iText.StyledXmlParser.Css.Resolve.CssQuotes(quotes[0], quotes[1]));
                }
                else
                {
                    LogManager.GetLogger(typeof(iText.StyledXmlParser.Css.Resolve.CssQuotes)).Error(MessageFormatUtil.Format(iText.StyledXmlParser.LogMessageConstant
                                                                                                                             .QUOTES_PROPERTY_INVALID, quotesString));
                }
            }
            return(fallbackToDefault ? CreateDefaultQuotes() : null);
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Extracts shorthand properties as list of string lists from a string, where the top level
        /// list is shorthand property and the lower level list is properties included in shorthand property.
        /// </summary>
        /// <param name="str">the source string with shorthand properties</param>
        /// <returns>the list of string lists</returns>
        public static IList <IList <String> > ExtractShorthandProperties(String str)
        {
            IList <IList <String> >      result       = new List <IList <String> >();
            IList <String>               currentLayer = new List <String>();
            CssDeclarationValueTokenizer tokenizer    = new CssDeclarationValueTokenizer(str);

            CssDeclarationValueTokenizer.Token currentToken = tokenizer.GetNextValidToken();
            while (currentToken != null)
            {
                if (currentToken.GetType() == CssDeclarationValueTokenizer.TokenType.COMMA)
                {
                    result.Add(currentLayer);
                    currentLayer = new List <String>();
                }
                else
                {
                    currentLayer.Add(currentToken.GetValue());
                }
                currentToken = tokenizer.GetNextValidToken();
            }
            result.Add(currentLayer);
            return(result);
        }
        private static bool CheckNonPagesTargetCounterPresence(CssDeclaration declaration)
        {
            bool nonPagesTargetCounterPresent = false;

            if (CssConstants.CONTENT.Equals(declaration.GetProperty()))
            {
                CssDeclarationValueTokenizer       tokenizer = new CssDeclarationValueTokenizer(declaration.GetExpression());
                CssDeclarationValueTokenizer.Token token;
                while ((token = tokenizer.GetNextValidToken()) != null)
                {
                    if (token.IsString())
                    {
                        continue;
                    }
                    if (token.GetValue().StartsWith(CssConstants.TARGET_COUNTER + "("))
                    {
                        String paramsStr = token.GetValue().JSubstring(CssConstants.TARGET_COUNTER.Length + 1, token.GetValue().Length
                                                                       - 1);
                        String[] @params = iText.IO.Util.StringUtil.Split(paramsStr, ",");
                        nonPagesTargetCounterPresent = nonPagesTargetCounterPresent || (@params.Length >= TARGET_COUNTER_MIN_PARAMS_SIZE &&
                                                                                        !CheckTargetCounterParamsForPageOrPagesReferencePresence(@params));
                    }
                    else
                    {
                        if (token.GetValue().StartsWith(CssConstants.TARGET_COUNTERS + "("))
                        {
                            String paramsStr = token.GetValue().JSubstring(CssConstants.TARGET_COUNTERS.Length + 1, token.GetValue().Length
                                                                           - 1);
                            String[] @params = iText.IO.Util.StringUtil.Split(paramsStr, ",");
                            nonPagesTargetCounterPresent = nonPagesTargetCounterPresent || (@params.Length >= TARGET_COUNTERS_MIN_PARAMS_SIZE &&
                                                                                            !CheckTargetCounterParamsForPageOrPagesReferencePresence(@params));
                        }
                    }
                }
            }
            return(nonPagesTargetCounterPresent);
        }
Ejemplo n.º 9
0
        /// <summary>Resolves content.</summary>
        /// <param name="styles">the styles map</param>
        /// <param name="contentContainer">the content container</param>
        /// <param name="context">the CSS context</param>
        /// <returns>
        /// a list of
        /// <see cref="iText.StyledXmlParser.Node.INode"/>
        /// instances
        /// </returns>
        internal static IList <INode> ResolveContent(IDictionary <String, String> styles, INode contentContainer, CssContext
                                                     context)
        {
            String        contentStr = styles.Get(CssConstants.CONTENT);
            IList <INode> result     = new List <INode>();

            if (contentStr == null || CssConstants.NONE.Equals(contentStr) || CssConstants.NORMAL.Equals(contentStr))
            {
                return(null);
            }
            CssDeclarationValueTokenizer tokenizer = new CssDeclarationValueTokenizer(contentStr);

            CssDeclarationValueTokenizer.Token token;
            CssQuotes quotes = null;

            while ((token = tokenizer.GetNextValidToken()) != null)
            {
                if (token.IsString())
                {
                    result.Add(new CssContentPropertyResolver.ContentTextNode(contentContainer, token.GetValue()));
                }
                else
                {
                    if (token.GetValue().StartsWith(CssConstants.COUNTERS + "("))
                    {
                        String paramsStr = token.GetValue().JSubstring(CssConstants.COUNTERS.Length + 1, token.GetValue().Length -
                                                                       1);
                        String[] @params = iText.IO.Util.StringUtil.Split(paramsStr, ",");
                        if (@params.Length == 0)
                        {
                            return(ErrorFallback(contentStr));
                        }
                        // Counters are denoted by case-sensitive identifiers
                        String counterName          = @params[0].Trim();
                        String counterSeparationStr = @params[1].Trim();
                        counterSeparationStr = counterSeparationStr.JSubstring(1, counterSeparationStr.Length - 1);
                        String            listStyleType  = @params.Length > 2 ? @params[2].Trim() : null;
                        CssCounterManager counterManager = context.GetCounterManager();
                        INode             scope          = contentContainer;
                        if (CssConstants.PAGE.Equals(counterName))
                        {
                            result.Add(new PageCountElementNode(false, contentContainer));
                        }
                        else
                        {
                            if (CssConstants.PAGES.Equals(counterName))
                            {
                                result.Add(new PageCountElementNode(true, contentContainer));
                            }
                            else
                            {
                                String resolvedCounter = counterManager.ResolveCounters(counterName, counterSeparationStr, listStyleType,
                                                                                        scope);
                                if (resolvedCounter == null)
                                {
                                    logger.Error(MessageFormatUtil.Format(iText.Html2pdf.LogMessageConstant.UNABLE_TO_RESOLVE_COUNTER, counterName
                                                                          ));
                                }
                                else
                                {
                                    result.Add(new CssContentPropertyResolver.ContentTextNode(scope, resolvedCounter));
                                }
                            }
                        }
                    }
                    else
                    {
                        if (token.GetValue().StartsWith(CssConstants.COUNTER + "("))
                        {
                            String paramsStr = token.GetValue().JSubstring(CssConstants.COUNTER.Length + 1, token.GetValue().Length -
                                                                           1);
                            String[] @params = iText.IO.Util.StringUtil.Split(paramsStr, ",");
                            if (@params.Length == 0)
                            {
                                return(ErrorFallback(contentStr));
                            }
                            // Counters are denoted by case-sensitive identifiers
                            String            counterName    = @params[0].Trim();
                            String            listStyleType  = @params.Length > 1 ? @params[1].Trim() : null;
                            CssCounterManager counterManager = context.GetCounterManager();
                            INode             scope          = contentContainer;
                            if (CssConstants.PAGE.Equals(counterName))
                            {
                                result.Add(new PageCountElementNode(false, contentContainer));
                            }
                            else
                            {
                                if (CssConstants.PAGES.Equals(counterName))
                                {
                                    result.Add(new PageCountElementNode(true, contentContainer));
                                }
                                else
                                {
                                    String resolvedCounter = counterManager.ResolveCounter(counterName, listStyleType, scope);
                                    if (resolvedCounter == null)
                                    {
                                        logger.Error(MessageFormatUtil.Format(iText.Html2pdf.LogMessageConstant.UNABLE_TO_RESOLVE_COUNTER, counterName
                                                                              ));
                                    }
                                    else
                                    {
                                        result.Add(new CssContentPropertyResolver.ContentTextNode(scope, resolvedCounter));
                                    }
                                }
                            }
                        }
                        else
                        {
                            if (token.GetValue().StartsWith("url("))
                            {
                                IDictionary <String, String> attributes = new Dictionary <String, String>();
                                attributes.Put(AttributeConstants.SRC, CssUtils.ExtractUrl(token.GetValue()));
                                //TODO: probably should add user agent styles on CssContentElementNode creation, not here.
                                attributes.Put(AttributeConstants.STYLE, CssConstants.DISPLAY + ":" + CssConstants.INLINE_BLOCK);
                                result.Add(new CssContentElementNode(contentContainer, TagConstants.IMG, attributes));
                            }
                            else
                            {
                                if (CssGradientUtil.IsCssLinearGradientValue(token.GetValue()))
                                {
                                    IDictionary <String, String> attributes = new Dictionary <String, String>();
                                    attributes.Put(AttributeConstants.STYLE, CssConstants.BACKGROUND_IMAGE + ":" + token.GetValue() + ";" + CssConstants
                                                   .HEIGHT + ":" + CssConstants.INHERIT + ";" + CssConstants.WIDTH + ":" + CssConstants.INHERIT + ";");
                                    result.Add(new CssContentElementNode(contentContainer, TagConstants.DIV, attributes));
                                }
                                else
                                {
                                    if (token.GetValue().StartsWith("attr(") && contentContainer is CssPseudoElementNode)
                                    {
                                        int endBracket = token.GetValue().IndexOf(')');
                                        if (endBracket > 5)
                                        {
                                            String attrName = token.GetValue().JSubstring(5, endBracket);
                                            if (attrName.Contains("(") || attrName.Contains(" ") || attrName.Contains("'") || attrName.Contains("\""))
                                            {
                                                return(ErrorFallback(contentStr));
                                            }
                                            IElementNode element = (IElementNode)contentContainer.ParentNode();
                                            String       value   = element.GetAttribute(attrName);
                                            result.Add(new CssContentPropertyResolver.ContentTextNode(contentContainer, value == null ? "" : value));
                                        }
                                    }
                                    else
                                    {
                                        if (token.GetValue().EndsWith("quote") && contentContainer is IStylesContainer)
                                        {
                                            if (quotes == null)
                                            {
                                                quotes = CssQuotes.CreateQuotes(styles.Get(CssConstants.QUOTES), true);
                                            }
                                            String value = quotes.ResolveQuote(token.GetValue(), context);
                                            if (value == null)
                                            {
                                                return(ErrorFallback(contentStr));
                                            }
                                            result.Add(new CssContentPropertyResolver.ContentTextNode(contentContainer, value));
                                        }
                                        else
                                        {
                                            if (token.GetValue().StartsWith(CssConstants.ELEMENT + "(") && contentContainer is PageMarginBoxContextNode
                                                )
                                            {
                                                String paramsStr = token.GetValue().JSubstring(CssConstants.ELEMENT.Length + 1, token.GetValue().Length -
                                                                                               1);
                                                String[] @params = iText.IO.Util.StringUtil.Split(paramsStr, ",");
                                                if (@params.Length == 0)
                                                {
                                                    return(ErrorFallback(contentStr));
                                                }
                                                String name = @params[0].Trim();
                                                String runningElementOccurrence = null;
                                                if (@params.Length > 1)
                                                {
                                                    runningElementOccurrence = @params[1].Trim();
                                                }
                                                result.Add(new PageMarginRunningElementNode(name, runningElementOccurrence));
                                            }
                                            else
                                            {
                                                return(ErrorFallback(contentStr));
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            return(result);
        }
Ejemplo n.º 10
0
        private static void AddStopColors(AbstractLinearGradientBuilder builder, IList <String> argumentsList, int
                                          stopsStartIndex, float emValue, float remValue)
        {
            GradientColorStop lastCreatedStopColor = null;
            int lastStopIndex = argumentsList.Count - 1;

            for (int i = stopsStartIndex; i <= lastStopIndex; ++i)
            {
                String         argument     = argumentsList[i];
                IList <String> elementsList = new List <String>();
                CssDeclarationValueTokenizer       tokenizer = new CssDeclarationValueTokenizer(argument);
                CssDeclarationValueTokenizer.Token nextToken;
                while ((nextToken = tokenizer.GetNextValidToken()) != null)
                {
                    elementsList.Add(nextToken.GetValue());
                }
                // cases: color, color + offset, color + offset + offset, offset (hint)
                if (elementsList.IsEmpty() || elementsList.Count > 3)
                {
                    throw new StyledXMLParserException(MessageFormatUtil.Format(StyledXMLParserException.INVALID_GRADIENT_COLOR_STOP_VALUE
                                                                                , argument));
                }
                if (CssUtils.IsColorProperty(elementsList[0]))
                {
                    float[] rgba = CssUtils.ParseRgbaColor(elementsList[0]);
                    if (elementsList.Count == 1)
                    {
                        UnitValue offset = i == stopsStartIndex ? new UnitValue(UnitValue.PERCENT, 0f) : i == lastStopIndex ? new
                                           UnitValue(UnitValue.PERCENT, 100f) : null;
                        lastCreatedStopColor = CreateStopColor(rgba, offset);
                        builder.AddColorStop(lastCreatedStopColor);
                    }
                    else
                    {
                        for (int j = 1; j < elementsList.Count; ++j)
                        {
                            if (CssUtils.IsNumericValue(elementsList[j]))
                            {
                                // the numeric value is invalid in linear gradient function.
                                // So check it here as parsing method will use the default pt metric
                                throw new StyledXMLParserException(MessageFormatUtil.Format(StyledXMLParserException.INVALID_GRADIENT_COLOR_STOP_VALUE
                                                                                            , argument));
                            }
                            UnitValue offset = CssUtils.ParseLengthValueToPt(elementsList[j], emValue, remValue);
                            if (offset == null)
                            {
                                throw new StyledXMLParserException(MessageFormatUtil.Format(StyledXMLParserException.INVALID_GRADIENT_COLOR_STOP_VALUE
                                                                                            , argument));
                            }
                            lastCreatedStopColor = CreateStopColor(rgba, offset);
                            builder.AddColorStop(lastCreatedStopColor);
                        }
                    }
                }
                else
                {
                    // it should be a color hint case
                    if (elementsList.Count != 1 || lastCreatedStopColor == null || lastCreatedStopColor.GetHintOffsetType() !=
                        GradientColorStop.HintOffsetType.NONE || i == lastStopIndex)
                    {
                        // hint is not a single value, or no color at the beginning,
                        // or two hints in a row, or hint as a last value
                        throw new StyledXMLParserException(MessageFormatUtil.Format(StyledXMLParserException.INVALID_GRADIENT_COLOR_STOP_VALUE
                                                                                    , argument));
                    }
                    UnitValue hint = CssUtils.ParseLengthValueToPt(elementsList[0], emValue, remValue);
                    if (hint == null)
                    {
                        throw new StyledXMLParserException(MessageFormatUtil.Format(StyledXMLParserException.INVALID_GRADIENT_COLOR_STOP_VALUE
                                                                                    , argument));
                    }
                    if (hint.GetUnitType() == UnitValue.PERCENT)
                    {
                        lastCreatedStopColor.SetHint(hint.GetValue() / 100, GradientColorStop.HintOffsetType.RELATIVE_ON_GRADIENT);
                    }
                    else
                    {
                        lastCreatedStopColor.SetHint(hint.GetValue(), GradientColorStop.HintOffsetType.ABSOLUTE_ON_GRADIENT);
                    }
                }
            }
        }