org.primaresearch.web.gwt.client.ui.page.renderer.PageRenderer.java Source code

Java tutorial

Introduction

Here is the source code for org.primaresearch.web.gwt.client.ui.page.renderer.PageRenderer.java

Source

/*
 * Copyright 2014 PRImA Research Lab, University of Salford, United Kingdom
 *
 * 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 org.primaresearch.web.gwt.client.ui.page.renderer;

import java.util.ArrayList;
import java.util.List;

import org.primaresearch.web.gwt.client.page.PageLayoutC;
import org.primaresearch.web.gwt.client.ui.DocumentImageListener;
import org.primaresearch.web.gwt.client.ui.DocumentImageSource;
import org.primaresearch.web.gwt.client.ui.RenderStyles;
import org.primaresearch.web.gwt.client.ui.page.SelectionManager;

import com.google.gwt.canvas.client.Canvas;
import com.google.gwt.canvas.dom.client.Context2d;
import com.google.gwt.canvas.dom.client.CssColor;
import com.google.gwt.dom.client.CanvasElement;
import com.google.gwt.dom.client.ImageElement;
import com.google.gwt.user.client.ui.Image;

/**
 * Basic renderer for a document page that renders the document image.<br>
 * Add renderer plugins for additional layers.
 * 
 * @author Christian Clausner
 *
 */
public class PageRenderer implements DocumentImageListener {

    private Canvas canvas;
    private Canvas imageBuffer;
    private boolean imageBuffered = false;

    private PageLayoutC pageLayout;
    private String pageContentToRender = null;
    private String contentObjectToHighlightFaintly = null;
    private SelectionManager selectionManager;

    private DocumentImageSource imageSource;
    private int width = 0;
    private int height = 0;
    private Context2d context;
    private double zoomFactor = 1.0;
    private RenderStyles renderStyles = DefaultRenderStyles.getInstance();

    private List<RendererPlugin> plugins = new ArrayList<RendererPlugin>();

    /**
     * Constructor
     * @param canvas Drawing canvas
     * @param pageLayout Page layout to render
     * @param selectionManager Page object selection manager (to highlight selected objects for instance)
     * @param imageSource Document image source
     */
    public PageRenderer(Canvas canvas, PageLayoutC pageLayout, SelectionManager selectionManager,
            DocumentImageSource imageSource) {
        this.canvas = canvas;
        imageBuffer = Canvas.createIfSupported();
        this.pageLayout = pageLayout;
        this.selectionManager = selectionManager;
        this.imageSource = imageSource;
        imageSource.addListener(this);
    }

    /**
     * Add renderer plug-in (layer)
     */
    public void addPlugin(RendererPlugin plugin) {
        this.plugins.add(plugin);
    }

    /**
     * Remove renderer plug-in (layer)
     */
    public void removePlugin(RendererPlugin plugin) {
        plugins.remove(plugin);
    }

    /**
     * Redraws everything using the current zoom factor
     */
    public void refresh() {
        refresh(this.zoomFactor);
    }

    /**
     * Redraws everything
     * @param zoom Zoom factor to use
     */
    public synchronized void refresh(double zoom) {
        if (canvas == null)
            return;

        this.context = canvas.getContext2d();

        onZoom(zoom);

        //Draw image      
        try {
            Image image = imageSource.getImage(zoom);
            if (image == null)
                drawBackground();
            else
                drawImage(/*image*/);
        } catch (Exception exc) {
            exc.printStackTrace();
        }

        //Process plugins
        for (int i = 0; i < plugins.size(); i++)
            plugins.get(i).render(this);
    }

    /**
     * Apply new zoom
     * @param zoomFactor Zoom factor to use
     */
    private void onZoom(double zoomFactor) {
        this.zoomFactor = zoomFactor;

        //Update size
        int newWidth = (int) (pageLayout.getWidth() * zoomFactor);
        int newHeight = (int) (pageLayout.getHeight() * zoomFactor);
        if ((newWidth != width || newHeight != height) && newHeight > 0 && newWidth > 0) {
            canvas.setSize(newWidth + "px", newHeight + "px");
            canvas.setCoordinateSpaceWidth(newWidth);
            canvas.setCoordinateSpaceHeight(newHeight);

            width = newWidth;
            height = newHeight;
        }

        context.setTransform(zoomFactor, 0, 0, zoomFactor, 0, 0);
    }

    /**
     * Draws grey background
     */
    private void drawBackground() {
        CssColor liteGrey = CssColor.make(200, 200, 200);
        CssColor darkGrey = CssColor.make(100, 100, 100);

        boolean dark;
        boolean startLineDark = false;
        final int rectSize = 200;
        for (int x = 0; x < canvas.getCoordinateSpaceWidth(); x += rectSize) {
            dark = startLineDark;
            for (int y = 0; y < canvas.getCoordinateSpaceWidth(); y += rectSize) {
                context.setFillStyle(dark ? darkGrey : liteGrey);
                context.fillRect(x, y, x + rectSize, y + rectSize);
                context.fill();
                dark = !dark;
            }
            startLineDark = !startLineDark;
        }
    }

    /**
     * Draws the document image into a buffer
     */
    private void bufferImage() {
        //
        //CC: Comment on using a separate canvas as buffer for the page image:
        //
        //The straightforward approach of directly drawing the image on the main canvas
        //proved to slow in Chrome (though extremely fast in IE).
        //As a workaround we now draw the image on an internal canvas (buffer). 
        //We then draw the buffer canvas on the main canvas. This improves
        //performance in Chrome considerably, but slows down IE a little bit.

        if (imageBuffered)
            return;

        Image image = imageSource.getImage(zoomFactor);
        if (image == null)
            return;

        imageBuffer.setSize(pageLayout.getWidth() + "px", pageLayout.getHeight() + "px");
        imageBuffer.getCanvasElement().setWidth(pageLayout.getWidth());
        imageBuffer.getCanvasElement().setHeight(pageLayout.getHeight());
        imageBuffer.setCoordinateSpaceWidth(pageLayout.getWidth());
        imageBuffer.setCoordinateSpaceHeight(pageLayout.getHeight());

        imageBuffer.getContext2d().drawImage((ImageElement) (image).getElement().cast(), 0, 0);

        imageBuffered = true;
    }

    /**
     * Draws the document image onto the canvas
     */
    private void drawImage(/*Image image*/) {

        //context.putImageData(((ImageElement)image.getElement().cast()), 0, 0);
        //context.drawImage((ImageElement)image.getElement().cast(), 0, 0);
        CanvasElement ce = imageBuffer.getCanvasElement();
        //int w = imageBuffer.getElement().getClientWidth();
        context.drawImage(ce, 0, 0);
    }

    /**
     * Specify which page content objects to render
     * @param pageContentToRender See PageLayoutC.TYPE_ constants)
     */
    public void setPageContentToRender(String pageContentToRender) {
        this.pageContentToRender = pageContentToRender;
    }

    /**
     * Returns the current setting for which page content objects to render
     * @return See PageLayoutC.TYPE_ constants)
     */
    public String getPageContentToRender() {
        return pageContentToRender;
    }

    /**
     * Sets a style provider for rendering page content objects (e.g. fill colour of text region). 
     */
    public void setRenderStyles(RenderStyles renderStyles) {
        this.renderStyles = renderStyles;
    }

    /**
     * Highlights the content object with the given ID
     * @param id Content object ID. Use <code>null</code> to highlight nothing
     */
    public void highlightContentObject(String id) {
        if (id == null && contentObjectToHighlightFaintly == null) //No change
            return;
        if (id == null) { //No highlight
            contentObjectToHighlightFaintly = null;
            refresh(zoomFactor);
            return;
        }
        if (!id.equals(contentObjectToHighlightFaintly)) { //Changed
            contentObjectToHighlightFaintly = id;
            refresh(zoomFactor);
        }
    }

    public PageLayoutC getPageLayout() {
        return pageLayout;
    }

    public String getContentObjectToHighlightFaintly() {
        return contentObjectToHighlightFaintly;
    }

    public SelectionManager getSelectionManager() {
        return selectionManager;
    }

    public Context2d getContext() {
        return context;
    }

    public RenderStyles getRenderStyles() {
        return renderStyles;
    }

    public double getZoomFactor() {
        return zoomFactor;
    }

    public int measureTextWidth(String text, String font) {
        Context2d context = canvas.getContext2d();
        context.setFont(font);
        return (int) context.measureText(text).getWidth();
    }

    @Override
    public void imageLoaded() {
        bufferImage();
        refresh();
    }

}