mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-09-27 11:52:51 +02:00
* Fix bug #29989, add compression and JPEG image support (by Ondrej Pokorny)
git-svn-id: trunk@33484 -
This commit is contained in:
parent
b46969cfa8
commit
591f3dc879
@ -28,7 +28,7 @@ uses
|
|||||||
SysUtils,
|
SysUtils,
|
||||||
StrUtils,
|
StrUtils,
|
||||||
contnrs,
|
contnrs,
|
||||||
fpImage,
|
fpImage, FPReadJPEG,
|
||||||
zstream,
|
zstream,
|
||||||
fpparsettf;
|
fpparsettf;
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ type
|
|||||||
TPDFPageLayout = (lSingle, lTwo, lContinuous);
|
TPDFPageLayout = (lSingle, lTwo, lContinuous);
|
||||||
TPDFUnitOfMeasure = (uomInches, uomMillimeters, uomCentimeters, uomPixels);
|
TPDFUnitOfMeasure = (uomInches, uomMillimeters, uomCentimeters, uomPixels);
|
||||||
|
|
||||||
TPDFOption = (poOutLine, poCompressText, poCompressFonts);
|
TPDFOption = (poOutLine, poCompressText, poCompressFonts, poCompressImages, poUseRawJPEG);
|
||||||
TPDFOptions = set of TPDFOption;
|
TPDFOptions = set of TPDFOption;
|
||||||
|
|
||||||
EPDF = Class(Exception);
|
EPDF = Class(Exception);
|
||||||
@ -667,12 +667,14 @@ type
|
|||||||
Property Pages[AIndex : Integer] : TPDFPage Read GetP; Default;
|
Property Pages[AIndex : Integer] : TPDFPage Read GetP; Default;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
TPDFImageCompression = (icNone, icDeflate, icJPEG);
|
||||||
|
|
||||||
TPDFImageItem = Class(TCollectionItem)
|
TPDFImageItem = Class(TCollectionItem)
|
||||||
private
|
private
|
||||||
FImage: TFPCustomImage;
|
FImage: TFPCustomImage;
|
||||||
FOwnsImage: Boolean;
|
FOwnsImage: Boolean;
|
||||||
FStreamed: TBytes;
|
FStreamed: TBytes;
|
||||||
|
FCompression: TPDFImageCompression;
|
||||||
FWidth,FHeight : Integer;
|
FWidth,FHeight : Integer;
|
||||||
function GetHeight: Integer;
|
function GetHeight: Integer;
|
||||||
function GetStreamed: TBytes;
|
function GetStreamed: TBytes;
|
||||||
@ -681,7 +683,7 @@ type
|
|||||||
procedure SetStreamed(AValue: TBytes);
|
procedure SetStreamed(AValue: TBytes);
|
||||||
Public
|
Public
|
||||||
Destructor Destroy; override;
|
Destructor Destroy; override;
|
||||||
Procedure CreateStreamedData;
|
Procedure CreateStreamedData(AUseCompression: Boolean);
|
||||||
Function WriteImageStream(AStream: TStream): int64; virtual;
|
Function WriteImageStream(AStream: TStream): int64; virtual;
|
||||||
function Equals(AImage: TFPCustomImage): boolean; reintroduce;
|
function Equals(AImage: TFPCustomImage): boolean; reintroduce;
|
||||||
Property Image : TFPCustomImage Read FImage Write SetImage;
|
Property Image : TFPCustomImage Read FImage Write SetImage;
|
||||||
@ -692,13 +694,23 @@ type
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
TPDFImages = CLass(TCollection)
|
{ TPDFImages }
|
||||||
private
|
|
||||||
|
TPDFImages = Class(TCollection)
|
||||||
|
Private
|
||||||
|
FOwner: TPDFDocument;
|
||||||
function GetI(AIndex : Integer): TPDFImageItem;
|
function GetI(AIndex : Integer): TPDFImageItem;
|
||||||
|
Protected
|
||||||
|
function GetOwner: TPersistent; override;
|
||||||
Public
|
Public
|
||||||
|
Constructor Create(AOwner: TPDFDocument; AItemClass : TCollectionItemClass);
|
||||||
Function AddImageItem : TPDFImageItem;
|
Function AddImageItem : TPDFImageItem;
|
||||||
|
Function AddJPEGStream(Const AStream : TStream; Width,Height : Integer): Integer;
|
||||||
|
Function AddFromStream(Const AStream : TStream; Handler : TFPCustomImageReaderClass;
|
||||||
|
KeepImage : Boolean = False): Integer;
|
||||||
Function AddFromFile(Const AFileName : String; KeepImage : Boolean = False): Integer;
|
Function AddFromFile(Const AFileName : String; KeepImage : Boolean = False): Integer;
|
||||||
Property Images[AIndex : Integer] : TPDFImageItem Read GetI; default;
|
Property Images[AIndex : Integer] : TPDFImageItem Read GetI; default;
|
||||||
|
Property Owner: TPDFDocument read FOwner;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
@ -734,6 +746,8 @@ type
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
{ TPDFDocument }
|
||||||
|
|
||||||
TPDFDocument = class(TComponent)
|
TPDFDocument = class(TComponent)
|
||||||
private
|
private
|
||||||
FCatalogue: integer;
|
FCatalogue: integer;
|
||||||
@ -763,6 +777,13 @@ type
|
|||||||
procedure SetInfos(AValue: TPDFInfos);
|
procedure SetInfos(AValue: TPDFInfos);
|
||||||
procedure SetLineStyles(AValue: TPDFLineStyleDefs);
|
procedure SetLineStyles(AValue: TPDFLineStyleDefs);
|
||||||
protected
|
protected
|
||||||
|
// Create all kinds of things, virtual so they can be overridden to create descendents instead
|
||||||
|
function CreatePDFPages: TPDFPages; virtual;
|
||||||
|
function CreateLineStyles: TPDFLineStyleDefs; virtual;
|
||||||
|
function CreateFontDefs: TPDFFontDefs; virtual;
|
||||||
|
function CreatePDFImages: TPDFImages; virtual;
|
||||||
|
function CreatePDFInfos: TPDFInfos; virtual;
|
||||||
|
function CreateSectionList: TPDFSectionList; virtual;
|
||||||
// Returns next prevoutline
|
// Returns next prevoutline
|
||||||
function CreateSectionOutLine(Const SectionIndex,OutLineRoot,ParentOutLine,NextSect,PrevSect : Integer): Integer; virtual;
|
function CreateSectionOutLine(Const SectionIndex,OutLineRoot,ParentOutLine,NextSect,PrevSect : Integer): Integer; virtual;
|
||||||
Function CreateSectionsOutLine : Integer; virtual;
|
Function CreateSectionsOutLine : Integer; virtual;
|
||||||
@ -1954,7 +1975,12 @@ end;
|
|||||||
function TPDFImageItem.GetStreamed: TBytes;
|
function TPDFImageItem.GetStreamed: TBytes;
|
||||||
begin
|
begin
|
||||||
if Length(FStreamed)=0 then
|
if Length(FStreamed)=0 then
|
||||||
CreateStreamedData;
|
begin
|
||||||
|
if Collection.Owner is TPDFDocument then
|
||||||
|
CreateStreamedData(poCompressImages in TPDFDocument(Collection.Owner).Options)
|
||||||
|
else
|
||||||
|
CreateStreamedData(True);
|
||||||
|
end;
|
||||||
Result:=FStreamed;
|
Result:=FStreamed;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -1988,24 +2014,52 @@ begin
|
|||||||
inherited Destroy;
|
inherited Destroy;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TPDFImageItem.CreateStreamedData;
|
procedure TPDFImageItem.CreateStreamedData(AUseCompression: Boolean);
|
||||||
|
|
||||||
Var
|
Var
|
||||||
I,X,Y : Integer;
|
X,Y : Integer;
|
||||||
C : TFPColor;
|
C : TFPColor;
|
||||||
|
MS : TMemoryStream;
|
||||||
|
Str : TStream;
|
||||||
|
CWhite : TFPColor; // white color
|
||||||
begin
|
begin
|
||||||
|
FillChar(CWhite, SizeOf(CWhite), $FF);
|
||||||
FWidth:=Image.Width;
|
FWidth:=Image.Width;
|
||||||
FHeight:=Image.Height;
|
FHeight:=Image.Height;
|
||||||
SetLength(FStreamed,FWidth*FHeight*3);
|
Str := nil;
|
||||||
I:=0;
|
MS := TMemoryStream.Create;
|
||||||
|
try
|
||||||
|
if AUseCompression then
|
||||||
|
begin
|
||||||
|
FCompression := icDeflate;
|
||||||
|
Str := Tcompressionstream.create(cldefault, MS)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
FCompression := icNone;
|
||||||
|
Str := MS;
|
||||||
|
end;
|
||||||
for Y:=0 to FHeight-1 do
|
for Y:=0 to FHeight-1 do
|
||||||
for X:=0 to FWidth-1 do
|
for X:=0 to FWidth-1 do
|
||||||
begin
|
begin
|
||||||
C:=Image.Colors[x,y];
|
C:=Image.Colors[x,y];
|
||||||
FStreamed[I]:=C.Red shr 8;
|
if C.alpha < $FFFF then // remove alpha channel - assume white background
|
||||||
FStreamed[I+1]:=C.Green shr 8;
|
C := AlphaBlend(CWhite, C);
|
||||||
FStreamed[I+2]:=C.blue shr 8;
|
|
||||||
Inc(I,3);
|
Str.WriteByte(C.Red shr 8);
|
||||||
|
Str.WriteByte(C.Green shr 8);
|
||||||
|
Str.WriteByte(C.blue shr 8);
|
||||||
|
end;
|
||||||
|
if Str<>MS then
|
||||||
|
Str.Free;
|
||||||
|
Str := nil;
|
||||||
|
SetLength(FStreamed, MS.Size);
|
||||||
|
MS.Position := 0;
|
||||||
|
if MS.Size>0 then
|
||||||
|
MS.ReadBuffer(FStreamed[0], MS.Size);
|
||||||
|
finally
|
||||||
|
Str.Free;
|
||||||
|
MS.Free;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -2046,28 +2100,138 @@ begin
|
|||||||
Result:=Items[AIndex] as TPDFImageItem;
|
Result:=Items[AIndex] as TPDFImageItem;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TPDFImages.GetOwner: TPersistent;
|
||||||
|
begin
|
||||||
|
Result := FOwner;
|
||||||
|
end;
|
||||||
|
|
||||||
function TPDFImages.AddImageItem: TPDFImageItem;
|
function TPDFImages.AddImageItem: TPDFImageItem;
|
||||||
begin
|
begin
|
||||||
Result:=Add as TPDFImageItem;
|
Result:=Add as TPDFImageItem;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TPDFImages.AddJPEGStream(const AStream: TStream; Width, Height: Integer
|
||||||
|
): Integer;
|
||||||
|
Var
|
||||||
|
IP : TPDFImageItem;
|
||||||
|
|
||||||
|
begin
|
||||||
|
IP:=AddImageItem;
|
||||||
|
IP.FWidth := Width;
|
||||||
|
IP.FHeight := Height;
|
||||||
|
IP.FCompression := icJPEG;
|
||||||
|
SetLength(IP.FStreamed, AStream.Size-AStream.Position);
|
||||||
|
if Length(IP.FStreamed)>0 then
|
||||||
|
AStream.ReadBuffer(IP.FStreamed[0], Length(IP.FStreamed));
|
||||||
|
Result:=Count-1;
|
||||||
|
end;
|
||||||
|
|
||||||
|
constructor TPDFImages.Create(AOwner: TPDFDocument;
|
||||||
|
AItemClass: TCollectionItemClass);
|
||||||
|
begin
|
||||||
|
inherited Create(AItemClass);
|
||||||
|
FOwner := AOwner;
|
||||||
|
end;
|
||||||
|
|
||||||
function TPDFImages.AddFromFile(const AFileName: String; KeepImage: Boolean): Integer;
|
function TPDFImages.AddFromFile(const AFileName: String; KeepImage: Boolean): Integer;
|
||||||
|
|
||||||
|
{$IF NOT (FPC_FULLVERSION >= 30101)}
|
||||||
|
function FindReaderFromExtension(extension: String): TFPCustomImageReaderClass;
|
||||||
|
var s : string;
|
||||||
|
r : integer;
|
||||||
|
begin
|
||||||
|
extension := lowercase (extension);
|
||||||
|
if (extension <> '') and (extension[1] = '.') then
|
||||||
|
system.delete (extension,1,1);
|
||||||
|
with ImageHandlers do
|
||||||
|
begin
|
||||||
|
r := count-1;
|
||||||
|
s := extension + ';';
|
||||||
|
while (r >= 0) do
|
||||||
|
begin
|
||||||
|
Result := ImageReader[TypeNames[r]];
|
||||||
|
if (pos(s,Extensions[TypeNames[r]]+';') <> 0) then
|
||||||
|
Exit;
|
||||||
|
dec (r);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
Result := nil;
|
||||||
|
end;
|
||||||
|
function FindReaderFromFileName(const filename: String
|
||||||
|
): TFPCustomImageReaderClass;
|
||||||
|
begin
|
||||||
|
Result := FindReaderFromExtension(ExtractFileExt(filename));
|
||||||
|
end;
|
||||||
|
{$ENDIF}
|
||||||
|
|
||||||
|
var
|
||||||
|
FS: TFileStream;
|
||||||
|
|
||||||
|
begin
|
||||||
|
FS := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyNone);
|
||||||
|
try
|
||||||
|
Result := AddFromStream(FS,
|
||||||
|
{$IF (FPC_FULLVERSION >= 30101)}TFPCustomImage.{$ENDIF}FindReaderFromFileName(AFileName), KeepImage);
|
||||||
|
finally
|
||||||
|
FS.Free;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TPDFImages.AddFromStream(const AStream: TStream;
|
||||||
|
Handler: TFPCustomImageReaderClass; KeepImage: Boolean): Integer;
|
||||||
|
|
||||||
Var
|
Var
|
||||||
I : TFPMemoryImage;
|
I : TFPMemoryImage;
|
||||||
IP : TPDFImageItem;
|
IP : TPDFImageItem;
|
||||||
|
JPEG : TFPReaderJPEG;
|
||||||
|
Reader: TFPCustomImageReader;
|
||||||
|
{$IF (FPC_FULLVERSION >= 30101)}
|
||||||
|
Size : TPoint;
|
||||||
|
{$ELSE}
|
||||||
|
startPos: Int64;
|
||||||
|
{$ENDIF}
|
||||||
|
|
||||||
begin
|
begin
|
||||||
|
if (poUseRawJPEG in Owner.Options) and Handler.InheritsFrom(TFPReaderJPEG) then
|
||||||
|
begin
|
||||||
|
JPEG := TFPReaderJPEG.Create;
|
||||||
|
try
|
||||||
|
{$IF (FPC_FULLVERSION >= 30101)}
|
||||||
|
Size := JPEG.ImageSize(AStream);
|
||||||
|
Result := AddJPEGStream(AStream, Size.X, Size.Y);
|
||||||
|
{$ELSE}
|
||||||
I:=TFPMemoryImage.Create(0,0);
|
I:=TFPMemoryImage.Create(0,0);
|
||||||
I.LoadFromFile(AFileName);
|
try
|
||||||
|
startPos := AStream.Position;
|
||||||
|
I.LoadFromStream(AStream, JPEG);
|
||||||
|
AStream.Position := startPos;
|
||||||
|
Result := AddJPEGStream(AStream, I.Width, I.Height);
|
||||||
|
finally
|
||||||
|
I.Free;
|
||||||
|
end;
|
||||||
|
{$ENDIF}
|
||||||
|
finally
|
||||||
|
JPEG.Free;
|
||||||
|
end;
|
||||||
|
end else
|
||||||
|
begin
|
||||||
IP:=AddImageItem;
|
IP:=AddImageItem;
|
||||||
|
I:=TFPMemoryImage.Create(0,0);
|
||||||
|
Reader := Handler.Create;
|
||||||
|
try
|
||||||
|
I.LoadFromStream(AStream, Reader);
|
||||||
|
finally
|
||||||
|
Reader.Free;
|
||||||
|
end;
|
||||||
IP.Image:=I;
|
IP.Image:=I;
|
||||||
if Not KeepImage then
|
if Not KeepImage then
|
||||||
begin
|
begin
|
||||||
IP.CreateStreamedData;
|
Writeln('Compressing : ',poCompressImages in Owner.Options);
|
||||||
|
IP.CreateStreamedData(poCompressImages in Owner.Options);
|
||||||
IP.FImage:=Nil; // not through property, that would clear the image
|
IP.FImage:=Nil; // not through property, that would clear the image
|
||||||
i.Free;
|
i.Free;
|
||||||
end;
|
end;
|
||||||
|
end;
|
||||||
Result:=Count-1;
|
Result:=Count-1;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -2689,6 +2853,10 @@ begin
|
|||||||
D:=Document.GlobalXRefs[AObject].Dict;
|
D:=Document.GlobalXRefs[AObject].Dict;
|
||||||
D.AddInteger('Length',ISize);
|
D.AddInteger('Length',ISize);
|
||||||
LastElement.Write(AStream);
|
LastElement.Write(AStream);
|
||||||
|
case Document.Images[NumImg].FCompression of
|
||||||
|
icJPEG: WriteString('/Filter /DCTDecode'+CRLF, AStream);
|
||||||
|
icDeflate: WriteString('/Filter /FlateDecode'+CRLF, AStream);
|
||||||
|
end;
|
||||||
WriteString('>>', AStream);
|
WriteString('>>', AStream);
|
||||||
// write image stream in xobject dictionary
|
// write image stream in xobject dictionary
|
||||||
Document.Images[NumImg].WriteImageStream(AStream);
|
Document.Images[NumImg].WriteImageStream(AStream);
|
||||||
@ -3351,22 +3519,58 @@ begin
|
|||||||
Raise EPDF.CreateFmt(SErrNoGlobalDict,[AName]);
|
Raise EPDF.CreateFmt(SErrNoGlobalDict,[AName]);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
Function TPDFDocument.CreateLineStyles : TPDFLineStyleDefs;
|
||||||
|
|
||||||
|
begin
|
||||||
|
Result:=TPDFLineStyleDefs.Create(TPDFLineStyleDef);
|
||||||
|
end;
|
||||||
|
|
||||||
|
Function TPDFDocument.CreateSectionList : TPDFSectionList;
|
||||||
|
|
||||||
|
begin
|
||||||
|
Result:=TPDFSectionList.Create(TPDFSection)
|
||||||
|
end;
|
||||||
|
|
||||||
|
Function TPDFDocument.CreateFontDefs : TPDFFontDefs;
|
||||||
|
|
||||||
|
begin
|
||||||
|
TPDFFontDefs.Create(TPDFFont);
|
||||||
|
end;
|
||||||
|
|
||||||
|
Function TPDFDocument.CreatePDFInfos : TPDFInfos;
|
||||||
|
|
||||||
|
begin
|
||||||
|
Result:=TPDFInfos.Create;
|
||||||
|
end;
|
||||||
|
|
||||||
|
Function TPDFDocument.CreatePDFImages : TPDFImages;
|
||||||
|
|
||||||
|
begin
|
||||||
|
Result:=TPDFImages.Create(Self,TPDFImageItem);
|
||||||
|
end;
|
||||||
|
|
||||||
|
Function TPDFDocument.CreatePDFPages : TPDFPages;
|
||||||
|
|
||||||
|
begin
|
||||||
|
Result:=TPDFPages.Create(Self);
|
||||||
|
end;
|
||||||
|
|
||||||
constructor TPDFDocument.Create(AOwner : TComponent);
|
constructor TPDFDocument.Create(AOwner : TComponent);
|
||||||
begin
|
begin
|
||||||
inherited Create(AOwner);
|
inherited Create(AOwner);
|
||||||
FLineStyleDefs:=TPDFLineStyleDefs.Create(TPDFLineStyleDef);
|
|
||||||
FSections:=TPDFSectionList.Create(TPDFSection);
|
|
||||||
FFontFiles:=TStringList.Create;
|
FFontFiles:=TStringList.Create;
|
||||||
FFonts:=TPDFFontDefs.Create(TPDFFont);
|
FLineStyleDefs:=CreateLineStyles;
|
||||||
FInfos:=TPDFInfos.Create;
|
FSections:=CreateSectionList;
|
||||||
FImages:=TPDFImages.Create(TPDFImageItem);
|
FFonts:=CreateFontDefs;
|
||||||
FPages:=TPDFPages.Create(Self);
|
FInfos:=CreatePDFInfos;
|
||||||
|
FImages:=CreatePDFImages;
|
||||||
|
FPages:=CreatePDFPages;
|
||||||
FPreferences:=True;
|
FPreferences:=True;
|
||||||
FPageLayout:=lSingle;
|
FPageLayout:=lSingle;
|
||||||
FDefaultPaperType:=ptA4;
|
FDefaultPaperType:=ptA4;
|
||||||
FDefaultOrientation:=ppoPortrait;
|
FDefaultOrientation:=ppoPortrait;
|
||||||
FZoomValue:='100';
|
FZoomValue:='100';
|
||||||
FOptions := [poCompressFonts];
|
FOptions := [poCompressFonts, poCompressImages];
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TPDFDocument.StartDocument;
|
procedure TPDFDocument.StartDocument;
|
||||||
|
Loading…
Reference in New Issue
Block a user