from Quartz import *
from Cocoa import *
import Utilities
import QuartzTextDrawing

import sys
import objc

def getTextString():
    # These unicode values are the characters: Q, u, a, r, t, z,
    # eighthnote, floral heart, black chess queen, and two CJK characters.
    # Note: Create an NSString, because we'll use NSString-specific API's, otherwise
    # we could just have used a python unicode object
    return NSString.stringWithString_(u'\u0051\u0075\u0061\u0072\u0074\u007A\u266A\u2766\u265B\u3042\u304E')

doPointDrawing=1

def drawNSStringWithAttributes():
    textString = getTextString()
    if doPointDrawing:
        context = NSGraphicsContext.currentContext().graphicsPort()

    # Text Line 1. Draw with default attributes.
    p = NSMakePoint(20.0, 400.0)

    # Draw text with default text attributes. The point supplied is
    # not the text baseline but rather the lower-left corner of the box
    # which bounds the text.
    textString.drawAtPoint_withAttributes_(p, None)

    if doPointDrawing:
        Utilities.drawPoint(context, p)

    # Text Line 2. Draw with a specific font and color.

    # Position the text 50 units below the previous text.
    p.y -= 50

    # Set attributes to use when drawing the string.
    stringAttributes = {
        # Use the font with the PostScript name "Times-Roman" at 40 point.
        NSFontAttributeName: NSFont.fontWithName_size_("Times-Roman", 40),

        # Set the color attribute to an opaque red.
        NSForegroundColorAttributeName: NSColor.colorWithCalibratedRed_green_blue_alpha_(0.663, 0, 0.031, 1.0)
    }

    # Draw the text.
    textString.drawAtPoint_withAttributes_(p, stringAttributes)

    if doPointDrawing:
        Utilities.drawPoint(context, p)

    # Text Line 3. Draw stroked text.

    # Position the text 50 units below the previous text.
    p.y -= 50

    # Panther and later support stroke attributes. A positive value
    # of the stroke width attribute produces text that is stroked rather
    # than filled.
    stringAttributes[NSStrokeWidthAttributeName] = 3.0
    textString.drawAtPoint_withAttributes_(p, stringAttributes)

    if doPointDrawing:
        Utilities.drawPoint(context, p)

    # Text Line 4. Draw with fill and stroke.

    p.y -= 50

    # Panther and later support stroke attributes. A negative value
    # of the stroke width attribute results in text that is both filled
    # and stroked.
    stringAttributes[NSStrokeWidthAttributeName] = -3.0
    # Set the stroke color attribute to black.
    stringAttributes[NSStrokeColorAttributeName] = NSColor.colorWithCalibratedRed_green_blue_alpha_(0, 0, 0, 1.0)

    textString.drawAtPoint_withAttributes_(p, stringAttributes)

    if doPointDrawing:
        Utilities.drawPoint(context, p)

    # Text Line 5. Draw at baseline.
    # Tiger and later support the drawWithRect method which allows
    # string text drawing from a point on the text baseline.
    p.y -= 50
    rect = NSRect(
            origin=p,
            size=NSSize(0,0),
        )
    textString.drawWithRect_options_attributes_(
            rect, NSStringDrawingDisableScreenFontSubstitution,
            stringAttributes)

    if doPointDrawing:
        Utilities.drawPoint(context, p)

_myLayout = None
_textStorage = None
_myTextRange = None
def drawWithNSLayout():
    global _myLayout, _textStorage, _myTextRange

    if _myLayout is None:
        # Initialize the text storage with the string to draw.
        _textStorage = NSTextStorage.alloc().initWithString_(getTextString())
        # Initialize the layout manager to use with the text storage.
        _myLayout = NSLayoutManager.alloc().init()
        # Allocate and initialize a text container object.
        textContainer = NSTextContainer.alloc().init()
        # Add the text container to the layout.
        _myLayout.addTextContainer_(textContainer)
        # Release the text container since the layout retains it and
        # this code no longer needs it.
        del textContainer
        # Add the layout to the text storage.
        _textStorage.addLayoutManager_(_myLayout)

        # Set attributes to use when drawing the string.
        stringAttributes = {
            # Use the font with the PostScript name "Times-Roman" at 40 point.
            NSFontAttributeName: NSFont.fontWithName_size_("Times-Roman", 40),

            # Set the text color attribute to an opaque red.
            NSForegroundColorAttributeName:  NSColor.colorWithCalibratedRed_green_blue_alpha_(0.663, 0, 0.031, 1.0),
        }

        # Create the range of text for the entire length of text
        # in the textStorage object.
        _myTextRange = NSMakeRange(0, _textStorage.length())
        # Set the attributes on the entire range of text.
        _textStorage.setAttributes_range_(stringAttributes, _myTextRange)

    # Set the point for drawing the layout.
    p = NSMakePoint(20.0, 400.0)

    # Draw the text range at the point.
    _myLayout.drawGlyphsForGlyphRange_atPoint_(_myTextRange, p)

    if doPointDrawing:
        context = NSGraphicsContext.currentContext().graphicsPort()
        Utilities.drawPoint(context, p)

# The interface to the NSLayoutManager subclass.
class MyNSLayoutManager (NSLayoutManager):
    # The extra instance variables for this subclass.
    _textMode = objc.ivar()
    _fColor = objc.ivar()
    _sColor = objc.ivar()
    _yStartPosition = objc.ivar()
    _lineWidth = objc.ivar()
    _clippingDrawProc = objc.ivar()
    _clippingInfo = objc.ivar()

    # Public methods to set the special attributes
    # of the MyNSLayoutManager instance.
    def setTextMode_(self, textMode):
        self._textMode = textMode

    def setFillColor_(self, color):
        self._fColor = color

    def setStrokeColor_(self, color):
        self._sColor = color

    def setTextLineWidth_(self, width):
        self._lineWidth = width

    def setClippingDrawProc_withInfo_(self, clippingDrawProc, info):
        self._clippingDrawProc = clippingDrawProc
        self._clippingInfo = info

    def init(self):
        self = super(MyNSLayoutManager, self).init()
        if self is None:
            return None


        # Initialize the custom instance variables.
        self._textMode = kCGTextFill
        self._fColor = None
        self._sColor = None
        self._yStartPosition = 0
        self._lineWidth = 1
        self._clippingDrawProc = None
        self._clippingInfo = None
        return self

    # This code overrides this method to record the y coordinate
    # to use as the True baseline for the text drawing.
    def drawGlyphsForGlyphRange_atPoint_(self, glyphsToShow, origin):
        self._yStartPosition = origin.y
        super(MyNSLayoutManager, self).drawGlyphsForGlyphRange_atPoint_(glyphsToShow, origin)

    # This is the rendering method of NSLayoutManager that the
    # code overrides to perform its custom rendering.
    def showPackedGlyphs_length_glyphRange_atPoint_font_color_printAdjustment_(
            self, glyphs, glyphLen, glyphRange, point, font, color, printingAdjustment):

       # Obtain the destination drawing context.
        context = NSGraphicsContext.currentContext().graphicsPort()

        # Adjust start position y value based on the adjusted y coordinate.
        # This ensures the text baseline is at the starting position
        # passed to drawGlyphsForGlyphRange. This technique won't work
        # for super, subscripts, or underlines but that's OK for this example.
        point.y = _yStartPosition

        # The Quartz graphics state should be preserved by showPackedGlyphs.
        CGContextSaveGState(context)

        # Set the desired text drawing mode.
        CGContextSetTextDrawingMode(context, self._textMode)

        # Set the fill color if needed.
        if (self._textMode == kCGTextFill or _self.textMode == kCGTextFillStroke or
                self._textMode == kCGTextFillClip or _textMode == kCGTextFillStrokeClip):
            if self._fColor is not None:
                CGContextSetFillColorWithColor(context, self._fColor)

        # Set the  line width and the stroke color if needed.
        if (self._textMode == kCGTextStroke or self._textMode == kCGTextFillStroke or
                self._textMode == kCGTextStrokeClip or self._textMode == kCGTextFillStrokeClip):
            CGContextSetLineWidth(context, self._lineWidth)
            if self._sColor is not None:
                CGContextSetStrokeColorWithColor(context, self._sColor)

        # Now draw the text. Check whether to adjust for printing widths
        # and if needed adjust extra character spacing accordingly.
        if printingAdjustment.width != 0.0:
            # If printingAdjustment width is non-zero then the text
            # needs to be adjusted. printingAdjustment is the per character
            # adjustment required for this piece of text. Because
            # the Quartz text character spacing set is transformed by
            # the text matrix, this code needs to factor out that effect
            # prior to setting it. Cocoa sets the text matrix to account
            # for the point size of the font so we factor that out of the
            # per character width supplied here.
            charAdjust = printingAdjustment.width / font.pointSize()
            CGContextSetCharacterSpacing(context, charAdjust)
        else:
            CGContextSetCharacterSpacing(context, 0.0)

        # Draw the glyphs. The total number of glyphs is the length
        # of the glyphs string passed to showPackedGlyphs, divided by 2
        # since there are two bytes per glyph.
        CGContextShowGlyphsAtPoint(context, point.x, point.y, glyphs, glyphLen/2)

        # If the text drawing mode requires clipping and there is
        # a custom clipping proc, call it. This allows drawing through
        # clipped text before the graphics state is restored.
        if (self._textMode == kCGTextClip or self._textMode == kCGTextFillClip or
                self._textMode == kCGTextStrokeClip or
                self._textMode == kCGTextFillStrokeClip) and self._clippingDrawProc is not None:

            self._clippingDrawProc(context, point.x, point.y, self._clippingInfo)

        CGContextRestoreGState(context)

def MyClipProc(c, x, y, info):
    CGContextTranslateCTM(c, x, y)
    CGContextSetStrokeColorWithColor(c, Utilities.getRGBOpaqueBlackColor())
    # Draw a grid of lines through the clip.
    QuartzTextDrawing.drawGridLines(c);

_myLayout = None
_textStorage = None
_myTextRange = None
def drawWithCustomNSLayout():
    global _myLayout, _textStorage, _myTextRange

    if _myLayout is None:
        textContainer = NSTextContainer.alloc().init()

        _textStorage = NSTextStorage.alloc().initWithString_(getTextString())
        # Create an instance of the MyNSLayoutManager subclass of NSLayoutManager.
        _myLayout = MyNSLayoutManager.alloc().init()
        _myLayout.addTextContainer_(textContainer)
        # The layout retains the text container so this code can release it.
        del textContainer
        _textStorage.addLayoutManager_(_myLayout)

        # Set attributes to use when drawing the string.
        stringAttributes = {
            # Use the font with the PostScript name "Times-Roman" at 40 point.
            NSFontAttributeName: NSFont.fontWithName_size_("Times-Roman", 40),
        }

        # Create the range.
        _myTextRange = NSMakeRange(0, _textStorage.length())
        # Set the attributes on the entire range of text.
        _textStorage.setAttributes_range_(stringAttributes, _myTextRange)

    p = NSMakePoint(20.0, 400.0)

    # Set the custom attributes of the layout subclass so that
    # the text will be filled with black.
    _myLayout.setTextMode_(kCGTextFill)
    _myLayout.setFillColor_(Utilities.getRGBOpaqueBlackColor())

    # Draw text line 1.
    _myLayout.drawGlyphsForGlyphRange_atPoint_(_myTextRange, p)

    if doPointDrawing:
        context = NSGraphicsContext.currentContext().graphicsPort()
        Utilities.drawPoint(context, p)

    # Set the custom attributes of the layout subclass so that
    # the text will be stroked with black.
    _myLayout.setTextMode_(kCGTextStroke)
    _myLayout.setStrokeColor_(Utilities.getRGBOpaqueBlackColor())
    _myLayout.setTextLineWidth_(2)

    # Draw text line 2.
    p.y -= 50;
    _myLayout.drawGlyphsForGlyphRange_atPoint_(_myTextRange, p)

    if doPointDrawing:
        Utilities.drawPoint(context, p)

    p.y -= 50;

    # Set the custom attributes of the layout subclass so that
    # the text will be filled and stroked and the fill color
    # will be red. Since the stroke color hasn't changed it
    # will be stroked with black.
    _myLayout.setTextMode_(kCGTextFillStroke)
    _myLayout.setFillColor_(Utilities.getRGBOpaqueRedColor())
    # Draw text line 3.
    _myLayout.drawGlyphsForGlyphRange_atPoint_(_myTextRange, p)

    if doPointDrawing:
        Utilities.drawPoint(context, p)

    p.y -= 50;

    # Set the custom attributes of the layout subclass so that
    # the text will be filled, stroked, then clipped.
    _myLayout.setTextMode_(kCGTextFillStrokeClip)

    # Set the clipping proc to MyClipProc which requires
    # no info data.
    _myLayout.setClippingDrawProc_withInfo_(MyClipProc, None)

    # Draw text line 4.
    _myLayout.drawGlyphsForGlyphRange_atPoint_(_myTextRange, p)

    if doPointDrawing:
        Utilities.drawPoint(context, p)

    # Set the clipping proc to None for future drawing.
    _myLayout.setClippingDrawProc_withInfo_(None, None)
