Search code examples
delphibitmapencodedelphi-xe7

Convert BitMap to string without line breaks?


Somewhere I found this code to convert a BitMap to a string:

function Base64FromBitmap(Bitmap: TBitmap): string;
var
  Input: TBytesStream;
  Output: TStringStream;
begin
  Input := TBytesStream.Create;
  try
    Bitmap.SaveToStream(Input);
    Input.Position := 0;
    Output := TStringStream.Create('', TEncoding.ASCII);
    try
      Soap.EncdDecd.EncodeStream(Input, Output);
      Result := Output.DataString;
    finally
      Output.Free;
    end;
  finally
    Input.Free;
  end;
end;

However, this gives back a block of wrapped lines. Is it possible to get one single line without line breaks?


Solution

  • XE7 introduces the System.NetEncoding unit which contains a base64 encoder. This is intended to be used rather than the encoder from Soap.EncdDecd.

    The constructor of TBase64Encoding has overloads that allow you to specify characters per line (and line break text). Unfortunately Embarcadero haven't deigned to provide documentation yet so we need to read the source to work out what exactly to pass. Having done that, it becomes clear that line breaks are inserted only if the number of characters per line is strictly positive. So create an encoding object like this:

    Encoding := TBase64Encoding.Create(0);
    

    Once you have a suitable encoding object use Encode to encode your stream of bytes to text. And Decode to reverse the process.

    That could look like this:

    function Base64FromBitmap(Bitmap: TBitmap): string;
    var
      Input: TBytesStream;
      Output: TStringStream;
      Encoding: TBase64Encoding;
    begin
      Input := TBytesStream.Create;
      try
        Bitmap.SaveToStream(Input);
        Input.Position := 0;
        Output := TStringStream.Create('', TEncoding.ASCII);
        try
          Encoding := TBase64Encoding.Create(0);
          try
            Encoding.Encode(Input, Output);
            Result := Output.DataString;
          finally
            Encoding.Free;
          end;
        finally
          Output.Free;
        end;
      finally
        Input.Free;
      end;
    end;
    
    procedure BitmapFromBase64(Base64: string; Bitmap: TBitmap);
    var
      Input: TStringStream;
      Output: TBytesStream;
      Encoding: TBase64Encoding;
    begin
      Input := TStringStream.Create(Base64, TEncoding.ASCII);
      try
        Output := TBytesStream.Create;
        try
          Encoding := TBase64Encoding.Create(0);
          try
            Encoding.Decode(Input, Output);
            Output.Position := 0;
            Bitmap.LoadFromStream(Output);
          finally
            Encoding.Free;
          end;
        finally
          Output.Free;
        end;
      finally
        Input.Free;
      end;
    end;
    

    Alternatively you can use EncodeBytesToString and DecodeStringToBytes. Which might look like this:

    function Base64FromBitmap(Bitmap: TBitmap): string;
    var
      Stream: TBytesStream;
      Encoding: TBase64Encoding;
    begin
      Stream := TBytesStream.Create;
      try
        Bitmap.SaveToStream(Stream);
        Encoding := TBase64Encoding.Create(0);
        try
          Result := Encoding.EncodeBytesToString(Copy(Stream.Bytes, 0, Stream.Size));
        finally
          Encoding.Free;
        end;
      finally
        Stream.Free;
      end;
    end;
    
    procedure BitmapFromBase64(Base64: string; Bitmap: TBitmap);
    var
      Stream: TBytesStream;
      Bytes: TBytes;
      Encoding: TBase64Encoding;
    begin
      Stream := TBytesStream.Create;
      try
        Encoding := TBase64Encoding.Create(0);
        try
          Bytes := Encoding.DecodeStringToBytes(Base64);
          Stream.WriteData(Bytes, Length(Bytes));
          Stream.Position := 0;
          Bitmap.LoadFromStream(Stream);
        finally
          Encoding.Free;
        end;
      finally
        Stream.Free;
      end;
    end;
    

    What would be really good would be if we could have something like the compression and decompression streams that are available with zlib. We could create an encoding stream, save the bitmap to it, and read out the base64 text. In the other direction we'd feed base64 text into a decoding stream, and then load the bitmap from it. This should really be part of the NetEncoding unit, but it would be simple enough to add as a class helper.