diff --git a/Source/SharpFont/FTStream.cs b/Source/SharpFont/FTStream.cs index 927c6e0..abb0f9d 100644 --- a/Source/SharpFont/FTStream.cs +++ b/Source/SharpFont/FTStream.cs @@ -43,14 +43,14 @@ namespace SharpFont /// The number of bytes effectively read by the stream. [CLSCompliant(false)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate uint StreamIOFunc(NativeReference stream, uint offset, IntPtr buffer, uint count); + public delegate uint StreamIOFunc(IntPtr stream, uint offset, IntPtr buffer, uint count); /// /// A function used to close a given input stream. /// /// A handle to the target stream. [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void StreamCloseFunc(NativeReference stream); + public delegate void StreamCloseFunc(IntPtr stream); /// /// A handle to an input stream. @@ -84,7 +84,7 @@ public IntPtr Base return rec.@base; } } - + /// /// Gets the stream size in bytes. /// diff --git a/Source/SharpFont/Face.cs b/Source/SharpFont/Face.cs index fea801b..01b78ea 100644 --- a/Source/SharpFont/Face.cs +++ b/Source/SharpFont/Face.cs @@ -24,6 +24,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE using System; using System.Collections.Generic; +using System.IO; using System.Runtime.InteropServices; using SharpFont.Bdf; @@ -53,6 +54,12 @@ public sealed class Face : NativeObject, IDisposable private Library parentLibrary; private List childSizes; + GCHandle customStreamHandle; + IntPtr customStreamPtr; + private IntPtr openArgsPtr; + private StreamIOFunc streamIOFunc; + private StreamCloseFunc streamCloseFunc; + #endregion #region Constructors @@ -67,6 +74,8 @@ public Face(Library library, string path) { } + + /// /// Initializes a new instance of the class. /// @@ -85,7 +94,85 @@ public Face(Library library, string path, int faceIndex) Reference = reference; } - //TODO make an overload with a FileStream instead of a byte[] + + /// + /// Initializes a new instance of the class. + /// + /// The parent library. + /// The stream of the font file. + /// The index of the face to take from the file. + /// The stream is automatically disposed with the face. + public Face(Library library, Stream stream, int faceIndex, bool takeStreamOwnership) + : this(library, new StreamAccessor(stream, takeStreamOwnership), faceIndex) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The parent library. + /// Custom object for handling stream access to the font file. + /// The index of the face to take from the file. + public Face(Library library, ICustomStreamAccessor streamAccessor, int faceIndex) + : this(library) + { + IntPtr reference; + + streamIOFunc = new StreamIOFunc(StreamIOFunc); + streamCloseFunc = new StreamCloseFunc(StreamCloseFunc); + + customStreamHandle = GCHandle.Alloc(streamAccessor); + + StreamRec streamRec = new StreamRec(); + streamRec.size = (UIntPtr)streamAccessor.Length; + streamRec.descriptor.pointer = GCHandle.ToIntPtr(customStreamHandle); + streamRec.read = streamIOFunc; + streamRec.close = streamCloseFunc; + + customStreamPtr = Marshal.AllocHGlobal(Marshal.SizeOf(streamRec)); + Marshal.StructureToPtr(streamRec, customStreamPtr, false); + + + OpenArgsRec openArgs = new OpenArgsRec(); + openArgs.flags = OpenFlags.Stream; + openArgs.stream = customStreamPtr; + + openArgsPtr = Marshal.AllocHGlobal(Marshal.SizeOf(openArgs)); + Marshal.StructureToPtr(openArgs, openArgsPtr, false); + + + Error err = FT.FT_Open_Face(library.Reference, openArgsPtr, faceIndex, out reference); + if (err != Error.Ok) + throw new FreeTypeException(err); + + Reference = reference; + } + + private uint StreamIOFunc(IntPtr streamPtr, uint offset, IntPtr buffer, uint count) + { + FTStream ftStream = new FTStream(streamPtr); + ICustomStreamAccessor streamAccessor = ((GCHandle)ftStream.Descriptor.Pointer).Target as ICustomStreamAccessor; + if (streamAccessor != null) + { + streamAccessor.Seek((int)offset); + if (count > 0) + { + byte[] readbuffer = new byte[count]; + int bytesread = streamAccessor.Read(readbuffer); + Marshal.Copy(readbuffer, 0, buffer, (int)count); + return (uint)bytesread; + } + } + return 0; + } + + private void StreamCloseFunc(IntPtr streamPtr) + { + FTStream ftStream = new FTStream(streamPtr); + ICustomStreamAccessor streamAccessor = ((GCHandle)ftStream.Descriptor.Pointer).Target as ICustomStreamAccessor; + if (streamAccessor != null) streamAccessor.Close(); + } + /// /// Initializes a new instance of the class from a file that's already loaded into memory. @@ -2401,6 +2488,10 @@ private void Dispose(bool disposing) if (memoryFaceHandle.IsAllocated) memoryFaceHandle.Free(); + if (openArgsPtr != IntPtr.Zero) Marshal.FreeHGlobal(openArgsPtr); + if (customStreamPtr != IntPtr.Zero) Marshal.FreeHGlobal(customStreamPtr); + if (customStreamHandle.IsAllocated) customStreamHandle.Free(); + EventHandler handler = Disposed; if (handler != null) handler(this, EventArgs.Empty); diff --git a/Source/SharpFont/ICustomStreamAccessor.cs b/Source/SharpFont/ICustomStreamAccessor.cs new file mode 100644 index 0000000..138eb1a --- /dev/null +++ b/Source/SharpFont/ICustomStreamAccessor.cs @@ -0,0 +1,56 @@ +#region MIT License +/*Copyright (c) 2015-2016 Robert Rouhani + +SharpFont based on Tao.FreeType, Copyright (c) 2003-2007 Tao Framework Team + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.*/ +#endregion + +namespace SharpFont +{ + /// + /// Interface for providing access to a custom stream representing a font file. + /// + public interface ICustomStreamAccessor + { + /// + /// Seeks to a specified position within a stream. + /// + /// Position from the beginning of the stream to seek to. + /// The new position within the stream. + int Seek(int position); + + /// + /// Reads bytes from the stream. The maximum number of bytes to read is specified by the buffer length. + /// + /// Buffer to receive read bytes. + /// The actual number of bytes read from the stream. + int Read(byte[] buffer); + + /// + /// Closes the stream. Called by freetype once the font face is destroyed. + /// + void Close(); + + /// + /// Returns total length of the stream in bytes. + /// + int Length { get; } + } +} diff --git a/Source/SharpFont/SharpFont.csproj b/Source/SharpFont/SharpFont.csproj index 4ed82c3..b52f4cb 100644 --- a/Source/SharpFont/SharpFont.csproj +++ b/Source/SharpFont/SharpFont.csproj @@ -119,6 +119,7 @@ + @@ -205,6 +206,7 @@ + diff --git a/Source/SharpFont/StreamAccessor.cs b/Source/SharpFont/StreamAccessor.cs new file mode 100644 index 0000000..6892f8d --- /dev/null +++ b/Source/SharpFont/StreamAccessor.cs @@ -0,0 +1,76 @@ +#region MIT License +/*Copyright (c) 2015-2016 Robert Rouhani + +SharpFont based on Tao.FreeType, Copyright (c) 2003-2007 Tao Framework Team + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.*/ +#endregion + +using System; +using System.IO; + +namespace SharpFont +{ + /// + /// Custom font stream accessor based on a generic Stream object. + /// + internal class StreamAccessor : ICustomStreamAccessor + { + private Stream _stream; + private bool _takeStreamOwnership; + + public StreamAccessor(Stream stream, bool takeStreamOwnership) + { + if (stream == null) + throw new ArgumentException("Stream cannot be null", "stream"); + + if (!stream.CanRead || !stream.CanSeek) + throw new ArgumentException("Stream must support reading and seeking", "stream"); + + _stream = stream; + _takeStreamOwnership = takeStreamOwnership; + } + + public void Close() + { + if (_takeStreamOwnership) + { + _stream.Dispose(); + } + } + + public int Read(byte[] buffer) + { + return _stream.Read(buffer, 0, buffer.Length); + } + + public int Seek(int position) + { + return (int)_stream.Seek(position, SeekOrigin.Begin); + } + + public int Length + { + get + { + return (int)_stream.Length; + } + } + } +}