2. — Scott Spendolini
— scott.spendolini@enkitec.com
— @sspendol
— Ex-Oracle Employee of 10 years
— Senior Product Manager for Oracle APEX
from 2002 through 2005
— Founded Sumner Technologies
in October 2005
— Co-Founded Sumneva in January 2010
— Joined Enkitec in June 2012
— Oracle Ace Director
— Author, Expert Oracle Application Express Security
— Co-Author, Pro Oracle Application Express
— “Scott” on OTN Forums
About the Presenter
2
6. What is PL/PDF?
6
—Third party product designed by Oranext
—http://plpdf.com
—Provides a set of PL/SQL based APIs that can
generate PDF files from the Oracle database
—Easy to securely
integrate with APEX
—Can be called from a
Page or Application
Process
7. PL/PDF Features
— Robust reporting engine
— Charts
— Bar, Line & Pie
— Barcode Printing
— Use Existing PDF files as Templates
— TrueType Font Embedding
— Encrypted PDF Documents
— Native Support for PNG & JPEG images
— Other types supported via Oracle InterMedia
7
8. Licensing
— Licensed per instance of Oracle
— $600 per instance
— Can download a trial for free
— 5 Page Maximum
— Watermark Appears on all pages
— No additional hardware is required
— PL/PDF installs into the same database as APEX
8
9. The BLOB!
— Result of PL/PDF is nothing more than an Oracle
BLOB
— Thus, you can do anything with PL/PDF output
that you can with an Oracle BLOB:
— E-Mail it as an attachment
— Download it to a browser
— Store it in a table
— Index it with Oracle Text
— Schedule a procedure to
generate and e-mail PDF files
9
12. Layout & Control
12
— All layout is handled via API calls
— There is no GUI tool to design a PL/PDF report
— Seems limiting, and can be at times
— Template feature allows you to use any tool to create the
structure of a document and use PL/PDF APIs only to
populate the data
— Also, there are APIs that minimize the amount of code
required when creating reports
13. Extras
13
— In addition to its core APIs, PL/PDF offers a
number of extra features
— Templates
— Charts
— Bar, Line & Pie
— Bar Codes
— Encryption
— OpenOffice Forms
— TrueType Font Support
14. Templates
14
— PL/PDF allows you to include a PDF template as a
background for any page
— No need to use PL/PDF to create the structure of a
document; focus on filling in the data
— Any PDF document can be used
— Easy to create PDF documents from Word, Acrobat, etc.
17. sumnevaSERT
Settings Summary 28 out of 37 possible points 75.68%
Session Duration
0%
0 out of 2 possible points
Security
60%
6 out of 10 possible points
Application Settings
84.62%
11 out of 13 possible points
Authentication Scheme
91.67%
11 out of 12 possible points
Security: 4
Application Settings: 2
Session Duration: 2
Authentication Scheme: 1
Exceptions Awaiting Approval
No Data Found
Stale Exceptions
No Data Found
Charts
17
19. Other Features
19
— Ability to encrypt PDF files
— Support for OpenOffice PDF Forms
— LZW compression
— TrueType Font Support
20. Sweet Spots
— PL/PDF is the best solution when:
— The core report definition does not change often
— End Users do not need to create one-off reports
— You have an abundance of PL/SQL resources and little
money
20
22. Installation Options
22
— PL/PDF is installed by running a series of SQL
scripts
— Can use to install on any target OS
— Need to be able to connect to the target database via
SQL*Net
— Some additional features may need to be
manually installed
— Charts, Bar Codes, etc.
— Installation should take about 15 minutes
23. Where to Put It
— Since PL/PDF is a set of PL/SQL objects, you’ll
need to select a schema to put it in
— Typically use PLPDF, but can name the schema anything
— Best to use its own schema and then create
grants & synonyms to access from other schemas
— You’ll need to do this manually
— Start with just PLPDF & PLPDF_TYPE; synonyms for other
objects can be added as needed
23
25. APEX Integration
25
— Once installed and the corresponding grants are
created to your parse as schema, there is
NOTHING ADDITIONAL TO CONFIGURE in order to
integrate PL/PDF with APEX
— PL/PDF - like APEX - is PL/SQL
— Thus, as long as your parse-as schema can access the
PL/PDF objects, that’s all you’ll need
— Seriously.
— There’s nothing else to configure.
— Try it. You’ll see.
26. APEX Integration
— PL/PDF must be called as part of the Page
Rendering process in APEX
— And it must occur before anything else
— Best Practice:
— Create an On Load PL/SQL Process that calls PL/PDF
— Set the Condition to Request = Expression 1
— Add a button/link to that page which sets the Request to
trigger the PL/PDF call
26
27. PL/SQL Package
— It’s best to put the logic for a PL/PDF report into
a PL/SQL package
— Centralized
— Can re-use common functions
— More performant
— Easier to work with
— Be sure pass in parameters when possible so that
a single package may be called from multiple
pages or even outside of APEX
27
28. PL/SQL Process
— A simple PL/SQL Process should be created to
call the report package
— Must be set to fire Before Header and be the first
process executed
— Must be condition to only execute when Request = PRINT
— Can be an Application Process
— PL/SQL Code:
28
plpdf_print.print
(
p_order_id => :P29_ORDER_ID
);
29. Button
29
— A button can be used to trigger our PDF report
— Redirects to the page where the process is located
— Passes in PRINT to the Request value
30. Column Link
— A Column Link can also be used to print a report
— Redirects to the page where the process is located
— Passes in PRINT to the Request value
— Sets the value of the APEX Item which is passed to the
package
30
33. Goal
33
— To produce a Sales Receipt for an order
generated in the Sample Database Application
— Receipt will encompass a master-detail report
— Single row from the header
— Variable number of rows from the detail
— Should look professional and include a logo
34. Report Types
34
— PL/PDF can produce two “types” of reports
— Row/Column
— Produce rows & columns of data based on a SQL query
— Forms
— Fill in the data for a form, such as a Tax Form or Job Application
— It’s important to determine which type is needed
for a specific requirement up front
— OK to use both in a single report, if needed
35. Our Example
35
Form Report
Based on a single
row of data
across one or
more tables
Row/Column Report
Based on a SQL
Query returning
multiple rows of
data
36. Where to Start?
36
— PL/PDF can utilize a PDF
document as a template
— Simplifies code greatly, as
only the data needs to be
printed via the APIs
Eugene Bradley
Schoephoester Road
Windsor Locks, CT 06906
10/1/13 000003
00001 Business Shirt 1 $50 $50
00003 Trousers 2 $100 $200
$250
12345 Check
37. The ABCs of the APIs
37
— PL/PDF is nothing more than a set of PL/SQL APIs
— Like any other API set, there’s a learning curve
— Most APIs have default values on most
parameters
— Thus you only need to pass values to those you wish to set
— Use param => value notation for best results
— Refer to the documentation for full details
38. Where to Start
— Like any other problem, it all starts with the data
— Analyze the data required and determine the best way to get what you
need
— Create views where it makes sense to simplify SQL statements in APEX
or PL/SQL
38
DEMO_ORDERS
DEMO_ORDER_ITEMS
DEMO_CUSTOMERS
39. DEMO_ORDERS
— Since we’ll need to reference a row of
DEMO_ORDERS, it can be created as a ROWTYPE
variable and referenced throughout in the
PL/PDF APIs
39
DECLARE
l_order_t demo_orders%ROWTYPE;
BEGIN
-- Get the Order Row
SELECT * INTO l_order_t
FROM demo_orders
WHERE order_id = p_order_id;
40. init
— Called once to initialize PL/PDF
— Parameters:
— p_orientation
— p_unit
— p_format
40
41. newPage
— Creates a new page
— Called anytime a new page is needed
— PL/PDF will automatically create a new page when
needed if data overflows the current page
— Parameters:
— p_orientation
41
42. Initializing PL/PDF
42
-- Get the Template
SELECT blob_content INTO l_template_pdf
FROM wwv_flow_files
WHERE filename = 'sales_receipt.pdf';
l_template :=
plpdf_parser.getTemplate(l_template_pdf,1);
-- Initialize PL/PDF
plpdf.init(p_format => 'letter');
l_template_id := plpdf.InsTemplate(l_template);
-- Create a new page
plpdf.NewPage;
-- Place the template on the page
plpdf.useTemplate(l_template_id);
-- Get the Template
SELECT blob_content INTO l_template_pdf
FROM wwv_flow_files
WHERE filename = 'sales_receipt.pdf';
l_template :=
plpdf_parser.getTemplate(l_template_pdf,1);
-- Initialize PL/PDF
plpdf.init(p_format => 'letter');
l_template_id := plpdf.InsTemplate(l_template);
-- Create a new page
plpdf.NewPage;
-- Place the template on the page
plpdf.useTemplate(l_template_id);
43. setCurrentXY
— Sets the position of the cursor
— Parameters:
— p_x
— p_y
— Similar:
— setCurrentX
— setCurrentY
43
44. setPrintFont
— Sets the font family and style used for text
— Parameters:
— p_family
— p_style
— p_size
44
45. printCell
— Prints a line of text on the page
— Parameters:
45
• p_w
• p_h
• p_txt
• p_border
• p_ln
• p_align
• p_fill
• p_link
• p_clipping
47. Printing Customer Details
47
plpdf.SetPrintFont(
p_family => 'Courier', p_size => 10);
plpdf.setCurrentXY(11,60);
-- Customer Details
FOR x IN
(SELECT * FROM demo_customers
WHERE customer_id = l_order_t.customer_id)
LOOP
plpdf.printCell(70, 10, x.cust_first_name
|| ' ' || x.cust_last_name, NULL, NULL, 'L');
plpdf.LineBreak(4);
plpdf.setCurrentX(11);
plpdf.printCell(70, 10, x.cust_street_address1,
NULL, NULL, 'L');
48. Printing Customer Details
48
plpdf.LineBreak(4);
plpdf.setCurrentX(11);
IF x.cust_street_address2 IS NOT NULL THEN
plpdf.printCell(70,10,
x.cust_street_address2, NULL, NULL, 'L');
plpdf.LineBreak(4);
plpdf.setCurrentX(11);
END IF;
plpdf.printCell(70, 10, x.cust_city || ','
|| x.cust_state || ' '
|| x.cust_postal_code, NULL, NULL, 'L');
END LOOP;
49. Other Details
49
-- Date and Order Number
plpdf.setCurrentXY(152,60);
plpdf.printCell(26, 10,
TO_CHAR(l_order_t.order_timestamp,
'DD-MON-YYYY'),NULL,NULL,'C');
plpdf.printCell(30,10,'0000'
|| p_order_id,NULL,NULL,'C');
-- Payment Method
plpdf.setCurrentXY(10,95);
plpdf.printCell(28,10,'n/a' ,NULL,NULL,'C');
plpdf.printCell(28,10,'Credit',NULL,NULL,'C');
-- Reset Cursor
plpdf.setCurrentXY(10,114);
50. row_print
50
— ROW_PRINT allows for a lot more flexibility than
QUERY_PRINT
— Typically called from inside a loop
— FOR X IN (SELECT * FROM ...)
— DBMS_SQL
— Four different variations are available:
ROW_PRINT through ROW_PRINT4
52. Order Lines
52
— ROW_PRINT may or may not suffice for what you
need
— Often times just looping through a query and printing the
individual columns is just as effective
— Take some time to become familiar with it and
what it can and can’t do
55. Download File API
— You can also create a single procedure to handle
file downloads
— Pass in the BLOB and filename
— All PL/PDF reports can call this procedure
— In fact, it is recommended that PL/PDF reports
be based on a package
— Reusability
— Security
55
56. Printing the PDF
56
-- Send the contents of the PDF document to
l_blob and Print it
plpdf.SendDoc(l_blob);
-- Send the PDF document to the browser
owa_util.mime_header('application/pdf', FALSE);
htp.p('Content-Disposition: inline;
filename=' || p_filename || '.pdf') ;
htp.p('Content-Length: '
|| dbms_lob.getlength(l_blob)) ;
owa_util.http_header_close;
wpg_docload.download_file(l_blob);
apex_application.g_unrecoverable_error := TRUE;
57. Table of Contents
57
— PL/PDF allows you to automatically crete a Table
of Contents
— You can add “bookmarks” throughout your application that
link back to the main table of contents
— Fully automated; no need to calculate anything
59. PL/PDF Security
59
— Since PL/PDF is all PL/SQL, there are no specific
provisions when it comes to security
— Any database layer security will work
— Secure Views
— Label Security
— VPD
60. VPD Function
60
CREATE OR REPLACE FUNCTION plpdf_vpd
(p_schema IN VARCHAR2 DEFAULT NULL,
p_object IN VARCHAR2 DEFAULT NULL)
RETURN VARCHAR2
AS
BEGIN
RETURN q'!user_name = '!' || v('APP_USER')
|| q'!'!';
END;
/
66. Summary
66
— PL/PDF provides a robust, cost effective
solution for producing high quality PDF
documents from any APEX application
— Not always the right tool for every job; but in many cases,
it works quite well
— Low cost and zero footprint means that it should at least
be seriously considered