My Doc's Got No Nodes

Looking for a punchline since 2002

This had me stumped for a while, so here’s what you need to do:

Inside your form

<li>
    @Html.CheckBoxFor(m => m.XX) 
    @Html.LabelFor(m => m.XX, new {@class="checkbox"})
</li>

This is for a boolean XX in the model, marked with the appropriate attributes to get the label text. The important thing is the class attribute for the label, because that applies the style defined in the default css:

label.checkbox { display: inline;}

To make it appear on the same line as the checkbox itself.

XPS Observations

Mark

All these observations were made printing an XPS document which seems to have a problem with the embedded subsetted font. The printer was a non-XPS printer (the Xerox Phaser 8560) but the problems affected the Microsoft PostScript driver as much as the Xerox driver.

Code to test printing XPS using the native API can be found on Logos Bible Software Code Blog

If your text looks blurry / fuzzy in print, it could be because the print path has converted your text to bitmaps.

The font problem which hadn’t upset .net 3.5 caused .net 4.5 to render each word rasterized as an individual png.

Try printing to the Microsoft Xps Document Writer and looking inside the XPS document (below).

.net 4.5 is more fussy than .net 3.5

Compiling the same code for a different target framework can change the results you’ll get.

The native API (with raw spooling) is fussier still

A problematic XPS document could cause reams of pages with random junk at the top, or event have documents be reordered in the print queue.

The native API (with metafile spooling) behaves unpredictably with bad input

You might get pages of junk, you might get a complete document, you might get nothing at all.

The XPS format is pretty easy to mess with manually, once you’ve generated your document

  • Make a test harness that can print the XPS document, using
LocalPrintServer.GetDefaultPrintQueue().AddJob("PrintJob", file, false);

or the native code referenced above.

  • Extract the files from the XPS package by treating it as a zip (I used 7zip to extract the contents into a folder).
  • Have a look around the extracted Resource folders for images (.png) and embedded obfuscated fonts (.odttf).
  • Document text and references to the resources can be changed by editing the xml format file.
  • Select all the files and subfolders in the top level of the extracted package, and add them to a zip file named like “MyHackedUpDocument.xps” (i.e. use the zip format, but .xps extension).
  • You’ve got a new XPS document to try printing.

The obfuscated fonts can be deobfuscated if you want to see what they were originally, using code like this StackOverflow answer but be aware that they will usually have had any unused glyphs stripped out (subsetted).

XPS Packages can contain multiple documents with different (per-document) print tickets

But the .net API ignores them.

You need to feed the tickets to the print job’s document writer.

  • Pick the tickets out of the fixed documents using its FixedDocumentSequenceReader.
  • Create a writer with System.Printing.PrintQueue.CreateXpsDocumentWriter(queue) and handle WritingPrintTicketRequired to supply the ticket based on CurrentPrintTicketLevel and Sequence (which is 1-based).
  • Use MergeAndValidatePrintTicket with the queue’s DefaultPrintTicket to get a valid ticket with the settings for that document.

Suppose you want to use CreateItem to build a string containing a pattern, based on the value of a property being passed to your Target (say, to make an OpenCover Filter group).

If the Include attribute value includes an asterisk (even if escaped as hex) then a pattern match is attempted against the filesystem, and you end up with nothing in the group.

After several hours of messing about, I came up with a solution building the pattern with the word STAR instead of an asterisk, and using an inline C# Target to create a TaskItem with the correct text.

Here is the result, in a .proj file at the solution level and using the NuGet packages for OpenCover, NUnit.Runners and ReportGenerator.

<?xml version="1.0" encoding="utf-8"?>

<Project DefaultTargets="Clean" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
    <PropertyGroup>
        <OpenCoverVersion>4.5.1403</OpenCoverVersion>
        <NUnitVersion>2.6.2</NUnitVersion>
        <ReportGeneratorVersion>1.8.0.0</ReportGeneratorVersion>
    </PropertyGroup>
 
    <ItemGroup>
        <TestedNamespace Include="MyCode">
            <TestsNamespace>MyCode.Tests</TestsNamespace>
        </TestedNamespace>
        <TestedNamespace Include="MyWebStuff">
            <TestsNamespace>MyWebStuff.Tests</TestsNamespace>
        </TestedNamespace>
        <ReportFiles Include="@(TestedNamespace->'OpenCover\%(TestsNamespace).opencover.xml')"/>
    </ItemGroup>

    <PropertyGroup>
        <Configuration Condition="'$(Configuration)' == ''">Release</Configuration>
        <OpenCoverPath>$(MSBuildProjectDirectory)\packages\OpenCover.$(OpenCoverVersion)</OpenCoverPath>
        <OpenCoverMSBuildTasksPath>$(OpenCoverPath)\MSBuild</OpenCoverMSBuildTasksPath>
        <OpenCoverPath>$(MSBuildProjectDirectory)\packages\OpenCover.$(OpenCoverVersion)</OpenCoverPath>
        <OpenCoverMSBuildTasksPath>$(OpenCoverPath)\MSBuild</OpenCoverMSBuildTasksPath>
        <NUnitRunner>$(MSBuildProjectDirectory)\packages\NUnit.Runners.$(NUnitVersion)\tools\nunit-console-x86.exe</NUnitRunner>
        <ReportGenerator>$(MSBuildProjectDirectory)\packages\ReportGenerator.$(ReportGeneratorVersion)\ReportGenerator.exe</ReportGenerator>
    </PropertyGroup>

    <Import Project="$(OpenCoverMSBuildTasksPath)\OpenCover.targets"/>
    <UsingTask AssemblyFile="$(ReportGenerator)" TaskName="ReportGenerator"/>

    <UsingTask TaskName="TurnSTARintoActualStar" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
        <ParameterGroup>
            <Input ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
            <Result ParameterType="Microsoft.Build.Framework.ITaskItem[]" Output="true" />
        </ParameterGroup>

        <Task>
            <Code Type="Fragment" Language="cs">
                <![CDATA[
    if (Input.Length > 0)
    {
        Result = new TaskItem[Input.Length];
        for (int i = 0; i < Input.Length; i++)
        {
            ITaskItem item = Input[i];
            Result[i] = new TaskItem(item.GetMetadata("Identity").Replace("STAR", "*"));
        }
    }
]]>
            </Code>
        </Task>
    </UsingTask>
 
    <Target Name="Clean">
        <RemoveDir Directories="OpenCover" />
        <CallTarget Targets="Report"/>
    </Target>

    <Target Name="Report" DependsOnTargets="Build">
        <ReportGenerator
            TargetDirectory="OpenCover\Report"
            ReportTypes="Html"
            ReportFiles="@(ReportFiles)"
            />
    </Target>

    <Target Name="Build" Inputs="@(TestedNamespace)" Outputs="OpenCover\%(TestsNamespace).opencover.xml">
        <MakeDir Directories="OpenCover" />
 
        <CreateItem Include="+[%(TestedNamespace.Identity)STAR]STAR;-[%(TestedNamespace.TestsNamespace)]STAR;-[%(TestedNamespace.Identity)STAR]%(TestedNamespace.Identity).Properties.STAR">
            <Output TaskParameter="Include" ItemName="SafeFilters" />
        </CreateItem>

        <CreateItem Include="$(MSBuildProjectDirectory)\%(TestedNamespace.TestsNamespace)\bin\$(Configuration)\%(TestedNamespace.TestsNamespace).dll">
            <Output TaskParameter="Include" PropertyName="TestsAssemblyPath" />
        </CreateItem>

        <CreateItem Include='"$(TestsAssemblyPath)" /noshadow /xml:"OpenCover\%(TestedNamespace.TestsNamespace).nunit.xml"'>
            <Output TaskParameter="Include" PropertyName="NUnitArgs" />
        </CreateItem>

        <CreateItem Include="@(TestedNamespace->'OpenCover\%(TestsNamespace).opencover.xml')">
            <Output TaskParameter="Include" PropertyName="OpenCoverXml" />
        </CreateItem>

        <TurnSTARintoActualStar Input="@(SafeFilters)">
            <Output ItemName="Filters" TaskParameter="Result" />
        </TurnSTARintoActualStar>

        <OpenCover 
            ToolPath="$(OpenCoverPath)"
            Register="True" 
            Target="$(NUnitRunner)" 
            TargetArgs='$(NUnitArgs)'
            Output="$(OpenCoverXml)"
            CoverByTest="$(TestsAssemblyPath)"
            Filter="@(Filters)"/>
    </Target>

</Project>

Getting this running turned out to be a bit of a challenge. The information is out there, but spread all over the place, so I’m gathering it here.

  • Download the 12.10 network install (mini.iso) for i386 from here.
  • Create a virtual machine with a >5GB dynamic hard disk and with the virtual DVD drive pointed at your downloaded mini.iso
  • Select “Install” and press tab to edit the boot options
  • Add the text ata_piix.prefer_ms_hyperv=0 before the word quiet, then press Enter. Without this the installer won’t see the virtual hard disk to install to.
  • Go through the installer, and choose to install ssh-server, because if you have problems with the screen later, you can use putty to connect to the virtual machine (providing you can see the screen well enough to login, type clear, and then type ifconfig to get the IP address).
  • Once the install has completed, the virtual machine will reboot but Ubuntu won’t start because it can’t find the hard disk. If you don’t get the grub menu, reboot with shift held down.
  • Press ‘e’ to edit the command, and on the line beginning linux, add the text noreplace-paravirt vga=791 ata_piix.prefer_ms_hyperv=0 beforequiet.
  • Providing the magic vga number worked, you should be able to press F10 and boot into a visible virtual machine.
  • sudo into your favourite editor and edit /etc/default/grub to set the parameter GRUB_CMDLINE_LINUX_DEFAULT = "noreplace-paravirt vga=791 ata_piix.prefer_ms_hyperv=0 quiet"
  • Save changes and exit
  • sudo update-grub
  • sudo reboot

Hopefully you should now have a successful boot.

This is to help anyone else who has the problem we had with a generic Android tablet, and games that store the progress ‘in the cloud’ – like The Simpsons Tapped Out.

After installing this game on a new, generic Android tablet running Ice Cream Sandwich, the game started at level 15 with someone else’s data.

It turns out that the ‘unique’ identifier Secure.Android_ID wasn’t as unique as it should have been, apparently not an unusual problem with small manufacturers’ ROMs.

Note that changing the device ID could screw up your installed apps, lose your existing game data, make you ill or set fire to your pants (in approximate descending order of likelihood). Do this at your own risk. You know you will anyway.

It’s possible to change the Android_ID value (note – not the telephony ID, which is related to phones’ IMEI numbers) providing the tablet is rooted.

I installed and ran Root Checker Basic and found that the tablet was supplied rooted.

So then I installed and ran Device Mod to change the Android_Id value. I used 16 random hex digits from a WEP Key Generator as the new ID.

Then to make sure the new id was picked up by the game, I uninstalled it, rebooted the tablet, deleted the data folder for the game from Android/Data using a File Explorer like this one, and then reinstalled the game.

I hope this helps someone else in the same position. Or me again at some point in the future.

Comments

Thank you! The same thing happened to me with my n…

Unknown - Feb 27, 2013

Thank you! The same thing happened to me with my new Android tablet and this fix worked perfectly with (so far) no unintended consequences. And having no previous experience or understanding of this sort of thing, thank you for the very easy to follow instructions!


I'm glad that it's worked for you too

Mark Lambert - Feb 27, 2013

I'm glad that it's worked for you too!


Hi There, i am having the same problem as you both…

Anonymous - Mar 03, 2013

Hi There, i am having the same problem as you both but when i try to generate a 16 digit key they all have more or less than 16 ??


I just used one of the longer ones and took the fi…

Mark Lambert - Mar 03, 2013

I just used one of the longer ones and took the first 16 digits. It'd be a bad approach for cryptography, but all we're trying to do here is avoid having the same ID as someone else with an android tablet - so it should be OK.

Previous 2 of 8 Next