Dailycode.info

Short solution for short problems

Azure web apps and PDF sharp to generate pdf. (1.3 -> 1.5)

I was using PDF sharp 1.3 to generate PDF via my web api. I had some challenges but generally speaking it went fine. After some good tests I decided to publish it to Azure. Then things got strange. I got a 500 internal server error. After some investigation, this was the error:

font data could not retrieved

Well, this was a pain. Locally this worked fine, but not in azure. I found a nice explanation here, but no solution.

This is what is says:

Azure App Service (aka Azure Websites) enforces a number of more restrictive security constraints than a cloud service or IaaS (VMs). One of the things that is blocked is access to much of the GDI API surface area, which includes some font manipulation. As other folks have noted, if the same code works on a plain IaaS VM, a cloud service, or even a local desktop/laptop, then the problem you are running into is a hard block on the underlying GDI calls.

So the problem is that in web apps you cannot acces the fonts. the solution is to add font as a resource to your application and use the IFormatter interface of PDF sharp 1.5. 

Oh no, this means migrating the code and rewriting. Forntunately this was NOT the case. I looked for the nuget package (it's a beta vesion, so you need to include pre releases) 


I only had to remove the old reference to the charting dll and that was it. 

Now I could start to make a class that implements the IFormatter interface:

using PdfSharp.Fonts;
using System;
using System.IO;
using System.Reflection;
 
namespace spib.print
{
    public class SPIBFontresolver : IFontResolver

In my case I'm using the Verdana font. So where to find the fonts. Thats easy. Go to C:\Windows\Fonts\ and look for the fonts you want to use. copy them and paste them in a folder in your project.


I placed the fonts in a sub folder called font directly in my project. then I added them as embedded resource:


Here you see the project structure with the files:


Next we can complete the implementation of the interface. 

Implement the interface and complete. Here how I completed it in my case for the Verdana font:

public class SPIBFontresolver : IFontResolver
{
    public byte[] GetFont(string faceName)
    {
        switch (faceName)
        {
            case "Verdana#":
                return LoadFontData("spib.print.font.verdana.ttf"); ;
 
            case "Verdana#b":
                return LoadFontData("spib.print.font.verdanab.ttf"); ;
 
            case "Verdana#i":
                return LoadFontData("spib.print.font.verdanai.ttf");
 
            case "Verdana#bi":
                return LoadFontData("spib.print.font.verdanaz.ttf");
        }
 
        return null;
    }
 
    /// <summary>
    /// Returns the specified font from an embedded resource.
    /// </summary>
    private byte[] LoadFontData(string name)
    {
        var assembly = Assembly.GetExecutingAssembly();
 
        // Test code to find the names of embedded fonts - put a watch on "ourResources"
        //var ourResources = assembly.GetManifestResourceNames();
 
        using (Stream stream = assembly.GetManifestResourceStream(name))
        {
            if (stream == null)
                throw new ArgumentException("No resource with name " + name);
 
            int count = (int)stream.Length;
            byte[] data = new byte[count];
            stream.Read(data, 0, count);
            return data;
        }
    }
 
    public FontResolverInfo ResolveTypeface(string familyName, bool isBold, bool isItalic)
    {
        // Ignore case of font names.
        var name = familyName.ToLower().TrimEnd('#');
 
        // Deal with the fonts we know.
        switch (name)
        {
            case "verdana":
                if (isBold)
                {
                    if (isItalic)
                        return new FontResolverInfo("Verdana#bi");
                    return new FontResolverInfo("Verdana#b");
                }
                if (isItalic)
                    return new FontResolverInfo("Verdana#i");
                return new FontResolverInfo("Verdana#");
        }
 
        // We pass all other font requests to the default handler.
        // When running on a web server without sufficient permission, you can return a default font at this stage.
        return PlatformFontResolver.ResolveTypeface(familyName, isBold, isItalic);
    }
}

Last, you will need to add the formatter to your applcation where you are using it.

public PrintOffer()
{
    GlobalFontSettings.FontResolver = new SPIBFontresolver();

And now it works. So with some minimum easy code, you can add private fonts to Azure web apps. I tested it and got a desired result!

I got a good help here: https://stackoverflow.com/questions/27606877/pdfsharp-private-fonts-for-azure-1-50


EntityFramework: Adding IEnumerable to Cache.

My collegues were rewriting some code. This involved generic caching of default mapping objects. 

The mapper loops over a collection of database objects and maps them to business objects. Using seperate mapping tables in a seperate database, we had to map them in the loop. But we didn't want to get all the mappings from the database, instead cache them. 

When we looped over 870 objects and mapped them without the extra mappings, it finished in less then a second. When using the mapping to the database, it took about 2 minutes. Then we rewrote to use caching, but the first time the objects were filled in the cache, it still took 2 minutes. 

The code where the caching of the mappings was looked like this:

        private IEnumerable<TResult> GetMappings<TResult, TEntity>(bool refreshCache = false, Func<TEntity, TResult> mappingFunction = null)
            where TResult : StandardMapping, new()
            where TEntity : SQL.Mappings.StandardMapping, new()
        {
            var key = typeof(TResult).FullName.ToLower();
            if (refreshCache)
                CacheHelper.Remove(key);
            return CacheHelper.Get(key, () =>
            {
                var map = mappingFunction ?? GeneralMapper.FromSQL2BO<TResult>;
                return MasterDl.GetMappings<TEntity>().Select(m => map(m));//returning IEnumerable
            });
        }

The CacheHelper.Get would add the result if it was not cached yet. 

        public static T Get<T>(string key, Func<T> cacheMissCallback)
        {
            if (MemoryCache.Default.Contains(key))
            {
                return Get<T>(key);
            }
            else
            {
                var value = cacheMissCallback();
                Set(key, value);
                return value;
            }
        }

But still it took the same amount of time to load.

The problem was that the Enumerator was cached and not the result. So after I added a .ToList() that made sure the data was fetched from the DB , the delay was completely gone:

        private IEnumerable<TResult> GetMappings<TResult, TEntity>(bool refreshCache = false, Func<TEntity, TResult> mappingFunction = null)
            where TResult : StandardMapping, new()
            where TEntity : SQL.Mappings.StandardMapping, new()
        {
            var key = typeof(TResult).FullName.ToLower();
            if (refreshCache)
                CacheHelper.Remove(key);
            return CacheHelper.Get(key, () =>
            {
                var map = mappingFunction ?? GeneralMapper.FromSQL2BO<TResult>;
                return MasterDl.GetMappings<TEntity>().ToList().Select(m => map(m));//Tolist executes the query before returning an IEnumerable
            });
        }

Using an SQL profiler is a big help in these king of problems.

Here the result of the cached enumerator:


Here with the .ToList() added to resolve the Enumerator



Angular: date pipe not showing in IE when using time filter.

I'm having a lot of IE11 problems today with Angular 4. I understand that the framework focusses more one real browsers, unfortunately in the corporate world IE11 is still not dead. During development I use Chrome, at test phase the users use IE 11 and then the problems come.

Today I already had some difficulties on this subject. No the date pipe of Angular is not working as expected in IE11. When I set the date format to: dd/MM/yyyy HH:mm for example, it works in Chrome. But in IE it is showing nothing. It has something to do with a bug in the browser, when using the minutes (mm) it stops working. If you only use the date format (dd/MM/yyyy) in the pipe, it works for IE11.

So, after some lookup I decided to make my own pipe. Because for example the momentJS was to heavy to use only for this problem.

This is how the pipe code looks:

import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'dateNLBE'
})
export class DateNlBePipe implements PipeTransform {
transform(value: Date, type: string = ''): string {
if (value !== null) {
return `${value.toLocaleDateString('nl-BE')} ${value.getHours() < 10 ? '0' + value.getHours() : '' + value.getHours()}:
${value.getMinutes() < 10 ? '0' + value.getMinutes() : '' + value.getMinutes()}`;
}
return '';
}
}


Now you can use it in your code like this:

<td>{{o.CreatedOn | dateNLBE }}</td>

And this works both in IE and Chrome.


Angular: Formbuilder and select showing default value in Chrome, not in IE

I had a problem with FormBuilder and IE. In Chrome everything worked fine, the select first was empty and required. If no value selected then it would give an error. But in IE the first value was shown in the select but not selected. Very confusing. 

To solve this I had to add a default option to the select with value null:

<select id="type" class="form-control" formControlName="Type" [class.is-invalid]="hasError('Type')">
<option [ngValue]="null">Please select value</option>
<option *ngFor="let o of ficheTypes" [ngValue]="o.name">{{o.name}}</option>
</select>

Now in the initialize form I added null as default value for the model that was binbed to the select. (Marked below)

 private initializeForm(): void {
this.formGroup = this.formBuilder.group({
'Id': FormBuilderHelper.getFormEntry('Id', true, false, this.fiche, 0),
'Type': FormBuilderHelper.getFormEntry('Type', true, false, this.fiche, null),
});

tslint: for .. in .. gives error: (... in ...) statements must be filtered with an if statement (forin)

I was rewriting a pipe from angular 1 to 4. There was a statement where tslint was giving me this error:


The code looked like this:
for (i in o) {
r.push(o[i]);
}

To get rid of the warning from tslint, you have to write an if inside the for loop to check if the proprty really exitst:

for (i in o) {
if (o.hasOwnProperty(i)) {
r.push(o[i]);
}
}


Because the object o is not an array, it expects the if statement to be present after the for loop. A switch statement cannot be used either without tslint warnings.


Angular: download blob in IE11 not working.

I had an Angular 4 web app where the user could open a document coming as a blob from our DB. In IE11 this didn't work the normal way. The code looked like this:

try {
	let link = window.document.createElement('a');
	let blob = new Blob([data], { type: document.FileType });
	let url = window.URL.createObjectURL(blob);
	
	link.setAttribute('href', url);
	link.setAttribute('download', document.FileName);

	let event = window.document.createEvent('MouseEvents');
	event.initMouseEvent('click', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null);
	link.dispatchEvent(event);

	defer.resolve(true);
}
catch (ex) {
	self.logService.log(ex);
	defer.resolve(false);
}

The problem was that the link was not supposed to have the href and download attribute set for IE. Also the url had to be build up in a slightly different way using the msSaveOrOpenBlob:

try {
	let link = window.document.createElement('a');
	let blob = new Blob([data], { type: document.FileType });
	let url = null;

	if (navigator.msSaveBlob || navigator.msSaveOrOpenBlob) {
		url = navigator.msSaveOrOpenBlob(blob, document.FileName);
	} else {
		url = window.URL.createObjectURL(blob);
		link.href = url;
		link.setAttribute('download', document.FileName);
	}

	let event = window.document.createEvent('MouseEvents');
	event.initMouseEvent('click', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null);
	link.dispatchEvent(event);

	defer.resolve(true);
}
catch (ex) {
	self.logService.log(ex);
	defer.resolve(false);
}

This check if (navigator.msSaveBlob || navigator.msSaveOrOpenBlob) will see if the browser needs to use a different way to open the BLOB. I believe this is specific for Microsoft browsers. More on this here. This worked for me, I hope it can help you.


AngularJS : Smart table select search not working in IE 11

I was building a web site and testing it in Chrome. Worked fine. Untill the users started testing in IE!! There the filtering in the table using dropdowns (select) didn't work. After some looking around I found out that the smarttable was binding to the input event. But IE only fires the change event with select. So the binding didn't work. The smarttable version 2.1.8 has an option to bind the search to another event:

<select st-input-event="change" st-search="Month" class="form-control">
    <option value="">All</option>
    <option ng-repeat="row in vm.filteredConsignments | unique:'Month'" value="{{row.Month}}">{{row.Month}}</option>
</select>

The st-input-event attribute allows you to change the binding. Now it can work with the change event, it still works in Chrome, but now also in IE (in my case IE11).




MSSQL: convert datetime to time

I needed to show the time between a start and end datetime in one record in the DB. This could be done using the CONVERT function:

CONVERT(char(10),  EndTime - StartTime, 114) as TotalTime

Then the result looked like this: