示例#1
0
 @Override
 public void onEndPage(PdfWriter writer, Document document) {
   // This scales the image to the page,
   // use the image's width & height if you don't want to scale.
   float width = document.getPageSize().getWidth();
   float height = document.getPageSize().getHeight();
   try {
     Image background = Image.getInstance("e:/upload/toys/makbuz.jpg");
     writer.getDirectContentUnder().addImage(background, width, 0, 0, height, 0, 0);
   } catch (Exception e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
   }
 }
示例#2
0
  public void process(ObjectIO input, ObjectIO output) {

    try {

      // OJO pasarle el idemisor

      String idemisor = "1";

      recuperaInput(input);

      Rectangle pageSize = new Rectangle(PageSize.A4);
      // pageSize.setBackgroundColor(Color.TRANSLUCENT);
      pageSize.rotate();
      documento = new Document(pageSize);

      System.out.println("WIDTH " + documento.getPageSize().getWidth());
      System.out.println("HEIGHT " + documento.getPageSize().getHeight());

      namefile = "BarCode_" + idemisor + "_codigoBARRAS.pdf";
      filecrea = "C://DATOS//LRA//tmp//" + namefile;
      // filecrea = PropiedadesJLet.getInstance().getProperty("path.gen.recibo") +"emisor_"+
      // idemisor +"/"+ namefile;
      System.out.println(
          "***************************Se va a crear este codigo de barras: " + filecrea);
      writer = PdfWriter.getInstance(documento, new FileOutputStream(filecrea));
      documento.open();

      pintaCodigoBarras();

      absText(writer, texto1xx, xpostex1, ypostex1, 8);

      absText(writer, texto2xx, xpostex2, ypostex2, 8);

      try {
        output.setValue("idemisor", idemisor);
        output.setValue("filecrea", namefile);
        output.setValue("txmensaj", "Generacion Ok - ");
      } catch (Exception e) {
        e.printStackTrace();
      }

    } catch (DocumentException de) {
      System.err.println(de.getMessage());
    } catch (IOException ioe) {
      System.err.println(ioe.getMessage());
    } finally {
      documento.close();
    }
  }
示例#3
0
  //	---------------- for footer and header....------------------
  @Override
  public void onEndPage(PdfWriter writer, Document document) {

    try {

      Rectangle page = document.getPageSize();

      // we will print the header using the code below

      PdfPTable headTable = getHeader();
      headTable.setTotalWidth(page.getWidth() - document.leftMargin() - document.rightMargin());

      // the table is printed at the spcific location

      headTable.writeSelectedRows(0, -1, document.leftMargin(), 830, writer.getDirectContent());
      // we will print the footer using the code below

      PdfPTable footTable = getFooter();

      footTable.setTotalWidth(page.getWidth() - document.leftMargin() - document.rightMargin());

      footTable.writeSelectedRows(
          0, -1, document.leftMargin(), document.bottomMargin() - 560, writer.getDirectContent());

    } catch (Exception ex) {

      ex.printStackTrace();
    }

    // TODO Auto-generated method stub

  }
 /**
  * Calculates the maximum font size, which will fits all characters of the given fileContent into
  * the document's page width.
  *
  * @param document where the text will be inputed.
  * @param fileContent the content of the file the will be written.
  * @param fontName the name of the font which will be used while writing into a file.
  * @param encoding the encoding which will be used while writing into a file.
  * @return the max size of the font that will fit the string into the given document's page width.
  * @throws IOException
  * @throws DocumentException
  */
 public static float calculateFontSizeFittingAllStringIntoPageWidth(
     Document document, String fileContent, String fontName, String encoding)
     throws IOException, DocumentException {
   String longestLineOfParagraph = "";
   String newLineMarker = System.getProperty("line.separator");
   String[] lineArray = fileContent.split(newLineMarker);
   //	System.out.println("lineArray.length="+lineArray.length);
   for (int i = 0; i < lineArray.length; i++) {
     String line = lineArray[i];
     if (longestLineOfParagraph.length() < line.length()) longestLineOfParagraph = line;
   }
   // Round margins' size up...
   int marignLeft = (int) (document.leftMargin() + 1);
   int marginRight = (int) (document.rightMargin() + 1);
   // round page width down...
   int pageWidth = (int) document.getPageSize().getWidth();
   // ...to make sure we have correct max space for text.
   int maxSpaceForText = pageWidth - marignLeft - marginRight;
   System.out.println(
       "marignLeft="
           + marignLeft
           + ";  marginRight="
           + marginRight
           + ";  pageWidth="
           + pageWidth
           + ";  maxSpaceForText="
           + maxSpaceForText);
   float fontSize = 12.0f;
   BaseFont bf = BaseFont.createFont(fontName, encoding, BaseFont.EMBEDDED);
   float textWidthForFontSize = bf.getWidthPointKerned(longestLineOfParagraph, fontSize);
   float requiredFontSize = (fontSize * maxSpaceForText) / textWidthForFontSize;
   return requiredFontSize;
 }
示例#5
0
  public void pintaCodigoBarras() {

    Image imageLogo;
    Image imageBarc = null;
    String imageUrl;

    Font fuente = new Font(FontFamily.HELVETICA, 8, Font.NORMAL, new BaseColor(64, 64, 64));
    BaseColor bkcolor = new BaseColor(255, 255, 255);

    try {

      PdfPTable table = new PdfPTable(new float[] {1, 1, 1, 1, 1});
      // table.setTotalWidth(900);
      table.getDefaultCell().setBorder(0);
      //	imageUrl = PropiedadesJLet.getInstance().getProperty("path.img.logos") +
      // "logo_recibos.png";

      // imageLogo = Image.getInstance(imageUrl);

      int yinic = 805;
      int xinic = 0;

      int posicion = 0; // posicion donde comenzara a imprimir

      // for (int i = 0; i < listcode.size(); i++){

      String codeunic = codvalue;

      // System.out.println(codeunic.substring(0,5) +"-"+ codeunic.substring(6,7));

      imageBarc = getBarcode(documento, writer, codeunic);

      PdfPTable tablein = new PdfPTable(new float[] {1f, 3f, 1f});

      tablein.getDefaultCell().setBorder(0);
      tablein.setTotalWidth(documento.getPageSize().getWidth());

      tablein.addCell(FRAparen.getCelda(" ", fuente, bkcolor, "center")).setBorder(0);
      // tablein.addCell(imageLogo);
      tablein.addCell(FRAparen.getCelda(" ", fuente, bkcolor, "center")).setBorder(0);

      tablein.addCell(FRAparen.getCelda(" ", fuente, bkcolor, "center")).setBorder(0);
      tablein.addCell(imageBarc);
      tablein.addCell(FRAparen.getCelda(" ", fuente, bkcolor, "center")).setBorder(0);

      table.addCell(tablein);

      imageBarc.scaleAbsolute(anchcdba, altocdba);
      imageBarc.setAbsolutePosition(xposicio, yposicio);

      documento.add(imageBarc);

    } catch (Exception e) {

    }
  }
示例#6
0
  private static void imageToPDF(String folder, Collection<Path> all) {

    for (Path path : all) {

      Document document = new Document();

      String absolutePathInput = path.toFile().getAbsolutePath();
      String absolutePathOutput = folder + "/" + path.getFileName() + ".pdf";

      if (absolutePathInput.endsWith(".gif")
          ||
          //
          absolutePathInput.endsWith(".GIF")
          ||
          //
          absolutePathInput.endsWith(".jpg")
          ||
          //
          absolutePathInput.endsWith(".JPG")
          ||
          //
          absolutePathInput.endsWith(".png")
          ||
          //
          absolutePathInput.endsWith(".PNG")) {

        try {
          FileOutputStream fos = new FileOutputStream(absolutePathOutput);
          PdfWriter writer = PdfWriter.getInstance(document, fos);
          writer.open();
          document.open();

          int indentation = 0;

          Image image = Image.getInstance(absolutePathInput);
          float scaler =
              ((document.getPageSize().getWidth()
                          - document.leftMargin()
                          - document.rightMargin()
                          - indentation)
                      / image.getWidth())
                  * 100;

          image.scalePercent(scaler);

          document.add(image);
          document.close();
          writer.close();
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
    }
  }
  public static void buildPdf(PrinterJob job, OutputStream os) throws DocumentException {
    Document doc = new Document(getPageSizeFromPaper(job.getPaper()));
    PdfWriter pdfWriter = PdfWriter.getInstance(doc, os);
    doc.open();

    for (int i = 0; job.getNumberOfPages() < 0 || i < job.getNumberOfPages(); i++) {

      if (job.getNumberOfPages() < 0) {
        if (!job.render(DUMMYRENDERSPACE, i)) {
          break;
        }
      }

      PdfContentByte cb = pdfWriter.getDirectContent();
      PdfTemplate pdfTemplate =
          cb.createTemplate(doc.getPageSize().getWidth(), doc.getPageSize().getHeight());
      Graphics2D g2d =
          pdfTemplate.createGraphics(
              doc.getPageSize().getWidth(), doc.getPageSize().getHeight(), new DefaultFontMapper());
      try {
        if (job.render(g2d, i)) {
          g2d.dispose();
          if (i > 0) {
            doc.newPage();
          }
          cb.addTemplate(pdfTemplate, 0, 0);
        } else {
          throw new PrinterException("Unknow error occured.");
        }
      } catch (PrinterException ex) {
        break;
      } finally {

      }
    }
    doc.close();
  }
 /** 当报表没有配置PDF模板时,此方法在显示完每页PDF后执行 现在在此方法中演示显示完每页后向其中添加一个图片 */
 public void afterDisplayPdfPageWithoutTemplate(Document document, AbsReportType reportTypeObj) {
   super.afterDisplayPdfPageWithoutTemplate(document, reportTypeObj);
   ReportRequest rrequest = reportTypeObj.getReportRequest();
   String serverName = rrequest.getRequest().getServerName();
   String serverPort = String.valueOf(rrequest.getRequest().getServerPort());
   String imgurl =
       "http://"
           + serverName
           + ":"
           + serverPort
           + Config.webroot
           + "wabacusdemo/pdftemplate/logo.gif"; // 构造要添加图片的URL
   try {
     Image img = Image.getInstance(imgurl);
     float width = document.getPageSize().getWidth();
     float height = document.getPageSize().getHeight();
     // width = width - img.getWidth();
     img.setAbsolutePosition(width / 2, height / 2 + 300f);
     img.setAlignment(Image.ALIGN_CENTER);
     document.add(img);
   } catch (Exception e) {
     e.printStackTrace();
   }
 }
  @Override
  // initialization of the header table
  public void onEndPage(final PdfWriter writer, final Document document) {
    final PdfPTable header = new PdfPTable(2);
    final Phrase p = new Phrase();
    final Chunk ck = new Chunk(_challengeTitle + "\n" + _reportTitle, _font);
    p.add(ck);
    header.getDefaultCell().setBorderWidth(0);
    header.addCell(p);
    header.getDefaultCell().setHorizontalAlignment(com.itextpdf.text.Element.ALIGN_RIGHT);
    header.addCell(
        new Phrase(new Chunk("Tournament: " + _tournament + "\nDate: " + _formattedDate, _font)));
    final PdfPCell blankCell = new PdfPCell();
    blankCell.setBorder(0);
    blankCell.setBorderWidthTop(1.0f);
    blankCell.setColspan(2);
    header.addCell(blankCell);

    final PdfContentByte cb = writer.getDirectContent();
    cb.saveState();
    header.setTotalWidth(document.right() - document.left());
    header.writeSelectedRows(0, -1, document.left(), document.getPageSize().getHeight() - 10, cb);
    cb.restoreState();
  }
示例#10
0
  private static TableAndHighlighting createTable(
      PdfWriter docWriter,
      Document doc,
      float sideMargins,
      Dimension scrambleImageSize,
      String[] scrambles,
      Puzzle scrambler,
      HashMap<String, Color> colorScheme,
      String scrambleNumberPrefix,
      boolean forceHighlighting)
      throws DocumentException {
    PdfContentByte cb = docWriter.getDirectContent();

    PdfPTable table = new PdfPTable(3);

    int charsWide = scrambleNumberPrefix.length() + 1 + (int) Math.log10(scrambles.length);
    String wideString = "";
    for (int i = 0; i < charsWide; i++) {
      // M has got to be as wide or wider than the widest digit in our font
      wideString += "M";
    }
    wideString += ".";
    float col1Width = new Chunk(wideString).getWidthPoint();
    // I don't know why we need this, perhaps there's some padding?
    col1Width += 5;

    float availableWidth = doc.getPageSize().getWidth() - sideMargins;
    float scrambleColumnWidth =
        availableWidth - col1Width - scrambleImageSize.width - 2 * SCRAMBLE_IMAGE_PADDING;
    int availableScrambleHeight = scrambleImageSize.height - 2 * SCRAMBLE_IMAGE_PADDING;

    table.setTotalWidth(
        new float[] {
          col1Width, scrambleColumnWidth, scrambleImageSize.width + 2 * SCRAMBLE_IMAGE_PADDING
        });
    table.setLockedWidth(true);

    String longestScramble = "";
    String longestPaddedScramble = "";
    for (String scramble : scrambles) {
      if (scramble.length() > longestScramble.length()) {
        longestScramble = scramble;
      }

      String paddedScramble = padTurnsUniformly(scramble, "M");
      if (paddedScramble.length() > longestPaddedScramble.length()) {
        longestPaddedScramble = paddedScramble;
      }
    }
    // I don't know how to configure ColumnText.fitText's word wrapping characters,
    // so instead, I just replace each character I don't want to wrap with M, which
    // should be the widest character (we're using a monospaced font,
    // so that doesn't really matter), and won't get wrapped.
    char widestCharacter = 'M';
    longestPaddedScramble = longestPaddedScramble.replaceAll("\\S", widestCharacter + "");
    boolean tryToFitOnOneLine = true;
    if (longestPaddedScramble.indexOf("\n") >= 0) {
      // If the scramble contains newlines, then we *only* allow wrapping at the
      // newlines.
      longestPaddedScramble = longestPaddedScramble.replaceAll(" ", "M");
      tryToFitOnOneLine = false;
    }
    boolean oneLine = false;
    Font scrambleFont = null;

    try {
      BaseFont courier = BaseFont.createFont(BaseFont.COURIER, BaseFont.CP1252, BaseFont.EMBEDDED);
      Rectangle availableArea =
          new Rectangle(
              scrambleColumnWidth - 2 * SCRAMBLE_PADDING_HORIZONTAL,
              availableScrambleHeight
                  - SCRAMBLE_PADDING_VERTICAL_TOP
                  - SCRAMBLE_PADDING_VERTICAL_BOTTOM);
      float perfectFontSize =
          fitText(
              new Font(courier),
              longestPaddedScramble,
              availableArea,
              MAX_SCRAMBLE_FONT_SIZE,
              true);
      if (tryToFitOnOneLine) {
        String longestScrambleOneLine = longestScramble.replaceAll(".", widestCharacter + "");
        float perfectFontSizeForOneLine =
            fitText(
                new Font(courier),
                longestScrambleOneLine,
                availableArea,
                MAX_SCRAMBLE_FONT_SIZE,
                false);
        oneLine = perfectFontSizeForOneLine >= MINIMUM_ONE_LINE_FONT_SIZE;
        if (oneLine) {
          perfectFontSize = perfectFontSizeForOneLine;
        }
      }
      scrambleFont = new Font(courier, perfectFontSize, Font.NORMAL);
    } catch (IOException e) {
      l.log(Level.INFO, "", e);
    } catch (DocumentException e) {
      l.log(Level.INFO, "", e);
    }

    boolean highlight = forceHighlighting;
    for (int i = 0; i < scrambles.length; i++) {
      String scramble = scrambles[i];
      String paddedScramble =
          oneLine ? scramble : padTurnsUniformly(scramble, NON_BREAKING_SPACE + "");
      Chunk ch = new Chunk(scrambleNumberPrefix + (i + 1) + ".");
      PdfPCell nthscramble = new PdfPCell(new Paragraph(ch));
      nthscramble.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE);
      table.addCell(nthscramble);

      Phrase scramblePhrase = new Phrase();
      int nthLine = 1;
      LinkedList<Chunk> lineChunks =
          splitScrambleToLineChunks(paddedScramble, scrambleFont, scrambleColumnWidth);
      if (lineChunks.size() >= MIN_LINES_TO_ALTERNATE_HIGHLIGHTING) {
        highlight = true;
      }

      for (Chunk lineChunk : lineChunks) {
        if (highlight && (nthLine % 2 == 0)) {
          lineChunk.setBackground(HIGHLIGHT_COLOR);
        }
        scramblePhrase.add(lineChunk);
        nthLine++;
      }

      PdfPCell scrambleCell = new PdfPCell(new Paragraph(scramblePhrase));
      // We carefully inserted newlines ourselves to make stuff fit, don't
      // let itextpdf wrap lines for us.
      scrambleCell.setNoWrap(true);
      scrambleCell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE);
      // This shifts everything up a little bit, because I don't like how
      // ALIGN_MIDDLE works.
      scrambleCell.setPaddingTop(-SCRAMBLE_PADDING_VERTICAL_TOP);
      scrambleCell.setPaddingBottom(SCRAMBLE_PADDING_VERTICAL_BOTTOM);
      scrambleCell.setPaddingLeft(SCRAMBLE_PADDING_HORIZONTAL);
      scrambleCell.setPaddingRight(SCRAMBLE_PADDING_HORIZONTAL);
      // We space lines a little bit more here - it still fits in the cell height
      scrambleCell.setLeading(0, 1.1f);
      table.addCell(scrambleCell);

      if (scrambleImageSize.width > 0 && scrambleImageSize.height > 0) {
        PdfTemplate tp =
            cb.createTemplate(
                scrambleImageSize.width + 2 * SCRAMBLE_IMAGE_PADDING,
                scrambleImageSize.height + 2 * SCRAMBLE_IMAGE_PADDING);
        Graphics2D g2 =
            new PdfGraphics2D(tp, tp.getWidth(), tp.getHeight(), new DefaultFontMapper());
        g2.translate(SCRAMBLE_IMAGE_PADDING, SCRAMBLE_IMAGE_PADDING);

        try {
          Svg svg = scrambler.drawScramble(scramble, colorScheme);
          drawSvgToGraphics2D(svg, g2, scrambleImageSize);
        } catch (Exception e) {
          table.addCell("Error drawing scramble: " + e.getMessage());
          l.log(
              Level.WARNING,
              "Error drawing scramble, if you're having font issues, try installing ttf-dejavu.",
              e);
          continue;
        } finally {
          g2.dispose(); // iTextPdf blows up if we do not dispose of this
        }
        PdfPCell imgCell = new PdfPCell(Image.getInstance(tp), true);
        imgCell.setBackgroundColor(BaseColor.LIGHT_GRAY);
        imgCell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE);
        imgCell.setHorizontalAlignment(PdfPCell.ALIGN_MIDDLE);
        table.addCell(imgCell);
      } else {
        table.addCell("");
      }
    }

    TableAndHighlighting tableAndHighlighting = new TableAndHighlighting();
    tableAndHighlighting.table = table;
    tableAndHighlighting.highlighting = highlight;
    return tableAndHighlighting;
  }
示例#11
0
  private static void addScrambles(
      PdfWriter docWriter, Document doc, ScrambleRequest scrambleRequest, String globalTitle)
      throws DocumentException, IOException {
    if (scrambleRequest.fmc) {
      Rectangle pageSize = doc.getPageSize();
      for (int i = 0; i < scrambleRequest.scrambles.length; i++) {
        String scramble = scrambleRequest.scrambles[i];
        PdfContentByte cb = docWriter.getDirectContent();
        float LINE_THICKNESS = 0.5f;
        BaseFont bf =
            BaseFont.createFont(BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED);

        int bottom = 30;
        int left = 35;
        int right = (int) (pageSize.getWidth() - left);
        int top = (int) (pageSize.getHeight() - bottom);

        int height = top - bottom;
        int width = right - left;

        int solutionBorderTop = bottom + (int) (height * .5);
        int scrambleBorderTop = solutionBorderTop + 40;

        int competitorInfoBottom = top - (int) (height * .15);
        int gradeBottom = competitorInfoBottom - 50;
        int competitorInfoLeft = right - (int) (width * .45);

        int rulesRight = competitorInfoLeft;

        int padding = 5;

        // Outer border
        cb.setLineWidth(2f);
        cb.moveTo(left, top);
        cb.lineTo(left, bottom);
        cb.lineTo(right, bottom);
        cb.lineTo(right, top);

        // Solution border
        cb.moveTo(left, solutionBorderTop);
        cb.lineTo(right, solutionBorderTop);

        // Rules bottom border
        cb.moveTo(left, scrambleBorderTop);
        cb.lineTo(rulesRight, scrambleBorderTop);

        // Rules right border
        cb.lineTo(rulesRight, gradeBottom);

        // Grade bottom border
        cb.moveTo(competitorInfoLeft, gradeBottom);
        cb.lineTo(right, gradeBottom);

        // Competitor info bottom border
        cb.moveTo(competitorInfoLeft, competitorInfoBottom);
        cb.lineTo(right, competitorInfoBottom);

        // Competitor info left border
        cb.moveTo(competitorInfoLeft, gradeBottom);
        cb.lineTo(competitorInfoLeft, top);

        // Solution lines
        int availableSolutionWidth = right - left;
        int availableSolutionHeight = scrambleBorderTop - bottom;
        int lineWidth = 25;
        // int linesX = (availableSolutionWidth/lineWidth + 1)/2;
        int linesX = 10;
        int linesY = (int) Math.ceil(1.0 * WCA_MAX_MOVES_FMC / linesX);

        cb.setLineWidth(LINE_THICKNESS);
        cb.stroke();

        //              int allocatedX = (2*linesX-1)*lineWidth;
        int excessX = availableSolutionWidth - linesX * lineWidth;
        int moveCount = 0;
        solutionLines:
        for (int y = 0; y < linesY; y++) {
          for (int x = 0; x < linesX; x++) {
            if (moveCount >= WCA_MAX_MOVES_FMC) {
              break solutionLines;
            }
            int xPos = left + x * lineWidth + (x + 1) * excessX / (linesX + 1);
            int yPos = solutionBorderTop - (y + 1) * availableSolutionHeight / (linesY + 1);
            cb.moveTo(xPos, yPos);
            cb.lineTo(xPos + lineWidth, yPos);
            moveCount++;
          }
        }

        float UNDERLINE_THICKNESS = 0.2f;
        cb.setLineWidth(UNDERLINE_THICKNESS);
        cb.stroke();

        cb.beginText();
        int availableScrambleSpace = right - left - 2 * padding;
        int scrambleFontSize = 20;
        String scrambleStr = "Scramble: " + scramble;
        float scrambleWidth;
        do {
          scrambleFontSize--;
          scrambleWidth = bf.getWidthPoint(scrambleStr, scrambleFontSize);
        } while (scrambleWidth > availableScrambleSpace);

        cb.setFontAndSize(bf, scrambleFontSize);
        int scrambleY =
            3 + solutionBorderTop + (scrambleBorderTop - solutionBorderTop - scrambleFontSize) / 2;
        cb.showTextAligned(PdfContentByte.ALIGN_LEFT, scrambleStr, left + padding, scrambleY, 0);
        cb.endText();

        int availableScrambleWidth = right - rulesRight;
        int availableScrambleHeight = gradeBottom - scrambleBorderTop;
        Dimension dim =
            scrambleRequest.scrambler.getPreferredSize(
                availableScrambleWidth - 2, availableScrambleHeight - 2);
        PdfTemplate tp = cb.createTemplate(dim.width, dim.height);
        Graphics2D g2 = new PdfGraphics2D(tp, dim.width, dim.height, new DefaultFontMapper());

        try {
          Svg svg = scrambleRequest.scrambler.drawScramble(scramble, scrambleRequest.colorScheme);
          drawSvgToGraphics2D(svg, g2, dim);
        } catch (InvalidScrambleException e) {
          l.log(Level.INFO, "", e);
        } finally {
          g2.dispose();
        }

        cb.addImage(
            Image.getInstance(tp),
            dim.width,
            0,
            0,
            dim.height,
            rulesRight + (availableScrambleWidth - dim.width) / 2,
            scrambleBorderTop + (availableScrambleHeight - dim.height) / 2);

        ColumnText ct = new ColumnText(cb);

        int fontSize = 15;
        int marginBottom = 10;
        int offsetTop = 27;
        boolean showScrambleCount = scrambleRequest.scrambles.length > 1;
        if (showScrambleCount) {
          offsetTop -= fontSize + 2;
        }

        cb.beginText();
        cb.setFontAndSize(bf, fontSize);
        cb.showTextAligned(
            PdfContentByte.ALIGN_CENTER,
            globalTitle,
            competitorInfoLeft + (right - competitorInfoLeft) / 2,
            top - offsetTop,
            0);
        offsetTop += fontSize + 2;
        cb.endText();

        cb.beginText();
        cb.setFontAndSize(bf, fontSize);
        cb.showTextAligned(
            PdfContentByte.ALIGN_CENTER,
            scrambleRequest.title,
            competitorInfoLeft + (right - competitorInfoLeft) / 2,
            top - offsetTop,
            0);
        cb.endText();

        if (showScrambleCount) {
          cb.beginText();
          offsetTop += fontSize + 2;
          cb.setFontAndSize(bf, fontSize);
          cb.showTextAligned(
              PdfContentByte.ALIGN_CENTER,
              "Scramble " + (i + 1) + " of " + scrambleRequest.scrambles.length,
              competitorInfoLeft + (right - competitorInfoLeft) / 2,
              top - offsetTop,
              0);
          cb.endText();
        }

        offsetTop += fontSize + marginBottom;

        cb.beginText();
        fontSize = 15;
        cb.setFontAndSize(bf, fontSize);
        cb.showTextAligned(
            PdfContentByte.ALIGN_LEFT,
            "Competitor: __________________",
            competitorInfoLeft + padding,
            top - offsetTop,
            0);
        offsetTop += fontSize + marginBottom;
        cb.endText();

        cb.beginText();

        fontSize = 15;
        cb.setFontAndSize(bf, fontSize);
        cb.showTextAligned(
            PdfContentByte.ALIGN_LEFT, "WCA ID:", competitorInfoLeft + padding, top - offsetTop, 0);

        cb.setFontAndSize(bf, 19);
        int wcaIdLength = 63;
        cb.showTextAligned(
            PdfContentByte.ALIGN_LEFT,
            "_ _ _ _  _ _ _ _  _ _",
            competitorInfoLeft + padding + wcaIdLength,
            top - offsetTop,
            0);

        offsetTop += fontSize + (int) (marginBottom * 1.8);
        cb.endText();

        cb.beginText();
        fontSize = 11;
        cb.setFontAndSize(bf, fontSize);
        cb.showTextAligned(
            PdfContentByte.ALIGN_CENTER,
            "DO NOT FILL IF YOU ARE THE COMPETITOR",
            competitorInfoLeft + (right - competitorInfoLeft) / 2,
            top - offsetTop,
            0);
        offsetTop += fontSize + marginBottom;
        cb.endText();

        cb.beginText();
        fontSize = 11;
        cb.setFontAndSize(bf, fontSize);
        cb.showTextAligned(
            PdfContentByte.ALIGN_CENTER,
            "Graded by: _______________ Result: ______",
            competitorInfoLeft + (right - competitorInfoLeft) / 2,
            top - offsetTop,
            0);
        offsetTop += fontSize + marginBottom;
        cb.endText();

        cb.beginText();
        cb.setFontAndSize(bf, 25f);
        int MAGIC_NUMBER = 40; // kill me now
        cb.showTextAligned(
            PdfContentByte.ALIGN_CENTER,
            "Fewest Moves",
            left + (competitorInfoLeft - left) / 2,
            top - MAGIC_NUMBER,
            0);
        cb.endText();

        com.itextpdf.text.List rules = new com.itextpdf.text.List(com.itextpdf.text.List.UNORDERED);
        rules.add("Notate your solution by writing one move per bar.");
        rules.add("To delete moves, clearly erase/blacken them.");
        rules.add("Face moves F, B, R, L, U, and D are clockwise.");
        rules.add("Rotations x, y, and z follow R, U, and F.");
        rules.add("' inverts a move; 2 doubles a move. (e.g.: U', U2)");
        rules.add("w makes a face move into two layers. (e.g.: Uw)");
        rules.add("A [lowercase] move is a cube rotation. (e.g.: [u])");

        ct.addElement(rules);
        int rulesTop = competitorInfoBottom + 55;
        ct.setSimpleColumn(
            left + padding,
            scrambleBorderTop,
            competitorInfoLeft - padding,
            rulesTop,
            0,
            Element.ALIGN_LEFT);
        ct.go();

        rules = new com.itextpdf.text.List(com.itextpdf.text.List.UNORDERED);
        rules.add("You have 1 hour to find a solution.");
        rules.add("Your solution length will be counted in OBTM.");
        int maxMoves = WCA_MAX_MOVES_FMC;
        rules.add("Your solution must be at most " + maxMoves + " moves, including rotations.");
        rules.add(
            "Your solution must not be directly derived from any part of the scrambling algorithm.");
        ct.addElement(rules);
        MAGIC_NUMBER = 150; // kill me now
        ct.setSimpleColumn(
            left + padding,
            scrambleBorderTop,
            rulesRight - padding,
            rulesTop - MAGIC_NUMBER,
            0,
            Element.ALIGN_LEFT);
        ct.go();

        doc.newPage();
      }
    } else {
      Rectangle pageSize = doc.getPageSize();

      float sideMargins = 100 + doc.leftMargin() + doc.rightMargin();
      float availableWidth = pageSize.getWidth() - sideMargins;
      float vertMargins = doc.topMargin() + doc.bottomMargin();
      float availableHeight = pageSize.getHeight() - vertMargins;
      if (scrambleRequest.extraScrambles.length > 0) {
        availableHeight -= 20; // Yeee magic numbers. This should make space for the headerTable.
      }
      int scramblesPerPage =
          Math.min(MAX_SCRAMBLES_PER_PAGE, scrambleRequest.getAllScrambles().size());
      int maxScrambleImageHeight =
          (int) (availableHeight / scramblesPerPage - 2 * SCRAMBLE_IMAGE_PADDING);

      int maxScrambleImageWidth =
          (int)
              (availableWidth / 2); // We don't let scramble images take up more than half the page
      if (scrambleRequest.scrambler.getShortName().equals("minx")) {
        // TODO - If we allow the megaminx image to be too wide, the
        // megaminx scrambles get really tiny. This tweak allocates
        // a more optimal amount of space to the scrambles. This is possible
        // because the scrambles are so uniformly sized.
        maxScrambleImageWidth = 190;
      }

      Dimension scrambleImageSize =
          scrambleRequest.scrambler.getPreferredSize(maxScrambleImageWidth, maxScrambleImageHeight);

      // First do a dry run just to see if any scrambles require highlighting.
      // Then do the real run, and force highlighting on every scramble
      // if any scramble required it.
      boolean forceHighlighting = false;
      for (boolean dryRun : new boolean[] {true, false}) {
        String scrambleNumberPrefix = "";
        TableAndHighlighting tableAndHighlighting =
            createTable(
                docWriter,
                doc,
                sideMargins,
                scrambleImageSize,
                scrambleRequest.scrambles,
                scrambleRequest.scrambler,
                scrambleRequest.colorScheme,
                scrambleNumberPrefix,
                forceHighlighting);
        if (dryRun) {
          if (tableAndHighlighting.highlighting) {
            forceHighlighting = true;
            continue;
          }
        } else {
          doc.add(tableAndHighlighting.table);
        }

        if (scrambleRequest.extraScrambles.length > 0) {
          PdfPTable headerTable = new PdfPTable(1);
          headerTable.setTotalWidth(new float[] {availableWidth});
          headerTable.setLockedWidth(true);

          PdfPCell extraScramblesHeader = new PdfPCell(new Paragraph("Extra scrambles"));
          extraScramblesHeader.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE);
          extraScramblesHeader.setPaddingBottom(3);
          headerTable.addCell(extraScramblesHeader);
          if (!dryRun) {
            doc.add(headerTable);
          }

          scrambleNumberPrefix = "E";
          TableAndHighlighting extraTableAndHighlighting =
              createTable(
                  docWriter,
                  doc,
                  sideMargins,
                  scrambleImageSize,
                  scrambleRequest.extraScrambles,
                  scrambleRequest.scrambler,
                  scrambleRequest.colorScheme,
                  scrambleNumberPrefix,
                  forceHighlighting);
          if (dryRun) {
            if (tableAndHighlighting.highlighting) {
              forceHighlighting = true;
              continue;
            }
          } else {
            doc.add(extraTableAndHighlighting.table);
          }
        }
      }
    }
    doc.newPage();
  }