com.globalmentor.css.CSSTidier Maven / Gradle / Ivy
/*
* Copyright © 1996-2008 GlobalMentor, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.globalmentor.css;
import static com.globalmentor.css.spec.CSS.*;
import java.util.*;
import com.globalmentor.java.Strings;
import com.globalmentor.util.*;
import io.clogr.Clogged;
import org.w3c.dom.css.*;
/**
* Contains several routines for tidying CSS stylesheets.
*
* Currently this class removes non-OEB1 CSS properties.
*
*
* Options:
*
*
* MAKE_FONT_SIZES_RELATIVE
: Change all font sizes to be relative to a base font size. Currently this searches for a p
normal
* selector to use as the base font size. The current implementation assumes there is no hierarchical cascading.
* REMOVE_MS_OFFICE_PROPERTIES
: Removes all properties that begin with mso-
.
*
* @author Garret Wilson
*/
public class CSSTidier implements Clogged { //TODO add a feature to remove empty style declarations
/** Whether font sizes should be made relative to a base font size. */
public static final String MAKE_FONT_SIZES_RELATIVE_OPTION = "makeFontSizesRelative";
/** Default to not making font sizes relative. */
public static final boolean MAKE_FONT_SIZES_RELATIVE_OPTION_DEFAULT = false;
/** Whether Microsoft Office-specific properties should be removed. */
public static final String REMOVE_MS_OFFICE_PROPERTIES_OPTION = "removeMSOfficeProperties";
/** Default to removing MS Office properties. */
public static final boolean REMOVE_MS_OFFICE_PROPERTIES_OPTION_DEFAULT = true;
/** Whether font sizes should be made relative to a base font size. */
private boolean makeFontSizesRelative = MAKE_FONT_SIZES_RELATIVE_OPTION_DEFAULT;
/** @return Whether font sizes should be made relative to a base font size. */
public boolean isMakeFontSizesRelative() {
return makeFontSizesRelative;
}
/**
* Sets whether whether font sizes should be made relative to a base font size.
* @param newMakeFontSizesRelative true
if font sizes should be made relative to a base font size.
*/
public void setMakeFontSizesRelative(final boolean newMakeFontSizesRelative) {
makeFontSizesRelative = newMakeFontSizesRelative;
}
/** Whether Microsoft Office-specific properties should be removed. */
private boolean removeMSOfficeProperties = REMOVE_MS_OFFICE_PROPERTIES_OPTION_DEFAULT;
/** @return Whether Microsoft Office-specific properties should be removed. */
public boolean isRemoveMSOfficeProperties() {
return removeMSOfficeProperties;
}
/**
* Sets whether Microsoft Office-specific properties should be removed.
* @param newRemoveMSOfficeProperties true
if Microsoft Office- specific propertiees should be removed.
*/
public void setRemoveMSOfficeProperties(final boolean newRemoveMSOfficeProperties) {
removeMSOfficeProperties = newRemoveMSOfficeProperties;
}
/**
* Sets the options based on the contents of the option properties.
* @param options The properties which contain the options.
*/
public void setOptions(final Properties options) {
setMakeFontSizesRelative(PropertiesUtilities.getBooleanProperty(options, MAKE_FONT_SIZES_RELATIVE_OPTION, MAKE_FONT_SIZES_RELATIVE_OPTION_DEFAULT));
setRemoveMSOfficeProperties(
PropertiesUtilities.getBooleanProperty(options, REMOVE_MS_OFFICE_PROPERTIES_OPTION, REMOVE_MS_OFFICE_PROPERTIES_OPTION_DEFAULT));
}
/** The base font size, if we've found one, in its original CSS value. */
//TODO fix protected CSSPrimitiveValue baseFontSizeCSSValue=null;
//TODO comment
protected short baseFontSizeCssValueType;
protected float baseFontSizeValue;
/** Default constructor initialized with the default options. */
public CSSTidier() {
}
/**
* Constructs a CSS tidier with a list of options.
* @param options The properties which specify the tidy options.
*/
public CSSTidier(final Properties options) {
setOptions(options); //set the options from the properties
}
/**
* Tidies a CSS stylesheet.
* @param cssStyleSheet The stylesheet to tidy.
*/
public void tidy(final CSSStyleSheet cssStyleSheet) {
final CSSRuleList cssRuleList = cssStyleSheet.getCssRules(); //get the list of CSS rules
if(isMakeFontSizesRelative()) { //if we should make font sizes relative
final CSSPrimitiveValue baseFontSizeCSSValue = getBaseFontSize(cssRuleList); //search the stylesheet for a base font size
if(baseFontSizeCSSValue != null) { //if there is a base font size
baseFontSizeCssValueType = baseFontSizeCSSValue.getCssValueType(); //record the value type of the base font size
baseFontSizeValue = baseFontSizeCSSValue.getFloatValue(baseFontSizeCssValueType); //get the base font size value
} else
//if we couldn't find a base font size
baseFontSizeValue = -1; //show that we couldn't find a base font size
}
final int stylesheetRuleCount = cssRuleList.getLength(); //find out how many rules there are
for(int ruleIndex = 0; ruleIndex < stylesheetRuleCount; ++ruleIndex) { //look at each of the rules
final CSSRule cssRule = cssRuleList.item(ruleIndex); //get this CSS rule
tidy(cssRule); //tidy the CSS rule
}
//TODO del if not needed baseFontSizeCSSValue=null; //release our base font size CSS value, if we've referenced one
}
/**
* Tidies the specified CSS rule.
* @param cssRule The CSS rule to tidy.
*/
protected void tidy(final CSSRule cssRule) {
if(cssRule instanceof CSSStyleRule) { //TODO fix
final CSSStyleRule cssStyleRule = (CSSStyleRule)cssRule; //TODO fix
tidy(cssStyleRule.getStyle()); //tidy the style declaration
}
}
/**
* Tidies the specified CSS style declaration.
* @param cssStyleDeclaration The CSS style declaration to tidy.
*/
protected void tidy(final CSSStyleDeclaration cssStyleDeclaration) {
for(int propertyIndex = 0; propertyIndex < cssStyleDeclaration.getLength();) { //look at each property, checking the number of properties each time because we might delete one, thereby decreasing the number of properties
final String propertyName = cssStyleDeclaration.item(propertyIndex); //get the name of this property
if(shouldRemoveProperty(cssStyleDeclaration, propertyName)) { //if we should remove this property
getLogger().trace("we should remove property");
cssStyleDeclaration.removeProperty(propertyName); //remove this property
continue; //continue without increasing the index, since the next property is now at this index
}
//if we should make font sizes relative, and this is a font size
else if(isMakeFontSizesRelative() && CSS_PROP_FONT_SIZE.equals(propertyName)) {
//TODO del if not needed if(baseFontSizeCSSValue!=null) //if we know the base font size
if(baseFontSizeValue != -1) { //if we know the base font size TODO use a constant or something more robust, maybe
final CSSPrimitiveValue propertyCSSValue = (CSSPrimitiveValue)cssStyleDeclaration.getPropertyCSSValue(propertyName); //get the property with this name TODO we shouldn't really assume this is a primitive value
///TODO del when works if(baseFontSizeCSSValue.getCssValueType()==propertyCSSValue.getCssValueType()) //if both value types are the same
if(baseFontSizeCssValueType == propertyCSSValue.getCssValueType()) { //if both value types are the same
//TODO del when works final float baseFontSize=baseFontSizeCSSValue.getFloatValue(baseFontSizeCSSValue.getCssValueType()); //get the base font size
final float fontSizeValue = propertyCSSValue.getFloatValue(propertyCSSValue.getCssValueType()); //get the current font size
final float relativeSize = Math.round(fontSizeValue / baseFontSizeValue * 100); //get the relative size convert it to a percentage by multiplying by 100, rounding the value TODO use a constant here
propertyCSSValue.setFloatValue(propertyCSSValue.CSS_PERCENTAGE, relativeSize); //change the value to a relative size
}
}
}
++propertyIndex; //look at the next property
}
}
/**
* Searches the stylesheet for a base font size.
* @param cssRuleList The stylesheet's rule list.
* @return The CSS value representing the base font size used in the document, or null
if a base font size can't be found.
*/
protected CSSPrimitiveValue getBaseFontSize(final CSSRuleList cssRuleList) {
final int ruleCount = cssRuleList.getLength(); //find out how many rules there are
for(int ruleIndex = 0; ruleIndex < ruleCount; ++ruleIndex) { //look at each of the rules
final CSSRule cssRule = cssRuleList.item(ruleIndex); //get this CSS rule
if(cssRule instanceof CSSStyleRule) { //TODO fix
final CSSStyleRule cssStyleRule = (CSSStyleRule)cssRule; //TODO fix
final String selectorText = cssStyleRule.getSelectorText(); //get the selector text
//if this selects a normal paragraph TODO fix this; this is highly dependent on HTML and MSWord
if(selectorText.indexOf("p.") >= 0 && Strings.indexOfIgnoreCase(selectorText, "normal") >= 0) {
final CSSStyleDeclaration cssStyleDeclaration = cssStyleRule.getStyle(); //get the style declaration
final int propertyCount = cssStyleDeclaration.getLength(); //get the number of properties
for(int propertyIndex = 0; propertyIndex < propertyCount; ++propertyIndex) { //look at each property
final String propertyName = cssStyleDeclaration.item(propertyIndex); //get the name of this property
if(CSS_PROP_FONT_SIZE.equals(propertyName)) { //if this is the font-size property
final CSSValue propertyCSSValue = cssStyleDeclaration.getPropertyCSSValue(propertyName); //get the property with this name
if(propertyCSSValue.getCssValueType() == propertyCSSValue.CSS_PRIMITIVE_VALUE) { //if this is a primitive value
return (CSSPrimitiveValue)propertyCSSValue; //return this primitive value property as the base font size
}
}
}
}
}
}
return null; //show that we couldn't find a base font size
}
/**
* Determines whether a particular CSS property should be removed.
* @param cssStyleDeclaration The CSS style declaration containing the property
* @param propertyName The name of the property being examined.
* @return {@code true} if the property should be removed and {@code false} if not.
*/
protected boolean shouldRemoveProperty(final CSSStyleDeclaration cssStyleDeclaration, final String propertyName) {
//if we should remove MS Office properties, and this is an MS Office property
if(isRemoveMSOfficeProperties() && propertyName.startsWith("mso-")) //TODO use a constant here
return true; //show that we should remove this property
/*TODO move this to an OEBCSSTidier
if(!OEBCSS.isOEB1CSSProperty(propertyName)) //if this is not an OEB 1.0 CSS property
return true; //remove non-OEB CSS properties
*/
return false; //since we didn't find a reason to, don't remove this property
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy