/*
* Copyright 2015 Skynav, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY SKYNAV, INC. AND ITS CONTRIBUTORS “AS IS” AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL SKYNAV, INC. OR ITS CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.skynav.ttv.verifier.imsc;
import java.util.List;
import java.util.Map;
import javax.xml.namespace.QName;
import org.xml.sax.Locator;
import com.skynav.ttv.model.Model;
import com.skynav.ttv.model.imsc1.ebuttd.MultiRowAlign;
import com.skynav.ttv.model.ttml1.ttd.WritingMode;
import com.skynav.ttv.model.value.Length;
import com.skynav.ttv.model.value.TextOutline;
import com.skynav.ttv.util.Location;
import com.skynav.ttv.util.Reporter;
import com.skynav.ttv.verifier.VerifierContext;
import com.skynav.ttv.verifier.imsc.style.MultiRowAlignVerifier;
import com.skynav.ttv.verifier.smpte.ST20522010StyleVerifier;
import com.skynav.ttv.verifier.util.Lengths;
import com.skynav.ttv.verifier.util.MixedUnitsTreatment;
import com.skynav.ttv.verifier.util.NegativeTreatment;
import com.skynav.ttv.verifier.util.Outline;
import static com.skynav.ttv.model.imsc.IMSC1.Constants.*;
public class IMSC1StyleVerifier extends ST20522010StyleVerifier {
public static final QName forcedDisplayAttributeName = new QName(NAMESPACE_IMSC_STYLING,"forcedDisplay");
public static final QName linePaddingAttributeName = new QName(NAMESPACE_EBUTT_STYLING,"linePadding");
public static final QName multiRowAlignAttributeName = new QName(NAMESPACE_EBUTT_STYLING,"multiRowAlign");
private static final Object[][] styleAccessorMap = new Object[][] {
{
multiRowAlignAttributeName,
"MultiRowAlign",
MultiRowAlign.class,
MultiRowAlignVerifier.class,
Integer.valueOf(APPLIES_TO_P),
Boolean.FALSE,
Boolean.FALSE,
MultiRowAlign.AUTO,
MultiRowAlign.AUTO.value(),
},
};
public IMSC1StyleVerifier(Model model) {
super(model);
}
@Override
protected void populateAccessors(Map<QName, StyleAccessor> accessors) {
super.populateAccessors(accessors);
populateAccessors(accessors, styleAccessorMap);
}
@Override
public boolean isNegativeLengthPermitted(QName eltName, QName styleName) {
return false;
}
@Override
public boolean isLengthUnitsPermitted(QName eltName, QName styleName, Length.Unit units) {
if (units == Length.Unit.Cell)
return styleName.equals(linePaddingAttributeName);
else
return true;
}
@Override
protected boolean verifyAttributeItem(Object content, Locator locator, StyleAccessor sa, VerifierContext context) {
if (!super.verifyAttributeItem(content, locator, sa, context))
return false;
else {
boolean failed = false;
String profile = (String) context.getResourceState(getModel().makeResourceStateName("profile"));
if (profile == null)
profile = PROFILE_TEXT_ABSOLUTE;
QName name = sa.getStyleName();
if (name.equals(extentAttributeName)) {
failed = !verifyExtentOrOriginAttributeItem(content, locator, sa, context);
} else if (name.equals(originAttributeName)) {
failed = !verifyExtentOrOriginAttributeItem(content, locator, sa, context);
} else if (profile.equals(PROFILE_TEXT_ABSOLUTE)) {
if (!verifyAttributePermittedInTextProfile(content, locator, sa, context)) {
Reporter reporter = context.getReporter();
reporter.logError(reporter.message(locator,
"*KEY*", "Attribute ''{0}'' prohibited on ''{1}'' in {2} text profile.",
name, context.getBindingElementName(content), getModel().getName()));
failed = true;
} else {
if (name.equals(fontSizeAttributeName))
failed = !verifyFontSizeAttributeItem(content, locator, sa, context);
else if (name.equals(lineHeightAttributeName))
failed = !verifyLineHeightAttributeItem(content, locator, sa, context);
else if (name.equals(textOutlineAttributeName))
failed = !verifyTextOutlineAttributeItem(content, locator, sa, context);
}
} else if (profile.equals(PROFILE_IMAGE_ABSOLUTE)) {
if (!verifyAttributePermittedInImageProfile(content, locator, sa, context)) {
Reporter reporter = context.getReporter();
reporter.logError(reporter.message(locator,
"*KEY*", "Attribute ''{0}'' prohibited on ''{1}'' in {2} image profile.",
name, context.getBindingElementName(content), getModel().getName()));
failed = true;
} else {
if (name.equals(writingModeAttributeName))
failed = !verifyWritingModeAttributeItem(content, locator, sa, context);
}
}
return !failed;
}
}
private boolean verifyExtentOrOriginAttributeItem(Object content, Locator locator, StyleAccessor sa, VerifierContext context) {
boolean failed = false;
Object value = sa.getStyleValue(content);
QName name = sa.getStyleName();
if (value != null) {
assert value instanceof String;
String s = (String) value;
Location location = new Location(content, context.getBindingElementName(content), name, locator);
Integer[] minMax = new Integer[] { 2, 2 };
Object[] treatments = new Object[] { NegativeTreatment.Allow, MixedUnitsTreatment.Allow };
List<Length> lengths = new java.util.ArrayList<Length>();
if (Lengths.isLengths(s, location, context, minMax, treatments, lengths)) {
for (int i = 0, n = lengths.size(); i < n; ++i) {
Length l = lengths.get(i);
if (l != null) {
Length.Unit u = l.getUnits();
if ((u != Length.Unit.Pixel) && (u != Length.Unit.Percentage)) {
Reporter reporter = context.getReporter();
reporter.logError(reporter.message(locator,
"*KEY*", "Prohibited unit ''{0}'' used in length component ''{1}'' on {2}.", u, l, name));
failed = true;
}
}
}
}
}
return !failed;
}
private boolean verifyAttributePermittedInImageProfile(Object content, Locator locator, StyleAccessor sa, VerifierContext context) {
Object value = sa.getStyleValue(content);
if (value != null) {
QName name = sa.getStyleName();
if (name.equals(colorAttributeName))
return false;
else if (name.equals(directionAttributeName))
return false;
else if (name.equals(displayAlignAttributeName))
return false;
else if (name.equals(fontFamilyAttributeName))
return false;
else if (name.equals(fontSizeAttributeName))
return false;
else if (name.equals(fontStyleAttributeName))
return false;
else if (name.equals(fontWeightAttributeName))
return false;
else if (name.equals(lineHeightAttributeName))
return false;
else if (name.equals(paddingAttributeName))
return false;
else if (name.equals(textAlignAttributeName))
return false;
else if (name.equals(textDecorationAttributeName))
return false;
else if (name.equals(textOutlineAttributeName))
return false;
else if (name.equals(unicodeBidiAttributeName))
return false;
else if (name.equals(wrapOptionAttributeName))
return false;
}
return true;
}
private boolean verifyAttributePermittedInTextProfile(Object content, Locator locator, StyleAccessor sa, VerifierContext context) {
return true;
}
private boolean verifyFontSizeAttributeItem(Object content, Locator locator, StyleAccessor sa, VerifierContext context) {
Object value = sa.getStyleValue(content);
QName name = sa.getStyleName();
if (value != null) {
assert value instanceof String;
String s = (String) value;
Location location = new Location(content, context.getBindingElementName(content), name, locator);
Integer[] minMax = new Integer[] { 1, 2 };
Object[] treatments = new Object[] { NegativeTreatment.Error, MixedUnitsTreatment.Error };
List<Length> lengths = new java.util.ArrayList<Length>();
if (Lengths.isLengths(s, location, context, minMax, treatments, lengths)) {
if (lengths.size() > 1) {
Length w = lengths.get(0);
Length h = lengths.get(1);
if (!w.equals(h)) {
Reporter reporter = context.getReporter();
reporter.logError(reporter.message(locator, "*KEY*", "Anamorphic font size ''{0}'' prohibited.", s));
return false;
}
}
}
}
return true;
}
private boolean verifyLineHeightAttributeItem(Object content, Locator locator, StyleAccessor sa, VerifierContext context) {
// use of 'normal' (either explicitly or by initial value assignment) cannot be performed here,
// but must be performed post-ISD transformation; see TTXV for this test logic
return true;
}
private boolean verifyTextOutlineAttributeItem(Object content, Locator locator, StyleAccessor sa, VerifierContext context) {
Object value = sa.getStyleValue(content);
QName name = sa.getStyleName();
if (value != null) {
assert value instanceof String;
String s = (String) value;
Location location = new Location(content, context.getBindingElementName(content), name, locator);
TextOutline[] outline = new TextOutline[1];
if (Outline.isOutline(s, location, context, outline)) {
assert outline.length > 0;
if (outline[0] != null) {
Length b = outline[0].getBlur();
if ((b != null) && (b.getValue() > 0)) {
Reporter reporter = context.getReporter();
reporter.logError(reporter.message(locator, "*Key*", "Non-zero blur ''{0}'' prohibited.", s));
return false;
}
}
}
}
return true;
}
private boolean verifyWritingModeAttributeItem(Object content, Locator locator, StyleAccessor sa, VerifierContext context) {
boolean failed = false;
String profile = (String) context.getResourceState(getModel().makeResourceStateName("profile"));
if (profile == null)
profile = PROFILE_TEXT_ABSOLUTE;
Object value = sa.getStyleValue(content);
QName name = sa.getStyleName();
if (value != null) {
assert value instanceof WritingMode;
WritingMode wm = (WritingMode) value;
if (profile.equals(PROFILE_IMAGE_ABSOLUTE)) {
if (isVertical(wm)) {
Reporter reporter = context.getReporter();
reporter.logError(reporter.message(locator,
"*Key*", "Prohibited value ''{0}'' on ''{1}'' in {2} image profile.", wm.value(), name, getModel().getName()));
failed = true;
}
}
}
return !failed;
}
private static final boolean isVertical(WritingMode wm) {
if (wm == WritingMode.LRTB)
return true;
else if (wm == WritingMode.RLTB)
return true;
else if (wm == WritingMode.TB)
return true;
else
return false;
}
}