views:

30

answers:

1

I'm working with Objective-C++.

I'm trying to get the path outline of a text using NSBezierPaths appendBezierPathWithGlyphs. The problem is: the output is rather nonsense :(

What I've written:

  String str = Ascii8("test string");
  int length = str.getLength();

  NSFont* font = [NSFont fontWithDescriptor: [NSFontDescriptor fontDescriptorWithName:@"Times"
                                                                                 size:20]
                            textTransform: transform];

  NSTextStorage *storage = [[NSTextStorage alloc] initWithString:toNSString(str)];
  NSLayoutManager *manager = [[NSLayoutManager alloc] init];
  NSTextContainer *container = [[NSTextContainer alloc] init];

  [storage addLayoutManager:manager];
  [manager addTextContainer:container];

  NSGlyph* glyphs = new NSGlyph[length+1];
  [manager getGlyphs:glyphs range:NSMakeRange(0, length)];

  [container release];
  [manager release];
  [storage release];

  NSBezierPath* path = [NSBezierPath bezierPath];
  // If I don't do this I get an exception that currentPoint doesn't exist ...
  [path moveToPoint: NSMakePoint(0, 0)]; 
  [path appendBezierPathWithGlyphs:glyphs count:length inFont:font];

  delete[] glyphs;

  // NSBezierPath -> DoublePath
  for (NSInteger i=0; i<[path elementCount]; ++i)
  {
    NSPoint controlPoints[3];
    NSBezierPathElement el = [path elementAtIndex:i associatedPoints:controlPoints];
    if (el == NSMoveToBezierPathElement)
    {
      printf("move to (%f,%f)\n", controlPoints[0].x, controlPoints[0].y);
    }
    else if (el == NSLineToBezierPathElement)
    {
      printf("line to (%f, %f)\n", controlPoints[0].x, controlPoints[0].y);
    }
    else if (el == NSCurveToBezierPathElement)
    {
      printf("cubic to (%f, %f) (%f, %f) (%f, %f)\n", 
         controlPoints[0].x, controlPoints[0].y,
         controlPoints[1].x, controlPoints[1].y,
         controlPoints[2].x, controlPoints[2].y);
    }   
    else if (el == NSClosePathBezierPathElement) 
    {
      printf("close\n");
    }
  }

For example for the letter 't' I get this output:

move to (0.277832, 0.000000)
move to (0.254395, 0.450195)
line to (0.254395, 0.415039)
line to (0.154785, 0.415039)
line to (0.153809, 0.133789)
cubic to (0.153809, 0.109049) (0.155924, 0.090332) (0.160156, 0.077637)
cubic to (0.167969, 0.055176) (0.183268, 0.043945) (0.206055, 0.043945)
cubic to (0.217773, 0.043945) (0.227946, 0.046712) (0.236572, 0.052246)
cubic to (0.245199, 0.057780) (0.255046, 0.066569) (0.266113, 0.078613)
line to (0.278809, 0.067871)
line to (0.268066, 0.053223)
cubic to (0.251139, 0.030436) (0.233236, 0,014323) (0.214355, 0.004883)
cubic to (0.195475, -0.004557) (0.177246, -0.009277) (0.159668, -0.009277)
cubic to (0.121256, -0.009277) (0.095215, 0.007812) (0.081543, 0.041992)
cubic to (0.074056, 0.060547) (0.070312, 0.086263) (0.070312, 0.119141)
line to (0.070312, 0.415039)
line to (0.017090, 0.415039)
cubic to (0.015462, 0.416016) (0.014242, 0.416992) (0.013428, 0.417969)
cubic to (0.012614, 0.418945) (0.012207, 0.420247) (0.012207, 0.421875)
cubic to (0.012207, 0.425130) (0.012939, 0.427653) (0.014404, 0.429443)
cubic to (0.015869, 0.431234) (0.020508, 0.435384) (0.028320, 0.441895)
cubic to (0.050781, 0.460449) (0.066976, 0.475504) (0.076904, 0.487061)
cubic to (0.086833, 0.498617) (0.110189, 0.529134) (0.146973, 0.578613)
cubic to (0.151204, 0.578613) (0.153727, 0.578288) (0.154541, 0.577637)
cubic to (0.155355, 0.576986) (0.155762, 0.574544) (0.155762, 0.570312)
line to (0.155762, 0.450195)
close

This looks really wrong for me!

+1  A: 

What do you mean by “looks wrong”? Have you tried rendering the data? It’s valid.

Below is your code modified to output an SVG of the curve data, which appears correct (but upside down because the coordinate convention of SVG is different). Other than that, removing some random C++, and adding transform which is undefined in your extract, the only difference is that it correctly counts the number of glyphs with [manager numberOfGlyphs]. In the test case, this makes no difference, but in general the glyph count isn’t the same as the length of the string.

NSString *str = @"test string";
int length = str.length;
NSAffineTransform *transform = [NSAffineTransform transform];

NSFont* font = [NSFont fontWithDescriptor: [NSFontDescriptor fontDescriptorWithName:@"Times" size:20]
                            textTransform: transform];

NSTextStorage *storage = [[NSTextStorage alloc] initWithString:str];
NSLayoutManager *manager = [[NSLayoutManager alloc] init];
NSTextContainer *container = [[NSTextContainer alloc] init];

[storage addLayoutManager:manager];
[manager addTextContainer:container];

NSUInteger glyphCount = [manager numberOfGlyphs];
NSGlyph glyphs[glyphCount];
[manager getGlyphs:glyphs range:NSMakeRange(0, glyphCount)];

[container release];
[manager release];
[storage release];

NSBezierPath* path = [NSBezierPath bezierPath];
[path moveToPoint: NSMakePoint(0, 0)]; // If I don't do this I get an exception that currentPoint doesn't exist ...
[path appendBezierPathWithGlyphs:glyphs count:length inFont:font];


printf("<?xml version=\"1.0\" standalone=\"no\"?>\n"
       "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\"&gt;\n"
       "<svg viewBox=\"-5 -5 10 10\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\"&gt;\n"
       "\t<desc>Debug dump</desc>\n"
       "\t\n<path d=\"");

// NSBezierPath -> DoublePath
for (NSInteger i=0; i<[path elementCount]; ++i)
{
    if (i != 0)  printf(" ");

    NSPoint controlPoints[3];
    NSBezierPathElement el = [path elementAtIndex:i associatedPoints:controlPoints];
    if (el == NSMoveToBezierPathElement)
    {
    //  printf("move to (%f,%f)\n", controlPoints[0].x, controlPoints[0].y);
        printf("M%g %g", controlPoints[0].x, controlPoints[0].y);
    }
    else if (el == NSLineToBezierPathElement)
    {
    //  printf("line to (%f, %f)\n", controlPoints[0].x, controlPoints[0].y);
        printf("L%g %g", controlPoints[0].x, controlPoints[0].y);
    }
    else if (el == NSCurveToBezierPathElement)
    {
    //  printf("cubic to (%f, %f) (%f, %f) (%f, %f)\n", 
        printf("C%g %g %g %g %g %g",
               controlPoints[0].x, controlPoints[0].y,
               controlPoints[1].x, controlPoints[1].y,
               controlPoints[2].x, controlPoints[2].y);
    }   
    else if (el == NSClosePathBezierPathElement) 
    {
    //  printf("close\n");
        printf("Z");
    }
}

printf("\"/>\n</svg>\n");
Ahruman
Aren't the numbers much too small? They are so small that I only saw some dots ... after scaling it with large factor I saw something …
Font size isn’t part of the glyph. You’ll need to scale your transform.
Ahruman
Thanks for help!