Sunday, August 12, 2012

Finding Font Filename from Font Title

Well, all was going well with my code to create custom XPS documents, when I tried to be fancy with fonts. How to point the XPS engine at the correct font file so it could be included within the document as a resource??
I cannot yet find any APIs that help with this; any help would be appreciated.  Instead I managed to look up the registry for the name of the file associated with the font.  This is a bit of a hack, as font collections contained within one file probably will mess this up.
It works, sort of, and I wrote the code that calls this routine to use arial.ttf if all else fails, so there is a back up plan.
Here is the subroutine:

Imports System.IO
Imports Microsoft.Win32
Private Function ReadFontReg(ByVal strFontName As String) As String

        'read from the registry to get the font file name
        Dim regKey As RegistryKey
        Dim strAnswer As String = ""
        Dim strValues() As String

        regKey = Registry.LocalMachine.OpenSubKey("Software\Microsoft\Windows NT\CurrentVersion\Fonts", True)
        If Not regKey Is Nothing Then
            Try
                Dim i As Integer
                strValues = regKey.GetValueNames
                For i = 0 To strValues.Length - 1
                    If strValues(i) = strFontName Or strValues(i) = strFontName & " (TrueType)" Then
                        strAnswer = regKey.GetValue(strValues(i)).ToString
                        If Not File.Exists(strAnswer) Then
                            'add on the file path to the font folder
                            strAnswer = Environment.GetFolderPath(Environment.SpecialFolder.Fonts) & "\" & strAnswer
                        End If
                        Exit For
                    End If
                Next
            Catch ex As Exception

            Finally
                regKey.Close()
            End Try
        End If

        If strAnswer = "" Then
            'take a stab anyway. Might be lucky!
            Dim strSpecialFolderFonts As String
            Dim strFullPath As String

            strSpecialFolderFonts = Environment.GetFolderPath(Environment.SpecialFolder.Fonts) & "\"
            strFullPath = strSpecialFolderFonts & strFontName & ".ttf"
            If File.Exists(strFullPath) Then
                strAnswer = strFullPath
            Else
                strFullPath = strSpecialFolderFonts & strFontName & ".ttc"
                If File.Exists(strFullPath) Then
                    strAnswer = strFullPath
                End If
            End If
        End If
        Return strAnswer
    End Function

Saturday, August 11, 2012

XPS Document Bits and Pieces

This is a bit of a stream of consciousness note; I want to get down important definitions/descriptions of the document specification.

ECMA Document defining an XPS document (a PDF file, of course! *giggle*)
http://www.ecma-international.org/activities/XML%20Paper%20Specification/XPS%20Standard.pdf

Unit of measurement withing an XPS document is ST_GEOne. Otherwise/elsewhere known as an XPS unit.
Finding a definition of this unit was challenging!  Turns out it is 1/96th of an inch.  Yay for non-metric measurement systems!

<SideBar>I live in a non-US country, so I live in metric/ISO standard land. (what is with the US and their inability to get  with the metric program?!).  Do you know an inch is defined as exactly 25.4 mm?  So the stupid old measurement system of inches is actually defined in metric units.</SideBar>

I will typically use in my documents A4 or A5 text, so in various measurement units:
A4297 x 210 mm11.7 x 8.3 in 1122.5 x 793.7 xps units 
A5210 x 148 mm8.3 x 5.8 in 793.7 x 559.4 xps units

When the FixedDocument is defined, you can set the indicated Width and Height of the pages, but these can be overruled in the individual pages of the FixedDocument.  The Width and Height also define whether the page is Portrait or Landscape, so you want to set the printer correctly to avoid clipping.

A FixedPage has the attributes of
Required: Width, Height (physical media size)
Optional: ContentBox (where the content resides) this has 4 values: ContentOriginX, ContentOriginY, ContentWidth, ContentHeight
Optional: BleedBox (BleedOriginX, BleedOriginY, BleedWidth, BleedHeight)
Required: xml:lang eg "en-US", "en-AU"
Optional: Name (used if want to reference the FixedPage elsewhere by name)

Anyway, time to start coding my own XPS creation engine...






Creating Your Own XPS Document

Well, I cobbled together enough sample code to create custom XPS documents, at least in terms of the process to go through.  I haven't explored content creation options, that is my next challenge.

The process to create an XPS document in very basic outline is:

1/            Create an XPSDocument Package
2/            Add a FixedDocumentSequence at the Package root
3/            Add a FixedDocument to the FixedDocumentSequence with an IXpsFixedDocumentWriter
4/            Add page to the FixedDocument using an IXpsFixedPageWriter
5/            Add resources to the page, put them in a Dictionary collection for ease of use
6/            Write the page content using XML to describe the elements
7/            Commit the page/s to the FixedDocument
8             Optionally attach a PrintTicket
9/            Commit the FixedDocument to the FixedDocumentSequence
10/          Close the Package

Resources are embedded fonts and images.
Page contents are text, graphics and brushes (ie text, drawing and pictures)

So, some lines of code to create the elements described above:
Firstly, Imports (really hard to find sample code with correct Imports!)
Imports System.Printing
Imports System.IO
Imports System.Windows.Xps.Packaging
Imports System.IO.Packaging
Imports System.Windows.Xps
Imports System.Xml

1/ Using XPSDocPackage as Package = XPSDocPackage.Open(strDocumentPath)

2/ Dim xpsDoc as XpsDocument = New XpsDcoument(XPSDocPackage)

3/ Dim documentSequenceWriter As IXpsFixedDocumentSequenceWriter =   xpsDoc  .AddFixedDocumentSequence()

4/ Dim fixedPageWriter As IXpsFixedPageWriter = fixedDocumentWriter.AddFixedPage()

5/ Dim resources As Dictionary(Of String, List(Of XpsResource))
        ' Collections of images and fonts used in the current page.
        Dim xpsImages As New List(Of XpsResource)
        Dim xpsFonts As New List(Of XpsResource)

        Dim p_xpsImage As XpsImage
        Dim p_xpsFont As XpsFont

        ' Add, Write, and Commit image1
        p_xpsImage = fixedPageWriter.AddImage(XpsImageType.JpegImageType)
        WriteToStream(p_xpsImage.GetStream(), image1)
        p_xpsImage.Commit()
        xpsImages.Add(p_xpsImage) ' Add image1 as a required resource.

        ' Add, Write, and Commit font 1
        p_xpsFont = fixedPageWriter.AddFont()
        WriteObfuscatedStream(p_xpsFont.Uri.ToString(), p_xpsFont.GetStream(), font1)
        p_xpsFont.Commit()
        xpsFonts.Add(p_xpsFont) ' Add font1 as a required resource.

        ' Return the image and font resources in a combined collection.
        resources.Add("XpsImage", xpsImages)
        resources.Add("XpsFont", xpsFonts)
6/ TO COME!! (I need to flesh out my knowledge here!)

7/  fixedPageWriter.Commit()

8/ TO COME!! (I need to flesh out my knowledge here!)

9/ fixedDocumentWriter.Commit()

10/  xpsDoc.Close()