Skip to content

Commit

Permalink
feat(print): handle some print CORS errors and disable the print butt…
Browse files Browse the repository at this point in the history
…on while printing (#37)
  • Loading branch information
cbourget authored and mbarbeau committed May 3, 2017
1 parent 76a679f commit e20f5d6
Show file tree
Hide file tree
Showing 16 changed files with 120 additions and 83 deletions.
2 changes: 2 additions & 0 deletions src/demo-app/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@
<md-card-title>print.component</md-card-title>
<md-card-content>
<igo-print [map]="map"></igo-print>

<p *ngFor="let message of (messageService.messages$ | async)">{{message.text}}</p>
</md-card-content>
</md-card>

Expand Down
4 changes: 3 additions & 1 deletion src/demo-app/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Component, OnInit } from '@angular/core';

import { ContextService, Feature, FeatureService, IgoMap,
LanguageService, OverlayService, ToolService} from '../../lib';
LanguageService, MessageService, OverlayService,
ToolService } from '../../lib';

@Component({
selector: 'igo-demo',
Expand All @@ -15,6 +16,7 @@ export class AppComponent implements OnInit {

constructor(public contextService: ContextService,
public featureService: FeatureService,
public messageService: MessageService,
public overlayService: OverlayService,
public toolService: ToolService,
public language: LanguageService) {}
Expand Down
14 changes: 14 additions & 0 deletions src/lib/context/shared/map-context.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,20 @@ export class MapContextDirective implements OnInit, OnDestroy {
private handleContextChange(context: DetailedContext) {
if (context.map === undefined) { return; }

// This creates a new ol.Map when the context changes. Doing that
// allows the print tool to work properly even when the map's canvas
// has been tainted (CORS) with the layers of another context. This could
// have some side effects such as unbinding all listeners on the first map.
// A better solution would be to create a new map (preview) before
// printing and avoid the tainted canvas issue. This will come later so
// this snippet of code is kept here in case it takes too long becomes
// an issue

// const target = this.component.map.olMap.getTarget();
// this.component.map.olMap.setTarget(undefined);
// this.component.map.init();
// this.component.map.olMap.setTarget(target);

const viewOptions: MapViewContext = context.map.view;
if (viewOptions.keepCurrentView !== true) {
this.component.view = viewOptions as MapViewOptions;
Expand Down
17 changes: 17 additions & 0 deletions src/lib/core/message/message.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';

import { Message } from './message.interface';
import { MessageType } from './message.enum';


@Injectable()
Expand All @@ -18,4 +19,20 @@ export class MessageService {
this.messages$.next(messages_);
}

success(text: string, title?: string) {
this.message({
text: text,
title: title,
type: MessageType.SUCCESS
});
}

error(text: string, title?: string) {
this.message({
text: text,
title: title,
type: MessageType.ERROR
});
}

}
20 changes: 10 additions & 10 deletions src/lib/core/request/request.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,32 @@ import { Injectable } from '@angular/core';
import { Response } from '@angular/http';

import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';

import { Message, MessageService } from '../message';

@Injectable()
export class RequestService {

public requestCounter$ = new Subject<number>();

private count = 0;
public counter$ = new BehaviorSubject<number>(0);

constructor(private messageService: MessageService) { }

register(request: Observable<any>, title?: string) {
this.count += 1;
this.requestCounter$.next(this.count);
this.increment();

return request
.do((res) => this.handleError200(res))
.catch((res) => this.handleError(res, title))
.finally(this.unregister.call(this));
.finally(this.decrement.call(this));
}

increment() {
this.counter$.next(this.counter$.value + 1);
}

private unregister() {
this.count -= 1;
this.requestCounter$.next(this.count);
decrement() {
this.counter$.next(this.counter$.value - 1);
}

private handleError200(res: Response | any) {
Expand Down
4 changes: 4 additions & 0 deletions src/lib/map/shared/map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ export class IgoMap {
}

constructor() {
this.init();
}

init() {
this.olMap = new ol.Map({
controls: [
new ol.control.Attribution()
Expand Down
2 changes: 1 addition & 1 deletion src/lib/print/print-form/print-form.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
<button
md-raised-button
type="button"
[disabled]="!form.valid"
[disabled]="!form.valid || disabled"
(click)="handleFormSubmit(form.value, form.valid)">
{{'igo.print' | translate}}
</button>
Expand Down
9 changes: 8 additions & 1 deletion src/lib/print/print-form/print-form.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ export class PrintFormComponent {
public orientations = PrintOrientation;
public resolutions = PrintResolution;

@Input()
get disabled(): boolean { return this._disabled; }
set disabled(value: boolean) {
this._disabled = value;
}
private _disabled: boolean = false;

@Input()
get format(): PrintFormat { return this.formatField.value; }
set format(value: PrintFormat) {
Expand All @@ -37,7 +44,7 @@ export class PrintFormComponent {
@Input()
get resolution(): PrintResolution { return this.resolutionField.value; }
set resolution(value: PrintResolution) {
this.resolutionField.setValue(value || PrintResolution['72'], {
this.resolutionField.setValue(value || PrintResolution['96'], {
onlySelf: true
});
}
Expand Down
1 change: 1 addition & 0 deletions src/lib/print/print/print.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
[format]="format"
[orientation]="orientation"
[resolution]="resolution"
[disabled]="disabled"
(submit)="handleFormSubmit($event)">
</igo-print-form>
3 changes: 3 additions & 0 deletions src/lib/print/print/print.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { IgoTestModule } from '../../../test/module';
import { IgoSharedModule } from '../../shared';
import { MessageService, RequestService } from '../../core';

import { PrintService } from '../shared';
import { PrintFormComponent } from '../print-form';
Expand All @@ -23,6 +24,8 @@ describe('PrintComponent', () => {
PrintFormComponent
],
providers: [
MessageService,
RequestService,
PrintService
]
})
Expand Down
6 changes: 5 additions & 1 deletion src/lib/print/print/print.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import { PrintFormat, PrintOptions, PrintOrientation,
})
export class PrintComponent {

public disabled: boolean = false;

@Input()
get map(): IgoMap { return this._map; }
set map(value: IgoMap) {
Expand Down Expand Up @@ -43,7 +45,9 @@ export class PrintComponent {
constructor(private printService: PrintService) { }

handleFormSubmit(data: PrintOptions) {
this.printService.print(this.map, data);
this.disabled = true;
this.printService.print(this.map, data).subscribe((status) =>
this.disabled = false);
}

}
4 changes: 4 additions & 0 deletions src/lib/print/shared/print.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { TestBed, inject } from '@angular/core/testing';

import { MessageService, RequestService } from '../../core';

import { PrintService } from './print.service';

describe('PrintService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [],
providers: [
MessageService,
RequestService,
PrintService
]
});
Expand Down
69 changes: 43 additions & 26 deletions src/lib/print/shared/print.service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';

import { IgoMap } from '../../map';
import { SourceQueue } from '../../utils/sourcequeue';
import { MessageService, MessageType, RequestService } from '../../core';

import { PrintOptions } from './print.interface';
import { PrintDimension } from './print.type';
Expand All @@ -12,50 +14,65 @@ declare var jsPDF: any;
@Injectable()
export class PrintService {

constructor() {}
constructor(private messageService: MessageService,
private requestService: RequestService) {}

print(map: IgoMap, options: PrintOptions): Subject<MessageType> {
const status$ = new Subject();
const messageService = this.messageService;
const requestService = this.requestService;
requestService.increment();

print(map: IgoMap, options: PrintOptions) {
const format = options.format;
const resolution = +options.resolution;
const orientation = options.orientation;

const olMap = map.olMap;
const dim = PrintDimension[format];
const width = Math.round(dim[0] * resolution / 25.4);
const height = Math.round(dim[1] * resolution / 25.4);

const olMap = map.olMap;
const size = olMap.getSize();
const extent = olMap.getView().calculateExtent(size);

olMap.once('postcompose', function(event: any) {
const canvas = event.context.canvas;

new SourceQueue(olMap).subscribe(() => {
window.setTimeout(function() {
const pdf = new jsPDF(orientation, undefined, format);

let image;
try {
image = canvas.toDataURL('image/jpeg');
} catch (err) {
// TODO: Handle CORS errors
console.log('Security error: This map cannot be printed.');
}

if (image !== undefined) {
pdf.addImage(image, 'JPEG', 0, 0, dim[0], dim[1]);
pdf.save('map.pdf');
}

olMap.setSize(size);
olMap.getView().fit(extent);
olMap.renderSync();
}, 100);
}, this);
new SourceQueue(olMap).subscribe(() => window.setTimeout(function() {
const pdf = new jsPDF(orientation, undefined, format);

let image;
try {
image = canvas.toDataURL('image/jpeg');
} catch (err) {
console.log(err);
messageService.error(
'Security error: This map cannot be printed.', 'Print');
}

let status;
if (image !== undefined) {
pdf.addImage(image, 'JPEG', 0, 0, dim[0], dim[1]);
pdf.save('map.pdf');
status = MessageType.SUCCESS;
} else {
status = MessageType.ERROR;
}

olMap.setSize(size);
olMap.getView().fit(extent);
olMap.renderSync();

requestService.decrement();
status$.next(status);
}, 100));

});

olMap.setSize([width, height]);
olMap.getView().fit(extent);
olMap.renderSync();

return status$;
}

}
10 changes: 0 additions & 10 deletions src/lib/print/shared/templates/template-default.ts

This file was deleted.

33 changes: 0 additions & 33 deletions src/lib/print/shared/templates/template.ts

This file was deleted.

5 changes: 5 additions & 0 deletions src/lib/tool/tools/print-tool/print-tool.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { IgoTestModule } from '../../../../test/module';
import { IgoSharedModule } from '../../../shared';
import { IgoPrintModule } from '../../../print';
import { IgoMapModule } from '../../../map';
import { MessageService, RequestService } from '../../../core';

import { PrintToolComponent } from './print-tool.component';

Expand All @@ -21,6 +22,10 @@ describe('PrintToolComponent', () => {
],
declarations: [
PrintToolComponent
],
providers: [
MessageService,
RequestService
]
})
.compileComponents();
Expand Down

0 comments on commit e20f5d6

Please sign in to comment.