All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.xhtmlrenderer.layout.LayoutContext Maven / Gradle / Ivy

Go to download

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.

There is a newer version: 9.11.0
Show newest version
/*
 * {{{ header & license
 * Copyright (c) 2004, 2005 Joshua Marinacci, Torbjoern Gannholm
 *
 * 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 org.xhtmlrenderer.context.ContentFunctionFactory;
import org.xhtmlrenderer.context.StyleReference;
import org.xhtmlrenderer.css.constants.CSSName;
import org.xhtmlrenderer.css.constants.IdentValue;
import org.xhtmlrenderer.css.parser.CounterData;
import org.xhtmlrenderer.css.style.CalculatedStyle;
import org.xhtmlrenderer.css.style.CssContext;
import org.xhtmlrenderer.css.value.FontSpecification;
import org.xhtmlrenderer.extend.FSCanvas;
import org.xhtmlrenderer.extend.FontContext;
import org.xhtmlrenderer.extend.NamespaceHandler;
import org.xhtmlrenderer.extend.ReplacedElementFactory;
import org.xhtmlrenderer.extend.TextRenderer;
import org.xhtmlrenderer.extend.UserAgentCallback;
import org.xhtmlrenderer.render.Box;
import org.xhtmlrenderer.render.FSFont;
import org.xhtmlrenderer.render.FSFontMetrics;
import org.xhtmlrenderer.render.MarkerData;
import org.xhtmlrenderer.render.PageBox;

import javax.annotation.Nullable;
import java.awt.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * This class tracks state which changes over the course of a layout run.
 * Generally speaking, if possible, state information should be stored in the box
 * tree and not here.  It also provides pass-though calls to many methods in
 * {@link SharedContext}.
 */
public class LayoutContext implements CssContext {
    private final SharedContext _sharedContext;

    private Layer _rootLayer;

    private StyleTracker _firstLines;
    private StyleTracker _firstLetters;
    private MarkerData _currentMarkerData;

    private LinkedList _bfcs;
    private LinkedList _layers;

    private FontContext _fontContext;

    private final ContentFunctionFactory _contentFunctionFactory = new ContentFunctionFactory();

    private int _extraSpaceTop;
    private int _extraSpaceBottom;

    private final Map _counterContextMap = new HashMap<>();

    private String _pendingPageName;
    private String _pageName;

    private int _noPageBreak;

    private Layer _rootDocumentLayer;
    private PageBox _page;

    private boolean _mayCheckKeepTogether = true;

    private BreakAtLineContext _breakAtLineContext;

    public TextRenderer getTextRenderer() {
        return _sharedContext.getTextRenderer();
    }

    @Override
    public StyleReference getCss() {
        return _sharedContext.getCss();
    }

    public FSCanvas getCanvas() {
        return _sharedContext.getCanvas();
    }

    public Rectangle getFixedRectangle() {
        return _sharedContext.getFixedRectangle();
    }

    public NamespaceHandler getNamespaceHandler() {
        return _sharedContext.getNamespaceHandler();
    }

    //the stuff that needs to have a separate instance for each run.
    LayoutContext(SharedContext sharedContext) {
        _sharedContext = sharedContext;
        _bfcs = new LinkedList<>();
        _layers = new LinkedList<>();

        _firstLines = new StyleTracker();
        _firstLetters = new StyleTracker();
    }

    public void reInit(boolean keepLayers) {
        _firstLines = new StyleTracker();
        _firstLetters = new StyleTracker();
        _currentMarkerData = null;

        _bfcs = new LinkedList<>();

        if (! keepLayers) {
            _rootLayer = null;
            _layers = new LinkedList<>();
        }

        _extraSpaceTop = 0;
        _extraSpaceBottom = 0;
    }

    public LayoutState captureLayoutState() {
        LayoutState result = new LayoutState();

        result.setFirstLines(_firstLines);
        result.setFirstLetters(_firstLetters);
        result.setCurrentMarkerData(_currentMarkerData);

        result.setBFCs(_bfcs);

        if (isPrint()) {
            result.setPageName(getPageName());
            result.setExtraSpaceBottom(getExtraSpaceBottom());
            result.setExtraSpaceTop(getExtraSpaceTop());
            result.setNoPageBreak(getNoPageBreak());
        }

        return result;
    }

    public void restoreLayoutState(LayoutState layoutState) {
        _firstLines = layoutState.getFirstLines();
        _firstLetters = layoutState.getFirstLetters();

        _currentMarkerData = layoutState.getCurrentMarkerData();

        _bfcs = layoutState.getBFCs();

        if (isPrint()) {
            setPageName(layoutState.getPageName());
            setExtraSpaceBottom(layoutState.getExtraSpaceBottom());
            setExtraSpaceTop(layoutState.getExtraSpaceTop());
            setNoPageBreak(layoutState.getNoPageBreak());
        }
    }

    public LayoutState copyStateForRelayout() {
        LayoutState result = new LayoutState();

        result.setFirstLetters(_firstLetters.copyOf());
        result.setFirstLines(_firstLines.copyOf());
        result.setCurrentMarkerData(_currentMarkerData);

        if (isPrint()) {
            result.setPageName(getPageName());
        }

        return result;
    }

    public void restoreStateForRelayout(LayoutState layoutState) {
        _firstLines = layoutState.getFirstLines();
        _firstLetters = layoutState.getFirstLetters();

        _currentMarkerData = layoutState.getCurrentMarkerData();

        if (isPrint()) {
            setPageName(layoutState.getPageName());
        }
    }

    public BlockFormattingContext getBlockFormattingContext() {
        return _bfcs.getLast();
    }

    public void pushBFC(BlockFormattingContext bfc) {
        _bfcs.add(bfc);
    }

    public void popBFC() {
        _bfcs.removeLast();
    }

    public void pushLayer(Box master) {
        Layer layer;

        if (_rootLayer == null) {
            layer = new Layer(master);
            _rootLayer = layer;
        } else {
            Layer parent = getLayer();
            layer = new Layer(parent, master);
            parent.addChild(layer);
        }

        pushLayer(layer);
    }

    public void pushLayer(Layer layer) {
        _layers.add(layer);
    }

    public void popLayer() {
        Layer layer = getLayer();

        layer.finish(this);

        _layers.removeLast();
    }

    public Layer getLayer() {
        return _layers.getLast();
    }

    public Layer getRootLayer() {
        return _rootLayer;
    }

    public void translate(int x, int y) {
        getBlockFormattingContext().translate(x, y);
    }

    /* code to keep track of all the id'd boxes */
    public void addBoxId(String id, Box box) {
        _sharedContext.addBoxId(id, box);
    }

    public void removeBoxId(String id) {
        _sharedContext.removeBoxId(id);
    }

    public boolean isInteractive() {
        return _sharedContext.isInteractive();
    }

    @Override
    public float getMmPerDot() {
        return _sharedContext.getMmPerPx();
    }

    @Override
    public int getDotsPerPixel() {
        return _sharedContext.getDotsPerPixel();
    }

    @Override
    public float getFontSize2D(FontSpecification font) {
        return _sharedContext.getFont(font).getSize2D();
    }

    @Override
    public float getXHeight(FontSpecification parentFont) {
        return _sharedContext.getXHeight(getFontContext(), parentFont);
    }

    @Override
    public FSFont getFont(FontSpecification font) {
        return _sharedContext.getFont(font);
    }

    public UserAgentCallback getUac() {
        return _sharedContext.getUac();
    }

    public boolean isPrint() {
        return _sharedContext.isPrint();
    }

    public StyleTracker getFirstLinesTracker() {
        return _firstLines;
    }

    public StyleTracker getFirstLettersTracker() {
        return _firstLetters;
    }

    public MarkerData getCurrentMarkerData() {
        return _currentMarkerData;
    }

    public void setCurrentMarkerData(MarkerData currentMarkerData) {
        _currentMarkerData = currentMarkerData;
    }

    public ReplacedElementFactory getReplacedElementFactory() {
        return _sharedContext.getReplacedElementFactory();
    }

    public FontContext getFontContext() {
        return _fontContext;
    }

    public void setFontContext(FontContext fontContext) {
        _fontContext = fontContext;
    }

    public ContentFunctionFactory getContentFunctionFactory() {
        return _contentFunctionFactory;
    }

    public SharedContext getSharedContext() {
        return _sharedContext;
    }

    public int getExtraSpaceBottom() {
        return _extraSpaceBottom;
    }

    public void setExtraSpaceBottom(int extraSpaceBottom) {
        _extraSpaceBottom = extraSpaceBottom;
    }

    public int getExtraSpaceTop() {
        return _extraSpaceTop;
    }

    public void setExtraSpaceTop(int extraSpaceTop) {
        _extraSpaceTop = extraSpaceTop;
    }

    public void resolveCounters(CalculatedStyle style, Integer startIndex) {
        //new context for child elements
        CounterContext cc = new CounterContext(style, startIndex);
        _counterContextMap.put(style, cc);
    }

    public void resolveCounters(CalculatedStyle style) {
        resolveCounters(style, null);
    }

    public CounterContext getCounterContext(CalculatedStyle style) {
        return _counterContextMap.get(style);
    }

    @Override
    public FSFontMetrics getFSFontMetrics(FSFont font) {
        return getTextRenderer().getFSFontMetrics(getFontContext(), font, "");
    }

    public class CounterContext {
        private final Map _counters = new HashMap<>();
        /**
         * This is different because it needs to work even when the counter-properties cascade,
         * and it should also logically be redefined on each level (think list-items within list-items)
         */
        private CounterContext _parent;

        /**
         * A CounterContext should really be reflected in the element hierarchy, but CalculatedStyles
         * reflect the ancestor hierarchy just as well and also handles pseudo-elements seamlessly.
         */
        CounterContext(CalculatedStyle style, @Nullable Integer startIndex) {
            // Numbering restarted via 
    if (startIndex != null) { _counters.put("list-item", startIndex); } _parent = _counterContextMap.get(style.getParent()); if (_parent == null) _parent = new CounterContext();//top-level context, above root element //first the explicitly named counters List resets = style.getCounterReset(); if (resets != null) { for (CounterData cd : resets) { _parent.resetCounter(cd); } } List increments = style.getCounterIncrement(); if (increments != null) { for (CounterData cd : increments) { if (!_parent.incrementCounter(cd)) { _parent.resetCounter(new CounterData(cd.getName(), 0)); _parent.incrementCounter(cd); } } } // then the implicit list-item counter if (style.isIdent(CSSName.DISPLAY, IdentValue.LIST_ITEM)) { // Numbering restarted via
  1. if (startIndex != null) { _parent._counters.put("list-item", startIndex); } _parent.incrementListItemCounter(1); } } private CounterContext() { } /** * @return true if a counter was found and incremented */ private boolean incrementCounter(CounterData cd) { if ("list-item".equals(cd.getName())) {//reserved name for list-item counter in CSS3 incrementListItemCounter(cd.getValue()); return true; } else { Integer currentValue = _counters.get(cd.getName()); if (currentValue == null) { if (_parent == null) return false; return _parent.incrementCounter(cd); } else { _counters.put(cd.getName(), currentValue + cd.getValue()); return true; } } } private void incrementListItemCounter(int increment) { Integer currentValue = _counters.get("list-item"); if (currentValue == null) { currentValue = 0; } _counters.put("list-item", currentValue + increment); } private void resetCounter(CounterData cd) { _counters.put(cd.getName(), cd.getValue()); } public int getCurrentCounterValue(String name) { //only the counters of the parent are in scope //_parent is never null for a publicly accessible CounterContext Integer value = _parent.getCounter(name); if (value == null) { _parent.resetCounter(new CounterData(name, 0)); return 0; } else { return value; } } private Integer getCounter(String name) { Integer value = _counters.get(name); if (value != null) return value; if (_parent == null) return null; return _parent.getCounter(name); } public List getCurrentCounterValues(String name) { //only the counters of the parent are in scope //_parent is never null for a publicly accessible CounterContext List values = new ArrayList<>(); _parent.getCounterValues(name, values); if (values.isEmpty()) { _parent.resetCounter(new CounterData(name, 0)); values.add(0); } return values; } private void getCounterValues(String name, List values) { if (_parent != null) _parent.getCounterValues(name, values); Integer value = _counters.get(name); if (value != null) values.add(value); } } public String getPageName() { return _pageName; } public void setPageName(String currentPageName) { _pageName = currentPageName; } public int getNoPageBreak() { return _noPageBreak; } public void setNoPageBreak(int noPageBreak) { _noPageBreak = noPageBreak; } public boolean isPageBreaksAllowed() { return _noPageBreak == 0; } public String getPendingPageName() { return _pendingPageName; } public void setPendingPageName(String pendingPageName) { _pendingPageName = pendingPageName; } public Layer getRootDocumentLayer() { return _rootDocumentLayer; } public void setRootDocumentLayer(Layer rootDocumentLayer) { _rootDocumentLayer = rootDocumentLayer; } public PageBox getPage() { return _page; } public void setPage(PageBox page) { _page = page; } public boolean isMayCheckKeepTogether() { return _mayCheckKeepTogether; } public void setMayCheckKeepTogether(boolean mayKeepTogether) { _mayCheckKeepTogether = mayKeepTogether; } public BreakAtLineContext getBreakAtLineContext() { return _breakAtLineContext; } public void setBreakAtLineContext(BreakAtLineContext breakAtLineContext) { _breakAtLineContext = breakAtLineContext; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy