Wednesday, March 31, 2010

Printing and .NET

I needed to print a pattern to help mount a scope, which in turn made me learn .NET printing. As it turns out, it is really straightforward.

Here's an abbreviated guide.

First, add references to System.Drawing and System.Windows.Forms (the latter is for the printer selection dialog box).

Second, add the following usings:
using System.Drawing;
using System.Drawing.Printing;
using System.Windows.Forms;

Third, subclass the PrintDocument class and override OnPrintPage:
class PrintSomething : PrintDocument
{
    protected override void OnPrintPage(PrintPageEventArgs e)
    {
        ...
    }
}

The available paper space is defined by DefaultPageSettings.PrintableArea which is a rectangle with the coordinate system at (Left, Top) and extending down to (Right, Bottom). The units are in 1/100th of an inch, and the drawing coordinates are also in the same units. So for example, to draw a one inch circle in the middle of the page...
float middleX = (DefaultPageSettings.PrintableArea.Right +
    DefaultPageSettings.PrintableArea.Left) / 2.0F;
int middleY = (DefaultPageSettings.PrintableArea.Bottom +
    DefaultPageSettings.PrintableArea.Top) / 2.0F;
Pen p = new Pen(Color.Black, 4);
e.Graphics.DrawEllipse(p, middleX - 50.0F, middleY + 50.0F, 100.0F, 100.0F);

Finally, the following code displays the printer selecting dialog and renders the picture if user clicks OK.
static void Main(string[] args)
{
    PrintSomething lines = new PrintSomething();
    PrintDialog dlg = new PrintDialog();
    dlg.Document = lines;
    if (dlg.ShowDialog() == DialogResult.OK)
        lines.Print();
}

So here is the final program:
//-----------------------------------------------------------------------
// <copyright file="Program.cs" company="Sergey Solyanik">
// Copyright (C) Sergey Solyanik.
//
// This file is subject to the terms and conditions of the Microsoft Public License (MS-PL).
// See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL for more details.
// </copyright>
//----------------------------------------------------------------------- 
using System;
using System.Drawing;
using System.Drawing.Printing;
using System.Windows.Forms;

/// <summary>
/// This code prints a target that can be used for scope alignment.
/// </summary>
class PrintTarget : PrintDocument
{
    /// <summary>
    /// Prints out a target.
    /// </summary>
    /// <param name="args"> Arguments array. Not used. </param>
    static void Main(string[] args)
    {
        PrintTarget lines = new PrintTarget();
        PrintDialog dlg = new PrintDialog();
        dlg.Document = lines;
        if (dlg.ShowDialog() == DialogResult.OK)
            lines.Print();
    }

    /// <summary>
    /// Renders the page.
    /// </summary>
    /// <param name="e"> Printer parameters and graphics. </param>
    protected override void OnPrintPage(PrintPageEventArgs e)
    {
        Graphics g = e.Graphics;
        Pen p1 = new Pen(Color.Black, 0.5F);
        Pen p2 = new Pen(Color.Black, 1);
        Pen p4 = new Pen(Color.Black, 4);

        float step = 100.0F / 16.0F;

        int middleX = (int)((DefaultPageSettings.PrintableArea.Right -
            DefaultPageSettings.PrintableArea.Left) / step) / 2;
        int boundX = ((int)((DefaultPageSettings.PrintableArea.Right -
            DefaultPageSettings.PrintableArea.Left) / 200.0F)) * 16;
        int middleY = (int)((DefaultPageSettings.PrintableArea.Bottom -
            DefaultPageSettings.PrintableArea.Top) / step) / 2;
        int boundY = ((int)((DefaultPageSettings.PrintableArea.Bottom -
            DefaultPageSettings.PrintableArea.Top) / 200.0F)) * 16;

        float top = DefaultPageSettings.PrintableArea.Top +
            (middleY - boundY) * step;
        float bottom = DefaultPageSettings.PrintableArea.Top +
            (middleY + boundY) * step;
        float left = DefaultPageSettings.PrintableArea.Left +
            (middleX - boundX) * step;
        float right = DefaultPageSettings.PrintableArea.Left +
            (middleX + boundX) * step;
        for (int i = 0; i < boundX; ++i)
        {
            Pen p;
            if (i % 16 == 0)
                p = p4;
            else if (i % 8 == 0)
                p = p2;
            else
                p = p1;

            g.DrawLine(p,
                DefaultPageSettings.PrintableArea.Left +
                (middleX + i) * step,
                top,
                DefaultPageSettings.PrintableArea.Left +
                (middleX + i) * step,
                bottom);

            g.DrawLine(p,
                DefaultPageSettings.PrintableArea.Left +
                (middleX - i) * step,
                top,
                DefaultPageSettings.PrintableArea.Left +
                (middleX - i) * step,
                bottom);
        }

        for (int i = 0; i < boundY; ++i)
        {
            Pen p;
            if (i % 16 == 0)
                p = p4;
            else if (i % 8 == 0)
                p = p2;
            else
                p = p1;

            g.DrawLine(p,
                left,
                DefaultPageSettings.PrintableArea.Top +
                (middleY + i) * step,
                right,
                DefaultPageSettings.PrintableArea.Top +
                (middleY + i) * step);

            g.DrawLine(p,
                left,
                DefaultPageSettings.PrintableArea.Top +
                (middleY - i) * step,
                right,
                DefaultPageSettings.PrintableArea.Top +
                (middleY - i) * step);
        }

        for (int i = 1; i < 4; ++i)
        {
            g.DrawEllipse(p4,
                DefaultPageSettings.PrintableArea.Left +
                (middleX - 8 * i) * step,
                DefaultPageSettings.PrintableArea.Top +
                (middleY - 8 * i) * step,
                16 * i * step, 16 * i * step);
        }
    }
}

And here's what it prints:


Hint: Microsoft XPS document writer is a great way to debug printer output without wasting paper.

Thursday, March 4, 2010

South Dakota legislature

Idiocracy is alive and progressing. Here's a quote from a recent resolution just passed in South Dakota declaring global warming null and void. Among the clauses:

"That there are a variety of climatological, meteorological, astrological, thermological, cosmological, and ecological dynamics that can effect world weather phenomena and that the significance and interrelativity of these factors is largely speculative;"

(emphasis mine)

Note the "astrological" (!!!) and the fact that they don't even write proper English - in a resolution, no less (http://www.yourdictionary.com/grammar-rules/affect-effect-grammar.html).

For the Nth time, the global warming "sceptics" (you don't get to be a sceptic in an area where you have no clue whatsoever) have demonstrated their true face - a bunch of ignorant anti-science idiots.

A democracy without an educated demos does not work.

Here's the link...

http://legis.state.sd.us/sessions/2010/Bill.aspx?File=HCR1009P.htm

...and the screen shot, in case they correct it...

Thursday, February 25, 2010

Like, C#

Yesterday I read absolutely hilarious post on Like, Python (http://www.staringispolite.com/likepython/) - a valleygirl/hillbilly dialect of Python. It was a big hit at work, and people immediately started asking me about the C# version.

You know our motto - we deliver!

So after 30 minutes of stitching together pieces from Malevich's Syntax Highlighter (to allow proper treatment of comments and string constants) (http://malevich.codeplex.com), and Scriptster (http://scriptster.codeplex.com), I present you with Like, C#.

Like, C# uses the same extended set of "keywords" as Like, Python, so we can write this code:

ohai

totally using System man;

uh class like Program
{
ok static void Main(string[] args) bro
{
just Console.Write("yo! what's your name?");
ok so like string name = Console.ReadLine();

if actually (name.Equals(""))
omg toootally just return;

um yeah

plz Console.WriteLine(like "Hi {0}, nice to meet you!", name);
}
}


Full list of "keywords" is here:

  • Valleygirl: omg, so, like, totally, right, toootally

  • Frat guy: friggin, fuckin, dude, man, bro, broheim, broseph

  • Internets: lol, rofl, teh, ohai, plz

  • Snoop: yo, homey, homeboy, sup, dog, shit, girl, ma, biatch, ho, shiiit

  • Local: wicked, hella, anyways

  • Misc: just, hey, yeah, ok, um, uh, ah, actually, something



And here's the full source of Like, C# compiler if you want to adopt this new, rapidly growing language:

//-----------------------------------------------------------------------
// <copyright file="Program.cs" company="Sergey Solyanik">
// Copyright (C) Sergey Solyanik.
//
// This file is subject to the terms and conditions of the Microsoft Public License (MS-PL).
// See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL for more details.
// </copyright>
//-----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace LikeCSharp
{
/// <summary>
/// Implements Like, C# compiler.
/// </summary>
class Program
{
/// <summary>
/// Extension keywords.
/// </summary>
private static string[] keywords =
{
"omg", "so", "like", "totally", "right", "toootally",
"friggin", "fuckin", "dude", "man", "bro", "broheim",
"broseph", "lol", "rofl", "teh", "ohai", "plz",
"yo", "homey", "homeboy", "sup", "dog", "shit", "girl",
"ma", "biatch", "ho", "shiiit", "wicked", "hella", "anyways",
"just", "hey", "yeah", "ok", "um", "uh", "ah", "actually",
"something"
};

/// <summary>
/// Main entry point.
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
string runtimeEnvironmentPath =
System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory();
if (runtimeEnvironmentPath.EndsWith("\\"))
{
runtimeEnvironmentPath = runtimeEnvironmentPath.Substring(
0, runtimeEnvironmentPath.Length - 1);
}

string compilerExe = Path.Combine(runtimeEnvironmentPath, "csc.exe");

// However, if it's 2.0, there is a compiler-only extension (3.5).
// If 3.5 is indeed installed, use that.
string baseFrameworkPath = Path.GetDirectoryName(runtimeEnvironmentPath);
string runtimeName = Path.GetFileName(runtimeEnvironmentPath);
if ("v2.0.50727".Equals(runtimeName))
{
string compilerExe35 = Path.Combine(
Path.Combine(baseFrameworkPath, "v3.5"), "csc.exe");
if (File.Exists(compilerExe35))
{
compilerExe = compilerExe35;
}
}

StringBuilder sb = new StringBuilder();
bool firstArg = true;
HashSet<string> files = new HashSet<string>();

string keywordRE = "\"|'|//|/\\*|#?[a-z_0-9]+";
HashSet<string> keywordDictionary = new HashSet<string>(keywords);
Regex keywordRegex = new Regex(
keywordRE, RegexOptions.Compiled | RegexOptions.IgnoreCase);

foreach (string arg in args)
{
if (firstArg)
{
firstArg = false;
}
else
{
sb.Append(' ');
}

string newArg = arg;
if (arg.EndsWith(".lcs"))
{
newArg = Preprocess(arg, keywordRegex, keywordDictionary);
files.Add(newArg);
}

if (newArg.Contains(' '))
{
newArg = '"' + newArg + '"';
}

sb.Append(newArg);
}

Process compiler = new Process();
compiler.StartInfo.UseShellExecute = false;
compiler.StartInfo.FileName = compilerExe;
compiler.StartInfo.Arguments = sb.ToString();

compiler.Start();
compiler.WaitForExit();

Environment.ExitCode = compiler.ExitCode;

foreach (string file in files)
{
File.Delete(file);
}
}

/// <summary>
/// Converts file from Like, C# to normal C#.
/// </summary>
/// <param name="fileName"> File name to process. </param>
/// <param name="keywordRegex"> Regular expression that
/// matches language elements. </param>
/// <param name="keywordDictionary"> Like keywords. </param>
/// <returns> Name of the temporary file. </returns>
static string Preprocess(
string fileName,
Regex keywordRegex,
HashSet<string> keywordDictionary)
{
string tempDir = Environment.GetEnvironmentVariable("TEMP");
tempDir = Path.Combine(tempDir, "LIKECSHARP");
if (!Directory.Exists(tempDir))
{
Directory.CreateDirectory(tempDir);
}

string target = Path.Combine(
tempDir,
Path.GetFileNameWithoutExtension(fileName) + ".cs");

QuoteType quotes = QuoteType.None;
bool inComments = false;

StreamReader reader = new StreamReader(fileName);
StreamWriter writer = new StreamWriter(target);

for (; ; )
{
string s = reader.ReadLine();
if (s == null)
{
break;
}

StringBuilder encoded = new StringBuilder();

int i = 0;
while (i < s.Length)
{
if (quotes == QuoteType.Double)
{
int endQuotes = s.IndexOf("\"", i);
if (endQuotes == -1)
{
// The entire string is in quotes.
// The tag will close after the loop.
encoded.Append(s.Substring(i));
break;
}

encoded.Append(s.Substring(i, endQuotes + 1 - i));
if ((endQuotes > 0 && s[endQuotes - 1] != '\\') ||
(endQuotes > 1 && s[endQuotes - 1] == '\\' &&
s[endQuotes - 2] == '\\'))
{
// This is not an escaped quote.
quotes = QuoteType.None;
}

i = endQuotes + 1;
continue;
}

if (quotes == QuoteType.Verbatim)
{
int endQuotes = s.IndexOf("\"", i);
if (endQuotes == -1)
{
// The entire string is in quotes.
// The tag will close after the loop.
encoded.Append(s.Substring(i));
break;
}

if (s.IndexOf("\"\"", endQuotes) == endQuotes)
{
// This is an escaped quote.
encoded.Append(s.Substring(i, endQuotes + 2 - i));
i = endQuotes + 2;
continue;
}

encoded.Append(s.Substring(i, endQuotes + 1 - i));
quotes = QuoteType.None;

i = endQuotes + 1;
continue;
}

if (quotes == QuoteType.Single)
{
int endQuotes = s.IndexOf('\'', i);
if (endQuotes == -1)
{
// The entire string is in quotes.
// The tag will close after the loop.
encoded.Append(s.Substring(i));
break;
}

encoded.Append(s.Substring(i, endQuotes + 1 - i));
if ((endQuotes > 0 && s[endQuotes - 1] != '\\') ||
(endQuotes > 1 && s[endQuotes - 1] == '\\' &&
s[endQuotes - 2] == '\\'))
{
// This is not an escaped quote.
quotes = QuoteType.None;
}

i = endQuotes + 1;
continue;
}

if (inComments)
{
int endComments = s.IndexOf("*/", i);
if (endComments == -1)
{
// The entire string is in comments.
// The tag will close after the loop.
encoded.Append(s.Substring(i));
break;
}

inComments = false;

encoded.Append(
s.Substring(i, endComments + 2 - i));
i = endComments + 2;
continue;
}

Match nextMatch = keywordRegex.Match(s, i);
if (!nextMatch.Success)
{
encoded.Append(s, i, s.Length - i);
break;
}

if (i != nextMatch.Index)
{
encoded.Append(s, i, nextMatch.Index - i);
i = nextMatch.Index;
}

string matched = nextMatch.Groups[0].Value;

if (matched.Equals("'"))
{
encoded.Append("'");
quotes = QuoteType.Single;
++i;
continue;
}

if (matched.Equals("\""))
{
encoded.Append("\"");
quotes = QuoteType.Double;
++i;
continue;
}

if (matched.Equals("@\""))
{
encoded.Append("@\"");
quotes = QuoteType.Verbatim;
i += 2;
continue;
}

if (matched.Equals("//"))
{
// The rest of the line is comments.
encoded.Append(s.Substring(i));
break;
}

if (matched.Equals("/*"))
{
// Comments start.
encoded.Append("/*");
inComments = true;
i += 2;
continue;
}

if (keywordDictionary.Contains(matched))
{
// Keyword. Eat it.
i += matched.Length;
}
else
{
encoded.Append(matched);
i += matched.Length;
}
}

writer.WriteLine(encoded.ToString());
}

reader.Close();
writer.Close();

return target;
}

/// <summary>
/// Types of quoted string.
/// </summary>
enum QuoteType
{
None,
Single,
Double,
Verbatim
}
}
}

Sunday, January 17, 2010

William Buckley on General Franco

"General Franco is an authentic national hero. It is generally conceded that he above others had the combination of talents, the perseverance, and the sense of righteousness of his cause, that were required to wrest Spain from the hands of the visionaries, ideologues, Marxists and nihlistis that were imposing on her, in the thirties, a regime so grotesque as to do violence to the Spanish soul, to deny, even Spain's historical identity."

http://www.slate.com/id/2185301/pagenum/2

While we're at it, another quote, this time from Richard Nixon: "General Franco was a loyal friend and ally of the United States."

http://en.wikipedia.org/wiki/Francisco_Franco

When I am saying that in the US we're having a choice between fascists on the right, and the center-right party on the "left", people are thinking that I am exaggerating. I am not.

Friday, December 4, 2009

Sr Oracle needed at Bank of America

...presumably so that they could predict the bursting of the next bubble ahead of time.

30000 new troops for Afghanistan - yeeeeeeehaw!

Tuesday, December 1, 2009

Web applications!



2 browsers (IE and Firefox) running Ajax.org demo are maxing out T9300 on my laptop - a Core 2 Duo running at 2.5 GHz. This is plugged in.

Try it yourself:

http://www.ajax.org/public/presentation/tae/presentation2.html#home

Wednesday, November 25, 2009

Modern capitalism

The top 5 execs at Lehman Brothers made $1B from 2000 to 2008. The top 5 execs at Bearn Stearns made $1.4B during the same period.

"The whole idea of capitalism is that the people provide the capital and the executives take care of it for us. In this case, the people provided the capital, and the executives took it."

http://crooksandliars.com/susie-madrak/lehman-bros-bear-stearns-ceos-walked

Tuesday, October 20, 2009

Matt Taibbi on naked short selling, or what killed Bear Stearns

Really awesome article on naked shorts. There are only a handful of news organizations who do real journalism these days, and, surprisingly, Rolling Stones seems to be one of these few. I am also very impressed with Bloomberg reporting recently - it looks like after Murdoch bought WSJ the best people went to Bloomberg.

http://www.rollingstone.com/politics/story/30481512/wall_streets_naked_swindle/print

Here's the really scary thing - we've lost the integrity of our financial markets.

The reason US was so successful in attracting capital was the transparency with which financial markets here used to operate, and strong regulations that ensured this transparency - for the most part, the legacy of Great Depression. However, over the last couple of decades these regulations have been weakened, and now we're back to the turn of the century stock swindles and off the books accounting (http://www.creditloan.com/blog/mark-to-market-accounting-changes-favor-banks/).

All this - coupled with the dollar fall - can mean only one thing: US will certainly lose its status as a financial capital of the world within the next decade.

In other news, marijuana legislation has been relaxed (http://finance.yahoo.com/news/AP-Newsbreak-New-medical-apf-4109207182.html?x=0&sec=topStories&pos=main&asset=&ccode=). So instead of worrying about markets, botched wars, and disappearing industrial base, the population can just get stoned! Finally, a government that knows how to rule...

Friday, October 16, 2009

Government rationing of health care

There were all these inane reports in the press about "Obama's suicide panels" and government rationing the health care - all widely covered.

Let's see what they say about this beauty - an insurance company says that they will only cover a woman if she gets sterilized - because she had a C-section in the past.

Under the hill

Check out where you can deliver a birthday present on 1-800-flowers.com!

Monday, October 12, 2009

Think pay for performance works? Think again...

http://www.ted.com/talks/dan_pink_on_motivation.html

This TED talk alludes to a number of social experiments where adding monetary incentive has significantly decreased the productivity of creative work.

Extremely enlightening, a must watch for every manager - and yet another proof that the claim that the Wall Streeters need to be paid exorbitant amounts of money to retain "the best and the brightest" is just a crapload of bull.

United States v. 8,800 Pounds, More or Less of Powdered White Egg Product

"United States v. 8,800 Pounds, More or Less of Powdered White Egg Product, et al., No. 07-3671, 2008 U.S. App. LEXIS 26098 (8th Cir. Dec. 24, 2008)(defendant’s importation of egg whites from Peru without first obtaining a pasteurization certificate from the Peruvian government and without obtaining authorization from the Food Safety Inspection Service violated the Egg Products Inspection Act, which could result in the condemnation and destruction of the egg whites; trial court’s granting of summary judgment upheld)."

It's hidden somewhere around the middle of the list on this page: http://www.calt.iastate.edu/aglawrl.html

I kid you not!

Wednesday, October 7, 2009

Big Pharma and research

I love it when they say that the pharmaceuticals are so expensive in the US because the companies need this money to finance research. In reality, if you look at a typical big pharma income statement, you will discover that only ~20% of its expenses are research - the rest is mostly sales and marketing.

But here's another proof beyond any reasonable doubt that what big pharma is all about is sales - its donations to political parties. Researchers giving money to repubs? Basically, the same people who say that Earth is 10,000 years old, and dinosaurs walked the planet alongside people, radiocarbon dating be damned?

This political contributions profile is more in line with what you expect from used car salespeople than the research and development organizations...

http://www.goodguide.com/contributions#sector=Pharmaceuticals



By comparison, here's how the software sector looks like:

http://www.goodguide.com/contributions#sector=Computer/Internet&sort=total&query=

Tuesday, October 6, 2009

Bravo, Apple! You have class!

Apple quits the U.S. Chamber of Commerce over its ‘frustrating’ global warming denialism.

http://thinkprogress.org/2009/10/05/apple-quits-chamber/

Friday, October 2, 2009

TFS 2010 will be much easier to install

I've spent a week trying to get TFS 2008 installed on my home infrastructure. It was the most painful piece of software that I've ever installed. And it looks like I was not the only one who felt that:

"Installing TFS has been a pain point for years. Although it’s gotten better, 2010 represents a quantum leap. The TFS installer now has 3 wizards: Basic, Standard and Advanced. The big innovation is the new “Basic” install wizard. It is a Next, Next, Next install experience that allows you to install and configure TFS in about 20 minutes or less (assuming .NET and SQL Express are already on your computer – a little longer if TFS has to install them for you). Both will already be there if you’ve installed VS 2010. The Basic wizard will install and configure IIS (if it’s not already there), install and configure SQL Express (if it’s not already there), and install and configure TFS. The only thing that really pains me is installing .NET 4.0 requires a reboot :(."

http://blogs.msdn.com/bharry/archive/2009/10/01/tfs-2010-for-sourcesafe-users.aspx

Sunday, September 20, 2009

Guns and morons

I am slowly building up my collection of World War II and former communist block firearms. This involves scouring the internet with searches on certain keywords.

Recently, I had a sad realization - most of the US gun-owning community is not mentally qualified to operate a fork, rather than own a gun. Which may explain an extremely high rate of gun-related death in the United States - 30896 in 2006.

Here's a representative post (http://answers.yahoo.com/question/index?qid=20080920100316AAlVQMH):

"Should i buy an SKS, AK-47 or AR-15 rifle?

the cheaper the better, but not if it is total garbage. The purpose of owning one for me is to have a assault rifle before they are illegal. i want something that can pack a punch when the world goes to crap so i can defend my family. yes, the purpose would be to kill, so go somewhere else hippies, this is a hypothetical situation i would like to be prepared for if it ever happens."

"Tactical advantage", "firefight" are the terms that litter the gun boards, so instead of answering these questions in dozens of places, I decided to come up with one meta-response here.

"Dear Moron,

Let me try to answer your question from the liberal/hippie point of view, since this angle is typically not covered on the gun boards you are frequenting.

First of all, this is the stupidest thing I read on the internet today! Why, you might ask? Well, there are multiple reasons.

First, do you really expect that hippies will attack you and your family? Hippies? Seriously? Like this guy?


Second, if us hippies/liberals wanted to attack you, surely we wouldn't storm your house? Did you know that there is a strong positive correlation between education and liberal views? You might not realize that, but one skill that they teach in college is thinking. So if we really did want to get you, we would surely be able to devise a better approach.

For example, we could just wait outside your house, and spray you with bullets from a safe distance when you come out to buy groceries. Or if we wanted to force the events, why not setting your house on fire and then just shoot everything that comes out?

But in reality, we wouldn't even bother with this at all. We'd just send a black helicopter (http://en.wikipedia.org/wiki/Black_helicopter). As you know, being government/UN freaks, we have plenty at our command. It would take one missile to have your house look like this, from a safe distance:


Do you really think that AK-47 vs AR-15 would make a material difference here?

Finally, even if you really did get into one of the "firefights", I bet you would not have much luck in a "tactical combat situation". I've played with your kind in Halo. You will be the biggest target, in the center of the field, collecting all the bullets.


I bet like with any other profession, it takes years of hard work to train a soldier - not an act of buying a gun.

So take my advice - instead of wasting money on something that you aren't mentally qualified to operate and won't be able to use, buy a book. Or sign up for a history or biology class at your local community college. You really could use the extra IQ so that next time people won't look at your writing and say - Geez, this is the stupidest thing I've seen on the internet today!"

Sunday, September 13, 2009

Beowulf

The Queen Anne Blockbuster is going out of business, and, among other things, I picked up a copy of Beowulf (http://www.beowulfmovie.com/) for a couple of dollars.

Boy was this a weird experience! So weird that, in a sense, it deserves to be a cult movie - a la Striptease. For most of the movie we couldn't tell if these guys were being serious, or trying to spoof something. I am still not entirely sure. We laughed throughout big part of it.

From a really overdone dialog ((think of Gimili’s boasting in The Lord of the Rings, multiply that by 800, and you have the most humble of Beowulf’s pronouncements), to nude Angelina Jolie's feet, that seamlessly merged lack of shoes with high heels, to the way they were hiding - in a really conspicuous way - Beowulf's genitalia as he was fighting Grendel in the nude (this was very, very similar to the scene where Bart rides through the streets of Springfield in Simpsons The Movie) - it looked more like a comedy than what the movie was trying to present.

As a comedy it might have been mildly funny, though not hysterical. If you enjoyed Striptease, watch this one. Otherwise, it’s probably not worth the hour and a half.

Saturday, September 12, 2009

If you don't like George Bush, you should take a look at his voters

http://www.telegraph.co.uk/news/worldnews/northamerica/usa/6173399/Charles-Darwin-film-too-controversial-for-religious-America.html

"The film was chosen to open the Toronto Film Festival and has its British premiere on Sunday. It has been sold in almost every territory around the world, from Australia to Scandinavia.

However, US distributors have resolutely passed on a film which will prove hugely divisive in a country where, according to a Gallup poll conducted in February, only 39 per cent of Americans believe in the theory of evolution."

Friday, September 11, 2009

An oldie...

...but a goodie!

"Cheney Waits Until Last Minute Again To Buy Sept. 11 Gifts"

http://www.theonion.com/content/news/cheney_waits_until_last_minute

Sunday, September 6, 2009

How much for shipping, again?

Check out the Fedex Next Day rate on this $10 battery...


Think this is egregiously expensive? It is, but it still comes out way ahead of our local Best Buy where a similar (actually, less powerful) battery retails for $39.99 plus tax...

Wednesday, August 26, 2009

Classy Sarah Palin fans react to Kennedy's death

Just look at the spelling in these gems. The correlation between the level of education and political views is very real...

"thank you for maintaining my belief in you as a real american, however this country is now much better off, one less socialist, anti freedom senator."

"Now if we could just talk God into taking Arlin Spector, Harry Reid,and Nancy Pelosi America would be Eutopia!"

"good riddens"

"If he makes it into Heaven (& I doubt he will with his stance on abortion) I hope that God makes him babysit all the aborted children for eternity. God have mercy on his soul."

"Ted Kennedy dying has made my day...."

"He cannot fillibuster God. Good ridencance to a sorry person."

"It's about time, we can only hope Pelosi and Ried will be joining him very soon. All 3 of them should be buried in Moscow for whom they work so tirelessly."

http://www.alternet.org/blogs/peek/142220/sarah_palin%27s_facebook_%27friends%27_celebrate_ted_kennedy%27s_death%3A_%22one_less_socialist%2C%22_%22good_riddens%22/

"Brilliant" marketing

I love it when people say that Microsoft has terrible products which only succeed because of our amazing marketing.

http://www.pcworld.com/article/170820/microsoft_apologizes_for_racially_charged_image_alteration.html

Friday, August 21, 2009

Introducing Black Square, a MS Word document review system

Black Square is to specs what Malevich is to code. This release is 0.1 - treat it as a preview, it has not been field-tested at all. There are known important bugs. But if you do feel adventurous, give it a whirl!

http://blacksquare.codeplex.com/

Big thanks to Eric White (http://blogs.msdn.com/ericwhite/) who coded the most important part of the system - the document merger.

For more background on Black Square, see the blog post where it was originally introduced: http://1-800-magic.blogspot.com/2009/07/lockingunlocking-word-doc-files.html

Physics lessons in Alabama

Driving to work today I was listening to yesterday's Marketplace podcast on my Zune.

They were reading a piece about schools in Alabama having big trouble finding teachers in math and sciences. Imagine recruiting a biology teacher who'd have to teach "alternatives" to evolution! - must be super hard indeed.

So they are importing teachers from Philippines. One school mentioned in the show had its first physics class in several years(!) and it had just 17 people enroll. No wonder such a staggering percentage of US population is convinced that Earth is 6000 years old...

Wednesday, August 19, 2009

Just say no to political correctness

'Something strange has happened in America in the nine months since Barack Obama was elected. It has best been summarised by the comedian Bill Maher: "The Democrats have moved to the right, and the Republicans have moved to a mental hospital."'

http://www.independent.co.uk/opinion/commentators/johann-hari/johann-hari-republicans-religion-and-the-triumph-of-unreason-1773994.html

We liberals brought this upon ourselves. We should stop acting like the idiots' point of view is entitled to respect and equal treatment in the media. It's not. The cooks, the crooks, and the cons - including the neocons - need to be called for what they are, loud and clear.

In particular, we should stop trying to prove that science and religion are somehow compatible. They are not.

Science requires preponderance of evidence. For science, if something is unobservable in principle, it does not exist.

Religion requires faith in tales written thousands of years ago by semi-literate shepherds, claiming the miracles that have never been observed in practice.

Most Americans are big boys and girls - it's OK to have them face the hard choice.

Either you believe in science and enjoy the fruits of labor of these godless scientists (http://people-press.org/report/?pageid=1550) - including antibiotics, computers, jet travel, and sanitation. Or choose supernatural and go back to the middle ages and 40 years average life span.

Trying to have it both ways is like trying to lose weight without a diet. It simply does not work.

Tuesday, August 18, 2009

Dude, have you heard of Netflix?

I am channeling the Fake Steve Jobs now, but damn it, two insane press stories a day...

> That's largely because they're not a darn thing worth watching or
> playing that uses Moonlight/Silverlight. Go ahead visit the
> Silverlight site; let me know when you find something compelling. I didn't.

http://blogs.computerworld.com/14570/moonlight_2_arrives_and_falls_flat_on_its_face

Netflix of course uses Silverlight for streaming the part of its content that's available online.

A pundit tries OpenGoo. Hilarity ensues.

OpenGoo is an open source counterpart to Google Docs. The idea is that you download and host it on your own servers. This C|Net reporter gets really, really confused by this concept:
http://news.cnet.com/8301-13505_3-10310817-16.html

In case they remove this article, here's the screen shot (click on it to read):


This is even funnier than Investor Business Daily's "Steven Hawking would have no chance under British health care" piece... I am wondering, what kind of college degrees do these people have? And what kinds of colleges graduate these "journalists"?

Friday, August 14, 2009

British healthcare

Apparently there's been a bunch of ads on TV feeding US populace the horror stories about how horrible NHS is.

This is what real British - you know, the ones actually living there and using British health care (where doctors by and large are government employees and the health care costs are almost entirely paid by the government) - have to say about it:

"Watching these debates is like reading National Geographic. It's just impossible, from a European perspective, to understand what these people are on about. Their political views seem as backwards and removed from the world we live in as a shaman casting magic spells."

http://www.politics.co.uk/analysis/health/comment-i-ll-never-understand-americans-$1318652.htm

Also, from Investor's Business Daily editorial on 8/3: "People such as scientist Stephen Hawking wouldn't have a chance in the U.K. where the National Health Service would say the quality of life of this brilliant man, because of his physical handicaps, is essentially worthless."

After much ridiculing on the interwebs, they removed the sentence. Pity, it was yet another proof that money != brain.

Monday, August 10, 2009

Bingin' Malevich

I've done a bit of egosurfing today (http://en.wikipedia.org/wiki/Egosurfing) and found that my code review system (http:/malevich.codeplex.com) shows up as #2 when searching for Malevich in Bing.


Frankly, I was surprised.

By comparison, Google search has it towards the bottom of the second page. Mondrian - Google's own code review tool which inspired Malevich - is at the very bottom of the first page.

Now, there was a fresh batch of conspiracy theories fomented by the "technical" pundits that claim that Microsoft uses Bing put down Apple (http://www.pcworld.com/article/169750/bing_search_reveals_promicrosoft_results.html). To me this is like a toddler pushing a nuclear submarine to help it go faster: Windows certainly does not need help from Bing to compete with Apple.

I think there is a very simple explanation for both Malevich and Mac vs PC phenomena. The search engine certainly uses clickthrough data to guide its searches. Bing's biggest market share in the geek population is Microsoft employees - there are tens of thousands people here that use Bing. The overall market share of Bing is small enough so that a few thousand users can swing the search results considerably - bringing Malevich-the-code-review-system (which is also extremely popular here) and articles critical of Apple on top.

Admittedly, this is not nearly as juicy as the "evil Microsoft adjust the search results to favor itself" conspiracy theories, but it appears to be the simplest one :-).

http://en.wikipedia.org/wiki/Occam's_Razor

Saturday, August 8, 2009

GI Joe, the movie

No, I have not seen it. But I've read Roger Ebert review of it, which is a masterpiece on its own.

http://rogerebert.suntimes.com/apps/pbcs.dll/article?AID=/20090807/REVIEWS/908079997

"The two teams also each have a skilled Ninja fighter from Japan. Why is this, you might ask? Because Japan is a huge market for CGI animation and videogames, that's why. It also has a sequence set in the Egyptian desert, although there are no shots of dead robots or topless pyramids. And Cobra headquarters are buried within the miles-deep ice of Arctic. You think construction costs are high here. At one point the ice cap is exploded real good so it will sink and crush the G. I. Joe's submarine. We thought ice floated in water but, no, you can see big falling ice chunks real good here. It must be only in your Coke that it floats."

Tuesday, August 4, 2009

Developer Connection

If you thought (like I did) that TFS documentation was bad, check out this beauty:

http://developer.apple.com/documentation/appleapplications/Reference/WebKitDOMRef/DOMSelection_idl/Classes/DOMSelection/index.html

As a mental exercise, try to guess what does empty() do? Does it empty the selection? Or does it return true if the selection is empty?

Monday, August 3, 2009

Obama's birth certificate - a Kenyan angle!

FROM: Mr. James Thambo,
Email:jamesthambo@workmail.co.za
jamesthambo@msn.com
TO: Orly Taitz, Esq (Orly.Taitz@aol.com)

KEEP AS CONFIDENTIAL

Dear Mrs. Orly Taitz:

I am Mr. James Thambo, a Barrister to US President Barrak Hussein Obama's great great uncle Matimor Thambo Hussein. He has died 3 days ago after being sick with Cancer, and now I am in Charge for Executing his In-heritance. Before he died he gave me a Birth Certificate for US President Barrak Hussein Obama issued By Kenian Republic in 1961 that Proves that Barrak Hussein Obama is not a US Citizen. It is Numbered 47O44 and Executed with All Appropriate Authority. I will give you the name of my bank where said Certificate is stored and other important information if I receive a positive reply from you.

I want you to be my partner, to secretly transfer the Certificate of Birth to the United States where it would be Sold on ebay. My business Partners here has estimated the Auction Value for the US President Barrak Hussein Obama's Birth Certificate to be $100,000,000 (U. S. Dollars!). I cannot Sell it myself Because I am a Barrister to the late US President Barrak Hussein Obama's great great uncle Matimor Thambo Hussein and they would suspect me if I sell it myself. All you would have to do is place this Item for bid on Ebay, and receive the money into your account.

Your share will be 30% which is $30,000,000 (U. S. Dollars!). My own share will be 69%, which is $69,000,000. We shall keep 1% which is $1,000,000 for expenses. Reach me immediately by mail so that I can give you further details. Also provide me your direct tel/fax to reach you, and your bank account number, and your credit card so I can Certify the Authenticity.

Thank you and God Bless,

Mr. James Thambo.

------------------------

http://www.huffingtonpost.com/2009/08/03/kenyan-birth-certificate_n_249850.html

Friday, July 31, 2009

Removing duplicate comments from a word document

As I wrote before, I am working on Malevich-like system (http://malevich.codeplex.com) for reviewing specs in the same way we're reviewing code.

This work is based on Eric White's excellent blog post about merging comments from two identical files (http://blogs.msdn.com/ericwhite/archive/2009/07/28/merging-comments-from-multiple-open-xml-documents-into-a-single-document.aspx).

The idea is to have a web site where one uploads a Word document, the reviewers then download a locked copy of it which only allows adding comments. They then use Word to comment, and upload the files back. The server merges all comments (using Eric's code) back into the master copy. Every person who downloads the document afterwards gets the comments from all previous reviewers.

While working on this system, I had to add two things in terms of comment management.

First, I had to lock files so only adding comments is allowed. The code for this is here: http://1-800-magic.blogspot.com/2009/07/lockingunlocking-word-doc-files.html.

Second, Eric's code merges the comments by adding all comments from one document to the other. Unfortunately what this means is that after the very first reviewer has added his or her comments, every time someone else downloads the copy with these comments, adds more, and uploads the document back, the original set of comments gets duplicated. So I had to write code that cleans up this duplication.

The comments in the Word files leave in a special section accessible through MainDocumentPart.WordprocessingCommentsPart.Comments of the WordprocessingDocument class. They can be enumerated as follows:

WordprocessingDocument doc = WordprocessingDocument.Open(args[0], true);
foreach (Comment c in doc.MainDocumentPart.WordprocessingCommentsPart.Comments)
Console.WriteLine("{0} {1}:{2}", c.Id, c.Author, c.InnerText);


This section contains the comments themselves, but it does not have any information as to where the comments attach to the actual text in the Word document. Instead the comments attach via commentRangeStart, commentRanveEnd, and commentReference elements that are intersperced into the text of the paragraph:

<w:p>
<w:r>
<w:t xml:space="preserve">This is a test</w:t>
</w:r>
<w:commentRangeStart w:id="0" />
<w:commentRangeStart w:id="2" />
<w:commentRangeStart w:id="4" />
<w:r>
<w:t>document</w:t>
</w:r>
<w:r>
<w:rPr>
<w:rStyle w:val="CommentReference" />
</w:rPr>
<w:commentReference w:id="0" />
</w:r>
<w:commentRangeEnd w:id="0" />
<w:r>
<w:rPr>
<w:rStyle w:val="CommentReference" />
</w:rPr>
<w:commentReference w:id="2" />
</w:r>
<w:commentRangeEnd w:id="2" />
<w:r>
<w:rPr>
<w:rStyle w:val="CommentReference" />
</w:rPr>
<w:commentReference w:id="4" />
</w:r>
<w:commentRangeEnd w:id="4" />
<w:r>
<w:t>.</w:t>
</w:r>
</w:p>


To the developer, these elements are accessible from the root element of the Word document's MainDocumentPart:

foreach (CommentReference cRef in
doc.MainDocumentPart.RootElement.Descendants<CommentReference>())
Console.WriteLine("Found reference for {0}", cRef.Id);

foreach (CommentRangeStart baseRs in
doc.MainDocumentPart.RootElement.Descendants<CommentRangeStart>())
Console.WriteLine("Found range start for {0}", baseRs.Id);

foreach (CommentRangeEnd baseRe in
doc.MainDocumentPart.RootElement.Descendants<CommentRangeEnd>())
Console.WriteLine("Found range end for {0}", baseRe.Id);


Unlike the beauty of almost Lisp-like functional code that Eric wrote to merge comments, the code below goes through some contortions trying to determine that comments that have the same text and author really do start and end in the same place of the Word document. Location is important in determining the equivalence of comments because it is easy to imagine a whole bunch of separate, different comments with the same text, for example, "Here, too.", that would otherwise be considered equal.

To compile the code, you need to get and install Microsoft's OpenXML SDK 2.0 from here: http://www.microsoft.com/downloads/details.aspx?FamilyId=C6E744E5-36E9-45F5-8D8C-331DF206E0D0&displaylang=en, and add a reference to DocumentFormat.OpenXml assembly which the SDK installer puts in GAC.

Here's the code. It is rather self-explanatory: it collects all the relative elements from the document - comments, ranges, and comment reference points, determines which ones are duplicates, then removes the dupes.

There is subtlety that this code relies upon which appears to be true, but technically does not technically have to be - that for the comments that are attached to the same location the commentRangeStart and commentRangeEnd elements have the same sequence - e.g. if comment A's commentRangeStart preceedes comment B's commentRangeStart, then comment A's commentRangeEnd should preceed comment B's commentRangeEnd. While this seems to be true for Word, if you are adopting this code for general purpose OpenXML, I would recomment changing the logic to remove this dependency.


//-----------------------------------------------------------------------
// <copyright>
// Copyright (C) Sergey Solyanik.
//
// This file is subject to the terms and conditions of the Microsoft Public License (MS-PL).
// See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL for more details.
// </copyright>
//-----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Xml.Linq;

using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;

namespace RemoveDuplicateComments
{
/// <summary>
/// Removes duplicate comments in an OpenXML document.
/// </summary>
class Program
{
/// <summary>
/// Removes duplicate comment in an OpenXML document.
/// </summary>
/// <param name="args"> Command line arguments (file name). </param>
static void Main(string[] args)
{
if (args.Length != 1)
{
Console.WriteLine("Usage: removeduplicatecomments filename");
return;
}

Dictionary<int, Comment> comments =
new Dictionary<int, Comment>();
Dictionary<int, string> commentTexts =
new Dictionary<int, string>();
Dictionary<int, CommentRangeStart> commentRangeStarts =
new Dictionary<int, CommentRangeStart>();
Dictionary<int, CommentRangeEnd> commentRangeEnds =
new Dictionary<int, CommentRangeEnd>();
Dictionary<int, OpenXmlElement> commentReferenceParents =
new Dictionary<int, OpenXmlElement>();
HashSet<OpenXmlElement> commentReferenceParentsSet =
new HashSet<OpenXmlElement>();
HashSet<int> idsOfIdenticalStarts = new HashSet<int>();
HashSet<int> idsOfIdenticalEnds = new HashSet<int>();

WordprocessingDocument doc = WordprocessingDocument.Open(args[0], true);
foreach (Comment c in
doc.MainDocumentPart.WordprocessingCommentsPart.Comments)
{
Console.WriteLine("{0} {1}:{2}", c.Id, c.Author, c.InnerText);
int id = int.Parse(c.Id);
comments.Add(id, c);
commentTexts.Add(id, c.Author + " : " + c.InnerText);
}

foreach (CommentReference cRef in
doc.MainDocumentPart.RootElement.Descendants<CommentReference>())
{
Console.WriteLine("Found reference for {0}", cRef.Id);
commentReferenceParents.Add(int.Parse(cRef.Id), cRef.Parent);
commentReferenceParentsSet.Add(cRef.Parent);
}

foreach (CommentRangeStart baseRs in
doc.MainDocumentPart.RootElement.Descendants<CommentRangeStart>())
{
Console.WriteLine("Found range start for {0}", baseRs.Id);

int baseId = int.Parse(baseRs.Id);

commentRangeStarts[baseId] = baseRs;

string baseCommentText = commentTexts[baseId];

CommentRangeStart rs = baseRs;
for (; ; )
{
CommentRangeStart next = rs.NextSibling() as CommentRangeStart;
if (next == null)
break;

rs = next;

int rsId = int.Parse(rs.Id);
if (baseCommentText == commentTexts[rsId])
idsOfIdenticalStarts.Add(rsId);
}
}

foreach (CommentRangeEnd baseRe in
doc.MainDocumentPart.RootElement.Descendants<CommentRangeEnd>())
{
Console.WriteLine("Found range end for {0}", baseRe.Id);

int baseId = int.Parse(baseRe.Id);

commentRangeEnds[baseId] = baseRe;

string baseCommentText = commentTexts[baseId];

CommentRangeEnd re = baseRe;
for (; ; )
{
OpenXmlElement nextEl = re.NextSibling();
while (nextEl != null && commentReferenceParentsSet.Contains(nextEl))
nextEl = nextEl.NextSibling();

re = nextEl as CommentRangeEnd;
if (re == null)
break;

int reId = int.Parse(re.Id);
if (baseCommentText == commentTexts[reId])
idsOfIdenticalEnds.Add(reId);
}
}

foreach (int id in idsOfIdenticalStarts)
{
if (idsOfIdenticalEnds.Contains(id))
{
Console.WriteLine("Eliminating comment {0}", id);
commentRangeStarts[id].Remove();
commentRangeEnds[id].Remove();
commentReferenceParents[id].Remove();
comments[id].Remove();
}
}

doc.MainDocumentPart.RootElement.Save();
doc.MainDocumentPart.WordprocessingCommentsPart.RootElement.Save();

doc.Close();

Console.WriteLine("All done!");
}
}
}

Apple is replacing Microsoft as a company Linux advocates love to hate

Of course, there's still plenty of hate for everyone... still, so much fun to watch!

http://www.defectivebydesign.org/blog/jailbreaking-apple-iphone

Monday, July 27, 2009

Locking/unlocking Word doc files programmatically

My team is going through a planning milestone again, and this means reading, reviewing, and approving a lot of specs and design documents.

So for this weekend I was toying with the idea of setting up a clone of Malevich (http://malevich.codeplex.com) for document reviews.

Malevich is of course the tool we (and now a whole bunch of other teams inside and outside Microsoft) are using for code reviews. Its main target is to make commenting easy - you simply click on a line of source code, an edit box opens, you type your comment for that line, and that's it. You can read more about Malevich's inspirations and aspirations here: http://1-800-magic.blogspot.com/2009/01/malevich-introduction.html.

Over the last 7 months Malevich has proven to be a big success. It streamlined code review process in the development team, involved many people in code reviews who otherwise would not be participating, and did wonders for the quality of our code base.

All this made me start thinking about introducing a similar process for spec reviews. After all, a review is a review, right?

The biggest problem with the spec reviews turns out to be the file format. Malevich operates on text files, and so rendering these files on the screen, showing a difference between the two versions of a file, and associating comments with the line turns out to be very simple. Specs (at Microsoft) are traditionally written as Microsoft Word documents.

Word turns out to have a very nice commenting mechanism, but rendering documents on a web page is not nearly as straightforward, and diffing them... that's a whole another project!

While pondering this idea, I ran into this blog post by Eric White: http://blogs.msdn.com/ericwhite/archive/2009/07/05/comparing-two-open-xml-documents-using-the-zip-extension-method.aspx which describes how to determine if two Word documents are the same (modulo comments). The post served as my first introduction into OpenXML, which is the format behind the Word document. Also, I read that Eric was planning a blog post about merging comments from two documents, and this lead me to the following design for the spec review site.

I am going to put together a system very similar to Malevich (let's call it Black Square for now), but instead of text files, it would hold Word documents. To create a review request, a reviewee would upload a document to the server via a web site. Upon upload, the server will lock the Word file in a way that would prevent all modifications to it other than the comments. It will then make the document available for reviewers to download.

To perform a review, the reviewer downloads the document, comments on it using Office reviews functionality, and upload it back to the server. The server will then merge the comments back into the master document, making comments from everybody available to all subsequent reviewers as well as the reviewee.

I've shot Eric an email, and as it turned out, he had already largely completed his merger, and he gave me a preliminary copy to beta test (the final version is now here: http://blogs.msdn.com/ericwhite/archive/2009/07/28/merging-comments-from-multiple-open-xml-documents-into-a-single-document.aspx).

Then I spent part of the weekend coding. After a few hours I had a skeleton web site and needed to code the first meaningful action - locking a Word document so only comments could be added.

When I have to deal with large new API sets, I tend to program by Google - search for a code snippet that best illustrates the use of the API. Internet is a great resource for that (with the only exception - reading is fine, copying code with unclear copyright into commercial problems is not!), and Windows source is even better (although I cannot use that for the open source projects, for similar reasons).

Well, as it turned out, there is a dearth of samples when it comes to OpenXML programming. Unlike most of .NET APIs, MSDN has no examples of use in its API documentation. There are a few "How to" samples of solving and end-to-end problem which primarily focus on processing the text, not configuration options of the Word file. And the rest of the Internet is pretty much silent on the subject.

To make matters worse, the API is based on XML with a bunch of types derived from base XML elements, so Intellisense does not often works.

After some struggle (and help from Eric) I was able to make sense of the programming model. Here's what's going on here.

The document has a bunch of sections. You can look them up by changing the docx extension of the file into zip, and then opening it in your favorite archiver. You will find that the file is just a zipped archive of a bunch of XML files. What I've done to figure out what elements need to be changed to lock the file was making the copy of the file, expanding it, then locking the file, expanding the result, and then diffing it.

This led me to two elements: documentSecurity in properties of ExtendedFilePropertiesPart, and documentProtection. The first one was easy - it had a counterpart in the object model, "doc.ExtendedFilePropertiesPart.Properties.DocumentSecurity", setting it was very easy:

WordprocessingDocument doc = WordprocessingDocument.Open(args[1], true);
doc.ExtendedFilePropertiesPart.Properties.DocumentSecurity =
new DocumentFormat.OpenXml.ExtendedProperties.DocumentSecurity(isLock ? "8" : "0");
doc.ExtendedFilePropertiesPart.Properties.Save();
doc.Close();


The second was a setting in MainDocumentPart. The hiccup for me (a very novice XML developer - remember, most of my life was spent deep in the guts of OS, I have not touched managed code and all attendant goo until a few months ago!) was that settings were a collection of OpenXML elements, and DocumentProtection, despite the existence of the type, was not addressable in the direct way, as a property of the settings. Instead, the settings needed to be interpreted as an XML record, e.g. via LINQ to XML:

DocumentProtection dp =
doc.MainDocumentPart.DocumentSettingsPart.Settings
.ChildElements.First<DocumentProtection>();
if (dp != null)
dp.Remove();

if (isLock)
{
dp = new DocumentProtection();
dp.Edit = DocumentProtectionValues.Comments;
dp.Enforcement = DocumentFormat.OpenXml.Wordprocessing.BooleanValues.One;

doc.MainDocumentPart.DocumentSettingsPart.Settings.AppendChild(dp);
}

doc.MainDocumentPart.DocumentSettingsPart.Settings.Save();


So here's a full code snippet. It gives you a command line utility to lock and unlock Word files (unlocking the file will - I think - also remove the password protection, although I did not try this).

You need OpenXML Format SDK 2.0 to run this, available here: http://www.microsoft.com/downloads/details.aspx?FamilyId=C6E744E5-36E9-45F5-8D8C-331DF206E0D0&displaylang=en, and a reference to DocumentFormat.OpenXml in your project.


//-----------------------------------------------------------------------
// <copyright>
// Copyright (C) Sergey Solyanik.
//
// This file is subject to the terms and conditions of the Microsoft Public License (MS-PL).
// See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL for more details.
// </copyright>
//-----------------------------------------------------------------------
using System;
using System.Xml.Linq;

using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;

namespace LockDoc
{
/// <summary>
/// Manipulates modification permissions of an OpenXML document.
/// </summary>
class Program
{
/// <summary>
/// Locks/Unlocks an OpenXML document.
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
if (args.Length != 2)
{
Console.WriteLine("Usage: lockdoc lock|unlock filename.docx");
return;
}

bool isLock = false;
if (args[0].Equals("lock", StringComparison.OrdinalIgnoreCase))
{
isLock = true;
}
else if (!args[0].Equals("unlock", StringComparison.OrdinalIgnoreCase))
{
Console.Error.WriteLine("Wrong action!");
return;
}

WordprocessingDocument doc = WordprocessingDocument.Open(args[1], true);
doc.ExtendedFilePropertiesPart.Properties.DocumentSecurity =
new DocumentFormat.OpenXml.ExtendedProperties.DocumentSecurity
(isLock ? "8" : "0");
doc.ExtendedFilePropertiesPart.Properties.Save();

DocumentProtection dp =
doc.MainDocumentPart.DocumentSettingsPart
.Settings.ChildElements.First<DocumentProtection>();
if (dp != null)
{
dp.Remove();
}

if (isLock)
{
dp = new DocumentProtection();
dp.Edit = DocumentProtectionValues.Comments;
dp.Enforcement = DocumentFormat.OpenXml.Wordprocessing.BooleanValues.One;

doc.MainDocumentPart.DocumentSettingsPart.Settings.AppendChild(dp);
}

doc.MainDocumentPart.DocumentSettingsPart.Settings.Save();

doc.Close();
}
}
}


BTW, for the not faint-of-heart, here's the documentation for OpenXML format: http://www.ecma-international.org/publications/standards/Ecma-376.htm

And here are the Microsoft SDK docs: http://msdn.microsoft.com/en-us/library/bb448854(office.14).aspx

Wednesday, July 15, 2009

Freedom and the Bible

"Romans 13:1-7 (NLT): Everyone must submit to governing authorities. For all authority comes from God, and those in positions of authority have been placed there by God. 2 So anyone who rebels against authority is rebelling against what God has instituted, and they will be punished. 3 For the authorities do not strike fear in people who are doing right, but in those who are doing wrong. Would you like to live without fear of the authorities? Do what is right, and they will honor you. 4 The authorities are God’s servants, sent for your good. But if you are doing wrong, of course you should be afraid, for they have the power to punish you. They are God’s servants, sent for the very purpose of punishing those who do what is wrong. 5 So you must submit to them, not only to avoid punishment, but also to keep a clear conscience. 6 Pay your taxes, too, for these same reasons. For government workers need to be paid. They are serving God in what they do. 7 Give to everyone what you owe them: Pay your taxes and government fees to those who collect them, and give respect and honor to those who are in authority."

Wednesday, July 8, 2009

Among all the idiocy printed today about Chrome OS

...finally, the voice of reason! Ladies and Gentlemen, I give you... fake Steve Jobs!

http://fakesteve.blogspot.com/2009/07/lets-all-take-deep-breath-and-get-some.html

The mother of all bull...

"Google Drops A Nuclear Bomb On Microsoft. And It’s Made of Chrome."

http://www.techcrunch.com/2009/07/07/google-drops-a-nuclear-bomb-on-microsoft-and-its-made-of-chrome/

The idiots in the press are at it again, cooking a sensation by blowing up an interesting tidbit of information way out of proportion.

Let me point out two obvious facts.

(1) The entire consumer market is rather small as a share of Microsoft revenue (10%?). The netbooks most likely represent less than 1% of the company's revenue stream. You cannot possibly call a "nuclear bomb" something that targets so little money.

(2) The smart phone market will always be much bigger than a netbook market. So if the "nuclear bomb" metaphor made any sense, Apple has dropped it years ago with iPhone.

Here's another stupid quote of the day:

'"One of Google's major goals is to take Microsoft out, to systematically destroy their hold on the market," said Mr Enderle.

"Google wants to eliminate Microsoft and it's a unique battle. The strategy is good. The big question is, will it work?"'

http://news.bbc.co.uk/2/hi/technology/8139711.stm

When I was at Google, the last thing people there were thinking about was Microsoft. I maybe have heard Microsoft mentioned a grand total of 10 times in my year plus there. What Googlers do care about is building cool things that attract attention and make customers come to their sites. THAT strategy clearly works. Destroying Microsoft - not so much (Netscape tried that approach).

My own take on this - thank you, Google! Windows 8/IE 9 will be better for your efforts. It often takes a competitor to persuade us that a segment of a market is important (unfortunate, but true). With this announcement Google did just that.

Do you have a health insurance?

Don't be so sure. You might lose it when you actually need it. Apparently, insurance companies slap a $1M surcharge on corporate policies that carry expensive patients. The companies then face a choice of whether to essentially pay you a $1M+ salary or...

http://www.dailykos.com/storyonly/2009/7/7/751100/-How-I-lost-my-health-insurance-at-the-hairstylists

Incidentally, in 3/4 of all medical bankruptcies (which are half of all bankruptcies in the US) people had health insurance.

http://1-800-magic.blogspot.com/2009/05/us-healthcare-by-numbers.html

Monday, July 6, 2009

BMI is bogus... because it embarrasses USA

It was making sense up to a point where an author claimed that 200 years ago most people led sedentiary life styles, although I had to ignore his quip on "if the formula does not describe the data, rig the formula" (this, of course, is what science - at least theoretical physics - is all about).

But when I got to the end, it was this: BMI does not make sense because...

"10. It embarrasses the U.S.

It is embarrassing for one of the most scientifically, technologically and medicinally advanced nations in the world to base advice on how to prevent one of the leading causes of poor health and premature death (obesity) on a 200-year-old numerical hack developed by a mathematician who was not even an expert in what little was known about the human body back then."

http://www.npr.org/templates/story/story.php?storyId=106268439&sc=fb&cc=fp

Come to think about it, an even more ridiculous fact is that our entire space program is based on a 300-year-old formula developed by a theologian!



This pearl of logical reasoning comes to you directly from a Stanford (!) Professor (!) of Mathematics (!) Keith Devlin...

http://www.stanford.edu/~kdevlin/

P.S. The author of this blog takes no position on the validity of BMI as a measure of human obesity, only on the validity of the referenced above argument against it.