/*
* Copyright (c) 2006 Matthew Hall and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Matthew Hall - initial API and implementation
*/
package org.eclipse.nebula.paperclips.core;
import org.eclipse.nebula.paperclips.core.internal.util.Util;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Device;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Transform;
/**
* A decorator print that scales it's target larger or smaller.
* <p>
* <em>Note</em>: On Windows, this class depends on a bugfix available as of
* Eclipse build 3.2, release candidate 3 (2006-04-28). Prior to this release,
* using ScalePrint triggers the bug, causing the document to scale very large
* on paper. This bug manifests itself only on paper, not with on-screen
* viewing.
*
* @author Matthew Hall
*/
public class ScalePrint implements Print {
final Print target;
final Double scale;
/**
* Constructs a ScalePrint which scales down it's target to print at it's
* preferred size. This constructor is equivalent to calling new
* ScalePrint(target, null).
*
* @param target
* the print to scale down.
*/
public ScalePrint(Print target) {
this(target, null);
}
/**
* Constructs a ScalePrint which scales it's target by the given factor.
*
* @param target
* @param scale
* the scale factor (must be >0). A value of 2.0 draws at double
* the size, and a value of 0.5 draws at half the size. A null
* value automatically scales down so the target is rendered at
* it's preferred size.
*/
public ScalePrint(Print target, Double scale) {
Util.notNull(target);
if (scale != null && !(scale.doubleValue() > 0))
PaperClips.error(SWT.ERROR_INVALID_ARGUMENT,
"Scale " + scale + " must be > 0"); //$NON-NLS-1$ //$NON-NLS-2$
this.target = target;
this.scale = scale;
}
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((scale == null) ? 0 : scale.hashCode());
result = prime * result + ((target == null) ? 0 : target.hashCode());
return result;
}
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ScalePrint other = (ScalePrint) obj;
if (scale == null) {
if (other.scale != null)
return false;
} else if (!scale.equals(other.scale))
return false;
if (target == null) {
if (other.target != null)
return false;
} else if (!target.equals(other.target))
return false;
return true;
}
/**
* Returns the print being scaled.
*
* @return the print being scaled.
*/
public Print getTarget() {
return target;
}
/**
* Returns the scale by which the target will be scaled, or null (indicating
* automatic scale down to fit).
*
* @return the scale by which the target will be scaled, or null (indicating
* automatic scale down to fit).
*/
public Double getScale() {
return scale;
}
public PrintIterator iterator(Device device, GC gc) {
return new ScaleIterator(this, device, gc);
}
}
class ScaleIterator implements PrintIterator {
private final Device device;
private final PrintIterator target;
private final Double scale;
private final Point minimumSize;
private final Point preferredSize;
ScaleIterator(ScalePrint print, Device device, GC gc) {
Util.notNull(print, device, gc);
this.device = device;
this.target = print.target.iterator(device, gc);
this.scale = print.scale;
Point min = target.minimumSize();
Point pref = target.preferredSize();
if (scale == null) { // auto-scale
minimumSize = new Point(1, 1);
preferredSize = pref;
} else { // specific scale
double s = scale.doubleValue();
minimumSize = new Point((int) Math.ceil(min.x * s), (int) Math
.ceil(min.y * s));
preferredSize = new Point((int) Math.ceil(pref.x * s), (int) Math
.ceil(pref.y * s));
}
}
private ScaleIterator(ScaleIterator that) {
this.device = that.device;
this.target = that.target.copy();
this.scale = that.scale;
this.minimumSize = that.minimumSize;
this.preferredSize = that.preferredSize;
}
public Point minimumSize() {
return minimumSize;
}
public Point preferredSize() {
return preferredSize;
}
public boolean hasNext() {
return target.hasNext();
}
public PrintPiece next(int width, int height) {
// Find out what scale we're going to iterate at.
double scale;
Point pref = target.preferredSize();
if (this.scale == null)
scale = Math.min(Math.min((double) width / (double) pref.x,
(double) height / (double) pref.y), 1.0);
else
scale = this.scale.doubleValue();
// Calculate the width and height to be passed to the target.
final int scaledWidth = (int) Math.ceil(width / scale);
final int scaledHeight = (int) Math.ceil(height / scale);
PrintPiece target = PaperClips.next(this.target, scaledWidth,
scaledHeight);
if (target == null)
return null;
return new ScalePiece(device, target, scale, width, height);
}
public PrintIterator copy() {
return new ScaleIterator(this);
}
}
final class ScalePiece implements PrintPiece {
private final Device device;
private final PrintPiece target;
private final double scale;
private final Point size;
private Transform oldTransform;
private Transform transform;
ScalePiece(Device device, PrintPiece target, double scale, int maxWidth,
int maxHeight) {
Util.notNull(device, target);
this.device = device;
this.target = target;
this.scale = scale;
Point targetSize = target.getSize();
this.size = new Point(Math.min((int) Math.ceil(targetSize.x * scale),
maxWidth), Math.min((int) Math.ceil(targetSize.y * scale),
maxHeight));
}
public Point getSize() {
return new Point(size.x, size.y);
}
private Transform getOldTransform() {
if (oldTransform == null)
oldTransform = new Transform(device);
return oldTransform;
}
private Transform getTransform() {
if (transform == null)
transform = new Transform(device);
return transform;
}
public void paint(GC gc, int x, int y) {
Transform oldTransform = getOldTransform();
gc.getTransform(oldTransform);
Transform transform = getTransform();
gc.getTransform(transform);
transform.translate(x, y);
transform.scale((float) scale, (float) scale);
gc.setTransform(transform);
target.paint(gc, 0, 0);
gc.setTransform(oldTransform);
}
public void dispose() {
if (oldTransform != null) {
oldTransform.dispose();
oldTransform = null;
}
if (transform != null) {
transform.dispose();
transform = null;
}
target.dispose();
}
}