Browse Source

* Refactor commands, add commands in preparation of renderer

Michaël Van Canneyt 2 years ago
parent
commit
49809c5f42

+ 3 - 0
packages/fcl-pdf/fpmake.pp

@@ -65,6 +65,9 @@ begin
     T:=P.Targets.AddUnit('src/fppdfobjects.pp');
     T:=P.Targets.AddUnit('src/fppdfobjects.pp');
     T.Dependencies.AddUnit('fppdfconsts');
     T.Dependencies.AddUnit('fppdfconsts');
 
 
+    T:=P.Targets.AddUnit('src/fppdfcommands.pp');
+    T.Dependencies.AddUnit('fppdfobjects');
+
     T:=P.Targets.AddUnit('src/fppdfscanner.pp');
     T:=P.Targets.AddUnit('src/fppdfscanner.pp');
     T.ResourceStrings:=true;
     T.ResourceStrings:=true;
     T.Dependencies.AddUnit('fppdfobjects');
     T.Dependencies.AddUnit('fppdfobjects');

+ 33 - 15
packages/fcl-pdf/src/fppdfconsts.pp

@@ -1,17 +1,3 @@
-{ **********************************************************************
-  This file is part of the Free Component Library
-
-  PDF names/constants.
-  Copyright (c) 2022 by Michael Van Canneyt [email protected]
-
-  See the file COPYING.FPC, included in this distribution,
-  for details about the copyright.
-
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-
-  **********************************************************************}
 unit fppdfconsts;
 unit fppdfconsts;
 
 
 {$mode ObjFPC}{$H+}
 {$mode ObjFPC}{$H+}
@@ -236,7 +222,32 @@ const
   SCMAPKeyWMode          = 'WMode';
   SCMAPKeyWMode          = 'WMode';
   SCMAPKeyUseCMap        = 'UseCMap';
   SCMAPKeyUseCMap        = 'UseCMap';
 
 
-
+  SPDFExtGSStateType  = 'Type';
+  SPDFExtGSStateLW    = 'LW';
+  SPDFExtGSStateLC    = 'LC';
+  SPDFExtGSStateLJ    = 'LJ';
+  SPDFExtGSStateML    = 'ML';
+  SPDFExtGSStateD     = 'D';
+  SPDFExtGSStateRI    = 'RI';
+  SPDFExtGSStateop    = 'op';
+  SPDFExtGSStateOPM   = 'OPM';
+  SPDFExtGSStateFont  = 'Font';
+  SPDFExtGSStateBG    = 'BG';
+  SPDFExtGSStateBG2   = 'BG2';
+  SPDFExtGSStateUCR   = 'UCR';
+  SPDFExtGSStateUCR2  = 'UCR2';
+  SPDFExtGSStateTR    = 'TR';
+  SPDFExtGSStateTR2   = 'TR2';
+  SPDFExtGSStateHT    = 'HT';
+  SPDFExtGSStateFL    = 'FL';
+  SPDFExtGSStateSM    = 'SM';
+  SPDFExtGSStateSA    = 'SA';
+  SPDFExtGSStateBM    = 'BM';
+  SPDFExtGSStateSMask = 'SMask';
+  SPDFExtGSStateCA    = 'CA';
+  SPDFExtGSStateca_   = 'ca';
+  SPDFExtGSStateAIS   = 'AIS';
+  SPDFExtGSStateTK    = 'TK';
 
 
 
 
   // CJK Cmaps
   // CJK Cmaps
@@ -303,6 +314,13 @@ const
   CMAPIdentity_H = 'Identity-H';
   CMAPIdentity_H = 'Identity-H';
   CMAPIdentity_V = 'Identity-V';
   CMAPIdentity_V = 'Identity-V';
 
 
+  SPDFColorSpaceDeviceGray  = 'DeviceGray';
+  SPDFColorSpaceDeviceRGB   = 'DeviceRGB';
+  SPDFColorSpaceDeviceCMYK  = 'DeviceCMYK';
+  SPDFColorSpacePattern     = 'Pattern';
+  SPDFColorSpaceCalGray     = 'CalGray';
+  SPDFColorSpaceCalRGB      = 'CalRGB';
+
 implementation
 implementation
 
 
 end.
 end.

+ 319 - 316
packages/fcl-pdf/src/fppdfobjects.pp

@@ -26,13 +26,13 @@ interface
 
 
 uses
 uses
   TypInfo,
   TypInfo,
-  {$IFDEF DEBUGPDFALLOCATION}
   Types,
   Types,
-  {$ENDIF}
   rtlConsts, SysUtils, Classes, Contnrs, fppdfconsts;
   rtlConsts, SysUtils, Classes, Contnrs, fppdfconsts;
 
 
 Const
 Const
   PDFTextArraySpaceTreshold = 200;
   PDFTextArraySpaceTreshold = 200;
+  SegmentDelta = 16;
+  PointPrecision = 0.001;
 
 
 Type
 Type
   EPDF = Class(Exception);
   EPDF = Class(Exception);
@@ -57,6 +57,87 @@ Type
   );
   );
   TPDFTokenTypes = set of TPDFTokenType;
   TPDFTokenTypes = set of TPDFTokenType;
 
 
+  TPDFLineCapStyle = (lcsbutt,lcsRund,lcsProjecting);
+  TPDFLineJoinStyle = (ljsMiter,ljsRound,ljsBevel);
+
+  TPDFStrokeOption = (soClose, soStroke, soFill, soEvenOdd, soNonZeroWinding);
+  TPDFStrokeOptions = set of TPDFStrokeOption;
+
+  TPDFColorSpace = (csDict,csDeviceGray,csDeviceRGB,csDeviceCMYK);
+  TPDFColor = array[0..3] of single;
+  TPDFColorType = (ctStroke,ctFill);
+  TPDFFullColor = record
+    Space : TPDFColorSpace;
+    Color : TPDFColor;
+  end;
+
+  TPDFTextRenderModePart = (trmFill,trmStroke,trmAddToClippingPath);
+  TPDFTextRenderMode = Set of TPDFTextRenderModePart;
+
+  TPDFTransFormationLine = Array[0..2] of Single;
+  TPDFTransFormationMatrix = Array[0..2] of TPDFTransFormationLine;
+
+
+  { TPDFPoint }
+
+  TPDFPoint = Record
+    X,Y : Single;
+    class Function Create(aX,aY : Single) : TPDFPoint; static;
+    class operator +(const A, B: TPDFPoint): TPDFPoint;
+    class operator =(const A, B: TPDFPoint): Boolean;
+    Function AsString : String;
+  end;
+
+  TPDFRect = record
+    ll : TPDFPoint;
+    ur : TPDFPoint;
+  end;
+
+  TPDFSegmentType = (stStart,stLine,stBezierLimits,stBezierControl,stClose);
+
+  { TPDFPathSegment }
+
+  TPDFPathSegment = record
+     SegmentType : TPDFSegmentType;
+     P1,P2 : TPDFPoint;
+     Function Description : String;
+  end;
+  TPDFPathSegmentArray = array of TPDFPathSegment;
+
+  { TPDFPath }
+
+  TPDFPath = Record
+  Private
+    FSegments : TPDFPathSegmentArray;
+    FCount : Word;
+    Procedure AddSegment(aSegment : TPDFPathSegment);
+    function GetSegments(aIndex : Word): TPDFPathSegment;
+  Public
+    Procedure Add(aSegment : TPDFPathSegment);
+    Procedure AddBezier(aLimits,aControl : TPDFPathSegment);
+    Procedure Clear;
+    Property Segments[aIndex : Word] : TPDFPathSegment read GetSegments;
+    Property Count : Word Read FCount;
+  end;
+  { TTransFormation }
+
+  { TPDFTransFormation }
+
+  TPDFTransFormation = Record
+    M : TPDFTransFormationMatrix;
+    Class Function Default: TPDFTransformation; static;
+    Class function Create(const a,b,c,d,e,f : Single) : TPDFTransformation; static;
+    class operator *(const A, B: TPDFTransFormation): TPDFTransFormation;
+    procedure Translate(const aX,aY : Single);
+    procedure Translate(const P: TPDFPoint);
+  end;
+
+
+  TPDFDashPattern = record
+    Phase : Integer;
+    Pattern : TIntegerDynArray; // Maybe use boolean ?
+  end;
+
   { TPDFToken }
   { TPDFToken }
 
 
   TPDFToken = record
   TPDFToken = record
@@ -71,6 +152,7 @@ Type
     Function AsBEHexInteger : Integer;
     Function AsBEHexInteger : Integer;
     Function AsInteger : Integer;
     Function AsInteger : Integer;
     Function AsDouble : Double;
     Function AsDouble : Double;
+    Function AsSingle : Single;
     Function IsHexString : Boolean;
     Function IsHexString : Boolean;
     Function IsString : Boolean;
     Function IsString : Boolean;
     Function AsString : RawByteString;
     Function AsString : RawByteString;
@@ -172,6 +254,7 @@ Type
     function GetDescription : String; override;
     function GetDescription : String; override;
     Function IsKeyword (const aKeyWord : string) : Boolean;
     Function IsKeyword (const aKeyWord : string) : Boolean;
     Function IsInteger : Boolean;
     Function IsInteger : Boolean;
+    Function IsFloat : Boolean;
     Function IsInt64 : Boolean;
     Function IsInt64 : Boolean;
     Property TokenType : TPDFTokentype Read FTokenType;
     Property TokenType : TPDFTokentype Read FTokenType;
     Property Value : RawbyteString Read FValue Write FValue;
     Property Value : RawbyteString Read FValue Write FValue;
@@ -307,8 +390,10 @@ Type
     class function ElementType : TPDFElementType; override;
     class function ElementType : TPDFElementType; override;
     function GetDescription: String; override;
     function GetDescription: String; override;
     Function IsIntegerAt(aIndex : Integer) : Boolean;
     Function IsIntegerAt(aIndex : Integer) : Boolean;
+    Function IsFloatAt(aIndex : Integer) : Boolean;
     Function IsKeywordAt(aIndex : Integer; const aKeyWord: RawByteString) : Boolean;
     Function IsKeywordAt(aIndex : Integer; const aKeyWord: RawByteString) : Boolean;
     Function GetIntegerAt(aIndex : Integer) : Integer;
     Function GetIntegerAt(aIndex : Integer) : Integer;
+    Function GetFloatAt(aIndex : Integer) : Double;
   end;
   end;
 
 
   { TPDFDictEntry }
   { TPDFDictEntry }
@@ -344,6 +429,7 @@ Type
     Function GetValue(const aKeyword : RawByteString) : TPDFObject;
     Function GetValue(const aKeyword : RawByteString) : TPDFObject;
     Function GetStringValue(const aKeyword : RawByteString) : RawByteString;
     Function GetStringValue(const aKeyword : RawByteString) : RawByteString;
     Function GetIntegerValue(const aKeyword : RawByteString) : Integer;
     Function GetIntegerValue(const aKeyword : RawByteString) : Integer;
+    Function GetSingleValue(const aKeyword : RawByteString) : Single;
     Function GetInt64Value(const aKeyword : RawByteString) : Int64;
     Function GetInt64Value(const aKeyword : RawByteString) : Int64;
     Function GetArrayValue(const aKeyword : RawByteString) : TPDFArray;
     Function GetArrayValue(const aKeyword : RawByteString) : TPDFArray;
     Function GetDictionaryValue(const aKeyword : RawByteString) : TPDFDictionary;
     Function GetDictionaryValue(const aKeyword : RawByteString) : TPDFDictionary;
@@ -469,6 +555,7 @@ Type
     function FindResources: TPDFDictionary;
     function FindResources: TPDFDictionary;
     Function FindFontRefObj(const aFontName : String) : TPDFRef;
     Function FindFontRefObj(const aFontName : String) : TPDFRef;
     Function FindFontRef(const aFontName : String) : TPDFRefData;
     Function FindFontRef(const aFontName : String) : TPDFRefData;
+    Function GetMediaBox : TPDFRect;
     Property ParentRef : TPDFRef Read GetParentRef;
     Property ParentRef : TPDFRef Read GetParentRef;
     Property Parent : TPDFIndirect Read GetParent;
     Property Parent : TPDFIndirect Read GetParent;
     Property Contents[aIndex : integer] : TPDFIndirect Read GetContent;
     Property Contents[aIndex : integer] : TPDFIndirect Read GetContent;
@@ -476,6 +563,7 @@ Type
     Property ContentCount : Integer read GetContentCount;
     Property ContentCount : Integer read GetContentCount;
     Property CommandList : TPDFCommandList Read FCommandList;
     Property CommandList : TPDFCommandList Read FCommandList;
     Property Resources : TPDFDictionary Read GetResources;
     Property Resources : TPDFDictionary Read GetResources;
+
   end;
   end;
   TPDFPageClass = Class of TPDFPageObject;
   TPDFPageClass = Class of TPDFPageObject;
 
 
@@ -542,6 +630,8 @@ Type
   end;
   end;
 
 
   { TPDFCommand }
   { TPDFCommand }
+  TPDFCommandType = (cmtTextState,cmtText,cmtGraphicState,cmtColor,cmtPath,cmtStroke,cmtMarkedContent,cmtOther);
+
   TPDFCommand = Class(TPDFTokensObject)
   TPDFCommand = Class(TPDFTokensObject)
   private
   private
     class var _ClassList : TFPDataHashTable;
     class var _ClassList : TFPDataHashTable;
@@ -559,101 +649,17 @@ Type
     Class Procedure Register;
     Class Procedure Register;
     Class Procedure UnRegister;
     Class Procedure UnRegister;
     Constructor Create(const aCommand : String; aTokens : TPDFTokenArray); reintroduce;
     Constructor Create(const aCommand : String; aTokens : TPDFTokenArray); reintroduce;
+    class function CommandType : TPDFCommandType; virtual;
   Public
   Public
     Property Command : String Read FCommand Write FCommand;
     Property Command : String Read FCommand Write FCommand;
   end;
   end;
 
 
-  { TPDFBTCommand }
-
-  TPDFBTCommand = class(TPDFCommand)
-    Class Function RegisterCommandName : String;override;
-  end;
-
-  { TPDFETCommand }
-
-  TPDFETCommand = class(TPDFCommand)
-    Class Function RegisterCommandName : String; override;
-  end;
-
-  // Do not register this one.
-
-  { TPDFTextCommand }
-
-  TPDFTextCommand = Class(TPDFCommand)
-  Public
-    Function GetFullText(aUnicodeMap : TPDFCMap) : RawByteString; virtual; overload;
-    Function GetFullText : RawByteString; virtual; abstract; overload;
-  end;
-
-  { TPDFTJCommand }
-
-  TPDFTJCommand = class(TPDFTextCommand)
-    Class Function RegisterCommandName : String; override;
-    Function GetFullText(aUnicodeMap : TPDFCMap) : RawByteString; override; overload;
-    Function GetFullText : RawByteString; override;
-  end;
-
-  { TPDFTfCommand }
-
-  TPDFTfCommand = class(TPDFCommand)
-  private
-    function GetFontName: String;
-    function GetFontSize: Integer;
-  public
-    Class Function RegisterCommandName : String; override;
-    property FontName : String Read GetFontName;
-    Property FontSize : Integer Read GetFontSize;
-  end;
-
-  { TPDFTj_Command }
-
-  TPDFTj_Command = class(TPDFTextCommand)
-    Class Function RegisterCommandName : String; override;
-    Function GetFullText : RawByteString; override;
-  end;
-
-
-  { TPDFTdCommand }
-
-  TPDFTDCommand = class(TPDFCommand)
-    Class Function RegisterCommandName : String; override;
-  end;
-
-  { TPDFTdCommand }
-
-  { TPDFTd_Command }
-
-  TPDFTd_Command = class(TPDFCommand)
-    Class Function RegisterCommandName : String; override;
-  end;
-
-
-  { TPDFTfCommand }
-
-
-  TPDFImageData = record
-    Width,
-    height,
-    BitsPerComponent : Integer;
-    ColorSpace : String;
-    ColorSpaceComponents : Integer;
-    Filters : Array of String;
-  end;
-
-  { TPDFImageDataCommand }
-
-  TPDFImageDataCommand = Class(TPDFCommand)
-    Class Procedure ParseImageOperands(aOperands : TPDFTokenArray; Out aImageData : TPDFImageData);
-    Class Function RegisterCommandName : String; override;
-  end;
-
 
 
   { TPDFUnknownCommand }
   { TPDFUnknownCommand }
   // Catch all. Do not register
   // Catch all. Do not register
   TPDFUnknownCommand = class(TPDFCommand)
   TPDFUnknownCommand = class(TPDFCommand)
   end;
   end;
 
 
-
   { TPDFCommandEnumerator }
   { TPDFCommandEnumerator }
 
 
   TPDFCommandEnumerator = Class(TPDFContainerObjectEnumerator)
   TPDFCommandEnumerator = Class(TPDFContainerObjectEnumerator)
@@ -890,10 +896,14 @@ Type
     Property XRefs[aIndex : Integer] : TPDFXRef Read getXRef;
     Property XRefs[aIndex : Integer] : TPDFXRef Read getXRef;
   end;
   end;
 
 
+operator *(A : TPDFPoint; B: TPDFTransformation) c : TPDFPoint;
+
 implementation
 implementation
 
 
 Resourcestring
 Resourcestring
   SErrNotAnInteger = 'Token is not an integer';
   SErrNotAnInteger = 'Token is not an integer';
+  SErrNotASingle = 'Token is not a single-sized float';
+  SErrNotADouble = 'Token is not a double-sized float';
   SErrNotAString = 'Token is not a string';
   SErrNotAString = 'Token is not a string';
   SErrNotAName = 'Token is not a name';
   SErrNotAName = 'Token is not a name';
   SErrNotAnInt64 = 'Token is not an int64';
   SErrNotAnInt64 = 'Token is not an int64';
@@ -903,6 +913,151 @@ Resourcestring
   SErrDictValueIsNotDict = 'Dictionary entry %s is not a dictionary';
   SErrDictValueIsNotDict = 'Dictionary entry %s is not a dictionary';
   SErrNoFontAt = 'No font found at: %s';
   SErrNoFontAt = 'No font found at: %s';
 
 
+operator * (A: TPDFPoint; B: TPDFTransformation) c: TPDFPoint;
+begin
+  C.X:=a.X*B.M[0,0]+a.Y*B.M[1,0]+B.M[2,0];
+  C.Y:=a.X*B.M[0,1]+a.Y*B.M[1,1]+B.M[2,1];
+end;
+
+
+{ TPDFPoint }
+
+class function TPDFPoint.Create(aX, aY: Single): TPDFPoint;
+begin
+  Result.X:=aX;
+  Result.Y:=aY;
+end;
+
+class operator TPDFPoint.+(const A, B: TPDFPoint): TPDFPoint;
+begin
+  Result.X:=A.X+B.X;
+  Result.Y:=A.Y+B.Y;
+end;
+
+class operator TPDFPoint.=(const A, B: TPDFPoint): Boolean;
+begin
+  Result:=((A.X-B.X)<PointPrecision)
+          and ((A.Y-B.Y)<PointPrecision)
+end;
+
+function TPDFPoint.AsString: String;
+begin
+  Result:=Format('(%.4g,%.4g)',[X,Y])
+end;
+
+{ TPDFPathSegment }
+
+function TPDFPathSegment.Description: String;
+
+var
+  S : String;
+
+begin
+  Str(SegmentType,S);
+  Result:=Copy(S,3);
+  if SegmentType<>stClose then
+    Result:=Result+P1.AsString+'-'+P2.AsString;
+end;
+
+{ TPDFPath }
+
+procedure TPDFPath.AddSegment(aSegment: TPDFPathSegment);
+begin
+  Writeln('Adding : ',aSegment.Description);
+  if (FCount=Length(FSegments)) then
+    SetLength(FSegments,Length(FSegments)+SegmentDelta);
+
+  FSegments[FCount]:=aSegment;
+  inc(FCount);
+end;
+
+
+function TPDFPath.GetSegments(aIndex: Word): TPDFPathSegment;
+begin
+  if aIndex>=Count then
+    Raise EPDF.CreateFmt('Segment index out of bounds (max: %d)',[aIndex,Count-1]);
+  Result:=FSegments[aIndex];
+end;
+
+
+procedure TPDFPath.Add(aSegment: TPDFPathSegment);
+begin
+  if (aSegment.SegmentType in [stBezierLimits,stBezierControl]) then
+    Raise EPDF.Create('Use AddBezier to add segments of bezier curves');
+  AddSegment(aSegment);
+end;
+
+procedure TPDFPath.AddBezier(aLimits,aControl: TPDFPathSegment);
+begin
+  aLimits.SegmentType:=stBezierLimits;
+  aControl.SegmentType:=stBezierControl;
+  AddSegment(aLimits);
+  AddSegment(aControl);
+end;
+
+procedure TPDFPath.Clear;
+begin
+
+end;
+
+{ TTransFormation }
+
+class function TPDFTransFormation.Default: TPDFTransformation;
+begin
+  Result:=System.Default(TPDFTransformation);
+  Result.M[0,0]:=1;
+  Result.M[1,1]:=1;
+  Result.M[2,2]:=1;
+end;
+
+class function TPDFTransFormation.Create(const a, b, c, d, e, f: Single
+  ): TPDFTransformation;
+begin
+  Result:=System.Default(TPDFTransFormation);
+  Result.M[0,0]:=a;
+  Result.M[0,1]:=b;
+  Result.M[1,0]:=c;
+  Result.M[1,1]:=d;
+  Result.M[2,0]:=e;
+  Result.M[2,1]:=f;
+  Result.M[2,2]:=1;
+end;
+
+class operator TPDFTransFormation.*(const A,B: TPDFTransFormation
+  ): TPDFTransFormation;
+
+var
+  r : TPDFTransFormationLine;
+  i : byte;
+
+begin
+  for i:=0 to 2 do
+    begin
+    r:=a.m[i];
+    result.m[i,0]:=r[0]*b.m[0,0]
+                  +r[1]*b.m[1,0]
+                  +r[2]*b.m[2,0];
+    result.m[i,1]:=r[0]*b.m[0,1]
+                   +r[1]*b.m[1,1]
+                   +r[2]*b.m[2,1];
+    result.m[i,2]:=r[0]*b.m[0,2]
+                   +r[1]*b.m[1,2]
+                   +r[2]*b.m[2,2];
+    end;
+end;
+
+procedure TPDFTransFormation.Translate(const aX, aY: Single);
+begin
+  M[2,0]:=M[2,0]+aX;
+  M[2,1]:=M[2,1]+aY;
+end;
+
+procedure TPDFTransFormation.Translate(const P: TPDFPoint);
+begin
+  M[2,0]:=M[2,0]+P.X;
+  M[2,1]:=M[2,1]+P.Y;
+end;
+
 { TPDFTrailer }
 { TPDFTrailer }
 
 
 
 
@@ -1156,20 +1311,6 @@ begin
   Result:=Data.Interpret(aRaw);
   Result:=Data.Interpret(aRaw);
 end;
 end;
 
 
-{ TPDFTextCommand }
-
-function TPDFTextCommand.GetFullText(aUnicodeMap: TPDFCMap): RawByteString;
-
-Var
-  aRaw : RawByteString;
-
-begin
-  aRaw:=GetFullText();
-  if not Assigned(aUnicodeMap) then
-    Result:=aRaw
-  else
-    Result:=aUnicodeMap.InterPret(aRaw);
-end;
 
 
 { TPDFRefData }
 { TPDFRefData }
 
 
@@ -1279,207 +1420,6 @@ begin
     Result:=aDoc.FindInDirectObject(Ref);
     Result:=aDoc.FindInDirectObject(Ref);
 end;
 end;
 
 
-{ TPDFImageDataCommand }
-
-class procedure TPDFImageDataCommand.ParseImageOperands(
-  aOperands: TPDFTokenArray; out aImageData: TPDFImageData);
-
-Var
-  I,J : Integer;
-
-
-begin
-  aImageData:=Default(TPDFImageData);
-  I:=0;
-  While (I<Length(aOperands)-1) do
-    begin
-    if aOperands[i].IsName then
-      begin
-      Case Copy(aOperands[i].TokenData,2,Length(aOperands[i].TokenData)-1) of
-        SPDFImageKeyW :
-          begin
-          Inc(I);
-          aImageData.Width:=aOperands[i].AsInteger;
-          end;
-        SPDFImageKeyH :
-          begin
-          Inc(I);
-          aImageData.Height:=aOperands[i].AsInteger;
-          end;
-        SPDFImageKeyBPC:
-          begin
-          Inc(I);
-          aImageData.BitsPerComponent:=aOperands[i].AsInteger;
-          end;
-        SPDFImageKeyCS:
-          begin
-          Inc(I);
-          aImageData.ColorSpace:=aOperands[i].TokenData;
-          end;
-        SPDFImageKeyF:
-          begin
-          Inc(i);
-          If aOperands[i].TokenType<>ptSquareOpen then
-            begin
-            Inc(i);
-            aImageData.Filters:=[aOperands[i].TokenData];
-            end
-          else
-            begin
-            Inc(I);
-            J:=I;
-            While (J<Length(aOperands)) and (aOperands[J].TokenType<>ptSquareClose) do
-              Inc(J);
-            SetLength(aImageData.Filters,J);
-            J:=I;
-            While (J<Length(aOperands)) and (aOperands[J].TokenType<>ptSquareClose) do
-              begin
-              aImageData.Filters[J-I]:=aOperands[J].TokenData;
-              Inc(J);
-              end
-            end;
-          end;
-      end;
-      end;
-    inc(I);
-    end;
-  Case Copy(aImageData.ColorSpace,2,Length(aImageData.ColorSpace)-1) of
-    SPDFImageKeyCMYK : aImageData.ColorSpaceComponents:=4;
-    SPDFImageKeyRGB : aImageData.ColorSpaceComponents:=3;
-    SPDFImageKeyG : aImageData.ColorSpaceComponents:=1;
-  end;
-end;
-
-class function TPDFImageDataCommand.RegisterCommandName: String;
-begin
-  Result:='ID';
-end;
-
-{ TPDFTd_Command }
-
-class function TPDFTd_Command.RegisterCommandName: String;
-begin
-  Result:='Td';
-end;
-
-{ TPDFTj_Command }
-
-class function TPDFTj_Command.RegisterCommandName: String;
-begin
-  Result:='Tj';
-end;
-
-function TPDFTj_Command.GetFullText: RawByteString;
-begin
-  Result:='';
-  if Length(Self.Tokens)>0 then
-    try
-      Result:=Tokens[0].AsString;
-    except
-      on E : exception do
-        begin
-        Writeln('Exception ',E.ClassName,'getting text for token: "',E.Message,'". Token data :',GetDescription);
-        Raise;
-        end;
-
-    end;
-end;
-
-{ TPDFTfCommand }
-
-function TPDFTfCommand.GetFontName: String;
-begin
-  Result:='';
-  If (Length(Tokens)>0) then
-    if Tokens[0].IsString then
-      Result:=Tokens[0].AsString
-    else if Tokens[0].IsName then
-      Result:=Tokens[0].AsName;
-end;
-
-function TPDFTfCommand.GetFontSize: Integer;
-
-begin
-  Result:=0;
-  If (Length(Tokens)>1) and Tokens[1].IsInteger then
-    Result:=Tokens[1].AsInteger
-end;
-
-class function TPDFTfCommand.RegisterCommandName: String;
-begin
-  Result:='Tf';
-end;
-
-{ TPDFTdCommand }
-
-class function TPDFTdCommand.RegisterCommandName: String;
-begin
-  Result:='TD';
-end;
-
-{ TPDFTJCommand }
-
-class function TPDFTJCommand.RegisterCommandName: String;
-begin
-  Result:='TJ';
-end;
-
-function TPDFTJCommand.GetFullText(aUnicodeMap: TPDFCMap): RawByteString;
-Var
-  i : integer;
-
-begin
-  if aUnicodeMap=Nil then
-    Exit('');
-  Result:='';
-  if Length(Tokens)>=2 then
-    For I:=1 to Length(Tokens)-2 do
-      case Tokens[I].TokenType of
-      ptString,ptHexString:
-        Result:=Result+aUnicodeMap.InterPret(Tokens[I].TokenData);
-      ptNumber:
-        if Abs(Tokens[i].AsDouble)>PDFTextArraySpaceTreshold then
-          Result:=Result+' ';
-      else
-        Raise EConvertError.Create('Unexpected char');
-      end;
-end;
-
-function TPDFTJCommand.GetFullText: RawByteString;
-
-Var
-  i : integer;
-
-begin
-  Result:='';
-  if Length(Tokens)>=2 then
-    For I:=1 to Length(Tokens)-2 do
-      begin
-      if Tokens[I].TokenType=ptString then
-        Result:=Result+Tokens[I].TokenData
-      else if Tokens[i].IsNumber then
-        begin
-        if Abs(Tokens[i].AsDouble)>PDFTextArraySpaceTreshold then
-          Result:=Result+' '
-        end
-      else
-        Raise EConvertError.Create('Unexpected char');
-      end;
-end;
-
-{ TPDFETCommand }
-
-class function TPDFETCommand.RegisterCommandName: String;
-begin
-  Result:='ET';
-end;
-
-{ TPDFBTCommand }
-
-class function TPDFBTCommand.RegisterCommandName: String;
-begin
-  Result:='BT';
-end;
 
 
 { TPDFCommand }
 { TPDFCommand }
 
 
@@ -1526,6 +1466,7 @@ Var
   S : String;
   S : String;
 
 
 begin
 begin
+  Writeln('Registering ',classname,' for command ',RegisterCommandName,' and type ',CommandType);
   S:=RegisterCommandName;
   S:=RegisterCommandName;
   If S<>'' then
   If S<>'' then
     RegisterCommand(S,Self);
     RegisterCommand(S,Self);
@@ -1547,6 +1488,12 @@ begin
   FCommand:=aCommand;
   FCommand:=aCommand;
 end;
 end;
 
 
+class function TPDFCommand.CommandType: TPDFCommandType;
+begin
+  Result:=cmtOther;
+end;
+
+
 
 
 { TPDFCommandList }
 { TPDFCommandList }
 
 
@@ -1815,6 +1762,17 @@ begin
     Result:=aRef.FRef;
     Result:=aRef.FRef;
 end;
 end;
 
 
+function TPDFPageObject.GetMediaBox: TPDFRect;
+
+Var
+  Arr : TPDFArray;
+
+begin
+  Arr:=ObjectDict.GetArrayValue(SPDFPageKeyMediaBox);
+  Result.ll:=TPDFPoint.Create(Arr.GetFloatAt(0),Arr.GetFloatAt(1));
+  Result.ur:=TPDFPoint.Create(Arr.GetFloatAt(2),Arr.GetFloatAt(3));
+end;
+
 class function TPDFPageObject.CreateCommandList: TPDFCommandList;
 class function TPDFPageObject.CreateCommandList: TPDFCommandList;
 begin
 begin
   Result:=TPDFCommandList.Create;
   Result:=TPDFCommandList.Create;
@@ -2075,14 +2033,28 @@ var
 
 
 begin
 begin
   if not (TokenType=ptNumber) then
   if not (TokenType=ptNumber) then
-    Raise EConvertError.Create(SErrNotAnInteger)
+    Raise EConvertError.Create(SErrNotADouble)
   else
   else
     begin
     begin
     Val(TokenData,Result,C);
     Val(TokenData,Result,C);
     if C<>0 then
     if C<>0 then
-      Raise EConvertError.Create(SErrNotAnInteger)
+      Raise EConvertError.Create(SErrNotADouble)
     end;
     end;
+end;
 
 
+function TPDFToken.AsSingle: Single;
+var
+  c : Integer;
+
+begin
+  if not (TokenType=ptNumber) then
+    Raise EConvertError.Create(SErrNotASingle)
+  else
+    begin
+    Val(TokenData,Result,C);
+    if C<>0 then
+      Raise EConvertError.Create(SErrNotASingle)
+    end;
 end;
 end;
 
 
 function TPDFToken.IsHexString: Boolean;
 function TPDFToken.IsHexString: Boolean;
@@ -2497,6 +2469,12 @@ begin
   Result:=(Objects[aIndex] is TPDFValue) and  (TPDFValue(Objects[aIndex]).IsInteger);
   Result:=(Objects[aIndex] is TPDFValue) and  (TPDFValue(Objects[aIndex]).IsInteger);
 end;
 end;
 
 
+function TPDFArray.IsFloatAt(aIndex: Integer): Boolean;
+begin
+  Result:=(Objects[aIndex] is TPDFValue)
+          and ((TPDFValue(Objects[aIndex]).IsFloat) or (TPDFValue(Objects[aIndex]).IsInteger));
+end;
+
 function TPDFArray.IsKeywordAt(aIndex: Integer; const aKeyWord: RawByteString
 function TPDFArray.IsKeywordAt(aIndex: Integer; const aKeyWord: RawByteString
   ): Boolean;
   ): Boolean;
 begin
 begin
@@ -2511,6 +2489,19 @@ begin
     Raise EConvertError.Create('Array element %d is not an integer value');
     Raise EConvertError.Create('Array element %d is not an integer value');
 end;
 end;
 
 
+function TPDFArray.GetFloatAt(aIndex: Integer): Double;
+begin
+  If (Objects[aIndex] is TPDFValue) then
+    begin
+    if (TPDFValue(Objects[aIndex]).IsInteger) then
+      Result:=TPDFValue(Objects[aIndex]).AsInteger
+    else if (TPDFValue(Objects[aIndex]).IsFloat) then
+      Result:=TPDFValue(Objects[aIndex]).AsFloat;
+    end
+  else
+    Raise EConvertError.Create('Array element %d is not an integer value');
+end;
+
 { TPDFStream }
 { TPDFStream }
 
 
 constructor TPDFStream.Create(const aData: TBytes);
 constructor TPDFStream.Create(const aData: TBytes);
@@ -2619,6 +2610,17 @@ begin
   Result:=TryStrToInt(Value,I);
   Result:=TryStrToInt(Value,I);
 end;
 end;
 
 
+function TPDFValue.IsFloat: Boolean;
+Var
+  d : double;
+  c : Integer;
+
+begin
+  Val(Value,d,c);
+  D:=D*0;
+  Result:=(C=0);
+end;
+
 function TPDFValue.IsInt64: Boolean;
 function TPDFValue.IsInt64: Boolean;
 Var
 Var
   I : Int64;
   I : Int64;
@@ -2787,6 +2789,19 @@ begin
   Result:=aVal.AsInteger;
   Result:=aVal.AsInteger;
 end;
 end;
 
 
+function TPDFDictionary.GetSingleValue(const aKeyword: RawByteString): Single;
+Var
+  aVal : TPDFValue;
+
+begin
+  aVal:=FindValue(aKeyWord) as TPDFValue;
+  if (aVal=Nil) then
+    Raise EPDF.CreateFmt(SErrNoSuchDictValue,[aKeyWord]);
+  if not aVal.IsInteger then
+    Raise EPDF.CreateFmt(SErrDictValueIsNotInteger,[aKeyWord]);
+  Result:=aVal.AsInteger;
+end;
+
 function TPDFDictionary.GetArrayValue(const aKeyword: RawByteString): TPDFArray;
 function TPDFDictionary.GetArrayValue(const aKeyword: RawByteString): TPDFArray;
 
 
 begin
 begin
@@ -3266,17 +3281,6 @@ begin
   TPDFFontObject.Register;
   TPDFFontObject.Register;
 end;
 end;
 
 
-Procedure RegisterStandardCommands;
-
-begin
-  TPDFBTCommand.Register;
-  TPDFETCommand.Register;
-  TPDFTJCommand.Register;
-  TPDFTj_Command.Register;
-  TPDFTfCommand.Register;
-  TPDFTd_Command.Register;
-  TPDFTDCommand.Register;
-end;
 
 
 {$IFDEF DEBUGPDFALLOCATION}
 {$IFDEF DEBUGPDFALLOCATION}
 Procedure DumpAllocations;
 Procedure DumpAllocations;
@@ -3296,7 +3300,6 @@ end;
 
 
 initialization
 initialization
   RegisterStandardClasses;
   RegisterStandardClasses;
-  RegisterStandardCommands;
 
 
 finalization
 finalization
 {$IFDEF DEBUGPDFALLOCATION}
 {$IFDEF DEBUGPDFALLOCATION}

+ 1 - 1
packages/fcl-pdf/src/fppdfparser.pp

@@ -25,7 +25,7 @@ unit fppdfparser;
 interface
 interface
 
 
 uses
 uses
-  Types, Typinfo, Classes, SysUtils, fppdfobjects, fppdfscanner, fppdfsource, streamex, fppdfpredict;
+  Types, Typinfo, Classes, SysUtils, fppdfobjects, fppdfscanner, fppdfsource, streamex, fppdfpredict, fppdfcommands;
 
 
 Const
 Const
   PDFMaxTrailerDistance = 6;  // Maximum number of bytes to scan backwards for trailer dictionary end: >>
   PDFMaxTrailerDistance = 6;  // Maximum number of bytes to scan backwards for trailer dictionary end: >>

+ 2 - 1
packages/fcl-pdf/src/fppdfscanner.pp

@@ -589,7 +589,7 @@ begin
   Result:='';
   Result:='';
   aValue:=Char(aStartByte)+GetTillByte(Ord('>'));
   aValue:=Char(aStartByte)+GetTillByte(Ord('>'));
   lRawlen:=Length(aValue) div 2;
   lRawlen:=Length(aValue) div 2;
-  SetLength(Result{%H-},lRawLen);
+  SetLength(Result,lRawLen);
   lRes:=HexToBin(PChar(aValue),PChar(Result),lRawLen);
   lRes:=HexToBin(PChar(aValue),PChar(Result),lRawLen);
   if lRes=-1 then
   if lRes=-1 then
     DoError(senInvalidHexString,SErrInvalidHexString,[aValue]);
     DoError(senInvalidHexString,SErrInvalidHexString,[aValue]);
@@ -612,6 +612,7 @@ Var
   CharPos : Integer;
   CharPos : Integer;
   lOpenCount : Integer;
   lOpenCount : Integer;
   aByte,aByte2 : Byte;
   aByte,aByte2 : Byte;
+  aChar : Char absolute aByte;
   aChar2 : Char absolute aByte2;
   aChar2 : Char absolute aByte2;
   aChar3,aChar4 : Char;
   aChar3,aChar4 : Char;
   aOctal : integer;
   aOctal : integer;