Browse Source

--- Merging r41551 into '.':
A packages/fcl-pdf/examples/diamond.png
U packages/fcl-pdf/examples/testfppdf.lpr
U packages/fcl-pdf/src/fppdf.pp
--- Recording mergeinfo for merge of r41551 into '.':
G .
--- Merging r41552 into '.':
U packages/fcl-pdf/examples/testfppdf.lpi
G packages/fcl-pdf/src/fppdf.pp
--- Recording mergeinfo for merge of r41552 into '.':
G .
--- Merging r41553 into '.':
U packages/fcl-pdf/src/fpttf.pp
--- Recording mergeinfo for merge of r41553 into '.':
G .
--- Merging r41591 into '.':
G packages/fcl-pdf/src/fppdf.pp
--- Recording mergeinfo for merge of r41591 into '.':
G .
--- Merging r41592 into '.':
G packages/fcl-pdf/src/fpttf.pp
--- Recording mergeinfo for merge of r41592 into '.':
G .
--- Merging r41798 into '.':
G packages/fcl-pdf/src/fppdf.pp
--- Recording mergeinfo for merge of r41798 into '.':
G .
--- Merging r41799 into '.':
G packages/fcl-pdf/src/fppdf.pp
--- Recording mergeinfo for merge of r41799 into '.':
G .
--- Merging r41800 into '.':
U packages/fcl-pdf/utils/ttfdump.lpr
--- Recording mergeinfo for merge of r41800 into '.':
G .

# revisions: 41551,41552,41553,41591,41592,41798,41799,41800
r41551 | michael | 2019-03-02 13:21:28 +0100 (Sat, 02 Mar 2019) | 1 line
Changed paths:
A /trunk/packages/fcl-pdf/examples/diamond.png
M /trunk/packages/fcl-pdf/examples/testfppdf.lpr
M /trunk/packages/fcl-pdf/src/fppdf.pp

* Patch from Ondrej Pokorny to add transparency
r41552 | michael | 2019-03-02 13:31:18 +0100 (Sat, 02 Mar 2019) | 1 line
Changed paths:
M /trunk/packages/fcl-pdf/examples/testfppdf.lpi
M /trunk/packages/fcl-pdf/src/fppdf.pp

* Small change in API, use set instead of 2 booleans
r41553 | michael | 2019-03-02 15:05:23 +0100 (Sat, 02 Mar 2019) | 1 line
Changed paths:
M /trunk/packages/fcl-pdf/src/fpttf.pp

* Add (Get|Find)PostScriptFontName
r41591 | michael | 2019-03-05 12:23:56 +0100 (Tue, 05 Mar 2019) | 1 line
Changed paths:
M /trunk/packages/fcl-pdf/src/fppdf.pp

* Fix AV
r41592 | michael | 2019-03-05 12:24:23 +0100 (Tue, 05 Mar 2019) | 1 line
Changed paths:
M /trunk/packages/fcl-pdf/src/fpttf.pp

* Fixed typo
r41798 | michael | 2019-03-26 22:31:46 +0100 (Tue, 26 Mar 2019) | 1 line
Changed paths:
M /trunk/packages/fcl-pdf/src/fppdf.pp

* Fix bug ID #0035252, eliminate wrong combination of options
r41799 | michael | 2019-03-26 22:35:15 +0100 (Tue, 26 Mar 2019) | 1 line
Changed paths:
M /trunk/packages/fcl-pdf/src/fppdf.pp

* Fix range check error (bug ID 35251)
r41800 | michael | 2019-03-26 22:36:01 +0100 (Tue, 26 Mar 2019) | 1 line
Changed paths:
M /trunk/packages/fcl-pdf/utils/ttfdump.lpr

* Fix range check error (bug ID 35251)

git-svn-id: branches/fixes_3_2@41916 -

marco 6 years ago
parent
commit
204fac0c20

+ 1 - 0
.gitattributes

@@ -2576,6 +2576,7 @@ packages/fcl-passrc/tests/testpassrc.lpi svneol=native#text/plain
 packages/fcl-passrc/tests/testpassrc.lpr svneol=native#text/plain
 packages/fcl-pdf/Makefile svneol=native#text/plain
 packages/fcl-pdf/Makefile.fpc svneol=native#text/plain
+packages/fcl-pdf/examples/diamond.png -text svneol=unset#image/png
 packages/fcl-pdf/examples/poppy.jpg -text
 packages/fcl-pdf/examples/testfppdf.lpi svneol=native#text/plain
 packages/fcl-pdf/examples/testfppdf.lpr svneol=native#text/plain

+ 2 - 0
packages/fcl-base/src/base64.pp

@@ -425,6 +425,8 @@ var
   Outstream : TStringStream;
   Decoder   : TBase64DecodingStream;
 begin
+  if Length(s)=0 then
+    Exit('');
   SD:=S;
   while Length(Sd) mod 4 > 0 do 
     SD := SD + '=';

+ 1 - 0
packages/fcl-base/src/fpexprpars.pp

@@ -780,6 +780,7 @@ Type
     Property Identifiers : TFPExprIdentifierDefs Read FIdentifiers Write SetIdentifiers;
     Property BuiltIns : TBuiltInCategories Read FBuiltIns Write SetBuiltIns;
   end;
+  TFPExpressionParserClass = Class of TFPExpressionParser;
 
   { TExprBuiltInManager }
 

BIN
packages/fcl-pdf/examples/diamond.png


+ 10 - 5
packages/fcl-pdf/examples/testfppdf.lpi

@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <CONFIG>
   <ProjectOptions>
-    <Version Value="9"/>
+    <Version Value="11"/>
     <General>
       <Flags>
         <SaveOnlyProjectUnits Value="True"/>
@@ -19,9 +19,6 @@
     <i18n>
       <EnableI18N LFM="False"/>
     </i18n>
-    <VersionInfo>
-      <StringTable ProductVersion=""/>
-    </VersionInfo>
     <BuildModes Count="1">
       <Item1 Name="Default" Default="True"/>
     </BuildModes>
@@ -30,8 +27,16 @@
     </PublishOptions>
     <RunParams>
       <local>
-        <FormatVersion Value="1"/>
+        <CommandLineParams Value="-t 1"/>
       </local>
+      <FormatVersion Value="2"/>
+      <Modes Count="1">
+        <Mode0 Name="default">
+          <local>
+            <CommandLineParams Value="-t 1"/>
+          </local>
+        </Mode0>
+      </Modes>
     </RunParams>
     <Units Count="1">
       <Unit0>

+ 12 - 2
packages/fcl-pdf/examples/testfppdf.lpr

@@ -33,7 +33,8 @@ type
     FRawJPEG,
     FImageCompression,
     FTextCompression,
-    FFontCompression: boolean;
+    FFontCompression,
+    FImageTransparency: boolean;
     FNoFontEmbedding: boolean;
     FAddMetadata : Boolean;
     FSubsetFontEmbedding: boolean;
@@ -93,6 +94,8 @@ begin
     Include(lOpts,poCompressText);
   if FImageCompression then
     Include(lOpts,poCompressImages);
+  if FImageTransparency then
+    Include(lOpts,poUseImageTransparency);
   if FRawJPEG then
     Include(lOpts,poUseRawJPEG);
   if FAddMetadata then
@@ -302,7 +305,7 @@ procedure TPDFTestApp.SimpleImage(D: TPDFDocument; APage: integer);
 Var
   P: TPDFPage;
   FtTitle: integer;
-  IDX: Integer;
+  IDX, IDX_Diamond: Integer;
   W, H: Integer;
 begin
   P := D.Pages[APage];
@@ -323,6 +326,10 @@ begin
   { full size image }
   P.DrawImageRawSize(25, 130, W, H, IDX);  // left-bottom coordinate of image
   P.WriteText(145, 90, '[Full size (defined in pixels)]');
+  P.WriteText(145, 95, '+alpha-transparent overlay (if enabled)');
+
+  IDX_Diamond := D.Images.AddFromFile('diamond.png',False);
+  P.DrawImageRawSize(30, 125, D.Images[IDX_Diamond].Width, D.Images[IDX_Diamond].Height, IDX_Diamond);
 
   { quarter size image }
   P.DrawImageRawSize(25, 190, W shr 1, H shr 1, IDX); // could also have used: Integer(W div 2), Integer(H div 2)
@@ -817,6 +824,7 @@ begin
   FFontCompression := BoolFlag('f',true);
   FTextCompression := BoolFlag('t',False);
   FImageCompression := BoolFlag('i',False);
+  FImageTransparency := BoolFlag('t',False);
   FAddMetadata :=  BoolFlag('m',False);
   FRawJPEG:=BoolFlag('j',False);
 
@@ -881,6 +889,8 @@ begin
           '                disables compression. A value of 1 enables compression.');
   writeln('    -j <0|1>    Toggle use of JPEG. A value of 0' + LineEnding +
           '                disables use of JPEG images. A value of 1 writes jpeg file as-is');
+  writeln('    -t <0|1>    Toggle image transparency support. A value of 0' + LineEnding +
+          '                disables transparency. A value of 1 enables transparency.');
   writeln('');
 end;
 

+ 224 - 47
packages/fcl-pdf/src/fppdf.pp

@@ -69,7 +69,8 @@ type
   TPDFPageLayout = (lSingle, lTwo, lContinuous);
   TPDFUnitOfMeasure = (uomInches, uomMillimeters, uomCentimeters, uomPixels);
 
-  TPDFOption = (poOutLine, poCompressText, poCompressFonts, poCompressImages, poUseRawJPEG, poNoEmbeddedFonts, poPageOriginAtTop, poSubsetFont, poMetadataEntry, poNoTrailerID);
+  TPDFOption = (poOutLine, poCompressText, poCompressFonts, poCompressImages, poUseRawJPEG, poNoEmbeddedFonts,
+    poPageOriginAtTop, poSubsetFont, poMetadataEntry, poNoTrailerID, poUseImageTransparency);
   TPDFOptions = set of TPDFOption;
 
   EPDF = Class(Exception);
@@ -881,7 +882,8 @@ type
 
 
   TPDFImageCompression = (icNone, icDeflate, icJPEG);
-
+  TPDFImageStreamOption = (isoCompressed,isoTransparent);
+  TPDFImageStreamOptions = set of TPDFImageStreamOption;
 
   TPDFImageItem = Class(TCollectionItem)
   private
@@ -889,22 +891,33 @@ type
     FOwnsImage: Boolean;
     FStreamed: TBytes;
     FCompression: TPDFImageCompression;
+    FStreamedMask: TBytes;
+    FCompressionMask: TPDFImageCompression;
     FWidth,FHeight : Integer;
+    function GetHasMask: Boolean;
     function GetHeight: Integer;
     function GetStreamed: TBytes;
+    function GetStreamedMask: TBytes;
     function GetWidth: Integer;
     procedure SetImage(AValue: TFPCustomImage);
     procedure SetStreamed(AValue: TBytes);
+  Protected
+    Function WriteStream(const AStreamedData: TBytes; AStream: TStream): int64; virtual;
   Public
     Destructor Destroy; override;
-    Procedure CreateStreamedData(AUseCompression: Boolean);
-    Function WriteImageStream(AStream: TStream): int64; virtual;
+    Procedure CreateStreamedData(AUseCompression: Boolean); overload;
+    Procedure CreateStreamedData(aOptions : TPDFImageStreamOptions); overload;
+    procedure SetStreamedMask(const AValue: TBytes; const ACompression: TPDFImageCompression);
+    Function WriteImageStream(AStream: TStream): int64;
+    Function WriteMaskStream(AStream: TStream): int64;
     function Equals(AImage: TFPCustomImage): boolean; reintroduce;
     Property Image : TFPCustomImage Read FImage Write SetImage;
     Property StreamedData : TBytes Read GetStreamed Write SetStreamed;
+    Property StreamedMask : TBytes Read GetStreamedMask;
     Property OwnsImage : Boolean Read FOwnsImage Write FOwnsImage;
     Property Width : Integer Read GetWidth;
     Property Height : Integer Read GetHeight;
+    Property HasMask : Boolean read GetHasMask;
   end;
 
 
@@ -1013,6 +1026,7 @@ type
     procedure SetFonts(AValue: TPDFFontDefs);
     procedure SetInfos(AValue: TPDFInfos);
     procedure SetLineStyles(AValue: TPDFLineStyleDefs);
+    Procedure SetOptions(aValue : TPDFOptions);
   protected
     // Create all kinds of things, virtual so they can be overridden to create descendents instead
     function CreatePDFPages: TPDFPages; virtual;
@@ -1053,7 +1067,10 @@ type
     procedure CreateToUnicode(const AFontNum: integer);virtual;
     procedure CreateFontFileEntry(const AFontNum: integer);virtual;
     procedure CreateCIDSet(const AFontNum: integer); virtual;
-    procedure CreateImageEntry(ImgWidth, ImgHeight, NumImg: integer);virtual;
+    procedure CreateImageEntry(ImgWidth, ImgHeight, NumImg: integer;
+      out ImageDict: TPDFDictionary);virtual;
+    procedure CreateImageMaskEntry(ImgWidth, ImgHeight, NumImg: integer;
+      ImageDict: TPDFDictionary);virtual;
     function CreateAnnotEntry(const APageNum, AnnotNum: integer): integer; virtual;
     function CreateCIDToGIDMap(const AFontNum: integer): integer; virtual;
     procedure CreatePageStream(APage : TPDFPage; PageNum: integer);
@@ -1064,6 +1081,7 @@ type
     function IndexOfGlobalXRef(const AValue: string): integer;
     Function FindGlobalXRef(Const AName : String) : TPDFXRef;
     Function GlobalXRefByName(Const AName : String) : TPDFXRef;
+    Function ImageStreamOptions : TPDFImageStreamOptions;
     Property GlobalXRefs[AIndex : Integer] : TPDFXRef Read GetX;
     Property GlobalXRefCount : Integer Read GetXC;
     Property CurrentColor: string Read FCurrentColor Write FCurrentColor;
@@ -1109,7 +1127,7 @@ type
     Property ObjectCount : Integer Read FObjectCount;
     Property LineCapStyle: TPDFLineCapStyle Read FLineCapStyle Write FLineCapStyle;
   Published
-    Property Options : TPDFOptions Read FOptions Write FOPtions;
+    Property Options : TPDFOptions Read FOptions Write SetOptions;
     Property LineStyles : TPDFLineStyleDefs Read FLineStyleDefs Write SetLineStyles;
     property PageLayout: TPDFPageLayout read FPageLayout write FPageLayout default lSingle;
     Property Infos : TPDFInfos Read FInfos Write SetInfos;
@@ -1669,14 +1687,30 @@ var
   s: string;
   lst: TTextMappingList;
   lFont: TTFFileInfo;
+  lWidthIndex: integer;
 begin
   s := '';
   lst := Document.Fonts[EmbeddedFontNum].TextMapping;
   lst.Sort;
   lFont := Document.Fonts[EmbeddedFontNum].FTrueTypeFile;
-  // use decimal values for the output
+
+  {$IFDEF gdebug}
+  System.WriteLn('****** isFixedPitch = ', BoolToStr(lFont.PostScript.isFixedPitch > 0, True));
+  System.WriteLn('****** Head.UnitsPerEm := ', lFont.Head.UnitsPerEm );
+  System.WriteLn('****** HHead.numberOfHMetrics := ', lFont.HHead.numberOfHMetrics );
+  {$ENDIF}
+
+  { NOTE: Monospaced fonts may not have a width for every glyph
+          the last one is for subsequent glyphs.  }
   for i := 0 to lst.Count-1 do
-    s :=  s + Format(' %d [%d]', [ lst[i].GlyphID, TTTFFriendClass(lFont).ToNatural(lFont.Widths[lst[i].GlyphID].AdvanceWidth)]);
+  begin
+    if lst[i].GlyphID < lFont.HHead.numberOfHMetrics then
+      lWidthIndex := lst[i].GlyphID
+    else
+      lWidthIndex := lFont.HHead.numberOfHMetrics-1;
+    s :=  s + Format(' %d [%d]', [lst[i].GlyphID, TTTFFriendClass(lFont).ToNatural(lFont.Widths[lWidthIndex].AdvanceWidth)])
+  end;
+
   WriteString(s, AStream);
 end;
 
@@ -2831,17 +2865,29 @@ begin
 end;
 
 function TPDFImageItem.GetStreamed: TBytes;
+
+Var
+  Opts : TPDFImageStreamOptions;
+
 begin
+  Opts:=[];
   if Length(FStreamed)=0 then
-  begin
+    begin
     if Collection.Owner is TPDFDocument then
-      CreateStreamedData(poCompressImages in TPDFDocument(Collection.Owner).Options)
+      Opts:=TPDFDocument(Collection.Owner).ImageStreamOptions
     else
-      CreateStreamedData(True);
-  end;
+      Opts:=[isoCompressed,isoTransparent];
+    CreateStreamedData(Opts);
+    end;
   Result:=FStreamed;
 end;
 
+function TPDFImageItem.GetStreamedMask: TBytes;
+begin
+  GetStreamed; // calls CreateStreamedData
+  Result:=FStreamedMask;
+end;
+
 function TPDFImageItem.GetHeight: Integer;
 begin
   If Assigned(FImage) then
@@ -2865,6 +2911,25 @@ begin
   FStreamed:=AValue;
 end;
 
+procedure TPDFImageItem.SetStreamedMask(const AValue: TBytes;
+  const ACompression: TPDFImageCompression);
+begin
+  If AValue=FStreamedMask then exit;
+  SetLength(FStreamedMask,0);
+  FStreamedMask:=AValue;
+  FCompressionMask:=ACompression;
+end;
+
+function TPDFImageItem.WriteImageStream(AStream: TStream): int64;
+begin
+  Result:=WriteStream(FStreamed, AStream);
+end;
+
+function TPDFImageItem.WriteMaskStream(AStream: TStream): int64;
+begin
+  Result:=WriteStream(FStreamedMask, AStream);
+end;
+
 destructor TPDFImageItem.Destroy;
 begin
   if FOwnsImage then
@@ -2873,59 +2938,106 @@ begin
 end;
 
 procedure TPDFImageItem.CreateStreamedData(AUseCompression: Boolean);
+
+begin
+  CreateStreamedData([isoCompressed]);
+end;
+
+Procedure TPDFImageItem.CreateStreamedData(aOptions : TPDFImageStreamOptions);
+
+
+  function NeedsTransparency: Boolean;
+  var
+    Y, X: Integer;
+  begin
+    for Y:=0 to FHeight-1 do
+      for X:=0 to FWidth-1 do
+        begin
+        if Image.Colors[x,y].alpha < $FFFF then // has alpha channel
+          Exit(True);
+        end;
+    Result:=False;
+  end;
+
+  procedure CreateStream(out MS: TMemoryStream; out Str: TStream;
+    out Compression: TPDFImageCompression);
+  begin
+    MS := TMemoryStream.Create;
+    if (isoCompressed in aOptions) then
+      begin
+      Compression := icDeflate;
+      Str := Tcompressionstream.create(cldefault, MS);
+      end
+    else
+      begin
+      Compression := icNone;
+      Str := MS;
+      end;
+  end;
+
+  procedure StreamToBuffer(const MS: TMemoryStream; var Str: TStream; out Buffer: TBytes);
+  begin
+    if Str<>MS then
+      Str.Free;
+    Str := nil;
+    SetLength(Buffer, MS.Size);
+    MS.Position := 0;
+    if MS.Size>0 then
+      MS.ReadBuffer(Buffer[0], MS.Size);
+  end;
+
 Var
   X,Y : Integer;
   C : TFPColor;
-  MS : TMemoryStream;
-  Str : TStream;
+  MS,MSMask : TMemoryStream;
+  Str,StrMask : TStream;
   CWhite : TFPColor; // white color
+  CreateMask : Boolean;
 begin
   FillMem(@CWhite, SizeOf(CWhite), $FF);
   FWidth:=Image.Width;
   FHeight:=Image.Height;
+  CreateMask:=(isoTransparent in aOptions) and NeedsTransparency;
+  MS := nil;
   Str := nil;
-  MS := TMemoryStream.Create;
+  MSMask := nil;
+  StrMask := nil;
   try
-    if AUseCompression then
-      begin
-      FCompression := icDeflate;
-      Str := Tcompressionstream.create(cldefault, MS)
-      end
-    else
-      begin
-      FCompression := icNone;
-      Str := MS;
-      end;
+    CreateStream(MS, Str, FCompression);
+    if CreateMask then
+      CreateStream(MSMask, StrMask, FCompressionMask);
     for Y:=0 to FHeight-1 do
       for X:=0 to FWidth-1 do
         begin
         C:=Image.Colors[x,y];
-        if C.alpha < $FFFF then // remove alpha channel - assume white background
+        if CreateMask then
+          StrMask.WriteByte(C.Alpha shr 8)
+        else
+        if (C.alpha < $FFFF) then // remove alpha channel - assume white background
           C := AlphaBlend(CWhite, C);
 
         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);
+    StreamToBuffer(MS, Str, FStreamed);
+    if CreateMask then
+      StreamToBuffer(MSMask, StrMask, FStreamedMask);
   finally
     Str.Free;
+    StrMask.Free;
     MS.Free;
+    MSMask.Free;
   end;
 end;
 
-function TPDFImageItem.WriteImageStream(AStream: TStream): int64;
+function TPDFImageItem.WriteStream(const AStreamedData: TBytes;
+  AStream: TStream): int64;
 var
   Img : TBytes;
 begin
   TPDFObject.WriteString(CRLF+'stream'+CRLF,AStream);
-  Img:=StreamedData;
+  Img:=AStreamedData;
   Result:=Length(Img);
   AStream.WriteBuffer(Img[0],Result);
   TPDFObject.WriteString(CRLF, AStream);
@@ -2956,6 +3068,11 @@ begin
       end;
 end;
 
+function TPDFImageItem.GetHasMask: Boolean;
+begin
+  Result := Length(FStreamedMask)>0;
+end;
+
 { TPDFImages }
 
 function TPDFImages.GetI(AIndex : Integer): TPDFImageItem;
@@ -3092,7 +3209,7 @@ begin
     IP.Image:=I;
     if Not KeepImage then
       begin
-      IP.CreateStreamedData(poCompressImages in Owner.Options);
+      IP.CreateStreamedData(Owner.ImageStreamOptions);
       IP.FImage:=Nil; // not through property, that would clear the image
       i.Free;
       end;
@@ -4055,6 +4172,22 @@ begin
         begin
           if (E.FKey.Name='Name') then
           begin
+            if (TPDFObject(E.Value) is TPDFName) and (TPDFName(E.Value).Name[1]='M') then
+            begin
+              NumImg:=StrToInt(Copy(TPDFName(E.Value).Name, 2, Length(TPDFName(E.Value).Name) - 1));
+              // write image stream length in xobject dictionary
+              ISize:=Length(Document.Images[NumImg].StreamedMask);
+              D:=Document.GlobalXRefs[AObject].Dict;
+              D.AddInteger('Length',ISize);
+              LastElement.Write(AStream);
+              case Document.Images[NumImg].FCompressionMask of
+                icJPEG: WriteString('/Filter /DCTDecode'+CRLF, AStream);
+                icDeflate: WriteString('/Filter /FlateDecode'+CRLF, AStream);
+              end;
+              WriteString('>>', AStream);
+              // write image stream in xobject dictionary
+              Document.Images[NumImg].WriteMaskStream(AStream);
+            end else
             if (TPDFObject(E.Value) is TPDFName) and (TPDFName(E.Value).Name[1]='I') then
             begin
               NumImg:=StrToInt(Copy(TPDFName(E.Value).Name, 2, Length(TPDFName(E.Value).Name) - 1));
@@ -4372,6 +4505,14 @@ begin
   FInfos.Assign(AValue);
 end;
 
+procedure TPDFDocument.SetOptions(AValue: TPDFOptions);
+begin
+  if FOptions=AValue then Exit;
+  if (poNoEmbeddedFonts in  aValue) then
+    Exclude(aValue,poSubsetFont);
+  FOptions:=aValue;
+end;
+
 procedure TPDFDocument.SetLineStyles(AValue: TPDFLineStyleDefs);
 begin
   if FLineStyleDefs=AValue then Exit;
@@ -5087,24 +5228,25 @@ begin
   lXRef.FStream.AddItem(TPDFCIDSet.Create(self, AFontNum));
 end;
 
-procedure TPDFDocument.CreateImageEntry(ImgWidth, ImgHeight, NumImg: integer);
+procedure TPDFDocument.CreateImageEntry(ImgWidth, ImgHeight, NumImg: integer;
+  out ImageDict: TPDFDictionary);
 var
   N: TPDFName;
-  IDict,ADict: TPDFDictionary;
+  ADict: TPDFDictionary;
   i: integer;
   lXRef: integer;
 begin
   lXRef := GlobalXRefCount; // reference to be used later
 
-  IDict:=CreateGlobalXRef.Dict;
-  IDict.AddName('Type','XObject');
-  IDict.AddName('Subtype','Image');
-  IDict.AddInteger('Width',ImgWidth);
-  IDict.AddInteger('Height',ImgHeight);
-  IDict.AddName('ColorSpace','DeviceRGB');
-  IDict.AddInteger('BitsPerComponent',8);
+  ImageDict:=CreateGlobalXRef.Dict;
+  ImageDict.AddName('Type','XObject');
+  ImageDict.AddName('Subtype','Image');
+  ImageDict.AddInteger('Width',ImgWidth);
+  ImageDict.AddInteger('Height',ImgHeight);
+  ImageDict.AddName('ColorSpace','DeviceRGB');
+  ImageDict.AddInteger('BitsPerComponent',8);
   N:=CreateName('I'+IntToStr(NumImg)); // Needed later
-  IDict.AddElement('Name',N);
+  ImageDict.AddElement('Name',N);
 
   // now find where we must add the image xref - we are looking for "Resources"
   for i := 1 to GlobalXRefCount-1 do
@@ -5125,6 +5267,27 @@ begin
   end;
 end;
 
+procedure TPDFDocument.CreateImageMaskEntry(ImgWidth, ImgHeight,
+  NumImg: integer; ImageDict: TPDFDictionary);
+var
+  N: TPDFName;
+  MDict: TPDFDictionary;
+  lXRef: integer;
+begin
+  lXRef := GlobalXRefCount; // reference to be used later
+
+  MDict:=CreateGlobalXRef.Dict;
+  MDict.AddName('Type','XObject');
+  MDict.AddName('Subtype','Image');
+  MDict.AddInteger('Width',ImgWidth);
+  MDict.AddInteger('Height',ImgHeight);
+  MDict.AddName('ColorSpace','DeviceGray');
+  MDict.AddInteger('BitsPerComponent',8);
+  N:=CreateName('M'+IntToStr(NumImg)); // Needed later
+  MDict.AddElement('Name',N);
+  ImageDict.AddReference('SMask', lXRef);
+end;
+
 function TPDFDocument.CreateAnnotEntry(const APageNum, AnnotNum: integer): integer;
 var
   lDict, ADict: TPDFDictionary;
@@ -5228,6 +5391,15 @@ begin
     Raise EPDF.CreateFmt(rsErrNoGlobalDict,[AName]);
 end;
 
+function TPDFDocument.ImageStreamOptions: TPDFImageStreamOptions;
+begin
+  Result:=[];
+  if (poCompressImages in Options) then
+    Include(Result,isoCompressed);
+  if (poUseImageTransparency in Options) then
+    Include(Result,isoTransparent);
+end;
+
 function TPDFDocument.CreateLineStyles: TPDFLineStyleDefs;
 begin
   Result:=TPDFLineStyleDefs.Create(TPDFLineStyleDef);
@@ -5492,9 +5664,14 @@ end;
 procedure TPDFDocument.CreateImageEntries;
 Var
   I : Integer;
+  IDict : TPDFDictionary;
 begin
   for i:=0 to Images.Count-1 do
-    CreateImageEntry(Images[i].Width,Images[i].Height,i);
+    begin
+    CreateImageEntry(Images[i].Width,Images[i].Height,i,IDict);
+    if Images[i].HasMask then
+      CreateImageMaskEntry(Images[i].Width,Images[i].Height,i,IDict);
+    end;
 end;
 
 procedure TPDFDocument.CreateAnnotEntries(const APageNum: integer; const APageDict: TPDFDictionary);

+ 57 - 0
packages/fcl-pdf/src/fpttf.pp

@@ -85,6 +85,7 @@ type
   TFPFontCacheItemArray = Array of TFPFontCacheItem;
 
   { TFPFontCacheList }
+  EFontNotFound = Class(Exception);
 
   TFPFontCacheList = class(TObject)
   private
@@ -97,6 +98,7 @@ type
     { Set any / or \ path delimiters to the OS specific delimiter }
     procedure   FixPathDelimiters;
   protected
+    function    DoFindPostScriptFontName(const AFontName: string; ABold: boolean; AItalic: boolean; Out aBaseFont : TFPFontCacheItem): String;
     function    GetCount: integer; virtual;
     function    GetItem(AIndex: Integer): TFPFontCacheItem; virtual;
     procedure   SetItem(AIndex: Integer; AValue: TFPFontCacheItem); virtual;
@@ -111,6 +113,10 @@ type
     procedure   ReadStandardFonts;
     property    Count: integer read GetCount;
     function    IndexOf(const AObject: TFPFontCacheItem): integer;
+    // Find postscript font name based on fontname and attributes
+    function    FindPostScriptFontName(const AFontName: string; ABold: boolean; AItalic: boolean): String;
+    // Same as Find, but raise exception when not found.
+    function    GetPostScriptFontName(const AFontName: string; ABold: boolean; AItalic: boolean): String;
     function    Find(const AFontCacheItem: TFPFontCacheItem): integer; overload;
     function    Find(const AFamilyName: string; ABold: boolean; AItalic: boolean): TFPFontCacheItem; overload;
     function    Find(const APostScriptName: string): TFPFontCacheItem; overload;
@@ -143,6 +149,7 @@ resourcestring
   rsNoSearchPathDefined = 'No search path was defined';
   rsNoFontFileName = 'The FileName property is empty, so we can''t load font data.';
   rsMissingFontFile = 'The font file <%s> can''t be found.';
+  SErrFontNotFound = 'The font <%s> can''t be found';
 
 var
   uFontCacheList: TFPFontCacheList;
@@ -591,6 +598,56 @@ begin
   Result := FList.IndexOf(AObject);
 end;
 
+function TFPFontCacheList.GetPostScriptFontName(const AFontName: string; ABold: boolean; AItalic: boolean): String;
+
+Var
+  lFC : TFPFontCacheItem;
+  lMissingFontName : String;
+
+begin
+  Result:=DoFindPostScriptFontName(aFontName,aBold,aItalic,lfc);
+  if (Result=aFontName) and (aBold or aItalic) then
+    begin
+    if lFC<>Nil then
+      lMissingFontName := lfc.FamilyName
+    else
+      lMissingFontName := aFontName;
+    if (aBold and AItalic) then
+      lMissingFontName := lMissingFontName + '-BoldItalic'
+    else if aBold then
+      lMissingFontName := lMissingFontName + '-Bold'
+    else if aItalic then
+      lMissingFontName := lMissingFontName + '-Italic';
+    raise EFontNotFound.CreateFmt(SErrFontNotFound, [lMissingFontName]);
+    end;
+end;
+
+function TFPFontCacheList.FindPostScriptFontName(const AFontName: string; ABold: boolean; AItalic: boolean): String;
+
+Var
+  lFC : TFPFontCacheItem;
+
+begin
+  Result:=DoFindPostScriptFontName(aFontName,aBold,aItalic,lfc);
+end;
+
+function  TFPFontCacheList.DoFindPostScriptFontName(const AFontName: string; ABold: boolean; AItalic: boolean; Out aBaseFont : TFPFontCacheItem): String;
+
+Var
+   lNewFC : TFPFontCacheItem;
+
+begin
+  Result:=aFontName;
+  aBaseFont := FindFont(aFontName);
+  if not Assigned(aBaseFont) then
+    exit;
+  // find corresponding font style (bold and/or italic)
+  lNewFC := Find(aBaseFont.FamilyName, aBold, aItalic);
+  if not Assigned(lNewFC) then
+    exit;
+  Result := lNewFC.PostScriptName;
+end;
+
 function TFPFontCacheList.Find(const AFontCacheItem: TFPFontCacheItem): integer;
 var
   i: integer;

+ 19 - 3
packages/fcl-pdf/utils/ttfdump.lpr

@@ -37,6 +37,21 @@ type
 { TMyApplication }
 
 procedure TMyApplication.DumpGlyphIndex;
+
+  procedure PrintGlyphWidth(const aIndex: UInt32);
+  var
+    lWidthIndex: integer;
+  begin
+    { NOTE: Monospaced fonts may not have a width for every glyph
+            the last one is for subsequent glyphs.  }
+    if aIndex < FFontFile.HHead.numberOfHMetrics then
+      lWidthIndex := FFontFile.Chars[aIndex]
+    else
+      lWidthIndex := FFontFile.HHead.numberOfHMetrics-1;
+
+    Writeln(Format('  %3d = %d', [FFontFile.Chars[aIndex], TFriendClass(FFontFile).ToNatural(FFontFile.Widths[lWidthIndex].AdvanceWidth)]));
+  end;
+
 begin
   Writeln('FHHead.numberOfHMetrics = ', FFontFile.HHead.numberOfHMetrics);
   Writeln('Length(Chars[]) = ', Length(FFontFile.Chars));
@@ -47,9 +62,9 @@ begin
   Writeln('  U+0048 (H) = ', Format('%d  (%0:4.4x)', [FFontFile.Chars[$0048]]));
   writeln;
   Writeln('Glyph widths:');
-  Writeln('  3 = ', TFriendClass(FFontFile).ToNatural(FFontFile.Widths[FFontFile.Chars[$0020]].AdvanceWidth));
-  Writeln('  4 = ', TFriendClass(FFontFile).ToNatural(FFontFile.Widths[FFontFile.Chars[$0021]].AdvanceWidth));
-  Writeln('  H = ', TFriendClass(FFontFile).ToNatural(FFontFile.Widths[FFontFile.Chars[$0048]].AdvanceWidth));
+  PrintGlyphWidth($0020);
+  PrintGlyphWidth($0021);
+  PrintGlyphWidth($0048);
 end;
 
 function TMyApplication.GetGlyphIndices(const AText: UnicodeString): TTextMappingList;
@@ -121,6 +136,7 @@ begin
   end;
 
   FFontFile.LoadFromFile(self.GetOptionValue('f'));
+  Writeln('Postscript.IsFixedPitch = ', BoolToStr(FFontFile.PostScript.isFixedPitch > 0, True));
   DumpGlyphIndex;
 
   // test #1

+ 25 - 17
packages/fcl-report/src/fpreport.pp

@@ -1664,7 +1664,7 @@ type
     destructor  Destroy; override;
     class function ReportKindToResultType(const AType: TFPReportFieldKind): TResultType;
     Function StreamToReportElements(aStream : TStream) : TFPObjectList;
-    Procedure Clear;
+    Procedure Clear(ClearData : Boolean = True);
     Procedure SaveDataToNames;
     Procedure RestoreDataFromNames;
     procedure WriteElement(AWriter: TFPReportStreamer; AOriginal: TFPReportElement = nil); override;
@@ -2062,7 +2062,8 @@ type
     property    ShapeType;
     property    Orientation;
     property    CornerRadius;
-    property    Color;
+    property    Color; 
+    property    StretchMode;
   end;
 
 
@@ -4459,14 +4460,11 @@ begin
       FCurTextBlock.FGColor := FLastFGColor;
     if FLastBGColor <> clNone then
       FCurTextBlock.BGColor := FLastBGColor;
+    if (([htBold,htItalic] * FTextBlockState)=[]) then
+      lNewFontName:=Font.Name
+    else
+      lNewFontName:=gTTFontCache.FindPostScriptFontname(Font.Name, htBold in FTextBlockState, htItalic in FTextBlockState);
 
-    lNewFontName := Font.Name;
-    if [htBold, htItalic] <= FTextBlockState then // test if it is a sub-set of FTextBlockState
-      lNewFontName := lNewFontName + '-BoldItalic'
-    else if htBold in FTextBlockState then
-      lNewFontName := lNewFontName + '-Bold'
-    else if htItalic in FTextBlockState then
-      lNewFontName := lNewFontName + '-Italic';
     FCurTextBlock.FontName := lNewFontName;
 
     FCurTextBlock.Width := TextWidth(FCurTextBlock.Text);
@@ -5179,8 +5177,13 @@ begin
 end;
 
 procedure TFPReportCustomShape.RecalcLayout;
+var
+  h: TFPReportUnits;
 begin
-  // Do nothing
+  if StretchMode = smDontStretch then
+    exit;
+  h := Layout.Height;
+  ApplyStretchMode(h);
 end;
 
 procedure TFPReportCustomShape.DoWriteLocalProperties(AWriter: TFPReportStreamer; AOriginal: TFPReportElement);
@@ -8523,7 +8526,7 @@ begin
   inherited Destroy;
 end;
 
-procedure TFPCustomReport.Clear;
+procedure TFPCustomReport.Clear(ClearData : Boolean = True);
 
 Var
   P : TFPReportCustomPage;
@@ -8538,7 +8541,8 @@ begin
   FIsFirstPass := False;
   // Collections
   FreeAndNil(FExpr); // Special case, recreated on run
-  FReportData.Clear;
+  if ClearData then
+    FReportData.Clear;
   if Assigned(FPages) then
     begin
     While PageCount>0 do
@@ -8603,6 +8607,7 @@ begin
     // local properties
     AWriter.WriteString('Title', Title);
     AWriter.WriteString('Author', Author);
+    AWriter.WriteBoolean('TwoPass',TwoPass);
     AWriter.WriteDateTime('DateCreated', DateCreated);
     // now the design-time images
     AWriter.PushElement('Images');
@@ -8671,6 +8676,7 @@ begin
       inherited ReadElement(AReader);
       FTitle := AReader.ReadString('Title', Title);
       FAuthor := AReader.ReadString('Author', Author);
+      FTwoPass := AReader.ReadBoolean('TwoPass',TwoPass);
       FDateCreated := AReader.ReadDateTime('DateCreated', Now);
 
       E := AReader.FindChild('Images');
@@ -10671,6 +10677,8 @@ end;
 procedure TFPReportData.Open;
 
 begin
+  if IsOpened then
+    exit;
   if Assigned(FOnOpen) then
     FOnOpen(Self);
   DoOpen;
@@ -12459,11 +12467,11 @@ begin
   TFPReportDataFooterBand.RegisterElement;
   TFPReportColumnHeaderBand.RegisterElement;
   TFPReportColumnFooterBand.RegisterElement;
-  TFPReportMemo.RegisterElement;
-  TFPReportImage.RegisterElement;
-  TFPReportCheckbox.RegisterElement;
-  TFPReportShape.RegisterElement;
-  TFPReportPage.RegisterElement;
+  TFPReportMemo.RegisterElement.FStandard:=True;
+  TFPReportImage.RegisterElement.FStandard:=True;
+  TFPReportCheckbox.RegisterElement.FStandard:=True;
+  TFPReportShape.RegisterElement.FStandard:=True;
+  TFPReportPage.RegisterElement.FStandard:=True;
 end;
 
 initialization

+ 38 - 4
packages/fcl-report/src/fpreportstreamer.pp

@@ -133,6 +133,7 @@ type
     function    StreamToHex(S: TStream): String;
     function    StreamsEqual(S1, S2: TStream): Boolean;
     function    HexToStringStream(S: String): TStringStream;
+    function    HexToMemoryStream(S: String): TMemoryStream;
     property    JSON: TJSONObject read Fjson write SetJSON;
     Property    OwnsJSON : Boolean Read FOwnsJSON Write SetOwnsJSON;
     property    CurrentElement: TJSONObject read FCurrentElement write SetCurrentElement;
@@ -447,17 +448,17 @@ end;
 function TFPReportJSONStreamer.ReadStream(AName: String; AValue: TStream): Boolean;
 var
   S: string;
-  SS: TStringStream;
+  MS : TMemoryStream;
 begin
   S := ReadString(AName, '');
   Result := (S <> '');
   if Result then
   begin
-    SS := HexToStringStream(S);
+    MS := HexToMemoryStream(S);
     try
-      AValue.CopyFrom(SS, 0);
+      AValue.CopyFrom(MS, 0);
     finally
-      SS.Free;
+      MS.Free();
     end;
   end;
 end;
@@ -698,4 +699,37 @@ begin
   end;
 end;
 
+function TFPReportJSONStreamer.HexToMemoryStream(S: String): TMemoryStream;
+var
+  T: array of Byte;
+  I, J: integer;
+  B: byte;
+  P: PByte;
+  H: string[3];
+begin
+  Result := nil;
+  SetLength(H, 3);
+  H[1] := '$';
+  if (S <> '') then
+  begin
+    SetLength(T, Length(S) div 2);
+    P := @T[0];
+    I := 1;
+    while I < Length(S) do
+    begin
+      H[2] := S[i];
+      Inc(I);
+      H[3] := S[i];
+      Inc(I);
+      Val(H, B, J);
+      if (J = 0) then
+        P^ := B
+      else
+        P^ := 0;
+      Inc(P);
+    end;
+    Result := TBytesStream.Create(T);
+  end;
+end;
+
 end.

+ 2 - 1
packages/rtl-console/fpmake.pp

@@ -78,6 +78,7 @@ begin
         AddInclude('keyscan.inc',AllUnixOSes);
         AddUnit   ('winevent',[win32,win64]);
         AddInclude('nwsys.inc',[netware]);
+        AddUnit   ('mouse',AllUnixOSes);
         AddUnit   ('video',[win16]);
       end;
 
@@ -87,7 +88,7 @@ begin
        AddInclude('mouseh.inc');
        AddInclude('mouse.inc');
        AddUnit   ('winevent',[win32,win64]);
-       AddUnit   ('video',[go32v2,msdos]);
+       AddUnit   ('video',[go32v2,msdos] + AllUnixOSes);
      end;
 
     T:=P.Targets.AddUnit('video.pp',VideoOSes);

+ 2 - 2
packages/rtl-console/src/unix/crt.pp

@@ -14,7 +14,7 @@
 unit Crt;
 
 Interface
-
+{$mode fpc} // Shortstring is assumed
 {$i crth.inc}
 
 Const
@@ -1263,8 +1263,8 @@ Begin
      i:=F.BufPos;
      if i>255 then
       i:=255;
-     Move(F.BufPTR^[idx],Temp[1],i);
      SetLength(Temp,i);
+     Move(F.BufPTR^[idx],Temp[1],i);
      DoWrite(Temp);
      dec(F.BufPos,i);
      inc(idx,i);

+ 32 - 8
packages/rtl-console/src/unix/keyboard.pp

@@ -96,6 +96,12 @@ const KbShiftUp    = $f0;
       KbShiftDown  = $f3;
       KbShiftHome  = $f4;
       KbShiftEnd   = $f5;
+      KbCtrlShiftUp    = $f6;
+      KbCtrlShiftDown  = $f7;
+      KbCtrlShiftRight = $f8;
+      KbCtrlShiftLeft  = $f9;
+      KbCtrlShiftHome  = $fa;
+      KbCtrlShiftEnd   = $fb;
 
       double_esc_hack_enabled : boolean = false;
 
@@ -494,7 +500,7 @@ const
     MouseEvent.buttons := 0;
     PutMouseEvent(MouseEvent);
   end;
-  
+
   procedure GenMouseEvent;
   var MouseEvent: TMouseEvent;
       ch : char;
@@ -869,7 +875,7 @@ type  key_sequence=packed record
         st:string[7];
       end;
 
-const key_sequences:array[0..289] of key_sequence=(
+const key_sequences:array[0..297] of key_sequence=(
        (char:0;scan:kbAltA;st:#27'A'),
        (char:0;scan:kbAltA;st:#27'a'),
        (char:0;scan:kbAltB;st:#27'B'),
@@ -1136,6 +1142,15 @@ const key_sequences:array[0..289] of key_sequence=(
        (char:0;scan:kbShiftHome;st:#27'[1;2H'),  {xterm}
        (char:0;scan:kbShiftHome;st:#27'[7$'),    {rxvt}
 
+       (char:0;scan:KbCtrlShiftUp;st:#27'[1;6A'),    {xterm}
+       (char:0;scan:KbCtrlShiftDown;st:#27'[1;6B'),  {xterm}
+       (char:0;scan:KbCtrlShiftRight;st:#27'[1;6C'), {xterm, xfce4}
+       (char:0;scan:KbCtrlShiftLeft;st:#27'[1;6D'),  {xterm, xfce4}
+       (char:0;scan:KbCtrlShiftHome;st:#27'[1;6H'),  {xterm}
+       (char:0;scan:KbCtrlShiftEnd;st:#27'[1;6F'),   {xterm}
+
+       (char:0;scan:kbCtrlPgDn;st:#27'[6;5~'),   {xterm}
+       (char:0;scan:kbCtrlPgUp;st:#27'[5;5~'),   {xterm}
        (char:0;scan:kbCtrlUp;st:#27'[1;5A'),     {xterm}
        (char:0;scan:kbCtrlDown;st:#27'[1;5B'),   {xterm}
        (char:0;scan:kbCtrlRight;st:#27'[1;5C'),  {xterm}
@@ -1304,7 +1319,7 @@ begin
               {This is the same hack as in findsequence; see findsequence for
                explanation.}
               ch:=ttyrecvchar;
-              {Alt+O cannot be used in this situation, it can be a function key.} 
+              {Alt+O cannot be used in this situation, it can be a function key.}
               if not(ch in ['a'..'z','A'..'N','P'..'Z','0'..'9','-','+','_','=']) then
                 begin
                   if intail=0 then
@@ -1361,11 +1376,11 @@ begin
         end
       else
         RestoreArray;
-   end
+   end;
 {$ifdef logging}
        writeln(f);
 {$endif logging}
-    ;
+
   ReadKey:=PopKey;
 End;
 
@@ -1541,6 +1556,8 @@ const
     kbAltDown,kbAltPgDn,kbAltIns,kbAltDel);
   ShiftArrow : array [kbShiftUp..kbShiftEnd] of byte =
    (kbUp,kbLeft,kbRight,kbDown,kbHome,kbEnd);
+  CtrlShiftArrow : array [kbCtrlShiftUp..kbCtrlShiftEnd] of byte =
+   (kbCtrlUp,kbCtrlDown,kbCtrlRight,kbCtrlLeft,kbCtrlHome,kbCtrlEnd);
 
 var
   MyScan:byte;
@@ -1601,10 +1618,17 @@ begin {main}
             kbF11..KbF12 : { sF11-sF12 }
               MyScan:=MyScan+kbShiftF11-kbF11;
           end;
-        if myscan in [kbShiftUp..kbShiftEnd] then
+        if myscan in [kbShiftUp..kbCtrlShiftEnd] then
           begin
-            myscan:=ShiftArrow[myscan];
-            sstate:=sstate or kbshift;
+            if myscan <= kbShiftEnd then
+            begin
+               myscan:=ShiftArrow[myscan];
+               sstate:=sstate or kbshift;
+            end else
+            begin
+               myscan:=CtrlShiftArrow[myscan];
+               sstate:=sstate or kbshift or kbCtrl;
+            end;
           end;
         if myscan=kbAltBack then
           sstate:=sstate or kbalt;

+ 1 - 1
packages/rtl-console/src/win/keyboard.pp

@@ -637,7 +637,7 @@ CONST
    (n : $00; s : $00; c : $00; a: $81),      {0B 0 }
    (n : $00; s : $00; c : $00; a: $82),      {0C ß }
    (n : $00; s : $00; c : $00; a: $00),      {0D}
-   (n : $00; s : $09; c : $00; a: $00),      {0E Backspace}
+   (n : $00; s : $00; c : $00; a: $00),      {0E Backspace}
    (n : $00; s : $0F; c : $94; a: $00));     {0F Tab }