/*
* Copyright 2015-2016 Igor Maznitsa.
*
* 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 com.igormaznitsa.mindmap.print;
import com.igormaznitsa.mindmap.model.MindMap;
import com.igormaznitsa.mindmap.model.logger.Logger;
import com.igormaznitsa.mindmap.model.logger.LoggerFactory;
import com.igormaznitsa.mindmap.swing.panel.MindMapPanel;
import com.igormaznitsa.mindmap.swing.panel.MindMapPanelConfig;
import com.igormaznitsa.mindmap.swing.panel.utils.Utils;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import javax.annotation.Nonnull;
import com.igormaznitsa.meta.annotation.MustNotContainNull;
import com.igormaznitsa.meta.common.utils.GetUtils;
public class MMDPrint {
private static final Logger LOGGER = LoggerFactory.getLogger(MMDPrint.class);
private final PrintPage[][] pages;
private static final double SMALL_SCALING_THRESHOLD = 0.20d;
private static final double SCALE_RATIO_THRESHOLD = 0.20d;
private static final BufferedImage EMPTY_IMAGE = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
public MMDPrint (@Nonnull final MindMapPanel panel, final int paperWidthInPixels, final int paperHeightInPixels, final double pageZoomFactor) {
LOGGER.info(String.format("Request to prepare print pages for %dx%d with scale %f", paperWidthInPixels, paperHeightInPixels, pageZoomFactor));
if (paperWidthInPixels <= 0 || paperHeightInPixels <= 0) {
this.pages = new PrintPage[0][0];
}
else {
final MindMapPanelConfig cfg = new MindMapPanelConfig(panel.getConfiguration(), false);
cfg.setDrawBackground(false);
cfg.setDropShadow(false);
cfg.setConnectorColor(Color.black);
cfg.setRootBackgroundColor(Color.black);
cfg.setRootTextColor(Color.white);
cfg.setFirstLevelBackgroundColor(Color.lightGray);
cfg.setFirstLevelTextColor(Color.black);
cfg.setOtherLevelBackgroundColor(Color.white);
cfg.setOtherLevelTextColor(Color.black);
cfg.setCollapsatorBorderColor(Color.black);
cfg.setCollapsatorBackgroundColor(Color.white);
cfg.setJumpLinkColor(Color.DARK_GRAY);
cfg.setElementBorderWidth(1.5f);
cfg.setCollapsatorBorderWidth(1.0f);
cfg.setConnectorWidth(2.0f);
cfg.setScale(pageZoomFactor);
final MindMap theModel = new MindMap(panel.getModel(), null);
BufferedImage renderedMap = GetUtils.ensureNonNull(MindMapPanel.renderMindMapAsImage(theModel, cfg, false),EMPTY_IMAGE);
if (renderedMap.getWidth() > paperWidthInPixels || renderedMap.getHeight() > paperHeightInPixels) {
// split to pages
int pagesHorz = Math.max(1, renderedMap.getWidth() / paperWidthInPixels + (renderedMap.getWidth() % paperWidthInPixels != 0 ? 1 : 0));
int pagesVert = Math.max(1, renderedMap.getHeight() / paperHeightInPixels + (renderedMap.getHeight() % paperHeightInPixels != 0 ? 1 : 0));
final boolean couldBeScaledForX = (renderedMap.getWidth() % paperWidthInPixels) <= Math.round(paperWidthInPixels * SMALL_SCALING_THRESHOLD);
final boolean couldBeScaledForY = (renderedMap.getHeight() % paperHeightInPixels) <= Math.round(paperHeightInPixels * SMALL_SCALING_THRESHOLD);
if (couldBeScaledForX || couldBeScaledForY) {
final double currentRatio = (double) Math.min(renderedMap.getWidth(), renderedMap.getHeight()) / (double) Math.max(renderedMap.getWidth(), renderedMap.getHeight());
final int potentialPageWidth = Math.min(renderedMap.getWidth(), Math.max(1, pagesHorz - (couldBeScaledForX ? 1 : 0)) * paperWidthInPixels);
final int potentialPageHeight = Math.min(renderedMap.getHeight(), Math.max(1, pagesVert - (couldBeScaledForY ? 1 : 0)) * paperHeightInPixels);
final double possibleRatio = (double) Math.min(potentialPageWidth, potentialPageHeight) / (double) Math.max(potentialPageWidth, potentialPageHeight);
if (Math.abs(currentRatio - possibleRatio) <= SCALE_RATIO_THRESHOLD) {
final BufferedImage scaledImage = new BufferedImage(potentialPageWidth, potentialPageHeight, BufferedImage.TYPE_INT_ARGB);
final Graphics2D sigfx = scaledImage.createGraphics();
Utils.prepareGraphicsForQuality(sigfx);
try {
sigfx.drawImage(renderedMap, 0, 0, scaledImage.getWidth(), scaledImage.getHeight(), null);
}
finally {
sigfx.dispose();
}
renderedMap = scaledImage;
pagesHorz = Math.max(1, renderedMap.getWidth() / paperWidthInPixels + (renderedMap.getWidth() % paperWidthInPixels == 0 ? 0 : 1));
pagesVert = Math.max(1, renderedMap.getHeight() / paperHeightInPixels + (renderedMap.getHeight() % paperHeightInPixels == 0 ? 0 : 1));
}
}
final boolean centerX = pagesHorz == 1;
final boolean centerY = pagesVert == 1;
this.pages = new PrintPage[pagesVert][pagesHorz];
for (int y = 0; y < pagesVert; y++) {
for (int x = 0; x < pagesHorz; x++) {
final int pageX = x;
final int pageY = y;
final BufferedImage theImage = renderedMap;
this.pages[pageY][pageX] = new PrintPage() {
@Override
public void print (@Nonnull final Graphics g) {
final Graphics2D gfx = (Graphics2D) g.create();
Utils.prepareGraphicsForQuality(gfx);
try {
final int xoffset = paperWidthInPixels * pageX;
final int yoffset = paperHeightInPixels * pageY;
int drawx = -xoffset;
int drawy = -yoffset;
if (centerX) {
drawx = (paperWidthInPixels - theImage.getWidth()) / 2;
}
if (centerY) {
drawy = (paperHeightInPixels - theImage.getHeight()) / 2;
}
gfx.drawImage(theImage, drawx, drawy, null);
}
finally {
gfx.dispose();
}
}
};
}
}
}
else {
// fill single page
final double scaleX = (double) paperWidthInPixels / (double) renderedMap.getWidth();
final double scaleY = (double) paperHeightInPixels / (double) renderedMap.getHeight();
cfg.setScale(Math.min(scaleX, scaleY));
final BufferedImage theImage = MindMapPanel.renderMindMapAsImage(theModel, cfg, false);
this.pages = new PrintPage[][]{{
new PrintPage() {
@Override
public void print (@Nonnull final Graphics g) {
final Graphics2D gfx = (Graphics2D) g.create();
Utils.prepareGraphicsForQuality(gfx);
try {
if (theImage != null) {
gfx.translate(((double) paperWidthInPixels - theImage.getWidth()) / 2, ((double) paperHeightInPixels - theImage.getHeight()) / 2);
gfx.drawImage(theImage, 0, 0, null);
}
}
finally {
gfx.dispose();
}
}
}
}};
}
}
}
@Nonnull
@MustNotContainNull
public PrintPage[][] getPages () {
return this.pages.clone();
}
}