PowerPDF: use fcl-image for image processing.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@7602 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
jesusr 2020-08-14 13:39:02 +00:00
parent 8dd89d1af9
commit 123e934a4f
6 changed files with 341 additions and 137 deletions

View File

@ -0,0 +1,175 @@
unit PdfImageLazTools;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils,
FPImage,
FPReadBMP, FPReadPNG, FPReadJPEG, // make sure we have the basic readers
Graphics, PDFTypes;
function CreateAlphaImage(const aWidth, aHeight: Integer): TFPMemoryImage;
procedure ConvertGraphicToFPImage(AImage: TGraphic; out fpImg: TFPCustomImage);
function CreateMaskStream(AImage: TFPCustomImage): TPDfImage;
function CreateIndexedColorArray(ABitmap: TFPCustomImage): TPdfArray;
implementation
type
{ TRasterImageHelper }
TRasterImageHelper = class helper for TRasterImage
public
function RequestRawStream(out rawStream: TMemoryStream): boolean;
end;
function CreateAlphaImage(const aWidth, aHeight: Integer): TFPMemoryImage;
var
aColor: TFPColor;
x: Integer;
begin
result := TFPMemoryImage.Create(aWidth, aHeight);
result.UsePalette := true;
result.Palette.Count := 256;
for x:=0 to $FF do
begin
aColor.Red:=x;
aColor.Red:=(aColor.Red shl 8) + aColor.Red;
aColor.Green:=aColor.Red;
aColor.Blue:=aColor.Red;
result.Palette.Color[x]:=aColor;
end;
end;
procedure ConvertGraphicToFPImage(AImage: TGraphic; out fpImg: TFPCustomImage);
var
rawImgStream: TMemoryStream = nil;
useOriginalStream: boolean = false;
begin
if (AImage is TRasterImage) then
useOriginalStream := TRasterImage(AImage).RequestRawStream(rawImgStream);
if not useOriginalStream then begin
rawImgStream := TMemoryStream.Create;
AImage.SaveToStream(rawImgStream);
rawImgStream.Position := 0;
end;
try
fpImg := TFPMemoryImage.Create(0, 0);
fpImg.UsePalette := false;
try
fpImg.LoadFromStream(rawImgStream);
except
fpImg.Free;
fpImg := nil;
raise
end;
finally
if not useOriginalStream then
rawImgStream.Free;
end;
end;
function CreateMaskStream(AImage: TFPCustomImage): TPDfImage;
var
pb: PByteArray;
y: Integer;
x: Integer;
begin
result := TPdfImage.CreateStream(nil);
with result do
try
with Attributes do
begin
AddItem('Type', TPdfName.CreateName('XObject'));
AddItem('Subtype', TPdfName.CreateName('Image'));
AddItem('Width', TPdfNumber.CreateNumber(aImage.Width));
AddItem('Height', TPdfNumber.CreateNumber(aImage.Height));
AddItem('BitsPerComponent', TPdfNumber.CreateNumber(8));
AddItem('ColorSpace',TPdfName.CreateName('DeviceGray'));
if USE_ZLIB then
PdfArrayByName('Filter').AddItem(TPdfName.CreateName('FlateDecode'));
new(pb);
for y := 0 to AImage.Height - 1 do
begin
for x := 0 to AImage.Width-1 do
pb^[x] := AImage.Pixels[x,y];
Stream.Write(pb^, AImage.Width);
end;
dispose(pb);
end;
finally
end;
end;
function CreateIndexedColorArray(ABitmap: TFPCustomImage): TPdfArray;
var
i: integer;
ColorTable: TPdfBinary;
NumOfColors: integer;
S: string;
procedure AddColor(Red,Green,Blue:byte);
begin
S := S + IntToHex(Red, 2) +
IntToHex(Green, 2) +
IntToHex(Blue, 2) +
' ';
end;
begin
// creating color table from palette of bitmap.
if (not ABitmap.UsePalette) then
raise EPdfInvalidImageFormat.Create('Expected indexed color image');
NumOfColors := ABitmap.Palette.Count;
// get/check palette entries
if ABitmap.Palette.Count=0 then
raise EPdfInvalidImageFormat.Create('failed to get Palette..');
ColorTable := TPdfBinary.Create;
S := '<';
for i := 0 to NumOfColors - 1 do
if i<ABitmap.Palette.Count then
with aBitmap.palette.color[i] do
AddColor(Red,Green,Blue)
else
AddColor(0,0,0);
S := S + '>';
ColorTable.Stream.Write(PChar(S)^, Length(S));
result := TPdfArray.CreateArray(nil);
with result do
begin
AddItem(TPdfName.CreateName('Indexed'));
AddItem(TPdfName.CreateName('DeviceRGB'));
AddItem(TPdfNumber.CreateNumber(NumOfColors - 1));
AddItem(ColorTable);
end;
end;
{ TRasterImageHelper }
function TRasterImageHelper.RequestRawStream(out rawStream: TMemoryStream
): boolean;
begin
// make direct use of the saved original stream to avoid re-copying
// this should be ok as it is very unlikely to change in the future.
rawStream := FSharedImage.SaveStream;
result := rawStream<>nil;
if result then
rawStream.Position := 0;
end;
end.

View File

@ -23,30 +23,29 @@
* 2001.09.01 changed the implementation of the image.
*
*}
{$IFDEF LAZ_POWERPDF}
{$H+}
{$IFDEF FPC}
{$MODE OBJFPC}{$H+}
{$ENDIF}
unit PdfImages;
interface
{$IFDEF UNIX}
{$IFDEF LAZ_POWERPDF}
{$ELSE}
{$IFNDEF FPC}
{$DEFINE USE_CLX}
{$ENDIF}
{$ENDIF}
uses
SysUtils,
{$IFNDEF USE_CLX}
{$IFDEF LAZ_POWERPDF}
LCLType, LCLIntf, Graphics, FPImage, IntfGraphics, BmpComn,
SysUtils, LCLType, LCLIntf, Graphics, FPImage, IntfGraphics, GraphType, PdFImageLazTools,
{$ELSE}
Windows, Graphics,
{$IFNDEF USE_CLX}
Windows, SysUtils, Graphics,
{$ELSE}
SysUtils, QGraphics, Qt,
{$ENDIF}
{$ELSE}
QGraphics, Qt,
{$ENDIF}
Classes, PdfTypes, PdfDoc;
@ -60,14 +59,13 @@ type
TPdfBitmapImage = class(TPdfImageCreator)
private
{$IFNDEF LAZ_POWERPDF}
function CreateIndexedColorArray(ABitmap: TBitmap): TPdfArray;
function CreateMaskStream(AImage: TFPCustomImage): TPDfImage;
{$ENDIF}
public
function CreateImage(AImage: TGraphic; ObjectMgr: TPdfObjectMgr=nil): TPdfImage; override;
end;
EPdfInvalidImageFormat = class(Exception);
function CreatePdfImage(AImage: TGraphic; ImageClassName: string; ObjectMgr: TPdfObjectMgr=nil): TPdfImage;
implementation
@ -103,6 +101,106 @@ type
PColorTable = ^TColorTable;
{$ENDIF}
{$IFDEF LAZ_POWERPDF}
function TPdfBitmapImage.CreateImage(AImage: TGraphic; ObjectMgr: TPdfObjectMgr=nil): TPdfImage;
var
fpImg: TFPCustomImage;
Alpha: TFPMemoryImage;
x, y, z: integer;
pb: PByteArray;
b: Byte;
aColor : TFPColor;
HasAlpha: Boolean;
MaskImage: TPdfImage;
procedure AddItems;
begin
with Result.Attributes do
begin
AddItem('Width', TPdfNumber.CreateNumber(aImage.Width));
AddItem('Height', TPdfNumber.CreateNumber(aImage.Height));
AddItem('BitsPerComponent', TPdfNumber.CreateNumber(8));
if USE_ZLIB then
PdfArrayByName('Filter').AddItem(TPdfName.CreateName('FlateDecode'));
end;
end;
begin
result := TPdfImage.CreateStream(nil);
with result do
try
with Attributes do
begin
AddItem('Type', TPdfName.CreateName('XObject'));
AddItem('Subtype', TPdfName.CreateName('Image'));
end;
// Convert a LCL TGraphic into a TFPCustomImage
ConvertGraphicToFPImage(AImage, fpImg);
if fpImg.UsePalette and (fpImg.Palette.Count>0) then
begin
for y := 0 to fpImg.Height - 1 do
begin
new(pb);
for x := 0 to fpImg.Width-1 do
pb^[x] := fpImg.Pixels[x,y];
Stream.Write(pb^, fpImg.Width);
dispose(pb);
end;
Attributes.AddItem('ColorSpace', CreateIndexedColorArray(fpImg));
end else
begin
Alpha := CreateAlphaImage(AImage.Width, AImage.Height);
hasAlpha := false;
for y := 0 to fpImg.Height - 1 do
begin
new(pb);
for x := 0 to fpImg.Width-1 do
begin
aColor := fpImg.Colors[x,y];
z:=1;
pb^[ 0 ] := acolor.red shr 8;
pb^[ z ] := acolor.green shr 8;
pb^[ z+1 ] := acolor.blue shr 8;
Stream.write(pb[ 0 ], 3);
b := acolor.alpha shr 8;
Alpha.Pixels[x,y] := b;
if acolor.Alpha<>AlphaOpaque then
HasAlpha := true;
end;
dispose(pb);
end;
if HasAlpha then begin
MaskImage := CreateMaskStream(Alpha);
if ObjectMgr<>nil then
ObjectMgr.AddObject(MaskImage);
Attributes.AddItem('SMask', MaskImage);
end;
Alpha.Free;
Attributes.AddItem('ColorSpace', TPdfName.CreateName('DeviceRGB'));
end;
AddItems;
fpImg.Free;
except
result.free;
raise;
end;
end;
{$ELSE}
function TPdfBitmapImage.CreateIndexedColorArray(ABitmap: TBitmap): TPdfArray;
var
{$IFNDEF USE_CLX}
@ -164,54 +262,12 @@ begin
end;
end;
function TPdfBitmapImage.CreateMaskStream(AImage: TFPCustomImage): TPDfImage;
var
pb: PByteArray;
y: Integer;
x: Integer;
begin
result := TPdfImage.CreateStream(nil);
with result do
try
with Attributes do
begin
AddItem('Type', TPdfName.CreateName('XObject'));
AddItem('Subtype', TPdfName.CreateName('Image'));
AddItem('Width', TPdfNumber.CreateNumber(aImage.Width));
AddItem('Height', TPdfNumber.CreateNumber(aImage.Height));
AddItem('BitsPerComponent', TPdfNumber.CreateNumber(8));
AddItem('ColorSpace',TPdfName.CreateName('DeviceGray'));
if USE_ZLIB then
PdfArrayByName('Filter').AddItem(TPdfName.CreateName('FlateDecode'));
new(pb);
for y := 0 to AImage.Height - 1 do
begin
for x := 0 to AImage.Width-1 do
pb^[x] := AImage.Pixels[x,y];
Stream.Write(pb^, AImage.Width);
end;
dispose(pb);
end;
finally
end;
end;
function TPdfBitmapImage.CreateImage(AImage: TGraphic; ObjectMgr: TPdfObjectMgr=nil): TPdfImage;
var
ABitmap: TBitmap;
x, y: integer;
pb: PByteArray;
b: Byte;
{$IFDEF LAZ_POWERPDF}
aIntfImage: TLazIntfImage;
aColor : TFPColor;
Alpha : TFPMemoryImage;
maskimage : TPDFImage;
hasAlpha : boolean;
{$endif}
{$IFDEF USE_CLX}
const
PIXEL_COLOR_SIZE = 4;
@ -233,10 +289,6 @@ begin
try
Assign(AImage);
{$IFDEF FPC}
aIntfImage := TLazIntfImage.Create(0,0);
aIntfImage.LoadFromBitmap(aBitmap.Handle, aBitmap.MaskHandle);
{$ENDIF}
// if bitmap image has less then 8 bit color, set PixelFormat to 8 bit.
if (PixelFormat = pf1bit) or
{$IFNDEF USE_CLX}
@ -254,35 +306,15 @@ begin
// translate TBitmap object to pdf image format.
if PixelFormat = pf8bit then
begin
{$IFNDEF FPC}
for y := 0 to Height - 1 do
begin
pb := ScanLine[y];
Stream.Write(pb^, Width);
{$ELSE}
for y := 0 to aintfimage.Height - 1 do
begin
new(pb);
for x := 0 to aintfimage.Width-1 do
begin
aColor := aIntfImage.Colors[x,y];
{ kleurwaarden worden als 16bits waarden opgeslagen, we kappen er
dus 8 van af.
red is willekeurig genomen
}
pb^[x] := acolor.red shr 8;
end;
Stream.Write(pb^, Width);
dispose(pb);
{$ENDIF}
end;
Attributes.AddItem('ColorSpace', CreateIndexedColorArray(ABitmap));
end
else
begin
{$ifndef fpc}
for y := 0 to Height - 1 do
begin
pb := ScanLine[y];
@ -297,61 +329,12 @@ begin
end;
Attributes.AddItem('ColorSpace', TPdfName.CreateName('DeviceRGB'));
end;
{$else}
Alpha := TFPMemoryImage.Create(AImage.Width, AImage.Height);
Alpha.UsePalette := true;
Alpha.Palette.Count := 256;
for x:=0 to $FF do
begin
aColor.Red:=x;
aColor.Red:=(aColor.Red shl 8) + aColor.Red;
aColor.Green:=aColor.Red;
aColor.Blue:=aColor.Red;
Alpha.Palette.Color[x]:=aColor;
end;
HasAlpha := false;
for y := 0 to aintfimage.Height - 1 do
begin
new(pb);
for x := 0 to aintfimage.Width-1 do
begin
aColor := aIntfImage.Colors[x,y];
pb[ 0 ] := acolor.red shr 8;
pb[ 1 ] := acolor.green shr 8;
pb[ 2 ] := acolor.blue shr 8;
Stream.write(pb[ 0 ], 3);
b := acolor.alpha shr 8;
Alpha.Pixels[x,y] := b;
if acolor.Alpha<>AlphaOpaque then
HasAlpha := true;
end;
dispose(pb);
Attributes.AddItem('ColorSpace', TPdfName.CreateName('DeviceRGB'));
end;
if HasAlpha then begin
MaskImage := CreateMaskStream(Alpha);
if ObjectMgr<>nil then
ObjectMgr.AddObject(MaskImage);
Attributes.AddItem('SMask', MaskImage);
end;
Alpha.Free;
{$endif}
end;
with Attributes do
begin
{$IFDEF FPC}
AddItem('Width', TPdfNumber.CreateNumber(aintfimage.Width));
AddItem('Height', TPdfNumber.CreateNumber(aintfimage.Height));
{$ELSE}
AddItem('Width', TPdfNumber.CreateNumber(abitmap.Width));
AddItem('Height', TPdfNumber.CreateNumber(abitmap.Height));
{$ENDIF}
AddItem('BitsPerComponent', TPdfNumber.CreateNumber(8));
if USE_ZLIB then
PdfArrayByName('Filter').AddItem(TPdfName.CreateName('FlateDecode'));
@ -359,15 +342,12 @@ begin
finally
Free;
end;
{$IFDEF FPC}
aIntfImage.Free();
{$ENDIF}
except
result.Free;
raise;
end;
end;
{$ENDIF}
initialization
{$IFDEF LAZ_POWERPDF}

View File

@ -27,10 +27,12 @@ interface
uses
SysUtils, Classes, Graphics, PdfTypes, PdfDoc, PdfImages
{$IFDEF LAZ_POWERPDF}
,FPImage, FPReadJPEG, PdfImageLazTools
{$ELSE}
,JPEG
{$ENDIF}
;
type
{ TPdfJpegImage }
TPdfJpegImage = class(TPdfImageCreator)
@ -41,7 +43,48 @@ type
implementation
// CreateImage
{$IFDEF LAZ_POWERPDF}
function TPdfJpegImage.CreateImage(AImage: TGraphic; ObjectMgr: TPdfObjectMgr=nil): TPdfImage;
var
fpImg: TFPCustomImage;
begin
// check whether specified graphic is valid image.
if not (AImage is TJpegImage) then
raise EPdfInvalidValue.Create('only jpeg image is allowed.');
result := TPdfImage.CreateStream(nil);
try
ConvertGraphicToFPImage(AImage, fpImg);
TJpegImage(AImage).SaveToStream(result.Stream);
with result.Attributes do
begin
AddItem('Type', TPdfName.CreateName('XObject'));
AddItem('Subtype', TPdfName.CreateName('Image'));
if TJPegImage(AImage).GrayScale then
AddItem('ColorSpace', TPdfName.CreateName('DeviceGray'))
else
AddItem('ColorSpace', TPdfName.CreateName('DeviceRGB'));
AddItem('Width', TPdfNumber.CreateNumber(fpImg.Width));
AddItem('Height', TPdfNumber.CreateNumber(fpImg.Height));
AddItem('BitsPerComponent', TPdfNumber.CreateNumber(8));
PdfArrayByName('Filter').AddItem(TPdfName.CreateName('DCTDecode'));
end;
fpImg.Free;
except
result.Free;
raise;
end;
end;
{$ELSE}
function TPdfJpegImage.CreateImage(AImage: TGraphic; ObjectMgr: TPdfObjectMgr=nil): TPdfImage;
begin
// check whether specified graphic is valid image.
if not (AImage is TJpegImage) then
@ -70,6 +113,7 @@ begin
end;
end;
{$ENDIF}
initialization

View File

@ -283,6 +283,7 @@ type
EPdfInvalidValue = class(Exception);
EPdfInvalidOperation = class(Exception);
EPdfInvalidImageFormat = class(Exception);
{*
* utility functions.

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<CONFIG>
<Package Version="4">
<Package Version="5">
<PathDelim Value="\"/>
<Name Value="pack_powerpdf"/>
<Type Value="RunAndDesignTime"/>
@ -21,8 +21,8 @@
<CustomOptions Value="-dLAZ_POWERPDF"/>
</Other>
</CompilerOptions>
<Version Minor="9" Release="15"/>
<Files Count="12">
<Version Minor="9" Release="16"/>
<Files Count="13">
<Item1>
<Filename Value="PdfTypes.pas"/>
<UnitName Value="PdfTypes"/>
@ -72,7 +72,12 @@
<HasRegisterProc Value="True"/>
<UnitName Value="PowerPdf"/>
</Item12>
<Item13>
<Filename Value="PdfImageLazTools.pas"/>
<UnitName Value="PdfImageLazTools"/>
</Item13>
</Files>
<CompatibilityMode Value="True"/>
<RequiredPkgs Count="2">
<Item1>
<PackageName Value="LCL"/>
@ -87,7 +92,6 @@
</UsageOptions>
<PublishOptions>
<Version Value="2"/>
<IgnoreBinaries Value="False"/>
</PublishOptions>
</Package>
</CONFIG>

View File

@ -10,7 +10,7 @@ interface
uses
PdfTypes, PdfDoc, PdfJpCMap, PdfJPFonts, PdfGBFonts, PdfFonts, PdfImages,
PReport, PdfJpegImage, PRJpegImage, PRAnnotation, PowerPdf,
LazarusPackageIntf;
PdfImageLazTools, LazarusPackageIntf;
implementation