From cc0dccf6319f8d47456951e89be7cb2747274508 Mon Sep 17 00:00:00 2001
From: Justin Haygood
Date: Thu, 16 Jul 2026 18:59:43 -0400
Subject: [PATCH 1/4] Port of CSS engine from PeachPDF (ExCSS-based)
Port of CSS engine from PeachPDF
---
Source/HtmlRenderer/Adapters/RAdapter.cs | 11 +-
Source/HtmlRenderer/Core/CssData.cs | 935 +++++++--
Source/HtmlRenderer/Core/CssDefaults.cs | 111 ++
.../CssEngine/BackgroundPositionGrammar.cs | 189 ++
.../Core/CssEngine/BackgroundSizeGrammar.cs | 88 +
.../Core/CssEngine/Calc/CalcCategory.cs | 25 +
.../Core/CssEngine/Calc/CalcEvaluator.cs | 121 ++
.../Core/CssEngine/Calc/CalcNode.cs | 98 +
.../Core/CssEngine/Calc/CalcParser.cs | 238 +++
.../Core/CssEngine/Calc/CalcSerializer.cs | 259 +++
.../Core/CssEngine/Calc/CalcTypeChecker.cs | 150 ++
.../Core/CssEngine/Calc/CalcValue.cs | 33 +
.../Core/CssEngine/Calc/CalcValueConverter.cs | 41 +
.../Core/CssEngine/Conditions/AndCondition.cs | 31 +
.../Conditions/DeclarationCondition.cs | 28 +
.../CssEngine/Conditions/EmptyCondition.cs | 16 +
.../CssEngine/Conditions/GroupCondition.cs | 34 +
.../Core/CssEngine/Conditions/NotCondition.cs | 33 +
.../Core/CssEngine/Conditions/OrCondition.cs | 35 +
.../CssEngine/Enumerations/AlignContent.cs | 18 +
.../Core/CssEngine/Enumerations/AlignItem.cs | 17 +
.../Enumerations/AnimationDirection.cs | 10 +
.../Enumerations/AnimationFillStyle.cs | 10 +
.../Enumerations/BackgroundAttachment.cs | 9 +
.../Enumerations/BackgroundRepeat.cs | 10 +
.../Core/CssEngine/Enumerations/BlendMode.cs | 22 +
.../CssEngine/Enumerations/BorderRepeat.cs | 9 +
.../Core/CssEngine/Enumerations/BoxModel.cs | 9 +
.../Core/CssEngine/Enumerations/BreakMode.cs | 16 +
.../Core/CssEngine/Enumerations/ClearMode.cs | 10 +
.../Core/CssEngine/Enumerations/Colors.cs | 205 ++
.../CssEngine/Enumerations/Combinators.cs | 20 +
.../CssEngine/Enumerations/ContainerType.cs | 9 +
.../CssEngine/Enumerations/DirectionMode.cs | 8 +
.../CssEngine/Enumerations/DisplayMode.cs | 25 +
.../CssEngine/Enumerations/FeatureNames.cs | 48 +
.../Core/CssEngine/Enumerations/FillRule.cs | 8 +
.../CssEngine/Enumerations/FlexDirection.cs | 10 +
.../Core/CssEngine/Enumerations/FlexWrap.cs | 9 +
.../Core/CssEngine/Enumerations/Floating.cs | 9 +
.../Core/CssEngine/Enumerations/FontSize.cs | 16 +
.../CssEngine/Enumerations/FontStretch.cs | 15 +
.../Core/CssEngine/Enumerations/FontStyle.cs | 9 +
.../CssEngine/Enumerations/FontVariant.cs | 8 +
.../Core/CssEngine/Enumerations/FontWeight.cs | 10 +
.../CssEngine/Enumerations/FunctionNames.cs | 57 +
.../Enumerations/HorizontalAlignment.cs | 10 +
.../CssEngine/Enumerations/HoverAbility.cs | 9 +
.../CssEngine/Enumerations/IntrinsicSizing.cs | 10 +
.../CssEngine/Enumerations/JustifyContent.cs | 18 +
.../Core/CssEngine/Enumerations/Keywords.cs | 358 ++++
.../Core/CssEngine/Enumerations/LineStyle.cs | 16 +
.../CssEngine/Enumerations/ListPosition.cs | 8 +
.../Core/CssEngine/Enumerations/ListStyle.cs | 19 +
.../CssEngine/Enumerations/ObjectFitting.cs | 11 +
.../Core/CssEngine/Enumerations/Overflow.cs | 10 +
.../CssEngine/Enumerations/OverflowWrap.cs | 8 +
.../Core/CssEngine/Enumerations/ParseError.cs | 20 +
.../Core/CssEngine/Enumerations/PdfTagType.cs | 53 +
.../Core/CssEngine/Enumerations/PlayState.cs | 8 +
.../CssEngine/Enumerations/PointerAccuracy.cs | 9 +
.../CssEngine/Enumerations/PositionMode.cs | 11 +
.../CssEngine/Enumerations/PropertyFlags.cs | 15 +
.../CssEngine/Enumerations/PropertyNames.cs | 248 +++
.../Enumerations/PseudoClassNames.cs | 55 +
.../Enumerations/PseudoElementNames.cs | 14 +
.../Core/CssEngine/Enumerations/QuirksMode.cs | 9 +
.../Core/CssEngine/Enumerations/RuleNames.cs | 17 +
.../Core/CssEngine/Enumerations/RuleType.cs | 24 +
.../CssEngine/Enumerations/ScriptingState.cs | 9 +
.../CssEngine/Enumerations/StrokeLinecap.cs | 9 +
.../CssEngine/Enumerations/StrokeLinejoin.cs | 9 +
.../CssEngine/Enumerations/SystemCursor.cs | 42 +
.../Core/CssEngine/Enumerations/SystemFont.cs | 12 +
.../CssEngine/Enumerations/TextAlignLast.cs | 13 +
.../Core/CssEngine/Enumerations/TextAnchor.cs | 9 +
.../Enumerations/TextDecorationLine.cs | 10 +
.../Enumerations/TextDecorationStyle.cs | 11 +
.../CssEngine/Enumerations/TextJustify.cs | 15 +
.../CssEngine/Enumerations/TextTransform.cs | 11 +
.../Core/CssEngine/Enumerations/TokenType.cs | 39 +
.../CssEngine/Enumerations/UnicodeMode.cs | 12 +
.../Core/CssEngine/Enumerations/UnitNames.cs | 32 +
.../CssEngine/Enumerations/UpdateFrequency.cs | 9 +
.../Enumerations/VerticalAlignment.cs | 14 +
.../Core/CssEngine/Enumerations/Visibility.cs | 9 +
.../Core/CssEngine/Enumerations/Whitespace.cs | 11 +
.../Core/CssEngine/Enumerations/WordBreak.cs | 9 +
.../CssEngine/Extensions/CharExtensions.cs | 130 ++
.../Extensions/CollectionExtensions.cs | 36 +
.../CssEngine/Extensions/FormatExtensions.cs | 28 +
.../Extensions/PortableExtensions.cs | 27 +
.../CssEngine/Extensions/StringExtensions.cs | 106 +
.../Extensions/ValueConverterExtensions.cs | 190 ++
.../CssEngine/Extensions/ValueExtensions.cs | 514 +++++
.../Factories/AttributeSelectorFactory.cs | 52 +
.../Factories/MediaFeatureFactory.cs | 85 +
.../CssEngine/Factories/PropertyFactory.cs | 450 +++++
.../Factories/PseudoClassSelectorFactory.cs | 84 +
.../Factories/PseudoElementSelectorFactory.cs | 48 +
.../Formatting/CompressedStyleFormatter.cs | 123 ++
.../CssEngine/Functions/DocumentFunction.cs | 23 +
.../CssEngine/Functions/DomainFunction.cs | 20 +
.../CssEngine/Functions/IConditionFunction.cs | 7 +
.../CssEngine/Functions/IDocumentFunction.cs | 8 +
.../CssEngine/Functions/RegexpFunction.cs | 19 +
.../Core/CssEngine/Functions/UrlFunction.cs | 17 +
.../CssEngine/Functions/UrlPrefixFunction.cs | 16 +
.../MediaFeatures/AspectRatioMediaFeature.cs | 11 +
.../MediaFeatures/ColorIndexMediaFeature.cs | 16 +
.../MediaFeatures/ColorMediaFeature.cs | 16 +
.../DeviceAspectRatioMediaFeature.cs | 11 +
.../MediaFeatures/DeviceHeightMediaFeature.cs | 11 +
.../MediaFeatures/DevicePixelRatioFeature.cs | 11 +
.../MediaFeatures/DeviceWidthMediaFeature.cs | 11 +
.../MediaFeatures/GridMediaFeature.cs | 11 +
.../MediaFeatures/HeightMediaFeature.cs | 11 +
.../MediaFeatures/HoverMediaFeature.cs | 13 +
.../CssEngine/MediaFeatures/MediaFeature.cs | 69 +
.../MediaFeatures/MonochromeMediaFeature.cs | 14 +
.../MediaFeatures/OrientationMediaFeature.cs | 13 +
.../MediaFeatures/PointerMediaFeature.cs | 13 +
.../MediaFeatures/ResolutionMediaFeature.cs | 11 +
.../MediaFeatures/ScanMediaFeature.cs | 14 +
.../MediaFeatures/ScriptingMediaFeature.cs | 13 +
.../MediaFeatures/SizeMediaFeature.cs | 11 +
.../MediaFeatures/UnknownMediaFeature.cs | 11 +
.../UpdateFrequencyMediaFeature.cs | 13 +
.../MediaFeatures/WidthMediaFeature.cs | 11 +
.../Core/CssEngine/Model/Combinator.cs | 80 +
.../Core/CssEngine/Model/Comment.cs | 19 +
.../Core/CssEngine/Model/Converters.cs | 513 +++++
.../Core/CssEngine/Model/IMediaFeature.cs | 9 +
.../Core/CssEngine/Model/ISelector.cs | 8 +
.../Core/CssEngine/Model/IStyleFormattable.cs | 9 +
.../Core/CssEngine/Model/IStyleFormatter.cs | 18 +
.../Core/CssEngine/Model/IStylesheetNode.cs | 10 +
.../Core/CssEngine/Model/IValueConverter.cs | 21 +
.../HtmlRenderer/Core/CssEngine/Model/Map.cs | 651 +++++++
.../Core/CssEngine/Model/MediaList.cs | 76 +
.../Core/CssEngine/Model/Medium.cs | 49 +
.../Core/CssEngine/Model/ParseException.cs | 11 +
.../Core/CssEngine/Model/ParserExtensions.cs | 162 ++
.../Core/CssEngine/Model/ParserOptions.cs | 13 +
.../HtmlRenderer/Core/CssEngine/Model/Pool.cs | 78 +
.../Core/CssEngine/Model/Priority.cs | 103 +
.../Core/CssEngine/Model/StyleDeclaration.cs | 1706 +++++++++++++++++
.../Core/CssEngine/Model/Stylesheet.cs | 70 +
.../Core/CssEngine/Model/StylesheetNode.cs | 92 +
.../Core/CssEngine/Model/StylesheetText.cs | 31 +
.../Core/CssEngine/Model/Symbols.cs | 90 +
.../Core/CssEngine/Model/TaskEx.cs | 26 +
.../Core/CssEngine/Model/TextPosition.cs | 97 +
.../Core/CssEngine/Model/TextRange.cs | 55 +
.../Core/CssEngine/Model/TextSource.cs | 230 +++
.../Core/CssEngine/Model/TokenValue.cs | 58 +
.../Core/CssEngine/Model/TransformMatrix.cs | 106 +
.../HtmlRenderer/Core/CssEngine/Model/Url.cs | 873 +++++++++
.../Core/CssEngine/Model/ValueBuilder.cs | 136 ++
.../Core/CssEngine/Parser/Lexer.cs | 1225 ++++++++++++
.../Core/CssEngine/Parser/LexerBase.cs | 173 ++
.../CssEngine/Parser/SelectorConstructor.cs | 918 +++++++++
.../CssEngine/Parser/StylesheetComposer.cs | 1313 +++++++++++++
.../Core/CssEngine/Parser/StylesheetParser.cs | 213 ++
.../Core/CssEngine/Parser/TokenizerError.cs | 17 +
.../Core/CssEngine/Rules/CharsetRule.cs | 26 +
.../Core/CssEngine/Rules/ConditionRule.cs | 10 +
.../Core/CssEngine/Rules/ContainerRule.cs | 32 +
.../Core/CssEngine/Rules/DeclarationRule.cs | 130 ++
.../Core/CssEngine/Rules/DocumentRule.cs | 41 +
.../Core/CssEngine/Rules/FontFaceRule.cs | 63 +
.../Core/CssEngine/Rules/GroupingRule.cs | 34 +
.../Core/CssEngine/Rules/ICharsetRule.cs | 7 +
.../Core/CssEngine/Rules/IConditionRule.cs | 7 +
.../Core/CssEngine/Rules/IContainer.cs | 8 +
.../Core/CssEngine/Rules/IFontFaceRule.cs | 14 +
.../Core/CssEngine/Rules/IGroupingRule.cs | 9 +
.../Core/CssEngine/Rules/IImportRule.cs | 8 +
.../Core/CssEngine/Rules/IKeyframeRule.cs | 9 +
.../Core/CssEngine/Rules/IKeyframesRule.cs | 11 +
.../Core/CssEngine/Rules/IMarginRule.cs | 8 +
.../Core/CssEngine/Rules/IMediaRule.cs | 7 +
.../Core/CssEngine/Rules/INamespaceRule.cs | 8 +
.../Core/CssEngine/Rules/IPageRule.cs | 8 +
.../Core/CssEngine/Rules/IRule.cs | 10 +
.../Core/CssEngine/Rules/IRuleCreator.cs | 7 +
.../Core/CssEngine/Rules/IRuleList.cs | 10 +
.../Core/CssEngine/Rules/IStyleRule.cs | 9 +
.../Core/CssEngine/Rules/ISupportsRule.cs | 7 +
.../Core/CssEngine/Rules/ImportRule.cs | 36 +
.../Core/CssEngine/Rules/KeyframeRule.cs | 37 +
.../Core/CssEngine/Rules/KeyframesRule.cs | 53 +
.../Core/CssEngine/Rules/MarginStyleRule.cs | 32 +
.../Core/CssEngine/Rules/MediaRule.cs | 27 +
.../Core/CssEngine/Rules/NamespaceRule.cs | 66 +
.../Core/CssEngine/Rules/PageRule.cs | 47 +
.../HtmlRenderer/Core/CssEngine/Rules/Rule.cs | 65 +
.../Core/CssEngine/Rules/RuleList.cs | 78 +
.../Core/CssEngine/Rules/StyleRule.cs | 33 +
.../Core/CssEngine/Rules/SupportsRule.cs | 43 +
.../Core/CssEngine/Rules/UnknownRule.cs | 20 +
.../Core/CssEngine/Rules/ViewportRule.cs | 15 +
.../Core/CssEngine/Selectors/AllSelector.cs | 14 +
.../Selectors/AttrAvailableSelector.cs | 10 +
.../CssEngine/Selectors/AttrBeginsSelector.cs | 10 +
.../Selectors/AttrContainsSelector.cs | 10 +
.../CssEngine/Selectors/AttrEndsSelector.cs | 10 +
.../CssEngine/Selectors/AttrHyphenSelector.cs | 10 +
.../CssEngine/Selectors/AttrListSelector.cs | 10 +
.../CssEngine/Selectors/AttrMatchSelector.cs | 10 +
.../Selectors/AttrNotMatchSelector.cs | 10 +
.../CssEngine/Selectors/AttrSelectorBase.cs | 13 +
.../Core/CssEngine/Selectors/ChildSelector.cs | 49 +
.../Core/CssEngine/Selectors/ClassSelector.cs | 17 +
.../CssEngine/Selectors/CombinatorSelector.cs | 8 +
.../CssEngine/Selectors/ComplexSelector.cs | 80 +
.../CssEngine/Selectors/CompoundSelector.cs | 12 +
.../CssEngine/Selectors/FirstChildSelector.cs | 11 +
.../Selectors/FirstColumnSelector.cs | 10 +
.../CssEngine/Selectors/FirstTypeSelector.cs | 10 +
.../Core/CssEngine/Selectors/HasSelector.cs | 33 +
.../Core/CssEngine/Selectors/IAttrSelector.cs | 8 +
.../Core/CssEngine/Selectors/IdSelector.cs | 17 +
.../CssEngine/Selectors/KeyframeSelector.cs | 75 +
.../CssEngine/Selectors/LastChildSelector.cs | 10 +
.../CssEngine/Selectors/LastColumnSelector.cs | 10 +
.../CssEngine/Selectors/LastTypeSelector.cs | 10 +
.../Core/CssEngine/Selectors/ListSelector.cs | 34 +
.../CssEngine/Selectors/MatchesSelector.cs | 37 +
.../CssEngine/Selectors/NamespaceSelector.cs | 14 +
.../Core/CssEngine/Selectors/NotSelector.cs | 30 +
.../CssEngine/Selectors/OnlyChildSelector.cs | 10 +
.../CssEngine/Selectors/OnlyOfTypeSelector.cs | 10 +
.../Selectors/PseudoClassSelector.cs | 17 +
.../Selectors/PseudoElementSelector.cs | 17 +
.../Core/CssEngine/Selectors/SelectorBase.cs | 21 +
.../Core/CssEngine/Selectors/Selectors.cs | 55 +
.../Core/CssEngine/Selectors/TypeSelector.cs | 17 +
.../CssEngine/Selectors/UnknownSelector.cs | 16 +
.../Animation/AnimationDelayProperty.cs | 15 +
.../Animation/AnimationDirectionProperty.cs | 15 +
.../Animation/AnimationDurationProperty.cs | 14 +
.../Animation/AnimationFillModeProperty.cs | 15 +
.../AnimationIterationCountProperty.cs | 15 +
.../Animation/AnimationNameProperty.cs | 15 +
.../Animation/AnimationPlayStateProperty.cs | 15 +
.../Animation/AnimationProperty.cs | 26 +
.../AnimationTimingFunctionProperty.cs | 15 +
.../BackgroundAttachmentProperty.cs | 15 +
.../Background/BackgroundClipProperty.cs | 15 +
.../Background/BackgroundColorProperty.cs | 14 +
.../Background/BackgroundImageProperty.cs | 14 +
.../Background/BackgroundOriginProperty.cs | 15 +
.../Background/BackgroundPositionProperty.cs | 15 +
.../Background/BackgroundProperty.cs | 38 +
.../Background/BackgroundRepeatProperty.cs | 15 +
.../Background/BackgroundSizeProperty.cs | 15 +
.../Border/BorderBottomColorProperty.cs | 15 +
.../Border/BorderBottomProperty.cs | 20 +
.../Border/BorderBottomStyleProperty.cs | 15 +
.../Border/BorderBottomWidthProperty.cs | 14 +
.../Border/BorderCollapseProperty.cs | 14 +
.../Border/BorderColorProperty.cs | 16 +
.../Border/BorderLeftColorProperty.cs | 15 +
.../Border/BorderLeftProperty.cs | 20 +
.../Border/BorderLeftStyleProperty.cs | 15 +
.../Border/BorderLeftWidthProperty.cs | 14 +
.../StyleProperties/Border/BorderProperty.cs | 26 +
.../Border/BorderRightColorProperty.cs | 15 +
.../Border/BorderRightProperty.cs | 20 +
.../Border/BorderRightStyleProperty.cs | 15 +
.../Border/BorderRightWidthProperty.cs | 14 +
.../Border/BorderSpacingProperty.cs | 15 +
.../Border/BorderStyleProperty.cs | 16 +
.../Border/BorderTopColorProperty.cs | 15 +
.../Border/BorderTopProperty.cs | 20 +
.../Border/BorderTopStyleProperty.cs | 15 +
.../Border/BorderTopWidthProperty.cs | 14 +
.../Border/BorderWidthProperty.cs | 16 +
.../BorderImage/BorderImageOutsetProperty.cs | 15 +
.../BorderImage/BorderImageProperty.cs | 26 +
.../BorderImage/BorderImageRepeatProperty.cs | 15 +
.../BorderImage/BorderImageSliceProperty.cs | 23 +
.../BorderImage/BorderImageSourceProperty.cs | 14 +
.../BorderImage/BorderImageWidthProperty.cs | 15 +
.../BorderBottomLeftRadiusProperty.cs | 15 +
.../BorderBottomRightRadiusProperty.cs | 15 +
.../BorderRadius/BorderRadiusProperty.cs | 14 +
.../BorderTopLeftRadiusProperty.cs | 15 +
.../BorderTopRightRadiusProperty.cs | 15 +
.../StyleProperties/Box/BoxDecorationBreak.cs | 14 +
.../StyleProperties/Box/BoxShadowProperty.cs | 14 +
.../StyleProperties/Box/BoxSizingProperty.cs | 13 +
.../Box/MarginBottomProperty.cs | 15 +
.../StyleProperties/Box/MarginLeftProperty.cs | 15 +
.../StyleProperties/Box/MarginProperty.cs | 17 +
.../Box/MarginRightProperty.cs | 15 +
.../StyleProperties/Box/MarginTopProperty.cs | 15 +
.../Box/PaddingBottomProperty.cs | 15 +
.../Box/PaddingLeftProperty.cs | 15 +
.../StyleProperties/Box/PaddingProperty.cs | 17 +
.../Box/PaddingRightProperty .cs | 15 +
.../StyleProperties/Box/PaddingTopProperty.cs | 15 +
.../Break/BreakAfterProperty.cs | 15 +
.../Break/BreakBeforeProperty.cs | 15 +
.../Break/BreakInsideProperty.cs | 15 +
.../Break/PageBreakAfterProperty.cs | 15 +
.../Break/PageBreakBeforeProperty.cs | 15 +
.../Break/PageBreakInsideProperty.cs | 17 +
.../StyleProperties/CaptionSideProperty.cs | 13 +
.../Columns/ColumnCountProperty.cs | 14 +
.../Columns/ColumnFillProperty.cs | 14 +
.../Columns/ColumnGapProperty.cs | 17 +
.../Columns/ColumnRuleColorProperty.cs | 14 +
.../Columns/ColumnRuleProperty.cs | 19 +
.../Columns/ColumnRuleStyleProperty.cs | 15 +
.../Columns/ColumnRuleWidthProperty.cs | 14 +
.../Columns/ColumnSpanProperty.cs | 14 +
.../Columns/ColumnWidthProperty.cs | 15 +
.../Columns/ColumnsProperty.cs | 18 +
.../Container/ContainerNameProperty.cs | 15 +
.../Container/ContainerTypeProperty.cs | 15 +
.../StyleProperties/ContentProperty.cs | 61 +
.../Coordinate/BottomProperty.cs | 15 +
.../Coordinate/HeightProperty.cs | 15 +
.../Coordinate/LeftProperty.cs | 15 +
.../Coordinate/MaxHeightProperty.cs | 15 +
.../Coordinate/MaxWidthProperty.cs | 15 +
.../Coordinate/MinHeightProperty.cs | 15 +
.../Coordinate/MinWidthProperty.cs | 15 +
.../Coordinate/RightProperty .cs | 15 +
.../StyleProperties/Coordinate/TopProperty.cs | 15 +
.../Coordinate/WidthProperty.cs | 15 +
.../StyleProperties/CursorProperty.cs | 21 +
.../StyleProperties/CustomProperty.cs | 12 +
.../StyleProperties/EmptyCellsProperty.cs | 14 +
.../StyleProperties/FeatureProperty.cs | 16 +
.../Fill/FillOpacityProperty.cs | 14 +
.../StyleProperties/Fill/FillProperty.cs | 14 +
.../StyleProperties/Fill/FillRuleProperty.cs | 15 +
.../Flexbox/AlignContentProperty.cs | 13 +
.../Flexbox/AlignItemsProperty.cs | 14 +
.../Flexbox/AlignSelfProperty.cs | 13 +
.../Flexbox/FlexBasisProperty.cs | 13 +
.../Flexbox/FlexDirectionProperty.cs | 13 +
.../Flexbox/FlexFlowProperty.cs | 13 +
.../Flexbox/FlexGrowProperty.cs | 13 +
.../StyleProperties/Flexbox/FlexProperty.cs | 13 +
.../Flexbox/FlexShrinkProperty.cs | 13 +
.../Flexbox/FlexWrapProperty.cs | 13 +
.../Flexbox/JustifyContentProperty.cs | 14 +
.../StyleProperties/Flexbox/OrderProperty.cs | 15 +
.../StyleProperties/Flow/ClearProperty.cs | 15 +
.../StyleProperties/Flow/DisplayProperty.cs | 15 +
.../StyleProperties/Flow/FloatProperty.cs | 14 +
.../StyleProperties/Flow/OverflowProperty.cs | 15 +
.../StyleProperties/Flow/PositionProperty.cs | 15 +
.../StyleProperties/Flow/ZIndexProperty.cs | 14 +
.../StyleProperties/Font/ColorProperty.cs | 14 +
.../Font/FontFamilyProperty.cs | 48 +
.../StyleProperties/Font/FontProperty.cs | 27 +
.../Font/FontSizeAdjustProperty.cs | 14 +
.../StyleProperties/Font/FontSizeProperty.cs | 15 +
.../Font/FontStretchProperty.cs | 15 +
.../StyleProperties/Font/FontStyleProperty.cs | 15 +
.../Font/FontVariantProperty.cs | 15 +
.../Font/FontWeightProperty.cs | 17 +
.../Font/LetterSpacingProperty.cs | 14 +
.../Font/LineHeightProperty.cs | 15 +
.../StyleProperties/Font/SrcProperty.cs | 12 +
.../Font/UnicodeRangeProperty.cs | 12 +
.../Font/WordSpacingProperty.cs | 15 +
.../CssEngine/StyleProperties/GapProperty.cs | 15 +
.../StyleProperties/HyphensProperty.cs | 18 +
.../CssEngine/StyleProperties/IProperties .cs | 14 +
.../CssEngine/StyleProperties/IProperty.cs | 10 +
.../StyleProperties/IPropertyValue.cs | 9 +
.../List/CounterIncrementProperty.cs | 17 +
.../List/CounterResetProperty.cs | 17 +
.../List/ListStyleImageProperty.cs | 14 +
.../List/ListStylePositionProperty.cs | 15 +
.../StyleProperties/List/ListStyleProperty.cs | 19 +
.../List/ListStyleTypeProperty.cs | 15 +
.../StyleProperties/OrphansProperty.cs | 14 +
.../Outline/OutlineColorProperty.cs | 15 +
.../Outline/OutlineProperty.cs | 19 +
.../Outline/OutlineStyleProperty.cs | 15 +
.../Outline/OutlineWidthProperty.cs | 14 +
.../StyleProperties/PageNameProperty.cs | 13 +
.../StyleProperties/PageSizeProperty.cs | 13 +
.../StyleProperties/PdfTagTypeProperty.cs | 11 +
.../CssEngine/StyleProperties/Property.cs | 65 +
.../StyleProperties/QuotesProperty.cs | 15 +
.../StyleProperties/RowGapProperty.cs | 15 +
.../StyleProperties/ShorthandProperty.cs | 25 +
.../Sizing/ObjectFitProperty.cs | 15 +
.../Sizing/ObjectPositionProperty.cs | 14 +
.../StyleProperties/StringSetProperty.cs | 18 +
.../Stroke/StrokeDasharrayProperty.cs | 14 +
.../Stroke/StrokeDashoffsetProperty.cs | 14 +
.../Stroke/StrokeLinecapProperty.cs | 15 +
.../Stroke/StrokeLinejoinProperty.cs | 14 +
.../Stroke/StrokeMiterlimitProperty.cs | 14 +
.../Stroke/StrokeOpacityProperty.cs | 14 +
.../StyleProperties/Stroke/StrokeProperty.cs | 14 +
.../Stroke/StrokeWidthProperty.cs | 14 +
.../StyleProperties/TableLayoutProperty.cs | 14 +
.../StyleProperties/Text/DirectionProperty.cs | 15 +
.../Text/OverflowWrapProperty.cs | 14 +
.../Text/TextAlignLastProperty.cs | 14 +
.../StyleProperties/Text/TextAlignProperty.cs | 15 +
.../Text/TextAnchorProperty.cs | 14 +
.../Text/TextDecorationColorProperty.cs | 14 +
.../Text/TextDecorationLineProperty.cs | 14 +
.../Text/TextDecorationProperty.cs | 19 +
.../Text/TextDecorationStyleProperty.cs | 15 +
.../Text/TextIndentProperty.cs | 15 +
.../Text/TextJustifyProperty.cs | 14 +
.../Text/TextShadowProperty.cs | 14 +
.../Text/TextTransformProperty.cs | 15 +
.../Text/VerticalAlignProperty.cs | 17 +
.../Text/WhiteSpaceProperty.cs | 15 +
.../StyleProperties/Text/WordBreakProperty.cs | 14 +
.../Transform/PerspectiveOriginProperty.cs | 31 +
.../Transform/PerspectiveProperty.cs | 15 +
.../Transform/TransformOriginProperty.cs | 35 +
.../Transform/TransformProperty.cs | 15 +
.../Transform/TransformStyleProperty.cs | 15 +
.../Transition/TransitionDelayProperty.cs | 15 +
.../Transition/TransitionDurationProperty.cs | 15 +
.../Transition/TransitionProperty.cs | 23 +
.../Transition/TransitionPropertyProperty.cs | 15 +
.../TransitionTimingFunctionProperty.cs | 15 +
.../UnicodeBidirectionalProperty.cs | 15 +
.../StyleProperties/UnknownProperty.cs | 12 +
.../Visibility/BackfaceVisibilityProperty.cs | 14 +
.../Visibility/ClipProperty.cs | 14 +
.../Visibility/OpacityProperty.cs | 13 +
.../Visibility/VisibilityProperty.cs | 15 +
.../StyleProperties/WidowsProperty.cs | 14 +
.../Core/CssEngine/TextEncoding.cs | 305 +++
.../Core/CssEngine/Tokens/ColorToken.cs | 17 +
.../Core/CssEngine/Tokens/CommentToken.cs | 19 +
.../Core/CssEngine/Tokens/FunctionToken.cs | 49 +
.../Core/CssEngine/Tokens/KeywordToken.cs | 25 +
.../Core/CssEngine/Tokens/NumberToken.cs | 39 +
.../Core/CssEngine/Tokens/RangeToken.cs | 51 +
.../Core/CssEngine/Tokens/StringToken.cs | 20 +
.../Core/CssEngine/Tokens/Token.cs | 24 +
.../Core/CssEngine/Tokens/UnitToken.cs | 22 +
.../Core/CssEngine/Tokens/UrlToken.cs | 21 +
.../ValueConverters/AnyValueConverter.cs | 34 +
.../ArgumentsValueConverter.cs | 67 +
.../BackgroundPositionValueConverter.cs | 73 +
.../BackgroundSizeValueConverter.cs | 54 +
.../ValueConverters/BorderRadiusConverter.cs | 127 ++
.../ConditionalStartsWithValueConverter.cs | 92 +
.../ConstraintValueConverter.cs | 62 +
.../ContentFunctionConverter.cs | 73 +
.../ContinuousValueConverter.cs | 79 +
.../DictionaryValueConverter.cs | 46 +
.../ValueConverters/EndListValueConverter.cs | 104 +
.../ValueConverters/FallbackValueConverter.cs | 44 +
.../ValueConverters/FunctionValueConverter.cs | 59 +
.../ValueConverters/GradientConverter.cs | 249 +++
.../IdentifierValueConverter.cs | 84 +
.../ValueConverters/ListValueConverter.cs | 107 ++
.../OneOrMoreValueConverter.cs | 101 +
.../ValueConverters/OptionValueConverter.cs | 79 +
.../ValueConverters/OrValueConverter.cs | 26 +
.../OrderedOptionsConverter.cs | 105 +
.../ValueConverters/PeriodicValueConverter.cs | 132 ++
.../ValueConverters/RequiredValueConverter.cs | 25 +
.../StartsWithValueConverter.cs | 78 +
.../StringFunctionConverter.cs | 88 +
.../StringSetValueConverter.cs | 136 ++
.../ValueConverters/StringValueConverter.cs | 38 +
.../ValueConverters/StringsValueConverter.cs | 55 +
.../ValueConverters/StructValueConverter.cs | 48 +
.../TimeBasedShorthandConverter.cs | 209 ++
.../UnorderedOptionsConverter.cs | 151 ++
.../ValueConverters/UrlValueConverter.cs | 38 +
.../Core/CssEngine/Values/Angle.cs | 217 +++
.../Core/CssEngine/Values/Color.cs | 384 ++++
.../Core/CssEngine/Values/Counter.cs | 16 +
.../Values/CubicBezierTimingFunction.cs | 18 +
.../Core/CssEngine/Values/Frequency.cs | 139 ++
.../Core/CssEngine/Values/GradientStop.cs | 14 +
.../Core/CssEngine/Values/IGradient.cs | 10 +
.../Core/CssEngine/Values/IImageSource.cs | 6 +
.../Core/CssEngine/Values/ITimingFunction.cs | 6 +
.../Core/CssEngine/Values/ITransform.cs | 7 +
.../Core/CssEngine/Values/Length.cs | 364 ++++
.../Core/CssEngine/Values/LinearGradient.cs | 21 +
.../Core/CssEngine/Values/MatrixTransform.cs | 17 +
.../Core/CssEngine/Values/Number.cs | 102 +
.../Core/CssEngine/Values/Percent.cs | 125 ++
.../CssEngine/Values/PerspectiveTransform.cs | 32 +
.../Core/CssEngine/Values/Point.cs | 41 +
.../Core/CssEngine/Values/PortNumbers.cs | 26 +
.../Core/CssEngine/Values/ProtocolNames.cs | 50 +
.../Core/CssEngine/Values/RadialGradient.cs | 40 +
.../Core/CssEngine/Values/Resolution.cs | 121 ++
.../Core/CssEngine/Values/RotateTransform.cs | 38 +
.../Core/CssEngine/Values/ScaleTransform.cs | 21 +
.../Core/CssEngine/Values/Shadow.cs | 22 +
.../Core/CssEngine/Values/Shape.cs | 18 +
.../Core/CssEngine/Values/SkewTransform.cs | 23 +
.../CssEngine/Values/StepsTimingFunction.cs | 16 +
.../Core/CssEngine/Values/Time.cs | 161 ++
.../CssEngine/Values/TranslateTransform.cs | 24 +
.../HtmlRenderer/Core/CssEngine/license.txt | 21 +
Source/HtmlRenderer/Core/Dom/CssBox.cs | 18 +
.../HtmlRenderer/Core/Dom/CssBoxProperties.cs | 13 +
.../HtmlRenderer/Core/Dom/CssContentEngine.cs | 131 ++
Source/HtmlRenderer/Core/Dom/HoverBoxBlock.cs | 16 +-
Source/HtmlRenderer/Core/Entities/CssBlock.cs | 235 ---
.../Core/Entities/CssBlockSelectorItem.cs | 74 -
Source/HtmlRenderer/Core/HtmlContainerInt.cs | 8 +-
Source/HtmlRenderer/Core/Parse/CssParser.cs | 942 ++-------
.../HtmlRenderer/Core/Parse/CssValueParser.cs | 216 ++-
Source/HtmlRenderer/Core/Parse/DomParser.cs | 723 +++++--
.../Core/Parse/RegexParserHelper.cs | 227 ---
.../HtmlRenderer/Core/Utils/CssConstants.cs | 6 +
Source/HtmlRenderer/Core/Utils/CssUtils.cs | 53 +
Source/HtmlRenderer/Core/Utils/DomUtils.cs | 126 +-
526 files changed, 27397 insertions(+), 1649 deletions(-)
create mode 100644 Source/HtmlRenderer/Core/CssEngine/BackgroundPositionGrammar.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/BackgroundSizeGrammar.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Calc/CalcCategory.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Calc/CalcEvaluator.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Calc/CalcNode.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Calc/CalcParser.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Calc/CalcSerializer.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Calc/CalcTypeChecker.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Calc/CalcValue.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Calc/CalcValueConverter.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Conditions/AndCondition.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Conditions/DeclarationCondition.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Conditions/EmptyCondition.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Conditions/GroupCondition.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Conditions/NotCondition.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Conditions/OrCondition.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/AlignContent.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/AlignItem.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/AnimationDirection.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/AnimationFillStyle.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/BackgroundAttachment.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/BackgroundRepeat.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/BlendMode.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/BorderRepeat.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/BoxModel.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/BreakMode.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/ClearMode.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/Colors.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/Combinators.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/ContainerType.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/DirectionMode.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/DisplayMode.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/FeatureNames.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/FillRule.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/FlexDirection.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/FlexWrap.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/Floating.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/FontSize.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/FontStretch.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/FontStyle.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/FontVariant.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/FontWeight.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/FunctionNames.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/HorizontalAlignment.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/HoverAbility.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/IntrinsicSizing.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/JustifyContent.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/Keywords.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/LineStyle.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/ListPosition.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/ListStyle.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/ObjectFitting.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/Overflow.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/OverflowWrap.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/ParseError.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/PdfTagType.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/PlayState.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/PointerAccuracy.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/PositionMode.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/PropertyFlags.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/PropertyNames.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/PseudoClassNames.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/PseudoElementNames.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/QuirksMode.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/RuleNames.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/RuleType.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/ScriptingState.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/StrokeLinecap.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/StrokeLinejoin.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/SystemCursor.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/SystemFont.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/TextAlignLast.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/TextAnchor.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/TextDecorationLine.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/TextDecorationStyle.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/TextJustify.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/TextTransform.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/TokenType.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/UnicodeMode.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/UnitNames.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/UpdateFrequency.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/VerticalAlignment.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/Visibility.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/Whitespace.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Enumerations/WordBreak.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Extensions/CharExtensions.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Extensions/CollectionExtensions.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Extensions/FormatExtensions.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Extensions/PortableExtensions.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Extensions/StringExtensions.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Extensions/ValueConverterExtensions.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Extensions/ValueExtensions.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Factories/AttributeSelectorFactory.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Factories/MediaFeatureFactory.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Factories/PropertyFactory.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Factories/PseudoClassSelectorFactory.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Factories/PseudoElementSelectorFactory.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Formatting/CompressedStyleFormatter.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Functions/DocumentFunction.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Functions/DomainFunction.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Functions/IConditionFunction.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Functions/IDocumentFunction.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Functions/RegexpFunction.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Functions/UrlFunction.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Functions/UrlPrefixFunction.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/MediaFeatures/AspectRatioMediaFeature.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/MediaFeatures/ColorIndexMediaFeature.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/MediaFeatures/ColorMediaFeature.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/MediaFeatures/DeviceAspectRatioMediaFeature.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/MediaFeatures/DeviceHeightMediaFeature.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/MediaFeatures/DevicePixelRatioFeature.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/MediaFeatures/DeviceWidthMediaFeature.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/MediaFeatures/GridMediaFeature.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/MediaFeatures/HeightMediaFeature.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/MediaFeatures/HoverMediaFeature.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/MediaFeatures/MediaFeature.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/MediaFeatures/MonochromeMediaFeature.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/MediaFeatures/OrientationMediaFeature.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/MediaFeatures/PointerMediaFeature.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/MediaFeatures/ResolutionMediaFeature.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/MediaFeatures/ScanMediaFeature.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/MediaFeatures/ScriptingMediaFeature.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/MediaFeatures/SizeMediaFeature.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/MediaFeatures/UnknownMediaFeature.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/MediaFeatures/UpdateFrequencyMediaFeature.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/MediaFeatures/WidthMediaFeature.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Model/Combinator.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Model/Comment.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Model/Converters.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Model/IMediaFeature.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Model/ISelector.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Model/IStyleFormattable.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Model/IStyleFormatter.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Model/IStylesheetNode.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Model/IValueConverter.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Model/Map.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Model/MediaList.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Model/Medium.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Model/ParseException.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Model/ParserExtensions.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Model/ParserOptions.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Model/Pool.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Model/Priority.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Model/StyleDeclaration.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Model/Stylesheet.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Model/StylesheetNode.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Model/StylesheetText.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Model/Symbols.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Model/TaskEx.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Model/TextPosition.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Model/TextRange.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Model/TextSource.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Model/TokenValue.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Model/TransformMatrix.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Model/Url.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Model/ValueBuilder.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Parser/Lexer.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Parser/LexerBase.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Parser/SelectorConstructor.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Parser/StylesheetComposer.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Parser/StylesheetParser.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Parser/TokenizerError.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Rules/CharsetRule.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Rules/ConditionRule.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Rules/ContainerRule.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Rules/DeclarationRule.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Rules/DocumentRule.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Rules/FontFaceRule.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Rules/GroupingRule.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Rules/ICharsetRule.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Rules/IConditionRule.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Rules/IContainer.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Rules/IFontFaceRule.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Rules/IGroupingRule.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Rules/IImportRule.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Rules/IKeyframeRule.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Rules/IKeyframesRule.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Rules/IMarginRule.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Rules/IMediaRule.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Rules/INamespaceRule.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Rules/IPageRule.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Rules/IRule.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Rules/IRuleCreator.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Rules/IRuleList.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Rules/IStyleRule.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Rules/ISupportsRule.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Rules/ImportRule.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Rules/KeyframeRule.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Rules/KeyframesRule.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Rules/MarginStyleRule.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Rules/MediaRule.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Rules/NamespaceRule.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Rules/PageRule.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Rules/Rule.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Rules/RuleList.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Rules/StyleRule.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Rules/SupportsRule.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Rules/UnknownRule.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Rules/ViewportRule.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Selectors/AllSelector.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Selectors/AttrAvailableSelector.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Selectors/AttrBeginsSelector.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Selectors/AttrContainsSelector.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Selectors/AttrEndsSelector.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Selectors/AttrHyphenSelector.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Selectors/AttrListSelector.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Selectors/AttrMatchSelector.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Selectors/AttrNotMatchSelector.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Selectors/AttrSelectorBase.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Selectors/ChildSelector.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Selectors/ClassSelector.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Selectors/CombinatorSelector.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Selectors/ComplexSelector.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Selectors/CompoundSelector.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Selectors/FirstChildSelector.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Selectors/FirstColumnSelector.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Selectors/FirstTypeSelector.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Selectors/HasSelector.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Selectors/IAttrSelector.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Selectors/IdSelector.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Selectors/KeyframeSelector.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Selectors/LastChildSelector.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Selectors/LastColumnSelector.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Selectors/LastTypeSelector.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Selectors/ListSelector.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Selectors/MatchesSelector.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Selectors/NamespaceSelector.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Selectors/NotSelector.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Selectors/OnlyChildSelector.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Selectors/OnlyOfTypeSelector.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Selectors/PseudoClassSelector.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Selectors/PseudoElementSelector.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Selectors/SelectorBase.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Selectors/Selectors.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Selectors/TypeSelector.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Selectors/UnknownSelector.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Animation/AnimationDelayProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Animation/AnimationDirectionProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Animation/AnimationDurationProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Animation/AnimationFillModeProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Animation/AnimationIterationCountProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Animation/AnimationNameProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Animation/AnimationPlayStateProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Animation/AnimationProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Animation/AnimationTimingFunctionProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Background/BackgroundAttachmentProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Background/BackgroundClipProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Background/BackgroundColorProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Background/BackgroundImageProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Background/BackgroundOriginProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Background/BackgroundPositionProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Background/BackgroundProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Background/BackgroundRepeatProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Background/BackgroundSizeProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Border/BorderBottomColorProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Border/BorderBottomProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Border/BorderBottomStyleProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Border/BorderBottomWidthProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Border/BorderCollapseProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Border/BorderColorProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Border/BorderLeftColorProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Border/BorderLeftProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Border/BorderLeftStyleProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Border/BorderLeftWidthProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Border/BorderProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Border/BorderRightColorProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Border/BorderRightProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Border/BorderRightStyleProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Border/BorderRightWidthProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Border/BorderSpacingProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Border/BorderStyleProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Border/BorderTopColorProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Border/BorderTopProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Border/BorderTopStyleProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Border/BorderTopWidthProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Border/BorderWidthProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/BorderImage/BorderImageOutsetProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/BorderImage/BorderImageProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/BorderImage/BorderImageRepeatProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/BorderImage/BorderImageSliceProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/BorderImage/BorderImageSourceProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/BorderImage/BorderImageWidthProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/BorderRadius/BorderBottomLeftRadiusProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/BorderRadius/BorderBottomRightRadiusProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/BorderRadius/BorderRadiusProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/BorderRadius/BorderTopLeftRadiusProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/BorderRadius/BorderTopRightRadiusProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Box/BoxDecorationBreak.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Box/BoxShadowProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Box/BoxSizingProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Box/MarginBottomProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Box/MarginLeftProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Box/MarginProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Box/MarginRightProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Box/MarginTopProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Box/PaddingBottomProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Box/PaddingLeftProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Box/PaddingProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Box/PaddingRightProperty .cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Box/PaddingTopProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Break/BreakAfterProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Break/BreakBeforeProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Break/BreakInsideProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Break/PageBreakAfterProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Break/PageBreakBeforeProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Break/PageBreakInsideProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/CaptionSideProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Columns/ColumnCountProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Columns/ColumnFillProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Columns/ColumnGapProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Columns/ColumnRuleColorProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Columns/ColumnRuleProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Columns/ColumnRuleStyleProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Columns/ColumnRuleWidthProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Columns/ColumnSpanProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Columns/ColumnWidthProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Columns/ColumnsProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Container/ContainerNameProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Container/ContainerTypeProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/ContentProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Coordinate/BottomProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Coordinate/HeightProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Coordinate/LeftProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Coordinate/MaxHeightProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Coordinate/MaxWidthProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Coordinate/MinHeightProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Coordinate/MinWidthProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Coordinate/RightProperty .cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Coordinate/TopProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Coordinate/WidthProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/CursorProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/CustomProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/EmptyCellsProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/FeatureProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Fill/FillOpacityProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Fill/FillProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Fill/FillRuleProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Flexbox/AlignContentProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Flexbox/AlignItemsProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Flexbox/AlignSelfProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Flexbox/FlexBasisProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Flexbox/FlexDirectionProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Flexbox/FlexFlowProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Flexbox/FlexGrowProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Flexbox/FlexProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Flexbox/FlexShrinkProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Flexbox/FlexWrapProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Flexbox/JustifyContentProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Flexbox/OrderProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Flow/ClearProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Flow/DisplayProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Flow/FloatProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Flow/OverflowProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Flow/PositionProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Flow/ZIndexProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Font/ColorProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Font/FontFamilyProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Font/FontProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Font/FontSizeAdjustProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Font/FontSizeProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Font/FontStretchProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Font/FontStyleProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Font/FontVariantProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Font/FontWeightProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Font/LetterSpacingProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Font/LineHeightProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Font/SrcProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Font/UnicodeRangeProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Font/WordSpacingProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/GapProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/HyphensProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/IProperties .cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/IProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/IPropertyValue.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/List/CounterIncrementProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/List/CounterResetProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/List/ListStyleImageProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/List/ListStylePositionProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/List/ListStyleProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/List/ListStyleTypeProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/OrphansProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Outline/OutlineColorProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Outline/OutlineProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Outline/OutlineStyleProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Outline/OutlineWidthProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/PageNameProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/PageSizeProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/PdfTagTypeProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Property.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/QuotesProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/RowGapProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/ShorthandProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Sizing/ObjectFitProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Sizing/ObjectPositionProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/StringSetProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Stroke/StrokeDasharrayProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Stroke/StrokeDashoffsetProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Stroke/StrokeLinecapProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Stroke/StrokeLinejoinProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Stroke/StrokeMiterlimitProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Stroke/StrokeOpacityProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Stroke/StrokeProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Stroke/StrokeWidthProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/TableLayoutProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Text/DirectionProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Text/OverflowWrapProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Text/TextAlignLastProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Text/TextAlignProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Text/TextAnchorProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Text/TextDecorationColorProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Text/TextDecorationLineProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Text/TextDecorationProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Text/TextDecorationStyleProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Text/TextIndentProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Text/TextJustifyProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Text/TextShadowProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Text/TextTransformProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Text/VerticalAlignProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Text/WhiteSpaceProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Text/WordBreakProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Transform/PerspectiveOriginProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Transform/PerspectiveProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Transform/TransformOriginProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Transform/TransformProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Transform/TransformStyleProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Transition/TransitionDelayProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Transition/TransitionDurationProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Transition/TransitionProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Transition/TransitionPropertyProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Transition/TransitionTimingFunctionProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/UnicodeBidirectionalProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/UnknownProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Visibility/BackfaceVisibilityProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Visibility/ClipProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Visibility/OpacityProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/Visibility/VisibilityProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/StyleProperties/WidowsProperty.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/TextEncoding.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Tokens/ColorToken.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Tokens/CommentToken.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Tokens/FunctionToken.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Tokens/KeywordToken.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Tokens/NumberToken.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Tokens/RangeToken.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Tokens/StringToken.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Tokens/Token.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Tokens/UnitToken.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Tokens/UrlToken.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/ValueConverters/AnyValueConverter.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/ValueConverters/ArgumentsValueConverter.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/ValueConverters/BackgroundPositionValueConverter.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/ValueConverters/BackgroundSizeValueConverter.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/ValueConverters/BorderRadiusConverter.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/ValueConverters/ConditionalStartsWithValueConverter.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/ValueConverters/ConstraintValueConverter.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/ValueConverters/ContentFunctionConverter.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/ValueConverters/ContinuousValueConverter.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/ValueConverters/DictionaryValueConverter.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/ValueConverters/EndListValueConverter.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/ValueConverters/FallbackValueConverter.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/ValueConverters/FunctionValueConverter.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/ValueConverters/GradientConverter.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/ValueConverters/IdentifierValueConverter.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/ValueConverters/ListValueConverter.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/ValueConverters/OneOrMoreValueConverter.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/ValueConverters/OptionValueConverter.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/ValueConverters/OrValueConverter.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/ValueConverters/OrderedOptionsConverter.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/ValueConverters/PeriodicValueConverter.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/ValueConverters/RequiredValueConverter.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/ValueConverters/StartsWithValueConverter.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/ValueConverters/StringFunctionConverter.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/ValueConverters/StringSetValueConverter.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/ValueConverters/StringValueConverter.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/ValueConverters/StringsValueConverter.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/ValueConverters/StructValueConverter.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/ValueConverters/TimeBasedShorthandConverter.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/ValueConverters/UnorderedOptionsConverter.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/ValueConverters/UrlValueConverter.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Values/Angle.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Values/Color.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Values/Counter.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Values/CubicBezierTimingFunction.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Values/Frequency.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Values/GradientStop.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Values/IGradient.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Values/IImageSource.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Values/ITimingFunction.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Values/ITransform.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Values/Length.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Values/LinearGradient.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Values/MatrixTransform.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Values/Number.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Values/Percent.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Values/PerspectiveTransform.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Values/Point.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Values/PortNumbers.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Values/ProtocolNames.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Values/RadialGradient.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Values/Resolution.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Values/RotateTransform.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Values/ScaleTransform.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Values/Shadow.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Values/Shape.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Values/SkewTransform.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Values/StepsTimingFunction.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Values/Time.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/Values/TranslateTransform.cs
create mode 100644 Source/HtmlRenderer/Core/CssEngine/license.txt
create mode 100644 Source/HtmlRenderer/Core/Dom/CssContentEngine.cs
delete mode 100644 Source/HtmlRenderer/Core/Entities/CssBlock.cs
delete mode 100644 Source/HtmlRenderer/Core/Entities/CssBlockSelectorItem.cs
delete mode 100644 Source/HtmlRenderer/Core/Parse/RegexParserHelper.cs
diff --git a/Source/HtmlRenderer/Adapters/RAdapter.cs b/Source/HtmlRenderer/Adapters/RAdapter.cs
index 6b13459c1..8a5afc3b9 100644
--- a/Source/HtmlRenderer/Adapters/RAdapter.cs
+++ b/Source/HtmlRenderer/Adapters/RAdapter.cs
@@ -85,7 +85,16 @@ protected RAdapter()
///
public CssData DefaultCssData
{
- get { return _defaultCssData ?? (_defaultCssData = CssData.Parse(this, CssDefaults.DefaultStyleSheet, false)); }
+ get
+ {
+ if (_defaultCssData == null)
+ {
+ _defaultCssData = CssData.Parse(this, CssDefaults.DefaultStyleSheet, false);
+ foreach (var stylesheet in _defaultCssData.Stylesheets)
+ stylesheet.IsUserAgent = true;
+ }
+ return _defaultCssData;
+ }
}
///
diff --git a/Source/HtmlRenderer/Core/CssData.cs b/Source/HtmlRenderer/Core/CssData.cs
index f0e4a8106..179956178 100644
--- a/Source/HtmlRenderer/Core/CssData.cs
+++ b/Source/HtmlRenderer/Core/CssData.cs
@@ -6,54 +6,51 @@
// like the days and months;
// they die and are reborn,
// like the four seasons."
-//
+//
// - Sun Tsu,
// "The Art of War"
using System;
using System.Collections.Generic;
+using System.Linq;
using TheArtOfDev.HtmlRenderer.Adapters;
-using TheArtOfDev.HtmlRenderer.Core.Entities;
+using TheArtOfDev.HtmlRenderer.Core.CssEngine;
+using TheArtOfDev.HtmlRenderer.Core.Dom;
using TheArtOfDev.HtmlRenderer.Core.Parse;
using TheArtOfDev.HtmlRenderer.Core.Utils;
namespace TheArtOfDev.HtmlRenderer.Core
{
///
- /// Holds parsed stylesheet css blocks arranged by media and classes.
- ///
+ /// Holds parsed stylesheets (UA default + author + inline), and provides origin-aware,
+ /// specificity-ordered selector matching against the box tree.
///
///
- /// To learn more about CSS blocks visit CSS spec: http://www.w3.org/TR/CSS21/syndata.html#block
+ /// Ported from PeachPDF's Html/Core/CssData.cs (itself a fork of Tyler Brinks' ExCSS), adapted to
+ /// HTML-Renderer's CssBox/HtmlTag shape. One deliberate omission from the source: the
+ /// ::marker pseudo-element box-synthesis path is removed - ::before/::after
+ /// synthesis is kept, but this codebase has no CssBox.IsMarkerPseudoElement concept and no
+ /// consumer for it (list markers are painted procedurally, not via a synthesized box).
///
public sealed class CssData
{
- #region Fields and Consts
-
///
- /// used to return empty array
+ /// The parsed stylesheets that make up this CssData, in the order they were added (UA default
+ /// first, then author/`<link>`/`<style>` stylesheets in document order). Each carries its own
+ /// flag so origin-aware cascade phases can be resolved.
///
- private static readonly List _emptyArray = new List();
-
- ///
- /// dictionary of media type to dictionary of css class name to the cssBlocks collection with all the data.
- ///
- private readonly Dictionary>> _mediaBlocks = new Dictionary>>(StringComparer.InvariantCultureIgnoreCase);
-
- #endregion
-
+ internal List Stylesheets { get; } = new List();
///
/// Init.
///
internal CssData()
{
- _mediaBlocks.Add("all", new Dictionary>(StringComparer.InvariantCultureIgnoreCase));
}
///
/// Parse the given stylesheet to object.
- /// If is true the parsed css blocks are added to the
+ /// If is true the parsed css blocks are added to the
/// default css data (as defined by W3), merged if class name already exists. If false only the data in the given stylesheet is returned.
///
///
@@ -63,152 +60,872 @@ internal CssData()
/// the parsed css data
public static CssData Parse(RAdapter adapter, string stylesheet, bool combineWithDefault = true)
{
- CssParser parser = new CssParser(adapter);
+ var parser = new CssParser(adapter);
return parser.ParseStyleSheet(stylesheet, combineWithDefault);
}
///
- /// dictionary of media type to dictionary of css class name to the cssBlocks collection with all the data
+ /// Combine this CSS data's stylesheets with 's.
+ ///
+ /// the CSS data to combine with
+ public void Combine(CssData other)
+ {
+ ArgChecker.AssertArgNotNull(other, "other");
+ Stylesheets.AddRange(other.Stylesheets);
+ }
+
+ ///
+ /// Create a shallow copy of this css data - the returned instance has its own
+ /// list (so appending a new stylesheet to the clone doesn't affect the original), but the
+ /// instances themselves are shared (they're never mutated after parsing).
///
- internal IDictionary>> MediaBlocks
+ /// cloned object
+ public CssData Clone()
+ {
+ var clone = new CssData();
+ clone.Stylesheets.AddRange(Stylesheets);
+ return clone;
+ }
+
+ // --- Rule index -----------------------------------------------------------------------
+ //
+ // Matching every stylesheet rule against every CssBox in the document (the naive approach)
+ // is O(rules x boxes) and dominates cascade cost on large documents. Real browser engines
+ // avoid this by bucketing rules by the "subject" simple selector (the one that must match the
+ // box itself, e.g. the tag/class/id) so a box only needs to test the handful of rules that
+ // could plausibly match it, instead of the whole stylesheet. DoesSelectorMatch remains the
+ // source of truth for whether a rule actually matches - the index only narrows the candidates.
+ //
+ // Built lazily, once, the first time this CssData's rules are queried. Safe because by the
+ // time CascadeApplyStyles starts querying rules for the box tree, DomParser has already
+ // finished building/cloning CssData from ");
}
From f63ff1cfaa5a8eab300ae9c3f12afd7d9f8cd287 Mon Sep 17 00:00:00 2001
From: Justin Haygood
Date: Thu, 16 Jul 2026 19:02:18 -0400
Subject: [PATCH 2/4] Fix CalcSerializer
---
Source/HtmlRenderer/Core/CssEngine/Calc/CalcSerializer.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Source/HtmlRenderer/Core/CssEngine/Calc/CalcSerializer.cs b/Source/HtmlRenderer/Core/CssEngine/Calc/CalcSerializer.cs
index 511bf39d2..39edbb0fa 100644
--- a/Source/HtmlRenderer/Core/CssEngine/Calc/CalcSerializer.cs
+++ b/Source/HtmlRenderer/Core/CssEngine/Calc/CalcSerializer.cs
@@ -48,7 +48,7 @@ public static string Serialize(CalcNode node, CalcCategory category)
case UnaryCalcNode unary:
{
var value = FoldAngle(unary.Operand);
- return value is null ? null : unary.Negative ? -value.Value : value.Value;
+ return value is null ? null : (double?)(unary.Negative ? -value.Value : value.Value);
}
case BinaryCalcNode binary when binary.Operator == '+' || binary.Operator == '-':
From 0179a9ab1314b35f9c1c33a833f46c1b6e779a15 Mon Sep 17 00:00:00 2001
From: Justin Haygood
Date: Thu, 16 Jul 2026 21:11:58 -0400
Subject: [PATCH 3/4] Replace custom CSS with standards
The Demo app depending on 2 HTML-Renderer extensions for background gradients and border radius.
This ports a minimal version of the CSS spec compliant versions from PeachPDF
---
Source/Demo/Common/DemoUtils.cs | 4 +-
Source/Demo/Common/Samples/00.Intro.htm | 2 +-
.../Common/Samples/07.Additional features.htm | 81 ++--
Source/Demo/Common/Samples/08.Tooltip.htm | 5 +-
Source/Demo/Common/TestSamples/12.Text.htm | 16 +-
Source/Demo/Common/TestSamples/16.Borders.htm | 12 +-
.../Adapters/GradientBrushAdapter.cs | 46 ++
.../Adapters/GraphicsAdapter.cs | 72 ++++
.../Adapters/GraphicsPathAdapter.cs | 8 +-
.../Adapters/PdfSharpAdapter.cs | 13 +-
.../Adapters/GraphicsPathAdapter.cs | 4 +-
.../HtmlRenderer.WPF/Adapters/WpfAdapter.cs | 21 +-
.../Adapters/GraphicsPathAdapter.cs | 8 +-
.../Adapters/WinFormsAdapter.cs | 28 +-
Source/HtmlRenderer/Adapters/RAdapter.cs | 24 +-
Source/HtmlRenderer/Adapters/RGraphics.cs | 13 +-
Source/HtmlRenderer/Adapters/RGraphicsPath.cs | 15 +-
Source/HtmlRenderer/Core/CssDefaults.cs | 16 +-
Source/HtmlRenderer/Core/Dom/CssBox.cs | 22 +-
.../HtmlRenderer/Core/Dom/CssBoxProperties.cs | 396 +++++++++++-------
Source/HtmlRenderer/Core/Entities/CssImage.cs | 32 ++
.../Core/Entities/ParsedLinearGradient.cs | 18 +
.../Core/Handlers/BordersDrawHandler.cs | 59 +--
.../Core/Handlers/CssImagePainter.cs | 234 +++++++++++
Source/HtmlRenderer/Core/Parse/CssParser.cs | 10 +
.../HtmlRenderer/Core/Parse/CssValueParser.cs | 215 ++++++++++
Source/HtmlRenderer/Core/Utils/CssUtils.cs | 51 +--
Source/HtmlRenderer/Core/Utils/RenderUtils.cs | 56 ++-
28 files changed, 1112 insertions(+), 369 deletions(-)
create mode 100644 Source/HtmlRenderer.PdfSharp/Adapters/GradientBrushAdapter.cs
create mode 100644 Source/HtmlRenderer/Core/Entities/CssImage.cs
create mode 100644 Source/HtmlRenderer/Core/Entities/ParsedLinearGradient.cs
create mode 100644 Source/HtmlRenderer/Core/Handlers/CssImagePainter.cs
diff --git a/Source/Demo/Common/DemoUtils.cs b/Source/Demo/Common/DemoUtils.cs
index 682f325f5..a14b33b3f 100644
--- a/Source/Demo/Common/DemoUtils.cs
+++ b/Source/Demo/Common/DemoUtils.cs
@@ -74,8 +74,8 @@ public static string GetStylesheet(string src)
a:link { text-decoration: none; }
a:hover { text-decoration: underline; }
.gray { color:gray; }
- .example { background-color:#efefef; corner-radius:5px; padding:0.5em; }
- .whitehole { background-color:white; corner-radius:10px; padding:15px; }
+ .example { background-color:#efefef; border-radius:5px; padding:0.5em; }
+ .whitehole { background-color:white; border-radius:10px; padding:15px; }
.caption { font-size: 1.1em }
.comment { color: green; margin-bottom: 5px; margin-left: 3px; }
.comment2 { color: green; }";
diff --git a/Source/Demo/Common/Samples/00.Intro.htm b/Source/Demo/Common/Samples/00.Intro.htm
index d310818b4..7f9f9f565 100644
--- a/Source/Demo/Common/Samples/00.Intro.htm
+++ b/Source/Demo/Common/Samples/00.Intro.htm
@@ -3,7 +3,7 @@
Intro
-
+
HTML Renderer Project - $$Platform$$
diff --git a/Source/Demo/Common/Samples/07.Additional features.htm b/Source/Demo/Common/Samples/07.Additional features.htm
index 764e9511c..bd07880e9 100644
--- a/Source/Demo/Common/Samples/07.Additional features.htm
+++ b/Source/Demo/Common/Samples/07.Additional features.htm
@@ -5,20 +5,18 @@
@@ -54,7 +54,7 @@ Transparent text
elementum dolor ac mauris. Ut tristique. In varius volutpat metus. Integer leo dolor, tristique a, dignissim ac, iaculis eget, elit. Donec arcu.
-
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur ornare mollis elit. Integer_sagittis_Fusce_elementum_commodo_felis_Vivamus_lacinia_eleifend_libero_Donec
lacus.
diff --git a/Source/Demo/Common/TestSamples/16.Borders.htm b/Source/Demo/Common/TestSamples/16.Borders.htm
index 6fae0757c..117dfbcae 100644
--- a/Source/Demo/Common/TestSamples/16.Borders.htm
+++ b/Source/Demo/Common/TestSamples/16.Borders.htm
@@ -12,13 +12,13 @@
- border 1px with corner-radius 5px
+
+ border 1px with border-radius 5px
-
- border 2px with corner-radius 10px
+
+ border 2px with border-radius 10px
@@ -37,8 +37,8 @@
-
- dashed border 2px with corner-radius 10px
+
+ dashed border 2px with border-radius 10px
diff --git a/Source/HtmlRenderer.PdfSharp/Adapters/GradientBrushAdapter.cs b/Source/HtmlRenderer.PdfSharp/Adapters/GradientBrushAdapter.cs
new file mode 100644
index 000000000..b92bf73d3
--- /dev/null
+++ b/Source/HtmlRenderer.PdfSharp/Adapters/GradientBrushAdapter.cs
@@ -0,0 +1,46 @@
+// "Therefore those skilled at the unorthodox
+// are infinite as heaven and earth,
+// inexhaustible as the great rivers.
+// When they come to an end,
+// they begin again,
+// like the days and months;
+// they die and are reborn,
+// like the four seasons."
+//
+// - Sun Tsu,
+// "The Art of War"
+
+using TheArtOfDev.HtmlRenderer.Adapters;
+using TheArtOfDev.HtmlRenderer.Adapters.Entities;
+
+namespace TheArtOfDev.HtmlRenderer.PdfSharp.Adapters
+{
+ ///
+ /// A multi-stop linear gradient "brush" for the PdfSharp backend. Unlike ,
+ /// this does not wrap a real PdfSharp.Drawing.XBrush - XLinearGradientBrush in the
+ /// PDFsharp 1.50 package this project depends on only supports 2 colors, no stop list. Instead this
+ /// just carries the gradient line and stops; 's DrawPath/
+ /// DrawRectangle special-case this type and paint it as a series of adjacent 2-color
+ /// XLinearGradientBrush bands, one per consecutive stop pair - each band is a real 2-color
+ /// linear gradient by definition, so the composite is an exact piecewise-linear rendering, not an
+ /// approximation.
+ ///
+ internal sealed class GradientBrushAdapter : RBrush
+ {
+ public GradientBrushAdapter(RPoint p1, RPoint p2, (RColor Color, double Position)[] stops)
+ {
+ P1 = p1;
+ P2 = p2;
+ Stops = stops;
+ }
+
+ public RPoint P1 { get; }
+
+ public RPoint P2 { get; }
+
+ public (RColor Color, double Position)[] Stops { get; }
+
+ public override void Dispose()
+ { }
+ }
+}
diff --git a/Source/HtmlRenderer.PdfSharp/Adapters/GraphicsAdapter.cs b/Source/HtmlRenderer.PdfSharp/Adapters/GraphicsAdapter.cs
index c6d406c58..76ede5ea1 100644
--- a/Source/HtmlRenderer.PdfSharp/Adapters/GraphicsAdapter.cs
+++ b/Source/HtmlRenderer.PdfSharp/Adapters/GraphicsAdapter.cs
@@ -155,6 +155,14 @@ public override void DrawRectangle(RPen pen, double x, double y, double width, d
public override void DrawRectangle(RBrush brush, double x, double y, double width, double height)
{
+ if (brush is GradientBrushAdapter gradient)
+ {
+ var rectPath = new XGraphicsPath();
+ rectPath.AddRectangle(x, y, width, height);
+ FillGradient(gradient, rectPath);
+ return;
+ }
+
var xBrush = ((BrushAdapter)brush).Brush;
var xTextureBrush = xBrush as XTextureBrush;
if (xTextureBrush != null)
@@ -188,6 +196,12 @@ public override void DrawPath(RPen pen, RGraphicsPath path)
public override void DrawPath(RBrush brush, RGraphicsPath path)
{
+ if (brush is GradientBrushAdapter gradient)
+ {
+ FillGradient(gradient, ((GraphicsPathAdapter)path).GraphicsPath);
+ return;
+ }
+
_g.DrawPath((XBrush)((BrushAdapter)brush).Brush, ((GraphicsPathAdapter)path).GraphicsPath);
}
@@ -199,6 +213,64 @@ public override void DrawPolygon(RBrush brush, RPoint[] points)
}
}
+ ///
+ /// Paints a multi-stop linear gradient by clipping to and drawing
+ /// one real 2-color band per consecutive stop pair, each
+ /// spanning the full perpendicular extent needed to cover the target - see
+ /// for why this backend needs banding instead of a single brush.
+ ///
+ private void FillGradient(GradientBrushAdapter gradient, XGraphicsPath targetPath)
+ {
+ var stops = gradient.Stops;
+ if (stops.Length == 0)
+ return;
+
+ _g.Save();
+ _g.IntersectClip(targetPath);
+
+ double dx = gradient.P2.X - gradient.P1.X;
+ double dy = gradient.P2.Y - gradient.P1.Y;
+ double len = Math.Sqrt(dx * dx + dy * dy);
+
+ if (stops.Length == 1 || len < 1e-6)
+ {
+ // Degenerate gradient line (single stop, or a zero-size box) - just flat-fill with the
+ // last color, matching what a real linear gradient converges to in that case.
+ var flatBrush = new XSolidBrush(Utils.Convert(stops[stops.Length - 1].Color));
+ _g.DrawRectangle(flatBrush, -1e5, -1e5, 2e5, 2e5);
+ _g.Restore();
+ _g.DrawRectangle(XBrushes.White, 0, 0, 0.1, 0.1);
+ return;
+ }
+
+ double ux = dx / len, uy = dy / len;
+ double perpX = -uy, perpY = ux;
+ double perpHalf = Math.Max(len, 1.0) * 4.0;
+
+ for (int i = 0; i < stops.Length - 1; i++)
+ {
+ double t1 = stops[i].Position, t2 = stops[i + 1].Position;
+ var bp1 = new XPoint(gradient.P1.X + ux * len * t1, gradient.P1.Y + uy * len * t1);
+ var bp2 = new XPoint(gradient.P1.X + ux * len * t2, gradient.P1.Y + uy * len * t2);
+
+ var band = new[]
+ {
+ new XPoint(bp1.X - perpX * perpHalf, bp1.Y - perpY * perpHalf),
+ new XPoint(bp1.X + perpX * perpHalf, bp1.Y + perpY * perpHalf),
+ new XPoint(bp2.X + perpX * perpHalf, bp2.Y + perpY * perpHalf),
+ new XPoint(bp2.X - perpX * perpHalf, bp2.Y - perpY * perpHalf),
+ };
+
+ var bandBrush = new XLinearGradientBrush(bp1, bp2, Utils.Convert(stops[i].Color), Utils.Convert(stops[i + 1].Color));
+ _g.DrawPolygon(bandBrush, band, XFillMode.Winding);
+ }
+
+ _g.Restore();
+
+ // handle bug in PdfSharp that keeps the brush color for next string draw
+ _g.DrawRectangle(XBrushes.White, 0, 0, 0.1, 0.1);
+ }
+
#endregion
}
}
\ No newline at end of file
diff --git a/Source/HtmlRenderer.PdfSharp/Adapters/GraphicsPathAdapter.cs b/Source/HtmlRenderer.PdfSharp/Adapters/GraphicsPathAdapter.cs
index 48290543c..9e057d8e9 100644
--- a/Source/HtmlRenderer.PdfSharp/Adapters/GraphicsPathAdapter.cs
+++ b/Source/HtmlRenderer.PdfSharp/Adapters/GraphicsPathAdapter.cs
@@ -51,11 +51,11 @@ public override void LineTo(double x, double y)
_lastPoint = new RPoint(x, y);
}
- public override void ArcTo(double x, double y, double size, Corner corner)
+ public override void ArcTo(double x, double y, double radiusX, double radiusY, Corner corner)
{
- float left = (float)(Math.Min(x, _lastPoint.X) - (corner == Corner.TopRight || corner == Corner.BottomRight ? size : 0));
- float top = (float)(Math.Min(y, _lastPoint.Y) - (corner == Corner.BottomLeft || corner == Corner.BottomRight ? size : 0));
- _graphicsPath.AddArc(left, top, (float)size * 2, (float)size * 2, GetStartAngle(corner), 90);
+ float left = (float)(Math.Min(x, _lastPoint.X) - (corner == Corner.TopRight || corner == Corner.BottomRight ? radiusX : 0));
+ float top = (float)(Math.Min(y, _lastPoint.Y) - (corner == Corner.BottomLeft || corner == Corner.BottomRight ? radiusY : 0));
+ _graphicsPath.AddArc(left, top, (float)radiusX * 2, (float)radiusY * 2, GetStartAngle(corner), 90);
_lastPoint = new RPoint(x, y);
}
diff --git a/Source/HtmlRenderer.PdfSharp/Adapters/PdfSharpAdapter.cs b/Source/HtmlRenderer.PdfSharp/Adapters/PdfSharpAdapter.cs
index e5fa309c0..f286da15b 100644
--- a/Source/HtmlRenderer.PdfSharp/Adapters/PdfSharpAdapter.cs
+++ b/Source/HtmlRenderer.PdfSharp/Adapters/PdfSharpAdapter.cs
@@ -93,18 +93,9 @@ protected override RBrush CreateSolidBrush(RColor color)
return new BrushAdapter(solidBrush);
}
- protected override RBrush CreateLinearGradientBrush(RRect rect, RColor color1, RColor color2, double angle)
+ protected override RBrush CreateLinearGradientBrush(RPoint p1, RPoint p2, (RColor Color, double Position)[] stops)
{
- XLinearGradientMode mode;
- if (angle < 45)
- mode = XLinearGradientMode.ForwardDiagonal;
- else if (angle < 90)
- mode = XLinearGradientMode.Vertical;
- else if (angle < 135)
- mode = XLinearGradientMode.BackwardDiagonal;
- else
- mode = XLinearGradientMode.Horizontal;
- return new BrushAdapter(new XLinearGradientBrush(Utils.Convert(rect), Utils.Convert(color1), Utils.Convert(color2), mode));
+ return new GradientBrushAdapter(p1, p2, stops);
}
protected override RImage ConvertImageInt(object image)
diff --git a/Source/HtmlRenderer.WPF/Adapters/GraphicsPathAdapter.cs b/Source/HtmlRenderer.WPF/Adapters/GraphicsPathAdapter.cs
index a6ffb66bb..af19357b4 100644
--- a/Source/HtmlRenderer.WPF/Adapters/GraphicsPathAdapter.cs
+++ b/Source/HtmlRenderer.WPF/Adapters/GraphicsPathAdapter.cs
@@ -46,9 +46,9 @@ public override void LineTo(double x, double y)
_geometryContext.LineTo(new Point(x, y), true, true);
}
- public override void ArcTo(double x, double y, double size, Corner corner)
+ public override void ArcTo(double x, double y, double radiusX, double radiusY, Corner corner)
{
- _geometryContext.ArcTo(new Point(x, y), new Size(size, size), 0, false, SweepDirection.Clockwise, true, true);
+ _geometryContext.ArcTo(new Point(x, y), new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise, true, true);
}
///
diff --git a/Source/HtmlRenderer.WPF/Adapters/WpfAdapter.cs b/Source/HtmlRenderer.WPF/Adapters/WpfAdapter.cs
index 3ea88d9c4..4de4c8777 100644
--- a/Source/HtmlRenderer.WPF/Adapters/WpfAdapter.cs
+++ b/Source/HtmlRenderer.WPF/Adapters/WpfAdapter.cs
@@ -102,14 +102,19 @@ protected override RBrush CreateSolidBrush(RColor color)
return new BrushAdapter(solidBrush);
}
- protected override RBrush CreateLinearGradientBrush(RRect rect, RColor color1, RColor color2, double angle)
- {
- var startColor = angle <= 180 ? Utils.Convert(color1) : Utils.Convert(color2);
- var endColor = angle <= 180 ? Utils.Convert(color2) : Utils.Convert(color1);
- angle = angle <= 180 ? angle : angle - 180;
- double x = angle < 135 ? Math.Max((angle - 45) / 90, 0) : 1;
- double y = angle <= 45 ? Math.Max(0.5 - angle / 90, 0) : angle > 135 ? Math.Abs(1.5 - angle / 90) : 0;
- return new BrushAdapter(new LinearGradientBrush(startColor, endColor, new Point(x, y), new Point(1 - x, 1 - y)));
+ protected override RBrush CreateLinearGradientBrush(RPoint p1, RPoint p2, (RColor Color, double Position)[] stops)
+ {
+ var gradientStops = new GradientStopCollection(stops.Length);
+ foreach (var stop in stops)
+ gradientStops.Add(new GradientStop(Utils.Convert(stop.Color), stop.Position));
+
+ var brush = new LinearGradientBrush(gradientStops, 0)
+ {
+ MappingMode = BrushMappingMode.Absolute,
+ StartPoint = Utils.Convert(p1),
+ EndPoint = Utils.Convert(p2)
+ };
+ return new BrushAdapter(brush);
}
protected override RImage ConvertImageInt(object image)
diff --git a/Source/HtmlRenderer.WinForms/Adapters/GraphicsPathAdapter.cs b/Source/HtmlRenderer.WinForms/Adapters/GraphicsPathAdapter.cs
index 6c4bbd061..d3a7b3f2c 100644
--- a/Source/HtmlRenderer.WinForms/Adapters/GraphicsPathAdapter.cs
+++ b/Source/HtmlRenderer.WinForms/Adapters/GraphicsPathAdapter.cs
@@ -51,11 +51,11 @@ public override void LineTo(double x, double y)
_lastPoint = new RPoint(x, y);
}
- public override void ArcTo(double x, double y, double size, Corner corner)
+ public override void ArcTo(double x, double y, double radiusX, double radiusY, Corner corner)
{
- float left = (float)(Math.Min(x, _lastPoint.X) - (corner == Corner.TopRight || corner == Corner.BottomRight ? size : 0));
- float top = (float)(Math.Min(y, _lastPoint.Y) - (corner == Corner.BottomLeft || corner == Corner.BottomRight ? size : 0));
- _graphicsPath.AddArc(left, top, (float)size * 2, (float)size * 2, GetStartAngle(corner), 90);
+ float left = (float)(Math.Min(x, _lastPoint.X) - (corner == Corner.TopRight || corner == Corner.BottomRight ? radiusX : 0));
+ float top = (float)(Math.Min(y, _lastPoint.Y) - (corner == Corner.BottomLeft || corner == Corner.BottomRight ? radiusY : 0));
+ _graphicsPath.AddArc(left, top, (float)radiusX * 2, (float)radiusY * 2, GetStartAngle(corner), 90);
_lastPoint = new RPoint(x, y);
}
diff --git a/Source/HtmlRenderer.WinForms/Adapters/WinFormsAdapter.cs b/Source/HtmlRenderer.WinForms/Adapters/WinFormsAdapter.cs
index 16bc83db8..10da9891a 100644
--- a/Source/HtmlRenderer.WinForms/Adapters/WinFormsAdapter.cs
+++ b/Source/HtmlRenderer.WinForms/Adapters/WinFormsAdapter.cs
@@ -10,6 +10,7 @@
// - Sun Tsu,
// "The Art of War"
+using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
@@ -83,9 +84,32 @@ protected override RBrush CreateSolidBrush(RColor color)
return new BrushAdapter(solidBrush, false);
}
- protected override RBrush CreateLinearGradientBrush(RRect rect, RColor color1, RColor color2, double angle)
+ protected override RBrush CreateLinearGradientBrush(RPoint p1, RPoint p2, (RColor Color, double Position)[] stops)
{
- return new BrushAdapter(new LinearGradientBrush(Utils.Convert(rect), Utils.Convert(color1), Utils.Convert(color2), (float)angle), true);
+ var brush = new LinearGradientBrush(Utils.Convert(p1), Utils.Convert(p2), Color.Black, Color.Black);
+
+ var colors = new Color[stops.Length];
+ var positions = new float[stops.Length];
+ for (int i = 0; i < stops.Length; i++)
+ {
+ colors[i] = Utils.Convert(stops[i].Color);
+ var pos = (float)Math.Min(Math.Max(stops[i].Position, 0.0), 1.0);
+ // GDI+ requires strictly increasing positions - nudge any duplicate up by an epsilon.
+ positions[i] = i > 0 && pos <= positions[i - 1] ? positions[i - 1] + 0.0001f : pos;
+ }
+ // GDI+ requires the first/last position to be exactly 0/1 - forcing them here (rather than
+ // requiring the caller to pre-normalize) also matches spec behavior for a gradient whose
+ // outermost stops aren't at the very ends: the outermost color simply extends flat to the edge.
+ positions[0] = 0f;
+ positions[positions.Length - 1] = 1f;
+
+ brush.InterpolationColors = new ColorBlend
+ {
+ Colors = colors,
+ Positions = positions
+ };
+
+ return new BrushAdapter(brush, true);
}
protected override RImage ConvertImageInt(object image)
diff --git a/Source/HtmlRenderer/Adapters/RAdapter.cs b/Source/HtmlRenderer/Adapters/RAdapter.cs
index 8a5afc3b9..1a19f7a57 100644
--- a/Source/HtmlRenderer/Adapters/RAdapter.cs
+++ b/Source/HtmlRenderer/Adapters/RAdapter.cs
@@ -139,16 +139,15 @@ public RBrush GetSolidBrush(RColor color)
}
///
- /// Get linear gradient color brush from to .
+ /// Get a multi-stop linear gradient brush along the line from to .
///
- /// the rectangle to get the brush for
- /// the start color of the gradient
- /// the end color of the gradient
- /// the angle to move the gradient from start color to end color in the rectangle
+ /// the gradient line's start point
+ /// the gradient line's end point
+ /// color stops, each with a position in [0,1] along the gradient line
/// linear gradient color brush instance
- public RBrush GetLinearGradientBrush(RRect rect, RColor color1, RColor color2, double angle)
+ public RBrush GetLinearGradientBrush(RPoint p1, RPoint p2, (RColor Color, double Position)[] stops)
{
- return CreateLinearGradientBrush(rect, color1, color2, angle);
+ return CreateLinearGradientBrush(p1, p2, stops);
}
///
@@ -360,14 +359,13 @@ internal RFont CreateFont(RFontFamily family, double size, RFontStyle style)
protected abstract RBrush CreateSolidBrush(RColor color);
///
- /// Get linear gradient color brush from to .
+ /// Get a multi-stop linear gradient brush along the line from to .
///
- /// the rectangle to get the brush for
- /// the start color of the gradient
- /// the end color of the gradient
- /// the angle to move the gradient from start color to end color in the rectangle
+ /// the gradient line's start point
+ /// the gradient line's end point
+ /// color stops, each with a position in [0,1] along the gradient line
/// linear gradient color brush instance
- protected abstract RBrush CreateLinearGradientBrush(RRect rect, RColor color1, RColor color2, double angle);
+ protected abstract RBrush CreateLinearGradientBrush(RPoint p1, RPoint p2, (RColor Color, double Position)[] stops);
///
/// Convert image object returned from to .
diff --git a/Source/HtmlRenderer/Adapters/RGraphics.cs b/Source/HtmlRenderer/Adapters/RGraphics.cs
index af54f2ae4..22ba0df77 100644
--- a/Source/HtmlRenderer/Adapters/RGraphics.cs
+++ b/Source/HtmlRenderer/Adapters/RGraphics.cs
@@ -76,16 +76,15 @@ public RBrush GetSolidBrush(RColor color)
}
///
- /// Get linear gradient color brush from to .
+ /// Get a multi-stop linear gradient brush along the line from to .
///
- /// the rectangle to get the brush for
- /// the start color of the gradient
- /// the end color of the gradient
- /// the angle to move the gradient from start color to end color in the rectangle
+ /// the gradient line's start point
+ /// the gradient line's end point
+ /// color stops, each with a position in [0,1] along the gradient line
/// linear gradient color brush instance
- public RBrush GetLinearGradientBrush(RRect rect, RColor color1, RColor color2, double angle)
+ public RBrush GetLinearGradientBrush(RPoint p1, RPoint p2, (RColor Color, double Position)[] stops)
{
- return _adapter.GetLinearGradientBrush(rect, color1, color2, angle);
+ return _adapter.GetLinearGradientBrush(p1, p2, stops);
}
///
diff --git a/Source/HtmlRenderer/Adapters/RGraphicsPath.cs b/Source/HtmlRenderer/Adapters/RGraphicsPath.cs
index 21c86bc1a..474781a5e 100644
--- a/Source/HtmlRenderer/Adapters/RGraphicsPath.cs
+++ b/Source/HtmlRenderer/Adapters/RGraphicsPath.cs
@@ -30,10 +30,19 @@ public abstract class RGraphicsPath : IDisposable
public abstract void LineTo(double x, double y);
///
- /// Add circular arc of the given size to the given point from the last point.
+ /// Add an elliptical arc with independent horizontal (X) and vertical (Y) radii to the given
+ /// point from the last point - supports elliptical border-radius corners.
///
- public abstract void ArcTo(double x, double y, double size, Corner corner);
-
+ public abstract void ArcTo(double x, double y, double radiusX, double radiusY, Corner corner);
+
+ ///
+ /// Add a circular arc of the given size to the given point from the last point.
+ ///
+ public void ArcTo(double x, double y, double size, Corner corner)
+ {
+ ArcTo(x, y, size, size, corner);
+ }
+
///
/// Release path resources.
///
diff --git a/Source/HtmlRenderer/Core/CssDefaults.cs b/Source/HtmlRenderer/Core/CssDefaults.cs
index 01e35959a..ec9fce4d9 100644
--- a/Source/HtmlRenderer/Core/CssDefaults.cs
+++ b/Source/HtmlRenderer/Core/CssDefaults.cs
@@ -119,9 +119,8 @@ @media print {
/*This is the background of the HtmlToolTip*/
.htmltooltip {
border:solid 1px #767676;
- background-color:white;
- background-gradient:#E4E5F0;
- padding: 8px;
+ background-color:#E4E5F0;
+ padding: 8px;
Font: 9pt Tahoma;
}";
@@ -139,8 +138,6 @@ @media print {
new System.Collections.Generic.Dictionary(System.StringComparer.OrdinalIgnoreCase)
{
{ "background-color", "transparent" },
- { "background-gradient", "none" },
- { "background-gradient-angle", "90" },
{ "background-image", "none" },
{ "background-position", "0% 0%" },
{ "background-repeat", "repeat" },
@@ -160,11 +157,10 @@ @media print {
{ "border-collapse", "separate" },
{ "color", "black" },
{ "content", "normal" },
- { "corner-nw-radius", "0" },
- { "corner-ne-radius", "0" },
- { "corner-se-radius", "0" },
- { "corner-sw-radius", "0" },
- { "corner-radius", "0" },
+ { "border-top-left-radius", "0 0" },
+ { "border-top-right-radius", "0 0" },
+ { "border-bottom-right-radius", "0 0" },
+ { "border-bottom-left-radius", "0 0" },
{ "empty-cells", "show" },
{ "direction", "ltr" },
{ "display", "inline" },
diff --git a/Source/HtmlRenderer/Core/Dom/CssBox.cs b/Source/HtmlRenderer/Core/Dom/CssBox.cs
index 6a7140a0d..b01a818ff 100644
--- a/Source/HtmlRenderer/Core/Dom/CssBox.cs
+++ b/Source/HtmlRenderer/Core/Dom/CssBox.cs
@@ -729,10 +729,10 @@ internal virtual void MeasureWordsSize(RGraphics g)
{
if (!_wordsSizeMeasured)
{
- if (BackgroundImage != CssConstants.None && _imageLoadHandler == null)
+ if (ActualBackgroundImage is CssImage.Url urlImage && _imageLoadHandler == null)
{
_imageLoadHandler = new ImageLoadHandler(HtmlContainer, OnImageLoadComplete);
- _imageLoadHandler.LoadImage(BackgroundImage, HtmlTag != null ? HtmlTag.Attributes : null);
+ _imageLoadHandler.LoadImage(urlImage.Href, HtmlTag != null ? HtmlTag.Attributes : null);
}
MeasureWordSpacing(g);
@@ -1320,15 +1320,18 @@ protected void PaintBackground(RGraphics g, RRect rect, bool isFirst, bool isLas
{
RBrush brush = null;
- if (BackgroundGradient != CssConstants.None)
+ var backgroundImage = ActualBackgroundImage;
+ if (backgroundImage is CssImage.LinearGradient linearGradient)
{
- brush = g.GetLinearGradientBrush(rect, ActualBackgroundColor, ActualBackgroundGradient, ActualBackgroundGradientAngle);
+ brush = CssImagePainter.GetLinearGradientBrush(g, rect, linearGradient.Gradient);
}
else if (RenderUtils.IsColorVisible(ActualBackgroundColor))
{
brush = g.GetSolidBrush(ActualBackgroundColor);
}
+ var rad = ComputeRadii(rect);
+
if (brush != null)
{
// TODO:a handle it correctly (tables background)
@@ -1336,13 +1339,13 @@ protected void PaintBackground(RGraphics g, RRect rect, bool isFirst, bool isLas
// rectangle.Width -= ActualWordSpacing + CssUtils.GetWordEndWhitespace(ActualFont);
RGraphicsPath roundrect = null;
- if (IsRounded)
+ if (rad.IsRounded)
{
- roundrect = RenderUtils.GetRoundRect(g, rect, ActualCornerNw, ActualCornerNe, ActualCornerSe, ActualCornerSw);
+ roundrect = RenderUtils.GetRoundRect(g, rect, rad.TLX, rad.TLY, rad.TRX, rad.TRY, rad.BRX, rad.BRY, rad.BLX, rad.BLY);
}
Object prevMode = null;
- if (HtmlContainer != null && !HtmlContainer.AvoidGeometryAntialias && IsRounded)
+ if (HtmlContainer != null && !HtmlContainer.AvoidGeometryAntialias && rad.IsRounded)
{
prevMode = g.SetAntiAliasSmoothingMode();
}
@@ -1543,6 +1546,11 @@ protected override RColor GetActualColor(string colorStr)
return HtmlContainer.CssParser.ParseColor(colorStr);
}
+ protected override CssImage GetActualBackgroundImageValue(string value)
+ {
+ return HtmlContainer.CssParser.ParseBackgroundImage(value);
+ }
+
protected override RPoint GetActualLocation(string X, string Y)
{
var left = CssValueParser.ParseLength(X, this.HtmlContainer.PageSize.Width, this, null);
diff --git a/Source/HtmlRenderer/Core/Dom/CssBoxProperties.cs b/Source/HtmlRenderer/Core/Dom/CssBoxProperties.cs
index 6aefc5f2e..d5c9b59d8 100644
--- a/Source/HtmlRenderer/Core/Dom/CssBoxProperties.cs
+++ b/Source/HtmlRenderer/Core/Dom/CssBoxProperties.cs
@@ -16,6 +16,7 @@
using System.Text.RegularExpressions;
using TheArtOfDev.HtmlRenderer.Adapters;
using TheArtOfDev.HtmlRenderer.Adapters.Entities;
+using TheArtOfDev.HtmlRenderer.Core.Entities;
using TheArtOfDev.HtmlRenderer.Core.Parse;
using TheArtOfDev.HtmlRenderer.Core.Utils;
@@ -33,8 +34,6 @@ internal abstract class CssBoxProperties
#region CSS Fields
private string _backgroundColor = "transparent";
- private string _backgroundGradient = "none";
- private string _backgroundGradientAngle = "90";
private string _backgroundImage = "none";
private string _backgroundPosition = "0% 0%";
private string _backgroundRepeat = "repeat";
@@ -55,11 +54,10 @@ internal abstract class CssBoxProperties
private string _bottom;
private string _color = "black";
private string _content = "normal";
- private string _cornerNwRadius = "0";
- private string _cornerNeRadius = "0";
- private string _cornerSeRadius = "0";
- private string _cornerSwRadius = "0";
- private string _cornerRadius = "0";
+ private string _borderTopLeftRadius = "0 0";
+ private string _borderTopRightRadius = "0 0";
+ private string _borderBottomRightRadius = "0 0";
+ private string _borderBottomLeftRadius = "0 0";
private string _emptyCells = "show";
private string _direction = "ltr";
private string _display = "inline";
@@ -121,12 +119,15 @@ internal abstract class CssBoxProperties
///
private RSize _size;
- private double _actualCornerNw = double.NaN;
- private double _actualCornerNe = double.NaN;
- private double _actualCornerSw = double.NaN;
- private double _actualCornerSe = double.NaN;
+ private double _actualBorderTopLeftRadiusX = double.NaN;
+ private double _actualBorderTopLeftRadiusY = double.NaN;
+ private double _actualBorderTopRightRadiusX = double.NaN;
+ private double _actualBorderTopRightRadiusY = double.NaN;
+ private double _actualBorderBottomRightRadiusX = double.NaN;
+ private double _actualBorderBottomRightRadiusY = double.NaN;
+ private double _actualBorderBottomLeftRadiusX = double.NaN;
+ private double _actualBorderBottomLeftRadiusY = double.NaN;
private RColor _actualColor = RColor.Empty;
- private double _actualBackgroundGradientAngle = double.NaN;
private double _actualHeight = double.NaN;
private double _actualWidth = double.NaN;
private double _actualPaddingTop = double.NaN;
@@ -152,7 +153,8 @@ internal abstract class CssBoxProperties
private double _actualTextIndent = double.NaN;
private double _actualBorderSpacingHorizontal = double.NaN;
private double _actualBorderSpacingVertical = double.NaN;
- private RColor _actualBackgroundGradient = RColor.Empty;
+ private bool _actualBackgroundImageComputed;
+ private CssImage _actualBackgroundImage;
private RColor _actualBorderTopColor = RColor.Empty;
private RColor _actualBorderLeftColor = RColor.Empty;
private RColor _actualBorderBottomColor = RColor.Empty;
@@ -281,66 +283,96 @@ public string BorderCollapse
set { _borderCollapse = value; }
}
- public string CornerRadius
+ ///
+ /// The "border-radius" shorthand: sets all four corners at once, with an optional "/" separating
+ /// horizontal and vertical radii for elliptical corners (e.g. "10px 5px / 20px 10px"). Each side
+ /// of the "/" independently follows the standard 1/2/3/4-value CSS expansion rule
+ /// (1=all, 2=TL+BR/TR+BL, 3=TL/TR+BL/BR, 4=TL/TR/BR/BL). Write-only, like the other border-*
+ /// shorthands in this class - the four longhand properties below hold the actual state.
+ ///
+ public string BorderRadius
{
- get { return _cornerRadius; }
set
{
- MatchCollection r = RegexParserUtils.Match(RegexParserUtils.CssLength, value);
+ int slash = value.IndexOf('/');
+ string hGroup = (slash >= 0 ? value.Substring(0, slash) : value).Trim();
+ string vGroup = (slash >= 0 ? value.Substring(slash + 1) : hGroup).Trim();
- switch (r.Count)
- {
- case 1:
- CornerNeRadius = r[0].Value;
- CornerNwRadius = r[0].Value;
- CornerSeRadius = r[0].Value;
- CornerSwRadius = r[0].Value;
- break;
- case 2:
- CornerNeRadius = r[0].Value;
- CornerNwRadius = r[0].Value;
- CornerSeRadius = r[1].Value;
- CornerSwRadius = r[1].Value;
- break;
- case 3:
- CornerNeRadius = r[0].Value;
- CornerNwRadius = r[1].Value;
- CornerSeRadius = r[2].Value;
- break;
- case 4:
- CornerNeRadius = r[0].Value;
- CornerNwRadius = r[1].Value;
- CornerSeRadius = r[2].Value;
- CornerSwRadius = r[3].Value;
- break;
- }
+ string[] h = ExpandRadiusShorthand(hGroup);
+ string[] v = ExpandRadiusShorthand(vGroup);
- _cornerRadius = value;
+ BorderTopLeftRadius = h[0] + " " + v[0];
+ BorderTopRightRadius = h[1] + " " + v[1];
+ BorderBottomRightRadius = h[2] + " " + v[2];
+ BorderBottomLeftRadius = h[3] + " " + v[3];
}
}
- public string CornerNwRadius
+ ///
+ /// Expands a 1-4 value length group into [TopLeft, TopRight, BottomRight, BottomLeft] per the
+ /// standard CSS shorthand rule.
+ ///
+ private static string[] ExpandRadiusShorthand(string group)
{
- get { return _cornerNwRadius; }
- set { _cornerNwRadius = value; }
+ MatchCollection r = RegexParserUtils.Match(RegexParserUtils.CssLength, group);
+
+ switch (r.Count)
+ {
+ case 1:
+ return new[] { r[0].Value, r[0].Value, r[0].Value, r[0].Value };
+ case 2:
+ return new[] { r[0].Value, r[1].Value, r[0].Value, r[1].Value };
+ case 3:
+ return new[] { r[0].Value, r[1].Value, r[2].Value, r[1].Value };
+ case 4:
+ return new[] { r[0].Value, r[1].Value, r[2].Value, r[3].Value };
+ default:
+ return new[] { "0", "0", "0", "0" };
+ }
}
- public string CornerNeRadius
+ public string BorderTopLeftRadius
{
- get { return _cornerNeRadius; }
- set { _cornerNeRadius = value; }
+ get { return _borderTopLeftRadius; }
+ set
+ {
+ _borderTopLeftRadius = value;
+ _actualBorderTopLeftRadiusX = double.NaN;
+ _actualBorderTopLeftRadiusY = double.NaN;
+ }
}
- public string CornerSeRadius
+ public string BorderTopRightRadius
{
- get { return _cornerSeRadius; }
- set { _cornerSeRadius = value; }
+ get { return _borderTopRightRadius; }
+ set
+ {
+ _borderTopRightRadius = value;
+ _actualBorderTopRightRadiusX = double.NaN;
+ _actualBorderTopRightRadiusY = double.NaN;
+ }
}
- public string CornerSwRadius
+ public string BorderBottomRightRadius
{
- get { return _cornerSwRadius; }
- set { _cornerSwRadius = value; }
+ get { return _borderBottomRightRadius; }
+ set
+ {
+ _borderBottomRightRadius = value;
+ _actualBorderBottomRightRadiusX = double.NaN;
+ _actualBorderBottomRightRadiusY = double.NaN;
+ }
+ }
+
+ public string BorderBottomLeftRadius
+ {
+ get { return _borderBottomLeftRadius; }
+ set
+ {
+ _borderBottomLeftRadius = value;
+ _actualBorderBottomLeftRadiusX = double.NaN;
+ _actualBorderBottomLeftRadiusY = double.NaN;
+ }
}
public string MarginBottom
@@ -471,7 +503,11 @@ public string BackgroundColor
public string BackgroundImage
{
get { return _backgroundImage; }
- set { _backgroundImage = value; }
+ set
+ {
+ _backgroundImage = value;
+ _actualBackgroundImageComputed = false;
+ }
}
public string BackgroundPosition
@@ -486,18 +522,6 @@ public string BackgroundRepeat
set { _backgroundRepeat = value; }
}
- public string BackgroundGradient
- {
- get { return _backgroundGradient; }
- set { _backgroundGradient = value; }
- }
-
- public string BackgroundGradientAngle
- {
- get { return _backgroundGradientAngle; }
- set { _backgroundGradientAngle = value; }
- }
-
public string Color
{
get { return _color; }
@@ -1066,6 +1090,26 @@ public RColor ActualBorderTopColor
protected abstract RColor GetActualColor(string colorStr);
+ protected abstract CssImage GetActualBackgroundImageValue(string value);
+
+ ///
+ /// Gets the parsed "background-image" value - either a url() reference or a linear-gradient(),
+ /// lazily resolved (needs adapter-dependent named-color lookup for gradient stops, see
+ /// ) and cached until is set again.
+ ///
+ public CssImage ActualBackgroundImage
+ {
+ get
+ {
+ if (!_actualBackgroundImageComputed)
+ {
+ _actualBackgroundImage = GetActualBackgroundImageValue(BackgroundImage);
+ _actualBackgroundImageComputed = true;
+ }
+ return _actualBackgroundImage;
+ }
+ }
+
///
/// Gets the actual Left border Color
///
@@ -1111,72 +1155,147 @@ public RColor ActualBorderRightColor
}
}
- ///
- /// Gets the actual length of the north west corner
- ///
- public double ActualCornerNw
+ public double ActualBorderTopLeftRadiusX
{
get
{
- if (double.IsNaN(_actualCornerNw))
- {
- _actualCornerNw = CssValueParser.ParseLength(CornerNwRadius, 0, this);
- }
- return _actualCornerNw;
+ if (double.IsNaN(_actualBorderTopLeftRadiusX))
+ _actualBorderTopLeftRadiusX = CssValueParser.ParseLength(FirstCssValue(BorderTopLeftRadius), Size.Width, this);
+ return _actualBorderTopLeftRadiusX;
}
}
- ///
- /// Gets the actual length of the north east corner
- ///
- public double ActualCornerNe
+ public double ActualBorderTopLeftRadiusY
{
get
{
- if (double.IsNaN(_actualCornerNe))
- {
- _actualCornerNe = CssValueParser.ParseLength(CornerNeRadius, 0, this);
- }
- return _actualCornerNe;
+ if (double.IsNaN(_actualBorderTopLeftRadiusY))
+ _actualBorderTopLeftRadiusY = CssValueParser.ParseLength(SecondCssValue(BorderTopLeftRadius), Size.Height, this);
+ return _actualBorderTopLeftRadiusY;
}
}
- ///
- /// Gets the actual length of the south east corner
- ///
- public double ActualCornerSe
+ public double ActualBorderTopRightRadiusX
{
get
{
- if (double.IsNaN(_actualCornerSe))
- {
- _actualCornerSe = CssValueParser.ParseLength(CornerSeRadius, 0, this);
- }
- return _actualCornerSe;
+ if (double.IsNaN(_actualBorderTopRightRadiusX))
+ _actualBorderTopRightRadiusX = CssValueParser.ParseLength(FirstCssValue(BorderTopRightRadius), Size.Width, this);
+ return _actualBorderTopRightRadiusX;
}
}
- ///
- /// Gets the actual length of the south west corner
- ///
- public double ActualCornerSw
+ public double ActualBorderTopRightRadiusY
{
get
{
- if (double.IsNaN(_actualCornerSw))
- {
- _actualCornerSw = CssValueParser.ParseLength(CornerSwRadius, 0, this);
- }
- return _actualCornerSw;
+ if (double.IsNaN(_actualBorderTopRightRadiusY))
+ _actualBorderTopRightRadiusY = CssValueParser.ParseLength(SecondCssValue(BorderTopRightRadius), Size.Height, this);
+ return _actualBorderTopRightRadiusY;
+ }
+ }
+
+ public double ActualBorderBottomRightRadiusX
+ {
+ get
+ {
+ if (double.IsNaN(_actualBorderBottomRightRadiusX))
+ _actualBorderBottomRightRadiusX = CssValueParser.ParseLength(FirstCssValue(BorderBottomRightRadius), Size.Width, this);
+ return _actualBorderBottomRightRadiusX;
+ }
+ }
+
+ public double ActualBorderBottomRightRadiusY
+ {
+ get
+ {
+ if (double.IsNaN(_actualBorderBottomRightRadiusY))
+ _actualBorderBottomRightRadiusY = CssValueParser.ParseLength(SecondCssValue(BorderBottomRightRadius), Size.Height, this);
+ return _actualBorderBottomRightRadiusY;
+ }
+ }
+
+ public double ActualBorderBottomLeftRadiusX
+ {
+ get
+ {
+ if (double.IsNaN(_actualBorderBottomLeftRadiusX))
+ _actualBorderBottomLeftRadiusX = CssValueParser.ParseLength(FirstCssValue(BorderBottomLeftRadius), Size.Width, this);
+ return _actualBorderBottomLeftRadiusX;
+ }
+ }
+
+ public double ActualBorderBottomLeftRadiusY
+ {
+ get
+ {
+ if (double.IsNaN(_actualBorderBottomLeftRadiusY))
+ _actualBorderBottomLeftRadiusY = CssValueParser.ParseLength(SecondCssValue(BorderBottomLeftRadius), Size.Height, this);
+ return _actualBorderBottomLeftRadiusY;
}
}
///
- /// Gets a value indicating if at least one of the corners of the box is rounded
+ /// Returns the first top-level-whitespace-delimited token in a "border-*-radius" longhand value
+ /// (the horizontal radius).
+ ///
+ private static string FirstCssValue(string value)
+ {
+ foreach (var token in CssValueParser.SplitTopLevelWhitespace(value))
+ return token;
+ return value;
+ }
+
+ ///
+ /// Returns the second top-level-whitespace-delimited token in a "border-*-radius" longhand value
+ /// (the vertical radius), or the first if there is no second (spec: omitted v-radius = h-radius).
+ ///
+ private static string SecondCssValue(string value)
+ {
+ var tokens = new List(CssValueParser.SplitTopLevelWhitespace(value));
+ return tokens.Count > 1 ? tokens[1] : value;
+ }
+
+ ///
+ /// Computes overlap-reduced corner radii for the given rendering rectangle, per CSS Backgrounds
+ /// §4. Horizontal and vertical axes are reduced independently so that, e.g., two large radii on
+ /// the same edge never sum to more than that edge's length.
+ ///
+ public BorderRadii ComputeRadii(RRect rect)
+ {
+ double tlX = ActualBorderTopLeftRadiusX, tlY = ActualBorderTopLeftRadiusY;
+ double trX = ActualBorderTopRightRadiusX, trY = ActualBorderTopRightRadiusY;
+ double brX = ActualBorderBottomRightRadiusX, brY = ActualBorderBottomRightRadiusY;
+ double blX = ActualBorderBottomLeftRadiusX, blY = ActualBorderBottomLeftRadiusY;
+
+ // Horizontal reduction: check top side and bottom side independently.
+ double fTop = tlX + trX > 0 && rect.Width > 0 ? rect.Width / (tlX + trX) : 1.0;
+ double fBot = blX + brX > 0 && rect.Width > 0 ? rect.Width / (blX + brX) : 1.0;
+ double fX = Math.Min(1.0, Math.Min(fTop, fBot));
+
+ // Vertical reduction: check left side and right side independently.
+ double fLeft = tlY + blY > 0 && rect.Height > 0 ? rect.Height / (tlY + blY) : 1.0;
+ double fRight = trY + brY > 0 && rect.Height > 0 ? rect.Height / (trY + brY) : 1.0;
+ double fY = Math.Min(1.0, Math.Min(fLeft, fRight));
+
+ return new BorderRadii(tlX * fX, tlY * fY, trX * fX, trY * fY,
+ brX * fX, brY * fY, blX * fX, blY * fY);
+ }
+
+ ///
+ /// Gets a value indicating if at least one of the corners of the box is rounded. A fast,
+ /// rect-independent pre-check using the raw (not overlap-reduced) actual radii - use
+ /// when the actual per-corner values are needed for painting.
///
public bool IsRounded
{
- get { return ActualCornerNe > 0f || ActualCornerNw > 0f || ActualCornerSe > 0f || ActualCornerSw > 0f; }
+ get
+ {
+ return ActualBorderTopLeftRadiusX > 0 || ActualBorderTopLeftRadiusY > 0 ||
+ ActualBorderTopRightRadiusX > 0 || ActualBorderTopRightRadiusY > 0 ||
+ ActualBorderBottomRightRadiusX > 0 || ActualBorderBottomRightRadiusY > 0 ||
+ ActualBorderBottomLeftRadiusX > 0 || ActualBorderBottomLeftRadiusY > 0;
+ }
}
///
@@ -1220,37 +1339,6 @@ public RColor ActualBackgroundColor
}
}
- ///
- /// Gets the second color that creates a gradient for the background
- ///
- public RColor ActualBackgroundGradient
- {
- get
- {
- if (_actualBackgroundGradient.IsEmpty)
- {
- _actualBackgroundGradient = GetActualColor(BackgroundGradient);
- }
- return _actualBackgroundGradient;
- }
- }
-
- ///
- /// Gets the actual angle specified for the background gradient
- ///
- public double ActualBackgroundGradientAngle
- {
- get
- {
- if (double.IsNaN(_actualBackgroundGradientAngle))
- {
- _actualBackgroundGradientAngle = CssValueParser.ParseNumber(BackgroundGradientAngle, 360f);
- }
-
- return _actualBackgroundGradientAngle;
- }
- }
-
///
/// Gets the actual font of the parent
///
@@ -1528,8 +1616,6 @@ protected void InheritStyle(CssBox p, bool everything)
if (everything)
{
_backgroundColor = p._backgroundColor;
- _backgroundGradient = p._backgroundGradient;
- _backgroundGradientAngle = p._backgroundGradientAngle;
_backgroundImage = p._backgroundImage;
_backgroundPosition = p._backgroundPosition;
_backgroundRepeat = p._backgroundRepeat;
@@ -1546,11 +1632,10 @@ protected void InheritStyle(CssBox p, bool everything)
_borderBottomStyle = p._borderBottomStyle;
_borderLeftStyle = p._borderLeftStyle;
_bottom = p._bottom;
- _cornerNwRadius = p._cornerNwRadius;
- _cornerNeRadius = p._cornerNeRadius;
- _cornerSeRadius = p._cornerSeRadius;
- _cornerSwRadius = p._cornerSwRadius;
- _cornerRadius = p._cornerRadius;
+ _borderTopLeftRadius = p._borderTopLeftRadius;
+ _borderTopRightRadius = p._borderTopRightRadius;
+ _borderBottomRightRadius = p._borderBottomRightRadius;
+ _borderBottomLeftRadius = p._borderBottomLeftRadius;
_display = p._display;
_float = p._float;
_height = p._height;
@@ -1577,4 +1662,27 @@ protected void InheritStyle(CssBox p, bool everything)
}
}
}
+
+ ///
+ /// Holds the eight computed (overlap-reduced) corner radii for a box rectangle - see
+ /// .
+ ///
+ internal struct BorderRadii
+ {
+ public readonly double TLX, TLY, TRX, TRY, BRX, BRY, BLX, BLY;
+
+ public BorderRadii(double tlX, double tlY, double trX, double trY,
+ double brX, double brY, double blX, double blY)
+ {
+ TLX = tlX; TLY = tlY;
+ TRX = trX; TRY = trY;
+ BRX = brX; BRY = brY;
+ BLX = blX; BLY = blY;
+ }
+
+ public bool IsRounded
+ {
+ get { return TLX > 0 || TLY > 0 || TRX > 0 || TRY > 0 || BRX > 0 || BRY > 0 || BLX > 0 || BLY > 0; }
+ }
+ }
}
\ No newline at end of file
diff --git a/Source/HtmlRenderer/Core/Entities/CssImage.cs b/Source/HtmlRenderer/Core/Entities/CssImage.cs
new file mode 100644
index 000000000..570f0d275
--- /dev/null
+++ b/Source/HtmlRenderer/Core/Entities/CssImage.cs
@@ -0,0 +1,32 @@
+namespace TheArtOfDev.HtmlRenderer.Core.Entities
+{
+ ///
+ /// The parsed form of a single background-image value: either a url() reference or a
+ /// gradient function. Unlike PeachPDF's CssImage (which this is a trimmed-down port of, same
+ /// name for an easy diff), background-image here is a single value, not a comma-separated
+ /// layered list - this project only backports enough to replace the old non-standard
+ /// "background-gradient" property.
+ ///
+ internal abstract class CssImage
+ {
+ internal sealed class Url : CssImage
+ {
+ public Url(string href)
+ {
+ Href = href;
+ }
+
+ public string Href { get; }
+ }
+
+ internal sealed class LinearGradient : CssImage
+ {
+ public LinearGradient(ParsedLinearGradient gradient)
+ {
+ Gradient = gradient;
+ }
+
+ public ParsedLinearGradient Gradient { get; }
+ }
+ }
+}
diff --git a/Source/HtmlRenderer/Core/Entities/ParsedLinearGradient.cs b/Source/HtmlRenderer/Core/Entities/ParsedLinearGradient.cs
new file mode 100644
index 000000000..cb43e701f
--- /dev/null
+++ b/Source/HtmlRenderer/Core/Entities/ParsedLinearGradient.cs
@@ -0,0 +1,18 @@
+using TheArtOfDev.HtmlRenderer.Adapters.Entities;
+using TheArtOfDev.HtmlRenderer.Core.CssEngine;
+
+namespace TheArtOfDev.HtmlRenderer.Core.Entities
+{
+ ///
+ /// A fully parsed linear-gradient()/repeating-linear-gradient() value - the angle and
+ /// raw (not yet position-resolved) color stop list. Ported from PeachPDF's
+ /// Html/Core/Entities/ParsedLinearGradient.cs, minus CSS Color 4 interpolation-color-space support
+ /// (ColorSpace/HueMethod) - gradients always interpolate in sRGB here.
+ ///
+ internal sealed class ParsedLinearGradient
+ {
+ public double AngleRad { get; set; }
+ public (RColor? Color, Length? Position, bool IsHint)[] Stops { get; set; }
+ public bool IsRepeating { get; set; }
+ }
+}
diff --git a/Source/HtmlRenderer/Core/Handlers/BordersDrawHandler.cs b/Source/HtmlRenderer/Core/Handlers/BordersDrawHandler.cs
index b20c331cb..2cd90b33f 100644
--- a/Source/HtmlRenderer/Core/Handlers/BordersDrawHandler.cs
+++ b/Source/HtmlRenderer/Core/Handlers/BordersDrawHandler.cs
@@ -202,73 +202,76 @@ private static void SetInOutsetRectanglePoints(Border border, CssBox b, RRect r,
/// Beveled border path, null if there is no rounded corners
private static RGraphicsPath GetRoundedBorderPath(RGraphics g, Border border, CssBox b, RRect r)
{
+ var rad = b.ComputeRadii(r);
+ if (!rad.IsRounded) return null;
+
RGraphicsPath path = null;
switch (border)
{
case Border.Top:
- if (b.ActualCornerNw > 0 || b.ActualCornerNe > 0)
+ if (rad.TLX > 0 || rad.TLY > 0 || rad.TRX > 0 || rad.TRY > 0)
{
path = g.GetGraphicsPath();
- path.Start(r.Left + b.ActualBorderLeftWidth / 2, r.Top + b.ActualBorderTopWidth / 2 + b.ActualCornerNw);
+ path.Start(r.Left + b.ActualBorderLeftWidth / 2, r.Top + b.ActualBorderTopWidth / 2 + rad.TLY);
- if (b.ActualCornerNw > 0)
- path.ArcTo(r.Left + b.ActualBorderLeftWidth / 2 + b.ActualCornerNw, r.Top + b.ActualBorderTopWidth / 2, b.ActualCornerNw, RGraphicsPath.Corner.TopLeft);
+ if (rad.TLX > 0 || rad.TLY > 0)
+ path.ArcTo(r.Left + b.ActualBorderLeftWidth / 2 + rad.TLX, r.Top + b.ActualBorderTopWidth / 2, rad.TLX, rad.TLY, RGraphicsPath.Corner.TopLeft);
- path.LineTo(r.Right - b.ActualBorderRightWidth / 2 - b.ActualCornerNe, r.Top + b.ActualBorderTopWidth / 2);
+ path.LineTo(r.Right - b.ActualBorderRightWidth / 2 - rad.TRX, r.Top + b.ActualBorderTopWidth / 2);
- if (b.ActualCornerNe > 0)
- path.ArcTo(r.Right - b.ActualBorderRightWidth / 2, r.Top + b.ActualBorderTopWidth / 2 + b.ActualCornerNe, b.ActualCornerNe, RGraphicsPath.Corner.TopRight);
+ if (rad.TRX > 0 || rad.TRY > 0)
+ path.ArcTo(r.Right - b.ActualBorderRightWidth / 2, r.Top + b.ActualBorderTopWidth / 2 + rad.TRY, rad.TRX, rad.TRY, RGraphicsPath.Corner.TopRight);
}
break;
case Border.Bottom:
- if (b.ActualCornerSw > 0 || b.ActualCornerSe > 0)
+ if (rad.BLX > 0 || rad.BLY > 0 || rad.BRX > 0 || rad.BRY > 0)
{
path = g.GetGraphicsPath();
- path.Start(r.Right - b.ActualBorderRightWidth / 2, r.Bottom - b.ActualBorderBottomWidth / 2 - b.ActualCornerSe);
+ path.Start(r.Right - b.ActualBorderRightWidth / 2, r.Bottom - b.ActualBorderBottomWidth / 2 - rad.BRY);
- if (b.ActualCornerSe > 0)
- path.ArcTo(r.Right - b.ActualBorderRightWidth / 2 - b.ActualCornerSe, r.Bottom - b.ActualBorderBottomWidth / 2, b.ActualCornerSe, RGraphicsPath.Corner.BottomRight);
+ if (rad.BRX > 0 || rad.BRY > 0)
+ path.ArcTo(r.Right - b.ActualBorderRightWidth / 2 - rad.BRX, r.Bottom - b.ActualBorderBottomWidth / 2, rad.BRX, rad.BRY, RGraphicsPath.Corner.BottomRight);
- path.LineTo(r.Left + b.ActualBorderLeftWidth / 2 + b.ActualCornerSw, r.Bottom - b.ActualBorderBottomWidth / 2);
+ path.LineTo(r.Left + b.ActualBorderLeftWidth / 2 + rad.BLX, r.Bottom - b.ActualBorderBottomWidth / 2);
- if (b.ActualCornerSw > 0)
- path.ArcTo(r.Left + b.ActualBorderLeftWidth / 2, r.Bottom - b.ActualBorderBottomWidth / 2 - b.ActualCornerSw, b.ActualCornerSw, RGraphicsPath.Corner.BottomLeft);
+ if (rad.BLX > 0 || rad.BLY > 0)
+ path.ArcTo(r.Left + b.ActualBorderLeftWidth / 2, r.Bottom - b.ActualBorderBottomWidth / 2 - rad.BLY, rad.BLX, rad.BLY, RGraphicsPath.Corner.BottomLeft);
}
break;
case Border.Right:
- if (b.ActualCornerNe > 0 || b.ActualCornerSe > 0)
+ if (rad.TRX > 0 || rad.TRY > 0 || rad.BRX > 0 || rad.BRY > 0)
{
path = g.GetGraphicsPath();
bool noTop = b.BorderTopStyle == CssConstants.None || b.BorderTopStyle == CssConstants.Hidden;
bool noBottom = b.BorderBottomStyle == CssConstants.None || b.BorderBottomStyle == CssConstants.Hidden;
- path.Start(r.Right - b.ActualBorderRightWidth / 2 - (noTop ? b.ActualCornerNe : 0), r.Top + b.ActualBorderTopWidth / 2 + (noTop ? 0 : b.ActualCornerNe));
+ path.Start(r.Right - b.ActualBorderRightWidth / 2 - (noTop ? rad.TRX : 0), r.Top + b.ActualBorderTopWidth / 2 + (noTop ? 0 : rad.TRY));
- if (b.ActualCornerNe > 0 && noTop)
- path.ArcTo(r.Right - b.ActualBorderLeftWidth / 2, r.Top + b.ActualBorderTopWidth / 2 + b.ActualCornerNe, b.ActualCornerNe, RGraphicsPath.Corner.TopRight);
+ if ((rad.TRX > 0 || rad.TRY > 0) && noTop)
+ path.ArcTo(r.Right - b.ActualBorderLeftWidth / 2, r.Top + b.ActualBorderTopWidth / 2 + rad.TRY, rad.TRX, rad.TRY, RGraphicsPath.Corner.TopRight);
- path.LineTo(r.Right - b.ActualBorderRightWidth / 2, r.Bottom - b.ActualBorderBottomWidth / 2 - b.ActualCornerSe);
+ path.LineTo(r.Right - b.ActualBorderRightWidth / 2, r.Bottom - b.ActualBorderBottomWidth / 2 - rad.BRY);
- if (b.ActualCornerSe > 0 && noBottom)
- path.ArcTo(r.Right - b.ActualBorderRightWidth / 2 - b.ActualCornerSe, r.Bottom - b.ActualBorderBottomWidth / 2, b.ActualCornerSe, RGraphicsPath.Corner.BottomRight);
+ if ((rad.BRX > 0 || rad.BRY > 0) && noBottom)
+ path.ArcTo(r.Right - b.ActualBorderRightWidth / 2 - rad.BRX, r.Bottom - b.ActualBorderBottomWidth / 2, rad.BRX, rad.BRY, RGraphicsPath.Corner.BottomRight);
}
break;
case Border.Left:
- if (b.ActualCornerNw > 0 || b.ActualCornerSw > 0)
+ if (rad.TLX > 0 || rad.TLY > 0 || rad.BLX > 0 || rad.BLY > 0)
{
path = g.GetGraphicsPath();
bool noTop = b.BorderTopStyle == CssConstants.None || b.BorderTopStyle == CssConstants.Hidden;
bool noBottom = b.BorderBottomStyle == CssConstants.None || b.BorderBottomStyle == CssConstants.Hidden;
- path.Start(r.Left + b.ActualBorderLeftWidth / 2 + (noBottom ? b.ActualCornerSw : 0), r.Bottom - b.ActualBorderBottomWidth / 2 - (noBottom ? 0 : b.ActualCornerSw));
+ path.Start(r.Left + b.ActualBorderLeftWidth / 2 + (noBottom ? rad.BLX : 0), r.Bottom - b.ActualBorderBottomWidth / 2 - (noBottom ? 0 : rad.BLY));
- if (b.ActualCornerSw > 0 && noBottom)
- path.ArcTo(r.Left + b.ActualBorderLeftWidth / 2, r.Bottom - b.ActualBorderBottomWidth / 2 - b.ActualCornerSw, b.ActualCornerSw, RGraphicsPath.Corner.BottomLeft);
+ if ((rad.BLX > 0 || rad.BLY > 0) && noBottom)
+ path.ArcTo(r.Left + b.ActualBorderLeftWidth / 2, r.Bottom - b.ActualBorderBottomWidth / 2 - rad.BLY, rad.BLX, rad.BLY, RGraphicsPath.Corner.BottomLeft);
- path.LineTo(r.Left + b.ActualBorderLeftWidth / 2, r.Top + b.ActualBorderTopWidth / 2 + b.ActualCornerNw);
+ path.LineTo(r.Left + b.ActualBorderLeftWidth / 2, r.Top + b.ActualBorderTopWidth / 2 + rad.TLY);
- if (b.ActualCornerNw > 0 && noTop)
- path.ArcTo(r.Left + b.ActualBorderLeftWidth / 2 + b.ActualCornerNw, r.Top + b.ActualBorderTopWidth / 2, b.ActualCornerNw, RGraphicsPath.Corner.TopLeft);
+ if ((rad.TLX > 0 || rad.TLY > 0) && noTop)
+ path.ArcTo(r.Left + b.ActualBorderLeftWidth / 2 + rad.TLX, r.Top + b.ActualBorderTopWidth / 2, rad.TLX, rad.TLY, RGraphicsPath.Corner.TopLeft);
}
break;
}
diff --git a/Source/HtmlRenderer/Core/Handlers/CssImagePainter.cs b/Source/HtmlRenderer/Core/Handlers/CssImagePainter.cs
new file mode 100644
index 000000000..e8fe16d7d
--- /dev/null
+++ b/Source/HtmlRenderer/Core/Handlers/CssImagePainter.cs
@@ -0,0 +1,234 @@
+// "Therefore those skilled at the unorthodox
+// are infinite as heaven and earth,
+// inexhaustible as the great rivers.
+// When they come to an end,
+// they begin again,
+// like the days and months;
+// they die and are reborn,
+// like the four seasons."
+//
+// - Sun Tsu,
+// "The Art of War"
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using TheArtOfDev.HtmlRenderer.Adapters;
+using TheArtOfDev.HtmlRenderer.Adapters.Entities;
+using TheArtOfDev.HtmlRenderer.Core.CssEngine;
+using TheArtOfDev.HtmlRenderer.Core.Entities;
+
+namespace TheArtOfDev.HtmlRenderer.Core.Handlers
+{
+ ///
+ /// Turns a parsed linear-gradient() value into a brush ready to paint. Ported from PeachPDF's
+ /// Html/Core/Handlers/CssImagePainter.cs, trimmed to the linear-gradient-only, single (non-layered)
+ /// background-image case this engine supports - see .
+ ///
+ internal static class CssImagePainter
+ {
+ ///
+ /// Builds the brush for a linear-gradient() background, filling the given rectangle.
+ ///
+ public static RBrush GetLinearGradientBrush(RGraphics g, RRect rect, ParsedLinearGradient gradient)
+ {
+ var (p1, p2) = ComputeGradientLine(rect, gradient.AngleRad);
+ double gdx = p2.X - p1.X, gdy = p2.Y - p1.Y;
+ double gradientLength = Math.Sqrt(gdx * gdx + gdy * gdy);
+ var stops = NormalizeGradientStops(gradient.Stops, gradientLength);
+ if (gradient.IsRepeating)
+ stops = ExpandRepeatingStops(stops);
+ return g.GetLinearGradientBrush(p1, p2, stops);
+ }
+
+ ///
+ /// Computes the CSS gradient line endpoints for the given box rect and angle (standard CSS
+ /// "corner-to-corner projection" algorithm - the line is centered on the box and long enough that
+ /// the box's bounding rectangle is fully spanned along the gradient direction).
+ ///
+ private static (RPoint p1, RPoint p2) ComputeGradientLine(RRect rect, double angleRad)
+ {
+ double dx = Math.Sin(angleRad);
+ double dy = -Math.Cos(angleRad);
+ double cx = rect.X + rect.Width / 2;
+ double cy = rect.Y + rect.Height / 2;
+ double halfLen = Math.Abs(dx) * rect.Width / 2 + Math.Abs(dy) * rect.Height / 2;
+ var p1 = new RPoint(cx - dx * halfLen, cy - dy * halfLen);
+ var p2 = new RPoint(cx + dx * halfLen, cy + dy * halfLen);
+ return (p1, p2);
+ }
+
+ private static double? ConvertLength(Length? length, double gradientLength, double emPx = 16.0)
+ {
+ if (!length.HasValue) return null;
+ var len = length.Value;
+ if (len.Type == Length.Unit.Percent)
+ return len.Value / 100.0;
+ if (len.IsAbsolute)
+ return gradientLength > 0 ? len.ToPixel() / gradientLength : 0.0;
+ if (len.Type == Length.Unit.Em)
+ return gradientLength > 0 ? len.Value * emPx / gradientLength : 0.0;
+ return null;
+ }
+
+ private static RColor LerpColor(RColor a, RColor b, double t)
+ {
+ t = t < 0.0 ? 0.0 : (t > 1.0 ? 1.0 : t);
+ return RColor.FromArgb(
+ (int)Math.Round(a.A + t * (b.A - a.A)),
+ (int)Math.Round(a.R + t * (b.R - a.R)),
+ (int)Math.Round(a.G + t * (b.G - a.G)),
+ (int)Math.Round(a.B + t * (b.B - a.B)));
+ }
+
+ ///
+ /// Resolves each stop's position to a [0,1] fraction of the gradient line (per CSS Images §3.5.5:
+ /// the first/last stop default to 0%/100%, middle stops without an explicit position are spaced
+ /// evenly between their neighbors, and bare-position "hints" between two color stops bias the
+ /// interpolation curve rather than being a stop themselves).
+ ///
+ private static (RColor Color, double Position)[] NormalizeGradientStops(
+ (RColor? Color, Length? Position, bool IsHint)[] stops,
+ double gradientLength,
+ double emPx = 16.0)
+ {
+ var colorStops = stops.Where(s => !s.IsHint).ToArray();
+ int n = colorStops.Length;
+ if (n == 0) return new (RColor, double)[0];
+
+ var rawPos = new double?[n];
+ for (int i = 0; i < n; i++)
+ rawPos[i] = ConvertLength(colorStops[i].Position, gradientLength, emPx);
+
+ var resolved = new (RColor Color, double Position)[n];
+ double first = rawPos[0] ?? 0.0;
+ double last = rawPos[n - 1] ?? 1.0;
+ resolved[0] = (colorStops[0].Color.Value, first);
+ resolved[n - 1] = (colorStops[n - 1].Color.Value, last);
+
+ int runStart = -1;
+ for (int i = 1; i < n - 1; i++)
+ {
+ if (rawPos[i].HasValue)
+ {
+ resolved[i] = (colorStops[i].Color.Value, rawPos[i].Value);
+ if (runStart >= 0)
+ {
+ double posA = resolved[runStart - 1].Position;
+ double posB = resolved[i].Position;
+ int count = i - runStart + 1;
+ for (int j = runStart; j < i; j++)
+ {
+ double t = (double)(j - runStart + 1) / count;
+ resolved[j] = (colorStops[j].Color.Value, posA + t * (posB - posA));
+ }
+ runStart = -1;
+ }
+ }
+ else
+ {
+ if (runStart < 0) runStart = i;
+ resolved[i] = (colorStops[i].Color.Value, 0);
+ }
+ }
+ if (runStart >= 0)
+ {
+ double posA = resolved[runStart - 1].Position;
+ double posB = resolved[n - 1].Position;
+ int count = n - 1 - runStart + 1;
+ for (int j = runStart; j < n - 1; j++)
+ {
+ double t = (double)(j - runStart + 1) / count;
+ resolved[j] = (colorStops[j].Color.Value, posA + t * (posB - posA));
+ }
+ }
+
+ if (!stops.Any(s => s.IsHint))
+ return resolved;
+
+ var result = new List<(RColor Color, double Position)>();
+ int colorIdx = 0;
+ for (int i = 0; i < stops.Length; i++)
+ {
+ if (!stops[i].IsHint)
+ {
+ result.Add(resolved[colorIdx++]);
+ }
+ else
+ {
+ if (colorIdx == 0 || colorIdx >= n) continue;
+ var s1 = resolved[colorIdx - 1];
+ var s2 = resolved[colorIdx];
+ double range = s2.Position - s1.Position;
+ double hintPos = ConvertLength(stops[i].Position, gradientLength, emPx) ?? (s1.Position + range * 0.5);
+ double h = range > 1e-9
+ ? Math.Min(Math.Max((hintPos - s1.Position) / range, 1e-9), 1.0 - 1e-9)
+ : 0.5;
+ const int kSteps = 7;
+ double logHalf = Math.Log(0.5);
+ double logH = Math.Log(h);
+ for (int k = 1; k <= kSteps; k++)
+ {
+ double t = (double)k / (kSteps + 1);
+ double curved = Math.Pow(t, logHalf / logH);
+ result.Add((LerpColor(s1.Color, s2.Color, curved), s1.Position + t * range));
+ }
+ }
+ }
+ return result.ToArray();
+ }
+
+ ///
+ /// Flattens a repeating gradient's stop tile into a single non-repeating stop list spanning the
+ /// full [0,1] gradient line, by replicating the tile as many times as needed in both directions.
+ ///
+ private static (RColor Color, double Position)[] ExpandRepeatingStops((RColor Color, double Position)[] stops)
+ {
+ if (stops.Length < 2) return stops;
+ double tileStart = stops[0].Position;
+ double tileEnd = stops[stops.Length - 1].Position;
+ double tileLen = tileEnd - tileStart;
+ if (tileLen < 1e-6 || (tileStart <= 0.0 && tileEnd >= 1.0)) return stops;
+ const double eps = 0.0001;
+ var result = new List<(RColor Color, double Position)>();
+ int kMin = (int)Math.Floor(-tileEnd / tileLen);
+ int kMax = (int)Math.Ceiling((1.0 - tileStart) / tileLen);
+ for (int k = kMin; k <= kMax; k++)
+ {
+ double kOffset = k * tileLen;
+ for (int i = 0; i < stops.Length; i++)
+ {
+ double rawPos = stops[i].Position + kOffset;
+ bool isLastStop = i == stops.Length - 1;
+ double adjPos = isLastStop && k < kMax ? rawPos - eps : rawPos;
+ if (adjPos >= -eps && adjPos <= 1.0 + eps)
+ result.Add((stops[i].Color, Math.Min(Math.Max(adjPos, 0.0), 1.0)));
+ }
+ }
+ result.Sort((a, b) => a.Position.CompareTo(b.Position));
+ if (result.Count == 0) return stops;
+ if (result[0].Position > eps)
+ result.Insert(0, (SampleRepeatingColor(stops, tileStart, tileLen, 0.0), 0.0));
+ if (result[result.Count - 1].Position < 1.0 - eps)
+ result.Add((SampleRepeatingColor(stops, tileStart, tileLen, 1.0), 1.0));
+ return result.ToArray();
+ }
+
+ private static RColor SampleRepeatingColor((RColor Color, double Position)[] stops, double tileStart, double tileLen, double pos)
+ {
+ double relPos = (pos - tileStart) % tileLen;
+ if (relPos < 0) relPos += tileLen;
+ double absWithinTile = tileStart + relPos;
+ for (int i = 0; i < stops.Length - 1; i++)
+ {
+ if (absWithinTile >= stops[i].Position && absWithinTile <= stops[i + 1].Position)
+ {
+ double range = stops[i + 1].Position - stops[i].Position;
+ double t = range > 1e-12 ? (absWithinTile - stops[i].Position) / range : 0.0;
+ return LerpColor(stops[i].Color, stops[i + 1].Color, t);
+ }
+ }
+ return absWithinTile <= stops[0].Position ? stops[0].Color : stops[stops.Length - 1].Color;
+ }
+ }
+}
diff --git a/Source/HtmlRenderer/Core/Parse/CssParser.cs b/Source/HtmlRenderer/Core/Parse/CssParser.cs
index b492f6e68..4c3d7c289 100644
--- a/Source/HtmlRenderer/Core/Parse/CssParser.cs
+++ b/Source/HtmlRenderer/Core/Parse/CssParser.cs
@@ -16,6 +16,7 @@
using TheArtOfDev.HtmlRenderer.Adapters;
using TheArtOfDev.HtmlRenderer.Adapters.Entities;
using TheArtOfDev.HtmlRenderer.Core.CssEngine;
+using TheArtOfDev.HtmlRenderer.Core.Entities;
using TheArtOfDev.HtmlRenderer.Core.Handlers;
using TheArtOfDev.HtmlRenderer.Core.Utils;
@@ -155,6 +156,15 @@ public bool IsColorValid(string colorValue)
return _valueParser.IsColorValid(colorValue);
}
+ ///
+ /// Parses a "background-image" value into a url() reference or a linear-gradient() - see
+ /// .
+ ///
+ public CssImage ParseBackgroundImage(string value)
+ {
+ return _valueParser.ParseImage(value);
+ }
+
///
/// Parse a complex font family css property to check if it contains multiple fonts and if the font exists.
/// returns the font family name to use or 'inherit' if failed.
diff --git a/Source/HtmlRenderer/Core/Parse/CssValueParser.cs b/Source/HtmlRenderer/Core/Parse/CssValueParser.cs
index e1f6d738e..5efeb06c3 100644
--- a/Source/HtmlRenderer/Core/Parse/CssValueParser.cs
+++ b/Source/HtmlRenderer/Core/Parse/CssValueParser.cs
@@ -13,10 +13,13 @@
using System;
using System.Collections.Generic;
using System.Globalization;
+using System.Linq;
+using System.Text;
using TheArtOfDev.HtmlRenderer.Adapters;
using TheArtOfDev.HtmlRenderer.Adapters.Entities;
using TheArtOfDev.HtmlRenderer.Core.CssEngine;
using TheArtOfDev.HtmlRenderer.Core.Dom;
+using TheArtOfDev.HtmlRenderer.Core.Entities;
using TheArtOfDev.HtmlRenderer.Core.Utils;
namespace TheArtOfDev.HtmlRenderer.Core.Parse
@@ -753,5 +756,217 @@ private static int ParseHexInt(string str, int idx, int length)
}
#endregion
+
+
+ #region Background image / gradient parsing
+
+ ///
+ /// Parses a single background-image value: either a url() reference or a
+ /// linear-gradient()/repeating-linear-gradient() function. Returns null for "none",
+ /// an empty value, or anything else this engine doesn't recognize (matching this engine's existing
+ /// silent-failure behavior for unsupported values).
+ /// Ported from PeachPDF's Html/Core/Parse/CssValueParser.ParseImage, trimmed to the single-value,
+ /// linear-gradient-only case this engine supports (no layered background-image, no radial/conic).
+ ///
+ public CssImage ParseImage(string value)
+ {
+ if (string.IsNullOrWhiteSpace(value) || string.Equals(value, CssConstants.None, StringComparison.OrdinalIgnoreCase))
+ return null;
+
+ var tokens = GetCssTokens(value);
+
+ var urlToken = tokens.OfType().FirstOrDefault();
+ if (urlToken != null)
+ return new CssImage.Url(urlToken.Data);
+
+ var funcToken = tokens.OfType().FirstOrDefault();
+ if (funcToken == null)
+ return null;
+
+ if (string.Equals(funcToken.Data, FunctionNames.LinearGradient, StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(funcToken.Data, FunctionNames.RepeatingLinearGradient, StringComparison.OrdinalIgnoreCase))
+ {
+ var gradient = ParseLinearGradient(value);
+ return gradient != null ? new CssImage.LinearGradient(gradient) : null;
+ }
+
+ return null;
+ }
+
+ ///
+ /// Parses a linear-gradient()/repeating-linear-gradient() function value: an
+ /// optional angle or "to <side>" direction (default 180deg, top to bottom), followed by 2+
+ /// comma-separated color stops (each optionally followed by 1-2 length/percent positions), and
+ /// bare-position color hints between stops. Returns null if the value isn't a recognized gradient
+ /// function or doesn't have at least 2 real color stops.
+ /// Ported from PeachPDF's Html/Core/Parse/CssValueParser.ParseLinearGradient, minus CSS Color 4
+ /// interpolation-color-space ("in oklab" etc.) support - see ParsedLinearGradient.
+ ///
+ private ParsedLinearGradient ParseLinearGradient(string value)
+ {
+ var tokens = GetCssTokens(value);
+
+ var funcToken = tokens.OfType().FirstOrDefault(t =>
+ string.Equals(t.Data, FunctionNames.LinearGradient, StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(t.Data, FunctionNames.RepeatingLinearGradient, StringComparison.OrdinalIgnoreCase));
+
+ if (funcToken == null)
+ return null;
+
+ bool isRepeating = string.Equals(funcToken.Data, FunctionNames.RepeatingLinearGradient, StringComparison.OrdinalIgnoreCase);
+
+ var args = funcToken.ArgumentTokens.ToList();
+ if (args.Count == 0)
+ return null;
+
+ double angleRad = Math.PI; // default: 180deg = top to bottom
+ int stopOffset = 0;
+
+ var firstGroup = args[0];
+ var firstIdents = firstGroup.Where(t => t.Type == TokenType.Ident).Select(t => t.Data.ToLowerInvariant()).ToList();
+
+ if (firstIdents.Count > 0 && firstIdents[0] == "to")
+ {
+ // keyword direction: "to right", "to bottom left", etc.
+ angleRad = SideKeywordsToAngleRad(firstIdents.Skip(1).ToList());
+ stopOffset = 1;
+ }
+ else
+ {
+ var angle = firstGroup.ToAngle();
+ if (angle.HasValue)
+ {
+ angleRad = angle.Value.ToRadian();
+ stopOffset = 1;
+ }
+ // else no angle token, stopOffset stays 0 - first group is already the first color stop
+ }
+
+ var stopGroups = args.Skip(stopOffset).ToList();
+ if (stopGroups.Count < 2)
+ return null;
+
+ var stops = new List<(RColor? Color, Length? Position, bool IsHint)>();
+
+ foreach (var group in stopGroups)
+ {
+ var items = group.ToItems();
+ if (items.Count == 0)
+ continue;
+
+ Length? position1 = null;
+ Length? position2 = null;
+ int colorItemCount = items.Count;
+
+ // Last item may be a length/percent position.
+ var lastItem = items[items.Count - 1];
+ var pv = lastItem.ToDistance();
+ if (pv.HasValue)
+ {
+ position1 = pv.Value;
+ colorItemCount--;
+
+ // Two-position shorthand (e.g. "red 0 50%").
+ if (colorItemCount > 0)
+ {
+ var pv2 = items[colorItemCount - 1].ToDistance();
+ if (pv2.HasValue)
+ {
+ position2 = position1;
+ position1 = pv2.Value;
+ colorItemCount--;
+ }
+ }
+ }
+
+ if (colorItemCount == 0)
+ {
+ // Bare position with no color - a color hint.
+ if (position1.HasValue && !position2.HasValue)
+ stops.Add((null, position1, true));
+ continue;
+ }
+
+ var colorText = BuildColorText(items.Take(colorItemCount));
+ if (string.IsNullOrWhiteSpace(colorText))
+ continue;
+
+ var color = GetActualColor(colorText);
+ stops.Add((color, position1, false));
+ if (position2.HasValue)
+ stops.Add((color, position2, false));
+ }
+
+ if (stops.Count(s => !s.IsHint) < 2)
+ return null;
+
+ return new ParsedLinearGradient
+ {
+ AngleRad = angleRad,
+ Stops = stops.ToArray(),
+ IsRepeating = isRepeating,
+ };
+ }
+
+ ///
+ /// Converts "to <side> [<side>]" direction keywords (e.g. "right", "bottom left") to
+ /// the equivalent gradient-line angle in radians, per the CSS Images spec's side/corner table.
+ ///
+ private static double SideKeywordsToAngleRad(List sides)
+ {
+ bool hasTop = sides.Contains("top");
+ bool hasBottom = sides.Contains("bottom");
+ bool hasLeft = sides.Contains("left");
+ bool hasRight = sides.Contains("right");
+
+ if (hasTop && hasRight) return Math.PI / 4; // 45deg
+ if (hasBottom && hasRight) return 3 * Math.PI / 4; // 135deg
+ if (hasBottom && hasLeft) return 5 * Math.PI / 4; // 225deg
+ if (hasTop && hasLeft) return 7 * Math.PI / 4; // 315deg
+ if (hasTop) return 0; // 0deg
+ if (hasRight) return Math.PI / 2; // 90deg
+ if (hasBottom) return Math.PI; // 180deg
+ if (hasLeft) return 3 * Math.PI / 2; // 270deg
+
+ return Math.PI; // default
+ }
+
+ ///
+ /// Re-serializes a color stop's token groups (everything before its trailing position token(s))
+ /// back to CSS text so it can be handed to as a normal color string.
+ ///
+ private static string BuildColorText(IEnumerable> itemGroups)
+ {
+ var sb = new StringBuilder();
+ foreach (var group in itemGroups)
+ {
+ sb.Append(group.ToText());
+ }
+ return sb.ToString().Trim();
+ }
+
+ ///
+ /// Splits a CSS value string on top-level whitespace (paren-depth-aware, so a calc()/gradient()
+ /// value's internal spaces aren't mistaken for a delimiter). Used to split a "border-radius"
+ /// corner value like "10px 5%" into its horizontal/vertical components.
+ ///
+ internal static IEnumerable SplitTopLevelWhitespace(string value)
+ {
+ int depth = 0, start = 0;
+ for (int i = 0; i < value.Length; i++)
+ {
+ char c = value[i];
+ if (c == '(') depth++;
+ else if (c == ')') depth--;
+ else if (char.IsWhiteSpace(c) && depth == 0)
+ {
+ if (i > start) yield return value.Substring(start, i - start);
+ start = i + 1;
+ }
+ }
+ if (start < value.Length) yield return value.Substring(start);
+ }
+
+ #endregion
}
}
\ No newline at end of file
diff --git a/Source/HtmlRenderer/Core/Utils/CssUtils.cs b/Source/HtmlRenderer/Core/Utils/CssUtils.cs
index a6b5deebc..79f8402ca 100644
--- a/Source/HtmlRenderer/Core/Utils/CssUtils.cs
+++ b/Source/HtmlRenderer/Core/Utils/CssUtils.cs
@@ -44,12 +44,11 @@ internal static class CssUtils
"border-bottom-style", "border-left-style", "border-right-style", "border-top-style",
"border-bottom-color", "border-left-color", "border-right-color", "border-top-color",
"border-spacing", "border-collapse",
- "corner-radius", "corner-nw-radius", "corner-ne-radius", "corner-se-radius", "corner-sw-radius",
+ "border-top-left-radius", "border-top-right-radius", "border-bottom-right-radius", "border-bottom-left-radius",
"margin-bottom", "margin-left", "margin-right", "margin-top",
"padding-bottom", "padding-left", "padding-right", "padding-top",
"page-break-inside", "left", "top", "width", "max-width", "height",
"background-color", "background-image", "background-position", "background-repeat",
- "background-gradient", "background-gradient-angle",
"content", "color", "display", "direction", "empty-cells", "float", "position",
"line-height", "vertical-align", "text-indent", "text-align", "text-decoration",
"white-space", "word-break", "visibility", "word-spacing",
@@ -124,16 +123,14 @@ public static string GetPropertyValue(CssBox cssBox, string propName)
return cssBox.BorderSpacing;
case "border-collapse":
return cssBox.BorderCollapse;
- case "corner-radius":
- return cssBox.CornerRadius;
- case "corner-nw-radius":
- return cssBox.CornerNwRadius;
- case "corner-ne-radius":
- return cssBox.CornerNeRadius;
- case "corner-se-radius":
- return cssBox.CornerSeRadius;
- case "corner-sw-radius":
- return cssBox.CornerSwRadius;
+ case "border-top-left-radius":
+ return cssBox.BorderTopLeftRadius;
+ case "border-top-right-radius":
+ return cssBox.BorderTopRightRadius;
+ case "border-bottom-right-radius":
+ return cssBox.BorderBottomRightRadius;
+ case "border-bottom-left-radius":
+ return cssBox.BorderBottomLeftRadius;
case "margin-bottom":
return cssBox.MarginBottom;
case "margin-left":
@@ -170,10 +167,6 @@ public static string GetPropertyValue(CssBox cssBox, string propName)
return cssBox.BackgroundPosition;
case "background-repeat":
return cssBox.BackgroundRepeat;
- case "background-gradient":
- return cssBox.BackgroundGradient;
- case "background-gradient-angle":
- return cssBox.BackgroundGradientAngle;
case "content":
return cssBox.Content;
case "color":
@@ -283,20 +276,20 @@ public static void SetPropertyValue(CssBox cssBox, string propName, string value
case "border-collapse":
cssBox.BorderCollapse = value;
break;
- case "corner-radius":
- cssBox.CornerRadius = value;
+ case "border-radius":
+ cssBox.BorderRadius = value;
break;
- case "corner-nw-radius":
- cssBox.CornerNwRadius = value;
+ case "border-top-left-radius":
+ cssBox.BorderTopLeftRadius = value;
break;
- case "corner-ne-radius":
- cssBox.CornerNeRadius = value;
+ case "border-top-right-radius":
+ cssBox.BorderTopRightRadius = value;
break;
- case "corner-se-radius":
- cssBox.CornerSeRadius = value;
+ case "border-bottom-right-radius":
+ cssBox.BorderBottomRightRadius = value;
break;
- case "corner-sw-radius":
- cssBox.CornerSwRadius = value;
+ case "border-bottom-left-radius":
+ cssBox.BorderBottomLeftRadius = value;
break;
case "margin-bottom":
cssBox.MarginBottom = value;
@@ -352,12 +345,6 @@ public static void SetPropertyValue(CssBox cssBox, string propName, string value
case "background-repeat":
cssBox.BackgroundRepeat = value;
break;
- case "background-gradient":
- cssBox.BackgroundGradient = value;
- break;
- case "background-gradient-angle":
- cssBox.BackgroundGradientAngle = value;
- break;
case "color":
cssBox.Color = value;
break;
diff --git a/Source/HtmlRenderer/Core/Utils/RenderUtils.cs b/Source/HtmlRenderer/Core/Utils/RenderUtils.cs
index 2726b5fb2..091c75191 100644
--- a/Source/HtmlRenderer/Core/Utils/RenderUtils.cs
+++ b/Source/HtmlRenderer/Core/Utils/RenderUtils.cs
@@ -95,44 +95,42 @@ public static void DrawImageErrorIcon(RGraphics g, HtmlContainerInt htmlContaine
}
///
- /// Creates a rounded rectangle using the specified corner radius
- /// NW-----NE
+ /// Creates a rounded rectangle path. Each corner has independent horizontal (X) and vertical (Y)
+ /// radii, supporting elliptical corners per the CSS border-radius spec.
+ /// TL-----TR
/// | |
/// | |
- /// SW-----SE
+ /// BL-----BR
///
/// the device to draw into
/// Rectangle to round
- /// Radius of the north east corner
- /// Radius of the north west corner
- /// Radius of the south east corner
- /// Radius of the south west corner
/// GraphicsPath with the lines of the rounded rectangle ready to be painted
- public static RGraphicsPath GetRoundRect(RGraphics g, RRect rect, double nwRadius, double neRadius, double seRadius, double swRadius)
+ public static RGraphicsPath GetRoundRect(RGraphics g, RRect rect,
+ double tlX, double tlY, double trX, double trY,
+ double brX, double brY, double blX, double blY)
{
var path = g.GetGraphicsPath();
- path.Start(rect.Left + nwRadius, rect.Top);
-
- path.LineTo(rect.Right - neRadius, rect.Y);
-
- if (neRadius > 0f)
- path.ArcTo(rect.Right, rect.Top + neRadius, neRadius, RGraphicsPath.Corner.TopRight);
-
- path.LineTo(rect.Right, rect.Bottom - seRadius);
-
- if (seRadius > 0f)
- path.ArcTo(rect.Right - seRadius, rect.Bottom, seRadius, RGraphicsPath.Corner.BottomRight);
-
- path.LineTo(rect.Left + swRadius, rect.Bottom);
-
- if (swRadius > 0f)
- path.ArcTo(rect.Left, rect.Bottom - swRadius, swRadius, RGraphicsPath.Corner.BottomLeft);
-
- path.LineTo(rect.Left, rect.Top + nwRadius);
-
- if (nwRadius > 0f)
- path.ArcTo(rect.Left + nwRadius, rect.Top, nwRadius, RGraphicsPath.Corner.TopLeft);
+ // Top edge: start after TL corner, end before TR corner.
+ path.Start(rect.Left + tlX, rect.Top);
+ path.LineTo(rect.Right - trX, rect.Top);
+ if (trX > 0 || trY > 0)
+ path.ArcTo(rect.Right, rect.Top + trY, trX, trY, RGraphicsPath.Corner.TopRight);
+
+ // Right edge.
+ path.LineTo(rect.Right, rect.Bottom - brY);
+ if (brX > 0 || brY > 0)
+ path.ArcTo(rect.Right - brX, rect.Bottom, brX, brY, RGraphicsPath.Corner.BottomRight);
+
+ // Bottom edge.
+ path.LineTo(rect.Left + blX, rect.Bottom);
+ if (blX > 0 || blY > 0)
+ path.ArcTo(rect.Left, rect.Bottom - blY, blX, blY, RGraphicsPath.Corner.BottomLeft);
+
+ // Left edge.
+ path.LineTo(rect.Left, rect.Top + tlY);
+ if (tlX > 0 || tlY > 0)
+ path.ArcTo(rect.Left + tlX, rect.Top, tlX, tlY, RGraphicsPath.Corner.TopLeft);
return path;
}
From bc3884e42511400b52226b05e124c19b34ad3dae Mon Sep 17 00:00:00 2001
From: Justin Haygood
Date: Thu, 16 Jul 2026 21:39:17 -0400
Subject: [PATCH 4/4] Fixes
Fixes
---
.../HtmlRenderer/Core/Dom/CssContentEngine.cs | 96 +++++++------------
.../HtmlRenderer/Core/Parse/CssValueParser.cs | 2 +-
2 files changed, 33 insertions(+), 65 deletions(-)
diff --git a/Source/HtmlRenderer/Core/Dom/CssContentEngine.cs b/Source/HtmlRenderer/Core/Dom/CssContentEngine.cs
index 9ddb80fe0..e3d4b55e3 100644
--- a/Source/HtmlRenderer/Core/Dom/CssContentEngine.cs
+++ b/Source/HtmlRenderer/Core/Dom/CssContentEngine.cs
@@ -10,7 +10,11 @@
// - Sun Tsu,
// "The Art of War"
+using System;
+using System.Linq;
using System.Text;
+using TheArtOfDev.HtmlRenderer.Core.CssEngine;
+using TheArtOfDev.HtmlRenderer.Core.Parse;
using TheArtOfDev.HtmlRenderer.Core.Utils;
namespace TheArtOfDev.HtmlRenderer.Core.Dom
@@ -23,7 +27,12 @@ namespace TheArtOfDev.HtmlRenderer.Core.Dom
/// attr(), and the four quote keywords are supported; counter()/counters()/
/// string()/content() are not (CSS counters and named strings remain out of scope
/// for this backport - an unrecognized component is simply skipped, the same graceful-ignore
- /// behavior this engine already applies to unsupported CSS elsewhere).
+ /// behavior this engine already applies to unsupported CSS elsewhere).
+ /// Tokenizes via the shared CSS lexer () rather than
+ /// hand-parsing the CSS text, so string literals (e.g. "\A", the standard UA-stylesheet
+ /// line-break trick used by br:before) get the same correct escape decoding the lexer
+ /// already applies when the stylesheet is first parsed - see CalcParser/BackgroundPositionGrammar
+ /// for the same "don't write two independent parsers for the same grammar" precedent.
///
internal static class CssContentEngine
{
@@ -50,82 +59,41 @@ public static void ApplyContent(CssBox box)
private static string Resolve(CssBox box, string content)
{
var sb = new StringBuilder();
- var i = 0;
- while (i < content.Length)
+
+ foreach (var token in CssValueParser.GetCssTokens(content))
{
- if (char.IsWhiteSpace(content[i]))
+ if (token is StringToken stringToken)
{
- i++;
- continue;
+ sb.Append(stringToken.Data);
}
-
- if (content[i] == '"' || content[i] == '\'')
+ else if (token is FunctionToken functionToken &&
+ functionToken.Data.Equals(FunctionNames.Attr, StringComparison.OrdinalIgnoreCase))
{
- var quote = content[i];
- var start = i + 1;
- var j = start;
- while (j < content.Length && content[j] != quote)
+ if (functionToken.ArgumentTokens.FirstOrDefault() is KeywordToken attrNameToken)
{
- if (content[j] == '\\' && j + 1 < content.Length)
- j++;
- j++;
+ var sourceBox = box.IsPseudoElement && box.ParentBox != null ? box.ParentBox : box;
+ var attrValue = sourceBox.GetAttribute(attrNameToken.Data, string.Empty);
+ sb.Append(attrValue);
}
- sb.Append(Unescape(content.Substring(start, j - start)));
- i = j + 1;
- continue;
}
-
- var tokenEnd = i;
- while (tokenEnd < content.Length && !char.IsWhiteSpace(content[tokenEnd]))
- tokenEnd++;
- var token = content.Substring(i, tokenEnd - i);
- i = tokenEnd;
-
- if (token.StartsWith("attr(", System.StringComparison.OrdinalIgnoreCase) && token.EndsWith(")"))
+ else if (token is KeywordToken keywordToken)
{
- var attrName = token.Substring(5, token.Length - 6).Trim();
- var sourceBox = box.IsPseudoElement && box.ParentBox != null ? box.ParentBox : box;
- var attrValue = sourceBox.GetAttribute(attrName, string.Empty);
- sb.Append(attrValue);
- }
- else if (token.Equals("open-quote", System.StringComparison.OrdinalIgnoreCase))
- {
- sb.Append('“');
- }
- else if (token.Equals("close-quote", System.StringComparison.OrdinalIgnoreCase))
- {
- sb.Append('”');
- }
- else if (token.Equals("no-open-quote", System.StringComparison.OrdinalIgnoreCase) ||
- token.Equals("no-close-quote", System.StringComparison.OrdinalIgnoreCase))
- {
- // produces no text, but still counts as a quoting depth change per spec - depth
- // tracking isn't implemented here (no "quotes" property support), so this is a no-op.
+ if (keywordToken.Data.Equals(Keywords.OpenQuote, StringComparison.OrdinalIgnoreCase))
+ {
+ sb.Append('“');
+ }
+ else if (keywordToken.Data.Equals(Keywords.CloseQuote, StringComparison.OrdinalIgnoreCase))
+ {
+ sb.Append('”');
+ }
+ // no-open-quote/no-close-quote produce no text, but still count as a quoting depth
+ // change per spec - depth tracking isn't implemented here (no "quotes" property
+ // support), so they (like any other unrecognized keyword) are a no-op.
}
// counter()/counters()/string()/content() and anything else unrecognized: skip.
}
return sb.ToString();
}
-
- private static string Unescape(string s)
- {
- if (s.IndexOf('\\') < 0) return s;
-
- var sb = new StringBuilder(s.Length);
- for (var i = 0; i < s.Length; i++)
- {
- if (s[i] == '\\' && i + 1 < s.Length)
- {
- i++;
- sb.Append(s[i]);
- }
- else
- {
- sb.Append(s[i]);
- }
- }
- return sb.ToString();
- }
}
}
diff --git a/Source/HtmlRenderer/Core/Parse/CssValueParser.cs b/Source/HtmlRenderer/Core/Parse/CssValueParser.cs
index 5efeb06c3..1a7205f5e 100644
--- a/Source/HtmlRenderer/Core/Parse/CssValueParser.cs
+++ b/Source/HtmlRenderer/Core/Parse/CssValueParser.cs
@@ -126,7 +126,7 @@ public static bool IsValidLength(string value)
/// skipping whitespace/end-of-file tokens - the same "just enough tokenization to recognize a
/// single top-level function call" approach used to detect calc()-family expressions.
///
- private static List GetCssTokens(string value)
+ internal static List GetCssTokens(string value)
{
var lexer = new Lexer(value);
var tokens = new List();