iTEXT: PDF Pada Aplikasi Berbasis Web

iTEXT juga dapat digunakan pada aplikasi Java berbasis Web, misalnya pada servlet yang menghasilkan file PDF. Sebagai contoh, berikut ini adalah servlet yang menggunakan iTEXT untuk membuat file PDF on-the-fly:

package aplikasi;

public class LatihanPDF extends 
  HttpServlet {

 @Override
 protected void doGet(
    HttpServletRequest req, 
    HttpServletResponse resp)
 throws ServletException, IOException {
	
  Document document = new Document();
  resp.setContentType("application/pdf");
  try {
    PdfWriter.getInstance(document, 
       resp.getOutputStream());
    document.open();
    document.add(new 
Paragraph("Hi, apa kabar? 
Ini seharusnya muncul di browser."));
  } catch (DocumentException e) {
    e.printStackTrace();
  }
  document.close();
 }
}

Aku juga tidak lupa menambahkan definisi berikut ke web.xml:

<servlet>
  <servlet-name>LatihanPDF</servlet-name>
  <servlet-class>aplikasi.LatihanPDF</servlet-class>		
</servlet>
<servlet-mapping>
  <servlet-name>LatihanPDF</servlet-name>

  <url-pattern>/aplikasi/latihanPDF</url-pattern>
</servlet-mapping>

Saat aku mencoba URL http://localhost:8080/sample/aplikasi/latihanPDF (URL tergantung pada konfigurasi application server) di Internet Explorer 7 dan FireFox 3, kedua browser tersebut secara otomatis menampilkan isi PDF di dalam browser melalui plug-in yang tersedia saat instalasi Adobe Reader.

Jika aku ingin PDF tersebut tidak langsung muncul di dalam browser, tetapi yang muncul adalah pilihan bagi user untuk mendownload PDF tersebut ke komputer-nya, aku dapat menambahkan baris berikut di program di atas:

resp.setHeader("Content-Disposition", 
  " attachment; filename=\"latihan.pdf\"");

Bagi yang ingin menggunakan JSP untuk menghasilkan PDF, perlu mengingat bahwa JSP ditujukan untuk menghasilkan teks HTML, sementara PDF adalah data binary. Lagipula, JSP nantinya juga akan diterjemahkan menjadi servlet. Salah satu masalah kalau menggunakan JSP adalah kita harus berhati-hati dalam menulis whitespace (karakter tidak terlihat) seperti Enter (ganti baris), spasi, dan tab. Jika ingin membuat PDF, karena berupa binary, maka tidak boleh ada karakter-karakter whitespace yang tidak diinginkan tersebut diluar <% .. %>. Berikut ini adalah contoh JSP sederhana yang menghasilkan PDF:

<%@
page import=
 "java.io.*,com.lowagie.text.*,com.lowagie.text.pdf.*"
%><%
  response.setContentType("application/pdf");
  Document document = new Document();
  ByteArrayOutputStream buffer = new 
     ByteArrayOutputStream();
  PdfWriter.getInstance(document,buffer);
  document.open();
  document.add(new 
      Paragraph("TEST.. INI JSP MENGHASILKAN PDF"));
  document.close();
  DataOutput output =
     new DataOutputStream(
        response.getOutputStream());
  byte[] bytes = buffer.toByteArray();
  response.setContentLength(bytes.length);
  for (int i=0; i<bytes.length; i++) {
    output.writeByte(bytes[i]);
  }
%>

Perhatikan bahwa aku tidak memberikan sedikitpun spasi atau ganti baris di luar <% … %>, termasuk di baris terakhir, tidak ada ganti baris atau spasi setelah karakter &gt. Bagaimana jika seandainya lupa? Misalnya, kode di atas aku rubah menjadi:

<%@
page import="java.io.*,com.lowagie.text.*,com.lowagie.text.pdf.*"
%> <== [ini ganti baris]
<%
  response.setContentType("application/pdf");
  Document document = new Document();
  ...
%>

Aku akan mendapatkan pesan kesalahan getOutputStream() has already been called for this response. Coba perhatikan hasil terjemahan JSP di atas ke dalam servlet yang kira-kira sebagai berikut:

response.setContentType("text/html");
ageContext = _jspxFactory.getPageContext(this, 
   request, response,
   null, true, 8192, true);
_jspx_page_context = pageContext;
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
_jspx_out = out;

out.write('\r');
out.write('\n');

response.setContentType("application/pdf");
Document document = new Document();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();

...

DataOutput output =
 new DataOutputStream(response.getOutputStream());

...

Perhatikan bahwa gara-gara ada sebuah ganti baris, atau karakter “\r\n”, contentType telah terlebih dahulu diatur menjadi text/html dan outputStream telah dipakai untuk menuliskan HTML berisi ganti baris ini. Tentu saja Java protes saat aku ingin mengubah content ke binary PDF.

Iklan

iTEXT: Aplikasi Java Swing Dan PDF

Jika aku mengembangkan aplikasi berbasis Swing, iTEXT memberikan kesempatan bagiku untuk mencetak komponen Swing ke dalam PDF dengan mudah. Salah satu contoh klasik adalah mencetak JTable langsung ke PDF. Pada kode berikut ini aku membuat sebuah JFrame yang terdiri atas sebuah JTable dan JButton untuk mencetak ke PDF:

tblUtama = new JTable(10,3);
btnCetakPDF = new JButton("CETAK PDF");
btnCetakPDF.addActionListener(
  new ActionListener() {

 public void actionPerformed(ActionEvent e) {

   Document document = new
     Document(new Rectangle(300,300));
   try {
     PdfWriter writer = PdfWriter.getInstance(
       document,
       new FileOutputStream("c:\\latihan.pdf"));
     document.open();
     PdfContentByte cb =
       writer.getDirectContent();
     Graphics2D g2 = cb.createGraphics(300,300);
     tblUtama.print(g2);
     g2.dispose();
     document.close();
   } catch (Exception ex) {
     ex.printStackTrace();
   }
 }
});
setLayout(new BorderLayout());
add(tblUtama, BorderLayout.CENTER);
add(btnCetakPDF, BorderLayout.SOUTH);

Jika program di atas dijalankan, JTable yang telah di-isi akan di-render apa adanya ke file PDF. Kegunaan lain misalnya untuk mencetak isi JTextPane secara langsung ke file PDF.

Aku akan mencoba membuat sebuah program sederhana yang menarik. Program ini terdiri atas sebuah panel yang memungkinkan pengguna untuk menggambar dengan men-drag mouse di panel. Turunan dari JPanel ini, aku beri nama PanelGambar, yang telah dilengkapi MouseMotionListener dan MouseListener untuk keperluan menggambar. Data gambar sendiri aku letakkan pada class DataGambar yang berisi List yang mengandung List of Point. Setiap List of Point ini mewakili titik-titik yang dilalui mouse saat drag dimulai hingga drag selesai.

Setelah selesai menggambar di PanelGambar, user dapat menekan tombol btnCetakPDF untuk mendapatkan versi PDF dari apa yang sudah digambarnya di PanelGambar. Kode lengkap programnya adalah:

public class LatihanSwing extends JFrame {

  private PanelGambar kanvas =
    new PanelGambar();
  private JButton btnCetakPDF =
    new JButton("CETAK PDF");

  public LatihanSwing() {
    super("Latihan iTEXT");

    btnCetakPDF.addActionListener(
      new ActionListener() {

      @Override
      public void actionPerformed(
        ActionEvent e) {
  Document doc = new Document(new
    Rectangle(kanvas.getWidth(),kanvas.getHeight()));
  try {
    PdfWriter writer = PdfWriter.getInstance(doc,
      new FileOutputStream("c:\\latihan.pdf"));
    doc.open();
    PdfContentByte cb = writer.getDirectContent();
    Graphics2D g2 = (Graphics2D) cb.createGraphics(
       kanvas.getWidth(),kanvas.getHeight());
    kanvas.print(g2);
    g2.dispose();
    doc.close();
  } catch (Exception ex) {
    ex.printStackTrace();
  }
  }
  });
  setLayout(new BorderLayout());
  add(kanvas, BorderLayout.CENTER);
  add(btnCetakPDF, BorderLayout.SOUTH);

  pack();
  setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  setVisible(true);
}

public static void main(String[] args) {
  new LatihanSwing();
}

}

class PanelGambar extends JPanel {

  private boolean reset = true;
  private DataGambar dataGambar = new DataGambar();

  public PanelGambar() {

   super.addMouseMotionListener(
      new MouseMotionListener() {

   @Override
   public void mouseDragged(MouseEvent e) {
    if (reset) {
      reset = false;
      dataGambar.reset();
    } else {
      dataGambar.tambahPoint(e.getPoint());
    }
    repaint();
   }

   @Override
   public void mouseMoved(MouseEvent e) {}
  });

  super.addMouseListener(new MouseListener() {

    @Override
    public void mouseClicked(MouseEvent e) {}

    @Override
    public void mouseEntered(MouseEvent e) {}

    @Override
    public void mouseExited(MouseEvent e) {}

    @Override
    public void mousePressed(MouseEvent e) {}

    @Override
    public void mouseReleased(MouseEvent e) {
      reset = true;
    }

  });
 }

 @Override
 public Dimension getPreferredSize() {
   return new Dimension(500,300);
 }

 @Override
 protected void paintComponent(Graphics g) {
   super.paintComponent(g);

   List<List<Point>> lstDataGambar =
     dataGambar.getDataGambar();
   for (List<Point> lstSegment : lstDataGambar) {
     Point prevPoint = null;
     for (Point point : lstSegment) {
       if (prevPoint!=null) {
	  g.drawLine(
            (int)prevPoint.getX(),
            (int)prevPoint.getY(),
            (int)point.getX(),
            (int)point.getY());
       }
       prevPoint = point;
     }
   }
 }

}

class DataGambar {

  private List<List<Point>> lstDataGambar;
  private List<Point> lstCurrentSegment;

  public DataGambar() {
    lstDataGambar = new ArrayList<List<Point>>();
  }

  public void tambahPoint(Point point) {
    lstCurrentSegment.add(point);
  }

  public void reset() {
    lstCurrentSegment = new ArrayList<Point>();
    lstDataGambar.add(lstCurrentSegment);
  }

  public List<List<Point>> getDataGambar() {
    return lstDataGambar;
  }

}

iTEXT: Menggambar Ke File PDF

iTEXT dapat membuat gambar barcode secara otomatis. Ia menyediakan beberapa class untuk merepresentasikan barcode seperti Barcode128, BarcodeInter25, dan Barcode39. Berikut ini adalah contoh membuat barcode yang merepresentasikan kode “BARANG BAGUS”:

PdfWriter writer = PdfWriter.getInstance(
  document,
  new FileOutputStream("c:\\latihan.pdf"));
document.open();

Barcode39 barcode = new Barcode39();
barcode.setCode("BARANG BAGUS");
document.add(barcode.createImageWithBarcode(
  writer.getDirectContent(),
  null, null));

Untuk menggambar secara bebas ke PDF melalui iTEXT, aku dapat menggunakan PdfContentByte. Pada coding di atas, aku mendapatkan PdfContentByte melalui writer.getDirectContent(). Untuk mendapatkan contect layer yang lebih bawah (yang akan ditimpa oleh directContext), aku dapat menggunakan writer.getDirectContentUnder(). Pola penggambaran pada file PDF diwakili oleh tujuh aksi yaitu:

  • moveTo(x,y), memindahkan posisi ke koordinat (x,y).
  • lineTo(x,y), memindahkan posisi ke koordinat (x,y), serta membuat segmen garis dari koordinat semula hingga koordinat (x,y).
  • curveTo(x1,y1,x2,y2,x3,y3), memindahkan posisi ke koordinat (x3,y3), serta membuat kurva Bezier berdasarkan control point (x1,y1) dan (x2,y2).
  • curveTo(x2,y2,x3,y3), memindahkan posisi ke koordinat (x3,y3), serta membuat kurva Bezier berdasarkan control point yang terakhir dipakai dan (x2,y2).
  • curveFromTo(x1,y1,x3,y3), memindahkan posisi ke koordinat (x3,y3), serta membuat kurva Bezier berdasarkan control point (x1,y1) dan (x3,y3).
  • closePath(), menutup subpath dengan menambahkan segment garis dari posisi sekarang ke awal dari subpath.
  • rectangle(x,y,width,height).

Jangan lupa bahwa setelah digambar, pola harus diberi warna atau di-cat, misalnya dengan method berikut:

  • stroke(), memberi warna pada garis (tidak men-fill bentuk).
  • closePathStroke(), sama seperti melakukan closePath() diikuti dengan stroke().
  • fill(), mengisi sebuah bentuk dengan warna.
  • eoFill(), mengisi sebuah bentuk denga warna dengan even-odd rule.
  • fillStroke(), sama seperti melakukan fill() diikuti dengan stroke().
  • eoFillStroke().
  • closePathFillStroke(), sama seperti melakukan closePath() diikuti dengan fillStroke().
  • closePathEoFillStroke().
  • newPath(), mengakhiri path tanpa memberi stroke atau fill.

Secara default, koordinat yang dipakai iTEXT berdasarkan pada titik (0,0) di pojok kiri bawah (berbeda dengan Graphics2D milik Java). Berikut ini adalah contoh menggambar bentuk bintang di PDF:

PdfWriter writer = PdfWriter.getInstance(
  document,
  new FileOutputStream("c:\\latihan.pdf"));

document.open();

PdfContentByte cb =
  writer.getDirectContent();
cb.moveTo(100f, 300f);
cb.lineTo(300f, 300f);
cb.lineTo(100f, 200f);
cb.lineTo(200f, 400f);
cb.lineTo(300f, 200f);

cb.setColorStroke(Color.BLUE);
cb.closePathStroke();

Jika seandainya bintang di atas di fill dengan method closePathEoFillStroke(), bagian tengahnya tidak akan di-fill sehingga terlihat perbedaan overlap-nya. O ya, PdfContentByte juga menyediakan sejumlah fungsi untuk mempermudah dalam menggambar bentuk dasar, seperti circle() dan ellipse() (jauh lebih mudah ketimbang harus membentuk lingkaran dengan curveTo()).

iTEXT: Manipulasi Element Teks

Bagian paling kecil yang merepresentasikan teks dalam iTEXT adalah Chunk. Chunk mewakili String dengan font, ukuran, dan style yang sama. Kumpulan dari Chunk membentuk Phrase. Salah satu bentuk Phrase adalah Paragraph.

Sebagai latihan, misalnya, aku ingin menulis dengan jenis Font TIMES_ROMAN, ukuran 15, dan style bold, aku dapat menggunakan kode seperti berikut ini:

PdfWriter.getInstance(document, 
  new FileOutputStream("c:\\latihan.pdf"));
document.open();

Font font = new Font(
  Font.TIMES_ROMAN, 
  15, 
  Font.BOLD);

Chunk tulisan = new 
  Chunk("I MISS U", font);

document.add(tulisan);

Jika Chunk mencapai batas halaman, ia tidak akan ganti baris secara otomatis (karena secara default line spacing adalah 0). Oleh sebab itu, aku tidak menggunakan Chunk secara langsung. Misalnya, aku menggunakannya bersama dengan Phrase, seperti pada:

Font font = new Font(
  Font.TIMES_ROMAN, 15, 
  Font.BOLD);
Chunk tulisan = new 
  Chunk("I MISS U", font);
chunk.setUnderline(2f, -2f);

Phrase phrase = new 
  Phrase(tulisan);
phrase.setLeading(20f);
for (int i=0; i<100; i++) {
  document.add(phrase);
  document.add(Chunk.NEWLINE);
}

Kode di atas mencetak 100 baris tulisan “I MISS U” dengan jarak antar baris berupa 20 point. O ya, setUnderline(2f, -2f) akan menyebabkan tulisan digarisbawahi dengan garis setebal 2f dan jarak 2f di bawah baseline.

Jika aku ingin ganti baris secara otomatis, tanpa harus menambahkan document.add(Chunk.NEWLINE), aku dapat menggunakan Paragraph. Lebih dari itu, dengan Paragraph, aku dapat mengatur alignment (rata kiri, rata kanan, rata tengah, atau justified). Berikut ini adalah contohnya:

Font font = new Font(
  Font.TIMES_ROMAN, 15, 
  Font.ITALIC);
font.setColor(Color.PINK);

Phrase kalimat = new Phrase(
  new Chunk("DO U MISS ME?", 
    font));
Paragraph p1 = new 
  Paragraph(kalimat);
			
p1.setAlignment(
  Paragraph.ALIGN_LEFT);
document.add(p1);
			
p1.setAlignment(
  Paragraph.ALIGN_CENTER);
document.add(p1);
			
p1.setAlignment(
  Paragraph.ALIGN_RIGHT);
document.add(p1);

Untuk membuat daftar berurut, aku dapat menggunakan List dan ListItem seperti pada contoh berikut:

Font font = new Font(Font.TIMES_ROMAN, 
  15, Font.ITALIC);
font.setColor(Color.PINK);
Chunk chunk = new 
  Chunk("DO U MISS ME?", font);
			
List list = new List(List.ORDERED);
list.add(new ListItem(chunk));
list.add(new ListItem(chunk));
list.add(new ListItem(chunk));
list.add(new ListItem(chunk));
			
document.add(list);

Kode di atas akan mencetak list berurut mulai dari angka 1,2,3 hingga 4, seperti:

1. DO U MISS ME?
2. DO U MISS ME?
3. DO U MISS ME?
4. DO U MISS ME?

Untuk mengubah HTML atau potongan HTML agar dapat langsung diterjemahkan oleh iTEXT kedalam pdf, kode berikut dapat dipergunakan:

iTEXT: Membuat File PDF Melalui Java

iTEXT adalah API open-source yang dapat dipakai untuk menghasilkan file PDF secara dinamis melalui kode program. Salah satu contoh penggunaannya, misalnya, pada implementasi fungsi “Klik Untuk Versi PDF” di halaman web. Atau contoh lainnya, untuk menghasil report dalam bentuk file PDF (Jasper Reports memakai iTEXT sebagai PDF engine-nya).

Membuat PDF dengan bantuan iTEXT menjadi tugas yang gampang, semudah coding berikut:

Document document = new Document();
try {

  PdfWriter.getInstance(
     document,
     new FileOutputStream(
        "c:\\latihan.pdf"));
  document.open();
  document.add(
     new Paragraph(
        "Hi, ini PDF pertamaku!"));

} catch (Exception e) {
  e.printStackTrace();
}

document.close();

Setelah kode program di atas dijalankan, akan terbentuk file latihan.pdf di c:\. Jika dibuka dengan Adobe Acrobat atau Adobe Reader, file PDF tersebut berisi sebuah tulisan Hi, ini PDF pertamaku.

Untuk mengubah ukuran halaman dan warna latar, aku dapat memberikan sebuah Rectangle saat membuat Document, seperti dengan kode berikut:

Rectangle pageSize = new
  Rectangle(500f, 100f);
pageSize.setBackgroundColor(
  Color.LIGHT_GRAY);
Document document = new
  Document(pageSize);

Potongan kode program di atas akan menghasil PDF dengan halaman 500 point x 100 point, dan background abu-abu.

Bagi yang sudah biasa memakai Graphics2D di Java, iTEXT juga menyediakan cara untuk memanipulasi isi PDF dengan menggunakan object tersebut, seperti yang ditunjukkan kode berikut:

PdfWriter writer = PdfWriter.getInstance(
  document,
  new FileOutputStream(
    "c:\\latihan.pdf"));

document.open();

PdfContentByte contentByte =
  writer.getDirectContent();
Graphics2D g =
  contentByte.createGraphics(500f, 100f);
g.drawRect(10,10,480,80);
g.drawString(
  "Hi, ini gambar lewat Java",
  20,20);
g.dispose();

Untuk membaca PDF yang sudah ada, aku dapat menggunakan PdfReader, seperti pada contoh kode berikut:

PdfReader reader = new PdfReader(
  "c:\\iTEXT in action.pdf");

System.out.println("Version = " +
  reader.getPdfVersion());
System.out.println("Number of Pages = " +
  reader.getNumberOfPages());

Rectangle rect = reader.getPageSize(1);
System.out.println("Page #1 Size = " +
  rect.getWidth() + " x " +
  rect.getHeight());

List lstDaftarIsi =
  SimpleBookmark.getBookmark(reader);

System.out.println("Daftar Isi:");
for (Map itemDaftarIsi : lstDaftarIsi) {
  System.out.println(
    itemDaftarIsi.get("Title"));
}