Back to... PDF Writer

Source file : pdf_out.ads



-------------------------------------------------------------------------------------
--
--  PDF_OUT - A low level package for writing Adobe Acrobat PDF (*) files
--
--  Pure Ada 95 code, 100% portable: OS-, CPU- and compiler- independent.
--
--  Version / date / download info: see the version, reference, web strings
--    defined at the end of the public part of this package.

--  Legal licensing note:

--  Copyright (c) 2014 .. 2023 Gautier de Montmollin

--  Permission is hereby granted, free of charge, to any person obtaining a copy
--  of this software and associated documentation files (the "Software"), to deal
--  in the Software without restriction, including without limitation the rights
--  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
--  copies of the Software, and to permit persons to whom the Software is
--  furnished to do so, subject to the following conditions:

--  The above copyright notice and this permission notice shall be included in
--  all copies or substantial portions of the Software.

--  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
--  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
--  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
--  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
--  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
--  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
--  THE SOFTWARE.

--  NB: this is the MIT License, as found 12-Sep-2007 on the site
--  http://www.opensource.org/licenses/mit-license.php

--  (*) All Trademarks mentioned are properties of their respective owners.
-------------------------------------------------------------------------------------
--
--  Follow the steps below to create a PDF document stream.
--  You can also get inspiration from the demos, tests & tools.
--
--  1. Create
--
--  2. | Put (pdf, data),
--     | New_Line (pdf), ... : other "Text_IO"-like (full list below)
--     | Image (pdf, ...)    : raster images
--     | Move/Line/...       : vector graphics
--     | New_Page (pdf)
--
--  3. Close
--
--  4. (PDF_Out_String only) function Contents returns the full .pdf
--
--  Header and footer are set up by overriding the corresponding methods.
--
--  Note: the standard PDF measurement unit is a "point", set as 1/72 inch.
--
--  All technical references are to PDF 1.7 format, ISO 32000-1:2008 standard
--  https://opensource.adobe.com/dc-acrobat-sdk-docs/standards/pdfstandards/pdf/PDF32000_2008.pdf
--
--------------------------------------------------------------------------

with Ada.Streams.Stream_IO,
     Ada.Strings.Unbounded,
     Ada.Strings.Wide_Unbounded,
     Ada.Strings.Wide_Wide_Unbounded,
     Ada.Text_IO;

with System;

package PDF_Out is

  -------------------------------------------------------------------
  --  The abstract PDF output stream root type.                    --
  --  From this package, you can use the following derived types:  --
  --     * PDF_Out_File    : output in a file                      --
  --     * PDF_Out_String  : output in a string                    --
  --  Of course you can define your own derived types.             --
  -------------------------------------------------------------------

  type PDF_Out_Stream is abstract tagged private;

  PDF_stream_not_created,
  PDF_stream_not_closed,
  Not_implemented : exception;

  type PDF_type is (
    PDF_1_3  --  PDF 1.3
  );

  Default_PDF_type : constant PDF_type := PDF_1_3;

  type Real is digits System.Max_Digits;

  package Real_IO is new Ada.Text_IO.Float_IO (Real);

  type Point is record
    x, y : Real;
  end record;

  function "+"(P1, P2 : Point) return Point;
  pragma Inline ("+");

  function "*"(f : Real; P : Point) return Point;
  pragma Inline ("*");

  type Rectangle is record
    x_min, y_min,
    width, height : Real;
  end record;

  function "+"(P : Point; r : Rectangle) return Rectangle;
  pragma Inline ("+");

  --  Scaling. r.x_min and r.y_min are preserved.
  function "*"(f : Real; r : Rectangle) return Rectangle;
  pragma Inline ("*");

  function X_Max (r : Rectangle) return Real;
  function Y_Max (r : Rectangle) return Real;

  use Ada.Strings.Unbounded;

  ------------------------------
  --  (2) Document contents:  --
  ------------------------------

  procedure Put (pdf  : in out PDF_Out_Stream;
                 num  : in Real;
                 fore : in Ada.Text_IO.Field := Real_IO.Default_Fore;
                 aft  : in Ada.Text_IO.Field := Real_IO.Default_Aft;
                 exp  : in Ada.Text_IO.Field := Real_IO.Default_Exp);

  procedure Put (pdf   : in out PDF_Out_Stream;
                 num   : in Integer;
                 width : in Ada.Text_IO.Field       := 0;  --  ignored
                 base  : in Ada.Text_IO.Number_Base := 10);

  procedure Put (pdf : in out PDF_Out_Stream; str : String);
  procedure Put (pdf : in out PDF_Out_Stream; str : Unbounded_String);

  procedure Put_Line (pdf  : in out PDF_Out_Stream;
                      num  : in Real;
                      fore : in Ada.Text_IO.Field := Real_IO.Default_Fore;
                      aft  : in Ada.Text_IO.Field := Real_IO.Default_Aft;
                      exp  : in Ada.Text_IO.Field := Real_IO.Default_Exp);

  procedure Put_Line (pdf   : in out PDF_Out_Stream;
                      num   : in Integer;
                      width : in Ada.Text_IO.Field       := 0;  --  ignored
                      base  : in Ada.Text_IO.Number_Base := 10);

  procedure Put_Line (pdf : in out PDF_Out_Stream; str : String);
  procedure Put_Line (pdf : in out PDF_Out_Stream; str : Unbounded_String);

  procedure New_Line (pdf : in out PDF_Out_Stream; Spacing : Positive := 1);
  procedure New_Page (pdf : in out PDF_Out_Stream);

  --  Support for Wide_String's, usually containing UTF-16 or UCS-2 strings.
  --  Caution: only the ISO-8859-1 (Latin-1) subset is currently supported.

  procedure Put_WS (pdf : in out PDF_Out_Stream; w_str : Wide_String);

  procedure Put_Line_WS
     (pdf : in out PDF_Out_Stream; w_str : Wide_String);

  procedure Put_Line_WS
     (pdf   : in out PDF_Out_Stream;
      w_str :        Ada.Strings.Wide_Unbounded.Unbounded_Wide_String);

  --  Support for Wide_Wide_String's, containing UTF-32 strings.
  --  Caution: only the ISO-8859-1 (Latin-1) subset is currently supported.

  procedure Put_WWS (pdf : in out PDF_Out_Stream; ww_str : Wide_Wide_String);

  procedure Put_Line_WWS
     (pdf : in out PDF_Out_Stream; ww_str : Wide_Wide_String);

  procedure Put_Line_WWS
     (pdf    : in out PDF_Out_Stream;
      ww_str :        Ada.Strings.Wide_Wide_Unbounded.Unbounded_Wide_Wide_String);

  --  Call to Finish_Page is optional, but can be necessary in some circumstances,
  --  for instance for displaying the footer correctly before changing page
  --  orientation or margins for the following pages.
  procedure Finish_Page (pdf : in out PDF_Out_Stream);

  procedure Text_XY (pdf : in out PDF_Out_Stream; x, y : Real);
  procedure Put_XY (pdf : in out PDF_Out_Stream; x, y : Real; str : String);

  function Col (pdf : in PDF_Out_Stream) return Positive;
  function Line (pdf : in PDF_Out_Stream) return Positive;
  function Page (pdf : in PDF_Out_Stream) return Natural;

  type Font_Type is
     ( --  The 14 standard fonts
      Courier,
      Courier_Bold,
      Courier_Bold_Oblique,
      Courier_Oblique,
      Helvetica,
      Helvetica_Bold,
      Helvetica_Bold_Oblique,
      Helvetica_Oblique,
      Symbol,
      Times_Bold,
      Times_Bold_Italic,
      Times_Italic,
      Times_Roman,
      Zapf_Dingbats,
      --  Fonts imported into the PDF document
      External_Font
     );

  subtype Standard_Font_Type is Font_Type range Courier .. Zapf_Dingbats;

  --  Select one of the Adobe PDF standard fonts.
  --  The encoding is on 8 bits and follows the "Windows Code Page 1252"
  --  encoding (called WinAnsiEncoding in the PDF standard).
  --  See Annex D, especially "Table D.1 - Latin-text encodings" for details.
  procedure Font (pdf : in out PDF_Out_Stream; f : Standard_Font_Type);

  --  Set the font size.
  --  In general the size is a scale factor (see Table 105, Tf operator).
  --  For standard fonts the unit seems to be the Point (pt).
  procedure Font_Size (pdf : in out PDF_Out_Stream; size : Real);

  procedure Line_Spacing (pdf : in out PDF_Out_Stream; factor : Real);  --  as multiple of font size
  default_line_spacing : constant := 1.2;
  procedure Line_Spacing_Pt (pdf : in out PDF_Out_Stream; pt : Real);   --  in Point (pt) units

  --------------
  --  Colors  --
  --------------

  --  0.0 = minimum intensity
  --  1.0 = maximum intensity.
  subtype Color_Value is Real range 0.0 .. 1.0;

  type Color_Type is record
    red, green, blue : Color_Value;
  end record;

  black : constant Color_Type := (0.0, 0.0, 0.0);

  procedure Color (pdf : in out PDF_Out_Stream; c : Color_Type);
  procedure Stroking_Color (pdf : in out PDF_Out_Stream; c : Color_Type);

  type Rendering_Mode is (
    fill, stroke, fill_then_stroke, invisible,
    --  Same, but also add text to path for clipping.
    fill_and_add_to_path,
    stroke_and_add_to_path,
    fill_then_stroke_and_add_to_path,
    add_to_path
  );

  procedure Text_Rendering_Mode (pdf : in out PDF_Out_Stream; r : Rendering_Mode);

  ----------------
  --  Graphics  --
  ----------------

  --  Insert an image from a file
  procedure Image (pdf : in out PDF_Out_Stream; file_name : String; target : Rectangle);

  --  For calibrating the target rectangle in the Image procedure, you may need this:
  function Get_pixel_dimensions (image_file_name : String) return Rectangle;
  --  Caution: scaling is up to you! The rectangle returned by the function
  --  is (0.0, 0.0, width, height), with 1 pixel = 1pt.

  -----------------------
  --  Vector graphics  --
  -----------------------

  initial_line_width : constant := 1.0;  --  See Table 52, 8.4.1
  procedure Line_Width (pdf : in out PDF_Out_Stream; width : Real);

  --  Draw a single line segment:
  procedure Single_Line (pdf : in out PDF_Out_Stream; from, to : Point);

  subtype Path_Rendering_Mode is Rendering_Mode range fill .. fill_then_stroke;

  --  Draw simple figures.
  --  Rectangle:
  procedure Draw
    (pdf       : in out PDF_Out_Stream;
     what      :        Rectangle;
     rendering :        Path_Rendering_Mode);

  --  Paths:

  type Inside_path_rule is (nonzero_winding_number, even_odd);
  --  Rule to determine how to fill areas within a (non-trivial) path.
  --  See 8.5.3.3.2 and 8.5.3.3.3 of PDF specification

  procedure Move (pdf : in out PDF_Out_Stream; to : Point);
  procedure Line (pdf : in out PDF_Out_Stream; to : Point);
  procedure Cubic_Bezier
    (pdf                  : in out PDF_Out_Stream;
     control_1, control_2 :        Point;
     to                   :        Point);
  --  All lines and curves and the eventual filling inside the path
  --  will be drawn when path is completed, with Finish_Path:

  procedure Finish_Path (
    pdf        : in out PDF_Out_Stream;
    close_path :        Boolean;
    rendering  :        Path_Rendering_Mode;  --  fill, stroke, or both
    rule       :        Inside_path_rule
  );

  ------------
  --  Misc  --
  ------------

  --  In the likely case some PDF feature is not yet implemented in
  --  this package, you can insert direct PDF code - at your own risk ;-).
  --
  --  NB: the state the PDF machine is either in text-writing
  --  mode, or graphics mode. To make outputs compliant with the PDF
  --  standard, if you want to insert graphics code, please
  --  use the Insert_Graphics_PDF_Code below. For text-related stuff,
  --  use Insert_Text_PDF_Code.
  --
  procedure Insert_PDF_Code (pdf : in out PDF_Out_Stream; code : String);
  pragma Inline (Insert_PDF_Code);

  --  This is for direct text PDF code insertion (text-writing mode
  --  will be switched on). In PDF language these are the T... commands.
  --
  procedure Insert_Text_PDF_Code (pdf : in out PDF_Out_Stream; code : String);

  --  This is for direct graphics PDF code insertion (text-writing mode
  --  will be switched off for the graphics output).
  --
  procedure Insert_Graphics_PDF_Code (pdf : in out PDF_Out_Stream; code : String);

  --  Image (representation in digits) functions for numbers, designed to
  --  take the least possible room, albeit without loss of precision.
  --  Useful for inserting PDF code.
  function Img (p : Integer) return String;
  function Img (x : Real; prec : Positive := Real'Digits) return String;

  --  Document information
  procedure Title (pdf : in out PDF_Out_Stream; s : String);
  procedure Author (pdf : in out PDF_Out_Stream; s : String);
  procedure Subject (pdf : in out PDF_Out_Stream; s : String);
  procedure Keywords (pdf : in out PDF_Out_Stream; s : String);
  procedure Creator_Application (pdf : in out PDF_Out_Stream; s : String);

  -------------------
  --  Page layout  --
  -------------------

  --  You need to override the Header and Footer methods
  --  for setting up your custom header and footer. By default they do nothing.
  procedure Page_Header (pdf : in out PDF_Out_Stream);
  procedure Page_Footer (pdf : in out PDF_Out_Stream);

  --  They have to be called before New_Page in order to influence the next page.
  --  For the first page, call them before any output (typically right after Create).
  --
  procedure Left_Margin (pdf : out PDF_Out_Stream; pts : Real);
  function Left_Margin (pdf : PDF_Out_Stream) return Real;
  procedure Right_Margin (pdf : out PDF_Out_Stream; pts : Real);
  function Right_Margin (pdf : PDF_Out_Stream) return Real;
  procedure Top_Margin (pdf : out PDF_Out_Stream; pts : Real);
  function Top_Margin (pdf : PDF_Out_Stream) return Real;
  procedure Bottom_Margin (pdf : out PDF_Out_Stream; pts : Real);
  function Bottom_Margin (pdf : PDF_Out_Stream) return Real;
  --
  type Margins_Type is record
    left, right, top, bottom : Real;
  end record;

  --  Some distances in Points

  one_cm   : constant := 72.0 / 2.54;
  cm_2_5   : constant := one_cm * 2.5;
  one_inch : constant := 72.0;

  cm_2_5_margins : constant Margins_Type := (cm_2_5, cm_2_5, cm_2_5, cm_2_5);

  procedure Margins (pdf : out PDF_Out_Stream; new_margins : Margins_Type);
  function Margins (pdf : PDF_Out_Stream) return Margins_Type;

  --  A4 is 21.0 x 29.7 cm
  A4_portrait : constant Rectangle := (0.0, 0.0, 21.0 * one_cm, 29.7 * one_cm);
  A4_landscape : constant Rectangle := (0.0, 0.0, A4_portrait.height, A4_portrait.width);

  procedure Page_Setup (pdf : in out PDF_Out_Stream; layout : Rectangle);

  function Layout (pdf : PDF_Out_Stream) return Rectangle;

  --  Set_Index and Index are not directly useful for PDF_Out users.
  --  They are private indeed, but they must be visible (RM 3.9.3(10)).

  --  Set the index on the stream
  procedure Set_Index (pdf : in out PDF_Out_Stream;
                       to : Ada.Streams.Stream_IO.Positive_Count)
  is abstract;

  --  Return the index of the stream
  function Index (pdf : PDF_Out_Stream) return Ada.Streams.Stream_IO.Count
  is abstract;

  -------------------------------------------------------------------
  --  Here are derived stream types, pre-defined in this package.  --
  -------------------------------------------------------------------
  --  * Output to a file:

  type PDF_Out_File is new PDF_Out_Stream with private;

  procedure Create
    (pdf        : in out PDF_Out_File;
     file_name