+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
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.
*/
* The Y margin.
*/
protected int marginY = (int) PrintUnit.INCHES.toPoints(0.25f);
-
+
/**
* Constructor. Initialize this printable with the component to be printed.
*
* @param transition the component to be printed
*/
public AbstractPrintableTransition(boolean isDoubleBuffered, Transition transition) {
- super(isDoubleBuffered);
- setBackground(Color.white);
init(transition);
}
* @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.
g2.setColor(Color.BLACK);
g2.setStroke(thinStroke);
+ g2.translate(getOffsetX(), getOffsetY());
draw(g2);
}
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;
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();
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;
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);
--- /dev/null
+/*
+ * 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;
+ }
+}
* 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.
* @param points an array of points.
*/
public PrintableFinSet (Coordinate[] points) {
- super(false);
+ //super(false);
init(points);
- setBackground(Color.white);
+ //setBackground(Color.white);
}
/**
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.
* @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;
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);
}
-
}
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;
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);
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;
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;
import java.awt.*;
import java.awt.image.BufferedImage;
+import java.util.ArrayList;
import java.util.List;
+import java.util.ListIterator;
import java.util.Set;
/**
*/
protected Set<Integer> stages;
+ /**
+ * Strategy for fitting multiple components onto a page.
+ */
+ protected PageFitPrintStrategy pageFitPrint;
+
/**
* Constructor.
*
* @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;
}
/**
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);
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.
*
--- /dev/null
+/*
+ * 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;
+ }
+ }
+}
*/
protected Set<Integer> stages;
+ /**
+ * Strategy for fitting multiple components onto a page.
+ */
+ protected PageFitPrintStrategy pageFitPrint;
+
/**
* Constructor.
*
* @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;
}
/**
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()),
*
* @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.