วันอังคารที่ 23 พฤศจิกายน พ.ศ. 2553

VB.NET 2005 มีอะไรใหม่ (What's new in VB.NET 2005)

VB.NET 2005 มีอะไรใหม่ (What's new in VB.NET 2005)
มีหลายคนสงสัยว่า VB8 หรือ VB.NET 2005 ปรับปรุงอะไรเพิ่มขึ้นบ้างจากเวอร์ชั่นก่อน ผมเลยค้นคว้ามาบอกกันไว้ครับ เผื่อหลายๆ คนต้องเอาไปอธิบายคนที่สงสัยอีกหลายๆ คน

VB.NET 2005 นั้น มาพร้อมกับ .NET 2.0 ซึ่งได้ปรับปรุงตัวแปลภาษาไป 14 ข้อดังนี้

1. เพิ่ม Continue Statement
คำสั่ง Continue จะใช้ร่วมกับคำสั่ง Do, For, While มีประโยชน์ในการจบการวนครั้งปัจจุบัน แล้วไปเริ่มวนครั้งถัดไปเลย ตัวอย่างการใช้งาน


'การใช้ Continue เพื่อทำลูปครั้งถัดไปเลย
Dim Result As Byte = 0
For Count As Byte = 0 To 4
If Count = 2 Then Continue For
Result += Count
Next Count



จากโค้ดด้านบน การทำงานจะผ่านบรรทัด Result += Count เพียง 4 ครั้งเท่านั้น ทำให้ตัวแปร Result สุดท้ายมีค่าเป็น 0 + 1 +3 + 4 = 8 เพราะในรอบที่ 3 คำสั่ง Continue จะทำงาน ซึ่งมีผลให้ทำงาน ที่ Next ถัดไปแทน

2. เพิ่มความสามารถในการเข้าถึง Form แบบ Visual Basic 6
ถ้าใครเคยใช้ VB6 แล้วเปลี่ยนมาใช้ VB.NET 2003 จะพบว่า สิ่งที่ต้องปรับตัวมากที่สุด คือ เรื่อง Object-Oreinted Programming ซึ่งแม้กระทั่ง Form ที่ใช้ ก็ต้องคิดแบบ OO นั้นคือ Form ทุก Form ต้องเป็น Class ดังนั้นจะเรียกใช้ Form ใดๆได้ ก็ต้องสร้าง Instance หรือ Object ของ Form นั้นๆก่อนเสมอ ซึ่งเป็นเรื่องวุ่นวายมากสำหรับคนที่คุ้นเคย VB6

เมื่อมีผู้เรียกร้องเข้ามามาก ว่าความง่ายของ VB หายไปเยอะ ทางผู้พัฒนา VB Compiler จึงนำ concept เดิมมาใช้ นั้นคือ สามารถเข้าถึงหรือเรียกใช้ Form อื่นๆ ได้โดยไม่ต้องสร้าง Object ก่อน ดังตัวอย่างข้างล่าง

จากเดิมใน VB.NET 2003 เมื่อต้องใช้งาน Form อื่นๆ

'Form1 เรียก Form2 จะต้องสร้าง Object ของ Form2 ก่อนเสมอ

Public Class Form1
Dim objForm2 As New Form2

Sub ShowForm2()
objForm5.Show()
End Sub
End Class


เมื่อต้องใช้งาน Form อื่นๆ ใน VB.NET 2005 ก็สามารถงานได้ง่ายๆ (โดยแบบเดิมก็ยังใช้ได้อยู่)

'Form1 เรียก Form2 โดยไม่ต้องสร้าง Object ของ Form2 เลย
Public Class Form1
Sub ShowForm2()
Form2.Show()
End Sub
End Class

3. เพิ่ม IsNot Operator
จากเดิมที่เวลาเปรียบเทียบ ตัวแปรอ็อบเจ็ค ว่าชี้ไปที่ Object เดียวกันรึเปล่า ก็ใช้คำสั่ง Is หรือ Not ... IS ... ซึ่ง syntax อ่านยาก เพราะต้องมานั่งนิเสธ (~) คำตอบอีกที ทางทีมงานเลยออกคำสั่ง IsNot มาให้ใช้อีกตัว เพื่อทำให้โค้ดอ่านง่ายขึ้น เช่น

'Object1 และ Object2 มาจาก Class เดียวกัน แต่ชี้ไปคนละ Instance
Dim Object1, Object2 As New Object

'ทั้ง 2 บรรทัดข้างล่างนี้ มีความหมายเหมือนกัน
If Not (Object1 Is Object2) Then MsgฺBox("ทั้ง 2 ตัวแปร ไม่ได้ชี้ไปที่อ็อบเจ็คเดียวกัน")
If Object1 IsNot Object2 Then MsgฺBox("ทั้ง 2 ตัวแปร ไม่ได้ชี้ไปที่อ็อบเจ็คเดียวกัน")


4. เพิ่ม TryCast Operator
คำสั่งในการแปลงชนิดข้อมูล (Type Convertion) ในรุ่นก่อน มีอยู่ 2 คำสั่ง คือ CType และ DirectCast ซึ่งทั้ง 2 คำสั่ง เมื่อไม่สามารถแปลงชนิดข้อมูลได้ จะต้องดักข้อผิดพลาดขึ้นด้วยคลาส InvalidCastException แต่สำหรับคำสั่ง TryCast นั้น เมื่อไม่สามารถแปลงชินดข้อมูลได้ คำสังจะ return ค่าออกมาเป็น Nothing เช่น

ในรุ่นก่อนจะมีเพียงคำสั่ง CType และ DirectCast

'กรณีตัวอย่างการใช้ CType และ DirectCast และเปรียบเทียบความแตกต่าง
Try
Dim x As Object = 1.23
Dim y As Integer = CTpye(x, Interger)
'จะมีข้อผิดพลาด(Fails) ในบรรทัดนี้ เพราะในขณะ run-time นั้น x จะมี type เป็น Double
'และการใช้คำสั่ง DirectCast นั้น ชนิดข้อมูลทั้ง 2 ต้องมีความสัมพันธ์แบบ Inheritance กัน
'ซึ่ง Double และ Integer ก็ไม่ได้สืบทอดกันมา
Dim z As Integer = DirectCast(x, Integer)
Catch ex As InvalidCastException
MessageBox.Show("Casting error")
End Try

ตัวอย่างการตรวจสอบ type ก่อน convert และใช้ DirectCast ในการ casting

Dim obj As Object
Dim PrintableObject As IPrintable
If TypeOf obj Is IPrintable Then
PrintableObject = DirectCast(obj, IPrintable)
PrintableObject.Print()
End If

ตัวอย่างคำสั่ง TryCast ที่เป็นการตรวจสอบ type และ convert ให้คำสังเดียว

'กรณีใช้ TryCast จะคล้ายกับคำสั่ง DirectCast เพราะคำสั่งจะ return Nothing
'เมื่อทั้งสองชนิดไม่ได้มีความสัมพันธ์แบบ Inheritance กัน
Dim x As Double = 1.23
Dim y As Object = TryCast(x, Integer)
If y Is Nothing Then
MessageBox.Show("Cannot casting")
End If

(มีฝรั่งบางคนท้วง การตั้ง Keyword การเปลงชนิดข้อมูลของทั้งสามตัวนี้ว่า ไม่คล้องจองกัน คือ CType, DirectCast, TryCast น่าจะตั้งชื่อเป็น Cast, DirectCast, TryCast มากกว่า มันถึงจะลงท้ายด้วย Cast เหมือนกัน ซึงจำง่ายกว่า)

5. เพิ่ม Using Statement
คำสั่งนี้มีประโยชน์มาก สำหรับผู้ที่ต้องใช้งาน unmaged resource อื่นๆ เช่น File, COM wrapper, SQL Connection ซึ่งการใช้คำสั่ง Using ... End Using จะมีการคืนทรัพยากร (dispose) ให้อัตโนมัติ ซึ่งในรุ่นก่อนจำเป็นต้องใช้ คำสั่ง Try ... End Try เพื่อคืนทรัพยากรให้ระบบ ลองดูตัวอย่างข้างล่างนี้

ตัวอย่าง การคืนทรัพยากรระบบโดยใช้ Try

'ทรัพยากรจะถูกคืนให้ระบบ หลัง Finally Statement
Try
Dim fs As New System.IO.StreamReader("C:\boot.ini")
Console.Write(fs.ReadToEnd)
Catch ex As Exception
Console.Write(ex.message)
Finally
fs.Close()
fs.Dispose()
End Try

ถ้าเปลี่ยนมาใช้ Using ก็จะสะดวกขึ้นมาก

'คำสั่ง Using จะคืนทรัพยากรให้ระบบ เมื่อจบ End Using
Using fs As New System.IO.StreamReader("C:\boot.ini")
Console.Write(fs.ReadToEnd)
End Using

6. เพิ่มวิธีการประกาศ index ของ array โดยระบุ 0 เป็น index เริ่มต้น
ปกติ array ใน VB.NET จะเริ่มจาก 0 อยู่แล้ว แต่เพื่อให้โค้ดอ่านง่ายขึ้น โดยเฉพาะคนที่มาจาก VB6 ซึ่งจะเพิ่มส่วนที่มีการบอกไว้ว่า index ของ array นั้นเริ่มที่ 0 เช่น

Dim Num(0 To 9) As Interger

ก็มีความหมายเดียวกับ

Dim Num(9) As Interger

7. Properties (Get, Set) สามารถกำหนด Access Level ต่างกันได้
เดิมทีนั้น Method Get, Set Access Level จะเหมือนกันโดยปริยาย แต่ใน 2005 สามารถให้ Get และ Set มี Access Level คนละอย่างกันได้ ดังตัวอย่าง

'คลาสนี้จะอนุญาตให้คลาสอื่นที่สืบทอดไป สามารถอ่านค่า salaryValue ได้
'แต่การเขียนจะอนุญาตเฉพาะภายในคลาสตัวเองเท่านั้น
Public Class employee
Private salaryValue As Double
Protected Property salary() As Double
Get
Return salaryValue
End Get
Private Set(ByVal value As Double)
salaryValue = value
End Set
End Property
End Class

8. เพิ่มชนิดข้อมูล 3 Unsigned และ 1 Signed
VB.NET 2005 เพิ่ม Data Type แบบจำนวนเต็ม ที่ไม่มีเครื่องหมาย +,- มาเกี่ยวข้อง หรือพูดง่ายๆ ว่า เป็นค่าบวกอย่างเดียว ได้แก่ UShort, UInteger, ULong และเพิ่ม SByte ที่มีเครื่องหมาย +/- ด้วย รายละเอียดดังนี้

UShort มีขนาด 16 bits (2 bytes) เก็บค่าได้ตั้งแต่ 0 ถึง 65,535
UInterger มีขนาด 32 bits (4 bytes) เก็บค่าได้ตั้งแต่ 0 ถึง 4,294,967,295
ULong มีขนาด 64 bits (8 bytes) เก็บค่าได้ตั้งแต่ 0 ถึง 18,446,744,073,709,551,615
SByte มีขนาด 8 bits (1 bytes) เก็บค่าได้ตั้งแต่ -127 ถึง 128

9. เพิ่มชนิดข้อมูล Nullable
ปกติในตัวแปรแบบ Value Type จะเก็บค่าใดค่าหนึ่งตามชนิดข้อมูล แม้จะไม่ได้ใส่ค่าใดๆ ไป ค่าในตัวแปรก็จะมีค่าโดย Default อยู่แล้ว แต่ใน VB.NET 2005 ได้เพิ่มความสามารถให้ตัวแปรไม่มีค่าเริ่มต้น หรือไม่เก็บค่าใดๆ เลยก็ได้ ด้วยคำสั่ง Nullable เช่น

'การประกาศให้ตัวแปรไม่เก็บค่าใดๆ
Dim Number As Nullable(Of Integer)
Number = 20

'กำหนดให้ตัวแปร Number ไม่มีค่าใดๆ
Number = Nothing

'ตรวจสอบค่าในตัวแปร Number ใช้ property ชื่อ HasValue (ไม่สามารถใช้ Is Nothing ในการตรวจสอบได้)
If Number.HasValue Then
MsgBox("Value is " & Number.Value)
Else
MsgBox("No Value")
End If

์ี10. เพิ่มความสามารถในการทำ Operator Overloading
แม้จะไม่มีสถานะการณ์ที่ต้องให้ใช้ Operator Overloading บ่อยๆ แต่มันก็เป็นความสามารถด้าน OO ที่ Advance และมีประโยชน์มาก เพราะบางครั้งเราก็ต้องการใช้ Operator ที่มีอยู่ในความหมายอื่นๆ หรือใช้กับ operands ที่เป็นอ็อบเจ็คอื่นๆ เช่น operator + ปกติแล้วจะใช้กับ operands 2 ตัว ที่เป็นตัวเลข (primitive type) หรืออ็อบเจ็คตัวเลข (reference type) เท่านั้น อันหมายถึงการเอาค่าตัวเลข 2 ตัว มาบวกกัน แต่ถ้าต้องการใช้กับอ็อบเจ็คอื่นๆ ก็ต้อง overload มาปรับแต่งเอง เช่น

ต้องการ overload ตัว operator เครื่องหมาย + เพื่อให้สามารถรวมค่าความยาวของอ็อบเจ็คดินสอได้

'สร้างคลาสดินสอ ที่มีการใช้ Overload เครื่องหมาย + ไว้
Public Class Pencil
Private Length As Integer

Public Sub New(ByVal Length As Integer)
Me.Length = Length
End Sub

Public Overrides Function ToString() As String
Return Length.ToString()
End Function

Public Shared Operator +(ByVal PencilA As Pencil, _
ByVal PencilB As Pencil) As Integer

Return PencilA.Length + PencilB.Length
End Operator
End Class

'สร้างอ็อบเจ็คดินสอ แล้วใช้ operator + จะไม่เกิด error เพราะมีการกำหนดให้ + ใช้ได้กับคลาสนี้แล้ว
'สมมติความยาวดินสอมีหน่วยเป็น เซนติเมตร
Public Shared Sub Main()
Dim PencilA As New Pencil(10)
Dim PencilB As New Pencil(12)

Dim TwoPencilLength As Integer = PencilA + PencilB

Console.WriteLine("รวมความยาวของดินสอ 2 แท่ง เท่ากับ " & _
TwoPencilLength .ToString() & " เซนติเมตร.")
End Sub

ตัวอย่างนี้แสดงให้เห็นถึงการ Overload เครื่องหมาย + มาใช้กับอ็อบเจ็คอื่นๆ ซึ่งที่จริงสามารถดัดแปลง Operator + ไปในความหมายอื่นๆ ก็ได้ เช่น เอาไปเปรียบเทียบหาดินสอที่ยาวที่สุด เป็นต้น

11. เพิ่มความสามารถในการแบ่งโค้ดออกเป็นส่วนๆ โดยใช้ Partial Types
ตัวอย่างที่เห็นได้ชัด คือ การสร้าง Form ใน Visual Studio.NET 2005 เพราะมีการ generate โค้ดแบบ Partial ทำให้เกิดไฟล์ขึ้น 2 ไฟล์ คือไฟล์ xxx.Designer.vb ที่มีการแยกรายละเอียดของการจัดการ Control ต่างๆ และไฟล์ xxx.vb ที่มีเพียง Class...End Class ซึ่งรอการเพิ่ม event procedure และส่วนที่เราต้องเพิ่มเติมลงไป และยังซ่อน xxx.Designer.vb ไว้ข้างหลัง xxx.vb อีกด้วย การแบ่งโค้ดแบบนี้ช่วยโปรแกรมเมอร์มือใหม่ ไม่ต้องเห็นโค้ดที่ยังไม่เข้าใจ และช่วยลดโค้ดที่ไม่ใช่จุดประสงค์หลักของโปรแกรมออกไป (แต่ผมคิดว่าข้อเสียของการทำ Partial คือ ต้องการการจัดการไฟล์เพิ่มขึ้น ซึ่งคงจะยุ่งยากมากขึ้นสำหรับคนที่ใช้ Partial บน IDE ที่ไม่ได้มีการจัดการเรื่องนี้ดีพอ) ตัวอย่างโค้ดคงจะเห็นบ่อยๆ ในผู้ที่ใช้ Visual Studio.NET 2005 แล้ว ผมเลยจะให้ดูโค้ดสั้นๆ ที่ไม่ได้ถูก generate ขึ้นมา

จากคลาสรูปแบบทั่วไป

'ไฟล์ Man.vb
Public Class Man
Sub Walk()
....
End Sub

Sub Run()
....
End Sub
End Class

เมื่อนำมาเขียนแบบ Partial จะต้องเก็บโค้ดไว้เป็น 2 ไฟล์ ซึ่งควรตั้งชื่อไฟล์ให้สอดคลองกันตามวัตถุประสงค์ เพื่อให้ง่ายต่อการ maintain ภายหลัง

'ไฟล์ ManWalk.vb
Partial Public Class Man
Sub Walk()
....
End Sub
End Class

'ไฟล์ ManRun.vb
Partial Public Class Man
Sub Run()
....
End Sub
End Class

12. เพิ่ม Generic Types
เป็นความสามารถด้าน OOP ชั้นสูงอีกตัวที่พูดถึงกันมาก แม้กระทั้ง Java 1.5 ก็ยัง implement ตัว Generic นี้ลงไปด้วย (รวมทั้งเพิ่ง implement เรื่อง enum ที่มีใน VB6 มากนานมากแล้ว) การใช้งาน Generic จะใช้ตรงส่วน Parameter ของ Class, Structure, Interface, Procedure หรือ Delegate ก็ได้ มีประโยชน์ในการกำหนดให้ parameter เป็น data type ที่หลากหลายได้ พูดไปก็คงนึกภาพไม่ออกครับ ต้องลองดูโค้ดเลย

กำหนดคลาสให้มีการใช้ Generic

'คลาสนกแก้ว
Public Class Parrot(Of T)
Sub Speak(ByVal Word As T)
MsgBox(Word.ToString)
End Sub
End Class

การใช้งานคลาสที่เป็น Generic จะพบว่า parameter สามารถเป็น data type ได้หลายแบบ ดังตัวอย่างข้างล่าง ที่เป็นได้ทั้ง Integer หรือ String

'การสร้างอ็อบเจ็คนกแก้ว แล้วให้พูดเลข 3
Dim objParrot As New Parrot(Of Integer)
objParrot.Speak(3)

'การสร้างอ็อบเจ็คนกแก้ว แล้วให้พูดว่า Hello
Dim objParrot As New Parrot(Of String)
objParrot.Speak("Hello")

13. สามารถปรับแต่ง Event ได้ ด้วยคีเวิร์ด Custom
เป็นความสามารถที่ไม่มีใน C# ที่ชาว VB สามารถนำไปโม้ได้ ซึ่งเหนือกว่าด้วยการลองทำ serialize object ดู พบว่าโค้ด Event ยาวกว่า แต่โค้ดสั้นกว่าและใช้ง่ายกว่าเวลานำไปใช้ อ่านเพิ่มเติมที่ .NET 2.0 solution to serialization of objects that raise events และ Better version of the .NET 2.0 event/serialization solution ประโยชน์ของการปรับแต่ง Event คือ การเพิ่มเหตุกาณ์หรือ trigger ให้กับ object ได้ ตามต้องการ ถ้า object นั้นไม่มี Event ที่ต้องการ โครงสร้าง Custom Event เป็นดังนี้

Public Custom Event .... As EventHandler
AddHandler(ByVal value As EventHandler)
...
End AddHandler

RemoveHandler(ByVal value As EventHandler)
...
End RemoveHandler

RaiseEvent(ByVal sender As Object, ByVal e As EventArgs)
...
End RaiseEvent
End Event

AddHandler จะเป็นที่ใส่คำสั่งต่างๆ เมื่อ add event ให้ object
RemoveHandler จะเป็นที่ใส่คำสั่งต่างๆ เมื่อ remove event จาก object
RaiseEvent จะเป็นที่ใส่คำสั่งต่างๆ เมื่อมีการ trig event จาก object

14. เพิ่ม parameter การแจ้งเตือน ในการทำงานของ Compiler
เป็น option ของ compiler ในการแจ้ง warning ซึ่งเพิ่มขึ้นมา 2 option คือ /nowarn หมายถึง ไม่ต้องแจ้ง warning ใดๆ เลย และ /warnaserror หมายถึง ให้แจ้ง warning เหมือนเป็น error เลย ทั้ง 2 option นี้ ถ้าใช้ VS.NET 2005 สามารถเซตได้ที่คลิ๊กขวาที่ project เลือก properties ข้างล่างสุด แล้วไปที่ menu ชื่อ compile จะพบ checkbox 2 ตัว ที่ชื่อ Disable All Warnings ก็คือ /nowarn และ Treat All Warnings as Errors ก็คือ /warnaserror นั้นเอง ตัวอย่างคำสั่งใน command-line ในการ compile

vbc /nowarn

และ

vbc /warnaserror+

วันพุธที่ 10 พฤศจิกายน พ.ศ. 2553

Create Nice-Looking PDFs with PHP and FPDF

Learn how to use the free FPDF library to produce great-looking PDF documents from within your PHP scripts. Full example included.

The PDF format can be a handy way to distribute documents to your visitors. A PDF document is self-contained, looks the same on any PDF reader, and is easy to print. PDFs are often used for reports, brochures, manuals, invoices, product data sheets, and lots more.

Often it's useful to be able to create PDF documents dynamically from within a PHP script. For example, you can produce a custom PDF report based on a user's preferences and include up-to-the-minute data.

In this tutorial I'll walk you through the process of creating a nice-looking, 2-page PDF document using PHP. You'll use the freely-available FPDF library to handle the nitty-gritty of PDF creation.

Here's what your PDF will look like (click to view the finished PDF):





=>info
=>If you want to try out the finished PHP script, you can grab the entire code at the end of the article.

Installing FPDF
To use FPDF, you first need to install the FPDF files on your website. To do this, download the FPDF archive file and extract it to a folder within your website. Call the folder fpdf.

Starting the PHP script
Now that you've installed FPDF, you can start writing your PHP script to produce the PDF report. Create a file called report.php in the same place that you saved your fpdf folder, and open the file in a text editor.

The first thing to do is include the FPDF library so that you can use it. The library is called fpdf.php, and it's inside the fpdf folder that you extracted earlier:

require_once( "fpdf/fpdf.php" );


Now add some variables to configure the report:

// Begin configuration


$textColour = array( 0, 0, 0 );

$headerColour = array( 100, 100, 100 );

$tableHeaderTopTextColour = array( 255, 255, 255 );

$tableHeaderTopFillColour = array( 125, 152, 179 );

$tableHeaderTopProductTextColour = array( 0, 0, 0 );

$tableHeaderTopProductFillColour = array( 143, 173, 204 );

$tableHeaderLeftTextColour = array( 99, 42, 57 );

$tableHeaderLeftFillColour = array( 184, 207, 229 );

$tableBorderColour = array( 50, 50, 50 );

$tableRowFillColour = array( 213, 170, 170 );

$reportName = "2009 Widget Sales Report";

$reportNameYPos = 160;

$logoFile = "widget-company-logo.png";

$logoXPos = 50;

$logoYPos = 108;

$logoWidth = 110;

$columnLabels = array( "Q1", "Q2", "Q3", "Q4" );

$rowLabels = array( "SupaWidget", "WonderWidget", "MegaWidget", "HyperWidget" );

$chartXPos = 20;

$chartYPos = 250;

$chartWidth = 160;

$chartHeight = 80;

$chartXLabel = "Product";

$chartYLabel = "2009 Sales";

$chartYStep = 20000;


$chartColours = array(

array( 255, 100, 100 ),

array( 100, 255, 100 ),

array( 100, 100, 255 ),

array( 255, 255, 100 ),

);


$data = array(

array( 9940, 10100, 9490, 11730 ),

array( 19310, 21140, 20560, 22590 ),

array( 25110, 26260, 25210, 28370 ),

array( 27650, 24550, 30040, 31980 ),

);



// End configuration


These variables make it easy to tweak your report by keeping all the key configuration data at the top of the file. The variables include:


Various colours used in the report.
Each colour is specified as a 3-element array containing red, green and blue values (in the range 0-255).

The report title ("2009 Widget Sales Report") and position.


The URL and dimensions of the company logo image.
You'll include this image in the title page of the report.

The row and column labels for the report data. You'll use these when displaying the table and chart in the report.
Configuration settings for the chart. These include the chart position, dimensions, axis labels, and the step value to use for the Y-axis scale.
The colours to use for the chart bars. As with the other report colours, these are specified as 3-element arrays. There are 4 colours: 1 for each bar in the chart.
The report data. This is a 2-dimensional array containing 4 rows of quarterly sales figures, 1 row per product.

Creating the title page
Now that you've set up the report configuration, you're ready to start building the PDF. First, you'll create the title page for the report. This consists of the company logo and the report name, centred in the page.

Creating the FPDF object

The first thing you need to do is create a new FPDF object to hold the PDF data. The FPDF constructor accepts 3 optional arguments, as follows:

The page orientation. Use 'P' for portrait, or 'L' for landscape. The default is 'P'.
The units to use for the page measurements. Use 'pt', 'mm', 'cm', or 'in'. The default is 'mm'.
The page format. Possible values include 'A3', 'A4', 'A5', 'Letter', and 'Legal'. Or you can specify a custom width and height with a 2-element array. The default value is 'A4'.
For this example, use a portrait orientation, millimetres for units, and A4 format:


/**
Create the title page
**/

$pdf = new FPDF( 'P', 'mm', 'A4' );
Setting the text colour

Now set the colour to use for text in the page. You do this by calling the FPDF SetTextColor() method, passing in the red, green and blue values of the colour to use (each value should be in the range 0-255). Use the colour values in the $textColour array that you created in the configuration section earlier:


$pdf->SetTextColor( $textColour[0], $textColour[1], $textColour[2] );
Creating a page

Now you're ready to create a new blank page in the PDF by calling FPDF's AddPage() method:


$pdf->AddPage();
Adding the logo image

FPDF makes it really easy to insert images in the page. Just call the Image() method, passing in the following arguments:

The path of the image file. This can be an absolute path, or relative to the PHP script. You can also use a URL.
The X and Y position of the top left corner of the image in the PDF. If you omit these then the current position is used.
The width and height of the image in the PDF. If you omit both values then the original image dimensions are used (at 72 DPI). If you omit 1 value then the other value is calculated automatically.
The image type. Allowed values include 'JPG', 'JPEG', 'PNG' and 'GIF' (upper- or lower-case). If you omit this value then FPDF guesses the image format from the filename extension.
A URL to link the image to. This is optional — if you supply a URL then the image becomes a clickable link.
All measurements, such as X and Y positions, widths and heights, use the units you specified when you created the PDF (mm in this case).

You can create your own logo image (make sure it's 300 DPI) or download my example image. Save your logo image in the same folder as your PHP script, then insert the image in the PDF as follows:


// Logo
$pdf->Image( $logoFile, $logoXPos, $logoYPos, $logoWidth );
Setting the font

FPDF lets you choose the font face, style and size to use for text in the PDF. To do this, you call the SetFont() method, which takes the following arguments:

The font family. You can use any of the following standard family names: 'Courier', 'Helvetica', 'Arial', 'Times', 'Symbol', or 'ZapfDingbats'.
The font style. Options include: '' (regular), 'B' (bold), 'I' (italic), and 'U' (underline). You can combine these — for example, 'BU' for bold, underlined text.
The font size. You specify this in points (it defaults to 12 points).
For the report name on the title page, use an Arial Bold 24-point font:


// Report Name
$pdf->SetFont( 'Arial', 'B', 24 );
As well as using the standard fonts, you can import any TrueType or Type 1 font using the AddFont() method. See the manual on the FPDF website for details.

Adding some text

You're now ready to add the report name. FPDF objects have a concept of "the current position", which is where the next piece of text or other element will be inserted. Since you want the report name to appear just after halfway down the page, you first need to move the current position down to this point, which is 160mm from the top of the page (this is stored in the $reportNameYPos configuration variable). To do this, use FPDF's Ln() method, which adds a line break with an optional height value:


$pdf->Ln( $reportNameYPos );
If you don't specify a height for the line break then the height of the last printed cell is used.

Now add the report name. There are a few different ways that you can add text using FPDF. In this case, you'll use the Cell() method, which, amongst other things, lets you easily centre text.

Cell() takes the following arguments (all of them optional):

The cell width and height. If you omit the width then the cell stretches to the right margin. If you omit the height then it defaults to zero.
The string of text to print. Defaults to ''.
Whether to draw a border around the cell. This can be either a number (0=no border, 1=border), or a string containing 1 or more of the following: 'L' (left), 'T' (top), 'R' (right), and 'B' (bottom). Default: 0.
Where to place the current position after drawing the cell. Values can be 0 (to the right), 1 (to the start of the next line), or 2 (below). Default: 0.
The text alignment. Possible values are 'L' (left align), 'C' (centre), or 'R' (right align). Default: 'L'.
Whether the cell background should be filled with colour. true = filled, false = transparent. Default: false.
A URL to link to. If specified, turns the text cell into a link.
Now, use Cell() to insert the report name and centre it, as follows:

$pdf->Cell( 0, 15, $reportName, 0, 0, 'C' );

Creating a page header and intro text
That's the title page done. Now you'll create the page containing some header text, a heading, and some intro text, followed by a table and chart of sales data.

First the page header. Add a new page, then output the page header, which consists of the report name centred at the top of the page using an Arial Regular 17-point font. Use the $headerColour configuration variable to set the text colour:


$pdf->AddPage();
$pdf->SetTextColor( $headerColour[0], $headerColour[1], $headerColour[2] );
$pdf->SetFont( 'Arial', '', 17 );
$pdf->Cell( 0, 15, $reportName, 0, 0, 'C' );
Now for the intro text. First print a heading using the regular text colour and an Arial 20-point font. Since you don't need this text to be centred, you can use the simpler Write() method, which takes the line height, the text to write, and an optional link URL:


$pdf->SetTextColor( $textColour[0], $textColour[1], $textColour[2] );
$pdf->SetFont( 'Arial', '', 20 );
$pdf->Write( 19, "2009 Was A Good Year" );
Now you can output the intro text itself in 12-point Arial. This consists of a 16mm line break, followed by the first paragraph, a 12mm line break, and the final paragraph. Give each line a line height of 6mm:


$pdf->Ln( 16 );
$pdf->SetFont( 'Arial', '', 12 );
$pdf->Write( 6, "Despite the economic downturn, WidgetCo had a strong year. Sales of the HyperWidget in particular exceeded expectations. The fourth quarter was generally the best performing; this was most likely due to our increased ad spend in Q3." );
$pdf->Ln( 12 );
$pdf->Write( 6, "2010 is expected to see increased sales growth as we expand into other countries." );
The Write() method automatically wraps text when it reaches the right side of the page.

Adding a table of data
Next you'll add a table of sales data below the intro text. First, set the border colour for the table. The SetDrawColor() method sets the colour to use for borders and other lines, so you can use this to set the table cell borders. Then move down 15mm to create a space between the intro text and the table:


$pdf->SetDrawColor( $tableBorderColour[0], $tableBorderColour[1], $tableBorderColour[2] );
$pdf->Ln( 15 );
Creating the table header row

The table header row consists of the "PRODUCT", "Q1, "Q2", "Q3", and "Q4" cells. The "PRODUCT" cell uses different text and background colours to the other header cells.

You already know to call the SetTextColor() method to set the text colour to use. To set the background colour to use, you call SetFillColor(), which takes the same RGB arguments as SetTextColor().

To create table cells you use — you guessed it — the Cell() method, specifying the cell width, height, contents, and alignment. You'll also pass 1 as the 4th argument to set a border, and true as the 7th argument to fill the cell with a background colour.

Here, then, is the code to create the table header row. First you set a bold font, then create the left-aligned "PRODUCT" cell with appropriate text and fill colours. Finally, you set colours for the remaining 4 header cells, then loop through the $columnLabels array to display the cells using centred text:


// Create the table header row
$pdf->SetFont( 'Arial', 'B', 15 );

// "PRODUCT" cell
$pdf->SetTextColor( $tableHeaderTopProductTextColour[0], $tableHeaderTopProductTextColour[1], $tableHeaderTopProductText
Colour[2] );
$pdf->SetFillColor( $tableHeaderTopProductFillColour[0], $tableHeaderTopProductFillColour[1], $tableHeaderTopProductFill
Colour[2] );
$pdf->Cell( 46, 12, " PRODUCT", 1, 0, 'L', true );

// Remaining header cells
$pdf->SetTextColor( $tableHeaderTopTextColour[0], $tableHeaderTopTextColour[1], $tableHeaderTopTextColour[2] );
$pdf->SetFillColor( $tableHeaderTopFillColour[0], $tableHeaderTopFillColour[1], $tableHeaderTopFillColour[2] );

for ( $i=0; $iCell( 36, 12, $columnLabels[$i], 1, 0, 'C', true );
}

$pdf->Ln( 12 );
The space character before the word "PRODUCT" in the code helps to pad the word within the table cell so that it isn't hard up against the left edge of the cell. The same trick is used later on with the product names in the left hand column. (Unfortunately there's currently no way to control cell padding with FPDF without extending the class.)

Creating the data rows

The rest of the table consists of 4 rows of sales figures — 1 row for each product — over the 4 quarters. First, set a couple of variables:


// Create the table data rows

$fill = false;
$row = 0;

These variables work as follows:

$fill : Whether a cell should be filled or not. You'll toggle this value every time you've drawn a row to create a striped row effect.

$row : The current row number. This lets you display the appropriate row label for each row as you move through the table.

Now you can loop through the $data array using a foreach loop, printing a row at a time. For each row you create the left header cell containing the product name, and the 4 data cells containing the sales data. Set appropriate text and background colours for each cell as you go.

To display the data cells, use a for loop to move through the 4-element array containing the data, calling the PHP number_format() function to print the sales figure with thousands separators.

After displaying a row, you increment the $row variable, toggle the $fill variable, and use Ln() to move down to the start of the next line, ready to output the next row.

Here's the code for the whole loop:


foreach ( $data as $dataRow ) {

// Create the left header cell
$pdf->SetFont( 'Arial', 'B', 15 );
$pdf->SetTextColor( $tableHeaderLeftTextColour[0], $tableHeaderLeftTextColour[1], $tableHeaderLeftTextColour[2] );
$pdf->SetFillColor( $tableHeaderLeftFillColour[0], $tableHeaderLeftFillColour[1], $tableHeaderLeftFillColour[2] );
$pdf->Cell( 46, 12, " " . $rowLabels[$row], 1, 0, 'L', $fill );

// Create the data cells
$pdf->SetTextColor( $textColour[0], $textColour[1], $textColour[2] );
$pdf->SetFillColor( $tableRowFillColour[0], $tableRowFillColour[1], $tableRowFillColour[2] );
$pdf->SetFont( 'Arial', '', 15 );

for ( $i=0; $iCell( 36, 12, ( ' }

$row++;
$fill = !$fill;
$pdf->Ln( 12 );
}


Creating a bar chart
The last element of the page is a bar chart showing the total sales figures for the 4 products over the whole year.

Calculating scales and bar width

The first thing to do is compute the scales for the X and Y axes. For the X scale this is simply the number of products to display divided by the desired chart width (subtracting some millimetres to allow for space to the left of the bars):


/***
Create the chart
***/

// Compute the X scale
$xScale = count($rowLabels) / ( $chartWidth - 40 );
To compute the Y scale, you need to find the total sales figure for each product, then determine the highest sales figure across all the products. You can then divide this by the desired chart height to get the Y scale:


// Compute the Y scale

$maxTotal = 0;

foreach ( $data as $dataRow ) {
$totalSales = 0;
foreach ( $dataRow as $dataCell ) $totalSales += $dataCell;
$maxTotal = ( $totalSales > $maxTotal ) ? $totalSales : $maxTotal;
}

$yScale = $maxTotal / $chartHeight;
Now that you know the X scale, you can work out the width (in mm) of each bar in the chart. This is the inverse of the X scale value, reduced by a factor of 1.5 to allow some space between each bar:


// Compute the bar width
$barWidth = ( 1 / $xScale ) / 1.5;
Adding the axis lines and labels

So far, so good. Now you can add the X and Y axis lines, data labels, and axis labels. Use Arial 10-point for the data labels.

To create lines in FDPF, you use the Line() method, which accepts 4 arguments: the X and Y co-ordinates of the start of the line, and the X and Y co-ordinates of the end of the line.

For the X axis, draw a horizontal line along the bottom of the chart, allowing 30mm for the Y-axis labels on the left. Then loop through each product name in the $rowLabels array, printing the product name as a text cell at the appropriate point:


// Add the axes:

$pdf->SetFont( 'Arial', '', 10 );

// X axis
$pdf->Line( $chartXPos + 30, $chartYPos, $chartXPos + $chartWidth, $chartYPos );

for ( $i=0; $i <>SetXY( $chartXPos + 40 + $i / $xScale, $chartYPos );
$pdf->Cell( $barWidth, 10, $rowLabels[$i], 0, 0, 'C' );
}
The SetXY() method lets you set the current position to a specific location on the page.

For the Y axis, draw a vertical line up the left side of the chart, again allowing 30mm for the Y-axis labels. Extend the line 8mm above the desired chart height to make room for the axis label later on. Then loop from zero up to the highest bar value, $maxTotal, that you calculated earlier. Jump in steps of $chartYStep (20,000) dollars. At each step, display the current value (right-aligned) and a short tick mark:


// Y axis
$pdf->Line( $chartXPos + 30, $chartYPos, $chartXPos + 30, $chartYPos - $chartHeight - 8 );

for ( $i=0; $i <= $maxTotal; $i += $chartYStep ) { $pdf->SetXY( $chartXPos + 7, $chartYPos - 5 - $i / $yScale );
$pdf->Cell( 20, 10, '$' . number_format( $i ), 0, 0, 'R' );
$pdf->Line( $chartXPos + 28, $chartYPos - $i / $yScale, $chartXPos + 30, $chartYPos - $i / $yScale );
}
Now you can add the axis labels. Use Arial Bold 12-point. Place the X-axis label below the data labels, and the Y-axis label at the top of the Y axis:


// Add the axis labels
$pdf->SetFont( 'Arial', 'B', 12 );
$pdf->SetXY( $chartWidth / 2 + 20, $chartYPos + 8 );
$pdf->Cell( 30, 10, $chartXLabel, 0, 0, 'C' );
$pdf->SetXY( $chartXPos + 7, $chartYPos - $chartHeight - 12 );
$pdf->Cell( 20, 10, $chartYLabel, 0, 0, 'R' );
Drawing the data bars

The last stage of creating the chart is to draw the bars themselves. To draw a bar you can use FPDF's Rect() method, which draws a rectangle using the following arguments:

The X and Y co-ordinates of the upper left corner of the rectangle.
The width and height of the rectangle.
The rectangle style. This can be 'D' or '' (draw a border), 'F' (fill with the current fill colour), or 'DF' / 'FD' (draw and fill).
Now draw the bars. First, Set a variable, $xPos, to track the current bar X position; set it 40mm to the right of the chart's start position to allow for the Y-axis labels and a gap at the start of the bars. Then create a variable, $bar, to hold the current bar number; you'll use this to work out which fill colour to use for each bar:


// Create the bars
$xPos = $chartXPos + 40;
$bar = 0;
Now loop through the $data array, totalling up the value in each row and drawing a bar from the X axis up to that value, scaled using $yScale. Colour each bar differently by using the $bar counter and the colour values in the $chartColours array. After you've drawn each bar, move the X position along to the start of the next bar, increment the $bar counter, and continue the loop:


foreach ( $data as $dataRow ) {

// Total up the sales figures for this product
$totalSales = 0;
foreach ( $dataRow as $dataCell ) $totalSales += $dataCell;

// Create the bar
$colourIndex = $bar % count( $chartColours );
$pdf->SetFillColor( $chartColours[$colourIndex][0], $chartColours[$colourIndex][1], $chartColours[$colourIndex][2] );
$pdf->Rect( $xPos, $chartYPos - ( $totalSales / $yScale ), $barWidth, $totalSales / $yScale, 'DF' );
$xPos += ( 1 / $xScale );
$bar++;
}
The above code uses the PHP modulus (%) operator to repeat the bar colours if the number of bars happens to be greater than the number of elements in the $chartColours array.

Sending the PDF to the browser
Your PDF is finished! The only thing left to do is send the PDF to the browser so that the user can view or download it.

To do this, you call FPDF's Output() method to send the PDF data. This accepts 2 arguments: the suggested filename for the PDF, and a destination flag. This flag can have any of the following values:

I : Displays the PDF inline if supported by the browser, otherwise it's downloaded.
D: Forces the PDF to be downloaded.
F: Saves the file to a folder on the server.
S: Returns the PDF data as a string.

For this example, use the I option to display the PDF inline if possible:


/***
Serve the PDF
***/

$pdf->Output( "report.pdf", "I" );

?>
Output() automatically sends an HTTP "Content-type: application/pdf" header, which tells the browser to expect a PDF document.

You're now ready to test your script. Open your browser and visit the script's URL — for example, www.example.com/report.php. You should see the PDF appear in your browser window. Alternatively you might see a dialog appear that lets you save the PDF to your hard drive. You can then open up the PDF in your PDF viewer, such as Acrobat Reader or Preview.

That's it! You've now created a PDF document on the fly using nothing but PHP and FPDF. Good work!

The complete script
Here's the complete PHP script for you to copy, paste, and play with:


SetTextColor( $textColour[0], $textColour[1], $textColour[2] );
$pdf->AddPage();

// Logo
$pdf->Image( $logoFile, $logoXPos, $logoYPos, $logoWidth );

// Report Name
$pdf->SetFont( 'Arial', 'B', 24 );
$pdf->Ln( $reportNameYPos );
$pdf->Cell( 0, 15, $reportName, 0, 0, 'C' );


/**
Create the page header, main heading, and intro text
**/

$pdf->AddPage();
$pdf->SetTextColor( $headerColour[0], $headerColour[1], $headerColour[2] );
$pdf->SetFont( 'Arial', '', 17 );
$pdf->Cell( 0, 15, $reportName, 0, 0, 'C' );
$pdf->SetTextColor( $textColour[0], $textColour[1], $textColour[2] );
$pdf->SetFont( 'Arial', '', 20 );
$pdf->Write( 19, "2009 Was A Good Year" );
$pdf->Ln( 16 );
$pdf->SetFont( 'Arial', '', 12 );
$pdf->Write( 6, "Despite the economic downturn, WidgetCo had a strong year. Sales of the HyperWidget in particular exceeded expectations. The fourth quarter was generally the best performing; this was most likely due to our increased ad spend in Q3." );
$pdf->Ln( 12 );
$pdf->Write( 6, "2010 is expected to see increased sales growth as we expand into other countries." );


/**
Create the table
**/

$pdf->SetDrawColor( $tableBorderColour[0], $tableBorderColour[1], $tableBorderColour[2] );
$pdf->Ln( 15 );

// Create the table header row
$pdf->SetFont( 'Arial', 'B', 15 );

// "PRODUCT" cell
$pdf->SetTextColor( $tableHeaderTopProductTextColour[0], $tableHeaderTopProductTextColour[1], $tableHeaderTopProductTextColour[2] );
$pdf->SetFillColor( $tableHeaderTopProductFillColour[0], $tableHeaderTopProductFillColour[1], $tableHeaderTopProductFillColour[2] );
$pdf->Cell( 46, 12, " PRODUCT", 1, 0, 'L', true );

// Remaining header cells
$pdf->SetTextColor( $tableHeaderTopTextColour[0], $tableHeaderTopTextColour[1], $tableHeaderTopTextColour[2] );
$pdf->SetFillColor( $tableHeaderTopFillColour[0], $tableHeaderTopFillColour[1], $tableHeaderTopFillColour[2] );

for ( $i=0; $iCell( 36, 12, $columnLabels[$i], 1, 0, 'C', true );
}

$pdf->Ln( 12 );

// Create the table data rows

$fill = false;
$row = 0;

foreach ( $data as $dataRow ) {

// Create the left header cell
$pdf->SetFont( 'Arial', 'B', 15 );
$pdf->SetTextColor( $tableHeaderLeftTextColour[0], $tableHeaderLeftTextColour[1], $tableHeaderLeftTextColour[2] );
$pdf->SetFillColor( $tableHeaderLeftFillColour[0], $tableHeaderLeftFillColour[1], $tableHeaderLeftFillColour[2] );
$pdf->Cell( 46, 12, " " . $rowLabels[$row], 1, 0, 'L', $fill );

// Create the data cells
$pdf->SetTextColor( $textColour[0], $textColour[1], $textColour[2] );
$pdf->SetFillColor( $tableRowFillColour[0], $tableRowFillColour[1], $tableRowFillColour[2] );
$pdf->SetFont( 'Arial', '', 15 );

for ( $i=0; $iCell( 36, 12, ( '$' . number_format( $dataRow[$i] ) ), 1, 0, 'C', $fill );
}

$row++;
$fill = !$fill;
$pdf->Ln( 12 );
}


/***
Create the chart
***/

// Compute the X scale
$xScale = count($rowLabels) / ( $chartWidth - 40 );

// Compute the Y scale

$maxTotal = 0;

foreach ( $data as $dataRow ) {
$totalSales = 0;
foreach ( $dataRow as $dataCell ) $totalSales += $dataCell;
$maxTotal = ( $totalSales > $maxTotal ) ? $totalSales : $maxTotal;
}

$yScale = $maxTotal / $chartHeight;

// Compute the bar width
$barWidth = ( 1 / $xScale ) / 1.5;

// Add the axes:

$pdf->SetFont( 'Arial', '', 10 );

// X axis
$pdf->Line( $chartXPos + 30, $chartYPos, $chartXPos + $chartWidth, $chartYPos );

for ( $i=0; $i <>SetXY( $chartXPos + 40 + $i / $xScale, $chartYPos );
$pdf->Cell( $barWidth, 10, $rowLabels[$i], 0, 0, 'C' );
}

// Y axis
$pdf->Line( $chartXPos + 30, $chartYPos, $chartXPos + 30, $chartYPos - $chartHeight - 8 );

for ( $i=0; $i <= $maxTotal; $i += $chartYStep ) { $pdf->SetXY( $chartXPos + 7, $chartYPos - 5 - $i / $yScale );
$pdf->Cell( 20, 10, '$' . number_format( $i ), 0, 0, 'R' );
$pdf->Line( $chartXPos + 28, $chartYPos - $i / $yScale, $chartXPos + 30, $chartYPos - $i / $yScale );
}

// Add the axis labels
$pdf->SetFont( 'Arial', 'B', 12 );
$pdf->SetXY( $chartWidth / 2 + 20, $chartYPos + 8 );
$pdf->Cell( 30, 10, $chartXLabel, 0, 0, 'C' );
$pdf->SetXY( $chartXPos + 7, $chartYPos - $chartHeight - 12 );
$pdf->Cell( 20, 10, $chartYLabel, 0, 0, 'R' );

// Create the bars
$xPos = $chartXPos + 40;
$bar = 0;

foreach ( $data as $dataRow ) {

// Total up the sales figures for this product
$totalSales = 0;
foreach ( $dataRow as $dataCell ) $totalSales += $dataCell;

// Create the bar
$colourIndex = $bar % count( $chartColours );
$pdf->SetFillColor( $chartColours[$colourIndex][0], $chartColours[$colourIndex][1], $chartColours[$colourIndex][2] );
$pdf->Rect( $xPos, $chartYPos - ( $totalSales / $yScale ), $barWidth, $totalSales / $yScale, 'DF' );
$xPos += ( 1 / $xScale );
$bar++;
}


/***
Serve the PDF
***/

$pdf->Output( "report.pdf", "I" );

?>

Summary
In this tutorial you've learned how to use PHP with the FPDF library to produce a good-looking PDF report. Along the way you've looked at some useful methods of the FPDF library, and seen some techniques for creating tables and charts using FPDF.

There's a lot more to FPDF, such as the ability to create headers and footers, use automatic page breaks, and more. Find out more by reading the online manual at the FPDF website.

Have fun!

--------------------------------------------
Tutorial by Matt Doyle | Level: Intermediate | Published on 26 February 2010
Categories:
Web Development > PHP
. number_format( $dataRow[$i] ) ), 1, 0, 'C', $fill );
}

$row++;
$fill = !$fill;
$pdf->Ln( 12 );
}


Creating a bar chart
The last element of the page is a bar chart showing the total sales figures for the 4 products over the whole year.

Calculating scales and bar width

The first thing to do is compute the scales for the X and Y axes. For the X scale this is simply the number of products to display divided by the desired chart width (subtracting some millimetres to allow for space to the left of the bars):


/***
Create the chart
***/

// Compute the X scale
$xScale = count($rowLabels) / ( $chartWidth - 40 );
To compute the Y scale, you need to find the total sales figure for each product, then determine the highest sales figure across all the products. You can then divide this by the desired chart height to get the Y scale:


// Compute the Y scale

$maxTotal = 0;

foreach ( $data as $dataRow ) {
$totalSales = 0;
foreach ( $dataRow as $dataCell ) $totalSales += $dataCell;
$maxTotal = ( $totalSales > $maxTotal ) ? $totalSales : $maxTotal;
}

$yScale = $maxTotal / $chartHeight;
Now that you know the X scale, you can work out the width (in mm) of each bar in the chart. This is the inverse of the X scale value, reduced by a factor of 1.5 to allow some space between each bar:


// Compute the bar width
$barWidth = ( 1 / $xScale ) / 1.5;
Adding the axis lines and labels

So far, so good. Now you can add the X and Y axis lines, data labels, and axis labels. Use Arial 10-point for the data labels.

To create lines in FDPF, you use the Line() method, which accepts 4 arguments: the X and Y co-ordinates of the start of the line, and the X and Y co-ordinates of the end of the line.

For the X axis, draw a horizontal line along the bottom of the chart, allowing 30mm for the Y-axis labels on the left. Then loop through each product name in the $rowLabels array, printing the product name as a text cell at the appropriate point:


// Add the axes:

$pdf->SetFont( 'Arial', '', 10 );

// X axis
$pdf->Line( $chartXPos + 30, $chartYPos, $chartXPos + $chartWidth, $chartYPos );

for ( $i=0; $i <>SetXY( $chartXPos + 40 + $i / $xScale, $chartYPos );
$pdf->Cell( $barWidth, 10, $rowLabels[$i], 0, 0, 'C' );
}
The SetXY() method lets you set the current position to a specific location on the page.

For the Y axis, draw a vertical line up the left side of the chart, again allowing 30mm for the Y-axis labels. Extend the line 8mm above the desired chart height to make room for the axis label later on. Then loop from zero up to the highest bar value, $maxTotal, that you calculated earlier. Jump in steps of $chartYStep (20,000) dollars. At each step, display the current value (right-aligned) and a short tick mark:


// Y axis
$pdf->Line( $chartXPos + 30, $chartYPos, $chartXPos + 30, $chartYPos - $chartHeight - 8 );

for ( $i=0; $i <= $maxTotal; $i += $chartYStep ) { $pdf->SetXY( $chartXPos + 7, $chartYPos - 5 - $i / $yScale );
$pdf->Cell( 20, 10, '$' . number_format( $i ), 0, 0, 'R' );
$pdf->Line( $chartXPos + 28, $chartYPos - $i / $yScale, $chartXPos + 30, $chartYPos - $i / $yScale );
}
Now you can add the axis labels. Use Arial Bold 12-point. Place the X-axis label below the data labels, and the Y-axis label at the top of the Y axis:


// Add the axis labels
$pdf->SetFont( 'Arial', 'B', 12 );
$pdf->SetXY( $chartWidth / 2 + 20, $chartYPos + 8 );
$pdf->Cell( 30, 10, $chartXLabel, 0, 0, 'C' );
$pdf->SetXY( $chartXPos + 7, $chartYPos - $chartHeight - 12 );
$pdf->Cell( 20, 10, $chartYLabel, 0, 0, 'R' );
Drawing the data bars

The last stage of creating the chart is to draw the bars themselves. To draw a bar you can use FPDF's Rect() method, which draws a rectangle using the following arguments:

The X and Y co-ordinates of the upper left corner of the rectangle.
The width and height of the rectangle.
The rectangle style. This can be 'D' or '' (draw a border), 'F' (fill with the current fill colour), or 'DF' / 'FD' (draw and fill).
Now draw the bars. First, Set a variable, $xPos, to track the current bar X position; set it 40mm to the right of the chart's start position to allow for the Y-axis labels and a gap at the start of the bars. Then create a variable, $bar, to hold the current bar number; you'll use this to work out which fill colour to use for each bar:


// Create the bars
$xPos = $chartXPos + 40;
$bar = 0;
Now loop through the $data array, totalling up the value in each row and drawing a bar from the X axis up to that value, scaled using $yScale. Colour each bar differently by using the $bar counter and the colour values in the $chartColours array. After you've drawn each bar, move the X position along to the start of the next bar, increment the $bar counter, and continue the loop:


foreach ( $data as $dataRow ) {

// Total up the sales figures for this product
$totalSales = 0;
foreach ( $dataRow as $dataCell ) $totalSales += $dataCell;

// Create the bar
$colourIndex = $bar % count( $chartColours );
$pdf->SetFillColor( $chartColours[$colourIndex][0], $chartColours[$colourIndex][1], $chartColours[$colourIndex][2] );
$pdf->Rect( $xPos, $chartYPos - ( $totalSales / $yScale ), $barWidth, $totalSales / $yScale, 'DF' );
$xPos += ( 1 / $xScale );
$bar++;
}
The above code uses the PHP modulus (%) operator to repeat the bar colours if the number of bars happens to be greater than the number of elements in the $chartColours array.

Sending the PDF to the browser
Your PDF is finished! The only thing left to do is send the PDF to the browser so that the user can view or download it.

To do this, you call FPDF's Output() method to send the PDF data. This accepts 2 arguments: the suggested filename for the PDF, and a destination flag. This flag can have any of the following values:

I : Displays the PDF inline if supported by the browser, otherwise it's downloaded.
D: Forces the PDF to be downloaded.
F: Saves the file to a folder on the server.
S: Returns the PDF data as a string.

For this example, use the I option to display the PDF inline if possible:


/***
Serve the PDF
***/

$pdf->Output( "report.pdf", "I" );

?>
Output() automatically sends an HTTP "Content-type: application/pdf" header, which tells the browser to expect a PDF document.

You're now ready to test your script. Open your browser and visit the script's URL — for example, www.example.com/report.php. You should see the PDF appear in your browser window. Alternatively you might see a dialog appear that lets you save the PDF to your hard drive. You can then open up the PDF in your PDF viewer, such as Acrobat Reader or Preview.

That's it! You've now created a PDF document on the fly using nothing but PHP and FPDF. Good work!

The complete script
Here's the complete PHP script for you to copy, paste, and play with:


SetTextColor( $textColour[0], $textColour[1], $textColour[2] );
$pdf->AddPage();

// Logo
$pdf->Image( $logoFile, $logoXPos, $logoYPos, $logoWidth );

// Report Name
$pdf->SetFont( 'Arial', 'B', 24 );
$pdf->Ln( $reportNameYPos );
$pdf->Cell( 0, 15, $reportName, 0, 0, 'C' );


/**
Create the page header, main heading, and intro text
**/

$pdf->AddPage();
$pdf->SetTextColor( $headerColour[0], $headerColour[1], $headerColour[2] );
$pdf->SetFont( 'Arial', '', 17 );
$pdf->Cell( 0, 15, $reportName, 0, 0, 'C' );
$pdf->SetTextColor( $textColour[0], $textColour[1], $textColour[2] );
$pdf->SetFont( 'Arial', '', 20 );
$pdf->Write( 19, "2009 Was A Good Year" );
$pdf->Ln( 16 );
$pdf->SetFont( 'Arial', '', 12 );
$pdf->Write( 6, "Despite the economic downturn, WidgetCo had a strong year. Sales of the HyperWidget in particular exceeded expectations. The fourth quarter was generally the best performing; this was most likely due to our increased ad spend in Q3." );
$pdf->Ln( 12 );
$pdf->Write( 6, "2010 is expected to see increased sales growth as we expand into other countries." );


/**
Create the table
**/

$pdf->SetDrawColor( $tableBorderColour[0], $tableBorderColour[1], $tableBorderColour[2] );
$pdf->Ln( 15 );

// Create the table header row
$pdf->SetFont( 'Arial', 'B', 15 );

// "PRODUCT" cell
$pdf->SetTextColor( $tableHeaderTopProductTextColour[0], $tableHeaderTopProductTextColour[1], $tableHeaderTopProductTextColour[2] );
$pdf->SetFillColor( $tableHeaderTopProductFillColour[0], $tableHeaderTopProductFillColour[1], $tableHeaderTopProductFillColour[2] );
$pdf->Cell( 46, 12, " PRODUCT", 1, 0, 'L', true );

// Remaining header cells
$pdf->SetTextColor( $tableHeaderTopTextColour[0], $tableHeaderTopTextColour[1], $tableHeaderTopTextColour[2] );
$pdf->SetFillColor( $tableHeaderTopFillColour[0], $tableHeaderTopFillColour[1], $tableHeaderTopFillColour[2] );

for ( $i=0; $iCell( 36, 12, $columnLabels[$i], 1, 0, 'C', true );
}

$pdf->Ln( 12 );

// Create the table data rows

$fill = false;
$row = 0;

foreach ( $data as $dataRow ) {

// Create the left header cell
$pdf->SetFont( 'Arial', 'B', 15 );
$pdf->SetTextColor( $tableHeaderLeftTextColour[0], $tableHeaderLeftTextColour[1], $tableHeaderLeftTextColour[2] );
$pdf->SetFillColor( $tableHeaderLeftFillColour[0], $tableHeaderLeftFillColour[1], $tableHeaderLeftFillColour[2] );
$pdf->Cell( 46, 12, " " . $rowLabels[$row], 1, 0, 'L', $fill );

// Create the data cells
$pdf->SetTextColor( $textColour[0], $textColour[1], $textColour[2] );
$pdf->SetFillColor( $tableRowFillColour[0], $tableRowFillColour[1], $tableRowFillColour[2] );
$pdf->SetFont( 'Arial', '', 15 );

for ( $i=0; $iCell( 36, 12, ( '$' . number_format( $dataRow[$i] ) ), 1, 0, 'C', $fill );
}

$row++;
$fill = !$fill;
$pdf->Ln( 12 );
}


/***
Create the chart
***/

// Compute the X scale
$xScale = count($rowLabels) / ( $chartWidth - 40 );

// Compute the Y scale

$maxTotal = 0;

foreach ( $data as $dataRow ) {
$totalSales = 0;
foreach ( $dataRow as $dataCell ) $totalSales += $dataCell;
$maxTotal = ( $totalSales > $maxTotal ) ? $totalSales : $maxTotal;
}

$yScale = $maxTotal / $chartHeight;

// Compute the bar width
$barWidth = ( 1 / $xScale ) / 1.5;

// Add the axes:

$pdf->SetFont( 'Arial', '', 10 );

// X axis
$pdf->Line( $chartXPos + 30, $chartYPos, $chartXPos + $chartWidth, $chartYPos );

for ( $i=0; $i <>SetXY( $chartXPos + 40 + $i / $xScale, $chartYPos );
$pdf->Cell( $barWidth, 10, $rowLabels[$i], 0, 0, 'C' );
}

// Y axis
$pdf->Line( $chartXPos + 30, $chartYPos, $chartXPos + 30, $chartYPos - $chartHeight - 8 );

for ( $i=0; $i <= $maxTotal; $i += $chartYStep ) { $pdf->SetXY( $chartXPos + 7, $chartYPos - 5 - $i / $yScale );
$pdf->Cell( 20, 10, '$' . number_format( $i ), 0, 0, 'R' );
$pdf->Line( $chartXPos + 28, $chartYPos - $i / $yScale, $chartXPos + 30, $chartYPos - $i / $yScale );
}

// Add the axis labels
$pdf->SetFont( 'Arial', 'B', 12 );
$pdf->SetXY( $chartWidth / 2 + 20, $chartYPos + 8 );
$pdf->Cell( 30, 10, $chartXLabel, 0, 0, 'C' );
$pdf->SetXY( $chartXPos + 7, $chartYPos - $chartHeight - 12 );
$pdf->Cell( 20, 10, $chartYLabel, 0, 0, 'R' );

// Create the bars
$xPos = $chartXPos + 40;
$bar = 0;

foreach ( $data as $dataRow ) {

// Total up the sales figures for this product
$totalSales = 0;
foreach ( $dataRow as $dataCell ) $totalSales += $dataCell;

// Create the bar
$colourIndex = $bar % count( $chartColours );
$pdf->SetFillColor( $chartColours[$colourIndex][0], $chartColours[$colourIndex][1], $chartColours[$colourIndex][2] );
$pdf->Rect( $xPos, $chartYPos - ( $totalSales / $yScale ), $barWidth, $totalSales / $yScale, 'DF' );
$xPos += ( 1 / $xScale );
$bar++;
}


/***
Serve the PDF
***/

$pdf->Output( "report.pdf", "I" );

?>

Summary
In this tutorial you've learned how to use PHP with the FPDF library to produce a good-looking PDF report. Along the way you've looked at some useful methods of the FPDF library, and seen some techniques for creating tables and charts using FPDF.

There's a lot more to FPDF, such as the ability to create headers and footers, use automatic page breaks, and more. Find out more by reading the online manual at the FPDF website.

Have fun!

--------------------------------------------
Tutorial by Matt Doyle | Level: Intermediate | Published on 26 February 2010
Categories:
Web Development > PHP

วันจันทร์ที่ 1 พฤศจิกายน พ.ศ. 2553

setTimeout กับ setInterval นั้นมีข้อแตกต่างกัน

หลายคนอาจเคยเขียน JavaScript ในการจับเวลามาแล้ว แต่ทราบหรือไม่ว่าการใช้ setTimeout กับ setInterval นั้นมีข้อแตกต่างกันอยู่
setTimeout ใช้สำหรับการเรียก function ใดๆ ให้ทำงานหลังจากเวลาที่เรากำหนดไว้ เมื่อ function นั้นๆ ถูกเรียนแล้วมันก็จะจบการทำงานไป หากต้องการให้มันหยุดทำงานก่อนเวลาที่กำหนดไว้ก็ให้ใช้ clearTimeout มันก็จะหยุดให้เรา
ตัวอย่างการใช้งาน

var timeoutID = setTimeout(function() {alert("Hello TimeOut");}, 1000);

วิธีการยกเลิก timeout
clearTimeout(timeoutID);
เราจำเป็นต้องจำค่า ID ที่ได้จาก setTimeout เพื่อนำมาบอกให้มันหยุดทำงาน


setInterval จะเป็นการเรียกไปเรื่อยๆ จนกว่าเราจะสั่งให้มันหยุดทำงาน ด้วยคำสั่ง clearInterval
ตัวอย่างการใช้งาน

var intervalID = window.setInterval(function() {alert("Hello Interval");}, 1000);

วิธีการยกเลิก Interval
window.clearInterval(intervalID);

เช่นเดียวกับ clearTimeout ครับ