Singularity Magazine December 2010

Singularity December 2010

The end of the year will soon be upon us. The magic elves at Singularity HQ had been hard at work to bring you a whopping 79 page issue, brimming with illustrations and photos. In this December issue, you will meet Matt Pearce, a digital artist and illustrator. Read about the difficult start to his illustrator career in my interview with him.

You will also visit (by proxy) the Singapore Philatelic Museum, with special exhibits on Iron Man and the work of G. R. Lambert. You will also meet cosplayers from the Anime Festival Asia held in Singapore (no, they weren’t at the museum. Though that would cause quite a stir with the stamp collecting community).

Download the December 2010 issue. (13 MB. It’s huge. Please be patient.)

Feel free to share the magazine through email, blog or other means. You’re given the right to print and distribute the magazine electronically provided you don’t change any of the content or charge for it.

Also available at Scribd.

AONC review – be remarkable and question the status quo

The Art of Non-Conformity book

I wrote a review on The Art of Non-Conformity book (Amazon link), by Chris Guillebeau in the November issue of Singularity. Here’s an excerpt:

Interestingly, Chris compared the amount of time and effort spent working on his university thesis paper and his world domination manifesto. Both took roughly the same amount of time. But the former was read by only 3 people. His manifesto was downloaded more than 100,000 times.

When I read that, I was thinking, “That is so true!” My own thesis was read (presumably) by only 1 person, my advisor. My blog articles and this magazine are read by more than 1 person. Mathematically speaking, I’ve already done better. My work may not be as widely read as Chris’, but the concept is the same: more bang per word. Understand the result you’re going for.

Download the magazine with the entire review.

You can also buy Chris’ book too (Amazon link).

Basic guide to Excel Open XML

Ok, I don’t know why I didn’t think of it before. So it appears that there’s some need to create Excel files using Open XML SDK 2.0. I’m halfway done with my guide to doing that. It will be sold as an ebook (packaged with source code). It’s a consolidation of some of the articles I’ve already written on the subject, together with some all new articles. Included topics are:

  • How to create a stylesheet and use it
  • How to insert an image into a worksheet
  • How to insert multiple images into a worksheet
  • More advanced styling options
  • Setting column widths
  • Setting column and row settings (such as grouping columns and hiding rows)
  • Text alignment in a cell

When I teach, I believe in giving you the simplest explanation and code. This allows you to create more complex functions if you want. If I complicate it for you, you might not know what the individual parts are. It’s like giving you all 26 alphabets and let you play with it. Much better if you learn the individual alphabets and it’s up to you to form words with them.

I haven’t finalised the cost of the product, nor the release date. But I’m planning a “before Christmas” launch, so you should see it in my store soon. This is a heads up in case you’re agonising over a Christmas present for a programmer (or yourself). The price will be fair, considering that I will be saving you tons of hours of research and pain. It will also come with both C# and VB.NET supporting code.

Having worked in a mid-sized company, I understand that sometimes your IT department doesn’t allow third party products (or doesn’t have the budget to buy the license). I don’t consider the Open XML SDK 2.0 to be a third party product. Besides, it’s by Microsoft (and free!). Most companies should be able to accept that. The source code will only need the Open XML SDK (and the .NET Framework of course) to work.

My product license will be fairly flexible, so you can use my code to create commercial products. And the license is tied to the product, not to number of computers or developers or servers (or whatever complicated rule being used by other software companies). You only need to buy 1 copy. (But tell your friends to buy as well because I need to eat)

So yeah, that’s all I have to say. If there’s a particular topic you want me to cover, there’s still some time. Comment below and I might consider adding it to the guide. If this turns out to be popular (as in I get to eat a couple of meals because of it), I’ve got plans to include more Excel Open XML goodness, and even expand to Word and PowerPoint. We’ll see…

Interview with Iain Broome

Iain Broome

In the latest issue of Singularity, I did an interview with Iain Broome, an eminently practical fiction writer and copywriter. Iain’s novel A is for Angelica is represented by Tibor Jones & Associates, and he also talked about the Post-it system he used while writing the novel. Here’s a question I asked: If you can only give one piece of advice to first time authors, what will it be?

Gosh, just one? That’s tough. I know that many writers when asked this question say… write. But I’ve always thought that to be a bit of a kop out, so I’ll go for something else.

I think it’s important that writers know why they’re writing and manage their expectations. Us scribblers can occasionally struggle with a lack of self-awareness, I’m afraid. Sometimes we think our work is brilliant, and it’s really not. And sometimes we think that what we’ve written is useless, when actually it’s worth pursuing further. Either way, it’s always important to get good quality, trusted advice from peers.

It’s also important to find that difficult balance between having confidence in your work and not expecting instant success. I’ve written before in various places that I try and think about my writing on the following terms: Reach for the stars. Expect nothing. It’s important to believe that you can, for instance, get a novel published if you work hard and are willing to listen and improve. But it’s also vital that you don’t get ahead of yourself and expect it to happen. Like I say, it’s about managing expectations.

Read the rest of the interview here.

What do you think? Who else do you want me to interview?

Interest free tertiary education in Australia

A reader, Lachlan Wells, wrote this to me:

There is a system used here in Australia called HECS which is similar to what the named economist proposes except that you only pay what your degree is worth. The government loans you the money interest-free (but CPI indexed) and pays your entire tuition. After you reach a certain income threshold a percentage of your after-tax income is deducted from your pay to start paying off the debt.

This internalises a few issues that exist with the model you propose. Chiefly, if you are going to pay 4% of your income for life, you may as well go and study all the time and only have a crappy job on the side (at least that is what I would do since I love learning: if you are paying already, it is an incentive to get your moneys worth!). Also it is a government loan and not a loan through the university itself, so it can be deducted from income directly alongside income tax (so no messy tracking of income) and the money is not geared to any particular major (if it is an Arts degree in philosophy or an Engineering degree in microelectronics, the degree is paid for by HECS, so the university gets the funds it needs without the incentive of only churning out higher income degrees).

HECS is at least one thing I believe our government got right; most government programs have more faults than positives!

That was a response to the article I wrote on debtless university education. Thanks Lachlan for sharing the information.

So this HECS is Higher Education Contribution Scheme. As I understand it, it’s been replaced with HELP, Higher Education Loan Programme. But it’s also apparently listed as HECS-HELP, so take note.

The criteria to apply for HECS-HELP assistance is that

you are a Commonwealth supported student and

* an Australian citizen or
* the holder of a permanent humanitarian visa.

This university education tuition fee thing seems to be getting worse. It turns out that Britain’s plan to triple their tuition fees got a whole bunch of students and teachers riled up.

Disclaimer: You are advised to contact the appropriate authorities on relevant, up-to-date information. Criteria for application, loan repayment conditions and other nitty gritty details might change.

How to insert multiple images in Excel Open XML

A blog reader, Fabrice Sacré, sent me an email with code about how to insert multiple images into an Excel file using Open XML. (I had fun playing Yanni’s “In The Mirror” on the virtual piano on his site.) He based it on my article on inserting just one single image into an Excel file. Based on his email address and signature, he works in the Red Cross. Wow! I’m honoured to be saving lives somehow, somewhere, even if in a long, circuitous and indirect manner. Possibly, I’m thinking too highly of myself and there’s no impact at all to the world at large, but I’m trying not to consider that.

Now when I started with the original code, I was in that “create xlsx, rename to zip, scrutinise xml till I fall asleep” mode. Frankly speaking, I had no idea what I was doing most of the time. And in this respect, the order of the individual code/data components was important, but I don’t know which one should come first (in fact, I still don’t). In particular, the code for inserting an image was before the code for inserting data. That worked fine for 1 image, but didn’t turn out so well when I tried with Fabrice’s code.

Ok, here’s the thing. Fabrice used an Excel template file (.xltx). He loaded that up into a new Excel file, then inserted images. Basically, you have an Excel file, then you obtained the worksheet you want, and then insert images to that worksheet. After I understood that (I had to ask him to send his full source code, because he only sent the image insertion function code), I made some changes to my original code to use his function code, and it worked! I’m not going to post the full source code he sent, because it might be sensitive (Red Cross! Saving lives, people!).

Here are the 3 images I used:
Cloud streaks
I like clouds and blue skies.

Crystallised pavillion
I used the “Crystalize” [sic] function in Paint.NET.

Dozing cat
Everyone loves cats, right?

So here’s the main body of the code:

static void Main(string[] args)
{
    string sFile = "ExcelOpenXmlMultipleImageInsert.xlsx";
    if (File.Exists(sFile))
    {
        File.Delete(sFile);
    }
    try
    {
        BuildWorkbook(sFile);
    }
    catch (Exception e)
    {
        Console.WriteLine(e.ToString());
    }
    Console.WriteLine("Program end");
}

protected static void BuildWorkbook(string filename)
{
    using (SpreadsheetDocument xl = SpreadsheetDocument.Create(filename, SpreadsheetDocumentType.Workbook))
    {
        WorkbookPart wbp = xl.AddWorkbookPart();
        WorksheetPart wsp = wbp.AddNewPart<WorksheetPart>();
        Workbook wb = new Workbook();
        FileVersion fv = new FileVersion();
        fv.ApplicationName = "Microsoft Office Excel";
        Worksheet ws = new Worksheet();
        SheetData sd = new SheetData();

        WorkbookStylesPart wbsp = wbp.AddNewPart<WorkbookStylesPart>();
        wbsp.Stylesheet = CreateStylesheet();
        wbsp.Stylesheet.Save();

        Row r = new Row();
        r.RowIndex = 15;
        Cell c;
        c = new Cell();
        c.DataType = CellValues.String;
        c.StyleIndex = 1;
        c.CellReference = "G15";
        c.CellValue = new CellValue("We have multiple images!");
        r.Append(c);
        sd.Append(r);

        ws.Append(sd);                
        wsp.Worksheet = ws;

        // It happens the resolution for the 3 images are 72 dpi
        // Images are 240 by 180 pixels
        // Adjust as needed

        InsertImage(ws, 0, 0, "cloudstreaks.jpg");
        // 2286000 = 180 (pixels) * 914400 (magic number) / 72 (bitmap resolution)
        InsertImage(ws, 0, 2286000, "crystallisedpavilion.png");
        // 3048000 = 240 (pixels) * 914400 (magic number) / 72 (bitmap resolution)
        InsertImage(ws, 3048000, 0, "dozingcat.jpg");

        wsp.Worksheet.Save();
        Sheets sheets = new Sheets();
        Sheet sheet = new Sheet();
        sheet.Name = "Sheet1";
        sheet.SheetId = 1;
        sheet.Id = wbp.GetIdOfPart(wsp);
        sheets.Append(sheet);
        wb.Append(fv);
        wb.Append(sheets);

        xl.WorkbookPart.Workbook = wb;
        xl.WorkbookPart.Workbook.Save();
        xl.Close();
    }
}

I used the CreateStyleSheet() from this article. I want you to note that the data creation (the single Cell containing the sentence) is now before the image insertion. My approach was originally to model what I see on an Excel worksheet. I would most probably see the image(s) at the top, then the (table of) data below. Hence I modeled the coding to be as such. Apparently, that didn’t work out so well.

[UPDATE: For the CreateStyleSheet() function, it turns out that the NumberFormats class is no longer available in Open XML SDK 2.0. It’s now NumberingFormats. I’ve changed it for the source code for this article (but not for previous code articles)]

Fabrice’s approach was to use a template, then insert images to a particular worksheet. The data sheet was already created (via the template). My approach was to insert images and data in memory, so to speak, before committing the save to the worksheet/workbook parts. Basically, I was trying to create a worksheet out of thin air, add images, add data, and then plug it onto the workbook. Oh well, whatever works then…

Here’s the image insertion function Fabrice sent:

protected static void InsertImage(Worksheet ws, long x, long y, long? width, long? height, string sImagePath)
{
    try
    {
        WorksheetPart wsp = ws.WorksheetPart;
        DrawingsPart dp;
        ImagePart imgp;
        WorksheetDrawing wsd;

        ImagePartType ipt;
        switch (sImagePath.Substring(sImagePath.LastIndexOf('.') + 1).ToLower())
        {
            case "png":
                ipt = ImagePartType.Png;
                break;
            case "jpg":
            case "jpeg":
                ipt = ImagePartType.Jpeg;
                break;
            case "gif":
                ipt = ImagePartType.Gif;
                break;
            default:
                return;
        }

        if (wsp.DrawingsPart == null)
        {
            //----- no drawing part exists, add a new one

            dp = wsp.AddNewPart<DrawingsPart>();
            imgp = dp.AddImagePart(ipt, wsp.GetIdOfPart(dp));
            wsd = new WorksheetDrawing();
        }
        else
        {
            //----- use existing drawing part

            dp = wsp.DrawingsPart;
            imgp = dp.AddImagePart(ipt);
            dp.CreateRelationshipToPart(imgp);
            wsd = dp.WorksheetDrawing;
        }

        using (FileStream fs = new FileStream(sImagePath, FileMode.Open))
        {
            imgp.FeedData(fs);
        }

        int imageNumber = dp.ImageParts.Count<ImagePart>();
        if (imageNumber == 1)
        {
            Drawing drawing = new Drawing();
            drawing.Id = dp.GetIdOfPart(imgp);
            ws.Append(drawing);
        }

        NonVisualDrawingProperties nvdp = new NonVisualDrawingProperties();
        nvdp.Id = new UInt32Value((uint)(1024 + imageNumber));
        nvdp.Name = "Picture " + imageNumber.ToString();
        nvdp.Description = "";
        DocumentFormat.OpenXml.Drawing.PictureLocks picLocks = new DocumentFormat.OpenXml.Drawing.PictureLocks();
        picLocks.NoChangeAspect = true;
        picLocks.NoChangeArrowheads = true;
        NonVisualPictureDrawingProperties nvpdp = new NonVisualPictureDrawingProperties();
        nvpdp.PictureLocks = picLocks;
        NonVisualPictureProperties nvpp = new NonVisualPictureProperties();
        nvpp.NonVisualDrawingProperties = nvdp;
        nvpp.NonVisualPictureDrawingProperties = nvpdp;

        DocumentFormat.OpenXml.Drawing.Stretch stretch = new DocumentFormat.OpenXml.Drawing.Stretch();
        stretch.FillRectangle = new DocumentFormat.OpenXml.Drawing.FillRectangle();

        BlipFill blipFill = new BlipFill();
        DocumentFormat.OpenXml.Drawing.Blip blip = new DocumentFormat.OpenXml.Drawing.Blip();
        blip.Embed = dp.GetIdOfPart(imgp);
        blip.CompressionState = DocumentFormat.OpenXml.Drawing.BlipCompressionValues.Print;
        blipFill.Blip = blip;
        blipFill.SourceRectangle = new DocumentFormat.OpenXml.Drawing.SourceRectangle();
        blipFill.Append(stretch);

        DocumentFormat.OpenXml.Drawing.Transform2D t2d = new DocumentFormat.OpenXml.Drawing.Transform2D();
        DocumentFormat.OpenXml.Drawing.Offset offset = new DocumentFormat.OpenXml.Drawing.Offset();
        offset.X = 0;
        offset.Y = 0;
        t2d.Offset = offset;
        Bitmap bm = new Bitmap(sImagePath);

        DocumentFormat.OpenXml.Drawing.Extents extents = new DocumentFormat.OpenXml.Drawing.Extents();

        if (width == null)
            extents.Cx = (long)bm.Width * (long)((float)914400 / bm.HorizontalResolution);
        else
            extents.Cx = width;

        if (height == null)
            extents.Cy = (long)bm.Height * (long)((float)914400 / bm.VerticalResolution);
        else
            extents.Cy = height;

        bm.Dispose();
        t2d.Extents = extents;
        ShapeProperties sp = new ShapeProperties();
        sp.BlackWhiteMode = DocumentFormat.OpenXml.Drawing.BlackWhiteModeValues.Auto;
        sp.Transform2D = t2d;
        DocumentFormat.OpenXml.Drawing.PresetGeometry prstGeom = new DocumentFormat.OpenXml.Drawing.PresetGeometry();
        prstGeom.Preset = DocumentFormat.OpenXml.Drawing.ShapeTypeValues.Rectangle;
        prstGeom.AdjustValueList = new DocumentFormat.OpenXml.Drawing.AdjustValueList();
        sp.Append(prstGeom);
        sp.Append(new DocumentFormat.OpenXml.Drawing.NoFill());

        DocumentFormat.OpenXml.Drawing.Spreadsheet.Picture picture = new DocumentFormat.OpenXml.Drawing.Spreadsheet.Picture();
        picture.NonVisualPictureProperties = nvpp;
        picture.BlipFill = blipFill;
        picture.ShapeProperties = sp;

        Position pos = new Position();
        pos.X = x;
        pos.Y = y;
        Extent ext = new Extent();
        ext.Cx = extents.Cx;
        ext.Cy = extents.Cy;
        AbsoluteAnchor anchor = new AbsoluteAnchor();
        anchor.Position = pos;
        anchor.Extent = ext;
        anchor.Append(picture);
        anchor.Append(new ClientData());
        wsd.Append(anchor);
        wsd.Save(dp);
    }
    catch (Exception ex)
    {
        throw ex; // or do something more interesting if you want
    }
}

protected static void InsertImage(Worksheet ws, long x, long y, string sImagePath)
{
    InsertImage(ws, x, y, null, null, sImagePath);
}

As of now, it supports only the image formats gif, jpg (jpeg) and png. However, there’s no stopping you from adding support for bmp, tiff, emf and other image formats supported by the ImagePartType enumeration.

Download the source code and the resulting Excel file.

Have fun.

There’s updated material and source code, together with more information on how to work with Open XML. Click here to find out more.