package org.xmind.ui.internal.print.multipage;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.geometry.Insets;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.printing.PrinterData;
import org.eclipse.swt.widgets.Display;
import org.xmind.gef.GEF;
import org.xmind.gef.IGraphicalViewer;
import org.xmind.gef.image.FigureRenderer;
import org.xmind.gef.image.IExportSourceProvider;
import org.xmind.gef.ui.editor.IGraphicalEditorPage;
import org.xmind.gef.util.Properties;
import org.xmind.ui.internal.print.PrintConstants;
import org.xmind.ui.internal.print.PrintUtils;
import org.xmind.ui.mindmap.GhostShellProvider;
import org.xmind.ui.mindmap.IMindMap;
import org.xmind.ui.mindmap.IMindMapViewer;
import org.xmind.ui.mindmap.MindMapExportViewer;
import org.xmind.ui.mindmap.MindMapUI;
import org.xmind.ui.mindmap.MindMapViewerExportSourceProvider;
import org.xmind.ui.resources.ColorUtils;
public class PrintMultipagePreviewImageCreator {
private static final String IMG_BG_COLOR = "#ffffff"; //$NON-NLS-1$
private static class MindMapViewerPrintSourceProvider
extends MindMapViewerExportSourceProvider {
private IDialogSettings settings;
public MindMapViewerPrintSourceProvider(IGraphicalViewer viewer,
IDialogSettings settings) {
super(viewer);
this.settings = settings;
}
public MindMapViewerPrintSourceProvider(IGraphicalViewer viewer,
int margins, IDialogSettings settings) {
super(viewer, margins);
this.settings = settings;
}
@Override
public IFigure[] getContents() {
return collectContents();
}
private IFigure[] collectContents() {
List<IFigure> figures = new ArrayList<IFigure>(3);
collectContents(figures);
return figures.toArray(new IFigure[figures.size()]);
}
@Override
protected void collectContents(List<IFigure> figures) {
if (settings != null
&& !settings.getBoolean(PrintConstants.NO_BACKGROUND)) {
figures.add(getViewer().getLayer(GEF.LAYER_BACKGROUND));
}
figures.add(getViewer().getLayer(GEF.LAYER_CONTENTS));
figures.add(getViewer().getLayer(MindMapUI.LAYER_TITLE));
}
}
private Display display;
private IGraphicalEditorPage page;
private IMindMap mindmap;
private IDialogSettings settings;
private IGraphicalViewer exportViewer;
private IExportSourceProvider sourceProvider;
private Image fullImage;
private Image[][] singleImages;
private boolean isSourceImageValid;
private List<Image> images = new ArrayList<Image>();
public PrintMultipagePreviewImageCreator(Display display,
IGraphicalEditorPage page, IMindMap sourceMindmap,
IDialogSettings settings) {
this.display = display;
this.page = page;
this.mindmap = sourceMindmap;
this.settings = settings;
display.asyncExec(new Runnable() {
public void run() {
initSource();
}
});
}
private void initSource() {
Properties properties = new Properties();
properties.set(IMindMapViewer.VIEWER_GRADIENT, Boolean.FALSE);
//set plus minus visibility
boolean plusVisible = getBoolean(settings, PrintConstants.PLUS_VISIBLE,
PrintConstants.DEFAULT_PLUS_VISIBLE);
boolean minusVisible = getBoolean(settings,
PrintConstants.MINUS_VISIBLE,
PrintConstants.DEFAULT_MINUS_VISIBLE);
properties.set(IMindMapViewer.PLUS_VISIBLE, plusVisible);
properties.set(IMindMapViewer.MINUS_VISIBLE, minusVisible);
GhostShellProvider shell = new GhostShellProvider(display);
exportViewer = new MindMapExportViewer(shell, mindmap, properties);
sourceProvider = new MindMapViewerPrintSourceProvider(exportViewer, 0,
settings);
}
private boolean getBoolean(IDialogSettings settings, String key,
boolean defaultValue) {
boolean value = defaultValue;
if (settings.get(key) != null) {
value = settings.getBoolean(key);
}
return value;
}
private Image getFullImage() {
if (!isSourceImageValid && fullImage != null) {
fullImage.dispose();
fullImage = null;
}
if (fullImage == null || fullImage.isDisposed()) {
fullImage = createFullImage();
}
return fullImage;
}
private Image createFullImage() {
final org.eclipse.draw2d.geometry.Rectangle bounds = PrintMultipageUtils
.getSheetFigureBounds(page, mindmap);
final Image image = new Image(display, bounds.width, bounds.height);
final GC gc = new GC(image);
try {
Display.getDefault().syncExec(new Runnable() {
public void run() {
FigureRenderer render = new FigureRenderer();
render.setFigures(sourceProvider.getContents());
int margin = PrintMultipageUtils
.getMargin(sourceProvider.getSourceArea());
render.setBounds(new org.eclipse.draw2d.geometry.Rectangle(
sourceProvider.getSourceArea())
.expand(new Insets(margin)));
render.setScale(1);
render.render(gc, new Point(0, 0));
}
});
} finally {
gc.dispose();
}
isSourceImageValid = true;
return image;
}
public Image createPrintPreviewSingleImage() {
final int leftMarginPixel = PrintConstants.toPixel(getDouble(
PrintConstants.LEFT_MARGIN, PrintConstants.DEFAULT_MARGIN));
int rightMarginPixel = PrintConstants.toPixel(getDouble(
PrintConstants.RIGHT_MARGIN, PrintConstants.DEFAULT_MARGIN));
final int topMarginPixel = PrintConstants.toPixel(getDouble(
PrintConstants.TOP_MARGIN, PrintConstants.DEFAULT_MARGIN));
int bottomMarginPixel = PrintConstants.toPixel(getDouble(
PrintConstants.BOTTOM_MARGIN, PrintConstants.DEFAULT_MARGIN));
final int headerHeight = PrintUtils.getHeaderHeight(settings,
PrintConstants.DEFAULT_DPI);
int footerHeight = PrintUtils.getBottomHeight(settings,
PrintConstants.DEFAULT_DPI);
//Calculate the actual needed printed content of the each page according to the default paper size(A4)
//Then use the print content to fit different actual paper size
int orientation = getInteger(PrintConstants.ORIENTATION,
PrintConstants.DEFAULT_ORIENTATION);
int perPageWidth = orientation == PrinterData.LANDSCAPE
? PrintConstants.PAGE_LENGTH : PrintConstants.PAGE_SHORT;
int usefulPerPageWidth = perPageWidth - leftMarginPixel
- rightMarginPixel;
int perPageHeight = orientation == PrinterData.PORTRAIT
? PrintConstants.PAGE_LENGTH : PrintConstants.PAGE_SHORT;
int usefulPerPageHeight = perPageHeight - topMarginPixel
- bottomMarginPixel - headerHeight - footerHeight;
final org.eclipse.draw2d.geometry.Rectangle bounds = PrintMultipageUtils
.getSheetFigureBounds(page, mindmap);
double widthRatio = (double) usefulPerPageWidth / bounds.width;
double heightRatio = (double) usefulPerPageHeight / bounds.height;
final double ratio = Math.min(widthRatio, heightRatio);
final int actualWidth = (int) (usefulPerPageWidth / ratio);
final int actualHeight = (int) (usefulPerPageHeight / ratio);
final Image previewSingleImage = new Image(display,
(int) (perPageWidth / ratio), (int) (perPageHeight / ratio));
images.add(previewSingleImage);
final GC gc = new GC(previewSingleImage);
try {
Display.getDefault().syncExec(new Runnable() {
public void run() {
FigureRenderer render = new FigureRenderer();
render.setFigures(sourceProvider.getContents());
int margin = PrintMultipageUtils
.getMargin(sourceProvider.getSourceArea());
int widthPadding = (actualWidth - bounds.width) / 2;
int heightPadding = (actualHeight - bounds.height) / 2;
render.setBounds(new org.eclipse.draw2d.geometry.Rectangle(
sourceProvider.getSourceArea())
.expand(new Insets(margin))
.expand(new Insets(heightPadding,
widthPadding, heightPadding,
widthPadding)));
render.setScale(1);
gc.setClipping(
new Rectangle((int) (leftMarginPixel / ratio),
(int) ((topMarginPixel + headerHeight)
/ ratio),
actualWidth, actualHeight));
render.render(gc,
new Point((int) (leftMarginPixel
/ ratio),
(int) ((topMarginPixel + headerHeight) / ratio)));
}
});
} finally {
gc.dispose();
}
return previewSingleImage;
}
public Image createPrintPreviewDetailedImage() {
if (!checkImage()) {
return null;
}
Image[][] images = getSingleImages2();
int perWidth = images[0][0].getBounds().width;
int width = perWidth * images[0].length;
int perHeight = images[0][0].getBounds().height;
int height = perHeight * images.length;
Image detailedImage = new Image(images[0][0].getDevice(), width,
height);
this.images.add(detailedImage);
GC gc = new GC(detailedImage);
try {
for (int i = 0; i < images.length; i++) {
Image[] imageArr = images[i];
int y = i * perHeight;
for (int j = 0; j < imageArr.length; j++) {
Image image = imageArr[j];
int x = j * perWidth;
gc.drawImage(image, x, y);
}
}
} finally {
gc.dispose();
}
return detailedImage;
}
public Image[] getSingleImages() {
if (!checkImage()) {
return new Image[0];
}
Image[][] images = getSingleImages2();
//translate from image[][] to image[]
Image[] images2 = new Image[images.length * images[0].length];
for (int i = 0; i < images.length; i++) {
for (int j = 0; j < images[0].length; j++) {
images2[i * images[0].length + j] = images[i][j];
}
}
return images2;
}
private Image[][] getSingleImages2() {
if (singleImages == null || singleImages[0] == null
|| singleImages[0][0] == null
|| singleImages[0][0].isDisposed()) {
if (singleImages != null) {
for (Image[] images : singleImages) {
if (images != null) {
for (Image image : images) {
if (image != null) {
image.dispose();
}
}
}
}
}
singleImages = createSingleImages();
}
return singleImages;
}
//create detailed single images
private Image[][] createSingleImages() {
org.eclipse.draw2d.geometry.Rectangle bounds = PrintMultipageUtils
.getSheetFigureBounds(page, mindmap);
int sourceWidth = bounds.width;
int sourceHeight = bounds.height;
int leftMarginPixel = PrintConstants.toPixel(getDouble(
PrintConstants.LEFT_MARGIN, PrintConstants.DEFAULT_MARGIN));
int rightMarginPixel = PrintConstants.toPixel(getDouble(
PrintConstants.RIGHT_MARGIN, PrintConstants.DEFAULT_MARGIN));
int topMarginPixel = PrintConstants.toPixel(getDouble(
PrintConstants.TOP_MARGIN, PrintConstants.DEFAULT_MARGIN));
int bottomMarginPixel = PrintConstants.toPixel(getDouble(
PrintConstants.BOTTOM_MARGIN, PrintConstants.DEFAULT_MARGIN));
int headerHeight = PrintUtils.getHeaderHeight(settings,
PrintConstants.DEFAULT_DPI);
int footerHeight = PrintUtils.getBottomHeight(settings,
PrintConstants.DEFAULT_DPI);
//Calculate the actual needed printed content of the each page according to the default paper size(A4)
//Then use the print content to fit different actual paper size
int orientation = getInteger(PrintConstants.ORIENTATION,
PrintConstants.DEFAULT_ORIENTATION);
int perPageWidth = orientation == PrinterData.LANDSCAPE
? PrintConstants.PAGE_LENGTH : PrintConstants.PAGE_SHORT;
int usefulPerPageWidth = perPageWidth - leftMarginPixel
- rightMarginPixel;
int perPageHeight = orientation == PrinterData.PORTRAIT
? PrintConstants.PAGE_LENGTH : PrintConstants.PAGE_SHORT;
int usefulPerPageHeight = perPageHeight - topMarginPixel
- bottomMarginPixel - headerHeight - footerHeight;
int widthPages = getInteger(PrintConstants.WIDTH_PAGES, 1);
int heightPages = getInteger(PrintConstants.HEIGHT_PAGES, 1);
boolean isAspectRatio = settings
.getBoolean(PrintConstants.ASPECT_RATIO_LOCKED);
boolean fullWidth = !settings.getBoolean(PrintConstants.FILL_HEIGHT);
if (!isAspectRatio) {
double fillWidthratio = (double) usefulPerPageWidth * widthPages
/ sourceWidth;
double fillHeightRatio = (double) usefulPerPageHeight * heightPages
/ sourceHeight;
fullWidth = (fillWidthratio <= fillHeightRatio) ? true : false;
}
double ratio = fullWidth
? ((double) usefulPerPageWidth * widthPages / sourceWidth)
: ((double) usefulPerPageHeight * heightPages / sourceHeight);
//The actual needed printed content of the each page calculated by the default paper size(A4)
int usefulPerPageWidthByRatio = (int) (usefulPerPageWidth / ratio);
int usefulPerPageHeightByRatio = (int) (usefulPerPageHeight / ratio);
int usefulWidthPages = widthPages;
int usefulHeightPages = heightPages;
if (fullWidth) {
usefulHeightPages = sourceHeight / usefulPerPageHeightByRatio;
usefulHeightPages = (sourceHeight % usefulPerPageHeightByRatio == 0)
? usefulHeightPages : usefulHeightPages + 1;
} else {
usefulWidthPages = sourceWidth / usefulPerPageWidthByRatio;
usefulWidthPages = (sourceWidth % usefulPerPageWidthByRatio == 0)
? usefulWidthPages : usefulWidthPages + 1;
}
Image fullImage = getFullImage();
Image[][] images = new Image[heightPages][widthPages];
//create single images
for (int j = 0; j < heightPages; j++) {
int y0 = j * usefulPerPageHeightByRatio;
int height = sourceHeight - y0;
height = Math.min(height, usefulPerPageHeightByRatio);
for (int i = 0; i < widthPages; i++) {
int x0 = i * usefulPerPageWidthByRatio;
int width = sourceWidth - x0;
width = Math.min(width, usefulPerPageWidthByRatio);
Image image = new Image(display, (int) (perPageWidth / ratio),
(int) (perPageHeight / ratio));
this.images.add(image);
GC gc = new GC(image);
try {
//draw image background
Rectangle imageBounds = image.getBounds();
gc.setBackground(ColorUtils.getColor(IMG_BG_COLOR));
gc.fillRectangle(0, 0, imageBounds.width - 1,
imageBounds.height - 1);
int leftMarginByRatio = (int) (leftMarginPixel / ratio);
int topMarginByRation = (int) ((topMarginPixel
+ headerHeight) / ratio);
if (i < usefulWidthPages && j < usefulHeightPages) {
//draw image content
gc.drawImage(fullImage, x0, y0, width, height,
leftMarginByRatio, topMarginByRation, width,
height);
}
} finally {
gc.dispose();
}
images[j][i] = image;
}
}
return images;
}
//create full image with dash line
public Image createPrintPreviewRoughImage(int controlWidth,
int controlHeight) {
org.eclipse.draw2d.geometry.Rectangle bounds = PrintMultipageUtils
.getSheetFigureBounds(page, mindmap);
int sourceWidth = bounds.width;
int sourceHeight = bounds.height;
int leftMarginPixel = PrintConstants.toPixel(getDouble(
PrintConstants.LEFT_MARGIN, PrintConstants.DEFAULT_MARGIN));
int rightMarginPixel = PrintConstants.toPixel(getDouble(
PrintConstants.RIGHT_MARGIN, PrintConstants.DEFAULT_MARGIN));
int topMarginPixel = PrintConstants.toPixel(getDouble(
PrintConstants.TOP_MARGIN, PrintConstants.DEFAULT_MARGIN));
int bottomMarginPixel = PrintConstants.toPixel(getDouble(
PrintConstants.BOTTOM_MARGIN, PrintConstants.DEFAULT_MARGIN));
int headerHeight = PrintUtils.getHeaderHeight(settings,
PrintConstants.DEFAULT_DPI);
int footerHeight = PrintUtils.getBottomHeight(settings,
PrintConstants.DEFAULT_DPI);
//Calculate the actual needed printed content of the each page according to the default paper size(A4)
//Then use the print content to fit different actual paper size
int orientation = getInteger(PrintConstants.ORIENTATION,
PrintConstants.DEFAULT_ORIENTATION);
int perPageWidth = orientation == PrinterData.LANDSCAPE
? PrintConstants.PAGE_LENGTH : PrintConstants.PAGE_SHORT;
int usefulPerPageWidth = perPageWidth - leftMarginPixel
- rightMarginPixel;
int perPageHeight = orientation == PrinterData.PORTRAIT
? PrintConstants.PAGE_LENGTH : PrintConstants.PAGE_SHORT;
int usefulPerPageHeight = perPageHeight - topMarginPixel
- bottomMarginPixel - headerHeight - footerHeight;
int widthPages = getInteger(PrintConstants.WIDTH_PAGES, 1);
int heightPages = getInteger(PrintConstants.HEIGHT_PAGES, 1);
boolean isAspectRatio = settings
.getBoolean(PrintConstants.ASPECT_RATIO_LOCKED);
boolean fullWidth = !settings.getBoolean(PrintConstants.FILL_HEIGHT);
if (!isAspectRatio) {
double fillWidthratio = (double) usefulPerPageWidth * widthPages
/ sourceWidth;
double fillHeightRatio = (double) usefulPerPageHeight * heightPages
/ sourceHeight;
fullWidth = (fillWidthratio <= fillHeightRatio) ? true : false;
}
double ratio = fullWidth
? ((double) usefulPerPageWidth * widthPages / sourceWidth)
: ((double) usefulPerPageHeight * heightPages / sourceHeight);
//The actual needed printed content of the each page calculated by the default paper size(A4)
int usefulPerPageWidthByRatio = (int) (usefulPerPageWidth / ratio);
int usefulPerPageHeightByRatio = (int) (usefulPerPageHeight / ratio);
int usefulWidthPages = widthPages;
int usefulHeightPages = heightPages;
if (fullWidth) {
usefulHeightPages = sourceHeight / usefulPerPageHeightByRatio;
usefulHeightPages = (sourceHeight % usefulPerPageHeightByRatio == 0)
? usefulHeightPages : usefulHeightPages + 1;
} else {
usefulWidthPages = sourceWidth / usefulPerPageWidthByRatio;
usefulWidthPages = (sourceWidth % usefulPerPageWidthByRatio == 0)
? usefulWidthPages : usefulWidthPages + 1;
}
Image fullImage = getFullImage();
Image image = new Image(display,
(int) (usefulPerPageWidthByRatio) * widthPages,
(int) (usefulPerPageHeightByRatio) * heightPages);
images.add(image);
GC gc = new GC(image);
try {
//draw image background
Rectangle imageBounds = image.getBounds();
gc.setBackground(ColorUtils.getColor(IMG_BG_COLOR));
gc.fillRectangle(0, 0, imageBounds.width - 1,
imageBounds.height - 1);
gc.drawImage(fullImage, 0, 0);
gc.setForeground(ColorUtils.getColor("#90d483")); //$NON-NLS-1$
double showRatio = Math.min(
(double) controlWidth / imageBounds.width,
(double) controlHeight / imageBounds.height);
int lineWidth = (int) (2 / showRatio);
int showTickLength = 2;
int tickLength = Math.max(1, (int) (showTickLength / showRatio));
int showSpaceLength = 4;
int spaceLength = Math.max(1, (int) (showSpaceLength / showRatio));
gc.setLineWidth(lineWidth);
//draw portrait separator dash lines
for (int i = 1; i < widthPages; i++) {
PrintMultipagePreviewImageCreator.drawDashLine(gc,
i * usefulPerPageWidthByRatio, 0,
i * usefulPerPageWidthByRatio, imageBounds.height,
tickLength, spaceLength, lineWidth);
}
//draw landscape separator dash lines
for (int i = 1; i < heightPages; i++) {
PrintMultipagePreviewImageCreator.drawDashLine(gc, 0,
i * usefulPerPageHeightByRatio, imageBounds.width,
i * usefulPerPageHeightByRatio, tickLength, spaceLength,
lineWidth);
}
//draw image bounds(include margin)
gc.setLineWidth(1);
gc.setForeground(ColorUtils.getColor("#c0c0c0")); //$NON-NLS-1$
gc.drawRectangle(0, 0, imageBounds.width - 1,
imageBounds.height - 1);
} finally {
gc.dispose();
}
return image;
}
private double getDouble(String key, double defaultValue) {
try {
return settings.getDouble(key);
} catch (NumberFormatException e) {
}
return defaultValue;
}
private int getInteger(String key, int defaultValue) {
try {
return settings.getInt(key);
} catch (NumberFormatException e) {
}
return defaultValue;
}
public void releaseResource() {
if (images != null) {
Iterator<Image> ite = images.iterator();
while (ite.hasNext()) {
Image image = ite.next();
if (image != null) {
image.dispose();
ite.remove();
image = null;
}
}
images.clear();
}
}
public void dispose() {
releaseResource();
if (fullImage != null) {
fullImage.dispose();
}
}
public boolean checkImage() {
return !isImageLarge(PrintConstants.MAX_IMAGE_SIZE);
}
private boolean isImageLarge(int largeSize) {
org.eclipse.draw2d.geometry.Rectangle bounds = PrintMultipageUtils
.getSheetFigureBounds(page, mindmap);
return bounds.width * bounds.height > largeSize;
}
public void setSourceImageValid(boolean isSourceImageValid) {
this.isSourceImageValid = isSourceImageValid;
}
public void setShowBackground(boolean showBackground) {
settings.put(PrintConstants.NO_BACKGROUND, !showBackground);
}
public void setPlusVisible(boolean plusVisible) {
if (exportViewer != null) {
Properties properties = exportViewer.getProperties();
properties.set(IMindMapViewer.PLUS_VISIBLE, plusVisible);
}
}
public void setMinusVisible(boolean minusVisible) {
if (exportViewer != null) {
Properties properties = exportViewer.getProperties();
properties.set(IMindMapViewer.MINUS_VISIBLE, minusVisible);
}
}
/**
* Draw horizontal or vertical dashed line. (Sloping is unsupported)
*
*/
private static void drawDashLine(GC gc, int x1, int y1, int x2, int y2,
int tickLength, int spaceLength, int lineWidth) {
if (x1 != x2 && y1 != y2) {
throw new IllegalArgumentException(
"Must satisfy either 'x1 == x2' or 'y1 == y2'"); //$NON-NLS-1$
}
if (x1 == x2) {
int times = Math.abs(y1 - y2) / (tickLength + spaceLength);
for (int i = 0; i < times; i++) {
gc.drawLine(x1, i * (tickLength + spaceLength), x2,
i * (tickLength + spaceLength) + tickLength);
}
int remainder = Math.abs(y1 - y2) % (tickLength + spaceLength);
if (remainder > 0) {
int length = remainder < tickLength ? remainder : tickLength;
gc.drawLine(x1, times * (tickLength + spaceLength), x2,
times * (tickLength + spaceLength) + length);
}
} else {
int times = Math.abs(x1 - x2) / (tickLength + spaceLength);
for (int i = 0; i < times; i++) {
gc.drawLine(i * (tickLength + spaceLength), y1,
i * (tickLength + spaceLength) + tickLength, y2);
}
int remainder = Math.abs(x1 - x2) % (tickLength + spaceLength);
if (remainder > 0) {
int length = remainder < tickLength ? remainder : tickLength;
gc.drawLine(times * (tickLength + spaceLength), y1,
times * (tickLength + spaceLength) + length, y2);
}
}
}
}