From 95359b8baa7761527fede68aec12decefab5365c Mon Sep 17 00:00:00 2001 From: Jan Nedoma Date: Mon, 8 May 2017 09:43:14 +0200 Subject: [PATCH 1/2] Added streaming support to Face --- Source/SharpFont/FTStream.cs | 6 +-- Source/SharpFont/Face.cs | 93 +++++++++++++++++++++++++++++++++++- 2 files changed, 95 insertions(+), 4 deletions(-) diff --git a/Source/SharpFont/FTStream.cs b/Source/SharpFont/FTStream.cs index 927c6e07..abb0f9da 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 fea801bd..55a58d0c 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,13 @@ public sealed class Face : NativeObject, IDisposable private Library parentLibrary; private List childSizes; + private StreamIOFunc streamIOFunc; + private StreamCloseFunc streamCloseFunc; + private bool streamOwned; + private GCHandle streamHandle; + private IntPtr streamPtr; + private IntPtr openArgsPtr; + #endregion #region Constructors @@ -67,6 +75,8 @@ public Face(Library library, string path) { } + + /// /// Initializes a new instance of the class. /// @@ -85,7 +95,84 @@ 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) + { + 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"); + + IntPtr reference; + + streamIOFunc = new StreamIOFunc(StreamIOFunc); + streamCloseFunc = new StreamCloseFunc(StreamCloseFunc); + + streamOwned = takeStreamOwnership; + streamHandle = GCHandle.Alloc(stream); + + StreamRec streamRec = new StreamRec(); + streamRec.size = (UIntPtr)stream.Length; + streamRec.descriptor.pointer = GCHandle.ToIntPtr(streamHandle); + streamRec.read = streamIOFunc; + streamRec.close = streamCloseFunc; + + streamPtr = Marshal.AllocHGlobal(Marshal.SizeOf(streamRec)); + Marshal.StructureToPtr(streamRec, streamPtr, false); + + + OpenArgsRec openArgs = new OpenArgsRec(); + openArgs.flags = OpenFlags.Stream; + openArgs.stream = streamPtr; + + 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); + Stream stream = ((GCHandle)ftStream.Descriptor.Pointer).Target as Stream; + if (stream != null) + { + stream.Seek(offset, SeekOrigin.Begin); + if (count > 0) + { + byte[] readbuffer = new byte[count]; + uint bytesread = (uint)stream.Read(readbuffer, 0, (int)count); + Marshal.Copy(readbuffer, 0, buffer, (int)count); + return bytesread; + } + } + return 0; + } + + private void StreamCloseFunc(IntPtr streamPtr) + { + if (streamOwned) + { + FTStream ftStream = new FTStream(streamPtr); + Stream stream = ((GCHandle)ftStream.Descriptor.Pointer).Target as Stream; + if (stream != null) stream.Dispose(); + } + } + /// /// 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 (streamPtr != IntPtr.Zero) Marshal.FreeHGlobal(streamPtr); + if (streamHandle.IsAllocated) streamHandle.Free(); + EventHandler handler = Disposed; if (handler != null) handler(this, EventArgs.Empty); From bb4867b5a68ea602c2e44a40149aba417ff0091e Mon Sep 17 00:00:00 2001 From: Jan Nedoma Date: Sat, 13 May 2017 09:54:06 +0200 Subject: [PATCH 2/2] Generalized stream access via ICustomStreamAccessor interface. There is now a new Face constructor accepting object of this type. Previous Stream support reimplemented using the new interface. --- Source/SharpFont/Face.cs | 60 +++++++++--------- Source/SharpFont/ICustomStreamAccessor.cs | 56 +++++++++++++++++ Source/SharpFont/SharpFont.csproj | 2 + Source/SharpFont/StreamAccessor.cs | 76 +++++++++++++++++++++++ 4 files changed, 164 insertions(+), 30 deletions(-) create mode 100644 Source/SharpFont/ICustomStreamAccessor.cs create mode 100644 Source/SharpFont/StreamAccessor.cs diff --git a/Source/SharpFont/Face.cs b/Source/SharpFont/Face.cs index 55a58d0c..01b78ea6 100644 --- a/Source/SharpFont/Face.cs +++ b/Source/SharpFont/Face.cs @@ -54,12 +54,11 @@ 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; - private bool streamOwned; - private GCHandle streamHandle; - private IntPtr streamPtr; - private IntPtr openArgsPtr; #endregion @@ -104,35 +103,39 @@ public Face(Library library, string path, int faceIndex) /// 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) + : this(library, new StreamAccessor(stream, takeStreamOwnership), faceIndex) { - 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"); + } + /// + /// 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); - streamOwned = takeStreamOwnership; - streamHandle = GCHandle.Alloc(stream); + customStreamHandle = GCHandle.Alloc(streamAccessor); StreamRec streamRec = new StreamRec(); - streamRec.size = (UIntPtr)stream.Length; - streamRec.descriptor.pointer = GCHandle.ToIntPtr(streamHandle); + streamRec.size = (UIntPtr)streamAccessor.Length; + streamRec.descriptor.pointer = GCHandle.ToIntPtr(customStreamHandle); streamRec.read = streamIOFunc; streamRec.close = streamCloseFunc; - streamPtr = Marshal.AllocHGlobal(Marshal.SizeOf(streamRec)); - Marshal.StructureToPtr(streamRec, streamPtr, false); + customStreamPtr = Marshal.AllocHGlobal(Marshal.SizeOf(streamRec)); + Marshal.StructureToPtr(streamRec, customStreamPtr, false); OpenArgsRec openArgs = new OpenArgsRec(); openArgs.flags = OpenFlags.Stream; - openArgs.stream = streamPtr; + openArgs.stream = customStreamPtr; openArgsPtr = Marshal.AllocHGlobal(Marshal.SizeOf(openArgs)); Marshal.StructureToPtr(openArgs, openArgsPtr, false); @@ -148,16 +151,16 @@ public Face(Library library, Stream stream, int faceIndex, bool takeStreamOwners private uint StreamIOFunc(IntPtr streamPtr, uint offset, IntPtr buffer, uint count) { FTStream ftStream = new FTStream(streamPtr); - Stream stream = ((GCHandle)ftStream.Descriptor.Pointer).Target as Stream; - if (stream != null) + ICustomStreamAccessor streamAccessor = ((GCHandle)ftStream.Descriptor.Pointer).Target as ICustomStreamAccessor; + if (streamAccessor != null) { - stream.Seek(offset, SeekOrigin.Begin); + streamAccessor.Seek((int)offset); if (count > 0) { byte[] readbuffer = new byte[count]; - uint bytesread = (uint)stream.Read(readbuffer, 0, (int)count); + int bytesread = streamAccessor.Read(readbuffer); Marshal.Copy(readbuffer, 0, buffer, (int)count); - return bytesread; + return (uint)bytesread; } } return 0; @@ -165,12 +168,9 @@ private uint StreamIOFunc(IntPtr streamPtr, uint offset, IntPtr buffer, uint cou private void StreamCloseFunc(IntPtr streamPtr) { - if (streamOwned) - { - FTStream ftStream = new FTStream(streamPtr); - Stream stream = ((GCHandle)ftStream.Descriptor.Pointer).Target as Stream; - if (stream != null) stream.Dispose(); - } + FTStream ftStream = new FTStream(streamPtr); + ICustomStreamAccessor streamAccessor = ((GCHandle)ftStream.Descriptor.Pointer).Target as ICustomStreamAccessor; + if (streamAccessor != null) streamAccessor.Close(); } @@ -2489,8 +2489,8 @@ private void Dispose(bool disposing) memoryFaceHandle.Free(); if (openArgsPtr != IntPtr.Zero) Marshal.FreeHGlobal(openArgsPtr); - if (streamPtr != IntPtr.Zero) Marshal.FreeHGlobal(streamPtr); - if (streamHandle.IsAllocated) streamHandle.Free(); + if (customStreamPtr != IntPtr.Zero) Marshal.FreeHGlobal(customStreamPtr); + if (customStreamHandle.IsAllocated) customStreamHandle.Free(); EventHandler handler = Disposed; if (handler != null) diff --git a/Source/SharpFont/ICustomStreamAccessor.cs b/Source/SharpFont/ICustomStreamAccessor.cs new file mode 100644 index 00000000..138eb1a6 --- /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 4ed82c3d..b52f4cba 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 00000000..6892f8d0 --- /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; + } + } + } +}