
org.xhtmlrenderer.layout.SharedContext Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of flying-saucer-core Show documentation
Show all versions of flying-saucer-core Show documentation
Flying Saucer is a CSS 2.1 renderer written in Java. This artifact contains the core rendering and layout code as well as Java2D output.
The newest version!
/*
* {{{ header & license
* Copyright (c) 2004, 2005 Joshua Marinacci
*
* 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 org.xhtmlrenderer.layout;
import com.google.errorprone.annotations.CheckReturnValue;
import org.jspecify.annotations.Nullable;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xhtmlrenderer.context.AWTFontResolver;
import org.xhtmlrenderer.context.StyleReference;
import org.xhtmlrenderer.css.style.CalculatedStyle;
import org.xhtmlrenderer.css.style.EmptyStyle;
import org.xhtmlrenderer.css.value.FontSpecification;
import org.xhtmlrenderer.extend.FSCanvas;
import org.xhtmlrenderer.extend.FontContext;
import org.xhtmlrenderer.extend.FontResolver;
import org.xhtmlrenderer.extend.NamespaceHandler;
import org.xhtmlrenderer.extend.OutputDevice;
import org.xhtmlrenderer.extend.ReplacedElementFactory;
import org.xhtmlrenderer.extend.TextRenderer;
import org.xhtmlrenderer.extend.UserAgentCallback;
import org.xhtmlrenderer.layout.breaker.DefaultLineBreakingStrategy;
import org.xhtmlrenderer.layout.breaker.LineBreakingStrategy;
import org.xhtmlrenderer.render.Box;
import org.xhtmlrenderer.render.FSFont;
import org.xhtmlrenderer.render.FSFontMetrics;
import org.xhtmlrenderer.render.RenderingContext;
import org.xhtmlrenderer.simple.extend.FormSubmissionListener;
import org.xhtmlrenderer.swing.Java2DTextRenderer;
import org.xhtmlrenderer.swing.NaiveUserAgent;
import org.xhtmlrenderer.swing.SwingReplacedElementFactory;
import org.xhtmlrenderer.util.XRLog;
import java.awt.*;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import static java.util.Arrays.asList;
import static java.util.Objects.requireNonNull;
/**
* The SharedContext is that which is kept between successive layout and render runs.
*
* @author empty
*/
public final class SharedContext {
private static final Set PAGED_MEDIA_TYPES =
new HashSet<>(asList("print", "projection", "embossed", "handheld", "tv"));
private TextRenderer textRenderer;
private String media;
private UserAgentCallback uac;
private boolean interactive = true;
private final Map idMap = new HashMap<>();
/*
* used to adjust fonts, ems, points, into screen resolution
*/
private float dpi;
private static final int MM__PER__CM = 10;
private static final float CM__PER__IN = 2.54F;
/**
* dpi in a more usable way
*/
private float mm_per_dot;
private static final float DEFAULT_DPI = 72;
private boolean print;
private int dotsPerPixel = 1;
@Nullable
private Map styleMap;
private ReplacedElementFactory replacedElementFactory;
@Nullable
private Rectangle temporaryCanvas;
private LineBreakingStrategy lineBreakingStrategy = new DefaultLineBreakingStrategy();
public SharedContext() {
this(new NaiveUserAgent());
}
public SharedContext(UserAgentCallback userAgent, FontResolver fontResolver,
ReplacedElementFactory replacedElementFactory,
TextRenderer textRenderer,
float dpi, int dotsPerPixel) {
uac = requireNonNull(userAgent);
this.css = new StyleReference(userAgent);
this.fontResolver = requireNonNull(fontResolver);
this.replacedElementFactory = replacedElementFactory;
this.textRenderer = requireNonNull(textRenderer);
media = "screen";
setDPI(dpi);
setDotsPerPixel(dotsPerPixel);
setPrint(true);
setInteractive(false);
}
public SharedContext(UserAgentCallback uac, float dpi, int pixelsPerDot) {
this(uac);
setDPI(dpi);
setDotsPerPixel(pixelsPerDot);
}
public SharedContext(UserAgentCallback uac) {
fontResolver = new AWTFontResolver();
replacedElementFactory = new SwingReplacedElementFactory();
this.media = "screen";
this.uac = requireNonNull(uac);
this.css = new StyleReference(uac);
XRLog.render("Using CSS implementation from: " + getCss().getClass().getName());
this.textRenderer = new Java2DTextRenderer();
try {
setDPI(Toolkit.getDefaultToolkit().getScreenResolution());
} catch (HeadlessException e) {
setDPI(DEFAULT_DPI);
}
}
public SharedContext(UserAgentCallback uac, FontResolver fr, ReplacedElementFactory ref, TextRenderer tr, float dpi) {
fontResolver = requireNonNull(fr);
replacedElementFactory = ref;
this.media = "screen";
this.uac = uac;
this.css = new StyleReference(uac);
XRLog.render("Using CSS implementation from: " + getCss().getClass().getName());
this.textRenderer = requireNonNull(tr);
setDPI(dpi);
setPrint(true);
setInteractive(false);
}
public void setFormSubmissionListener(FormSubmissionListener fsl) {
replacedElementFactory.setFormSubmissionListener(fsl);
}
public LayoutContext newLayoutContextInstance(FontContext fontContext) {
return new LayoutContext(this, fontContext);
}
public RenderingContext newRenderingContextInstance(OutputDevice outputDevice, FontContext fontContext) {
return newRenderingContextInstance(outputDevice, fontContext, null, 0);
}
public RenderingContext newRenderingContextInstance(OutputDevice outputDevice, FontContext fontContext, @Nullable Layer rootLayer, int initialPageNo) {
return new RenderingContext(this, outputDevice, fontContext, rootLayer, initialPageNo);
}
/*
=========== Font stuff ============== */
/**
* Gets the fontResolver attribute of the Context object
*
* @return The fontResolver value
*/
@CheckReturnValue
public FontResolver getFontResolver() {
return fontResolver;
}
public void flushFonts() {
fontResolver.flushCache();
}
private FontResolver fontResolver;
/**
* The media for this context
*/
@CheckReturnValue
public String getMedia() {
return media;
}
private StyleReference css;
private boolean debug_draw_boxes;
private boolean debug_draw_line_boxes;
private boolean debug_draw_inline_boxes;
private boolean debug_draw_font_metrics;
@Nullable
private FSCanvas canvas;
@CheckReturnValue
public TextRenderer getTextRenderer() {
return textRenderer;
}
public boolean debugDrawBoxes() {
return debug_draw_boxes;
}
public boolean debugDrawLineBoxes() {
return debug_draw_line_boxes;
}
public boolean debugDrawInlineBoxes() {
return debug_draw_inline_boxes;
}
public boolean debugDrawFontMetrics() {
return debug_draw_font_metrics;
}
public void setDebug_draw_boxes(boolean debug_draw_boxes) {
this.debug_draw_boxes = debug_draw_boxes;
}
public void setDebug_draw_line_boxes(boolean debug_draw_line_boxes) {
this.debug_draw_line_boxes = debug_draw_line_boxes;
}
public void setDebug_draw_inline_boxes(boolean debug_draw_inline_boxes) {
this.debug_draw_inline_boxes = debug_draw_inline_boxes;
}
public void setDebug_draw_font_metrics(boolean debug_draw_font_metrics) {
this.debug_draw_font_metrics = debug_draw_font_metrics;
}
/*
=========== Selection Management ============== */
public StyleReference getCss() {
return css;
}
public void setCss(StyleReference css) {
this.css = css;
}
@Nullable
@CheckReturnValue
public FSCanvas getCanvas() {
return canvas;
}
public void setCanvas(FSCanvas canvas) {
this.canvas = canvas;
}
public void setTemporaryCanvas(Rectangle rect) {
temporaryCanvas = rect;
}
@Nullable
public Rectangle getFixedRectangle() {
if (getCanvas() == null) {
return temporaryCanvas;
} else {
Rectangle rect = getCanvas().getFixedRectangle();
rect.translate(getCanvas().getX(), getCanvas().getY());
return rect;
}
}
@Nullable
private NamespaceHandler namespaceHandler;
public void setNamespaceHandler(NamespaceHandler nh) {
namespaceHandler = nh;
}
@Nullable
public NamespaceHandler getNamespaceHandler() {
return namespaceHandler;
}
public void addBoxId(String id, Box box) {
idMap.put(id, box);
}
@Nullable
@CheckReturnValue
public Box getBoxById(String id) {
return idMap.get(id);
}
public void removeBoxId(String id) {
idMap.remove(id);
}
public Map getIdMap()
{
return idMap;
}
/**
* Sets the textRenderer attribute of the RenderingContext object
*
* @param textRenderer The new textRenderer value
* @deprecated pass textRenderer to a constructor instead of using setter
*/
@Deprecated
public void setTextRenderer(TextRenderer textRenderer) {
this.textRenderer = requireNonNull(textRenderer);
}
/**
*
* Set the current media type. This is usually something like screen
* or print . See the
* media section of the CSS 2.1 spec for more information on media
* types.
*
* @param media The new media value
*/
public void setMedia(String media) {
this.media = requireNonNull(media);
}
/**
* Gets the uac attribute of the RenderingContext object
*
* @return The uac value
*/
@CheckReturnValue
public UserAgentCallback getUac() {
return uac;
}
public UserAgentCallback getUserAgentCallback() {
return uac;
}
public void setUserAgentCallback(UserAgentCallback userAgentCallback) {
getCss().setUserAgentCallback(userAgentCallback);
uac = requireNonNull(userAgentCallback);
}
/**
* Gets the dPI attribute of the RenderingContext object
*
* @return The dPI value
*/
public float getDPI() {
return dpi;
}
/**
* Sets the effective DPI (Dots Per Inch) of the screen. You should normally
* never need to override the dpi, as it is already set to the system
* default by {@code Toolkit.getDefaultToolkit().getScreenResolution()}
* . You can override the value if you want to scale the fonts for
* accessibility or printing purposes. Currently, the DPI setting only
* affects font sizing.
*
* @param dpi The new dPI value
*/
public void setDPI(float dpi) {
this.dpi = dpi;
mm_per_dot = (CM__PER__IN * MM__PER__CM) / dpi;
}
/**
* Gets the dPI attribute in a more useful form of the RenderingContext object
*
* @return The dPI value
*/
public float getMmPerPx() {
return mm_per_dot;
}
@Nullable
@CheckReturnValue
public FSFont getFont(FontSpecification spec) {
return fontResolver.resolveFont(this, spec);
}
//strike-through offset should always be half of the height of lowercase x...
//and it is defined even for fonts without 'x'!
public float getXHeight(FontContext fontContext, FontSpecification fs) {
FSFont font = fontResolver.resolveFont(this, fs);
FSFontMetrics fm = getTextRenderer().getFSFontMetrics(fontContext, font, " ");
float sto = fm.getStrikethroughOffset();
return fm.getAscent() - 2 * Math.abs(sto) + fm.getStrikethroughThickness();
}
/**
* Gets the baseURL attribute of the RenderingContext object
*
* @return The baseURL value
*/
public String getBaseURL() {
return uac.getBaseURL();
}
/**
* Sets the baseURL attribute of the RenderingContext object
*
* @param url The new baseURL value
*/
public void setBaseURL(@Nullable String url) {
uac.setBaseURL(url);
}
/**
* Returns true if the currently set media type is paged. Currently, returns
* true only for print , projection , and embossed ,
* handheld , and tv . See the media section of the CSS
* 2.1 spec for more information on media types.
*
* @return The paged value
*/
public boolean isPaged() {
return PAGED_MEDIA_TYPES.contains(media);
}
public boolean isInteractive() {
return interactive;
}
public void setInteractive(boolean interactive) {
this.interactive = interactive;
}
public boolean isPrint() {
return print;
}
public void setPrint(boolean print) {
this.print = print;
setMedia(print ? "print" : "screen");
}
/**
*
* Adds or overrides a font mapping, meaning you can associate a particular
* font with a particular string. For example, the following would load a
* font out of the cool.ttf file and associate it with the name CoolFont
* :
*
*
* Font font = Font.createFont(Font.TRUETYPE_FONT,
* new FileInputStream("cool.ttf");
* setFontMapping("CoolFont", font);
*
*
* You could then put the following css in your page
* p { font-family: CoolFont Arial sans-serif; }
*
*
* You can also override existing font mappings, like replacing Arial with
* Helvetica.
*
* @param name The new font name
* @param font The actual Font to map
*/
/*
* add a new font mapping, or replace an existing one
*/
public void setFontMapping(String name, Font font) {
if (fontResolver instanceof AWTFontResolver awtFontResolver) {
awtFontResolver.setFontMapping(name, font);
}
}
/**
* @deprecated pass textRenderer to a constructor instead of using setter
*/
@Deprecated
public void setFontResolver(FontResolver resolver) {
fontResolver = requireNonNull(resolver);
}
public int getDotsPerPixel() {
return dotsPerPixel;
}
public void setDotsPerPixel(int pixelsPerDot) {
this.dotsPerPixel = pixelsPerDot;
}
public CalculatedStyle getStyle(Element e) {
return getStyle(e, false);
}
public CalculatedStyle getStyle(Element e, boolean restyle) {
Map localMap = styleMap;
if (localMap == null) {
localMap = new HashMap<>(1024, 0.75f);
}
CalculatedStyle result = null;
if (! restyle) {
result = localMap.get(e);
}
if (result == null) {
Node parent = e.getParentNode();
CalculatedStyle parentCalculatedStyle;
if (parent instanceof Document) {
parentCalculatedStyle = new EmptyStyle();
} else {
parentCalculatedStyle = getStyle((Element)parent, false);
}
result = parentCalculatedStyle.deriveStyle(getCss().getCascadedStyle(e, restyle));
localMap.put(e, result);
}
styleMap = localMap;
return result;
}
public void reset() {
styleMap = null;
idMap.clear();
replacedElementFactory.reset();
}
public ReplacedElementFactory getReplacedElementFactory() {
return replacedElementFactory;
}
public void setReplacedElementFactory(ReplacedElementFactory ref) {
if (ref == null) {
throw new NullPointerException("replacedElementFactory may not be null");
}
this.replacedElementFactory.reset();
this.replacedElementFactory = ref;
}
public LineBreakingStrategy getLineBreakingStrategy() {
return lineBreakingStrategy;
}
public void setLineBreakingStrategy(LineBreakingStrategy lineBreakingStrategy) {
this.lineBreakingStrategy = lineBreakingStrategy;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy