com.openhtmltopdf.context.ContentFunctionFactory Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of openhtmltopdf-core Show documentation
Show all versions of openhtmltopdf-core Show documentation
Open HTML to PDF is a CSS 2.1 renderer written in Java. This artifact contains the core rendering and layout code.
/*
* {{{ header & license
* Copyright (c) 2006 Wisconsin Court System
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
* }}}
*/
package com.openhtmltopdf.context;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.w3c.dom.css.CSSPrimitiveValue;
import com.openhtmltopdf.css.constants.IdentValue;
import com.openhtmltopdf.css.extend.ContentFunction;
import com.openhtmltopdf.css.parser.FSFunction;
import com.openhtmltopdf.css.parser.PropertyValue;
import com.openhtmltopdf.layout.CounterFunction;
import com.openhtmltopdf.layout.InlineBoxing;
import com.openhtmltopdf.layout.LayoutContext;
import com.openhtmltopdf.render.Box;
import com.openhtmltopdf.render.InlineLayoutBox;
import com.openhtmltopdf.render.InlineText;
import com.openhtmltopdf.render.LineBox;
import com.openhtmltopdf.render.PageBox;
import com.openhtmltopdf.render.RenderingContext;
public class ContentFunctionFactory {
private List _functions = new ArrayList();
{
_functions.add(new PageCounterFunction());
_functions.add(new PagesCounterFunction());
_functions.add(new TargetCounterFunction());
_functions.add(new LeaderFunction());
}
public ContentFunction lookupFunction(LayoutContext c, FSFunction function) {
for (Iterator i = _functions.iterator(); i.hasNext(); ) {
ContentFunction f = (ContentFunction)i.next();
if (f.canHandle(c, function)) {
return f;
}
}
return null;
}
public void registerFunction(ContentFunction function) {
_functions.add(function);
}
private static abstract class PageNumberFunction implements ContentFunction {
public boolean isStatic() {
return false;
}
public String calculate(LayoutContext c, FSFunction function) {
return null;
}
public String getLayoutReplacementText() {
return "999";
}
protected IdentValue getListStyleType(FSFunction function) {
IdentValue result = IdentValue.DECIMAL;
List parameters = function.getParameters();
if (parameters.size() == 2) {
PropertyValue pValue = (PropertyValue)parameters.get(1);
IdentValue iValue = IdentValue.valueOf(pValue.getStringValue());
if (iValue != null) {
result = iValue;
}
}
return result;
}
protected boolean isCounter(FSFunction function, String counterName) {
if (function.getName().equals("counter")) {
List parameters = function.getParameters();
if (parameters.size() == 1 || parameters.size() == 2) {
PropertyValue param = (PropertyValue)parameters.get(0);
if (param.getPrimitiveType() != CSSPrimitiveValue.CSS_IDENT ||
! param.getStringValue().equals(counterName)) {
return false;
}
if (parameters.size() == 2) {
param = (PropertyValue)parameters.get(1);
return param.getPrimitiveType() == CSSPrimitiveValue.CSS_IDENT;
}
return true;
}
}
return false;
}
}
private static class PageCounterFunction extends PageNumberFunction implements ContentFunction {
public String calculate(RenderingContext c, FSFunction function, InlineText text) {
int value = c.getRootLayer().getRelativePageNo(c) + 1;
return CounterFunction.createCounterText(getListStyleType(function), value);
}
public boolean canHandle(LayoutContext c, FSFunction function) {
return c.isPrint() && isCounter(function, "page");
}
}
private static class PagesCounterFunction extends PageNumberFunction implements ContentFunction {
public String calculate(RenderingContext c, FSFunction function, InlineText text) {
int value = c.getRootLayer().getRelativePageCount(c);
return CounterFunction.createCounterText(getListStyleType(function), value);
}
public boolean canHandle(LayoutContext c, FSFunction function) {
return c.isPrint() && isCounter(function, "pages");
}
}
/**
* Partially implements target counter as specified here:
* http://www.w3.org/TR/2007/WD-css3-gcpm-20070504/#cross-references
*/
private static class TargetCounterFunction implements ContentFunction {
public boolean isStatic() {
return false;
}
public String calculate(RenderingContext c, FSFunction function, InlineText text) {
String uri = text.getParent().getElement().getAttribute("href");
if (uri != null && uri.startsWith("#")) {
String anchor = uri.substring(1);
Box target = c.getBoxById(anchor);
if (target != null) {
int pageNo = c.getRootLayer().getRelativePageNo(c, target.getAbsY());
return CounterFunction.createCounterText(IdentValue.DECIMAL, pageNo + 1);
}
}
return "";
}
public String calculate(LayoutContext c, FSFunction function) {
return null;
}
public String getLayoutReplacementText() {
return "999";
}
public boolean canHandle(LayoutContext c, FSFunction function) {
if (c.isPrint() && function.getName().equals("target-counter")) {
List parameters = function.getParameters();
if (parameters.size() == 2 || parameters.size() == 3) {
FSFunction f = ((PropertyValue)parameters.get(0)).getFunction();
if (f == null ||
f.getParameters().size() != 1 ||
f.getParameters().get(0).getPrimitiveType() != CSSPrimitiveValue.CSS_IDENT ||
! f.getParameters().get(0).getStringValue().equals("href")) {
return false;
}
PropertyValue param = (PropertyValue)parameters.get(1);
return param.getPrimitiveType() == CSSPrimitiveValue.CSS_IDENT &&
param.getStringValue().equals("page");
}
}
return false;
}
}
/**
* Partially implements leaders as specified here:
* http://www.w3.org/TR/2007/WD-css3-gcpm-20070504/#leaders
*/
private static class LeaderFunction implements ContentFunction {
public boolean isStatic() {
return false;
}
public String calculate(RenderingContext c, FSFunction function, InlineText text) {
InlineLayoutBox iB = text.getParent();
LineBox lineBox = iB.getLineBox();
// There might be a target-counter function after this function.
// Because the leader should fill up the line, we need the correct
// width and must first compute the target-counter function.
boolean dynamic = false;
Iterator childIterator = lineBox.getChildIterator();
while (childIterator.hasNext()) {
Box child = (Box)childIterator.next();
if (child == iB) {
dynamic = true;
} else if (dynamic && child instanceof InlineLayoutBox) {
((InlineLayoutBox)child).lookForDynamicFunctions(c);
}
}
if (dynamic) {
int totalLineWidth = InlineBoxing.positionHorizontally(c, lineBox, 0);
lineBox.setContentWidth(totalLineWidth);
}
// Get leader value and value width
PropertyValue param = function.getParameters().get(0);
String value = param.getStringValue();
if (param.getPrimitiveType() == CSSPrimitiveValue.CSS_IDENT) {
if (value.equals("dotted")) {
value = ". ";
} else if (value.equals("solid")) {
value = "_";
} else if (value.equals("space")) {
value = " ";
}
}
// Compute value width using 100x string to get more precise width.
// Otherwise there might be a small gap at the right side. This is
// necessary because a TextRenderer usually use double/float for width.
StringBuilder tmp = new StringBuilder(100 * value.length());
for (int i = 0; i < 100; i++) {
tmp.append(value);
}
float valueWidth = c.getTextRenderer().getWidth(c.getFontContext(),
iB.getStyle().getFSFont(c), tmp.toString()) / 100f;
int spaceWidth = c.getTextRenderer().getWidth(c.getFontContext(),
iB.getStyle().getFSFont(c), " ");
// compute leader width and necessary count of values
int leaderWidth = iB.getContainingBlockWidth() - iB.getLineBox().getWidth() + text.getWidth();
int count = (int) ((leaderWidth - (2 * spaceWidth)) / valueWidth);
// build leader string
StringBuilder buf = new StringBuilder(count * value.length() + 2);
buf.append(' ');
for (int i = 0; i < count; i++) {
buf.append(value);
}
buf.append(' ');
String leaderString = buf.toString();
// set left margin to ensure that the leader is right aligned (for TOC)
int leaderStringWidth = c.getTextRenderer().getWidth(c.getFontContext(),
iB.getStyle().getFSFont(c), leaderString);
iB.setMarginLeft(c, leaderWidth - leaderStringWidth);
return leaderString;
}
public String calculate(LayoutContext c, FSFunction function) {
return null;
}
public String getLayoutReplacementText() {
return " . ";
}
public boolean canHandle(LayoutContext c, FSFunction function) {
if (c.isPrint() && function.getName().equals("leader")) {
List parameters = function.getParameters();
if (parameters.size() == 1) {
PropertyValue param = (PropertyValue)parameters.get(0);
return param.getPrimitiveType() == CSSPrimitiveValue.CSS_STRING ||
(param.getPrimitiveType() == CSSPrimitiveValue.CSS_IDENT &&
(param.getStringValue().equals("dotted") ||
param.getStringValue().equals("solid") ||
param.getStringValue().equals("space")));
}
}
return false;
}
}
}