PhilipMat

Templates for GoodNotes

Sat, Apr 28 2012

Recently I've been testing a few apps for taking notes and I have settled on GoodNotes.
It's a really good looking app and the developers are dedicated to it. How dedicated? They had a retina version out within days after the iPad 3 release.

After a few weeks of giving up paper and exclusively using the app, I've created a few templates for the type of notes I find myself taking most frequently.

While these templates have been build with GoodNotes in mind, you can take the SVG files and with very few tweaks target them towards other note taking apps.

Read on for an explanation of the design and thoughts that went into each template, how to import them into GoodNotes, and how you can help improve them, or jump to the end to see where you can get them.

The Templates

The template are geared towards writing and designing rather than viewing (read-only) so they account for the space occupied by UI elements. They also favor unobtrusive guiding elements so that they provide support for writing but not distract from what is being written or drawn.

960 Grid System

Two templates support the 960 Grid system (also employed by my favorite: Twitter Bootstrap) in 12 column format:

  • 960px Portrait - has longer columns for designing in portrait mode;
    960px portrait

  • 960px Landscape - has wider columns (33% wider), better suited for drawing in landscape.
    960px landscape

These two has been inspired by hellopanos's templates for Penultimate, which I found too dark and thus distracting. Since the iPad has a great screen a lighter color is very visible and let's the design take center stage while still providing alignment guidance. Space between columns is proportional to the width of the columns.

Since the design is performed within the constraint of the columns, I have chosen to indicate the outer/side border of the 960px grid with two faint lines. This way they are not distracting yet provide the visual indication of what the boundaries are.

iPad

Three iPad templates present an iPad that is scaled about 73% from the actual size (in pixels). FWIW, I even got the corner radius accurate and then I realized it doesn't really matter that much.

iPad template

The screen portion displays the status bar (20px scaled by 0.73) and if you squint hard enough you'll see marker lines for the 44pt navigation and 49pt tab bars. They are very faint so that they don't distract from designing your interface, but still present if you want to make use of them.

Meeting Notes

Inspired by a Penultimate template, it contains a top section to record the date/time and place of the meeting, the subject/topic/reason for meeting, and the participants. This section doesn't offer a great deal of space, but if you need more than that you're probably doing something wrong or at least ineffectual.

The template devotes most of its body to the meeting notes and is concluded by a checklist-like section to record action items.

Meeting Notes template

I gave each action item what I thought to be enough space, but you might want to consider writing in those sections using the GoodNotes magnifier mode.

Each section contains almost transparent text to indicate its purpose. I've thought it would be nice to provide such a hint, but I made it light enough so that it allows to write on top of it.

Task List

The task list template is geared toward being able to capture tasks quickly. That means that the space for each of the 14 lines is oversized so that you don't have to use the magnifier mode.

Task List template

The lines are a bit darker than the other templates as they intend to provide more guidance for writing. There is white space at the top of the templates, enough to write a title for the task list, but not so much that it's distracting it if you don't.

If you look at the image by itself you might think there's too much space at the bottom, but remember that GoodNotes has an area at the bottom where it displays the toolbar for bringing up the palm rest or magnifier. The template accounts for that.

ToDo List

The to-do template also has 14 rows of checkboxes but in two-column format as it's designed for shorter entries. You can use the magnifier mode if you need to capture more details.

ToDo template

Since to-do lists are more likely to have a title, the template provides more emphasis by separating the title area from the checklist through a darker line, while at the same time the individual to-do entries have lighter supporting lines to allow more focus on the written text.

How to Import the Templates

Using the images in GoodNotes is simple. Get the templates onto the device, then from within a notebook click the + button in the toolbar, click Import, select your image and go to town on it.

However, if you find yourself using one or more of the templates with some frequency, it might be worth going through the following five-step process to make them part of the template gallery.

  1. Click on the + button in the toolbar and choose Import
    Import image into GoodNotes

  2. Select your image from one of the image sources, depending on how you made available the templates to GoodNotes (in this case, I copied them to the app using iTunes).
    Select the template to import

  3. Repeat step 1: touch the + in the toolbar, but select Other Templates this time. You will be presented with the template selection dialog.
    Go to Template dialog

  4. Click the + button in the top left corner of this dialog, then select Current Template.
    Add current template to template list

  5. Give your template a recognizable name. Optionally, touch Edit then use the sizing chevron to move your template(s) in a better position.
    Name template and choose position

Tech Notes

One of the decisions I made early on was to optimize these templates towards input (writing/drawing), which meant having to account for GoodNotes's toolbar and palm-rest/magnifier. I could've started with a 768 x 1024 image to mimic the iPad's screen, but I've noticed that GoodNotes moves the template down so that its top is right below the toolbar, effectively clipping its bottom part and thus reducing the effective area.

For this reason, the templates are actually 768 x 972, given that GoodNotes's toolbar is 52 pixels high. The chevron that triggers the palm-rest/magnifier is 35 pixels high, so I designed all templates so that their bottom 40 pixels remains clear. I could've cropped the image and target 768 x 937, but I thought it looked better if the "paper" continued all the way to the bottom of the screen.

To create each template, I have started with a handcrafted SVG file and tweaked it for hours (mostly because I had no familiarity with SVG) until I got the desired result.

I then exported the images into PNG using OS X's qlmanage and ImageMagick's convert and loaded them into GoodNotes to check out the result and make sure the templates were comfortable to use.

Converting to PNG could've (should've) been a simple process because ImageMagick can convert SVG images, alas the feature-set support is limited, and occasionally, incorrectly implemented.

It would've been a great deal easier to create them in an image editor and, indeed, the first attempt was in Pixelmator and it took about 1/3 to half of the time it took to create the SVGs.

However, since the SVG files are text they are effectively the source code for the templates, something I can certainly appreciate as a software engineer.

As a bonus, you need but a text editor to quickly change their dimensions and create templates for another note taking app (e.g. Penultimate).

Where to Get Them

The templates are on github and the SVG files are within the source folder of that repository. If you're interested only in the PNG files, I've uploaded a zip file that contains them all.

The templates, both PNG and SVG files, are released under a Creative Commons BY-NC-SA license, which means you are free to share them and change (remix) them, but you cannot sell them, you must give proper attribution, and should you alter them you may distribute the resulting work only under the same license (or similar).

That being said, have fun hacking them. Looking forward to it.

Asymmetric Sparkbars

Thu, Mar 22 2012

I am creating some sparkbars, sparkline's poorer cousin, for a project I'll talk about soon, and I wanted to represent in a single chart both additions and subtractions.

As it happens, there's an entire range of Unicode characters devoted to blocks: U+2580 to U+259F, of which U+2581, "Lower one eighth block", through U+2588, "Full block", seem perfect for rendering text sparklines.

The smart way to handle this task would've been to use jQuery.sparkline, which makes use of the canvas element to draw some pretty impressive charts, but I stubbornly decided to stick with text.

I am starting with two data sets, one representing tasks completed per day - progress, the other being tasks created per day, for the same interval, and I will use a spark() function provided by node-textspark to convert each set into discrete values, returned to me as Unicode characters.

My starting point is these two sparklines:

Progress and regress

I'd love for the top of the second set to be glued to the bottom of the first set and thus give a good visual of what happened for each day. For that to happen I'll be using a different color and I will make use of negative space to represent the data points.

First I'll set the background color of the second series to red and the foreground color to white, effectively transforming the negative space, previously white - now red, into sparkbars that seem to hang from the top.

Progress and regress - in complimenting colors

Unfortunately, the block characters stop at the baseline and thus the space between the baseline and the beard line (not making this up) is visible and colored red (from the background).

To make that line go away, I'll set the height on the div containing the second series to the same height as the font, then set the overflow property to hidden so that it clips the bottom part, below the baseline.

Progress and regress - clipping below the baseline

Lastly, I'll set a negative top margin for the second div so that it gets closer to top series:

Progress and regress - in complimenting colors

I chose to leave a pixel between the series as I think it looks just a tad better than if they had no space between them:

Progress and regress - in complimenting colors

The ending HTML is deceptively simple and scales well when the browser zooms in.

<!DOCTYPE html>
<html>
    <head>
        <style type="text/css">
            .inverse .inner { background-color: red; color: white; }
            .sparkline-group { font: 'Courier New'; font-size: 18px; }
            .sparkline-group .sparkline.inverse {
                margin-top: -7px;
                height: 18px;
                overflow: hidden;
            }
        </style>
    </head>
    <body>
        <div class="sparkline-group">
            <div class="sparkline"><span id="spark1" class="inner"></span></div>
            <div class="sparkline inverse"><span id="spark2" class="inner"></span></div>
        </div> 
        
        <script src="spark.js" type="text/javascript"></script>
        <script type="text/javascript">
            var spark1 = [1,2,5,9,0, 5,4,8,9,1, 3,0,6,4,9, 1,2,3,9,5], 
                spark2 = [9,9,8,7,5, 4,8,9,6,6, 4,9,4,7,9, 1,6,7,4,9];
            document.getElementById('spark1').innerHTML = spark(spark1);
            document.getElementById('spark2').innerText = spark(spark2);
        </script>
    </body>
</html>

That's it.

Json.NET and NHibernate Serialization Blues

Mon, Feb 27 2012

After spending some irritating hours with Microsoft's JavaScriptSerializer, I've switched over to Json.NET and I've been happier. That matters. A lot.

I have better control over serialization, for example I can specify how to handle circular references (ReferenceLoopHandling), or whether to include nulls (NullValueHandling) and/or default values (DefaultValueHandling); it also gives me more injection points.

Yet it exhibits one frustrating behavior when one of the objects is masked by an NHibernate proxy, as it starts to go off the rails and soon the JSON code is riddled with __interceptors, and EventListeners, and various other critters that have no purpose being sent across the wire.

(Yes, arguably I shouldn't be directly serializing these NHibernate objects, but the DTOs I would use would look remarkably similar to the NH objects they would attempt to mock.)

The problem comes with the chaining of strategies a JsonSerializer uses to determine how to serialize a particular strategies. This chain is captured by the DefaultContractResolver in the CreateContract(Type) method (which thankfully and wisely is also virtual): it tries in succession to figure out if the type is a primitive, if it's decorated with Json*Attributes, if it's a dictionary, etc, and eventually it just gives up and creates a plain JsonObjectContract.

However, in the middle of that chain is a test for whether the type is ISerializable, and unfortunately the NHibernate proxies happen to have that attribute, and that is what causes the serializer to go down a rabbit hole.

My solution is to force it to use a JsonObjectContract when it encounters one of these proxies.

public class IgnoreSerializableJsonContractResolver : DefaultContractResolver
{
    protected override JsonContract CreateContract(System.Type objectType)
    {
        /* Behavior in base we're overriding:
        if (typeof(ISerializable).IsAssignableFrom(objectType))
            return CreateISerializableContract(objectType);
        //*/

        if (objectType.IsAutoClass 
              && objectType.Namespace == null 
              && typeof(ISerializable).IsAssignableFrom(objectType)) {

            return base.CreateObjectContract(objectType);
        }

        return base.CreateContract(objectType);
    }
}


var serializer = new JsonSerializer();
serializer.ContractResolver(new IgnoreSerializableJsonContractResolver());

I am not sure whether the condition is too restrictive or may generate false positives, but all the NH proxies I've encountered seem to exhibit the same behavior: they are auto-generated classes, without a namespace.

JSON Entity Converter

Mon, Feb 20 2012

Whether in the context of my friend Curtis's BORAX proposal, or my own, we will eventually need to serialize our domain objects and ship them to the browser.

Our best solutions is probably some sort of Domain Transfer Object (DTO), but for a second let's assume we take the quick route and turn our domain objects directly into JSON mush.

Is ASP.NET MVC 3 we can use the JsonResult class, but with it comes a problem: the JavaScriptSerializer, which JsonResult uses, cannot handle circular references (and rightfully so since JSON doesn't have references).

Let me illustrate using the following simplified domain classes (you can find the full code in this Gist):

public class Entity {  // Layer Supertype
    public int Id { get; set; }
}

public class Company : Entity { 
    public string Name { get; set; }
    public Contact MainContact { get; set; }
}

public class Contact : Entity {
    public string Name { get; set; }
    public Company WorksFor { get; set; }
}

When serializing an object of type Company, the JavaScriptSerializer will choke on Contact.WorksFor since it points back to the Company that the contact is associated with.

Before we talk about how we'll fix it, let's talk about the output. What I think I'd like to have when serialize a company to JSON is:

{
    "Id" : 1,
    "Name" : "Moof",
    "MainContact" : {
        "Id" : 100,
        "Name" : "Clarus",
        "WorksFor" : {
            "TypeName" : "Company",
            "Id" : 1
        }
    }
}

In other words, if one of the object we try to serialize has a property of type Entity, we'll just output its Id and assume the consumer of the JSON will know to request /refdata/{entitytypes}/{id} to resolve it, if needed.

Of course, this will not avoid circular references entirely, but merely provide an example on how to work with the class that will solve our particular problem: JavaScriptConverter.

JavaScriptConverter, of the System.Web.Script.Serialization clan, allows us to inject specific type handlers into JavaScriptSerializer and have it defer to our converter when encountering those types in the serialization process. And it gets better: our converter won't have to provide actual JSON, it merely has to return a dictionary of name-value pairs and the JavaScriptSerializer will take care of the actual transformation.

Here's how we could implement the required Serialize method:

public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer) {
    var ret = new Dictionary<string, object>();
    string name; object value;
    
    var props = obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty);
    foreach (PropertyInfo prop in props) {
        name = prop.Name;
        value = prop.GetValue(obj, null);
        if (value != null && typeof(Entity).IsAssignableFrom(prop.PropertyType))
            // real gist code: ret.Add(name, _reducer((Entity) value)); //
            ret.Add(name, new Dictionary<string, object> { 
                { "TypeName", value.GetType().name },
                { "Id", ((Entity) value).Id }
            });
        else
            ret.Add(name, value);
    }
    
    return ret;
}

(Of course the actual implementation is a bit more elegant, I hope)

We can now serialize objects such as these:

var co = new Company { Id = 1, Name = "Moof Inc." };
var clarus = new Contact { Id = 1, Name = "Clarus" };
var mark = new Contact { Id = 2, Name = "Mark" };
co.AddContact(clarus);
co.AddContact(mark);
co.MainContact = clarus;

var converter = new EntityConverter(new[] { typeof(Contact) });
var serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new[] { converter });
serializer.Serialize(co).Dump(); 

And get the following JSON representation:

{
    "Id": 1, 
    "Name": "Moof Inc.",
    "Contacts": [
        {
            "Id": 1, 
            "Name": "Clarus", 
            "WorksFor": {
                "Id": 1, 
                "TypeName": "Company"
            }
        }, 
        {
            "Id": 2, 
            "Name": "Mark", 
            "WorksFor": {
                "Id": 1, 
                "TypeName": "Company"
            }
        }
    ], 
    "MainContact": {
        "Id": 1, 
        "Name": "Clarus", 
        "WorksFor": {
            "Id": 1, 
            "TypeName": "Company"
        }
    } 
}

The full code - designed to work with the most excellect LINQPad, shows three attempts (four if we include the failed circular reference error) and with them the various ways the EntityConverter can control the JSON output.

If you want to use the code in Visual Studio, add a reference to System.Web.Extensions and import the System.Web.Script.Serialization namespace.

Setting up FxCop under CruiseControl.NET

Tue, Feb 14 2012

After spending up half a day trying to get FxCop v1.36 to play nice with CruiseControl.NET and MSBuild, I thought it was worth spending an extra hour or two documenting it.

There are two ways we can get FxCop running under CCNet: the easy way and (what I think it's) the right way.

Either way, we start by creating an FxCop project using the FxCop GUI; it makes it easy to add our assemblies and to select the rules we'd like enforced.

The easy way has us using an <Exec/> task within the MSBuild file:

<Target Name="FxCop" DependsOnTargets="Build;RunFastTests">
  <Delete Files="$(ProjectRoot)\fxcop.xml" />
  <Exec Command="&quot;$(FxCopPath)\FxCopCmd.exe&quot;
    /dic:&quot;$(ProjectRoot)\src\CodeNameDictionary.xml&quot;
    /project:$(ProjectRoot)\src\Default.FxCop /out:$(ProjectRoot)\fxcop.xml" 
    ContinueOnError="true">
  </Exec>
</Target>

In this version, we're telling FxCop command line tool to use a custom dictionary - more on this later, the project file we created earlier, and to output the results to an XML file.

When being run, FxCop tends to load the previous results, which would cause load errors if we renamed classes that previously had errors, so we'll <Delete/> the output file before running the task.

 

The nicer, more explicit way, has us use the supplemental tasks provided by the MSBuild Community Tasks Project, in particular the <FxCop/> task (grab at least version 1.3.0.528, part of the nightly builds - v1.2 doesn't properly support this task).

<Target Name="FxCop" DependsOnTargets="Build;RunFastTests">
  <Delete Files="$(ProjectRoot)\fxcop.xml" />
  <FxCop
    ToolPath="$(FxCopPath)\FxCopCmd.exe"
    ProjectFile="$(ProjectRoot)\src\Default.FxCop"
    CustomDictionary="$(ProjectRoot)\src\CodeNameDictionary.xml"
    FailOnError="false"
    AnalysisReportFileName="$(ProjectRoot)\fxcop.xml"
  />
</Target>

What you won't find in the MSBuild Community Tasks documentation is the requirement to use the ToolPath to point to the right version of FxCop. As of now, the <FxCop/> task looks explicitly for FxCop v1.32 and for a registry key that no longer exists when it tries to build the full command line:

...
if (string.IsNullOrEmpty(ToolPath)) {
  string fxCopPath = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
  fxCopPath = Path.Combine(fxCopPath, @"Microsoft FxCop 1.32");
  try {
    using (RegistryKey buildKey = Registry.ClassesRoot.OpenSubKey(@"FxCopProject\shell\Open\command")) {
      if (buildKey == null) {
        Log.LogError("Could not find the FxCopProject File command in the registry. Please make sure FxCop is installed.");
      } else {
        fxCopPath = buildKey.GetValue(null, fxCopPath).ToString();
        ...

The problem with the registry look up is that FxCop v1.36 uses a key called FxCop.Project.9.0, all which means that the <FxCop/> task fails to find the executable. That is why we have to use the ToolPath, inherited from its Task parent, to help it find it.

Alright, now the promised dictionary clarification.

If our namespaces, classes, etc. include non-lexical words, for example Flickr.Configurator, FxCop will announce
that our code breaks a naming rule - CA1704:IdentifiersShouldBeSpelledCorrectly - and will tell us to:

Correct the spelling of 'Flickr' in assembly ExternalDependencies.dll'.

Correct the spelling of 'Configurator' in assembly ExternalDependencies.dll'.

(Yes, Configurator is not an English word either - us developers have a lot of those.)

We'll fix this problem by adding a custom FxCop dictionary containing the words we want to mark as correct:

<Dictionary>
  <Words>
    <Recognized>
      <Word>Flickr</Word>
      <Word>Configurator</Word>
    </Recognized>
  </Words>
</Dictionary>

We'll then spell this dictionary using the CustomDictionary property of the <FxCop/> task.

We could and should also add the dictionary into the FxCop project file to make sure the GUI doesn't complain about those naming rules being broken either. Find the <CustomDictionaries/> node within the project file, Default.FxCop in the examples above, and add a <CustomDictionary/> node:

<CustomDictionaries SearchFxCopDir="True" SearchUserProfile="True" SearchProjectDir="True">
  <!-- Tells FxCop to find it in the same location as the Default.FxCop file -->
  <CustomDictionary Path="CodeAnalysisDictionary.xml" />
</CustomDictionaries>

 

CCNet captures all the output of a build into a giant result file using <merge> tasks. We'll follow the instructions on the CCNet website to merge in the generated fxcop.xml file:

<tasks>
  <merge>
    <files>
      <file>fxcop.xml</file>
      <!-- Other files to merge for your build would also be included here -->
    </files>
  </merge>
</tasks>

Finally, to be able view the results of the FxCop analysis in CCNet's web dashboard, we'll need to include the XSLT files, which extract and pretty up the entries merged in from fxcop.xml, into the dashboard.config file, typically located within C:\Program Files\CruiseControl.NET\webdashboard\ folder:

...
<buildPlugins>
  <buildReportBuildPlugin>
    <xslFileNames>
      <!-- other xsl file, such nunit and msbuild -->
      <xslFile>xsl\fxcop-summary_1_36.xsl</xslFile>
      <xslFile>xsl\fxcop-report_1_36.xsl</xslFile>
    </xslFileNames>
  </buildReportBuildPlugin>
  <buildLogBuildPlugin />
  <!-- These create two menu entries on the left, when viewing a project's build page -->
  <xslReportBuildPlugin description="FxCop Summary" actionName="FxCopSummary" xslFileName="xsl\fxcop-summary_1_36.xsl"></xslReportBuildPlugin>
  <xslReportBuildPlugin description="FxCop Report" actionName="FxCopReport" xslFileName="xsl\fxcop-report_1_36.xsl"></xslReportBuildPlugin>
</buildPlugins>

That's it. Next time you build you should see the FxCop details when you click the "FxCop Report" link.

The only one thing to note is that violating FxCop rules, no matter their level or the value of ContinueOnError, will not mark the build a broken. If you want FxCop failures to break the build, there is one further step you need to take.

Blog Archive →