mercoledì 2 luglio 2014

as_pdv3_v5 (english)

PL/SQL package for create PDF

(versione italiana)
Some time ago I found this package to create PDF documents directly from PL / SQL. During this time I added some features for the creation of reports and today I wanted to publish my work.

Your comments, bug indication, correction of english,  are appreciated.



Thank to who made the original package.


Today I've added new features to the procedure query2table, now it accepts blob fields as image and insert it into cell grid, with resizing and alignment, see the NEW POST HERE.

Download my  Lastest vesion  and documentation Github
.


This example create a PDF with breaking on the first field;
it doesn't need table because it uses random data.

-- Created on 25/06/2014 by VALR 
declare 

        i              INTEGER;
        v_vFileName    VARCHAR2(255);
        v_vOddColor    VARCHAR2(6) := 'd0d0d0';
        v_vHeadColor   VARCHAR2(6) := 'e0ffff';
        v_vOraDir      VARCHAR2(50) := 'PDF';
        v_vPageProc    VARCHAR2(32000);
        r_Fmt  as_pdf3_v5.tp_columns:=as_pdf3_v5.tp_columns();
        v_vSQL varchar2(4000);
begin
  v_vFileName    := 'Test_as_pf3_v5.pdf';
  -- Define Sheet Format 
  as_pdf3_v5.init;
  as_pdf3_v5.set_page_format('A4');
  as_pdf3_v5.set_page_orientation('P');
  as_pdf3_v5.set_margins(30, 10, 15, 10, 'mm');
     
  -- Define Header and Footer
  v_vPageProc := q'[
  begin
    §.set_font('helvetica', 'B', 10 );
    §.put_txt('mm',  5, 5, 'Valerio Rossetti');
    §.put_txt('mm',  90, 5, 'Data: ');
    §.set_font('helvetica', 'N', 10);
    §.put_txt('mm', 115,5, ']'||to_char(sysdate,'dd/mm/yy')||q'[');    
    §.put_txt('mm', 175,5, 'Page #PAGE_NR# of #PAGE_COUNT#');
  end;
  ]';
     
  as_pdf3_v5.set_page_proc(v_vPageProc);
  
  --If you use barcode font, remove comment 
  --as_pdf3_v5.load_ttf_font('PDF', 'ean13.ttf', 'CID', TRUE);

    -- Define column format
    begin
      r_fmt.extend(9);
      i:=1; -- (riga di rottura
      r_fmt(i).colWidth:=25;
      r_fmt(i).colLabel:='cod mkt';
      r_fmt(i).hFontStyle:='B';
      r_fmt(i).hFontSize:=10;
      r_fmt(i).hAlignment:='C';
      r_fmt(i).hAlignVert:='T';
      r_fmt(i).tAlignment:='L';
      r_fmt(i).tAlignVert:='B';
      r_fmt(i).tFontSize:=8;
      r_fmt(i).tCHeight := 7;
      r_fmt(i).hCHeight := 7;
      r_fmt(i).cellRow := 1;
      
      i:=i+1;--2
      r_fmt(i).colWidth:=20;
      r_fmt(i).colLabel:='cod_art';
      r_fmt(i).hFontStyle:='B';
      r_fmt(i).hFontSize:=10;
      r_fmt(i).hAlignment:='R';
      r_fmt(i).hAlignVert:='T';
      r_fmt(i).tAlignVert:='T';
      --r_fmt(i).offsetX := 0;
      r_fmt(i).tCHeight := 7;
      r_fmt(i).hCHeight := 7;
      
      i:=i+1;--3
      r_fmt(i).colWidth:=22;
      r_fmt(i).colLabel:='pz imb';
      r_fmt(i).hFontStyle:='B';
      r_fmt(i).hFontSize:=10;
      r_fmt(i).hAlignment:='C';
      r_fmt(i).hAlignVert:='T';
      r_fmt(i).tAlignment:='R';
      r_fmt(i).tAlignVert:='M';
      
      i:=i+1;--4
      r_fmt(i).colWidth:=12;
      r_fmt(i).colLabel:='udm V';
      r_fmt(i).hFontStyle:='B';
      r_fmt(i).hFontSize:=10;
      r_fmt(i).hAlignment:='C';
      r_fmt(i).hAlignVert:='T';
      r_fmt(i).tAlignment:='C';
      r_fmt(i).tAlignVert:='B';
      r_fmt(i).tBorder := as_pdf3_v5.BorderType('TB');
      
      i:=i+1;--5
      r_fmt(i).colWidth:=15;
      r_fmt(i).colLabel:='udm Lt';
      r_fmt(i).hFontStyle:='B';
      r_fmt(i).hFontSize:=10;
      r_fmt(i).hAlignment:='C';
      r_fmt(i).hAlignVert:='T';
      r_fmt(i).tAlignment:='C';
      r_fmt(i).tAlignVert:='B';
      
      i:=i+1;--6
      r_fmt(i).colWidth:=20;
      r_fmt(i).colLabel:='prz. vend.';
      r_fmt(i).hFontStyle:='B';
      r_fmt(i).hFontSize:=10;
      r_fmt(i).hAlignment:='C';
      r_fmt(i).hAlignVert:='T';
      r_fmt(i).tAlignment:='R';
      r_fmt(i).tAlignVert:='B';
      i:=i+1;--7
      r_fmt(i).colWidth:=20;
      r_fmt(i).colLabel:='prz. cost';
      r_fmt(i).hFontStyle:='B';
      r_fmt(i).hFontSize:=10;
      r_fmt(i).hAlignment:='C';
      r_fmt(i).hAlignVert:='T';
      r_fmt(i).tAlignment:='R';
      r_fmt(i).tAlignVert:='B';
      i:=i+1;--8
      r_fmt(i).colWidth:=16;
      r_fmt(i).colLabel:='margin';
      r_fmt(i).hFontStyle:='B';
      r_fmt(i).hFontSize:=10;
      r_fmt(i).hAlignment:='C';
      r_fmt(i).hAlignVert:='T';
      r_fmt(i).tAlignment:='R';
      r_fmt(i).tAlignVert:='B';
      r_fmt(i).tBorder := 15;
      i:=i+1;--9
      r_fmt(i).colWidth:=150;
      r_fmt(i).colLabel:='product descrition';
      r_fmt(i).hFontStyle:='B';
      r_fmt(i).hFontSize:=10;
      r_fmt(i).hAlignment:='L';
      r_fmt(i).hAlignVert:='T';
      r_fmt(i).hCHeight := 8;
      r_fmt(i).tAlignment:='L';
      r_fmt(i).tAlignVert:='C';
      r_fmt(i).tFontSize:=8;
      r_fmt(i).offsetX := 0;
      r_fmt(i).tCHeight := 8;
      r_fmt(i).cellRow:=2;
      r_fmt(i).tBorder := as_pdf3_v5.BorderType('LRBT');

    end;

     v_vSQL := q'[
SELECT cod_mkt,
       c_art, 
       pcs_imb,
       udm_vend,
       udm_list,
       prz_vend,
       prz_vend*.8 prz_cost,
       prz_vend*.2 margin,
       description
from (       
SELECT case when rownum <5 then '5201001' else '5201003' end cod_mkt,
             rownum*1000+rownum*124 c_art, 
             (trunc(rownum/3)+1)*4  pcs_imb,
             'N'  udm_vend,
             'KG' udm_list,
             round(dbms_random.value(40,2),2) prz_vend,
             round(dbms_random.value(8,2),2)  margin,
             'ART '||to_char(rownum*1000+rownum*124) description
        FROM DUAL d CONNECT BY ROWNUM <= 10
)
order by 1
   ]';
   dbms_output.put_line(v_vSQL);
         
   as_pdf3_v5.query2table(v_vSQL,
     r_fmt,
     as_pdf3_v5.tp_colors('000000',v_vHeadColor,'000000',
                          '000000','ffffff','000000', 
                          '000000',v_vOddColor,'000000'),
     15,15, 'mm',0,1
     );
         
    as_pdf3_v5.save_pdf(v_vOraDir, v_vFileName, TRUE);
  END;

99 commenti:

  1. Thank you! Got it to work and will attempt table queries to build a page.

    RispondiElimina
  2. Thanks Valerio, the word wrap is exactly what I'm looking for.
    The tp_columns, however doesnt seem to change font for hFontName, I tried entering both Helvetica, Times-Roman with no change, any ideas ?

    RispondiElimina
    Risposte
    1. You can try the new release linked in this page,
      http://valeriorossetti.blogspot.com/2014/07/aspdf3v5-new-features-versione-italiana.html
      this bug are fixed and some new featurs are added.

      Elimina
  3. Is there anyway to wrap text in headers?

    RispondiElimina
  4. No there isn't. I'll try to implement it as soon as possible

    RispondiElimina
    Risposte
    1. Thank you for the quick response. Can you also let me know if there is a way to adjust each row's height in a "query2table" to have a dynamic size. For example, I have noticed that if one cell has a really large number of characters then all the rows would have the same height of that cell. Isn't there a way to define that the height of the row would be the same as row's cell with the largest height?

      Elimina
    2. Is there anyway to make the height of each row in a "query2table" to be calculated dynamically based on the row's cell with the highest height. For example, I have seen that when you run the query2table, all rows have the same height and that is the height of the cell with the largest height in the whole table. What I am searching for is for each row to have the same height but when a cell becomes larger because of the content it has to display, then only that row will become bigger. Not all of them.

      Elimina
    3. Hi Giannos, in response to question 15:43, there is a way.
      You can define the minimum height for each rows using a negative value for p_tRowHeight, but I found a bug. The height was never cleared. I have released version 5.3.07 that corrects this problem.
      You con fund it in new post "AS_PDF3_V5 new features" (link at the top of this post)
      I did not understand if you need the function (question 12:00) to automatically find the maximum height to be applied to all rows in the table.

      Elimina
    4. I have just used v07 but it does not seem to do what I am seeking for. Maybe my description was not clear. Please see the image in the link(https://www.dropbox.com/s/2319l2rgyz03ktm/AS_PDF.png?dl=0),showing what the problem is. Thank you

      Elimina
    5. I'm sorry, your description was clear, but thwere was a little bug in the code, upgrade to v08 should fix it.

      Elimina
  5. I understand what you need, It should be easier than wordwrap on label

    RispondiElimina
  6. Pls help me below

    begin vr_test1('STANDARD'); end;

    ORA-01403: no data found
    ORA-06512: at "SYS.AS_PDF3_V5", line 4817
    ORA-06512: at "SYS.AS_PDF3_V5", line 5217
    ORA-06512: at "SYS.AS_PDF3_V5", line 5287
    ORA-06512: at "SYS.VR_TEST1", line 345
    ORA-06512: at line 1

    RispondiElimina
  7. Sorry for the bug, I fixed it in the package body at line 4792.
    The example was born with the first version and doesn't always work with the latest.
    Download link was updated too.

    RispondiElimina
  8. Hi Valerio,
    I need to add a line of text into the header and footer of a multiple-page PDF stored in database column. How can I do this?
    Any help would be very appreciated.

    HX

    RispondiElimina
    Risposte
    1. Hi H Xu,

      I did not understand very well,
      text header is stored in a database column.
      The value is the same for the whole document, or change for each page?

      I try to answer and then tell me if I missundertud


      This is sample code for setting Header & Footer.

      -- Define Header and Footer
      v_vPageProc := q'[
      begin
      §.set_font('helvetica', 'B', 10 );
      §.put_txt('mm', 5, 5, 'Valerio Rossetti');
      §.put_txt('mm', 90, 5, 'Data: ');
      §.set_font('helvetica', 'N', 10);
      §.put_txt('mm', 115,5, ']'||to_char(sysdate,'dd/mm/yy')||q'[');
      §.put_txt('mm', 175,5, 'Page #PAGE_NR# of #PAGE_COUNT#');
      end;
      ]';
      as_pdf3_v5.set_page_proc(v_vPageProc);

      The procedure set_page_proc defines a piece of code that is executed at the end of the document, which add a bit of text on each page.
      The special syntax is
      § which will be replaced with the name of the package
      #PAGE_NR# variable with the page counter
      #PAGE_COUNT# variable with total page count
      , but other than that it is a common begin .. end block
      you can replace static text with anything else.

      If header are the same for each page, store it into variable, and use into pageproc:

      v_vHeaderText varchar2(200);
      Select HeaderColumn into v_vHeaderText
      from HaderTable
      Where ..... ;

      v_vPageProc := replace(
      q'[
      begin
      §.set_font('helvetica', 'B', 10 );
      §.put_txt('mm', 5, 5, '##MY_HEAD##');
      end;
      ]', '##MY_HEAD##', v_vHeaderText);
      as_pdf3_v5.set_page_proc(v_vPageProc);

      if header change for every page you must insert the select code inside pageproc


      DECARE
      v_vHeaderText varchar2(200);
      BEGIN
      Select HeaderColumn into v_vHeaderText
      from HaderTable
      Where ..... ;

      §.put_txt('mm', 5, 5, v_vHeaderText);
      END;

      I hope that my suggestions will be helpful.
      Pleased to to respond in the future
      I'm sorry you had to wait 12 hours, but evaluates weekends and the time zone in Italy is 8:00 AM and I'm just woke up

      Valerio

      Elimina
  9. Hello;
    I'm trying to use query2table (v3.5.10) but I'm facing an issue :
    if I add tp_colors(color1, color2) for a query with only 2 columns this happens :
    ORA-29253: Invalid count argument passed to procedure dbms_sql.define_array
    if I just put NULL for the color part I have this
    ORA-06531: Reference to uninitialized collection

    Can you give me some help please ?
    Regards

    Doriane

    RispondiElimina
    Risposte
    1. Hi Doriane,

      as_pdf3_v5.tp_colors specifies 9 colors,
      firts 3 colour are paper, ink and border of heading row
      next 3 colour are paper, ink and border of even data row
      last 3 colour are paper, ink and border of odd rows
      If you don't wont odd/even highlight usa the same colous.
      (some like this)

      as_pdf3_v5.tp_colors('000000',v_vHeadColor,'000000',
      '000000','ffffff','000000',
      '000000',v_vOddColor,'000000')

      Thanks

      Elimina
    2. New version 3.5.11 solve your problem, remebre to use '' instead of null for p_colors parameter

      Elimina
  10. Hi Valerio Rossetti,
    I have problem when I try to copy/paste your version of as_pdf3. In pl sql I don't see procedures or function on left side, but everything compiles without warnings and errors. I can use your packet without problem, but it is very hard without seeing on the left side.
    And my next question is how to spread my table to the end of the page? I try with setting marrgins, changing t_y in some number... but without succes... For this I am using Anton's package-package body.

    Thanks,
    Milica

    RispondiElimina
  11. Hi Valerio,
    Thanks for posting your code it's really great...
    I have two questions for you. When I compiling your code I don't have errors or warning, and I can call your procedures and functions from other procedure, and everything works great. But it does not show me structure of procedures/functions on the left side, and it's wery hard to me to read and walk trought your code.
    Second question is about Anton's package body. I'm interested in posting table in pdf document from query2table. Everything is good but I don't know how to move my table to the bottom of page. Can you give me some tips? I know that solution is in y coordinate, but I'm not capable to get out of box :).
    Thank you,
    Milica

    RispondiElimina
    Risposte
    1. Thank you for appreciation.
      It's very strange, I'm using pl/SQL developer (V 11.0.5 by allroundAutomation), and using view spec & body I can view structure on left side (sceenshot here https://drive.google.com/open?id=0Bwc9IYMulx70cVZWNWdQRUIwZ0U).
      I do not remember how to move down the table, I should review the code. Have you tried to set x,y coordinates and print a blank line ?
      Give me a bit of time and I will be more specific.

      Elimina
    2. Thank you for responding.
      Yes I try to set coordinates and move table from top, but when I'm moving table, the result is cutting bottom of table and move to next page. I also try to set margins, but without success.
      Milica

      Elimina
    3. Let's see if I well understand your question .
      Do you want to insert a table aligned with the bottom of the sheet , in other words you'd like determine the height of the table to calculate the y coordinate to use?

      Elimina
  12. Yes. I want to have a control of position of table (bottom, left, right...), and if I want to choose to spread a table to be capable to doing that (within the limits of paper format, of course).
    Thank you very much for responding,
    Milica

    RispondiElimina
    Risposte
    1. The 3.5.11 version does not allow you to control the position of the table, but only to define the offset relative to the top and left margins .
      I realized the .12 version, now in the debugging phase, in which I implemented the horizontal alignment, it was easy because I know in advance the table width.
      The vertical alignment is more complicated because I don't know the height of the table until the end of the print process.
      If I know table height, can try to set p_startY = (PageHeight - TopMargin -BottomMargin -TableHeight)
      If I don't know the height of the table, I would need to perform printing process 2 times, the first time to determine the height of the table, then clean the print, set p_startY and repeat print.
      But, what should I do when the table is printed on multiple pages ? In this case, each page can have a different height of the table.
      I hope Valign is not needed in this case.
      Valerio

      Elimina
  13. Hi Valerio,
    I have to say this is a great package that help to make Oracle more powerful and solve many problems for developers like me.
    I have your package version of 3.5.12 and is also trying to use the query2table function. However, I am unable to print the table at the correct position after some header data. It always print at the top overlapping the header data. I wonder if I am missing something or doing something not right. I hope you can give me some advise.
    This is how I call it:
    as_pdf3_v5.query2table(v_vSQL,
    r_d1_fmt,
    'ffffff',
    0, -- no header
    6, -- row height
    'mm',
    20, -- p_startX
    0,
    1,
    250 -- p_startY
    );
    Thank you. William.

    RispondiElimina
  14. Hi Valerio,
    Sorry for the previous question. I finally figured out. I set the offsetX and offsetY in the first column and now it is working fine.
    Thank you. William

    RispondiElimina
  15. How can i show pdf in browser instead of saving ?

    RispondiElimina
    Risposte
    1. You can use get_pdf function which returns BLOB data of PDF.
      Example:
      select 'application/pdf', as_pdf3_v5.get_pdf() from dual

      Elimina
  16. Hi Valerio,
    I have used this package for creating pdf document using SQL query. Query is returning 2 rows but in pdf it is displaying in table and there are 3 rows including empty row on top.
    how to avoid the empty table row on top? do I need to comment any code?

    Thanks in advance..

    RispondiElimina
  17. Hi Valerio,
    I am facing a problem. When using query2table procedure to show query data in table form,
    Empty table row is adding on top and 3 rows displayed whereas query returns only 2 rows.

    Thanks.!

    RispondiElimina
    Risposte
    1. Hi Adhilakshmi,
      Without code I can only speculate, query2table presupposes a column header, do you have configured it ? (see exemple code at line 42).
      If you don't want the column headings, please set hRowHeight = 0, it's the fourth parameter of query2table, immediately after the colors
      I hope to be helpful, otherwise please send me your code.

      Valerio

      Elimina
  18. I was thinking back to your question, maybe you asking me how to get the clob PDF without saving it? You can do it insteda of
    as_pdf3_v5.save_pdf(v_vOraDir, v_vFileName, TRUE);
    use:
    blobPDF blob;
    blobPDF := as_pdf3_v5.get_pdf;
    now blobPDF contain your pdf file without saving it !

    Perhaps at this link our friend Tom gives us a cue to send the PDF stored into a blob to the browser
    https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::p11_question_id:232814159006

    RispondiElimina
  19. Hi,

    I am trying to use your code to create a pdf file.
    I have a dynamic sql and passing the query (v_query1) as follows:-

    as_pdf3_v5.query2table( v_query1,
    v_col_format,
    as_pdf3_v5.tp_colors('000000',v_vHeadColor,'000000','000000','ffffff','000000','000000',v_vOddColor,'000000'),15,15, 'mm',0,1);

    I am getting 'no data found' oracle error. When I am running the SQL in v_query1 in isolation then it is returning data.

    Can you please help?

    Thanks
    Saty

    RispondiElimina
    Risposte
    1. Hi Saty,
      I have never experienced this mistake, I can only make hypotheses,forgive me if I'm banal.
      I guess you've tried the example at the beginning of this page and it worked.

      The following code writes the number of rows returned by the dynamic query, if I understand using your query1 it works.

      declare
      v_rows number;
      v_query1 varchar2(1000) := 'select * from dual';
      begin
      execute immediate 'Select count(*) from ('||v_query1||')' into v_rows;
      DBMS_OUTPUT.PUT_LINE( to_char(v_rows));
      end;

      Can you run in debug mode to see the exact point where the error is generated?
      Sorry but the information is very few and for me it is difficult to replicate the error on my system.

      Elimina
    2. If you can, send me an example of your query and the chema of the tables at this email address:
      blogcodesnippets-valerio_at_yahoo_dot_it (replace _at_ and _dot_ width correct chars)

      Elimina
  20. Questo commento è stato eliminato da un amministratore del blog.

    RispondiElimina
  21. Questo commento è stato eliminato da un amministratore del blog.

    RispondiElimina
  22. Questo commento è stato eliminato da un amministratore del blog.

    RispondiElimina
  23. hi.
    im korean.
    this package good...
    but, have to use unicode (arial unicode)
    how to make init_core_font arialuni.ttf

    this method to slow (execute time : 60 seconds)
    as_pdf3_v5.set_font( as_pdf3_v5.load_ttf_font( 'MY_FONTS', 'arialuni.ttf', 'CID'), 10 );

    i'd appreciate it if you could tell me how to make "init_core_font <- arialuni.ttf".

    RispondiElimina
  24. Hi
    the core of this package was written by Anton Sheffer.

    The function load_ttf_font do this
    1 Load ttf file into blob
    2 Parse blob and load it font into a record of type tp_font named this_font
    3 add this_font into table of g_fonts and return index position

    I think that more time is used for parsing, my idea is:
    Add a table to Db with same recordtype of tp_font.
    Load ttf font and save this_font into table (only 1 time).
    Add a function load_ttf_font_from_db that read this_font from db ad add to font table.

    What do you think about it ?
    If you like, you can send me the functions to save and read the font from db so I add it to the package.

    RispondiElimina
    Risposte
    1. Thank you.

      If so, please let me try again.
      Thank you for responding to my inquiry.

      my email : sonicbaba@hanmail.net

      Elimina
  25. I have problems generating a loop for loop with query2table when executing query2table for the second time (vSQL, r_Fmt, as_pdf3_v5.tp_colors ('000000'), 15,5, 'mm'); I generate this error

    ORA-06502: PL / SQL: numeric or value error
    ORA-06512: in "ARAS.AS_PDF3_V5", line 1643
    ORA-06512: in "ARAS.AS_PDF3_V5", line 1664
    ORA-06512: in "ARAS.CREATE_PDF", line 648
    ORA-06512: online 13

    RispondiElimina
    Risposte
    1. Hi, please can you past me code line 1643 to 1664 ?

      Elimina
    2. clear...

      function set_font
      ( p_family varchar2
      , p_style varchar2 := 'N'
      , p_fontsize_pt number := null
      , p_output_to_doc boolean := true
      )
      return pls_integer
      is
      t_family varchar2(100);
      t_style varchar2(100);
      begin
      if p_family is null and g_current_font is null
      then
      return null;
      end if;
      if p_family is null and p_style is null and p_fontsize_pt is null
      then
      return null;
      end if;
      t_family := coalesce( lower( p_family )
      , g_fonts( g_current_font ).family
      );
      t_style := upper( p_style );
      t_style := case t_style
      when 'NORMAL' then 'N'
      when 'REGULAR' then 'N'
      when 'BOLD' then 'B'
      when 'ITALIC' then 'I'
      when 'OBLIQUE' then 'I'
      else t_style
      end;
      t_style := coalesce( t_style
      , case when g_current_font is null then 'N' else g_fonts( g_current_font ).style end
      );
      -- ******************************************LINE 1643***********************
      for i in g_fonts.first .. g_fonts.last
      loop
      if ( g_fonts( i ).family = t_family
      and g_fonts( i ).style = t_style
      )
      then
      return set_font( g_fonts( i ).fontname, p_fontsize_pt, p_output_to_doc );
      end if;
      end loop;
      return null;
      end;
      --
      procedure set_font
      ( p_family varchar2
      , p_style varchar2 := 'N'
      , p_fontsize_pt number := null
      , p_output_to_doc boolean := true
      )
      is
      t_dummy pls_integer;
      begin
      t_dummy := set_font( p_family, p_style, p_fontsize_pt, p_output_to_doc );
      end;

      -- Change only font style

      Elimina
    3. Thanks, I can try to debug it on Monday morning at work because at home I have not oracle database installed

      Elimina
    4. Ok Thanks, I'm going to send you the procedure so you can see what I'm trying to do

      /***************************************************************************************/

      /**********************The sql statement yields a result of two data*********************/

      v_vSQLs := v_vSQLs||' SELECT..... '||sENT;

      OPEN ccursor FOR v_vSQLs;
      LOOP
      FETCH ccursor INTO vLinea;
      EXIT WHEN ccursor%NOTFOUND;

      v_vSQL := '';

      i:=1; -- (riga di rottura
      r_Fmt.extend(3);
      r_Fmt(i).colWidth:=17;
      r_Fmt(i).colLabel:='No. Factura';
      r_Fmt(i).hFontStyle:='B';
      r_Fmt(i).hFontSize:=8;
      r_Fmt(i).hAlignVert:='T';
      r_Fmt(i).tAlignment:='C';
      r_Fmt(i).tCHeight := 5;
      r_Fmt(i).hCHeight := 7;
      r_Fmt(i).cellRow := 1;

      i:=i+1; --2
      r_Fmt(i).colWidth:=26;
      r_Fmt(i).colLabel:='Núm. Contenedor';
      r_Fmt(i).hFontStyle:='B';
      r_Fmt(i).hFontSize:=8;
      r_Fmt(i).hAlignVert:='T';
      r_Fmt(i).tAlignment:='C';

      i:=i+1; --3
      r_Fmt(i).colWidth:=29;
      r_Fmt(i).colLabel:='Número BL';
      r_Fmt(i).hFontStyle:='B';
      r_Fmt(i).hFontSize:=8;
      r_Fmt(i).hAlignment:='C';
      r_Fmt(i).hAlignVert:='T';
      r_Fmt(i).tAlignment:='C';


      v_vSQL := v_vSQL||'SELECT CAMPO1, CAMPO2, CAMPO3..... '||sENT;

      as_pdf3_v5.query2table(v_vSQL, r_Fmt, as_pdf3_v5.tp_colors('000000'), 15,5, 'mm');

      as_pdf3_v5.save_pdf(v_vOraDir, v_vFileName, TRUE);

      END LOOP;

      Elimina
    5. Good idea.
      If I understand, you would like to produce a pdf file for each record in the v_vSQLs query.
      Some questions,
      r_Fmt is defined into the loop, but his values seems to be constants with no reference to vLinea. You can try to move it out of the loop.
      The problem might be r_Fmt.extend (3) which adds 3 columns to each cycle, In this case in the second cycle, query2table finds a numbers of format columns different than the ones in the query.
      When you reference vLinea ?
      I hope this is a solution, please let me know if you can solve it, I'm curious

      Elimina
    6. Ok ... Thanks for your collaboration,

      I will answer your questions

      1) r_Fmt already pulling out of the loop and indeed now if I paint the detail, the problem that comes with this is that I need to be dynamic example values.

          I: = 1;
          IF vLinea = 'SHIP' THEN
              R_fmt.extend (5);
              R_fmt (i) .colWidth: = 38;
                                                      
              I: = i + 1; --5
              R_fmt (i) .colWidth: = 38;

              I: = i + 1; - 6
              R_fmt (i) .colWidth: = 38;

              I: = i + 1; --7
              R_fmt (i) .colWidth: = 38;

              I: = i + 1; - 10
              R_fmt (i) .colWidth: = 38;
          ELSE
                              
              IF vLinea = 'KN' THEN
                  R_fmt.extend (10);
                  R_fmt (i) .colWidth: = 17;
                                                              
                  I: = i + 1; --2
                  R_fmt (i) .colWidth: = 26;
                                                              
                  I: = i + 1; --3
                  R_fmt (i) .colWidth: = 29;
                                                                      
                  I: = i + 1; --4
                  R_fmt (i) .colWidth: = 16;

                  I: = i + 1; --5
                  R_fmt (i) .colWidth: = 12;

                  I: = i + 1; - 6
                  R_fmt (i) .colWidth: = 21;

                  I: = i + 1; --7
                  R_fmt (i) .colWidth: = 16;

                  I: = i + 1; --8
                  R_fmt (i) .colWidth: = 18;

                  I: = i + 1; - 9
                  R_fmt (i) .colWidth: = 16;

                  I: = i + 1; - 10
                  R_fmt (i) .colWidth: = 19;
                                            
              ELSE
                                  
                  R_Fmt.extend (9);
                  R_Fmt (i) .colWidth: = 20;

                  I: = i + 1; --2
                  IF vLinea = 'GEAR' THEN
                      R_Fmt (i) .colWidth: = 34;
                  ELSE
                      R_Fmt (i) .colWidth: = 29;
                  END IF;
                                                        
                  I: = i + 1; --3
                  R_Fmt (i) .colWidth: = 32;

                  I: = i + 1; --4
                  R_Fmt (i) .colWidth: = 23;
                                       
                  I: = i + 1; --5
                  R_Fmt (i) .colWidth: = 12;
                                      
                  I: = i + 1; - 6
                  R_Fmt (i) .colWidth: = 21;
                                      
                  IF vLinea = 'GEAR' THEN
                      I: = i + 1; --7
                      R_Fmt (i) .colWidth: = 15;
                                          
                      I: = i + 1; --8
                      R_Fmt (i) .colWidth: = 14;
                  ELSE
                      I: = i + 1; --7
                      R_Fmt (i) .colWidth: = 16;
                                          
                      I: = i + 1; --8
                      R_Fmt (i) .colWidth: = 18;
                  END IF;
                                      
                  I: = i + 1; - 9
                  R_Fmt (i) .colWidth: = 19;
              END IF;
          END IF;

      Where vLinea biene of an SQL statement which can bring me one or several records.

      With your answer I could already paint the SQL statement where it brings all the detail, now I need that for each vLinea also paint the header

      Elimina
  26. Well,
    number of columns depends from vLinea, you must put R_fmt definition into loop.
    Your code at point 1) work fine but we must clean R_Fmt at beginning of each loop
    I := 1;
    -- This Statment remove all elements from R_fmt
    if R_Fmt.count>0 Then
    R_Fmt.trim(R_Fmt.count)
    end if;
    --
    IF vLinea = 'SHIP' THEN
    ..

    RispondiElimina
    Risposte
    1. Questo commento è stato eliminato dall'autore.

      Elimina
    2. Thank you very much for your help.
      Sorry for the last thing you could tell me how I can know the last position of p_y, thanks.

      Elimina
    3. See below I understand your question in spanush

      Elimina
  27. You can use function get(7). Funtion get accept any constant c_get
    7 for current y, 6 for current x and so on

    RispondiElimina
    Risposte
    1. Sorry I tried to do it but I have not succeeded, if you could help me

      I'll show you what I'm trying to do, after painting this information

      As_pdf3_v5.query2table (v_vSQLc, r_Fmtc, as_pdf3_v5.tp_colors ('000000', 'ffffff', '000000', '000000', 'A4A4A4', '000000', '000000'));

      I want to know the position p_y to be able to start the footer

           As_pdf3_v5.set_font ('helvetica', 'B', 8);
           As_pdf3_v5.write ('* Please send the payment support to these mail *', p_x => 35, p_y => ?);

      Elimina
    2. If you write at current y position simply
      As_pdf3_v5.write ('* Please send the payment support to these mail *', p_x => 35);
      When p_y is null or missig procedure write use current y value, the same for p_x.

      Elimina
    3. Good day,
      OK ... Actually not placing the position And the pint underneath, this works for me perfect for the write () function, but for horizontal_line () no, for this function I must pass the parameter Y and I try to make the Page is enclosed in a box.

      Elimina
  28. try this (I'm at home and can't test it)

    as_pdf3_v5.horizontal_line( p_x := 1, p_y :=as_pdf3_v5.get( as_pdf3_v5.C_GET_Y ) , p_width := 100);

    replace p_x, p_width with correct values in PT.

    RispondiElimina
    Risposte
    1. Thank you for what you provided, it was very helpful.
      I'll bother you with one last thing as I can control the creation of a new sheet, that is if the information I paint in my pdf is very at the end of the sheet 1 I want my footer to go to the next page It does but it is divided, a piece in sheet 1 and the rest in sheet 2.

      Elimina
    2. Apologize me if my answer is silly.
      Use new_page procedure to create new sheet.

      function chkNewPage(y in number, t_line_height in number) return number is
      t_y number;
      begin
      -- Update t_y and check in new page are necessary
      t_y := y - t_line_height;
      if t_y < g_settings.margin_bottom then
      new_page;
      t_y := g_settings.page_height - g_settings.margin_top - t_line_height;
      end if;
      return(t_y);
      end;

      This function, (you can find itat beginning of Write procedure), is an example of how to test whether text goes beyond the end of the page.
      Instead of t_line_height, use eight of your footer = (LineHeight*NumeberOfRows).

      This is an example on how to calculate LineHeight
      t_line_height := nvl( conv2uu(p_line_height,p_um ), g_fonts( g_current_font ).fontsize );

      Elimina
  29. Buongiorno Valerio,
    complimenti per il suo eccellente lavoro,
    volevo chiederle come poter inserire internamente un nuovo font, come già presenti altri tipi di font, non posso caricare esternamente un font, ma non capisco come sia codificato il font interno, se esiste qualche generatore online per poter convertire un font nelle stringhe interne per la definizione di un nuovo font.

    Grazie
    Salvatore

    RispondiElimina
    Risposte
    1. Buonasera Salvatore,
      grazie per i complimenti, ma buona parte del merito è di Anton Sheffer.
      Se ho capito bene si riferisce alla procedura init_core_fonts, tale procedura è stata scritta da Anton e serve per definire i font incorporati in AdobeReader. Gli esempi mostrano come caricare un font da un file, tuttavia esistono più definizioni della procedura load_ttf_font, tra queste alcune accettano un blob. Ora non le resta che caricare in una variabile blob il contenuto del file con il font che vuole usare. Prendendo spunto dalla tecnica usata da Anton si potrebbe:
      leggere il file in blocchi (esempio 2Kbyte) codificare in base64 (utile per scongiurare la presenza di caratteri speciali o delimitatori), il testo ottenuto va racchiuso tra ' e associato a una variabile varchar2.
      Nel package si può aggiungere una procedura decodifica la stringa in base64 e la accoda alla variabile blob (usando la procedura dbms_lob.append ).

      Valerio

      Elimina
  30. WHO COULD I CREATE A PDF DOCUMENT BUT IN A PDF VERSION MORE THAN 1.4?? BECAUSE THIS PACKAGE SEEMS TO CREATE IT IN 1.3 VERSION....THANK YOU FOR YOUR HELP.

    RispondiElimina
  31. I do not know, I have only added some features to the original package by Anton Sheffer
    https://technology.amis.nl/2012/04/11/generating-a-pdf-document-with-some-plsql-as_pdf_mini-as_pdf3/

    RispondiElimina
  32. Hi, i want your help

    my PDF is up to five pages, I would like to leave it twice, could you tell me how I can do this.

    RispondiElimina
    Risposte
    1. Hello, I'm not sure I understand the question, do you want the number of pages to always be even? Or do you want to use the pdf document twice?

      In the first case you can modify procedure finish_pdf.

      procedure finish_pdf
      is
      t_xref number;
      t_info number(10);
      t_catalogue number(10);
      begin
      -- Add une page if document is empty
      if g_pages.count = 0 then
      new_page;
      end if;
      -- Add one page if number of pages is odd
      if mod(g_pages.count, 2) = 1 then
      new_page;
      end if;

      ...
      end;

      In the second case it is quite easy, in the example the last procedure
      as_pdf3_v5.save_pdf(v_vOraDir, v_vFileName, TRUE);
      save document to file and release CLOB variable where PDF is stored.
      Changing last parameter to FALSE, you preserve CLOB variable

      To get the CLOB variable after saving you will have to modify the function get_pdf
      in spec:
      function get_pdf(bFinish in boolean := TRUE) return BLOB;

      in body:
      function get_pdf(bFinish in boolean := TRUE) return BLOB is
      begin
      if bFinish then
      finish_pdf;
      end if;
      return g_pdf_doc;
      end;

      calling get_pdf(FALSE) you can retrieve CLOB variable containing PDF document and use it many times you want.

      Elimina
    2. Thanks, what I want is that you do not have a limit on the number of pages per document, apparently it has a limit of five pages

      Elimina
    3. Thanks, the problem I have is that the pdf document is generated until page five and I require that it does not have a page limit

      Elimina
    4. Questo commento è stato eliminato dall'autore.

      Elimina
    5. there is no limitation in the number of pages, I have in production reports of over than 100 pages.
      If you try example at top of this page and change the row
      FROM DUAL d CONNECT BY ROWNUM <= 10

      from 10 to 100 or more you obtain a pdf with 7 or more page.
      Ok, isn't a beauty example, the scope is to demostrate that there isn't a limitation.

      Does your the PDF production stop with some errorr code after page five ?
      For my experience often the problem is data to write. If you can send me an example of your pdf, I can try to debug it.

      Elimina
  33. Hi, my name is Toni and I’m a Spanish programmer.

    I’m modifying the code to adapt it to our needs in Spain, but I can’t get accentuated vowels (á,é,í,ó,ú, Á,É,Ó,Ú).

    You could tell me that I have to modify in the code as_pdf3_v5 to get accented vowels in the final pdf.

    I usually use truetype fonts that load with Loadttf embedded in the document.

    RispondiElimina
  34. How can I get a PDF/A document with the package AS_PDF3_V5?

    RispondiElimina
  35. Hi Tony, I'm sorry but I don't know how to help you.
    The package was written by Anton Sheffer, especially the procedures that write the PDF. I've only added some features. I believe that to adapt the package to the PDF / A format it is necessary to compare the documentation of the two formats and adjust all the file writing procedures.
    As for the accented vowels, I usually use them in my documents in Italian, but I usually don't use TTF. Give me a couple of days to do a test with TTF . See you soon

    RispondiElimina
  36. Hi Valerio,

    Thanks for sharing your work. I tried to run an example your provided (https://drive.google.com/file/d/0Bwc9IYMulx70X3FjVXZNS0h0S3M/edit) but it does not work as expected. the table in PDF file is corrupted. Take a look https://ibb.co/NCXg1Rn

    Any ideas why?

    Regards,
    Greg

    RispondiElimina
    Risposte
    1. with that example I wanted to show too many things at once, but result isn't very good.
      what do you expect to see? a classic table?
      The trick is in the configuration of the columns.

      Elimina
  37. Almost Worked Everything mentioned. Excellent work... Beautiful Barcodes ...All Good
    # Bug: When column width is 0 and the value is numeric, the value still gets printed.
    However if the column type is text, the column value gets hidden.

    I use column width=0 where I wish to hide the columns...
    Thanks, Please post an update !!

    RispondiElimina
  38. When query2table() is called with p_bulk_size=0 for an unlimited number of lines, and the result set happens to be empty, then dbms_sql.define_array() is called with cnt=0, which makes it throw an ORA-29253. One way to prevent this is to insert a case for bulk_size=0:
    case
    when p_bulk_size = 0 then
    null;
    when t_desc_tab( c ).col_type member of t_numerics then
    dbms_sql.define_array( p_c, c, n_tab, p_bulk_size, 1 );
    when t_desc_tab( c ).col_type member of t_dates then
    dbms_sql.define_array( p_c, c, d_tab, p_bulk_size, 1 );
    when t_desc_tab( c ).col_type member of t_chars then
    dbms_sql.define_array( p_c, c, v_tab, p_bulk_size, 1 );
    when t_desc_tab( c ).col_type member of t_blobs then
    dbms_sql.define_array( p_c, c, b_tab, p_bulk_size, 1 );
    else
    null;
    end case;

    That seems to successfully suppress the table when no data is found.

    RispondiElimina
  39. Hi valerio,
    I know it has been awhile since receiving questions. Hopefully I can still get some answers.
    1. Can I do any type of text alignment using write in header ? If not using write any other possibilities for text boxes with text wrapping in header without me having to compute manually?
    Thanks,
    Tom

    RispondiElimina
    Risposte
    1. Hi Tom
      in the above example, line 45 specifies the horizontal alignment for the column heading.
      unfortunately I never had the need to use text wrapping for the column header. The implementation is not trivial, it is necessary, at the very least, to transform the headers from string to string array and replace the procedure that calculates the height of the header row

      Elimina
  40. Valerio,
    Thanks for the response. I will work around. I am getting into details of working with query2table and when I hide the headers I am getting a space above the table I cannot seem to get rid of. The headers don't show but the space remains. I am trying to stack tables with no space in between. Any suggestion appreciated. Also any text underlining capability or do I have to implement manually. Great package !!!
    Thanks,
    Tom

    RispondiElimina
  41. Also in my previous question I should have put text alignment in "PAGE HEADER" ? Can you only use put in there and not write ?

    RispondiElimina
  42. I'm sorry, any experience with Oracle 19c. You can Try tro tpocontact original author Anton Sheffer

    RispondiElimina
  43. Hi Can you share the code encrypted Password in PDF.
    Thanks in Advance

    RispondiElimina
    Risposte
    1. I’m sorry I jave no code for encrypt pdf

      Elimina
  44. HI,
    I've generated PDF Successfully, Now i need PDF encrypted with Password. Can you share me the Code for the same.
    Thanks In advance

    RispondiElimina
  45. Hi valerio, I've generated PDF Successfully, use Barcodes but when scanning it, it does not show the correct information

    RispondiElimina
  46. Hi Mairena,
    how did you generate the barcode?

    RispondiElimina
  47. Hi Valerio,

    I am in the following circumstance.
    The pdf file already exists in a table on a BLOB column ...and inside the pdf file I have the strings #NAME# and #TABLE#.
    Using AS_PDF3_V5 package, can I replace in the pdf file the two strings with data taken from the database?
    1) #NAME# with customer name (e.g replacement of #PAGE_NR# with the current page number).
    2) #TABLE# with a query result (e.g using QUERY2TABLE procedure).

    Thanks for any help or advice,
    Ciprian

    RispondiElimina
  48. Hi Valerio, having generated successfully some PDF with your PL/SQL package, I find it however quite tedious to manually, by code, create the PDF report layout.
    I've come up with a different approach for generating PDF reports from an ORACLE SQL query which ease significantly the PDF layout creation.
    It involves a Powershell Script, a Reportviewer RDLC component and the Oracle Managed Client dll.
    The big advantage is that the PDF report creation, can be done very quickly from Visual Studio designer, graphically.
    The powershell script pull the data from the DB and send it to the report (classic .Net code ported to Powershell).
    You can then add the powershell script to a windows task scheduler for instance and voilà, you can send by mail PDF, XL or Word reports very easily.
    I'll be glad, if you're interested, to send you a complete test case.
    Fill free...
    Olivier

    RispondiElimina
  49. Great job Oliver.
    i changed job and i no longer follow pdf production in oracle, but your work may be useful to others. ASPDF is tedious, but in my case I was forced to produce pdf from automatic procedures scheduled in the oracle environment, so no windows.

    RispondiElimina
  50. Not a great solution but I added a double replace in txt2raw that repaces () with [] at the top into a new text variable and then use that and it's working with just that change. I haven't had time to go deeper into why yet and am calling it a day. If I need to use () or discover other characters, etc I'll update again.

    RispondiElimina
  51. I had the same problem. In package as_pdf3, I replaced
    function pdf_string( p_txt in blob )
    return blob
    is
    t_rv blob;
    with
    function pdf_string( p_txt in raw )
    return raw
    is
    t_rv raw(32767);

    ... and it worked like before!
    Cheers, Rob

    RispondiElimina
  52. Hi,
    I need to put a hyperlink on Image in the PDF document. So that by clicking on it to open the web page. Is there a way to do this with as_pdf3?

    RispondiElimina
  53. Sorry, I don't know how hyperlinks are implemented in pdf documents. This package is many years old and I have only added some features to Anton Sheffer's original pkg

    RispondiElimina
  54. I had an issue with ( ) brackets, I replaced them with [ ] and it fixed the package from hanging, I am not sure why.

    RispondiElimina
  55. Thanks for wonderful job. Table Column grouping is possible. i.e subcolumn

    RispondiElimina
  56. Thanks, most of the credit goes to anton sheffer. the requested functionality is not provided and I can't help you because I don't work on oracle structures anymore. Sorry

    RispondiElimina