From 3cce5133772bc0eac54bde260e946f5907ed024a Mon Sep 17 00:00:00 2001 From: danfickle Date: Fri, 12 Feb 2021 21:10:06 +1100 Subject: [PATCH 01/12] #649 Move background property builders to own file. So they can easily be edited to all allow multiple values. --- .../openhtmltopdf/css/constants/CSSName.java | 13 +- .../PrimitiveBackgroundPropertyBuilders.java | 263 ++++++++++++++++++ .../property/PrimitivePropertyBuilders.java | 254 +---------------- 3 files changed, 275 insertions(+), 255 deletions(-) create mode 100644 openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/PrimitiveBackgroundPropertyBuilders.java diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/constants/CSSName.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/constants/CSSName.java index 2fa7ea6a5..af6b1c0f4 100644 --- a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/constants/CSSName.java +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/constants/CSSName.java @@ -29,6 +29,7 @@ import com.openhtmltopdf.css.parser.CSSParser; import com.openhtmltopdf.css.parser.PropertyValue; import com.openhtmltopdf.css.parser.property.BackgroundPropertyBuilder; +import com.openhtmltopdf.css.parser.property.PrimitiveBackgroundPropertyBuilders; import com.openhtmltopdf.css.parser.property.BorderPropertyBuilders; import com.openhtmltopdf.css.parser.property.BorderSpacingPropertyBuilder; import com.openhtmltopdf.css.parser.property.ContentPropertyBuilder; @@ -146,7 +147,7 @@ public final class CSSName implements Comparable { PRIMITIVE, "transparent", NOT_INHERITED, - new PrimitivePropertyBuilders.BackgroundColor() + new PrimitiveBackgroundPropertyBuilders.BackgroundColor() ); /** @@ -158,7 +159,7 @@ public final class CSSName implements Comparable { PRIMITIVE, "none", NOT_INHERITED, - new PrimitivePropertyBuilders.BackgroundImage() + new PrimitiveBackgroundPropertyBuilders.BackgroundImage() ); /** @@ -170,7 +171,7 @@ public final class CSSName implements Comparable { PRIMITIVE, "repeat", NOT_INHERITED, - new PrimitivePropertyBuilders.BackgroundRepeat() + new PrimitiveBackgroundPropertyBuilders.BackgroundRepeat() ); /** @@ -182,7 +183,7 @@ public final class CSSName implements Comparable { PRIMITIVE, "scroll", NOT_INHERITED, - new PrimitivePropertyBuilders.BackgroundAttachment() + new PrimitiveBackgroundPropertyBuilders.BackgroundAttachment() ); /** @@ -194,7 +195,7 @@ public final class CSSName implements Comparable { PRIMITIVE, "0% 0%", NOT_INHERITED, - new PrimitivePropertyBuilders.BackgroundPosition() + new PrimitiveBackgroundPropertyBuilders.BackgroundPosition() ); public final static CSSName BACKGROUND_SIZE = @@ -203,7 +204,7 @@ public final class CSSName implements Comparable { PRIMITIVE, "auto auto", NOT_INHERITED, - new PrimitivePropertyBuilders.BackgroundSize() + new PrimitiveBackgroundPropertyBuilders.BackgroundSize() ); /** diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/PrimitiveBackgroundPropertyBuilders.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/PrimitiveBackgroundPropertyBuilders.java new file mode 100644 index 000000000..0b5461004 --- /dev/null +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/PrimitiveBackgroundPropertyBuilders.java @@ -0,0 +1,263 @@ +package com.openhtmltopdf.css.parser.property; + +import java.util.ArrayList; +import java.util.BitSet; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import com.openhtmltopdf.css.constants.CSSName; +import com.openhtmltopdf.css.constants.IdentValue; +import com.openhtmltopdf.css.parser.CSSParseException; +import com.openhtmltopdf.css.parser.CSSPrimitiveValue; +import com.openhtmltopdf.css.parser.CSSValue; +import com.openhtmltopdf.css.parser.PropertyValue; +import com.openhtmltopdf.css.parser.property.PrimitivePropertyBuilders.GenericColor; +import com.openhtmltopdf.css.parser.property.PrimitivePropertyBuilders.GenericURIWithNone; +import com.openhtmltopdf.css.parser.property.PrimitivePropertyBuilders.SingleIdent; +import com.openhtmltopdf.css.sheet.PropertyDeclaration; + +public class PrimitiveBackgroundPropertyBuilders { + public static class BackgroundImage extends GenericURIWithNone { + @Override + public List buildDeclarations( + CSSName cssName, List values, int origin, + boolean important, boolean inheritAllowed) { + + checkValueCount(cssName, 1, values.size()); + PropertyValue value = values.get(0); + + if (value.getPropertyValueType() == PropertyValue.VALUE_TYPE_FUNCTION && + Objects.equals(value.getFunction().getName(), "linear-gradient")) { + // TODO: Validation of linear-gradient args. + return Collections.singletonList( + new PropertyDeclaration(cssName, value, important, origin)); + } else { + return super.buildDeclarations(cssName, values, origin, important, inheritAllowed); + } + } + } + + public static class BackgroundColor extends GenericColor { + } + + public static class BackgroundSize extends AbstractPropertyBuilder { + private static final BitSet ALL_ALLOWED = PrimitivePropertyBuilders.setFor(new IdentValue[] { + IdentValue.AUTO, IdentValue.CONTAIN, IdentValue.COVER + }); + + @Override + public List buildDeclarations(CSSName cssName, List values, int origin, boolean important, boolean inheritAllowed) { + checkValueCount(cssName, 1, 2, values.size()); + + PropertyValue first = values.get(0); + PropertyValue second = null; + if (values.size() == 2) { + second = values.get(1); + } + + checkInheritAllowed(first, inheritAllowed); + if (values.size() == 1 && + first.getCssValueType() == CSSValue.CSS_INHERIT) { + return Collections.singletonList( + new PropertyDeclaration(cssName, first, important, origin)); + } + + if (second != null) { + checkInheritAllowed(second, false); + } + + checkIdentLengthOrPercentType(cssName, first); + if (second == null) { + if (first.getPrimitiveType() == CSSPrimitiveValue.CSS_IDENT) { + IdentValue firstIdent = checkIdent(cssName, first); + checkValidity(cssName, ALL_ALLOWED, firstIdent); + + if (firstIdent == IdentValue.CONTAIN || firstIdent == IdentValue.COVER) { + return Collections.singletonList( + new PropertyDeclaration(cssName, first, important, origin)); + } else { + return PrimitivePropertyBuilders.createTwoValueResponse(CSSName.BACKGROUND_SIZE, first, first, origin, important); + } + } else { + return PrimitivePropertyBuilders.createTwoValueResponse(CSSName.BACKGROUND_SIZE, first, new PropertyValue(IdentValue.AUTO), origin, important); + } + } else { + checkIdentLengthOrPercentType(cssName, second); + + if (first.getPrimitiveType() == CSSPrimitiveValue.CSS_IDENT) { + IdentValue firstIdent = checkIdent(cssName, first); + if (firstIdent != IdentValue.AUTO) { + throw new CSSParseException("The only ident value allowed here is 'auto'", -1); + } + } else if (first.getFloatValue() < 0.0f) { + throw new CSSParseException(cssName + " values cannot be negative", -1); + } + + if (second.getPrimitiveType() == CSSPrimitiveValue.CSS_IDENT) { + IdentValue secondIdent = checkIdent(cssName, second); + if (secondIdent != IdentValue.AUTO) { + throw new CSSParseException("The only ident value allowed here is 'auto'", -1); + } + } else if (second.getFloatValue() < 0.0f) { + throw new CSSParseException(cssName + " values cannot be negative", -1); + } + + return PrimitivePropertyBuilders.createTwoValueResponse(CSSName.BACKGROUND_SIZE, first, second, origin, important); + } + } + + } + + public static class BackgroundPosition extends AbstractPropertyBuilder { + @Override + public List buildDeclarations( + CSSName cssName, List values, int origin, boolean important, boolean inheritAllowed) { + checkValueCount(cssName, 1, 2, values.size()); + + PropertyValue first = values.get(0); + PropertyValue second = null; + if (values.size() == 2) { + second = values.get(1); + } + + checkInheritAllowed(first, inheritAllowed); + if (values.size() == 1 && + first.getCssValueType() == CSSValue.CSS_INHERIT) { + return Collections.singletonList( + new PropertyDeclaration(cssName, first, important, origin)); + } + + if (second != null) { + checkInheritAllowed(second, false); + } + + checkIdentLengthOrPercentType(cssName, first); + if (second == null) { + if (isLength(first) || first.getPrimitiveType() == CSSPrimitiveValue.CSS_PERCENTAGE) { + List responseValues = new ArrayList<>(2); + responseValues.add(first); + responseValues.add(new PropertyValue( + CSSPrimitiveValue.CSS_PERCENTAGE, 50.0f, "50%")); + return Collections.singletonList(new PropertyDeclaration( + CSSName.BACKGROUND_POSITION, + new PropertyValue(responseValues), important, origin)); + } + } else { + checkIdentLengthOrPercentType(cssName, second); + } + + + IdentValue firstIdent = null; + if (first.getPrimitiveType() == CSSPrimitiveValue.CSS_IDENT) { + firstIdent = checkIdent(cssName, first); + checkValidity(cssName, getAllowed(), firstIdent); + } + + IdentValue secondIdent = null; + if (second == null) { + secondIdent = IdentValue.CENTER; + } else if (second.getPrimitiveType() == CSSPrimitiveValue.CSS_IDENT) { + secondIdent = checkIdent(cssName, second); + checkValidity(cssName, getAllowed(), secondIdent); + } + + if (firstIdent == null && secondIdent == null) { + return Collections.singletonList(new PropertyDeclaration( + CSSName.BACKGROUND_POSITION, new PropertyValue(values), important, origin)); + } else if (firstIdent != null && secondIdent != null) { + if (firstIdent == IdentValue.TOP || firstIdent == IdentValue.BOTTOM || + secondIdent == IdentValue.LEFT || secondIdent == IdentValue.RIGHT) { + IdentValue temp = firstIdent; + firstIdent = secondIdent; + secondIdent = temp; + } + + checkIdentPosition(cssName, firstIdent, secondIdent); + + return createTwoPercentValueResponse( + getPercentForIdent(firstIdent), + getPercentForIdent(secondIdent), + important, + origin); + } else { + checkIdentPosition(cssName, firstIdent, secondIdent); + + List responseValues = new ArrayList<>(2); + + if (firstIdent == null) { + responseValues.add(first); + responseValues.add(createValueForIdent(secondIdent)); + } else { + responseValues.add(createValueForIdent(firstIdent)); + responseValues.add(second); + } + + return Collections.singletonList(new PropertyDeclaration( + CSSName.BACKGROUND_POSITION, + new PropertyValue(responseValues), important, origin)); + } + } + + private void checkIdentPosition(CSSName cssName, IdentValue firstIdent, IdentValue secondIdent) { + if (firstIdent == IdentValue.TOP || firstIdent == IdentValue.BOTTOM || + secondIdent == IdentValue.LEFT || secondIdent == IdentValue.RIGHT) { + throw new CSSParseException("Invalid combination of keywords in " + cssName, -1); + } + } + + private float getPercentForIdent(IdentValue ident) { + float percent = 0.0f; + + if (ident == IdentValue.CENTER) { + percent = 50.f; + } else if (ident == IdentValue.BOTTOM || ident == IdentValue.RIGHT) { + percent = 100.0f; + } + + return percent; + } + + private PropertyValue createValueForIdent(IdentValue ident) { + float percent = getPercentForIdent(ident); + return new PropertyValue( + CSSPrimitiveValue.CSS_PERCENTAGE, percent, percent + "%"); + } + + private List createTwoPercentValueResponse( + float percent1, float percent2, boolean important, int origin) { + PropertyValue value1 = new PropertyValue( + CSSPrimitiveValue.CSS_PERCENTAGE, percent1, percent1 + "%"); + PropertyValue value2 = new PropertyValue( + CSSPrimitiveValue.CSS_PERCENTAGE, percent2, percent2 + "%"); + + List values = new ArrayList<>(2); + values.add(value1); + values.add(value2); + + PropertyDeclaration result = new PropertyDeclaration( + CSSName.BACKGROUND_POSITION, + new PropertyValue(values), important, origin); + + return Collections.singletonList(result); + } + + private BitSet getAllowed() { + return PrimitivePropertyBuilders.BACKGROUND_POSITIONS; + } + } + + public static class BackgroundRepeat extends SingleIdent { + @Override + protected BitSet getAllowed() { + return PrimitivePropertyBuilders.BACKGROUND_REPEATS; + } + } + + public static class BackgroundAttachment extends SingleIdent { + @Override + protected BitSet getAllowed() { + return PrimitivePropertyBuilders.BACKGROUND_ATTACHMENTS; + } + } +} diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/PrimitivePropertyBuilders.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/PrimitivePropertyBuilders.java index 4569acc58..380595e87 100644 --- a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/PrimitivePropertyBuilders.java +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/PrimitivePropertyBuilders.java @@ -111,7 +111,7 @@ public class PrimitivePropertyBuilders { public static final PropertyBuilder MARGIN = new LengthLikeWithAuto(); public static final PropertyBuilder PADDING = new NonNegativeLengthLike(); - private static BitSet setFor(IdentValue[] values) { + static BitSet setFor(IdentValue[] values) { BitSet result = new BitSet(IdentValue.getIdentCount()); for (int i = 0; i < values.length; i++) { IdentValue ident = values[i]; @@ -120,7 +120,7 @@ private static BitSet setFor(IdentValue[] values) { return result; } - private static abstract class SingleIdent extends AbstractPropertyBuilder { + static abstract class SingleIdent extends AbstractPropertyBuilder { protected abstract BitSet getAllowed(); @Override @@ -142,7 +142,7 @@ public List buildDeclarations( } } - private static class GenericColor extends AbstractPropertyBuilder { + static class GenericColor extends AbstractPropertyBuilder { private static final BitSet ALLOWED = setFor( new IdentValue[] { IdentValue.TRANSPARENT }); @@ -495,7 +495,7 @@ protected BitSet getAllowed() { } } - private static class GenericURIWithNone extends AbstractPropertyBuilder { + static class GenericURIWithNone extends AbstractPropertyBuilder { // | none | inherit private static final BitSet ALLOWED = setFor(new IdentValue[] { IdentValue.NONE }); @@ -518,250 +518,6 @@ public List buildDeclarations( } } - public static class BackgroundAttachment extends SingleIdent { - @Override - protected BitSet getAllowed() { - return BACKGROUND_ATTACHMENTS; - } - } - - public static class BackgroundColor extends GenericColor { - } - - public static class BackgroundImage extends GenericURIWithNone { - @Override - public List buildDeclarations( - CSSName cssName, List values, int origin, - boolean important, boolean inheritAllowed) { - - checkValueCount(cssName, 1, values.size()); - PropertyValue value = values.get(0); - - if (value.getPropertyValueType() == PropertyValue.VALUE_TYPE_FUNCTION && - Objects.equals(value.getFunction().getName(), "linear-gradient")) { - // TODO: Validation of linear-gradient args. - return Collections.singletonList( - new PropertyDeclaration(cssName, value, important, origin)); - } else { - return super.buildDeclarations(cssName, values, origin, important, inheritAllowed); - } - } - - } - - public static class BackgroundSize extends AbstractPropertyBuilder { - private static final BitSet ALL_ALLOWED = setFor(new IdentValue[] { - IdentValue.AUTO, IdentValue.CONTAIN, IdentValue.COVER - }); - - @Override - public List buildDeclarations(CSSName cssName, List values, int origin, boolean important, boolean inheritAllowed) { - checkValueCount(cssName, 1, 2, values.size()); - - PropertyValue first = values.get(0); - PropertyValue second = null; - if (values.size() == 2) { - second = values.get(1); - } - - checkInheritAllowed(first, inheritAllowed); - if (values.size() == 1 && - first.getCssValueType() == CSSValue.CSS_INHERIT) { - return Collections.singletonList( - new PropertyDeclaration(cssName, first, important, origin)); - } - - if (second != null) { - checkInheritAllowed(second, false); - } - - checkIdentLengthOrPercentType(cssName, first); - if (second == null) { - if (first.getPrimitiveType() == CSSPrimitiveValue.CSS_IDENT) { - IdentValue firstIdent = checkIdent(cssName, first); - checkValidity(cssName, ALL_ALLOWED, firstIdent); - - if (firstIdent == IdentValue.CONTAIN || firstIdent == IdentValue.COVER) { - return Collections.singletonList( - new PropertyDeclaration(cssName, first, important, origin)); - } else { - return createTwoValueResponse(CSSName.BACKGROUND_SIZE, first, first, origin, important); - } - } else { - return createTwoValueResponse(CSSName.BACKGROUND_SIZE, first, new PropertyValue(IdentValue.AUTO), origin, important); - } - } else { - checkIdentLengthOrPercentType(cssName, second); - - if (first.getPrimitiveType() == CSSPrimitiveValue.CSS_IDENT) { - IdentValue firstIdent = checkIdent(cssName, first); - if (firstIdent != IdentValue.AUTO) { - throw new CSSParseException("The only ident value allowed here is 'auto'", -1); - } - } else if (first.getFloatValue() < 0.0f) { - throw new CSSParseException(cssName + " values cannot be negative", -1); - } - - if (second.getPrimitiveType() == CSSPrimitiveValue.CSS_IDENT) { - IdentValue secondIdent = checkIdent(cssName, second); - if (secondIdent != IdentValue.AUTO) { - throw new CSSParseException("The only ident value allowed here is 'auto'", -1); - } - } else if (second.getFloatValue() < 0.0f) { - throw new CSSParseException(cssName + " values cannot be negative", -1); - } - - return createTwoValueResponse(CSSName.BACKGROUND_SIZE, first, second, origin, important); - } - } - - } - - public static class BackgroundPosition extends AbstractPropertyBuilder { - @Override - public List buildDeclarations( - CSSName cssName, List values, int origin, boolean important, boolean inheritAllowed) { - checkValueCount(cssName, 1, 2, values.size()); - - PropertyValue first = values.get(0); - PropertyValue second = null; - if (values.size() == 2) { - second = values.get(1); - } - - checkInheritAllowed(first, inheritAllowed); - if (values.size() == 1 && - first.getCssValueType() == CSSValue.CSS_INHERIT) { - return Collections.singletonList( - new PropertyDeclaration(cssName, first, important, origin)); - } - - if (second != null) { - checkInheritAllowed(second, false); - } - - checkIdentLengthOrPercentType(cssName, first); - if (second == null) { - if (isLength(first) || first.getPrimitiveType() == CSSPrimitiveValue.CSS_PERCENTAGE) { - List responseValues = new ArrayList<>(2); - responseValues.add(first); - responseValues.add(new PropertyValue( - CSSPrimitiveValue.CSS_PERCENTAGE, 50.0f, "50%")); - return Collections.singletonList(new PropertyDeclaration( - CSSName.BACKGROUND_POSITION, - new PropertyValue(responseValues), important, origin)); - } - } else { - checkIdentLengthOrPercentType(cssName, second); - } - - - IdentValue firstIdent = null; - if (first.getPrimitiveType() == CSSPrimitiveValue.CSS_IDENT) { - firstIdent = checkIdent(cssName, first); - checkValidity(cssName, getAllowed(), firstIdent); - } - - IdentValue secondIdent = null; - if (second == null) { - secondIdent = IdentValue.CENTER; - } else if (second.getPrimitiveType() == CSSPrimitiveValue.CSS_IDENT) { - secondIdent = checkIdent(cssName, second); - checkValidity(cssName, getAllowed(), secondIdent); - } - - if (firstIdent == null && secondIdent == null) { - return Collections.singletonList(new PropertyDeclaration( - CSSName.BACKGROUND_POSITION, new PropertyValue(values), important, origin)); - } else if (firstIdent != null && secondIdent != null) { - if (firstIdent == IdentValue.TOP || firstIdent == IdentValue.BOTTOM || - secondIdent == IdentValue.LEFT || secondIdent == IdentValue.RIGHT) { - IdentValue temp = firstIdent; - firstIdent = secondIdent; - secondIdent = temp; - } - - checkIdentPosition(cssName, firstIdent, secondIdent); - - return createTwoPercentValueResponse( - getPercentForIdent(firstIdent), - getPercentForIdent(secondIdent), - important, - origin); - } else { - checkIdentPosition(cssName, firstIdent, secondIdent); - - List responseValues = new ArrayList<>(2); - - if (firstIdent == null) { - responseValues.add(first); - responseValues.add(createValueForIdent(secondIdent)); - } else { - responseValues.add(createValueForIdent(firstIdent)); - responseValues.add(second); - } - - return Collections.singletonList(new PropertyDeclaration( - CSSName.BACKGROUND_POSITION, - new PropertyValue(responseValues), important, origin)); - } - } - - private void checkIdentPosition(CSSName cssName, IdentValue firstIdent, IdentValue secondIdent) { - if (firstIdent == IdentValue.TOP || firstIdent == IdentValue.BOTTOM || - secondIdent == IdentValue.LEFT || secondIdent == IdentValue.RIGHT) { - throw new CSSParseException("Invalid combination of keywords in " + cssName, -1); - } - } - - private float getPercentForIdent(IdentValue ident) { - float percent = 0.0f; - - if (ident == IdentValue.CENTER) { - percent = 50.f; - } else if (ident == IdentValue.BOTTOM || ident == IdentValue.RIGHT) { - percent = 100.0f; - } - - return percent; - } - - private PropertyValue createValueForIdent(IdentValue ident) { - float percent = getPercentForIdent(ident); - return new PropertyValue( - CSSPrimitiveValue.CSS_PERCENTAGE, percent, percent + "%"); - } - - private List createTwoPercentValueResponse( - float percent1, float percent2, boolean important, int origin) { - PropertyValue value1 = new PropertyValue( - CSSPrimitiveValue.CSS_PERCENTAGE, percent1, percent1 + "%"); - PropertyValue value2 = new PropertyValue( - CSSPrimitiveValue.CSS_PERCENTAGE, percent2, percent2 + "%"); - - List values = new ArrayList<>(2); - values.add(value1); - values.add(value2); - - PropertyDeclaration result = new PropertyDeclaration( - CSSName.BACKGROUND_POSITION, - new PropertyValue(values), important, origin); - - return Collections.singletonList(result); - } - - private BitSet getAllowed() { - return BACKGROUND_POSITIONS; - } - } - - public static class BackgroundRepeat extends SingleIdent { - @Override - protected BitSet getAllowed() { - return BACKGROUND_REPEATS; - } - } - public static class BorderCollapse extends SingleIdent { // collapse | separate | inherit private static final BitSet ALLOWED = setFor( @@ -1768,7 +1524,7 @@ protected BitSet getAllowed() { } - private static List createTwoValueResponse(CSSName cssName, PropertyValue value1, PropertyValue value2, + static List createTwoValueResponse(CSSName cssName, PropertyValue value1, PropertyValue value2, int origin, boolean important) { List values = new ArrayList<>(2); values.add(value1); From 54e5e1b7f18f8cd45bd835b43444269bdac4fb54 Mon Sep 17 00:00:00 2001 From: danfickle Date: Fri, 12 Feb 2021 23:17:08 +1100 Subject: [PATCH 02/12] #649 Allow multiple values in background-image prop + Needs tests. + Needs other background propery support such as position and size. --- .../property/AbstractPropertyBuilder.java | 4 + .../PrimitiveBackgroundPropertyBuilders.java | 52 ++++- .../css/style/CalculatedStyle.java | 39 ++-- .../render/AbstractOutputDevice.java | 197 ++++++++++-------- 4 files changed, 190 insertions(+), 102 deletions(-) diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/AbstractPropertyBuilder.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/AbstractPropertyBuilder.java index 88fcc6d4b..86dd25428 100644 --- a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/AbstractPropertyBuilder.java +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/AbstractPropertyBuilder.java @@ -196,6 +196,10 @@ protected void checkInheritAllowed(CSSPrimitiveValue value, boolean inheritAllow } } + protected void checkForbidInherit(CSSPrimitiveValue value) { + checkInheritAllowed(value, false); + } + protected List checkInheritAll(CSSName[] all, List values, int origin, boolean important, boolean inheritAllowed) { if (values.size() == 1) { CSSPrimitiveValue value = values.get(0); diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/PrimitiveBackgroundPropertyBuilders.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/PrimitiveBackgroundPropertyBuilders.java index 0b5461004..b8d69dfc6 100644 --- a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/PrimitiveBackgroundPropertyBuilders.java +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/PrimitiveBackgroundPropertyBuilders.java @@ -5,6 +5,7 @@ import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; import com.openhtmltopdf.css.constants.CSSName; import com.openhtmltopdf.css.constants.IdentValue; @@ -13,27 +14,64 @@ import com.openhtmltopdf.css.parser.CSSValue; import com.openhtmltopdf.css.parser.PropertyValue; import com.openhtmltopdf.css.parser.property.PrimitivePropertyBuilders.GenericColor; -import com.openhtmltopdf.css.parser.property.PrimitivePropertyBuilders.GenericURIWithNone; import com.openhtmltopdf.css.parser.property.PrimitivePropertyBuilders.SingleIdent; import com.openhtmltopdf.css.sheet.PropertyDeclaration; public class PrimitiveBackgroundPropertyBuilders { - public static class BackgroundImage extends GenericURIWithNone { + private static BitSet setOf(IdentValue... val) { + return PrimitivePropertyBuilders.setFor(val); + } + + private abstract static class MultipleBackgroundValueBuilder extends AbstractPropertyBuilder { + protected abstract PropertyValue processValue(CSSName cssName, PropertyValue value); + @Override public List buildDeclarations( CSSName cssName, List values, int origin, boolean important, boolean inheritAllowed) { - checkValueCount(cssName, 1, values.size()); - PropertyValue value = values.get(0); + checkValueCount(cssName, 1, Integer.MAX_VALUE, values.size()); + + List res; + + if (values.size() == 1) { + PropertyValue val = values.get(0); + checkInheritAllowed(val, inheritAllowed); + + if (val.getCssValueType() != CSSValue.CSS_INHERIT) { + res = Collections.singletonList(processValue(cssName, val)); + } else { + res = Collections.singletonList(val); + } + } else { + res = + values.stream() + .peek(this::checkForbidInherit) + .map(val -> processValue(cssName, val)) + .collect(Collectors.toList()); + } + + return Collections.singletonList( + new PropertyDeclaration(cssName, new PropertyValue(res), important, origin)); + } + } + public static class BackgroundImage extends MultipleBackgroundValueBuilder { + @Override + protected PropertyValue processValue(CSSName cssName, PropertyValue value) { if (value.getPropertyValueType() == PropertyValue.VALUE_TYPE_FUNCTION && Objects.equals(value.getFunction().getName(), "linear-gradient")) { // TODO: Validation of linear-gradient args. - return Collections.singletonList( - new PropertyDeclaration(cssName, value, important, origin)); + return value; } else { - return super.buildDeclarations(cssName, values, origin, important, inheritAllowed); + checkIdentOrURIType(cssName, value); + + if (value.getPrimitiveType() == CSSPrimitiveValue.CSS_IDENT) { + IdentValue ident = checkIdent(cssName, value); + checkValidity(cssName, setOf(IdentValue.NONE), ident); + } + + return value; } } } diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/style/CalculatedStyle.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/style/CalculatedStyle.java index 95008aa16..3240b14ef 100644 --- a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/style/CalculatedStyle.java +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/style/CalculatedStyle.java @@ -1232,11 +1232,6 @@ public boolean isShowEmptyCells() { return isCollapseBorders() || isIdent(CSSName.EMPTY_CELLS, IdentValue.SHOW); } - public boolean isHasBackground() { - return ! (isIdent(CSSName.BACKGROUND_COLOR, IdentValue.TRANSPARENT) && - isIdent(CSSName.BACKGROUND_IMAGE, IdentValue.NONE)); - } - public List getTextDecorations() { FSDerivedValue value = valueByName(CSSName.TEXT_DECORATION); if (value == IdentValue.NONE) { @@ -1399,20 +1394,40 @@ public static int getCSSMaxHeight(CssContext c, Box box) { } } - public boolean isLinearGradient() { - FSDerivedValue value = valueByName(CSSName.BACKGROUND_IMAGE); - return value instanceof FunctionValue && - Objects.equals(((FunctionValue) value).getFunction().getName(), "linear-gradient"); + public boolean isHasBackground() { + return !isIdent(CSSName.BACKGROUND_COLOR, IdentValue.TRANSPARENT) || + isHasBackgroundImage(); + } + + public boolean isHasBackgroundImage() { + List backgroundImages = getBackgroundImages(); + + if (backgroundImages.size() == 1) { + return backgroundImages.get(0).getIdentValue() != IdentValue.NONE; + } else { + return backgroundImages.stream().anyMatch(val -> val.getIdentValue() != IdentValue.NONE); + } + } + + public boolean isLinearGradient(PropertyValue value) { + return value.getPropertyValueType() == PropertyValue.VALUE_TYPE_FUNCTION && + Objects.equals(value.getFunction().getName(), "linear-gradient"); } - public FSLinearGradient getLinearGradient(CssContext cssContext, int boxWidth, int boxHeight) { - if (!isLinearGradient()) { + public FSLinearGradient getLinearGradient( + PropertyValue value, CssContext cssContext, int boxWidth, int boxHeight) { + + if (!isLinearGradient(value)) { return null; } - FunctionValue value = (FunctionValue) valueByName(CSSName.BACKGROUND_IMAGE); return new FSLinearGradient(this, value.getFunction(), boxWidth, boxHeight, cssContext); } + + public List getBackgroundImages() { + ListValue values = (ListValue) valueByName(CSSName.BACKGROUND_IMAGE); + return values.getValues(); + } } /* diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/render/AbstractOutputDevice.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/render/AbstractOutputDevice.java index d5e328e1d..ad33d5a07 100644 --- a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/render/AbstractOutputDevice.java +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/render/AbstractOutputDevice.java @@ -43,6 +43,8 @@ import java.awt.*; import java.awt.geom.Area; +import java.util.ArrayList; +import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.logging.Level; @@ -198,15 +200,25 @@ public void paintBorder(RenderingContext c, CalculatedStyle style, Rectangle edg BorderPainter.paint(edge, sides, style.getBorder(c), c, 0, true); } - private FSImage getBackgroundImage(RenderingContext c, CalculatedStyle style) { - if (! style.isIdent(CSSName.BACKGROUND_IMAGE, IdentValue.NONE)) { - String uri = style.getStringProperty(CSSName.BACKGROUND_IMAGE); + private FSImage getBackgroundImage(PropertyValue bgImage, RenderingContext c) { + if (bgImage.getIdentValue() != IdentValue.NONE) { + String uri = bgImage.getStringValue(); + try { return c.getUac().getImageResource(uri).getImage(); } catch (Exception ex) { XRLog.log(Level.WARNING, LogMessageId.LogMessageId1Param.EXCEPTION_FAILED_TO_LOAD_BACKGROUND_IMAGE_AT_URI, uri, ex); } } + +// if (! style.isIdent(CSSName.BACKGROUND_IMAGE, IdentValue.NONE)) { +// String uri = style.getStringProperty(CSSName.BACKGROUND_IMAGE); +// try { +// return c.getUac().getImageResource(uri).getImage(); +// } catch (Exception ex) { +// XRLog.log(Level.WARNING, LogMessageId.LogMessageId1Param.EXCEPTION_FAILED_TO_LOAD_BACKGROUND_IMAGE_AT_URI, uri, ex); +// } +// } return null; } @@ -236,27 +248,13 @@ private void paintBackground0( return; } - FSColor backgroundColor = style.getBackgroundColor(); - FSImage backgroundImage = null; - FSLinearGradient backgroundLinearGradient = null; - - if (style.isLinearGradient()) { - backgroundLinearGradient = style.getLinearGradient(c, (int) (bgImageContainer.width - border.width()), (int) (bgImageContainer.height - border.height())); - } else { - backgroundImage = getBackgroundImage(c, style); + if (!style.isHasBackground()) { + return; } - // If the image width or height is zero, then there's nothing to draw. - // Also prevents infinte loop when trying to tile an image with zero size. - if (backgroundImage == null || backgroundImage.getHeight() == 0 || backgroundImage.getWidth() == 0) { - backgroundImage = null; - } + FSColor backgroundColor = style.getBackgroundColor(); + List bgImages = style.getBackgroundImages(); - if ( (backgroundColor == null || backgroundColor == FSRGBColor.TRANSPARENT) && - backgroundImage == null && backgroundLinearGradient == null) { - return; - } - Shape borderBoundsShape = BorderPainter.generateBorderBounds(backgroundBounds, border, true); // FIXME for issue 396 - generating an Area for a shape with curves is very very slow and @@ -265,7 +263,7 @@ private void paintBackground0( Area borderBounds = border.hasBorderRadius() && c.isFastRenderer() ? null : new Area(borderBoundsShape); Shape oldclip = null; - + if (!c.isFastRenderer()) { oldclip = getClip(); if(oldclip != null) { @@ -273,7 +271,7 @@ private void paintBackground0( borderBounds.intersect(new Area(oldclip)); } setClip(borderBounds); - } else if (backgroundImage != null || backgroundLinearGradient != null) { + } else if (style.isHasBackgroundImage()) { pushClip(borderBounds != null ? borderBounds : borderBoundsShape); } @@ -282,80 +280,113 @@ private void paintBackground0( fill(borderBounds != null ? borderBounds : borderBoundsShape); } - if (backgroundImage != null || backgroundLinearGradient != null) { - Rectangle localBGImageContainer = bgImageContainer; - if (style.isFixedBackground()) { - localBGImageContainer = c.getViewportRectangle(); - } - - int xoff = localBGImageContainer.x; - int yoff = localBGImageContainer.y; - - if (border != null) { - xoff += (int)border.left(); - yoff += (int)border.top(); - } - - if (backgroundImage != null) { - scaleBackgroundImage(c, style, localBGImageContainer, backgroundImage); + List bgImagesReversed = new ArrayList<>(bgImages); + Collections.reverse(bgImagesReversed); - float imageWidth = backgroundImage.getWidth(); - float imageHeight = backgroundImage.getHeight(); + for (PropertyValue bgImage : bgImagesReversed) { + if (style.isLinearGradient(bgImage)) { + FSLinearGradient backgroundLinearGradient = style.getLinearGradient(bgImage, c, (int) (bgImageContainer.width - border.width()), (int) (bgImageContainer.height - border.height())); - BackgroundPosition position = style.getBackgroundPosition(); - xoff += calcOffset( - c, style, position.getHorizontal(), localBGImageContainer.width, imageWidth); - yoff += calcOffset( - c, style, position.getVertical(), localBGImageContainer.height, imageHeight); + if (backgroundLinearGradient != null) { + Dimension xyoff = calcInitialXYOff(bgImageContainer, border, style, c); - boolean hrepeat = style.isHorizontalBackgroundRepeat(); - boolean vrepeat = style.isVerticalBackgroundRepeat(); + int xoff = xyoff.width; + int yoff = xyoff.height; - if (! hrepeat && ! vrepeat) { - Rectangle imageBounds = new Rectangle(xoff, yoff, (int)imageWidth, (int)imageHeight); - if (imageBounds.intersects(backgroundBounds)) { - drawImage(backgroundImage, xoff, yoff, style.isImageRenderingInterpolate()); - } - } else if (hrepeat && vrepeat) { - paintTiles( - backgroundImage, - adjustTo(backgroundBounds.x, xoff, (int)imageWidth), - adjustTo(backgroundBounds.y, yoff, (int)imageHeight), - backgroundBounds.x + backgroundBounds.width, - backgroundBounds.y + backgroundBounds.height, style.isImageRenderingInterpolate()); - } else if (hrepeat) { - xoff = adjustTo(backgroundBounds.x, xoff, (int)imageWidth); - Rectangle imageBounds = new Rectangle(xoff, yoff, (int)imageWidth, (int)imageHeight); - if (imageBounds.intersects(backgroundBounds)) { - paintHorizontalBand( - backgroundImage, - xoff, - yoff, - backgroundBounds.x + backgroundBounds.width, style.isImageRenderingInterpolate()); + drawLinearGradient(backgroundLinearGradient, new Rectangle(xoff, yoff, bgImageContainer.width, bgImageContainer.height)); } - } else if (vrepeat) { - yoff = adjustTo(backgroundBounds.y, yoff, (int)imageHeight); - Rectangle imageBounds = new Rectangle(xoff, yoff, (int)imageWidth, (int)imageHeight); - if (imageBounds.intersects(backgroundBounds)) { - paintVerticalBand( - backgroundImage, - xoff, - yoff, - backgroundBounds.y + backgroundBounds.height, style.isImageRenderingInterpolate()); + } else { + FSImage backgroundImage = getBackgroundImage(bgImage, c); + + // If the image width or height is zero, then there's nothing to draw. + // Also prevents infinte loop when trying to tile an image with zero size. + if (backgroundImage != null && backgroundImage.getHeight() != 0 && backgroundImage.getWidth() != 0) { + drawBgImage(c, style, backgroundBounds, bgImageContainer, border, backgroundImage); } - } // End background image painting. - } else if (backgroundLinearGradient != null) { - drawLinearGradient(backgroundLinearGradient, new Rectangle(xoff, yoff, bgImageContainer.width, bgImageContainer.height)); } } - + if (!c.isFastRenderer()) { setClip(oldclip); - } else if (backgroundImage != null || backgroundLinearGradient != null) { + } else if (style.isHasBackgroundImage()) { popClip(); } } + private Dimension calcInitialXYOff( + Rectangle bgImageContainer, + BorderPropertySet border, + CalculatedStyle style, + RenderingContext c) { + + Rectangle localBGImageContainer = bgImageContainer; + + if (style.isFixedBackground()) { + localBGImageContainer = c.getViewportRectangle(); + } + + int xoff = localBGImageContainer.x; + int yoff = localBGImageContainer.y; + + if (border != null) { + xoff += (int) border.left(); + yoff += (int) border.top(); + } + + return new Dimension(xoff, yoff); + } + + private void drawBgImage( + RenderingContext c, CalculatedStyle style, Rectangle backgroundBounds, + Rectangle bgImageContainer, BorderPropertySet border, FSImage backgroundImage) { + + Dimension xyoff = calcInitialXYOff(bgImageContainer, border, style, c); + + int xoff = xyoff.width; + int yoff = xyoff.height; + + Rectangle localBGImageContainer = style.isFixedBackground() ? + c.getViewportRectangle() : bgImageContainer; + + scaleBackgroundImage(c, style, localBGImageContainer, backgroundImage); + + float imageWidth = backgroundImage.getWidth(); + float imageHeight = backgroundImage.getHeight(); + + BackgroundPosition position = style.getBackgroundPosition(); + + xoff += calcOffset(c, style, position.getHorizontal(), localBGImageContainer.width, imageWidth); + yoff += calcOffset(c, style, position.getVertical(), localBGImageContainer.height, imageHeight); + + boolean hrepeat = style.isHorizontalBackgroundRepeat(); + boolean vrepeat = style.isVerticalBackgroundRepeat(); + + if (!hrepeat && !vrepeat) { + Rectangle imageBounds = new Rectangle(xoff, yoff, (int) imageWidth, (int) imageHeight); + if (imageBounds.intersects(backgroundBounds)) { + drawImage(backgroundImage, xoff, yoff, style.isImageRenderingInterpolate()); + } + } else if (hrepeat && vrepeat) { + paintTiles(backgroundImage, adjustTo(backgroundBounds.x, xoff, (int) imageWidth), + adjustTo(backgroundBounds.y, yoff, (int) imageHeight), backgroundBounds.x + backgroundBounds.width, + backgroundBounds.y + backgroundBounds.height, style.isImageRenderingInterpolate()); + } else if (hrepeat) { + xoff = adjustTo(backgroundBounds.x, xoff, (int) imageWidth); + Rectangle imageBounds = new Rectangle(xoff, yoff, (int) imageWidth, (int) imageHeight); + if (imageBounds.intersects(backgroundBounds)) { + paintHorizontalBand(backgroundImage, xoff, yoff, backgroundBounds.x + backgroundBounds.width, + style.isImageRenderingInterpolate()); + } + } else if (vrepeat) { + yoff = adjustTo(backgroundBounds.y, yoff, (int) imageHeight); + Rectangle imageBounds = new Rectangle(xoff, yoff, (int) imageWidth, (int) imageHeight); + if (imageBounds.intersects(backgroundBounds)) { + paintVerticalBand(backgroundImage, xoff, yoff, backgroundBounds.y + backgroundBounds.height, + style.isImageRenderingInterpolate()); + } + } + } + private int adjustTo(int target, int current, int imageDim) { int result = current; if (result > target) { From 22ad73220a2bc54d251dcd6589d33f5771dec5f4 Mon Sep 17 00:00:00 2001 From: danfickle Date: Fri, 12 Feb 2021 23:48:55 +1100 Subject: [PATCH 03/12] #649 Test for multiple image support. Passing. --- .../main/resources/demos/images/cc0-cat.png | Bin 0 -> 3084 bytes .../expected/issue-649-multiple-bg-images.pdf | Bin 0 -> 15247 bytes .../html/issue-649-multiple-bg-images.html | 21 ++++++++++++++++++ .../VisualRegressionTest.java | 8 +++++++ 4 files changed, 29 insertions(+) create mode 100644 openhtmltopdf-examples/src/main/resources/demos/images/cc0-cat.png create mode 100644 openhtmltopdf-examples/src/main/resources/visualtest/expected/issue-649-multiple-bg-images.pdf create mode 100644 openhtmltopdf-examples/src/main/resources/visualtest/html/issue-649-multiple-bg-images.html diff --git a/openhtmltopdf-examples/src/main/resources/demos/images/cc0-cat.png b/openhtmltopdf-examples/src/main/resources/demos/images/cc0-cat.png new file mode 100644 index 0000000000000000000000000000000000000000..968f2ca3a1bbb27d3bde93b835459960d92f4365 GIT binary patch literal 3084 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 aHU9@*_wY()r#qYg0000& literal 0 HcmV?d00001 diff --git a/openhtmltopdf-examples/src/main/resources/visualtest/expected/issue-649-multiple-bg-images.pdf b/openhtmltopdf-examples/src/main/resources/visualtest/expected/issue-649-multiple-bg-images.pdf new file mode 100644 index 0000000000000000000000000000000000000000..619dba0f1c05fee4c7b053492b3ed7e7d2e6849b GIT binary patch literal 15247 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 + + + + +
+ + diff --git a/openhtmltopdf-examples/src/test/java/com/openhtmltopdf/visualregressiontests/VisualRegressionTest.java b/openhtmltopdf-examples/src/test/java/com/openhtmltopdf/visualregressiontests/VisualRegressionTest.java index ad6c7b8ad..4ccfe9799 100644 --- a/openhtmltopdf-examples/src/test/java/com/openhtmltopdf/visualregressiontests/VisualRegressionTest.java +++ b/openhtmltopdf-examples/src/test/java/com/openhtmltopdf/visualregressiontests/VisualRegressionTest.java @@ -1375,6 +1375,14 @@ public void testIssue642TransformInline() throws IOException { assertTrue(vt.runTest("issue-642-transform-inline")); } + /** + * Tests that the background-image property allows multiple values. + */ + @Test + public void testIssue649MultipleBgImages() throws IOException { + assertTrue(vt.runTest("issue-649-multiple-bg-images")); + } + // TODO: // + Elements that appear just on generated overflow pages. // + content property (page counters, etc) From a27b3a1023e44304b3de7868c0c94d5b9f7026cc Mon Sep 17 00:00:00 2001 From: danfickle Date: Sat, 13 Feb 2021 00:10:15 +1100 Subject: [PATCH 04/12] #649 Fix build - background shorthand property must be kept in sync. --- .../property/BackgroundPropertyBuilder.java | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/BackgroundPropertyBuilder.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/BackgroundPropertyBuilder.java index 702b7c5b3..35ad7352f 100644 --- a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/BackgroundPropertyBuilder.java +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/BackgroundPropertyBuilder.java @@ -20,6 +20,7 @@ package com.openhtmltopdf.css.parser.property; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import com.openhtmltopdf.css.constants.CSSName; @@ -118,11 +119,13 @@ public List buildDeclarations( if (backgroundImage != null) { throw new CSSParseException("A background-image value cannot be set twice", -1); } - + + List bgImages = Collections.singletonList(value); + backgroundImage = new PropertyDeclaration( - CSSName.BACKGROUND_IMAGE, value, important, origin); + CSSName.BACKGROUND_IMAGE, new PropertyValue(bgImages), important, origin); } - + if (PrimitivePropertyBuilders.BACKGROUND_POSITIONS.get(ident.FS_ID)) { processingBackgroundPosition = true; } @@ -137,9 +140,11 @@ public List buildDeclarations( if (backgroundImage != null) { throw new CSSParseException("A background-image value cannot be set twice", -1); } - + + List bgImages = Collections.singletonList(value); + backgroundImage = new PropertyDeclaration( - CSSName.BACKGROUND_IMAGE, value, important, origin); + CSSName.BACKGROUND_IMAGE, new PropertyValue(bgImages), important, origin); } if (processingBackgroundPosition || isLength(value) || type == CSSPrimitiveValue.CSS_PERCENTAGE) { @@ -169,8 +174,10 @@ public List buildDeclarations( } if (backgroundImage == null) { + List bgImages = Collections.singletonList(new PropertyValue(IdentValue.NONE)); + backgroundImage = new PropertyDeclaration( - CSSName.BACKGROUND_IMAGE, new PropertyValue(IdentValue.NONE), important, origin); + CSSName.BACKGROUND_IMAGE, new PropertyValue(bgImages), important, origin); } if (backgroundRepeat == null) { From f8216447958ec2234d28c41b6b40e5d3e10d732a Mon Sep 17 00:00:00 2001 From: danfickle Date: Sun, 14 Feb 2021 20:19:45 +1100 Subject: [PATCH 05/12] #649 Reinstate support for inherit in background props With WIP test. --- .../PrimitiveBackgroundPropertyBuilders.java | 3 +- ...issue-649-multiple-bg-images-advanced.html | 32 +++++++++++++++++++ .../VisualRegressionTest.java | 9 ++++++ 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 openhtmltopdf-examples/src/main/resources/visualtest/html/issue-649-multiple-bg-images-advanced.html diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/PrimitiveBackgroundPropertyBuilders.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/PrimitiveBackgroundPropertyBuilders.java index b8d69dfc6..39ddafc69 100644 --- a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/PrimitiveBackgroundPropertyBuilders.java +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/PrimitiveBackgroundPropertyBuilders.java @@ -41,7 +41,8 @@ public List buildDeclarations( if (val.getCssValueType() != CSSValue.CSS_INHERIT) { res = Collections.singletonList(processValue(cssName, val)); } else { - res = Collections.singletonList(val); + return Collections.singletonList( + new PropertyDeclaration(cssName, val, important, origin)); } } else { res = diff --git a/openhtmltopdf-examples/src/main/resources/visualtest/html/issue-649-multiple-bg-images-advanced.html b/openhtmltopdf-examples/src/main/resources/visualtest/html/issue-649-multiple-bg-images-advanced.html new file mode 100644 index 000000000..c1343db76 --- /dev/null +++ b/openhtmltopdf-examples/src/main/resources/visualtest/html/issue-649-multiple-bg-images-advanced.html @@ -0,0 +1,32 @@ + + + + + +
+
+
+ + + diff --git a/openhtmltopdf-examples/src/test/java/com/openhtmltopdf/visualregressiontests/VisualRegressionTest.java b/openhtmltopdf-examples/src/test/java/com/openhtmltopdf/visualregressiontests/VisualRegressionTest.java index 4ccfe9799..e8c3d78ed 100644 --- a/openhtmltopdf-examples/src/test/java/com/openhtmltopdf/visualregressiontests/VisualRegressionTest.java +++ b/openhtmltopdf-examples/src/test/java/com/openhtmltopdf/visualregressiontests/VisualRegressionTest.java @@ -1383,6 +1383,15 @@ public void testIssue649MultipleBgImages() throws IOException { assertTrue(vt.runTest("issue-649-multiple-bg-images")); } + /** + * Tests that the other background-* properties allow multiple values. + */ + @Test + @Ignore // WIP + public void testIssue649MultipleBgImagesAdvanced() throws IOException { + assertTrue(vt.runTest("issue-649-multiple-bg-images-advanced")); + } + // TODO: // + Elements that appear just on generated overflow pages. // + content property (page counters, etc) From b94d62f1e8b314d37f6287931e44f25d57703568 Mon Sep 17 00:00:00 2001 From: danfickle Date: Sun, 14 Feb 2021 22:39:22 +1100 Subject: [PATCH 06/12] #649 Multiple items for background-position property --- .../PrimitiveBackgroundPropertyBuilders.java | 180 ++++++++++-------- .../css/style/CalculatedStyle.java | 63 ++++-- .../render/AbstractOutputDevice.java | 41 ++-- ...issue-649-multiple-bg-images-advanced.html | 23 ++- 4 files changed, 191 insertions(+), 116 deletions(-) diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/PrimitiveBackgroundPropertyBuilders.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/PrimitiveBackgroundPropertyBuilders.java index 39ddafc69..caf01b673 100644 --- a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/PrimitiveBackgroundPropertyBuilders.java +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/PrimitiveBackgroundPropertyBuilders.java @@ -1,11 +1,11 @@ package com.openhtmltopdf.css.parser.property; import java.util.ArrayList; +import java.util.Arrays; import java.util.BitSet; import java.util.Collections; import java.util.List; import java.util.Objects; -import java.util.stream.Collectors; import com.openhtmltopdf.css.constants.CSSName; import com.openhtmltopdf.css.constants.IdentValue; @@ -13,6 +13,7 @@ import com.openhtmltopdf.css.parser.CSSPrimitiveValue; import com.openhtmltopdf.css.parser.CSSValue; import com.openhtmltopdf.css.parser.PropertyValue; +import com.openhtmltopdf.css.parser.Token; import com.openhtmltopdf.css.parser.property.PrimitivePropertyBuilders.GenericColor; import com.openhtmltopdf.css.parser.property.PrimitivePropertyBuilders.SingleIdent; import com.openhtmltopdf.css.sheet.PropertyDeclaration; @@ -23,7 +24,15 @@ private static BitSet setOf(IdentValue... val) { } private abstract static class MultipleBackgroundValueBuilder extends AbstractPropertyBuilder { - protected abstract PropertyValue processValue(CSSName cssName, PropertyValue value); + protected abstract List processValue(CSSName cssName, PropertyValue value); + + protected List processValues(CSSName cssName, PropertyValue val1, PropertyValue val2) { + return Arrays.asList(val1, val2); + } + + protected boolean allowsTwoValueItems() { + return false; + } @Override public List buildDeclarations( @@ -39,17 +48,33 @@ public List buildDeclarations( checkInheritAllowed(val, inheritAllowed); if (val.getCssValueType() != CSSValue.CSS_INHERIT) { - res = Collections.singletonList(processValue(cssName, val)); + res = processValue(cssName, val); } else { return Collections.singletonList( new PropertyDeclaration(cssName, val, important, origin)); } } else { - res = - values.stream() - .peek(this::checkForbidInherit) - .map(val -> processValue(cssName, val)) - .collect(Collectors.toList()); + res = new ArrayList<>(values.size()); + + for (int i = 0; i < values.size(); i++) { + boolean atEnd = i == values.size() - 1; + boolean beforeComma = !atEnd && values.get(i + 1).getOperator() == Token.TK_COMMA; + + PropertyValue val1 = values.get(i); + PropertyValue val2 = !atEnd && !beforeComma ? values.get(i + 1) : null; + + checkForbidInherit(val1); + + if (val2 == null) { + res.addAll(processValue(cssName, val1)); + } else if (!allowsTwoValueItems()) { + checkValueCount(cssName, 1, 2); + } else { + checkForbidInherit(val2); + res.addAll(processValues(cssName, val1, val2)); + i++; + } + } } return Collections.singletonList( @@ -59,11 +84,11 @@ public List buildDeclarations( public static class BackgroundImage extends MultipleBackgroundValueBuilder { @Override - protected PropertyValue processValue(CSSName cssName, PropertyValue value) { + protected List processValue(CSSName cssName, PropertyValue value) { if (value.getPropertyValueType() == PropertyValue.VALUE_TYPE_FUNCTION && Objects.equals(value.getFunction().getName(), "linear-gradient")) { // TODO: Validation of linear-gradient args. - return value; + return Collections.singletonList(value); } else { checkIdentOrURIType(cssName, value); @@ -72,7 +97,7 @@ protected PropertyValue processValue(CSSName cssName, PropertyValue value) { checkValidity(cssName, setOf(IdentValue.NONE), ident); } - return value; + return Collections.singletonList(value); } } } @@ -148,44 +173,40 @@ public List buildDeclarations(CSSName cssName, List buildDeclarations( - CSSName cssName, List values, int origin, boolean important, boolean inheritAllowed) { - checkValueCount(cssName, 1, 2, values.size()); + protected List processValue(CSSName cssName, PropertyValue first) { + checkIdentLengthOrPercentType(cssName, first); - PropertyValue first = values.get(0); - PropertyValue second = null; - if (values.size() == 2) { - second = values.get(1); + if (isLength(first) || first.getPrimitiveType() == CSSPrimitiveValue.CSS_PERCENTAGE) { + return Arrays.asList(first, createValueForIdent(IdentValue.CENTER)); } - checkInheritAllowed(first, inheritAllowed); - if (values.size() == 1 && - first.getCssValueType() == CSSValue.CSS_INHERIT) { - return Collections.singletonList( - new PropertyDeclaration(cssName, first, important, origin)); - } + assert first.getPrimitiveType() == CSSPrimitiveValue.CSS_IDENT; - if (second != null) { - checkInheritAllowed(second, false); - } + IdentValue firstIdent = checkIdent(cssName, first); + checkValidity(cssName, getAllowed(), firstIdent); - checkIdentLengthOrPercentType(cssName, first); - if (second == null) { - if (isLength(first) || first.getPrimitiveType() == CSSPrimitiveValue.CSS_PERCENTAGE) { - List responseValues = new ArrayList<>(2); - responseValues.add(first); - responseValues.add(new PropertyValue( - CSSPrimitiveValue.CSS_PERCENTAGE, 50.0f, "50%")); - return Collections.singletonList(new PropertyDeclaration( - CSSName.BACKGROUND_POSITION, - new PropertyValue(responseValues), important, origin)); - } + if (firstIdent == IdentValue.TOP || + firstIdent == IdentValue.BOTTOM) { + return Arrays.asList( + createValueForIdent(IdentValue.CENTER), + createValueForIdent(firstIdent)); } else { - checkIdentLengthOrPercentType(cssName, second); + assert firstIdent == IdentValue.CENTER || + firstIdent == IdentValue.LEFT || + firstIdent == IdentValue.RIGHT; + + return Arrays.asList( + createValueForIdent(firstIdent), + createValueForIdent(IdentValue.CENTER)); } + } + @Override + protected List processValues(CSSName cssName, PropertyValue first, PropertyValue second) { + checkIdentLengthOrPercentType(cssName, first); + checkIdentLengthOrPercentType(cssName, second); IdentValue firstIdent = null; if (first.getPrimitiveType() == CSSPrimitiveValue.CSS_IDENT) { @@ -194,64 +215,79 @@ public List buildDeclarations( } IdentValue secondIdent = null; - if (second == null) { - secondIdent = IdentValue.CENTER; - } else if (second.getPrimitiveType() == CSSPrimitiveValue.CSS_IDENT) { + if (second.getPrimitiveType() == CSSPrimitiveValue.CSS_IDENT) { secondIdent = checkIdent(cssName, second); checkValidity(cssName, getAllowed(), secondIdent); } if (firstIdent == null && secondIdent == null) { - return Collections.singletonList(new PropertyDeclaration( - CSSName.BACKGROUND_POSITION, new PropertyValue(values), important, origin)); + assert isLength(first) || first.getPrimitiveType() == CSSPrimitiveValue.CSS_PERCENTAGE; + assert isLength(second) || second.getPrimitiveType() == CSSPrimitiveValue.CSS_PERCENTAGE; + + return Arrays.asList(first, second); } else if (firstIdent != null && secondIdent != null) { if (firstIdent == IdentValue.TOP || firstIdent == IdentValue.BOTTOM || - secondIdent == IdentValue.LEFT || secondIdent == IdentValue.RIGHT) { + secondIdent == IdentValue.LEFT || secondIdent == IdentValue.RIGHT) { + // CSS Standard allows to swap ident order. IdentValue temp = firstIdent; firstIdent = secondIdent; secondIdent = temp; } + // Check that we don't have "left left" or "bottom top" checkIdentPosition(cssName, firstIdent, secondIdent); - return createTwoPercentValueResponse( - getPercentForIdent(firstIdent), - getPercentForIdent(secondIdent), - important, - origin); + assert firstIdent == IdentValue.CENTER || + firstIdent == IdentValue.LEFT || + firstIdent == IdentValue.RIGHT; + + assert secondIdent == IdentValue.CENTER || + secondIdent == IdentValue.TOP || + secondIdent == IdentValue.BOTTOM; + + return Arrays.asList( + createValueForIdent(firstIdent), + createValueForIdent(secondIdent)); } else { + // Check that we don't have "70% left" or "bottom 40%" checkIdentPosition(cssName, firstIdent, secondIdent); - List responseValues = new ArrayList<>(2); - if (firstIdent == null) { - responseValues.add(first); - responseValues.add(createValueForIdent(secondIdent)); + assert isLength(first) || first.getPrimitiveType() == CSSPrimitiveValue.CSS_PERCENTAGE; + assert secondIdent != null; + + return Arrays.asList(first, createValueForIdent(secondIdent)); } else { - responseValues.add(createValueForIdent(firstIdent)); - responseValues.add(second); - } + assert firstIdent != null; + assert isLength(second) || second.getPrimitiveType() == CSSPrimitiveValue.CSS_PERCENTAGE; - return Collections.singletonList(new PropertyDeclaration( - CSSName.BACKGROUND_POSITION, - new PropertyValue(responseValues), important, origin)); + return Arrays.asList(createValueForIdent(firstIdent), second); + } } } + @Override + protected boolean allowsTwoValueItems() { + return true; + } + private void checkIdentPosition(CSSName cssName, IdentValue firstIdent, IdentValue secondIdent) { if (firstIdent == IdentValue.TOP || firstIdent == IdentValue.BOTTOM || - secondIdent == IdentValue.LEFT || secondIdent == IdentValue.RIGHT) { + secondIdent == IdentValue.LEFT || secondIdent == IdentValue.RIGHT) { throw new CSSParseException("Invalid combination of keywords in " + cssName, -1); } } private float getPercentForIdent(IdentValue ident) { - float percent = 0.0f; + float percent; if (ident == IdentValue.CENTER) { percent = 50.f; } else if (ident == IdentValue.BOTTOM || ident == IdentValue.RIGHT) { percent = 100.0f; + } else { + assert ident == IdentValue.TOP || ident == IdentValue.LEFT; + percent = 0.0f; } return percent; @@ -263,24 +299,6 @@ private PropertyValue createValueForIdent(IdentValue ident) { CSSPrimitiveValue.CSS_PERCENTAGE, percent, percent + "%"); } - private List createTwoPercentValueResponse( - float percent1, float percent2, boolean important, int origin) { - PropertyValue value1 = new PropertyValue( - CSSPrimitiveValue.CSS_PERCENTAGE, percent1, percent1 + "%"); - PropertyValue value2 = new PropertyValue( - CSSPrimitiveValue.CSS_PERCENTAGE, percent2, percent2 + "%"); - - List values = new ArrayList<>(2); - values.add(value1); - values.add(value2); - - PropertyDeclaration result = new PropertyDeclaration( - CSSName.BACKGROUND_POSITION, - new PropertyValue(values), important, origin); - - return Collections.singletonList(result); - } - private BitSet getAllowed() { return PrimitivePropertyBuilders.BACKGROUND_POSITIONS; } diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/style/CalculatedStyle.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/style/CalculatedStyle.java index 3240b14ef..776458e44 100644 --- a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/style/CalculatedStyle.java +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/style/CalculatedStyle.java @@ -21,6 +21,8 @@ package com.openhtmltopdf.css.style; import java.awt.Cursor; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.logging.Level; @@ -344,14 +346,6 @@ private BackgroundSize createBackgroundSize() { throw new RuntimeException("internal error"); } - public BackgroundPosition getBackgroundPosition() { - ListValue result = (ListValue) valueByName(CSSName.BACKGROUND_POSITION); - List values = result.getValues(); - - return new BackgroundPosition( - values.get(0), values.get(1)); - } - public List getCounterReset() { FSDerivedValue value = valueByName(CSSName.COUNTER_RESET); @@ -1400,7 +1394,7 @@ public boolean isHasBackground() { } public boolean isHasBackgroundImage() { - List backgroundImages = getBackgroundImages(); + List backgroundImages = ((ListValue) valueByName(CSSName.BACKGROUND_IMAGE)).getValues(); if (backgroundImages.size() == 1) { return backgroundImages.get(0).getIdentValue() != IdentValue.NONE; @@ -1409,6 +1403,17 @@ public boolean isHasBackgroundImage() { } } + public enum BackgroundImageType { + URI, GRADIENT, NONE; + } + + public static class BackgroundContainer { + public BackgroundImageType type; + public PropertyValue imageGradientOrNone; + + public BackgroundPosition backgroundPosition; + } + public boolean isLinearGradient(PropertyValue value) { return value.getPropertyValueType() == PropertyValue.VALUE_TYPE_FUNCTION && Objects.equals(value.getFunction().getName(), "linear-gradient"); @@ -1424,9 +1429,43 @@ public FSLinearGradient getLinearGradient( return new FSLinearGradient(this, value.getFunction(), boxWidth, boxHeight, cssContext); } - public List getBackgroundImages() { - ListValue values = (ListValue) valueByName(CSSName.BACKGROUND_IMAGE); - return values.getValues(); + public List getBackgroundImages() { + List images = ((ListValue) valueByName(CSSName.BACKGROUND_IMAGE)).getValues(); + List positions = ((ListValue) valueByName(CSSName.BACKGROUND_POSITION)).getValues(); + + assert positions.size() % 2 == 0; + + List posPairs = new ArrayList<>(positions.size() / 2); + for (int i = 0; i < positions.size(); i += 2) { + posPairs.add(new BackgroundPosition(positions.get(i), positions.get(i + 1))); + } + + List backgrounds = new ArrayList<>(images.size()); + + for (int i = 0; i < images.size(); i++) { + BackgroundContainer bg = new BackgroundContainer(); + PropertyValue img = images.get(i); + + if (isLinearGradient(img)) { + bg.type = BackgroundImageType.GRADIENT; + } else if (img.getIdentValue() == IdentValue.NONE) { + bg.type = BackgroundImageType.NONE; + } else { + bg.type = BackgroundImageType.URI; + } + + bg.imageGradientOrNone = img; + + // If less background-position values are provided than images, + // they must repeat. + bg.backgroundPosition = posPairs.get(i % posPairs.size()); + + backgrounds.add(bg); + } + + // Pre-reverse the images, from back to front. + Collections.reverse(backgrounds); + return backgrounds; } } diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/render/AbstractOutputDevice.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/render/AbstractOutputDevice.java index ad33d5a07..e051517e2 100644 --- a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/render/AbstractOutputDevice.java +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/render/AbstractOutputDevice.java @@ -30,6 +30,8 @@ import com.openhtmltopdf.css.style.BackgroundPosition; import com.openhtmltopdf.css.style.BackgroundSize; import com.openhtmltopdf.css.style.CalculatedStyle; +import com.openhtmltopdf.css.style.CalculatedStyle.BackgroundContainer; +import com.openhtmltopdf.css.style.CalculatedStyle.BackgroundImageType; import com.openhtmltopdf.css.style.CssContext; import com.openhtmltopdf.css.style.derived.BorderPropertySet; import com.openhtmltopdf.css.style.derived.FSLinearGradient; @@ -211,14 +213,6 @@ private FSImage getBackgroundImage(PropertyValue bgImage, RenderingContext c) { } } -// if (! style.isIdent(CSSName.BACKGROUND_IMAGE, IdentValue.NONE)) { -// String uri = style.getStringProperty(CSSName.BACKGROUND_IMAGE); -// try { -// return c.getUac().getImageResource(uri).getImage(); -// } catch (Exception ex) { -// XRLog.log(Level.WARNING, LogMessageId.LogMessageId1Param.EXCEPTION_FAILED_TO_LOAD_BACKGROUND_IMAGE_AT_URI, uri, ex); -// } -// } return null; } @@ -253,7 +247,7 @@ private void paintBackground0( } FSColor backgroundColor = style.getBackgroundColor(); - List bgImages = style.getBackgroundImages(); + List bgImages = style.getBackgroundImages(); Shape borderBoundsShape = BorderPainter.generateBorderBounds(backgroundBounds, border, true); @@ -280,12 +274,10 @@ private void paintBackground0( fill(borderBounds != null ? borderBounds : borderBoundsShape); } - List bgImagesReversed = new ArrayList<>(bgImages); - Collections.reverse(bgImagesReversed); - - for (PropertyValue bgImage : bgImagesReversed) { - if (style.isLinearGradient(bgImage)) { - FSLinearGradient backgroundLinearGradient = style.getLinearGradient(bgImage, c, (int) (bgImageContainer.width - border.width()), (int) (bgImageContainer.height - border.height())); + for (BackgroundContainer bgImage : bgImages) { + if (bgImage.type == BackgroundImageType.GRADIENT) { + FSLinearGradient backgroundLinearGradient = + style.getLinearGradient(bgImage.imageGradientOrNone, c, (int) (bgImageContainer.width - border.width()), (int) (bgImageContainer.height - border.height())); if (backgroundLinearGradient != null) { Dimension xyoff = calcInitialXYOff(bgImageContainer, border, style, c); @@ -295,13 +287,17 @@ private void paintBackground0( drawLinearGradient(backgroundLinearGradient, new Rectangle(xoff, yoff, bgImageContainer.width, bgImageContainer.height)); } + } else if (bgImage.type == BackgroundImageType.NONE) { + // Do nothing... } else { - FSImage backgroundImage = getBackgroundImage(bgImage, c); + assert bgImage.type == BackgroundImageType.URI; + + FSImage backgroundImage = getBackgroundImage(bgImage.imageGradientOrNone, c); // If the image width or height is zero, then there's nothing to draw. // Also prevents infinte loop when trying to tile an image with zero size. if (backgroundImage != null && backgroundImage.getHeight() != 0 && backgroundImage.getWidth() != 0) { - drawBgImage(c, style, backgroundBounds, bgImageContainer, border, backgroundImage); + drawBgImage(c, style, backgroundBounds, bgImageContainer, border, backgroundImage, bgImage); } } } @@ -337,8 +333,13 @@ private Dimension calcInitialXYOff( } private void drawBgImage( - RenderingContext c, CalculatedStyle style, Rectangle backgroundBounds, - Rectangle bgImageContainer, BorderPropertySet border, FSImage backgroundImage) { + RenderingContext c, + CalculatedStyle style, + Rectangle backgroundBounds, + Rectangle bgImageContainer, + BorderPropertySet border, + FSImage backgroundImage, + BackgroundContainer bgImage) { Dimension xyoff = calcInitialXYOff(bgImageContainer, border, style, c); @@ -353,7 +354,7 @@ private void drawBgImage( float imageWidth = backgroundImage.getWidth(); float imageHeight = backgroundImage.getHeight(); - BackgroundPosition position = style.getBackgroundPosition(); + BackgroundPosition position = bgImage.backgroundPosition; xoff += calcOffset(c, style, position.getHorizontal(), localBGImageContainer.width, imageWidth); yoff += calcOffset(c, style, position.getVertical(), localBGImageContainer.height, imageHeight); diff --git a/openhtmltopdf-examples/src/main/resources/visualtest/html/issue-649-multiple-bg-images-advanced.html b/openhtmltopdf-examples/src/main/resources/visualtest/html/issue-649-multiple-bg-images-advanced.html index c1343db76..9504b0c1c 100644 --- a/openhtmltopdf-examples/src/main/resources/visualtest/html/issue-649-multiple-bg-images-advanced.html +++ b/openhtmltopdf-examples/src/main/resources/visualtest/html/issue-649-multiple-bg-images-advanced.html @@ -2,7 +2,7 @@ @@ -28,5 +41,9 @@
+
+ +
+ From 0ad58c5253a675fa73928c371d555a41f958f93d Mon Sep 17 00:00:00 2001 From: danfickle Date: Sun, 14 Feb 2021 22:58:05 +1100 Subject: [PATCH 07/12] #649 Multiple values for background-repeat property --- .../property/BackgroundPropertyBuilder.java | 4 ++-- .../PrimitiveBackgroundPropertyBuilders.java | 11 ++++++++++- .../css/style/CalculatedStyle.java | 17 +++++++++-------- .../render/AbstractOutputDevice.java | 4 ++-- .../issue-649-multiple-bg-images-advanced.html | 8 ++++++++ 5 files changed, 31 insertions(+), 13 deletions(-) diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/BackgroundPropertyBuilder.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/BackgroundPropertyBuilder.java index 35ad7352f..addfc0bdb 100644 --- a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/BackgroundPropertyBuilder.java +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/BackgroundPropertyBuilder.java @@ -94,7 +94,7 @@ public List buildDeclarations( } backgroundRepeat = new PropertyDeclaration( - CSSName.BACKGROUND_REPEAT, value, important, origin); + CSSName.BACKGROUND_REPEAT, new PropertyValue(Collections.singletonList(value)), important, origin); } if (PrimitivePropertyBuilders.BACKGROUND_ATTACHMENTS.get(ident.FS_ID)) { @@ -182,7 +182,7 @@ public List buildDeclarations( if (backgroundRepeat == null) { backgroundRepeat = new PropertyDeclaration( - CSSName.BACKGROUND_REPEAT, new PropertyValue(IdentValue.REPEAT), important, origin); + CSSName.BACKGROUND_REPEAT, new PropertyValue(Collections.singletonList(new PropertyValue(IdentValue.REPEAT))), important, origin); } if (backgroundAttachment == null) { diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/PrimitiveBackgroundPropertyBuilders.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/PrimitiveBackgroundPropertyBuilders.java index caf01b673..1242c7aea 100644 --- a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/PrimitiveBackgroundPropertyBuilders.java +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/PrimitiveBackgroundPropertyBuilders.java @@ -304,8 +304,17 @@ private BitSet getAllowed() { } } - public static class BackgroundRepeat extends SingleIdent { + public static class BackgroundRepeat extends MultipleBackgroundValueBuilder { @Override + protected List processValue(CSSName cssName, PropertyValue value) { + checkIdentType(cssName, value); + IdentValue ident = checkIdent(cssName, value); + + checkValidity(cssName, getAllowed(), ident); + + return Collections.singletonList(value); + } + protected BitSet getAllowed() { return PrimitivePropertyBuilders.BACKGROUND_REPEATS; } diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/style/CalculatedStyle.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/style/CalculatedStyle.java index 776458e44..5519bf047 100644 --- a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/style/CalculatedStyle.java +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/style/CalculatedStyle.java @@ -791,8 +791,8 @@ public boolean isCleared() { return ! isIdent(CSSName.CLEAR, IdentValue.NONE); } - public IdentValue getBackgroundRepeat() { - return getIdent(CSSName.BACKGROUND_REPEAT); + public IdentValue getBackgroundRepeat(PropertyValue value) { + return value.getIdentValue(); } public IdentValue getBackgroundAttachment() { @@ -1011,14 +1011,12 @@ public boolean isOverflowVisible() { return valueByName(CSSName.OVERFLOW) == IdentValue.VISIBLE; } - public boolean isHorizontalBackgroundRepeat() { - IdentValue value = getIdent(CSSName.BACKGROUND_REPEAT); - return value == IdentValue.REPEAT_X || value == IdentValue.REPEAT; + public boolean isHorizontalBackgroundRepeat(PropertyValue value) { + return value.getIdentValue() == IdentValue.REPEAT_X || value.getIdentValue() == IdentValue.REPEAT; } - public boolean isVerticalBackgroundRepeat() { - IdentValue value = getIdent(CSSName.BACKGROUND_REPEAT); - return value == IdentValue.REPEAT_Y || value == IdentValue.REPEAT; + public boolean isVerticalBackgroundRepeat(PropertyValue value) { + return value.getIdentValue() == IdentValue.REPEAT_Y || value.getIdentValue() == IdentValue.REPEAT; } public boolean isTopAuto() { @@ -1412,6 +1410,7 @@ public static class BackgroundContainer { public PropertyValue imageGradientOrNone; public BackgroundPosition backgroundPosition; + public PropertyValue backgroundRepeat; } public boolean isLinearGradient(PropertyValue value) { @@ -1432,6 +1431,7 @@ public FSLinearGradient getLinearGradient( public List getBackgroundImages() { List images = ((ListValue) valueByName(CSSName.BACKGROUND_IMAGE)).getValues(); List positions = ((ListValue) valueByName(CSSName.BACKGROUND_POSITION)).getValues(); + List repeats = ((ListValue) valueByName(CSSName.BACKGROUND_REPEAT)).getValues(); assert positions.size() % 2 == 0; @@ -1459,6 +1459,7 @@ public List getBackgroundImages() { // If less background-position values are provided than images, // they must repeat. bg.backgroundPosition = posPairs.get(i % posPairs.size()); + bg.backgroundRepeat = repeats.get(i % repeats.size()); backgrounds.add(bg); } diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/render/AbstractOutputDevice.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/render/AbstractOutputDevice.java index e051517e2..5bc1a1445 100644 --- a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/render/AbstractOutputDevice.java +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/render/AbstractOutputDevice.java @@ -359,8 +359,8 @@ private void drawBgImage( xoff += calcOffset(c, style, position.getHorizontal(), localBGImageContainer.width, imageWidth); yoff += calcOffset(c, style, position.getVertical(), localBGImageContainer.height, imageHeight); - boolean hrepeat = style.isHorizontalBackgroundRepeat(); - boolean vrepeat = style.isVerticalBackgroundRepeat(); + boolean hrepeat = style.isHorizontalBackgroundRepeat(bgImage.backgroundRepeat); + boolean vrepeat = style.isVerticalBackgroundRepeat(bgImage.backgroundRepeat); if (!hrepeat && !vrepeat) { Rectangle imageBounds = new Rectangle(xoff, yoff, (int) imageWidth, (int) imageHeight); diff --git a/openhtmltopdf-examples/src/main/resources/visualtest/html/issue-649-multiple-bg-images-advanced.html b/openhtmltopdf-examples/src/main/resources/visualtest/html/issue-649-multiple-bg-images-advanced.html index 9504b0c1c..f7afc67c1 100644 --- a/openhtmltopdf-examples/src/main/resources/visualtest/html/issue-649-multiple-bg-images-advanced.html +++ b/openhtmltopdf-examples/src/main/resources/visualtest/html/issue-649-multiple-bg-images-advanced.html @@ -34,6 +34,12 @@ background-image: url(../../demos/images/cc0-cat.png), linear-gradient(to top, red, blue); background-position: 20% top; } +#five { + height: 200px; + background-image: url(../../demos/images/cc0-cat.png), url(../../demos/images/flyingsaucer.png), none; + background-repeat: no-repeat, repeat-y; + background-position: right; +} @@ -45,5 +51,7 @@
+
+ From 2dbce9b981c2dbf3708663c3744fb4b85fa47e38 Mon Sep 17 00:00:00 2001 From: danfickle Date: Mon, 15 Feb 2021 16:35:24 +1100 Subject: [PATCH 08/12] #649 Removed broken support for background-attachment: fixed Currently the property is a no-op as the only valid value is scroll. --- .../property/BackgroundPropertyBuilder.java | 33 ++++++++----------- .../PrimitiveBackgroundPropertyBuilders.java | 10 ++++-- .../property/PrimitivePropertyBuilders.java | 3 +- .../css/style/CalculatedStyle.java | 8 ----- .../java/com/openhtmltopdf/layout/Layer.java | 20 ----------- .../render/AbstractOutputDevice.java | 12 +++---- .../com/openhtmltopdf/render/BlockBox.java | 4 --- ...issue-649-multiple-bg-images-advanced.html | 1 + 8 files changed, 28 insertions(+), 63 deletions(-) diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/BackgroundPropertyBuilder.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/BackgroundPropertyBuilder.java index addfc0bdb..9f42fb23b 100644 --- a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/BackgroundPropertyBuilder.java +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/BackgroundPropertyBuilder.java @@ -20,6 +20,7 @@ package com.openhtmltopdf.css.parser.property; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -101,11 +102,11 @@ public List buildDeclarations( if (backgroundAttachment != null) { throw new CSSParseException("A background-attachment value cannot be set twice", -1); } - + backgroundAttachment = new PropertyDeclaration( - CSSName.BACKGROUND_ATTACHMENT, value, important, origin); + CSSName.BACKGROUND_ATTACHMENT, new PropertyValue(Collections.singletonList(value)), important, origin); } - + if (ident == IdentValue.TRANSPARENT) { if (backgroundColor != null) { throw new CSSParseException("A background-color value cannot be set twice", -1); @@ -172,25 +173,24 @@ public List buildDeclarations( backgroundColor = new PropertyDeclaration( CSSName.BACKGROUND_COLOR, new PropertyValue(IdentValue.TRANSPARENT), important, origin); } - + if (backgroundImage == null) { List bgImages = Collections.singletonList(new PropertyValue(IdentValue.NONE)); - + backgroundImage = new PropertyDeclaration( CSSName.BACKGROUND_IMAGE, new PropertyValue(bgImages), important, origin); } - + if (backgroundRepeat == null) { backgroundRepeat = new PropertyDeclaration( CSSName.BACKGROUND_REPEAT, new PropertyValue(Collections.singletonList(new PropertyValue(IdentValue.REPEAT))), important, origin); } - + if (backgroundAttachment == null) { backgroundAttachment = new PropertyDeclaration( - CSSName.BACKGROUND_ATTACHMENT, new PropertyValue(IdentValue.SCROLL), important, origin); - + CSSName.BACKGROUND_ATTACHMENT, new PropertyValue(Collections.singletonList(new PropertyValue(IdentValue.SCROLL))), important, origin); } - + if (backgroundPosition == null) { List v = new ArrayList<>(2); v.add(new PropertyValue(CSSPrimitiveValue.CSS_PERCENTAGE, 0.0f, "0%")); @@ -198,14 +198,9 @@ public List buildDeclarations( backgroundPosition = new PropertyDeclaration( CSSName.BACKGROUND_POSITION, new PropertyValue(v), important, origin); } - - result = new ArrayList<>(5); - result.add(backgroundColor); - result.add(backgroundImage); - result.add(backgroundRepeat); - result.add(backgroundAttachment); - result.add(backgroundPosition); - - return result; + + return Arrays.asList( + backgroundColor, backgroundImage, backgroundRepeat, + backgroundAttachment, backgroundPosition); } } diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/PrimitiveBackgroundPropertyBuilders.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/PrimitiveBackgroundPropertyBuilders.java index 1242c7aea..d2525800d 100644 --- a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/PrimitiveBackgroundPropertyBuilders.java +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/PrimitiveBackgroundPropertyBuilders.java @@ -15,7 +15,6 @@ import com.openhtmltopdf.css.parser.PropertyValue; import com.openhtmltopdf.css.parser.Token; import com.openhtmltopdf.css.parser.property.PrimitivePropertyBuilders.GenericColor; -import com.openhtmltopdf.css.parser.property.PrimitivePropertyBuilders.SingleIdent; import com.openhtmltopdf.css.sheet.PropertyDeclaration; public class PrimitiveBackgroundPropertyBuilders { @@ -304,7 +303,7 @@ private BitSet getAllowed() { } } - public static class BackgroundRepeat extends MultipleBackgroundValueBuilder { + private abstract static class MultipleIdentValue extends MultipleBackgroundValueBuilder { @Override protected List processValue(CSSName cssName, PropertyValue value) { checkIdentType(cssName, value); @@ -315,12 +314,17 @@ protected List processValue(CSSName cssName, PropertyValue value) return Collections.singletonList(value); } + protected abstract BitSet getAllowed(); + } + + public static class BackgroundRepeat extends MultipleIdentValue { + @Override protected BitSet getAllowed() { return PrimitivePropertyBuilders.BACKGROUND_REPEATS; } } - public static class BackgroundAttachment extends SingleIdent { + public static class BackgroundAttachment extends MultipleIdentValue { @Override protected BitSet getAllowed() { return PrimitivePropertyBuilders.BACKGROUND_ATTACHMENTS; diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/PrimitivePropertyBuilders.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/PrimitivePropertyBuilders.java index 380595e87..9d2272322 100644 --- a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/PrimitivePropertyBuilders.java +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/PrimitivePropertyBuilders.java @@ -86,7 +86,8 @@ public class PrimitivePropertyBuilders { // scroll | fixed | inherit public static final BitSet BACKGROUND_ATTACHMENTS = setFor( - new IdentValue[] { IdentValue.SCROLL, IdentValue.FIXED }); + new IdentValue[] { IdentValue.SCROLL + /*, IdentValue.FIXED - removed broken support for fixed in PR#650 by @danfickle */ }); // left | right | top | bottom | center public static final BitSet BACKGROUND_POSITIONS = setFor( diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/style/CalculatedStyle.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/style/CalculatedStyle.java index 5519bf047..f8c461e16 100644 --- a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/style/CalculatedStyle.java +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/style/CalculatedStyle.java @@ -795,14 +795,6 @@ public IdentValue getBackgroundRepeat(PropertyValue value) { return value.getIdentValue(); } - public IdentValue getBackgroundAttachment() { - return getIdent(CSSName.BACKGROUND_ATTACHMENT); - } - - public boolean isFixedBackground() { - return getIdent(CSSName.BACKGROUND_ATTACHMENT) == IdentValue.FIXED; - } - public boolean isInline() { return isIdent(CSSName.DISPLAY, IdentValue.INLINE) && ! (isFloated() || isAbsolute() || isFixed() || isRunning()); diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/layout/Layer.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/layout/Layer.java index 923b7e501..d495ae841 100644 --- a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/layout/Layer.java +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/layout/Layer.java @@ -869,26 +869,6 @@ private PaintingInfo calcPaintingDimension(LayoutContext c) { return result; } - @Deprecated // Not used. - private boolean containsFixedLayer() { - for (Layer child : getChildren()) { - if (child.getMaster().getStyle().isFixed() || child.containsFixedLayer()) { - return true; - } - } - return false; - } - - @Deprecated - public boolean containsFixedContent() { - return _fixedBackground || containsFixedLayer(); - } - - @Deprecated // We not longer support fixed background. - public void setFixedBackground(boolean b) { - _fixedBackground = b; - } - /** * The resulting list should not be modified. */ diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/render/AbstractOutputDevice.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/render/AbstractOutputDevice.java index 5bc1a1445..22b0d1642 100644 --- a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/render/AbstractOutputDevice.java +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/render/AbstractOutputDevice.java @@ -280,7 +280,7 @@ private void paintBackground0( style.getLinearGradient(bgImage.imageGradientOrNone, c, (int) (bgImageContainer.width - border.width()), (int) (bgImageContainer.height - border.height())); if (backgroundLinearGradient != null) { - Dimension xyoff = calcInitialXYOff(bgImageContainer, border, style, c); + Dimension xyoff = calcInitialXYOff(bgImage, bgImageContainer, border, style, c); int xoff = xyoff.width; int yoff = xyoff.height; @@ -310,6 +310,7 @@ private void paintBackground0( } private Dimension calcInitialXYOff( + BackgroundContainer bgImage, Rectangle bgImageContainer, BorderPropertySet border, CalculatedStyle style, @@ -317,10 +318,6 @@ private Dimension calcInitialXYOff( Rectangle localBGImageContainer = bgImageContainer; - if (style.isFixedBackground()) { - localBGImageContainer = c.getViewportRectangle(); - } - int xoff = localBGImageContainer.x; int yoff = localBGImageContainer.y; @@ -341,13 +338,12 @@ private void drawBgImage( FSImage backgroundImage, BackgroundContainer bgImage) { - Dimension xyoff = calcInitialXYOff(bgImageContainer, border, style, c); + Dimension xyoff = calcInitialXYOff(bgImage, bgImageContainer, border, style, c); int xoff = xyoff.width; int yoff = xyoff.height; - Rectangle localBGImageContainer = style.isFixedBackground() ? - c.getViewportRectangle() : bgImageContainer; + Rectangle localBGImageContainer = bgImageContainer; scaleBackgroundImage(c, style, localBGImageContainer, backgroundImage); diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/render/BlockBox.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/render/BlockBox.java index 82db84601..87b034e47 100755 --- a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/render/BlockBox.java +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/render/BlockBox.java @@ -999,10 +999,6 @@ public void layout(LayoutContext c, int contentStart) { c.pushLayer(this); } - if (style.isFixedBackground()) { - c.getRootLayer().setFixedBackground(true); - } - calcClearance(c); if (isRoot() || getStyle().establishesBFC() || isMarginAreaRoot()) { diff --git a/openhtmltopdf-examples/src/main/resources/visualtest/html/issue-649-multiple-bg-images-advanced.html b/openhtmltopdf-examples/src/main/resources/visualtest/html/issue-649-multiple-bg-images-advanced.html index f7afc67c1..2083bd254 100644 --- a/openhtmltopdf-examples/src/main/resources/visualtest/html/issue-649-multiple-bg-images-advanced.html +++ b/openhtmltopdf-examples/src/main/resources/visualtest/html/issue-649-multiple-bg-images-advanced.html @@ -39,6 +39,7 @@ background-image: url(../../demos/images/cc0-cat.png), url(../../demos/images/flyingsaucer.png), none; background-repeat: no-repeat, repeat-y; background-position: right; + background-attachment: scroll; } From 20798b0d60b7b93e57b575a0b31261d12c07be0f Mon Sep 17 00:00:00 2001 From: danfickle Date: Mon, 15 Feb 2021 17:25:13 +1100 Subject: [PATCH 09/12] #649 Support for multiple items in background-size --- .../PrimitiveBackgroundPropertyBuilders.java | 90 +++++++++---------- .../css/style/BackgroundSize.java | 23 ++--- .../css/style/CalculatedStyle.java | 42 ++------- .../render/AbstractOutputDevice.java | 6 +- ...issue-649-multiple-bg-images-advanced.html | 10 +++ 5 files changed, 69 insertions(+), 102 deletions(-) diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/PrimitiveBackgroundPropertyBuilders.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/PrimitiveBackgroundPropertyBuilders.java index d2525800d..c62a479c3 100644 --- a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/PrimitiveBackgroundPropertyBuilders.java +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/PrimitiveBackgroundPropertyBuilders.java @@ -104,72 +104,62 @@ protected List processValue(CSSName cssName, PropertyValue value) public static class BackgroundColor extends GenericColor { } - public static class BackgroundSize extends AbstractPropertyBuilder { - private static final BitSet ALL_ALLOWED = PrimitivePropertyBuilders.setFor(new IdentValue[] { + public static class BackgroundSize extends MultipleBackgroundValueBuilder { + private static final BitSet ALL_ALLOWED = setOf( IdentValue.AUTO, IdentValue.CONTAIN, IdentValue.COVER - }); + ); @Override - public List buildDeclarations(CSSName cssName, List values, int origin, boolean important, boolean inheritAllowed) { - checkValueCount(cssName, 1, 2, values.size()); + protected List processValue(CSSName cssName, PropertyValue first) { + checkIdentLengthOrPercentType(cssName, first); - PropertyValue first = values.get(0); - PropertyValue second = null; - if (values.size() == 2) { - second = values.get(1); - } + if (first.getPrimitiveType() == CSSPrimitiveValue.CSS_IDENT) { + IdentValue firstIdent = checkIdent(cssName, first); + checkValidity(cssName, ALL_ALLOWED, firstIdent); - checkInheritAllowed(first, inheritAllowed); - if (values.size() == 1 && - first.getCssValueType() == CSSValue.CSS_INHERIT) { - return Collections.singletonList( - new PropertyDeclaration(cssName, first, important, origin)); - } + assert firstIdent == IdentValue.AUTO || + firstIdent == IdentValue.COVER || + firstIdent == IdentValue.CONTAIN; + + // Items are expected to always return a pair so just repeat the ident. + return Arrays.asList(first, first); + } else { + assert isLength(first) || first.getPrimitiveType() == CSSPrimitiveValue.CSS_PERCENTAGE; - if (second != null) { - checkInheritAllowed(second, false); + return Arrays.asList(first, new PropertyValue(IdentValue.AUTO)); } + } + @Override + protected List processValues(CSSName cssName, PropertyValue first, PropertyValue second) { checkIdentLengthOrPercentType(cssName, first); - if (second == null) { - if (first.getPrimitiveType() == CSSPrimitiveValue.CSS_IDENT) { - IdentValue firstIdent = checkIdent(cssName, first); - checkValidity(cssName, ALL_ALLOWED, firstIdent); - - if (firstIdent == IdentValue.CONTAIN || firstIdent == IdentValue.COVER) { - return Collections.singletonList( - new PropertyDeclaration(cssName, first, important, origin)); - } else { - return PrimitivePropertyBuilders.createTwoValueResponse(CSSName.BACKGROUND_SIZE, first, first, origin, important); - } - } else { - return PrimitivePropertyBuilders.createTwoValueResponse(CSSName.BACKGROUND_SIZE, first, new PropertyValue(IdentValue.AUTO), origin, important); - } - } else { - checkIdentLengthOrPercentType(cssName, second); + checkIdentLengthOrPercentType(cssName, second); - if (first.getPrimitiveType() == CSSPrimitiveValue.CSS_IDENT) { - IdentValue firstIdent = checkIdent(cssName, first); - if (firstIdent != IdentValue.AUTO) { - throw new CSSParseException("The only ident value allowed here is 'auto'", -1); - } - } else if (first.getFloatValue() < 0.0f) { - throw new CSSParseException(cssName + " values cannot be negative", -1); + if (first.getPrimitiveType() == CSSPrimitiveValue.CSS_IDENT) { + IdentValue firstIdent = checkIdent(cssName, first); + if (firstIdent != IdentValue.AUTO) { + throw new CSSParseException("The only ident value allowed here is 'auto'", -1); } + } else if (first.getFloatValue() < 0.0f) { + throw new CSSParseException(cssName + " values cannot be negative", -1); + } - if (second.getPrimitiveType() == CSSPrimitiveValue.CSS_IDENT) { - IdentValue secondIdent = checkIdent(cssName, second); - if (secondIdent != IdentValue.AUTO) { - throw new CSSParseException("The only ident value allowed here is 'auto'", -1); - } - } else if (second.getFloatValue() < 0.0f) { - throw new CSSParseException(cssName + " values cannot be negative", -1); + if (second.getPrimitiveType() == CSSPrimitiveValue.CSS_IDENT) { + IdentValue secondIdent = checkIdent(cssName, second); + if (secondIdent != IdentValue.AUTO) { + throw new CSSParseException("The only ident value allowed here is 'auto'", -1); } - - return PrimitivePropertyBuilders.createTwoValueResponse(CSSName.BACKGROUND_SIZE, first, second, origin, important); + } else if (second.getFloatValue() < 0.0f) { + throw new CSSParseException(cssName + " values cannot be negative", -1); } + + return Arrays.asList(first, second); } + @Override + protected boolean allowsTwoValueItems() { + return true; + } } public static class BackgroundPosition extends MultipleBackgroundValueBuilder { diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/style/BackgroundSize.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/style/BackgroundSize.java index befb5b918..999cb4b60 100644 --- a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/style/BackgroundSize.java +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/style/BackgroundSize.java @@ -19,22 +19,12 @@ */ package com.openhtmltopdf.css.style; +import com.openhtmltopdf.css.constants.IdentValue; import com.openhtmltopdf.css.parser.PropertyValue; public class BackgroundSize { - private boolean _contain; - private boolean _cover; - private boolean _bothAuto; - - private PropertyValue _width; - private PropertyValue _height; - - - public BackgroundSize(boolean contain, boolean cover, boolean bothAuto) { - _contain = contain; - _cover = cover; - _bothAuto = bothAuto; - } + private final PropertyValue _width; + private final PropertyValue _height; public BackgroundSize(PropertyValue width, PropertyValue height) { _width = width; @@ -42,15 +32,16 @@ public BackgroundSize(PropertyValue width, PropertyValue height) { } public boolean isContain() { - return _contain; + return _width.getIdentValue() == IdentValue.CONTAIN; } public boolean isCover() { - return _cover; + return _width.getIdentValue() == IdentValue.COVER; } public boolean isBothAuto() { - return _bothAuto; + return _width.getIdentValue() == IdentValue.AUTO && + _height.getIdentValue() == IdentValue.AUTO; } public PropertyValue getWidth() { diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/style/CalculatedStyle.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/style/CalculatedStyle.java index f8c461e16..0013bb565 100644 --- a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/style/CalculatedStyle.java +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/style/CalculatedStyle.java @@ -313,39 +313,6 @@ public FSColor getBackgroundColor() { } } - public BackgroundSize getBackgroundSize() { - if (_backgroundSize == null) { - _backgroundSize = createBackgroundSize(); - } - - return _backgroundSize; - } - - private BackgroundSize createBackgroundSize() { - FSDerivedValue value = valueByName(CSSName.BACKGROUND_SIZE); - if (value instanceof IdentValue) { - IdentValue ident = (IdentValue)value; - if (ident == IdentValue.COVER) { - return new BackgroundSize(false, true, false); - } else if (ident == IdentValue.CONTAIN) { - return new BackgroundSize(true, false, false); - } - } else { - ListValue valueList = (ListValue)value; - List values = valueList.getValues(); - boolean firstAuto = values.get(0).getIdentValue() == IdentValue.AUTO; - boolean secondAuto = values.get(1).getIdentValue() == IdentValue.AUTO; - - if (firstAuto && secondAuto) { - return new BackgroundSize(false, false, true); - } else { - return new BackgroundSize(values.get(0), values.get(1)); - } - } - - throw new RuntimeException("internal error"); - } - public List getCounterReset() { FSDerivedValue value = valueByName(CSSName.COUNTER_RESET); @@ -1402,6 +1369,7 @@ public static class BackgroundContainer { public PropertyValue imageGradientOrNone; public BackgroundPosition backgroundPosition; + public BackgroundSize backgroundSize; public PropertyValue backgroundRepeat; } @@ -1424,14 +1392,21 @@ public List getBackgroundImages() { List images = ((ListValue) valueByName(CSSName.BACKGROUND_IMAGE)).getValues(); List positions = ((ListValue) valueByName(CSSName.BACKGROUND_POSITION)).getValues(); List repeats = ((ListValue) valueByName(CSSName.BACKGROUND_REPEAT)).getValues(); + List sizes = ((ListValue) valueByName(CSSName.BACKGROUND_SIZE)).getValues(); assert positions.size() % 2 == 0; + assert sizes.size() % 2 == 0; List posPairs = new ArrayList<>(positions.size() / 2); for (int i = 0; i < positions.size(); i += 2) { posPairs.add(new BackgroundPosition(positions.get(i), positions.get(i + 1))); } + List sizePairs = new ArrayList<>(sizes.size() / 2); + for (int i = 0; i < sizes.size(); i += 2) { + sizePairs.add(new BackgroundSize(sizes.get(i), sizes.get(i + 1))); + } + List backgrounds = new ArrayList<>(images.size()); for (int i = 0; i < images.size(); i++) { @@ -1451,6 +1426,7 @@ public List getBackgroundImages() { // If less background-position values are provided than images, // they must repeat. bg.backgroundPosition = posPairs.get(i % posPairs.size()); + bg.backgroundSize = sizePairs.get(i % sizePairs.size()); bg.backgroundRepeat = repeats.get(i % repeats.size()); backgrounds.add(bg); diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/render/AbstractOutputDevice.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/render/AbstractOutputDevice.java index 22b0d1642..fef62765b 100644 --- a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/render/AbstractOutputDevice.java +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/render/AbstractOutputDevice.java @@ -345,7 +345,7 @@ private void drawBgImage( Rectangle localBGImageContainer = bgImageContainer; - scaleBackgroundImage(c, style, localBGImageContainer, backgroundImage); + scaleBackgroundImage(c, style, localBGImageContainer, backgroundImage, bgImage); float imageWidth = backgroundImage.getWidth(); float imageHeight = backgroundImage.getHeight(); @@ -444,8 +444,8 @@ private int calcOffset(CssContext c, CalculatedStyle style, PropertyValue value, } } - private void scaleBackgroundImage(CssContext c, CalculatedStyle style, Rectangle backgroundContainer, FSImage image) { - BackgroundSize backgroundSize = style.getBackgroundSize(); + private void scaleBackgroundImage(CssContext c, CalculatedStyle style, Rectangle backgroundContainer, FSImage image, BackgroundContainer bgImage) { + BackgroundSize backgroundSize = bgImage.backgroundSize; if (! backgroundSize.isBothAuto()) { if (backgroundSize.isCover() || backgroundSize.isContain()) { diff --git a/openhtmltopdf-examples/src/main/resources/visualtest/html/issue-649-multiple-bg-images-advanced.html b/openhtmltopdf-examples/src/main/resources/visualtest/html/issue-649-multiple-bg-images-advanced.html index 2083bd254..eb2122a69 100644 --- a/openhtmltopdf-examples/src/main/resources/visualtest/html/issue-649-multiple-bg-images-advanced.html +++ b/openhtmltopdf-examples/src/main/resources/visualtest/html/issue-649-multiple-bg-images-advanced.html @@ -41,6 +41,14 @@ background-position: right; background-attachment: scroll; } +#six { + height: 200px; + background-image: url(../../demos/images/cc0-cat.png), url(../../demos/images/flyingsaucer.png), none; + background-repeat: no-repeat; + background-position: left, right; + background-attachment: scroll; + background-size: cover, 25% 70%; +} @@ -54,5 +62,7 @@
+
+ From 9532d6c41c40e44745220bace2b2c30ca21edab2 Mon Sep 17 00:00:00 2001 From: danfickle Date: Tue, 16 Feb 2021 20:42:26 +1100 Subject: [PATCH 10/12] #649 Advanced test for multiple background images --- .../issue-649-multiple-bg-images-advanced.pdf | Bin 0 -> 18494 bytes .../issue-649-multiple-bg-images-advanced.html | 11 ++++++++++- .../VisualRegressionTest.java | 1 - 3 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 openhtmltopdf-examples/src/main/resources/visualtest/expected/issue-649-multiple-bg-images-advanced.pdf diff --git a/openhtmltopdf-examples/src/main/resources/visualtest/expected/issue-649-multiple-bg-images-advanced.pdf b/openhtmltopdf-examples/src/main/resources/visualtest/expected/issue-649-multiple-bg-images-advanced.pdf new file mode 100644 index 0000000000000000000000000000000000000000..799829dc6b6039876c4207a9eeb0a21bcb2bff19 GIT binary patch literal 18494 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 literal 0 HcmV?d00001 diff --git a/openhtmltopdf-examples/src/main/resources/visualtest/html/issue-649-multiple-bg-images-advanced.html b/openhtmltopdf-examples/src/main/resources/visualtest/html/issue-649-multiple-bg-images-advanced.html index eb2122a69..0685c7ec9 100644 --- a/openhtmltopdf-examples/src/main/resources/visualtest/html/issue-649-multiple-bg-images-advanced.html +++ b/openhtmltopdf-examples/src/main/resources/visualtest/html/issue-649-multiple-bg-images-advanced.html @@ -2,7 +2,7 @@ @@ -64,5 +71,7 @@
+
+ diff --git a/openhtmltopdf-examples/src/test/java/com/openhtmltopdf/visualregressiontests/VisualRegressionTest.java b/openhtmltopdf-examples/src/test/java/com/openhtmltopdf/visualregressiontests/VisualRegressionTest.java index e8c3d78ed..2a14480e7 100644 --- a/openhtmltopdf-examples/src/test/java/com/openhtmltopdf/visualregressiontests/VisualRegressionTest.java +++ b/openhtmltopdf-examples/src/test/java/com/openhtmltopdf/visualregressiontests/VisualRegressionTest.java @@ -1387,7 +1387,6 @@ public void testIssue649MultipleBgImages() throws IOException { * Tests that the other background-* properties allow multiple values. */ @Test - @Ignore // WIP public void testIssue649MultipleBgImagesAdvanced() throws IOException { assertTrue(vt.runTest("issue-649-multiple-bg-images-advanced")); } From 8984de66b84aa3e6e1be5aba7574b9f1a4e56082 Mon Sep 17 00:00:00 2001 From: danfickle Date: Tue, 16 Feb 2021 20:54:12 +1100 Subject: [PATCH 11/12] #649 Test that multiple bg images can be used in page at-rule --- .../issue-649-multiple-bg-images-page-box.pdf | Bin 0 -> 15307 bytes ...issue-649-multiple-bg-images-page-box.html | 31 ++++++++++++++++++ .../VisualRegressionTest.java | 8 +++++ 3 files changed, 39 insertions(+) create mode 100644 openhtmltopdf-examples/src/main/resources/visualtest/expected/issue-649-multiple-bg-images-page-box.pdf create mode 100644 openhtmltopdf-examples/src/main/resources/visualtest/html/issue-649-multiple-bg-images-page-box.html diff --git a/openhtmltopdf-examples/src/main/resources/visualtest/expected/issue-649-multiple-bg-images-page-box.pdf b/openhtmltopdf-examples/src/main/resources/visualtest/expected/issue-649-multiple-bg-images-page-box.pdf new file mode 100644 index 0000000000000000000000000000000000000000..4342b7917375cbe2a868db3e598585355ace6ea8 GIT binary patch literal 15307 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 + + + + +
+ + diff --git a/openhtmltopdf-examples/src/test/java/com/openhtmltopdf/visualregressiontests/VisualRegressionTest.java b/openhtmltopdf-examples/src/test/java/com/openhtmltopdf/visualregressiontests/VisualRegressionTest.java index 2a14480e7..6e3e75bde 100644 --- a/openhtmltopdf-examples/src/test/java/com/openhtmltopdf/visualregressiontests/VisualRegressionTest.java +++ b/openhtmltopdf-examples/src/test/java/com/openhtmltopdf/visualregressiontests/VisualRegressionTest.java @@ -1391,6 +1391,14 @@ public void testIssue649MultipleBgImagesAdvanced() throws IOException { assertTrue(vt.runTest("issue-649-multiple-bg-images-advanced")); } + /** + * Tests that multiple background images can be used in a page at-rule. + */ + @Test + public void testIssue649MultipleBgImagesPageBox() throws IOException { + assertTrue(vt.runTest("issue-649-multiple-bg-images-page-box")); + } + // TODO: // + Elements that appear just on generated overflow pages. // + content property (page counters, etc) From 661f0e0cfa771fefa17a449b66df90c5f796e25b Mon Sep 17 00:00:00 2001 From: danfickle Date: Tue, 16 Feb 2021 22:19:53 +1100 Subject: [PATCH 12/12] #649 Link to web doc for background props from source This might work if we maintain it. --- .../property/BackgroundPropertyBuilder.java | 3 +++ .../PrimitiveBackgroundPropertyBuilders.java | 3 +++ .../css/style/CalculatedStyle.java | 8 ++++++ .../java/com/openhtmltopdf/util/WebDoc.java | 25 +++++++++++++++++++ .../openhtmltopdf/util/WebDocLocations.java | 5 ++++ 5 files changed, 44 insertions(+) create mode 100644 openhtmltopdf-core/src/main/java/com/openhtmltopdf/util/WebDoc.java create mode 100644 openhtmltopdf-core/src/main/java/com/openhtmltopdf/util/WebDocLocations.java diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/BackgroundPropertyBuilder.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/BackgroundPropertyBuilder.java index 9f42fb23b..95735dd38 100644 --- a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/BackgroundPropertyBuilder.java +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/BackgroundPropertyBuilder.java @@ -31,7 +31,10 @@ import com.openhtmltopdf.css.parser.FSRGBColor; import com.openhtmltopdf.css.parser.PropertyValue; import com.openhtmltopdf.css.sheet.PropertyDeclaration; +import com.openhtmltopdf.util.WebDoc; +import com.openhtmltopdf.util.WebDocLocations; +@WebDoc(WebDocLocations.CSS_BACKGROUND_PROPERTIES) public class BackgroundPropertyBuilder extends AbstractPropertyBuilder { // [<'background-color'> || <'background-image'> || <'background-repeat'> || // <'background-attachment'> || <'background-position'>] | inherit diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/PrimitiveBackgroundPropertyBuilders.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/PrimitiveBackgroundPropertyBuilders.java index c62a479c3..aac40791c 100644 --- a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/PrimitiveBackgroundPropertyBuilders.java +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/parser/property/PrimitiveBackgroundPropertyBuilders.java @@ -16,7 +16,10 @@ import com.openhtmltopdf.css.parser.Token; import com.openhtmltopdf.css.parser.property.PrimitivePropertyBuilders.GenericColor; import com.openhtmltopdf.css.sheet.PropertyDeclaration; +import com.openhtmltopdf.util.WebDoc; +import com.openhtmltopdf.util.WebDocLocations; +@WebDoc(WebDocLocations.CSS_BACKGROUND_PROPERTIES) public class PrimitiveBackgroundPropertyBuilders { private static BitSet setOf(IdentValue... val) { return PrimitivePropertyBuilders.setFor(val); diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/style/CalculatedStyle.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/style/CalculatedStyle.java index 0013bb565..51ea6fb22 100644 --- a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/style/CalculatedStyle.java +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/css/style/CalculatedStyle.java @@ -21,6 +21,7 @@ package com.openhtmltopdf.css.style; import java.awt.Cursor; +import java.lang.annotation.Documented; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -55,6 +56,8 @@ import com.openhtmltopdf.render.FSFontMetrics; import com.openhtmltopdf.render.RenderingContext; import com.openhtmltopdf.util.LogMessageId; +import com.openhtmltopdf.util.WebDoc; +import com.openhtmltopdf.util.WebDocLocations; import com.openhtmltopdf.util.XRLog; import com.openhtmltopdf.util.XRRuntimeException; @@ -1388,6 +1391,11 @@ public FSLinearGradient getLinearGradient( return new FSLinearGradient(this, value.getFunction(), boxWidth, boxHeight, cssContext); } + /** + * Gets the values of the background properties and combines in a list + * of BackgroundContainer values. + */ + @WebDoc(WebDocLocations.CSS_BACKGROUND_PROPERTIES) public List getBackgroundImages() { List images = ((ListValue) valueByName(CSSName.BACKGROUND_IMAGE)).getValues(); List positions = ((ListValue) valueByName(CSSName.BACKGROUND_POSITION)).getValues(); diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/util/WebDoc.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/util/WebDoc.java new file mode 100644 index 000000000..cb092cb0c --- /dev/null +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/util/WebDoc.java @@ -0,0 +1,25 @@ +package com.openhtmltopdf.util; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + + +/** + * Lets us specify a url where the type, method, etc + * is documented. Specified as an annotation so we can use + * IDE features to find instances and maybe tooling can use it too. + */ +@Retention(SOURCE) +@Target({ TYPE, FIELD, METHOD, CONSTRUCTOR }) +public @interface WebDoc { + /** + * A url where docs can be found. + */ + public String value(); +} diff --git a/openhtmltopdf-core/src/main/java/com/openhtmltopdf/util/WebDocLocations.java b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/util/WebDocLocations.java new file mode 100644 index 000000000..ab9a3ac38 --- /dev/null +++ b/openhtmltopdf-core/src/main/java/com/openhtmltopdf/util/WebDocLocations.java @@ -0,0 +1,5 @@ +package com.openhtmltopdf.util; + +public class WebDocLocations { + public static final String CSS_BACKGROUND_PROPERTIES = "https://github.com/danfickle/openhtmltopdf/wiki/Big-CSS-reference#background-properties"; +}