Odds and Ends: PDF Valentines Cards

Because it's Friday

This challenge was too good to resist. We've neglected to make our cards PDF/A compliant, which you are welcome to interpret as a commentary on the impermanence of romantic love, or perhaps it would have just taken longer to do.

Either way we hope you had a happy Hallmark day. The code is below, and if you want to generate your own cards for someone you love (or even someone you don't) you can do so with this form.

import org.faceless.pdf2.*;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.awt.geom.*;
import java.awt.*;

public class ValentineServlet extends HttpServlet {

  public static PDF makeCard(String to, String from) {
    PDF pdf = new PDF();
    PDFPage page = pdf.newPage("A5");

    // Make a heart. 
    GeneralPath p = new GeneralPath();
    p.moveTo(0, -10);
    p.curveTo(20, 30, 60, 30, 70, -10);
    p.curveTo(70, -30, 60, -40, 50, -50);
    p.lineTo(0, -90);
    p.lineTo(-50, -50);
    p.curveTo(-60, -40, -70, -30, -70, -10);
    p.curveTo(-60, 30, -20, 30, 0, -10);
    Rectangle2D r = p.getBounds();

    float linewidth = 8;
    PDFCanvas heart = new PDFCanvas((float)r.getWidth()+linewidth, (float)r.getHeight()+linewidth);
    PDFStyle style = new PDFStyle();
    style.setFillColor(new Color(255, 0, 128));
    style.setLineColor(new Color(128, 0, 0));
    style.setLineWeighting(linewidth);
    heart.setStyle(style);
    heart.transform(AffineTransform.getTranslateInstance(-r.getMinX()+linewidth/2, -r.getMinY()+linewidth/2));
    heart.drawShape(p);

    // Draw loads of hearts randomly rotated and
    // positioned onto a canvas
    canvas = new PDFCanvas(page.getWidth(), page.getHeight());
    for (int i=0;i<200;i++) {
      AffineTransform t = new AffineTransform();
      // Rotate left/right by <= 45°, scale up or down by factor of 2
      t.rotate((Math.random() - 0.5) * Math.PI / 2);
      t.translate(Math.random() * page.getWidth() - heart.getWidth()/2, Math.random() * page.getHeight() - heart.getHeight()/2);
      double scale = 1 / (Math.random() + 0.5);
      t.scale(scale, scale);
      canvas.save();
      canvas.transform(t);
      canvas.drawCanvas(heart, 0, 0, heart.getWidth(), heart.getHeight());
      canvas.restore();
    }
    page.drawCanvas(canvas, 0, 0, canvas.getWidth(), canvas.getHeight());

    // Add the text
    PDFCanvas canvas = new PDFCanvas(page.getWidth(), page.getHeight());
    PDFStyle textstyle = new PDFStyle();
    textstyle.setFillColor(Color.white);
    textstyle.setLineColor(Color.black);
    textstyle.setFontStyle(PDFStyle.FONTSTYLE_FILLEDOUTLINE);
    textstyle.setFont(new StandardFont(StandardFont.HELVETICA), 40);

    PDFStyle smallstyle = new PDFStyle(textstyle);
    smallstyle.setFont(new StandardFont(StandardFont.HELVETICABOLDOBLIQUE), 24);

    LayoutBox box = new LayoutBox(page.getWidth());
    if (to != null) {
      box.addText("Dear "+to+"\n\n", smallstyle, null);
    }
    box.addText("Roses are red\nViolets are blue\nHere's a PDF\nJust for You\n\n", textstyle, null);
    textstyle.setFont(new StandardFont(StandardFont.HELVETICABOLDOBLIQUE), 24);
    box.addText("Nothing says \"I Love You\"\nlike ISO PDF 32000-1:2008.\n\n", smallstyle, null);
    box.addText("Happy Valentines Day\nfrom ", smallstyle, null);
    if (from != null) {
      box.addText(from+" and ", smallstyle, null);
    }
    box.addText("BFO", smallstyle, null);
    page.drawLayoutBox(box, 50, 500);

    return pdf;
  }

  public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
    String from = req.getParameter("from");
    String to = req.getParameter("to");
    if (from != null && from.trim().length() == 0) {
      from = null;
    }
    if (to != null && to.trim().length() == 0) {
      to = null;
    }
    PDF pdf = makeCard(to, from);
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    pdf.render(out);
    out.close();

    res.setContentLength(out.size());
    res.setContentType("application/pdf");
    out.writeTo(res.getOutputStream());
  }

  public static void main(String[] args) throws Exception {
    String to = args.length > 0 ? args[0] : null;
    String from = args.length > 1 ? args[1] : null;
    PDF pdf = makeCard(to, from);
    pdf.render(new FileOutputStream("valentine.pdf"));
  }

}