Exporting SVG from Cocoa

Following from my previous post about SVG exporting in cocoa, here are some details on how this is done in SQLEditor. The example code is extracted from the live SQLEditor code base, but it’s been altered to simplify in a few places. Hopefully it doesn’t contain any bugs, but please point them out if you see them.

In addition, I don’t offer this up particularly as a tutorial, this is just the way that I did it, so there are possibly (probably?) better ways of doing this. Feel free to reuse any of it, in any way, if you wish using either BSD or Attribution 3.0 Unported . I’ll try and put a complete working example of it up on github soon. Update: Source code now on github

The key point is that SVG is just a dialect of XML. This means that we can use NSXMLDocument and friends to generate the SVG, which makes life much easier than it would be otherwise.

We use a standard NSXMLDocument

- (NSXMLDocument*)xmlDocument
{
id rootNode = [self exportToXMLNode];
id xmlDocument = [[[NSXMLDocument alloc] initWithRootElement:rootNode] autorelease];
[xmlDocument setVersion:@"1.0"];
[xmlDocument setCharacterEncoding:@"UTF-8"];
return xmlDocument;
}

The code to generate the root node looks like this:

- (NSXMLNode*)exportToXMLNode
{
    id exportNode = [NSXMLElement elementWithName:@"svg"];

	id viewBoxString = [NSString stringWithFormat:@"%.0f %.0f %.0f %.0f",imageBounds.origin.x,imageBounds.origin.y,imageBounds.size.width,imageBounds.size.height];

    [exportNode addAttribute:[NSXMLNode attributeWithName:@"viewBox" stringValue:viewBoxString]];
    [exportNode addAttribute:[NSXMLNode attributeWithName:@"width"
                                              stringValue:[NSString stringWithFormat:@"%.0f",imageBounds.size.width]]];
    [exportNode addAttribute:[NSXMLNode attributeWithName:@"height"
                                              stringValue:[NSString stringWithFormat:@"%.0f",imageBounds.size.height]]];

    id namespace = [NSXMLNode namespaceWithName:@"" stringValue:@"http://www.w3.org/2000/svg"];
    [exportNode setNamespaces:[NSArray arrayWithObject:namespace]];

    id transformGroup = [NSXMLNode elementWithName:@"g"];
    [transformGroup addAttribute:[NSXMLNode attributeWithName:@"transform" stringValue:@"translate(0.5,0.5)"]];

    [exportNode addChild:transformGroup];

    for (id entry in [self.container objectListByZOrder]) {

        if (![entry getPropertyAsBoolean:@"hidden"]) {
                id newNode = [self exportObjectToSVG:entry];

                if (newNode) {
                    [transformGroup addChild:newNode];
                }
            }

    }

	return exportNode;
}

objectListByZOrder returns an array of objects, ordered by zOrder, from back to front.

We use a transform group with an offset of (0.5,0.5) to align the image to the screen pixels. (The alternative would be to draw everything at 0.5 pixel offsets, but this is annoying)

We also use a viewBox, which is set to the value of imageBounds, a variable which contains the bounding rectangle of the objects in the document. This is already set up by the time this code is called.

getPropertyAsBoolean is part of the SQLEditor object properties system and in this case, its used to determine whether the object being drawn is hidden or not. Obviously if it’s hidden, then it doesn’t get drawn. At least one beta version of SQLEditor overlooked this fairly obvious point.  🙂

Now finally, an example object exporting section:

- (id)textElement:(NSString *)text atPoint:(NSPoint)textLocation
{
    id titleText = [MHSVGElement elementWithName:@"text"];
    id titleAttributes = [NSMutableDictionary dictionaryWithCapacity:1];
    [titleAttributes addEntriesFromDictionary:[self dictionaryForLocation:textLocation]];
    [titleAttributes setObject:@"10" forKey:@"dx"];
    [titleAttributes setObject:@"14" forKey:@"dy"];
    [titleAttributes setObject:@"black" forKey:@"fill"];
    [titleAttributes setObject:@"13" forKey:@"font-size"];
    [titleAttributes setObject:@"Lucida Grande" forKey:@"font-family"];
    [titleAttributes setObject:@"optimizeLegibility" forKey:@"text-rendering"];

    [titleText setAttributesWithDictionary:titleAttributes];

    [titleText setStringValue:text];
    return titleText;
}

- (id)rectangleElement:(NSRect)objectRect
             fillColor:(NSColor*)color
           strokeColor:(NSColor*)strokeColor
          cornerRadius:(NSUInteger)radius
{
    id mainFrame = [MHSVGElement elementWithName:@"rect"];

    id attributes = [NSMutableDictionary dictionaryWithCapacity:1];

    [attributes addEntriesFromDictionary:[self dictionaryForLocation:objectRect.origin]];
    [attributes addEntriesFromDictionary:[self dictionaryForSize:objectRect.size]];
    [attributes setObject:[NSNumber numberWithInteger:radius] forKey:@"rx"];
    [attributes setObject:[NSNumber numberWithInteger:radius] forKey:@"ry"];

    id strokeColorString = @"none";

    if (strokeColor) {
        strokeColorString = [self svgColor:strokeColor];
        [attributes setObject:@"1" forKey:@"stroke-width"];
        [attributes setObject:strokeColorString forKey:@"stroke"];
    }

    NSString* fillColorString = [self svgColor:color];

    [attributes setObject:fillColorString forKey:@"fill"];
    [attributes setObject:[NSString stringWithFormat:@"%f",[color alphaComponent]] forKey:@"fill-opacity"];

    [mainFrame setAttributesWithDictionary:attributes];

    return mainFrame;
}

- (NSXMLNode*)exportSQLCanvasAreaToSVG:(SQLCanvasArea*)object
{

    id commentGroup = [MHSVGElement elementWithName:@"g"];

    [commentGroup addAttribute:[NSXMLNode attributeWithName:@"class" stringValue:@"SQLCanvasArea"]];

    NSRect areaRect;
    areaRect.origin = [object location];
    areaRect.size = [object size];

    id mainFrame = [self rectangleElement:areaRect
                                fillColor:[self labelColorForObject:object]
                              strokeColor:[NSColor blackColor]
                             cornerRadius:5];

    [commentGroup addChild:mainFrame];

    id titleText = [self textElement:[object getName] atPoint:[object location]];
    [titleText addAttribute:[NSXMLElement attributeWithName:@"font-family" stringValue:@"Lucida Grande"]];
    [titleText addAttribute:[NSXMLElement attributeWithName:@"font-weight" stringValue:@"bold"]];

    [commentGroup addChild:titleText];

    return commentGroup;
}

Each object has a specific export<objectClass>ToSVG method, in this caseexportSQLCanvasAreaToSVG. It sets up a new group, draws the background and the title, then returns the xml element back, so it can be added to the main image.

At minimum, splitting up the drawing by object type is a good idea, probably better to have separate classes for drawing each type (possibly with some inheritance or composition)

As you can see, the text drawing section makes a number of hard coded assumptions right now. Those were chosen to match the SQLEditor default drawing styles as closely as possible. It’s not a perfect match though, due to the variation between drawing in a cocoa NSView/NSWindow vs drawing in a SVG context (usually in a browser). Fonts are something of an issue and there are issues with rendering across platforms.

Overall though, I’m quite pleased, the first phase of SVG export didn’t prove too troublesome to implement and added a useful feature to SQLEditor without either too much coding, or any extra dependencies.

Update
Example code (with minor changes) now on github: https://github.com/AngusHardie/cocoasvgexperiment

Posted in General | 2 Comments

A memorial to lab mice

Statue of a mouse knitting dna (imgur)- apparently a memorial to laboratory mice.

Regardless of opinions about lab use of animals, I think a statue remembering them is correct and proper.

Posted in General | Leave a comment

New SQLEditor database connection panel

The database connection setup panel in SQLEditor has been something I’ve been hoping to improve for a while.

This is the first attempt at a redesign:

sqleditor-new-db

 

The connections are now listed in a collapsable sidebar and can be both added and removed. You can also edit the connection in the main area. Changes are automatically saved.

It’s not ready yet, but it may appear sometime soon.

Posted in General | Leave a comment

SQLEditor, SVG and Cocoa

One of the new features in the latest SQLEditor 2.1 beta release  (v2.1.0b2, about 5.7MB) is SVG export. This allows you to create a diagram in SQLEditor and then export it to SVG format.

SVG format is a vector diagram format that can be rendered by many web browsers or edited in vector drawing programs like Inkscape.

Here is an embedded svg file:

viewbox

 

The neat thing is that if the image gets scaled it should scale fairly cleanly.
(The lines get a bit fuzzy due to the anti-aliasing)

viewbox

 

Click on the image and you should see the file displayed full size (no separate preview image!)

This should work in Chrome, Safari and Firefox. There are currently some issues with Opera which doesn’t draw the text. I think this is an error in the SQLEditor export code somewhere because I have some early test versions that do work in Opera.

I haven’t tested this with Internet Explorer, but SVG is apparently supported in v9.

I’m still fine tuning the details on this, the examples on this page were tweaked to include a defined view port so that they clip properly and don’t include a lot of white space. This should get done during the export in the next release.

I’m hoping to write another post about how SQLEditor is doing this, it proved to be fairly easy, except for the difficult bits.

 

Posted in SQLEditor | Leave a comment

Using NSDockTile for debug info

Often when I’m working on an app or something I end up running several different versions of it in a short period of time. There’s usually some bug that I’m trying to fix and I want to make sure that the bug occurs in the old version and doesn’t occur in the new version.

Sometimes though I get confused which version I’m running. Is the app in the dock the old version, or the new version or something else?

So today I had an idea: Why not put the version number on the dock tile?

Here’s the code to do it:

if (DEBUG) {
  id badgeText = [NSString stringWithFormat:@"D-%@",[[[NSBundle mainBundle] infoDictionary] valueForKey:@"CFBundleVersion"]];
  [[NSApp dockTile] setBadgeLabel:badgeText];
}

This assumes that you’ve got DEBUG defined somehow and that you have an infoDictionary with the relevant keys and values set.

And here’s what it looks like in the dock:

Regular version on the left, development version on the right.

I’m not sure how useful this will actually be, but it seemed a handy trick for development.

Posted in Macintosh, SQLEditor, Writing Software | Leave a comment

Xbox 360 and Playstation 3 were released before first iPhone

Xbox 360 released November 16, 2005 in USA
Playstation 3 released November 11, 2006, in Japan
Original iPhone released June 29, 2007, in USA – superseded 5 times

The consoles are still competitive platforms. Nothing in the phone world lasts that well.

This is not a criticism of the iPhone, which is a brilliant device, instead a thought about the longevity of the consoles.

Posted in General | Leave a comment

Is “Maximising Shareholder value” required – no it’s not

http://www.guardian.co.uk/sustainable-business/blog/maximising-shareholder-value-irony

The idea only dates from about 1976, it doesn’t seem to be in any law and it probably doesn’t lead to the best outcomes anyway.

Posted in General | Leave a comment

Multi-touch – finger tips wearing out?

I seem to be using more multi-touch stuff, but I do have this irrational fear that my finger tips will slowly start to wear away from constantly dragging them over metal or glass surfaces …

 

Posted in General | Leave a comment

Russian Documentary about Bread Day

Years ago I saw a documentary about Russians living in the country side where the local people had to push, by hand, a railway wagon filled with bread.

I could never find any trace of it later, but today I did:

Chlebnyy den (Bread Day) 1998
Directed by Sergei Dvortsevoy

The Harvard Film Archive showed it a while ago

Posted in General | Leave a comment

More modifier keys

I was working on the new labels panel in SQLEditor when I realised that I’d run out of modifier keys for clicking shortcuts. Tables already use several of the standard keys for selection control, leaving pretty much only option/alt remaining, and I’d already used it for something else. 🙁

Apple: please provide more modifier keys on the keyboard for even more complicated shortcuts!

😉

(Or more rationally, I’ll probably change it around and the commands will end up in a popup menu or something, which is actually better for discoverability anyway)

 

Posted in General | Leave a comment

Cancom Edinburgh Apple shop seems to be closed

Cancom seems to have closed their Edinburgh shop after being bought out by Trams.

Sorry about staff 🙁

Posted in General | 5 Comments

Digital Camera Teardown

Here is a video showing a breakdown of a lumix digital camera. (mikeselectricstuff/YouTube)

The amazing thing is how small the mechanical parts are, and how many there are in there. This camera probably costs no more than £200, yet there are minuscule parts and an amazing level of tiny details, all of which has to work reliably, without any maintenance, for a possible lifespan of several years.

The details of the lens assembly are particularly clever with multiple layers and motors which move the different parts. The coils which are printed onto the circuit board and form part of the movement mechanism is clever feature.

I’m amazed 🙂

 

Posted in General | Leave a comment

Kodak bankrupt?

Kodak, perhaps the photography company, appears to have filed for bankruptcy.

It seems to be something of a representative for the state of the non-digital photography market, which looks pretty much dead except for a few limited areas.

In recent years they seem to have sold off much of the research and development and focused on the hyper-competitive desktop inkjet printer market instead.
(Although the reasons for this decision still remain a mystery to me)

I remember having a kodak camera with kodak film, that got photos printed on kodak photo paper, but I guess people a bit younger than me might never have even seen a roll of film, nor for that matter a kodak camera.

It’s sad to see this happen to such an iconic company and I hope that things don’t turn out badly for the employees.

 

Posted in General | 2 Comments

The Web Development Kit Bundle

Just hours remain to buy the The Web Development Kit Bundle.

It’s got a useful mix of tools for Mac web development and its an excellent bargain even if you’re only looking for one or two items in the bundle. HTMLValidator is our contribution to the bundle which costs just 39.99

 

Posted in General | Leave a comment

Web Development Toolkit

HTMLValidator is one of the apps in the new Web Development Toolkit bundle, which is available now.

The bundle contains 10 apps for a single low price of just $39.99
If you’re doing web design, there’s probably something here for you
(unless you already own all the apps, in which case, it also makes a great gift)

The bundle includes:

The offer is running until December 28th, so you will have to be quick, or you might miss out!

Posted in Company News, Macintosh | Leave a comment

Versions, validateMenuItem: and NSMenuItem

If you find when developing in 10.7 that you get a versions menu with a NSMenuItem as one of the items, it might be worth checking to see whether you’re correctly using validateMenuItem:

This problem may happen if you return YES from validateMenuItem in a NSDocument subclass for menu items that you don’t actually control. (If you just return YES as a default for example)

If instead you return

[super validateMenuItem:item]

You should get the correct “Revert to Last Saved Version” menu item

I ran into this when I was doing testing on 10.7 and although documentation clearly states that you must call the super method in validateMenuItem: , it wasn’t immediately obvious to me what was causing the problem.

Hope this helps if you have the same problem.

Edit: This may have been fixed in OS X

Posted in Macintosh, SQLEditor | Leave a comment

Runesoft updates games for Lion

Last year I bought  “Robin Hood: The Legend of Sherwood”; it’s a fun, 3d person, tactical combat game where you play Robin Hood or one of his merry men in the fight against the sheriff of Notingham. When I bought it, it was a PowerPC game and I ran it under Rosetta on my Mac. With the arrival of Lion and the departure of Rosetta, it stopped working and I’d pretty much expected that I wouldn’t be able to play it except by installing 10.6 on a separate partition somewhere.

However this last week I saw something really surprising; Runesoft has released a patch to bring Intel compatibility to the game and it now works on Lion 🙂

I’m really impressed with this, thank you Runesoft!

Posted in Macintosh | Leave a comment

Flash player updating on Mac

Flash Player on the Mac is always in need of an update – or so it seems.
One particular problem is that the current update checking system seems to merely displays a web page with the current version numbers. That page also appears to contain no direct download links at all. The page it should link to is the Flash download page.

What it should ideally do is to automatically update using Sparkle or similar. I don’t want it to check constantly in the background, simply check and update when I click the “check Now” button.

Failing that, it should clearly state whether the current installation is up-to-date or not.
I don’t want to have to compare version numbers on a page that lists 8 different product version numbers. The software knows which variant it is, its own version number and a web service can be provided to display the latest numbers. It’s trivial to download the latest version number and test to see if it is the same as the installed version, then provide a direct download link if it isn’t.

In fairness some progress has been made in flash installing, at least they now include a standard pkg installer and progress is being made on providing release notes. But much more progress (or the demise of Flash) is still clearly needed.

Posted in Macintosh | Leave a comment

SQLEditor upgrades

One of the big questions I’ve been thinking about recently is how to price upgrades for SQLEditor. So far upgrades have all been free as 1.0 became 1.1 and eventually 1.7. But with the new 2.0 release appearing soon, the question is what should be charged.

Personally I’ve been annoyed with products where I paid money and a new release appears two weeks later which requires a paid upgrade. On the other hand, SQLEditor 2.0 will be a considerable upgrade from SQLEditor 1.0 (the first paid version), so I do think some additional fee is justified to fund development efforts.

But obviously I don’t want people who haven’t bought yet to have doubts as to whether they will be required to pay for the upgrade.

The stated policy is that anyone who bought SQLEditor within 12 months of a paid upgrade being released gets a free upgrade.

However given that the release date for the new version hasn’t been decided yet, I’ve decided to improve the arrangements for version 2.0:

Customers who bought SQLEditor 1.x after August 1st 2010 will get a free upgrade to SQLEditor 2.0.

This means that if you buy SQLEditor today you get 1.7.8 and when 2.0 is released you would get an upgrade to it free of charge.

 

Posted in Company News, General, SQLEditor, Writing Software | 1 Comment

Xcode 4 – Great!

Xcode 4 took a little while to get used to, but as I’ve been using it more, I’ve been liking it more.

The change initially is significant, and there were new ways of doing things and certain other things that had to be rethought altogether. But now, I’m starting to choose to use Xcode 4 when I have the choice, so I think I’ve got used to it

I recently released some new project files for Tesseract OCR cocoa, and they are built with Xcode 4 now. The simplification in the build system and the linking of the frameworks is a vast improvement. Workspaces are a gift to this type of multi-project build. 🙂

The only issue I have is that it doesn’t support 10.5 or PPC, so SQLEditor is stuck on Xcode 3 for a while longer.

Posted in Macintosh, Writing Software | 2 Comments