void CreateNativeFont(FontFamily familyName, float emSize, FontStyle style, GraphicsUnit unit, byte gdiCharSet, bool gdiVerticalFont) { sizeInPoints = ConversionHelpers.GraphicsUnitConversion(unit, GraphicsUnit.Point, 96f, emSize); bold = FontStyle.Bold == (style & FontStyle.Bold); italic = FontStyle.Italic == (style & FontStyle.Italic); underLine = FontStyle.Underline == (style & FontStyle.Underline); this.size = emSize; this.unit = unit; var size = sizeInPoints * 96f / 72f; UIFontDescriptorSymbolicTraits traits = 0; if (bold) { traits |= UIFontDescriptorSymbolicTraits.Bold; } if (italic) { traits |= UIFontDescriptorSymbolicTraits.Italic; } UIFont font = null; if (NSThread.Current.IsMainThread) { font = MakeFont(familyName.Name, size, traits); } else { NSThread.MainThread.InvokeOnMainThread(() => { font = MakeFont(familyName.Name, size, traits); }); } nativeFont = font.ToCTFont(); }
void CreateNativeFont(FontFamily familyName, float emSize, FontStyle style, GraphicsUnit unit, byte gdiCharSet, bool gdiVerticalFont) { this.sizeInPoints = ConversionHelpers.GraphicsUnitConversion(unit, GraphicsUnit.Point, 96f, emSize); this.bold = FontStyle.Bold == (style & FontStyle.Bold); this.italic = FontStyle.Italic == (style & FontStyle.Italic); this.underLine = FontStyle.Underline == (style & FontStyle.Underline); this.size = emSize; this.unit = unit; var size = sizeInPoints * 96f / 72f; var traits = (NSFontTraitMask)0; if (bold) { traits |= NSFontTraitMask.Bold; } if (italic) { traits |= NSFontTraitMask.Italic; } var f = NSFontWithFamily(familyName.Name, traits, RegularWeight, size) ?? NSSystemFont(RegularWeight, size); this.nativeFont = f.ToCTFont(); }
/// <summary> /// Draws the specified image, using its original physical size, at the location specified by a coordinate pair. /// </summary> /// <param name="image">Image.</param> /// <param name="x">The x coordinate.</param> /// <param name="y">The y coordinate.</param> public void DrawImage(Image image, float x, float y) { var width = image.physicalSize.Width; var height = image.physicalSize.Height; if (graphicsUnit != GraphicsUnit.Pixel) { width = ConversionHelpers.GraphicsUnitConversion(GraphicsUnit.Pixel, graphicsUnit, image.HorizontalResolution, width); height = ConversionHelpers.GraphicsUnitConversion(GraphicsUnit.Pixel, graphicsUnit, image.VerticalResolution, height); } DrawImage(image, new RectangleF(x, y, width, height)); }
/// <summary> /// Draws the specified portion of the specified Image at the specified location and with the specified size. /// /// The srcRect parameter specifies a rectangular portion of the image object to draw. This portion is scaled /// up or down (in the case where source rectangle overruns the bounds of the image) to fit inside the rectangle /// specified by the destRect parameter. /// </summary> /// <param name="image">Image.</param> /// <param name="destRect">Destination rect.</param> /// <param name="srcRect">Source rect.</param> /// <param name="srcUnit">Source unit.</param> public void DrawImage(Image image, RectangleF destRect, RectangleF srcRect, GraphicsUnit srcUnit) { if (image == null) { throw new ArgumentNullException("image"); } var srcRect1 = srcRect; // If the source units are not the same we need to convert them // The reason we check for Pixel here is that our graphics already has the Pixel's baked into the model view transform if (srcUnit != graphicsUnit && srcUnit != GraphicsUnit.Pixel) { ConversionHelpers.GraphicsUnitConversion(srcUnit, graphicsUnit, image.HorizontalResolution, image.VerticalResolution, ref srcRect1); } if (srcRect1.Location == Point.Empty && srcRect1.Size == image.Size) { DrawImage(image, destRect); return; } if (image.NativeCGImage == null) { throw new NotImplementedException(); } // Obtain the subImage var subImage = image.NativeCGImage.WithImageInRect(new CGRect(srcRect1.X, srcRect1.Y, srcRect1.Width, srcRect1.Height)); // If we do not have anything to draw then we exit here if (subImage == null || subImage.Width == 0 || subImage.Height == 0) { return; } var transform = image.imageTransform; // Reset our height on the transform to account for subImage transform.y0 = subImage.Height; // Make sure we scale the image in case the source rectangle // overruns our subimage bouncs width and/or height float scaleX = subImage.Width / srcRect1.Width; float scaleY = subImage.Height / srcRect1.Height; transform.Scale(scaleX, scaleY); // Now draw our image DrawImage(destRect, subImage, transform); }
void CreateNativeFont(FontFamily family, float emSize, FontStyle style, GraphicsUnit unit, byte gdiCharSet, bool gdiVerticalFont) { this.sizeInPoints = ConversionHelpers.GraphicsUnitConversion(unit, GraphicsUnit.Point, 96f, emSize); this.size = emSize; this.unit = unit; var size = sizeInPoints * 96f / 72f; var traits = CTFontSymbolicTraits.None; traits |= Bold ? CTFontSymbolicTraits.Bold : 0; traits |= Italic ? CTFontSymbolicTraits.Italic : 0; this.nativeFont = CTFontWithFamily(family, traits, size); }
/// <summary> /// Draws the specified image, using its original physical size, at the location specified by a coordinate pair. /// </summary> /// <param name="image">Image.</param> /// <param name="x">The x coordinate.</param> /// <param name="y">The y coordinate.</param> public void DrawImage(Image image, float x, float y) { var size = image.physicalSize; var width = size.Width; var height = size.Height; if (graphicsUnit != GraphicsUnit.Pixel) { width = ConversionHelpers.GraphicsUnitConversion(GraphicsUnit.Pixel, graphicsUnit, image.HorizontalResolution, width); height = ConversionHelpers.GraphicsUnitConversion(GraphicsUnit.Pixel, graphicsUnit, image.VerticalResolution, height); } var uss = context.ConvertSizeToUserSpace(new CGSize(width, height)); DrawImage(image, new RectangleF(x, y, (float)uss.Width, (float)uss.Height)); }
public Font(string familyName, float emSize, FontStyle style, GraphicsUnit unit, byte gdiCharSet, bool gdiVerticalFont) { if (emSize <= 0) { throw new ArgumentException("emSize is less than or equal to 0, evaluates to infinity, or is not a valid number.", "emSize"); } // convert to 96 Dpi to be consistent with Windows var dpiSize = emSize * dpiScale; try { nativeFont = new CTFont(familyName, dpiSize); } catch { //nativeFont = new CTFont("Lucida Grande",emSize); nativeFont = new CTFont("Helvetica", dpiSize); } CTFontSymbolicTraits tMask = CTFontSymbolicTraits.None; if ((style & FontStyle.Bold) == FontStyle.Bold) { tMask |= CTFontSymbolicTraits.Bold; } if ((style & FontStyle.Italic) == FontStyle.Italic) { tMask |= CTFontSymbolicTraits.Italic; } strikeThrough = (style & FontStyle.Strikeout) == FontStyle.Strikeout; underLine = (style & FontStyle.Underline) == FontStyle.Underline; var nativeFont2 = nativeFont.WithSymbolicTraits(dpiSize, tMask, tMask); if (nativeFont2 != null) { nativeFont = nativeFont2; } sizeInPoints = emSize; this.unit = unit; // FIXME // I do not like the hard coded 72 but am just trying to boot strap the Font class right now size = ConversionHelpers.GraphicsUnitConversion(GraphicsUnit.Point, unit, 72.0f, sizeInPoints); }
public void UnlockBits(BitmapData data) { if ((ImageLockMode)data.Reserved == ImageLockMode.ReadOnly) { return; } if (NativeCGImage.BitsPerPixel == 32) { if (!ConversionHelpers.sTablesInitialized) { ConversionHelpers.CalculateTables(); } Convert_BGRA_8888_To_P_RGBA_8888(bitmapBlock, data.Stride * data.Height); } }
/// <summary> /// Draws a portion of an image at a specified location. /// </summary> /// <param name="image">Image.</param> /// <param name="x">The x coordinate.</param> /// <param name="y">The y coordinate.</param> /// <param name="srcRect">Source rect.</param> /// <param name="srcUnit">Source unit.</param> public void DrawImage(Image image, float x, float y, RectangleF srcRect, GraphicsUnit srcUnit) { if (image == null) { throw new ArgumentNullException("image"); } var srcRect1 = srcRect; // If the source units are not the same we need to convert them // The reason we check for Pixel here is that our graphics already has the Pixel's baked into the model view transform if (srcUnit != graphicsUnit && srcUnit != GraphicsUnit.Pixel) { ConversionHelpers.GraphicsUnitConversion(srcUnit, graphicsUnit, image.VerticalResolution, image.HorizontalResolution, ref srcRect1); } DrawImage(image, new RectangleF(x, y, srcRect1.Width, srcRect1.Height), srcRect1, graphicsUnit); }
/// <summary> /// Draws a portion of an image at a specified location. /// </summary> /// <param name="image">Image.</param> /// <param name="x">The x coordinate.</param> /// <param name="y">The y coordinate.</param> /// <param name="srcRect">Source rect.</param> /// <param name="srcUnit">Source unit.</param> public void DrawImage(Image image, int x, int y, Rectangle srcRect, GraphicsUnit srcUnit) { if (image == null) { throw new ArgumentNullException("image"); } var width = image.physicalSize.Width; var height = image.physicalSize.Height; if (graphicsUnit != GraphicsUnit.Pixel) { width = ConversionHelpers.GraphicsUnitConversion(GraphicsUnit.Pixel, graphicsUnit, image.HorizontalResolution, width); height = ConversionHelpers.GraphicsUnitConversion(GraphicsUnit.Pixel, graphicsUnit, image.VerticalResolution, height); } DrawImage(image, new RectangleF(x, y, width, height), srcRect, srcUnit); }
void CreateNativeFont(FontFamily family, float emSize, FontStyle style, GraphicsUnit unit, byte gdiCharSet, bool gdiVerticalFont) { this.sizeInPoints = ConversionHelpers.GraphicsUnitConversion(unit, GraphicsUnit.Point, 96f, emSize); this.underLine = 0 != (style & FontStyle.Underline); this.strikeThrough = 0 != (style & FontStyle.Strikeout); this.size = emSize; this.unit = unit; var size = sizeInPoints * 96f / 72f; var traits = CTFontSymbolicTraits.None; traits |= style.IsBold() ? CTFontSymbolicTraits.Bold : 0; traits |= style.IsItalic() ? CTFontSymbolicTraits.Italic : 0; this.nativeFont = CTFontWithFamily(family, traits, size); }
private void CreateNativeFont(FontFamily familyName, float emSize, FontStyle style, GraphicsUnit unit, byte gdiCharSet, bool gdiVerticalFont) { // convert to 96 Dpi to be consistent with Windows var dpiSize = emSize * dpiScale; try { nativeFont = new CTFont(familyName.NativeDescriptor, dpiSize); } catch { nativeFont = new CTFont("Helvetica", dpiSize); } CTFontSymbolicTraits tMask = CTFontSymbolicTraits.None; if ((style & FontStyle.Bold) == FontStyle.Bold) { tMask |= CTFontSymbolicTraits.Bold; } if ((style & FontStyle.Italic) == FontStyle.Italic) { tMask |= CTFontSymbolicTraits.Italic; } strikeThrough = (style & FontStyle.Strikeout) == FontStyle.Strikeout; underLine = (style & FontStyle.Underline) == FontStyle.Underline; var nativeFont2 = nativeFont.WithSymbolicTraits(dpiSize, tMask, tMask); if (nativeFont2 != null) { nativeFont = nativeFont2; } bold = (nativeFont.SymbolicTraits & CTFontSymbolicTraits.Bold) == CTFontSymbolicTraits.Bold; italic = (nativeFont.SymbolicTraits & CTFontSymbolicTraits.Italic) == CTFontSymbolicTraits.Italic; sizeInPoints = emSize; this.unit = unit; // FIXME // I do not like the hard coded 72 but am just trying to boot strap the Font class right now size = ConversionHelpers.GraphicsUnitConversion(GraphicsUnit.Point, unit, 72.0f, sizeInPoints); }
public BitmapData LockBits(Rectangle rect, ImageLockMode flags, PixelFormat pixelFormat) { // We don't support conversion if (PixelFormat != pixelFormat) { throw new ArgumentException("", "pixelFormat"); } if (rect != new RectangleF(new PointF(0, 0), physicalDimension)) { throw new NotImplementedException("Sub rectangles of bitmaps not supported yet."); } // Bitmap created from external data, convert it if (bitmapBlock == IntPtr.Zero) { GetRenderableContext(); } BitmapData bitmapData = new BitmapData(); //bitmapData.Scan0 = (IntPtr)((long)pinnedScanArray.AddrOfPinnedObject() + ((rect.Left * NativeCGImage.BitsPerPixel + 7) / 8) + (rect.Top * NativeCGImage.BytesPerRow)); bitmapData.Scan0 = bitmapBlock; bitmapData.Height = (int)rect.Height; bitmapData.Width = (int)rect.Width; bitmapData.PixelFormat = pixelFormat; bitmapData.Stride = (int)NativeCGImage.BytesPerRow; bitmapData.Reserved = (int)flags; if (flags != ImageLockMode.WriteOnly) { if (NativeCGImage.BitsPerPixel == 32) { if (!ConversionHelpers.sTablesInitialized) { ConversionHelpers.CalculateTables(); } Convert_P_RGBA_8888_To_BGRA_8888(bitmapBlock, bitmapData.Stride * bitmapData.Height); } } return(bitmapData); }
// Our internal format is pre-multiplied alpha static unsafe void Convert_BGRA_8888_To_P_RGBA_8888(IntPtr bitmapBlock, int size) { byte temp = 0; byte alpha = 0; byte *buffer = (byte *)bitmapBlock; for (int sd = 0; sd < size; sd += 4) { alpha = buffer [sd + 3]; temp = buffer [sd]; // save off blue if (alpha < 255) { buffer [sd] = ConversionHelpers.PremultiplyValue(alpha, buffer [sd + 2]); // move red back buffer [sd + 1] = ConversionHelpers.PremultiplyValue(alpha, buffer [sd + 1]); buffer [sd + 2] = ConversionHelpers.PremultiplyValue(alpha, temp); } else { buffer [sd] = buffer [sd + 2]; // move red back buffer [sd + 2] = temp; } } }
// Our internal format is pre-multiplied alpha static unsafe void Convert_P_RGBA_8888_To_BGRA_8888(IntPtr bitmapBlock, int size) { byte temp = 0; byte alpha = 0; byte *buffer = (byte *)bitmapBlock; for (int x = 0; x < size; x += 4) { alpha = buffer [x + 3]; // Save off alpha temp = buffer [x]; // save off red if (alpha < 255) { buffer [x] = ConversionHelpers.UnpremultiplyValue(alpha, buffer [x + 2]); // move blue to red buffer [x + 1] = ConversionHelpers.UnpremultiplyValue(alpha, buffer [x + 1]); buffer [x + 2] = ConversionHelpers.UnpremultiplyValue(alpha, temp); // move the red to green } else { buffer [x] = buffer [x + 2]; // move blue to red buffer [x + 2] = temp; // move the red to green } } }
public void DrawImage(Image image, Rectangle destRect, float srcX, float srcY, float srcWidth, float srcHeight, GraphicsUnit srcUnit, ImageAttributes imageAttrs) { if (image == null) { throw new ArgumentNullException("image"); } var srcRect1 = new RectangleF(srcX, srcY, srcWidth, srcHeight); // If the source units are not the same we need to convert them // The reason we check for Pixel here is that our graphics already has the Pixel's baked into the model view transform if (srcUnit != graphicsUnit && srcUnit != GraphicsUnit.Pixel) { ConversionHelpers.GraphicsUnitConversion(srcUnit, graphicsUnit, image.HorizontalResolution, image.VerticalResolution, ref srcRect1); } if (image.NativeCGImage == null) { DrawImage(image, destRect); return; } // Obtain the subImage var subImage = image.NativeCGImage.WithImageInRect(srcRect1.ToCGRect()); // If we do not have anything to draw then we exit here if (subImage.Width == 0 || subImage.Height == 0) { return; } // var transform = image.imageTransform; //// // Reset our height on the transform to account for subImage // transform.y0 = subImage.Height; //// //// // Make sure we scale the image in case the source rectangle //// // overruns our subimage bouncs width and/or height // float scaleX = subImage.Width/srcRect1.Width; // float scaleY = subImage.Height/srcRect1.Height; // transform.Scale (scaleX, scaleY); bool attributesSet = imageAttrs != null && (imageAttrs.isColorMatrixSet || imageAttrs.isGammaSet); if (attributesSet) { InitializeImagingContext(); CIImage result = subImage; if (imageAttrs.isColorMatrixSet) { var ciFilter = CIFilter.FromName("CIColorMatrix"); ciFilter.SetDefaults(); ciFilter.SetValueForKey(result, new NSString("inputImage")); var inputRVector = new CIVector(imageAttrs.colorMatrix.Matrix00, imageAttrs.colorMatrix.Matrix01, imageAttrs.colorMatrix.Matrix02, imageAttrs.colorMatrix.Matrix03); var inputGVector = new CIVector(imageAttrs.colorMatrix.Matrix10, imageAttrs.colorMatrix.Matrix11, imageAttrs.colorMatrix.Matrix12, imageAttrs.colorMatrix.Matrix13); var inputBVector = new CIVector(imageAttrs.colorMatrix.Matrix20, imageAttrs.colorMatrix.Matrix21, imageAttrs.colorMatrix.Matrix22, imageAttrs.colorMatrix.Matrix23); var inputAVector = new CIVector(imageAttrs.colorMatrix.Matrix30, imageAttrs.colorMatrix.Matrix31, imageAttrs.colorMatrix.Matrix32, imageAttrs.colorMatrix.Matrix33); var inputBiasVector = new CIVector(imageAttrs.colorMatrix.Matrix40, imageAttrs.colorMatrix.Matrix41, imageAttrs.colorMatrix.Matrix42, imageAttrs.colorMatrix.Matrix43); ciFilter.SetValueForKey(inputRVector, new NSString("inputRVector")); ciFilter.SetValueForKey(inputGVector, new NSString("inputGVector")); ciFilter.SetValueForKey(inputBVector, new NSString("inputBVector")); ciFilter.SetValueForKey(inputAVector, new NSString("inputAVector")); ciFilter.SetValueForKey(inputBiasVector, new NSString("inputBiasVector")); result = (CIImage)ciFilter.ValueForKey(new NSString("outputImage")); } if (imageAttrs.isGammaSet) { var ciFilter = CIFilter.FromName("CIGammaAdjust"); ciFilter.SetDefaults(); ciFilter.SetValueForKey(result, new NSString("inputImage")); var inputPower = NSNumber.FromFloat(imageAttrs.gamma); ciFilter.SetValueForKey(inputPower, new NSString("inputPower")); result = (CIImage)ciFilter.ValueForKey(new NSString("outputImage")); } subImage = ciContext.CreateCGImage(result, result.Extent); } transform = image.imageTransform; transform.y0 = subImage.Height; float scaleX1 = subImage.Width / srcRect1.Width; float scaleY1 = subImage.Height / srcRect1.Height; transform.Scale(scaleX1, scaleY1); // Now draw our image DrawImage(destRect, subImage, transform); }
/// <summary> /// Draws the specified portion of the specified Image at the specified location and with the specified size. /// /// The destPoints specifies a parallelogram with the first point specifying the upper left corner, /// second point specifying the upper right corner and the third point specifying the lower left corner. /// /// The srcRect parameter specifies a rectangular portion of the image object to draw. This portion is scaled /// up or down (in the case where source rectangle overruns the bounds of the image) to fit inside the rectangle /// specified by the destRect parameter. /// </summary> /// <param name="image">Image.</param> /// <param name="destPoints">Destination points.</param> /// <param name="srcRect">Source rect.</param> /// <param name="srcUnit">Source unit.</param> public void DrawImage(Image image, PointF [] destPoints, RectangleF srcRect, GraphicsUnit srcUnit) { if (image == null) { throw new ArgumentNullException("image"); } if (destPoints == null) { throw new ArgumentNullException("destPoints"); } if (destPoints.Length < 3) { throw new ArgumentException("Destination points must be an array with a length of 3 or 4. " + "A length of 3 defines a parallelogram with the upper-left, upper-right, " + "and lower-left corners. A length of 4 defines a quadrilateral with the " + "fourth element of the array specifying the lower-right coordinate."); } // Windows throws a Not Implemented error if the points are more than 3 if (destPoints.Length > 3) { throw new NotImplementedException(); } var srcRect1 = srcRect; // If the source units are not the same we need to convert them // The reason we check for Pixel here is that our graphics already has the Pixel's baked into the model view transform if (srcUnit != graphicsUnit && srcUnit != GraphicsUnit.Pixel) { ConversionHelpers.GraphicsUnitConversion(srcUnit, graphicsUnit, image.HorizontalResolution, image.VerticalResolution, ref srcRect1); } if (srcRect1.Location == Point.Empty && srcRect1.Size == image.Size) { DrawImage(image, destPoints); return; } if (image.NativeCGImage == null) { throw new NotImplementedException(); } // Obtain the subImage var subImage = image.NativeCGImage.WithImageInRect(srcRect1.ToCGRect()); // If we do not have anything to draw then we exit here if (subImage.Width == 0 || subImage.Height == 0) { return; } // create our rectangle. Offset is 0 because the CreateGeometricTransform bakes our x,y offset in there. var rect = new RectangleF(0, 0, destPoints [1].X - destPoints [0].X, destPoints [2].Y - destPoints [0].Y); // We need to flip our Y axis so the image appears right side up var geoTransform = new CGAffineTransform(1, 0, 0, -1, 0, rect.Height); // Make sure we scale the image in case the source rectangle // overruns our subimage bounds (width and/or height) float scaleX = subImage.Width / srcRect1.Width; float scaleY = subImage.Height / srcRect1.Height; geoTransform.Scale(scaleX, scaleY); //var geott = GeomUtilities.CreateGeometricTransform (rect, destPoints); geoTransform.Multiply(GeomUtilities.CreateGeometricTransform(rect, destPoints)); // Apply our transform to the context context.ConcatCTM(geoTransform); // now we draw our image. context.DrawImage(rect.ToCGRect(), subImage); // Now we revert our image transform from the context var revert = CGAffineTransform.CGAffineTransformInvert(geoTransform); context.ConcatCTM(revert); subImage.Dispose(); }
public void DrawString(string s, Font font, Brush brush, RectangleF layoutRectangle, StringFormat format = null) { if (font == null) { throw new ArgumentNullException("font"); } if (brush == null) { throw new ArgumentNullException("brush"); } if (s == null || s.Length == 0) { return; } if (format == null) { format = StringFormat.GenericDefault; } // TODO: Take into consideration units // Not sure we need the Save and Restore around this yet. context.SaveState(); // TextMatrix is not part of the Graphics State and Restore var saveMatrix = context.TextMatrix; bool layoutAvailable = true; // context.SelectFont ( font.nativeFont.PostScriptName, // font.SizeInPoints, // CGTextEncoding.MacRoman); // // context.SetCharacterSpacing(1); // context.SetTextDrawingMode(CGTextDrawingMode.Fill); // 5 // // // Setup both the stroke and the fill ? // brush.Setup(this, true); // brush.Setup(this, false); // // var textMatrix = font.nativeFont.Matrix; // // textMatrix.Scale(1,-1); // context.TextMatrix = textMatrix; // // context.ShowTextAtPoint(layoutRectangle.X, // layoutRectangle.Y + font.nativeFont.CapHeightMetric, s); // // // First we call the brush with a fill of false so the brush can setup the stroke color // that the text will be using. // For LinearGradientBrush this will setup a TransparentLayer so the gradient can // be filled at the end. See comments. brush.Setup(this, false); // Stroke // I think we only Fill the text with no Stroke surrounding context.SetTextDrawingMode(CGTextDrawingMode.Fill); var attributedString = buildAttributedString(s, font, format, lastBrushColor); // Work out the geometry RectangleF insetBounds = layoutRectangle; if (insetBounds.Size == SizeF.Empty) { insetBounds.Width = boundingBox.Width; insetBounds.Height = boundingBox.Height; layoutAvailable = false; if (format.LineAlignment != StringAlignment.Near) { insetBounds.Size = MeasureString(s, font); } } PointF textPosition = new PointF(insetBounds.X, insetBounds.Y); float boundsWidth = insetBounds.Width; // Calculate the lines int start = 0; int length = (int)attributedString.Length; float baselineOffset = 0; var typesetter = new CTTypesetter(attributedString); // First we need to calculate the offset for Vertical Alignment if we // are using anything but Top if (layoutAvailable && format.LineAlignment != StringAlignment.Near) { while (start < length) { int count = (int)typesetter.SuggestLineBreak(start, boundsWidth); var line = typesetter.GetLine(new NSRange(start, count)); // Create and initialize some values from the bounds. nfloat ascent; nfloat descent; nfloat leading; line.GetTypographicBounds(out ascent, out descent, out leading); baselineOffset += (float)Math.Ceiling(ascent + descent + leading + 1); // +1 matches best to CTFramesetter's behavior line.Dispose(); start += count; } start = 0; } // If we are drawing vertial direction then we need to rotate our context transform by 90 degrees if ((format.FormatFlags & StringFormatFlags.DirectionVertical) == StringFormatFlags.DirectionVertical) { //textMatrix.Rotate (ConversionHelpers.DegreesToRadians (90)); var verticalOffset = 0.0f; while (start < length) { int count = (int)typesetter.SuggestLineBreak(start, boundsWidth); var line = typesetter.GetLine(new NSRange(start, count)); // Create and initialize some values from the bounds. nfloat ascent; nfloat descent; nfloat leading; line.GetTypographicBounds(out ascent, out descent, out leading); verticalOffset += (float)Math.Ceiling(ascent + descent + leading + 1); // +1 matches best to CTFramesetter's behavior line.Dispose(); start += count; } context.TranslateCTM(layoutRectangle.X, layoutRectangle.Y); context.RotateCTM(ConversionHelpers.DegreesToRadians(90)); context.TranslateCTM(-layoutRectangle.X, -layoutRectangle.Y); context.TranslateCTM(0, -verticalOffset); start = 0; } start = 0; while (start < length && textPosition.Y < insetBounds.Bottom) { // Now we ask the typesetter to break off a line for us. // This also will take into account line feeds embedded in the text. // Example: "This is text \n with a line feed embedded inside it" int count = (int)typesetter.SuggestLineBreak(start, boundsWidth); var line = typesetter.GetLine(new NSRange(start, count)); // Create and initialize some values from the bounds. nfloat ascent; nfloat descent; nfloat leading; double lineWidth = line.GetTypographicBounds(out ascent, out descent, out leading); // Calculate the string format if need be var penFlushness = 0.0f; if (format != null) { if (layoutAvailable) { if (format.Alignment == StringAlignment.Far) { penFlushness = (float)line.GetPenOffsetForFlush(1.0f, boundsWidth); } else if (format.Alignment == StringAlignment.Center) { penFlushness = (float)line.GetPenOffsetForFlush(0.5f, boundsWidth); } } else { // We were only passed in a point so we need to format based // on the point. if (format.Alignment == StringAlignment.Far) { penFlushness -= (float)lineWidth; } else if (format.Alignment == StringAlignment.Center) { penFlushness -= (float)lineWidth / 2.0f; } } } // initialize our Text Matrix or we could get trash in here var textMatrix = new CGAffineTransform( 1, 0, 0, -1, 0, ascent); if (format.LineAlignment == StringAlignment.Near) { textMatrix.Translate(penFlushness + textPosition.X, textPosition.Y); } if (format.LineAlignment == StringAlignment.Center) { if (layoutAvailable) { textMatrix.Translate(penFlushness + textPosition.X, textPosition.Y + ((insetBounds.Height / 2) - (baselineOffset / 2))); } else { textMatrix.Translate(penFlushness + textPosition.X, textPosition.Y - ((insetBounds.Height / 2) - (baselineOffset / 2))); } } if (format.LineAlignment == StringAlignment.Far) { if (layoutAvailable) { textMatrix.Translate(penFlushness + textPosition.X, textPosition.Y + ((insetBounds.Height) - (baselineOffset))); } else { textMatrix.Translate(penFlushness + textPosition.X, textPosition.Y - ((insetBounds.Height) - (baselineOffset))); } } context.TextMatrix = textMatrix; // and draw the line line.Draw(context); // Move the index beyond the line break. start += count; textPosition.Y += (float)Math.Ceiling(ascent + descent + leading + 1); // +1 matches best to CTFramesetter's behavior line.Dispose(); } // Now we call the brush with a fill of true so the brush can do the fill if need be // For LinearGradientBrush this will draw the Gradient and end the TransparentLayer. // See comments. brush.Setup(this, true); // Fill context.TextMatrix = saveMatrix; context.RestoreState(); }