Repo for the search and displace ingest module that takes odf, docx and pdf and transforms it into .md to be used with search and displace operations
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

685 lines
20 KiB

3 years ago
  1. {
  2. Vampyre Imaging Library
  3. by Marek Mauder
  4. http://imaginglib.sourceforge.net
  5. The contents of this file are used with permission, subject to the Mozilla
  6. Public License Version 1.1 (the "License"); you may not use this file except
  7. in compliance with the License. You may obtain a copy of the License at
  8. http://www.mozilla.org/MPL/MPL-1.1.html
  9. Software distributed under the License is distributed on an "AS IS" basis,
  10. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
  11. the specific language governing rights and limitations under the License.
  12. Alternatively, the contents of this file may be used under the terms of the
  13. GNU Lesser General Public License (the "LGPL License"), in which case the
  14. provisions of the LGPL License are applicable instead of those above.
  15. If you wish to allow use of your version of this file only under the terms
  16. of the LGPL License and not to allow others to use your version of this file
  17. under the MPL, indicate your decision by deleting the provisions above and
  18. replace them with the notice and other provisions required by the LGPL
  19. License. If you do not delete the provisions above, a recipient may use
  20. your version of this file under either the MPL or the LGPL License.
  21. For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html
  22. }
  23. { This unit contains default IO functions for reading from/writting to
  24. files, streams and memory.}
  25. unit ImagingIO;
  26. {$I ImagingOptions.inc}
  27. interface
  28. uses
  29. SysUtils, Classes, ImagingTypes, Imaging, ImagingUtility;
  30. type
  31. TMemoryIORec = record
  32. Data: ImagingUtility.PByteArray;
  33. Position: LongInt;
  34. Size: LongInt;
  35. end;
  36. PMemoryIORec = ^TMemoryIORec;
  37. var
  38. OriginalFileIO: TIOFunctions;
  39. FileIO: TIOFunctions;
  40. StreamIO: TIOFunctions;
  41. MemoryIO: TIOFunctions;
  42. { Helper function that returns size of input (from current position to the end)
  43. represented by Handle (and opened and operated on by members of IOFunctions).}
  44. function GetInputSize(const IOFunctions: TIOFunctions; Handle: TImagingHandle): Int64;
  45. { Helper function that initializes TMemoryIORec with given params.}
  46. function PrepareMemIO(Data: Pointer; Size: LongInt): TMemoryIORec;
  47. { Reads one text line from input (CR+LF, CR, or LF as line delimiter).}
  48. function ReadLine(const IOFunctions: TIOFunctions; Handle: TImagingHandle;
  49. out Line: AnsiString; FailOnControlChars: Boolean = False): Boolean;
  50. { Writes one text line to input with optional line delimiter.}
  51. procedure WriteLine(const IOFunctions: TIOFunctions; Handle: TImagingHandle;
  52. const Line: AnsiString; const LineEnding: AnsiString = sLineBreak);
  53. type
  54. TReadMemoryStream = class(TCustomMemoryStream)
  55. public
  56. constructor Create(Data: Pointer; Size: Integer);
  57. class function CreateFromIOHandle(const IOFunctions: TIOFunctions; Handle: TImagingHandle): TReadMemoryStream;
  58. end;
  59. TImagingIOStream = class(TStream)
  60. private
  61. FIO: TIOFunctions;
  62. FHandle: TImagingHandle;
  63. public
  64. constructor Create(const IOFunctions: TIOFunctions; Handle: TImagingHandle);
  65. end;
  66. implementation
  67. const
  68. DefaultBufferSize = 16 * 1024;
  69. type
  70. { Based on TaaBufferedStream
  71. Copyright (c) Julian M Bucknall 1997, 1999 }
  72. TBufferedStream = class
  73. private
  74. FBuffer: PByteArray;
  75. FBufSize: Integer;
  76. FBufStart: Integer;
  77. FBufPos: Integer;
  78. FBytesInBuf: Integer;
  79. FSize: Integer;
  80. FDirty: Boolean;
  81. FStream: TStream;
  82. function GetPosition: Integer;
  83. function GetSize: Integer;
  84. procedure ReadBuffer;
  85. procedure WriteBuffer;
  86. procedure SetPosition(const Value: Integer);
  87. public
  88. constructor Create(AStream: TStream);
  89. destructor Destroy; override;
  90. function Read(var Buffer; Count: Integer): Integer;
  91. function Write(const Buffer; Count: Integer): Integer;
  92. function Seek(Offset: Integer; Origin: Word): Integer;
  93. procedure Commit;
  94. property Stream: TStream read FStream;
  95. property Position: Integer read GetPosition write SetPosition;
  96. property Size: Integer read GetSize;
  97. end;
  98. constructor TBufferedStream.Create(AStream: TStream);
  99. begin
  100. inherited Create;
  101. FStream := AStream;
  102. FBufSize := DefaultBufferSize;
  103. GetMem(FBuffer, FBufSize);
  104. FBufPos := 0;
  105. FBytesInBuf := 0;
  106. FBufStart := 0;
  107. FDirty := False;
  108. FSize := AStream.Size;
  109. end;
  110. destructor TBufferedStream.Destroy;
  111. begin
  112. if FBuffer <> nil then
  113. begin
  114. Commit;
  115. FreeMem(FBuffer);
  116. end;
  117. FStream.Position := Position; // Make sure source stream has right position
  118. inherited Destroy;
  119. end;
  120. function TBufferedStream.GetPosition: Integer;
  121. begin
  122. Result := FBufStart + FBufPos;
  123. end;
  124. procedure TBufferedStream.SetPosition(const Value: Integer);
  125. begin
  126. Seek(Value, soFromCurrent);
  127. end;
  128. function TBufferedStream.GetSize: Integer;
  129. begin
  130. Result := FSize;
  131. end;
  132. procedure TBufferedStream.ReadBuffer;
  133. var
  134. SeekResult: Integer;
  135. begin
  136. SeekResult := FStream.Seek(FBufStart, 0);
  137. if SeekResult = -1 then
  138. raise Exception.Create('TBufferedStream.ReadBuffer: seek failed');
  139. FBytesInBuf := FStream.Read(FBuffer^, FBufSize);
  140. if FBytesInBuf <= 0 then
  141. raise Exception.Create('TBufferedStream.ReadBuffer: read failed');
  142. end;
  143. procedure TBufferedStream.WriteBuffer;
  144. var
  145. SeekResult: Integer;
  146. BytesWritten: Integer;
  147. begin
  148. SeekResult := FStream.Seek(FBufStart, 0);
  149. if SeekResult = -1 then
  150. raise Exception.Create('TBufferedStream.WriteBuffer: seek failed');
  151. BytesWritten := FStream.Write(FBuffer^, FBytesInBuf);
  152. if BytesWritten <> FBytesInBuf then
  153. raise Exception.Create('TBufferedStream.WriteBuffer: write failed');
  154. end;
  155. procedure TBufferedStream.Commit;
  156. begin
  157. if FDirty then
  158. begin
  159. WriteBuffer;
  160. FDirty := False;
  161. end;
  162. end;
  163. function TBufferedStream.Read(var Buffer; Count: Integer): Integer;
  164. var
  165. BufAsBytes : TByteArray absolute Buffer;
  166. BufIdx, BytesToGo, BytesToRead: Integer;
  167. begin
  168. // Calculate the actual number of bytes we can read - this depends on
  169. // the current position and size of the stream as well as the number
  170. // of bytes requested.
  171. BytesToGo := Count;
  172. if FSize < (FBufStart + FBufPos + Count) then
  173. BytesToGo := FSize - (FBufStart + FBufPos);
  174. if BytesToGo <= 0 then
  175. begin
  176. Result := 0;
  177. Exit;
  178. end;
  179. // Remember to return the result of our calculation
  180. Result := BytesToGo;
  181. BufIdx := 0;
  182. if FBytesInBuf = 0 then
  183. ReadBuffer;
  184. // Calculate the number of bytes we can read prior to the loop
  185. BytesToRead := FBytesInBuf - FBufPos;
  186. if BytesToRead > BytesToGo then
  187. BytesToRead := BytesToGo;
  188. // Copy from the stream buffer to the caller's buffer
  189. Move(FBuffer^[FBufPos], BufAsBytes[BufIdx], BytesToRead);
  190. // Calculate the number of bytes still to read}
  191. Dec(BytesToGo, BytesToRead);
  192. // while we have bytes to read, read them
  193. while BytesToGo > 0 do
  194. begin
  195. Inc(BufIdx, BytesToRead);
  196. // As we've exhausted this buffer-full, advance to the next, check
  197. // to see whether we need to write the buffer out first
  198. if FDirty then
  199. begin
  200. WriteBuffer;
  201. FDirty := false;
  202. end;
  203. Inc(FBufStart, FBufSize);
  204. FBufPos := 0;
  205. ReadBuffer;
  206. // Calculate the number of bytes we can read in this cycle
  207. BytesToRead := FBytesInBuf;
  208. if BytesToRead > BytesToGo then
  209. BytesToRead := BytesToGo;
  210. // Ccopy from the stream buffer to the caller's buffer
  211. Move(FBuffer^, BufAsBytes[BufIdx], BytesToRead);
  212. // Calculate the number of bytes still to read
  213. Dec(BytesToGo, BytesToRead);
  214. end;
  215. // Remember our new position
  216. Inc(FBufPos, BytesToRead);
  217. if FBufPos = FBufSize then
  218. begin
  219. Inc(FBufStart, FBufSize);
  220. FBufPos := 0;
  221. FBytesInBuf := 0;
  222. end;
  223. end;
  224. function TBufferedStream.Seek(Offset: Integer; Origin: Word): Integer;
  225. var
  226. NewBufStart, NewPos: Integer;
  227. begin
  228. // Calculate the new position
  229. case Origin of
  230. soFromBeginning : NewPos := Offset;
  231. soFromCurrent : NewPos := FBufStart + FBufPos + Offset;
  232. soFromEnd : NewPos := FSize + Offset;
  233. else
  234. raise Exception.Create('TBufferedStream.Seek: invalid origin');
  235. end;
  236. if (NewPos < 0) or (NewPos > FSize) then
  237. begin
  238. //NewPos := ClampInt(NewPos, 0, FSize); don't do this - for writing
  239. end;
  240. // Calculate which page of the file we need to be at
  241. NewBufStart := NewPos and not Pred(FBufSize);
  242. // If the new page is different than the old, mark the buffer as being
  243. // ready to be replenished, and if need be write out any dirty data
  244. if NewBufStart <> FBufStart then
  245. begin
  246. if FDirty then
  247. begin
  248. WriteBuffer;
  249. FDirty := False;
  250. end;
  251. FBufStart := NewBufStart;
  252. FBytesInBuf := 0;
  253. end;
  254. // Save the new position
  255. FBufPos := NewPos - NewBufStart;
  256. Result := NewPos;
  257. end;
  258. function TBufferedStream.Write(const Buffer; Count: Integer): Integer;
  259. var
  260. BufAsBytes: TByteArray absolute Buffer;
  261. BufIdx, BytesToGo, BytesToWrite: Integer;
  262. begin
  263. // When we write to this stream we always assume that we can write the
  264. // requested number of bytes: if we can't (eg, the disk is full) we'll
  265. // get an exception somewhere eventually.
  266. BytesToGo := Count;
  267. // Remember to return the result of our calculation
  268. Result := BytesToGo;
  269. BufIdx := 0;
  270. if (FBytesInBuf = 0) and (FSize > FBufStart) then
  271. ReadBuffer;
  272. // Calculate the number of bytes we can write prior to the loop
  273. BytesToWrite := FBufSize - FBufPos;
  274. if BytesToWrite > BytesToGo then
  275. BytesToWrite := BytesToGo;
  276. // Copy from the caller's buffer to the stream buffer
  277. Move(BufAsBytes[BufIdx], FBuffer^[FBufPos], BytesToWrite);
  278. // Mark our stream buffer as requiring a save to the actual stream,
  279. // note that this will suffice for the rest of the routine as well: no
  280. // inner routine will turn off the dirty flag.
  281. FDirty := True;
  282. // Calculate the number of bytes still to write
  283. Dec(BytesToGo, BytesToWrite);
  284. // While we have bytes to write, write them
  285. while BytesToGo > 0 do
  286. begin
  287. Inc(BufIdx, BytesToWrite);
  288. // As we've filled this buffer, write it out to the actual stream
  289. // and advance to the next buffer, reading it if required
  290. FBytesInBuf := FBufSize;
  291. WriteBuffer;
  292. Inc(FBufStart, FBufSize);
  293. FBufPos := 0;
  294. FBytesInBuf := 0;
  295. if FSize > FBufStart then
  296. ReadBuffer;
  297. // Calculate the number of bytes we can write in this cycle
  298. BytesToWrite := FBufSize;
  299. if BytesToWrite > BytesToGo then
  300. BytesToWrite := BytesToGo;
  301. // Copy from the caller's buffer to our buffer
  302. Move(BufAsBytes[BufIdx], FBuffer^, BytesToWrite);
  303. // Calculate the number of bytes still to write
  304. Dec(BytesToGo, BytesToWrite);
  305. end;
  306. // Remember our new position
  307. Inc(FBufPos, BytesToWrite);
  308. // Make sure the count of valid bytes is correct
  309. if FBytesInBuf < FBufPos then
  310. FBytesInBuf := FBufPos;
  311. // Make sure the stream size is correct
  312. if FSize < (FBufStart + FBytesInBuf) then
  313. FSize := FBufStart + FBytesInBuf;
  314. // If we're at the end of the buffer, write it out and advance to the
  315. // start of the next page
  316. if FBufPos = FBufSize then
  317. begin
  318. WriteBuffer;
  319. FDirty := False;
  320. Inc(FBufStart, FBufSize);
  321. FBufPos := 0;
  322. FBytesInBuf := 0;
  323. end;
  324. end;
  325. { File IO functions }
  326. function FileOpen(FileName: PChar; Mode: TOpenMode): TImagingHandle; cdecl;
  327. var
  328. Stream: TStream;
  329. begin
  330. Stream := nil;
  331. case Mode of
  332. omReadOnly: Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
  333. omCreate: Stream := TFileStream.Create(FileName, fmCreate);
  334. omReadWrite:
  335. begin
  336. if FileExists(FileName) then
  337. Stream := TFileStream.Create(FileName, fmOpenReadWrite or fmShareExclusive)
  338. else
  339. Stream := TFileStream.Create(FileName, fmCreate);
  340. end;
  341. end;
  342. Assert(Stream <> nil);
  343. Result := TBufferedStream.Create(Stream);
  344. end;
  345. procedure FileClose(Handle: TImagingHandle); cdecl;
  346. var
  347. Stream: TStream;
  348. begin
  349. Stream := TBufferedStream(Handle).Stream;
  350. TBufferedStream(Handle).Free;
  351. Stream.Free;
  352. end;
  353. function FileEof(Handle: TImagingHandle): Boolean; cdecl;
  354. begin
  355. Result := TBufferedStream(Handle).Position = TBufferedStream(Handle).Size;
  356. end;
  357. function FileSeek(Handle: TImagingHandle; Offset: Int64; Mode: TSeekMode): Int64; cdecl;
  358. begin
  359. Result := TBufferedStream(Handle).Seek(Offset, LongInt(Mode));
  360. end;
  361. function FileTell(Handle: TImagingHandle): Int64; cdecl;
  362. begin
  363. Result := TBufferedStream(Handle).Position;
  364. end;
  365. function FileRead(Handle: TImagingHandle; Buffer: Pointer; Count: LongInt): LongInt; cdecl;
  366. begin
  367. Result := TBufferedStream(Handle).Read(Buffer^, Count);
  368. end;
  369. function FileWrite(Handle: TImagingHandle; Buffer: Pointer; Count: LongInt): LongInt; cdecl;
  370. begin
  371. Result := TBufferedStream(Handle).Write(Buffer^, Count);
  372. end;
  373. { Stream IO functions }
  374. function StreamOpen(FileName: PChar; Mode: TOpenMode): TImagingHandle; cdecl;
  375. begin
  376. Result := FileName;
  377. end;
  378. procedure StreamClose(Handle: TImagingHandle); cdecl;
  379. begin
  380. end;
  381. function StreamEof(Handle: TImagingHandle): Boolean; cdecl;
  382. begin
  383. Result := TStream(Handle).Position = TStream(Handle).Size;
  384. end;
  385. function StreamSeek(Handle: TImagingHandle; Offset: Int64; Mode: TSeekMode): Int64; cdecl;
  386. begin
  387. Result := TStream(Handle).Seek(Offset, LongInt(Mode));
  388. end;
  389. function StreamTell(Handle: TImagingHandle): Int64; cdecl;
  390. begin
  391. Result := TStream(Handle).Position;
  392. end;
  393. function StreamRead(Handle: TImagingHandle; Buffer: Pointer; Count: LongInt):
  394. LongInt; cdecl;
  395. begin
  396. Result := TStream(Handle).Read(Buffer^, Count);
  397. end;
  398. function StreamWrite(Handle: TImagingHandle; Buffer: Pointer; Count: LongInt): LongInt; cdecl;
  399. begin
  400. Result := TStream(Handle).Write(Buffer^, Count);
  401. end;
  402. { Memory IO functions }
  403. function MemoryOpen(FileName: PChar; Mode: TOpenMode): TImagingHandle; cdecl;
  404. begin
  405. Result := FileName;
  406. end;
  407. procedure MemoryClose(Handle: TImagingHandle); cdecl;
  408. begin
  409. end;
  410. function MemoryEof(Handle: TImagingHandle): Boolean; cdecl;
  411. begin
  412. Result := PMemoryIORec(Handle).Position = PMemoryIORec(Handle).Size;
  413. end;
  414. function MemorySeek(Handle: TImagingHandle; Offset: Int64; Mode: TSeekMode): Int64; cdecl;
  415. begin
  416. Result := PMemoryIORec(Handle).Position;
  417. case Mode of
  418. smFromBeginning: Result := Offset;
  419. smFromCurrent: Result := PMemoryIORec(Handle).Position + Offset;
  420. smFromEnd: Result := PMemoryIORec(Handle).Size + Offset;
  421. end;
  422. //Result := ClampInt(Result, 0, PMemoryIORec(Handle).Size); don't do this - some file formats use it
  423. PMemoryIORec(Handle).Position := Result;
  424. end;
  425. function MemoryTell(Handle: TImagingHandle): Int64; cdecl;
  426. begin
  427. Result := PMemoryIORec(Handle).Position;
  428. end;
  429. function MemoryRead(Handle: TImagingHandle; Buffer: Pointer; Count: LongInt):
  430. LongInt; cdecl;
  431. var
  432. Rec: PMemoryIORec;
  433. begin
  434. Rec := PMemoryIORec(Handle);
  435. Result := Count;
  436. if Rec.Position + Count > Rec.Size then
  437. Result := Rec.Size - Rec.Position;
  438. Move(Rec.Data[Rec.Position], Buffer^, Result);
  439. Rec.Position := Rec.Position + Result;
  440. end;
  441. function MemoryWrite(Handle: TImagingHandle; Buffer: Pointer; Count: LongInt): LongInt; cdecl;
  442. var
  443. Rec: PMemoryIORec;
  444. begin
  445. Rec := PMemoryIORec(Handle);
  446. Result := Count;
  447. if Rec.Position + Count > Rec.Size then
  448. Result := Rec.Size - Rec.Position;
  449. Move(Buffer^, Rec.Data[Rec.Position], Result);
  450. Rec.Position := Rec.Position + Result;
  451. end;
  452. { Helper IO functions }
  453. function GetInputSize(const IOFunctions: TIOFunctions; Handle: TImagingHandle): Int64;
  454. var
  455. OldPos: Int64;
  456. begin
  457. OldPos := IOFunctions.Tell(Handle);
  458. IOFunctions.Seek(Handle, 0, smFromEnd);
  459. Result := IOFunctions.Tell(Handle);
  460. IOFunctions.Seek(Handle, OldPos, smFromBeginning);
  461. end;
  462. function PrepareMemIO(Data: Pointer; Size: LongInt): TMemoryIORec;
  463. begin
  464. Result.Data := Data;
  465. Result.Position := 0;
  466. Result.Size := Size;
  467. end;
  468. function ReadLine(const IOFunctions: TIOFunctions; Handle: TImagingHandle;
  469. out Line: AnsiString; FailOnControlChars: Boolean): Boolean;
  470. const
  471. MaxLine = 1024;
  472. var
  473. EolPos, Pos: Integer;
  474. C: AnsiChar;
  475. EolReached: Boolean;
  476. Endings: set of AnsiChar;
  477. begin
  478. Line := '';
  479. Pos := 0;
  480. EolPos := 0;
  481. EolReached := False;
  482. Endings := [#10, #13];
  483. Result := True;
  484. while not IOFunctions.Eof(Handle) do
  485. begin
  486. IOFunctions.Read(Handle, @C, SizeOf(C));
  487. if FailOnControlChars and (Byte(C) < $20) then
  488. begin
  489. Break;
  490. end;
  491. if not (C in Endings) then
  492. begin
  493. if EolReached then
  494. begin
  495. IOFunctions.Seek(Handle, EolPos, smFromBeginning);
  496. Exit;
  497. end
  498. else
  499. begin
  500. SetLength(Line, Length(Line) + 1);
  501. Line[Length(Line)] := C;
  502. end;
  503. end
  504. else if not EolReached then
  505. begin
  506. EolReached := True;
  507. EolPos := IOFunctions.Tell(Handle);
  508. end;
  509. Inc(Pos);
  510. if Pos >= MaxLine then
  511. begin
  512. Break;
  513. end;
  514. end;
  515. Result := False;
  516. IOFunctions.Seek(Handle, -Pos, smFromCurrent);
  517. end;
  518. procedure WriteLine(const IOFunctions: TIOFunctions; Handle: TImagingHandle;
  519. const Line: AnsiString; const LineEnding: AnsiString);
  520. var
  521. ToWrite: AnsiString;
  522. begin
  523. ToWrite := Line + LineEnding;
  524. IOFunctions.Write(Handle, @ToWrite[1], Length(ToWrite));
  525. end;
  526. { TReadMemoryStream }
  527. constructor TReadMemoryStream.Create(Data: Pointer; Size: Integer);
  528. begin
  529. SetPointer(Data, Size);
  530. end;
  531. class function TReadMemoryStream.CreateFromIOHandle(const IOFunctions: TIOFunctions; Handle: TImagingHandle): TReadMemoryStream;
  532. var
  533. Data: Pointer;
  534. Size: Integer;
  535. begin
  536. Size := GetInputSize(IOFunctions, Handle);
  537. GetMem(Data, Size);
  538. IOFunctions.Read(Handle, Data, Size);
  539. Result := TReadMemoryStream.Create(Data, Size);
  540. end;
  541. { TImagingIOStream }
  542. constructor TImagingIOStream.Create(const IOFunctions: TIOFunctions;
  543. Handle: TImagingHandle);
  544. begin
  545. end;
  546. initialization
  547. OriginalFileIO.Open := FileOpen;
  548. OriginalFileIO.Close := FileClose;
  549. OriginalFileIO.Eof := FileEof;
  550. OriginalFileIO.Seek := FileSeek;
  551. OriginalFileIO.Tell := FileTell;
  552. OriginalFileIO.Read := FileRead;
  553. OriginalFileIO.Write := FileWrite;
  554. StreamIO.Open := StreamOpen;
  555. StreamIO.Close := StreamClose;
  556. StreamIO.Eof := StreamEof;
  557. StreamIO.Seek := StreamSeek;
  558. StreamIO.Tell := StreamTell;
  559. StreamIO.Read := StreamRead;
  560. StreamIO.Write := StreamWrite;
  561. MemoryIO.Open := MemoryOpen;
  562. MemoryIO.Close := MemoryClose;
  563. MemoryIO.Eof := MemoryEof;
  564. MemoryIO.Seek := MemorySeek;
  565. MemoryIO.Tell := MemoryTell;
  566. MemoryIO.Read := MemoryRead;
  567. MemoryIO.Write := MemoryWrite;
  568. ResetFileIO;
  569. {
  570. File Notes:
  571. -- TODOS ----------------------------------------------------
  572. - nothing now
  573. -- 0.77.3 ---------------------------------------------------
  574. - IO functions now have 64bit sizes and offsets.
  575. - Added helper classes TReadMemoryStream and TImagingIOStream.
  576. -- 0.77.1 ---------------------------------------------------
  577. - Updated IO Open functions according to changes in ImagingTypes.
  578. - Added ReadLine and WriteLine functions.
  579. -- 0.23 Changes/Bug Fixes -----------------------------------
  580. - Added merge between buffered read-only and write-only file
  581. stream adapters - TIFF saving needed both reading and writing.
  582. - Fixed bug causing wrong value of TBufferedWriteFile.Size
  583. (needed to add buffer pos to size).
  584. -- 0.21 Changes/Bug Fixes -----------------------------------
  585. - Removed TMemoryIORec.Written, use Position to get proper memory
  586. position (Written didn't take Seeks into account).
  587. - Added TBufferedReadFile and TBufferedWriteFile classes for
  588. buffered file reading/writting. File IO functions now use these
  589. classes resulting in performance increase mainly in file formats
  590. that read/write many small chunks.
  591. - Added fmShareDenyWrite to FileOpenRead. You can now read
  592. files opened for reading by Imaging from other apps.
  593. - Added GetInputSize and PrepareMemIO helper functions.
  594. -- 0.19 Changes/Bug Fixes -----------------------------------
  595. - changed behaviour of MemorySeek to act as TStream
  596. based Seeks
  597. }
  598. end.