Added PageFitPrintStrategy and related files to print multiple fins, transitions, and nosecones onto the same page(s)

This commit is contained in:
Jason Blood 2012-05-10 02:48:13 +00:00
parent bd680c7e14
commit 64c3b0c83a
10 changed files with 328 additions and 76 deletions

View File

@ -1,3 +1,7 @@
2012-05-09 Jason Blood
* Add PageFitPrintStrategy and related files to print multiple fins, transitions, and nosecones onto the same page(s)
2012-04-19 Sampo Niskanen
* Allow opening recovery device on stage separation

View File

@ -6,7 +6,7 @@ import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
public abstract class AbstractPrintableTransition extends JPanel {
public abstract class AbstractPrintableTransition extends PrintableComponent {
/**
* The stroke of the transition arc.
*/
@ -21,7 +21,7 @@ public abstract class AbstractPrintableTransition extends JPanel {
* The Y margin.
*/
protected int marginY = (int) PrintUnit.INCHES.toPoints(0.25f);
/**
* Constructor. Initialize this printable with the component to be printed.
*
@ -29,8 +29,6 @@ public abstract class AbstractPrintableTransition extends JPanel {
* @param transition the component to be printed
*/
public AbstractPrintableTransition(boolean isDoubleBuffered, Transition transition) {
super(isDoubleBuffered);
setBackground(Color.white);
init(transition);
}
@ -49,7 +47,7 @@ public abstract class AbstractPrintableTransition extends JPanel {
* @param g2 the graphics context
*/
protected abstract void draw(Graphics2D g2);
/**
* Returns a generated image of the transition. May then be used wherever AWT images can be used, or converted to
* another image/picture format and used accordingly.
@ -80,6 +78,7 @@ public abstract class AbstractPrintableTransition extends JPanel {
g2.setColor(Color.BLACK);
g2.setStroke(thinStroke);
g2.translate(getOffsetX(), getOffsetY());
draw(g2);
}

View File

@ -12,6 +12,7 @@ import com.itextpdf.text.pdf.PdfBoolean;
import com.itextpdf.text.pdf.PdfName;
import com.itextpdf.text.pdf.PdfWriter;
import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.gui.print.visitor.PageFitPrintStrategy;
import net.sf.openrocket.gui.print.visitor.FinMarkingGuideStrategy;
import net.sf.openrocket.gui.print.visitor.FinSetPrintStrategy;
import net.sf.openrocket.gui.print.visitor.PartsDetailVisitorStrategy;
@ -53,6 +54,10 @@ public class PrintController {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
// Used to combine multiple components onto fewer sheets of paper
PageFitPrintStrategy pageFitPrint = new PageFitPrintStrategy(idoc, writer);
while (toBePrinted.hasNext()) {
PrintableContext printableContext = toBePrinted.next();
@ -65,23 +70,23 @@ public class PrintController {
idoc.newPage();
break;
case FIN_TEMPLATE:
final FinSetPrintStrategy finWriter = new FinSetPrintStrategy(idoc, writer, stages);
final FinSetPrintStrategy finWriter = new FinSetPrintStrategy(idoc, writer, stages, pageFitPrint);
finWriter.writeToDocument(doc.getRocket());
break;
case PARTS_DETAIL:
case PARTS_DETAIL:
final PartsDetailVisitorStrategy detailVisitor = new PartsDetailVisitorStrategy(idoc, writer, stages);
detailVisitor.writeToDocument(doc.getRocket());
detailVisitor.close();
idoc.newPage();
detailVisitor.writeToDocument(doc.getRocket());
detailVisitor.close();
idoc.newPage();
break;
case TRANSITION_TEMPLATE:
final TransitionStrategy tranWriter = new TransitionStrategy(idoc, writer, stages);
final TransitionStrategy tranWriter = new TransitionStrategy(idoc, writer, stages, pageFitPrint);
tranWriter.writeToDocument(doc.getRocket(), false);
idoc.newPage();
break;
case NOSE_CONE_TEMPLATE:
final TransitionStrategy coneWriter = new TransitionStrategy(idoc, writer, stages);
final TransitionStrategy coneWriter = new TransitionStrategy(idoc, writer, stages, pageFitPrint);
coneWriter.writeToDocument(doc.getRocket(), true);
idoc.newPage();
break;
@ -93,6 +98,10 @@ public class PrintController {
break;
}
}
// Write out parts that we are going to combine onto single sheets of paper
pageFitPrint.writeToDocument(doc.getRocket());
idoc.newPage();
//Stupid iText throws a really nasty exception if there is no data when close is called.
if (writer.getCurrentDocumentSize() <= 140) {
writer.setPageEmpty(false);

View File

@ -0,0 +1,96 @@
/*
* PrintableComponent.java
*/
package net.sf.openrocket.gui.print;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import javax.swing.JPanel;
/**
* Common interface for components we want to print. Used by PageFitPrintStrategy
*
* @author Jason Blood <dyster2000@gmail.com>
*/
public class PrintableComponent extends JPanel implements Printable {
/**
* The printing offsets
*/
private int offsetX = 0;
private int offsetY = 0;
/**
* Constructor.
*/
public PrintableComponent() {
super(false);
}
/**
* From the java.awt.print.Printable interface.
* <p/>
* Prints the page at the specified index into the specified {@link java.awt.Graphics} context in the specified
* format. A <code>PrinterJob</code> calls the <code>Printable</code> interface to request that a page be rendered
* into the context specified by <code>graphics</code>. The format of the page to be drawn is specified by
* <code>pageFormat</code>. The zero based index of the requested page is specified by <code>pageIndex</code>. If
* the requested page does not exist then this method returns NO_SUCH_PAGE; otherwise PAGE_EXISTS is returned. The
* <code>Graphics</code> class or subclass implements the {@link java.awt.print.PrinterGraphics} interface to
* provide additional information. If the <code>Printable</code> object aborts the print job then it throws a
* {@link java.awt.print.PrinterException}.
* <p/>
* Note: This is not currently used in OpenRocket. It's only here for reference.
*
* @param graphics the context into which the page is drawn
* @param pageFormat the size and orientation of the page being drawn
* @param pageIndex the zero based index of the page to be drawn
*
* @return PAGE_EXISTS if the page is rendered successfully or NO_SUCH_PAGE if <code>pageIndex</code> specifies a
* non-existent page.
*
* @throws java.awt.print.PrinterException
* thrown when the print job is terminated.
*/
@Override
public int print (final Graphics graphics, final PageFormat pageFormat, final int pageIndex)
throws PrinterException {
Graphics2D g2d = (Graphics2D) graphics;
PrintUtilities.translateToJavaOrigin(g2d, pageFormat);
PrintUtilities.disableDoubleBuffering(this);
paint(g2d);
PrintUtilities.enableDoubleBuffering(this);
return Printable.PAGE_EXISTS;
}
/**
* Set the offset this component will be printed to the page
* @param x X offset to print at.
* @param x Y offset to print at.
*/
public void setPrintOffset(int x, int y) {
offsetX = x;
offsetY = y;
}
/**
* Get the X offset this component will be printed to the page
* @return X offset to print at.
*/
public int getOffsetX() {
return offsetX;
}
/**
* Get the Y offset this component will be printed to the page
* @return Y offset to print at.
*/
public int getOffsetY() {
return offsetY;
}
}

View File

@ -19,7 +19,7 @@ import java.awt.print.PrinterException;
* modified) and rendering it within a JPanel. The JPanel is not actually visualized on a display, but instead renders
* it to a print device.
*/
public class PrintableFinSet extends JPanel implements Printable {
public class PrintableFinSet extends PrintableComponent {
/**
* The object that represents the shape (outline) of the fin. This gets drawn onto the Swing component.
@ -58,9 +58,9 @@ public class PrintableFinSet extends JPanel implements Printable {
* @param points an array of points.
*/
public PrintableFinSet (Coordinate[] points) {
super(false);
//super(false);
init(points);
setBackground(Color.white);
//setBackground(Color.white);
}
/**
@ -108,42 +108,6 @@ public class PrintableFinSet extends JPanel implements Printable {
return marginY;
}
/**
* From the java.awt.print.Printable interface.
* <p/>
* Prints the page at the specified index into the specified {@link java.awt.Graphics} context in the specified
* format. A <code>PrinterJob</code> calls the <code>Printable</code> interface to request that a page be rendered
* into the context specified by <code>graphics</code>. The format of the page to be drawn is specified by
* <code>pageFormat</code>. The zero based index of the requested page is specified by <code>pageIndex</code>. If
* the requested page does not exist then this method returns NO_SUCH_PAGE; otherwise PAGE_EXISTS is returned. The
* <code>Graphics</code> class or subclass implements the {@link java.awt.print.PrinterGraphics} interface to
* provide additional information. If the <code>Printable</code> object aborts the print job then it throws a
* {@link java.awt.print.PrinterException}.
* <p/>
* Note: This is not currently used in OpenRocket. It's only here for reference.
*
* @param graphics the context into which the page is drawn
* @param pageFormat the size and orientation of the page being drawn
* @param pageIndex the zero based index of the page to be drawn
*
* @return PAGE_EXISTS if the page is rendered successfully or NO_SUCH_PAGE if <code>pageIndex</code> specifies a
* non-existent page.
*
* @throws java.awt.print.PrinterException
* thrown when the print job is terminated.
*/
@Override
public int print (final Graphics graphics, final PageFormat pageFormat, final int pageIndex)
throws PrinterException {
Graphics2D g2d = (Graphics2D) graphics;
PrintUtilities.translateToJavaOrigin(g2d, pageFormat);
PrintUtilities.disableDoubleBuffering(this);
paint(g2d);
PrintUtilities.enableDoubleBuffering(this);
return Printable.PAGE_EXISTS;
}
/**
* Returns a generated image of the fin set. May then be used wherever AWT images can be used, or converted to
* another image/picture format and used accordingly.
@ -174,8 +138,7 @@ public class PrintableFinSet extends JPanel implements Printable {
* @param g the Java2D graphics context
*/
@Override
public void paintComponent (Graphics g) {
super.paintComponent(g);
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
int x = 0;
@ -189,11 +152,10 @@ public class PrintableFinSet extends JPanel implements Printable {
y = marginY + Math.abs(minY);
}
// Reset the origin.
g2d.translate(x, y);
g2d.translate(x + getOffsetX(), y + getOffsetY());
g2d.setPaint(TemplateProperties.getFillColor());
g2d.fill(polygon);
g2d.setPaint(TemplateProperties.getLineColor());
g2d.draw(polygon);
}
}

View File

@ -1,8 +1,12 @@
package net.sf.openrocket.gui.print;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import net.sf.openrocket.gui.rocketfigure.TransitionShapes;
import net.sf.openrocket.rocketcomponent.NoseCone;
@ -48,7 +52,7 @@ public class PrintableNoseCone extends AbstractPrintableTransition {
if (shapes != null && shapes.length > 0) {
Rectangle r = shapes[0].getBounds();
g2.translate(marginX + r.getHeight() / 2, marginY);
g2.translate(marginX + r.getHeight() / 2 + getOffsetX(), marginY + getOffsetY());
g2.rotate(Math.PI / 2);
for (Shape shape : shapes) {
g2.draw(shape);

View File

@ -1,12 +1,16 @@
package net.sf.openrocket.gui.print;
import java.awt.BasicStroke;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Arc2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import net.sf.openrocket.rocketcomponent.Transition;

View File

@ -9,6 +9,7 @@ import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfWriter;
import net.sf.openrocket.gui.print.ITextHelper;
import net.sf.openrocket.gui.print.PrintUnit;
import net.sf.openrocket.gui.print.PrintableFinSet;
import net.sf.openrocket.logging.LogHelper;
import net.sf.openrocket.rocketcomponent.FinSet;
@ -17,7 +18,9 @@ import net.sf.openrocket.startup.Application;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;
/**
@ -45,6 +48,11 @@ public class FinSetPrintStrategy {
*/
protected Set<Integer> stages;
/**
* Strategy for fitting multiple components onto a page.
*/
protected PageFitPrintStrategy pageFitPrint;
/**
* Constructor.
*
@ -52,10 +60,11 @@ public class FinSetPrintStrategy {
* @param theWriter The direct iText writer
* @param theStages The stages to be printed by this strategy
*/
public FinSetPrintStrategy(Document doc, PdfWriter theWriter, Set<Integer> theStages) {
public FinSetPrintStrategy(Document doc, PdfWriter theWriter, Set<Integer> theStages, PageFitPrintStrategy pageFit) {
document = doc;
writer = theWriter;
stages = theStages;
pageFitPrint = pageFit;
}
/**
@ -98,14 +107,14 @@ public class FinSetPrintStrategy {
java.awt.Dimension finSize = pfs.getSize();
final Dimension pageSize = getPageSize();
if (fitsOnOnePage(pageSize, finSize.getWidth(), finSize.getHeight())) {
printOnOnePage(pfs);
pageFitPrint.addComponent(pfs);
}
else {
BufferedImage image = (BufferedImage) pfs.createImage();
ITextHelper.renderImageAcrossPages(new Rectangle(pageSize.getWidth(), pageSize.getHeight()),
document, writer, image);
document.newPage();
}
document.newPage();
}
catch (DocumentException e) {
log.error("Could not render fin.", e);
@ -153,19 +162,6 @@ public class FinSetPrintStrategy {
return wRatio <= 1.0d && hRatio <= 1.0d;
}
/**
* Print the fin set.
*
* @param thePfs the printable fin set
*/
private void printOnOnePage (final PrintableFinSet thePfs) {
Dimension d = getPageSize();
PdfContentByte cb = writer.getDirectContent();
Graphics2D g2 = cb.createGraphics(d.width, d.height);
thePfs.print(g2);
g2.dispose();
}
/**
* Get the dimensions of the paper page.
*

View File

@ -0,0 +1,171 @@
/*
* PageFitPrintStrategy.java
*/
package net.sf.openrocket.gui.print.visitor;
import com.itextpdf.text.Document;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfWriter;
import net.sf.openrocket.gui.print.PrintUnit;
import net.sf.openrocket.gui.print.PrintableComponent;
import net.sf.openrocket.logging.LogHelper;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.startup.Application;
import java.awt.*;
import java.util.ArrayList;
import java.util.ListIterator;
import java.util.Set;
/**
* A strategy for drawing multiple rocket components onto as few pages as possible.
*
* @author Jason Blood <dyster2000@gmail.com>
*/
public class PageFitPrintStrategy {
/**
* The logger.
*/
private static final LogHelper log = Application.getLogger();
/**
* The iText document.
*/
protected Document document;
/**
* The direct iText writer.
*/
protected PdfWriter writer;
/**
* The stages selected.
*/
protected Set<Integer> stages;
protected ArrayList<PrintableComponent> componentToPrint;
/**
* Constructor.
*
* @param doc The iText document
* @param theWriter The direct iText writer
* @param theStages The stages to be printed by this strategy
*/
public PageFitPrintStrategy(Document doc, PdfWriter theWriter) {
document = doc;
writer = theWriter;
componentToPrint = new ArrayList<PrintableComponent>();
}
/**
* Add a component we want to print.
*
* @param component The component to add for printing
*/
public void addComponent(PrintableComponent component) {
componentToPrint.add(component);
}
/**
* Recurse through the given rocket component.
*
* @param root the root component; all children will be printed recursively
*/
public void writeToDocument (final RocketComponent root) {
fitPrintComponents();
}
/**
* Iterate through the components to print fitting them onto pages as best possible.
*/
private void fitPrintComponents() {
final Dimension pageSize = getPageSize();
double wPage = pageSize.getWidth();
double hPage = pageSize.getHeight();
int marginX = (int)(PrintUnit.POINTS_PER_INCH * 0.3f);
int marginY = (int)(PrintUnit.POINTS_PER_INCH * 0.3f);
PdfContentByte cb = writer.getDirectContent();
while (componentToPrint.size() > 0) {
int pageY = 0;
Boolean anyAddedToRow;
Graphics2D g2 = cb.createGraphics(pageSize.width, pageSize.height);
do {
// Fill the row
int rowX = 0;
int rowY = pageY;
ListIterator<PrintableComponent> entry = componentToPrint.listIterator();
anyAddedToRow = false;
while (entry.hasNext()) {
PrintableComponent component = entry.next();
java.awt.Dimension dim = component.getSize();
if ((rowX + dim.width < wPage) && (rowY + dim.height < hPage)) {
component.setPrintOffset(rowX, rowY);
rowX += dim.width + marginX;
if (rowY + dim.height + marginY > pageY)
pageY = rowY + dim.height + marginY;
entry.remove();
component.print(g2);
anyAddedToRow = true;
}
}
} while (anyAddedToRow);
g2.dispose();
document.newPage();
}
}
/**
* Get the dimensions of the paper page.
*
* @return an internal Dimension
*/
protected Dimension getPageSize () {
return new Dimension(document.getPageSize().getWidth(),
document.getPageSize().getHeight());
}
/**
* Convenience class to model a dimension.
*/
class Dimension {
/** Width, in points. */
public float width;
/** Height, in points. */
public float height;
/**
* Constructor.
* @param w width
* @param h height
*/
public Dimension (float w, float h) {
width = w;
height = h;
}
/**
* Get the width.
*
* @return the width
*/
public float getWidth () {
return width;
}
/**
* Get the height.
*
* @return the height
*/
public float getHeight () {
return height;
}
}
}

View File

@ -45,6 +45,11 @@ public class TransitionStrategy {
*/
protected Set<Integer> stages;
/**
* Strategy for fitting multiple components onto a page.
*/
protected PageFitPrintStrategy pageFitPrint;
/**
* Constructor.
*
@ -52,10 +57,11 @@ public class TransitionStrategy {
* @param theWriter The direct iText writer
* @param theStagesToVisit The stages to be visited by this strategy
*/
public TransitionStrategy(Document doc, PdfWriter theWriter, Set<Integer> theStagesToVisit) {
public TransitionStrategy(Document doc, PdfWriter theWriter, Set<Integer> theStagesToVisit, PageFitPrintStrategy pageFit) {
document = doc;
writer = theWriter;
stages = theStagesToVisit;
pageFitPrint = pageFit;
}
/**
@ -107,7 +113,8 @@ public class TransitionStrategy {
java.awt.Dimension size = pfs.getSize();
final Dimension pageSize = getPageSize();
if (fitsOnOnePage(pageSize, size.getWidth(), size.getHeight())) {
printOnOnePage(pfs);
pageFitPrint.addComponent(pfs);
//printOnOnePage(pfs);
} else {
BufferedImage image = (BufferedImage) pfs.createImage();
ITextHelper.renderImageAcrossPages(new Rectangle(pageSize.getWidth(), pageSize.getHeight()),
@ -141,14 +148,14 @@ public class TransitionStrategy {
*
* @param theTransition the printable transition
*/
private void printOnOnePage(final AbstractPrintableTransition theTransition) {
/*private void printOnOnePage(final AbstractPrintableTransition theTransition) {
Dimension d = getPageSize();
PdfContentByte cb = writer.getDirectContent();
Graphics2D g2 = cb.createGraphics(d.width, d.height);
theTransition.print(g2);
g2.dispose();
document.newPage();
}
}*/
/**
* Get the dimensions of the paper page.