import { Injectable } from '@angular/core'; import { Observable, Observer, of, } from 'rxjs'; import { map, } from 'rxjs/operators'; import { Uploader, UploadItem, } from 'angular2-http-file-upload'; import { PIG_FILL_MODES, PIGImageMetaInterface, PIGLayerImageInterface, PIGLayerInterface, PIGMaskInterface, PIGRenderInputsInterface, PIGResponseInterface, } from './../../../models/index'; import { HttpClient, } from '@angular/common/http'; @Injectable() export class PIGService { public uploadedImagesCache: { [key: string]: PIGImageMetaInterface } = {}; public cachedLayerComponents: { [key: string]: PIGLayerInterface, } = {}; constructor( private _http: HttpClient, ) {} public upload$( file: File, isProduction = true, ): Observable { return Observable.create(( observer: Observer, ) => { const uploader = new Uploader(); const uploadItem = new UploadItem(); uploadItem.url = this._getPIGUrl(isProduction) + '/upload/'; uploadItem.file = file; uploader.upload(uploadItem); uploader.onProgressUpload = (item, progress) => { observer.next(progress); }; uploader.onSuccessUpload = (item, response) => { observer.next( ( typeof response === 'string' ? JSON.parse(response) : response ) as PIGResponseInterface, ); observer.complete(); }; uploader.onErrorUpload = (item, response) => { observer.error(response); observer.complete(); }; }); } public adjustMaskValues( mask: PIGMaskInterface, variantWidth: number, variantHeight: number, ) { const bleedAreaCopy = { ...mask.bleed_area, }; const xPoints = []; const yPoints = []; bleedAreaCopy.points.forEach( (point) => { xPoints.push(...(point.map((i) => i.x))); yPoints.push(...(point.map((i) => i.y))); }, ); const minX = Math.min(...xPoints); const minY = Math.min(...yPoints); const maxX = Math.max(...xPoints); const maxY = Math.max(...yPoints); if (minX < 0 || minY < 0) { bleedAreaCopy.originalMinX = minX; bleedAreaCopy.originalMinY = minY; bleedAreaCopy.originalMaxX = maxX; bleedAreaCopy.originalMaxY = maxY; bleedAreaCopy.points = bleedAreaCopy.points.map((point) => ( point.map(({ x, y }) => ({ x: x - minX, y: y - minY, })) )); bleedAreaCopy.widthAdjustedForNegativeBleed = maxX - minX; bleedAreaCopy.heightAdjustedForNegativeBleed = maxY - minY; /* calculate the mask scale - used to scale masks which exceed the canvas size */ const maskScaleX = variantWidth / bleedAreaCopy.widthAdjustedForNegativeBleed; const maskScaleY = variantHeight / bleedAreaCopy.heightAdjustedForNegativeBleed; bleedAreaCopy.maskScale = Math.min( maskScaleX, maskScaleY, ); } else { bleedAreaCopy.widthAdjustedForNegativeBleed = variantWidth; bleedAreaCopy.heightAdjustedForNegativeBleed = variantHeight; } return { ...mask, bleed_area: bleedAreaCopy, }; } public processLayerComponents( product: PIGLayerInterface, imageVariantName: string, isProduction = true, ): PIGLayerImageInterface { const baseUrl = this._getPIGUrl(isProduction); const getImageURL = (path: string) => { return `${baseUrl}/${path}`; }; const imageVariant = product.images.find((productImage) => ( (productImage.name === imageVariantName) && Boolean(productImage.masks) )) || product.images.find( (productImage) => Boolean(productImage.masks), ); return { ...imageVariant, background: imageVariant.background && getImageURL(imageVariant.background), color_marker: imageVariant.color_marker && getImageURL(imageVariant.color_marker), color_overlay: imageVariant.color_overlay, foreground: { ...imageVariant.foreground, image: imageVariant.foreground && getImageURL(imageVariant.foreground.image), }, masks: imageVariant.masks.map((maskObj) => ({ ...this.adjustMaskValues( maskObj, imageVariant.width, imageVariant.height, ), mask: getImageURL(maskObj.mask), })), }; } public getLayerComponents$( templateId: string, imageVariantName = 'cover', isProduction = true, ): Observable { const pigData = this.cachedLayerComponents[templateId] ? of(this.cachedLayerComponents[templateId]) : this._http.get( `${this._getPIGUrl(isProduction)}/product/${templateId}/`, ).pipe( map((newPigData) => ( this.cachedLayerComponents[templateId] = newPigData )), ); return pigData.pipe( map((product) => { return this.processLayerComponents( product, imageVariantName, isProduction, ); }), ); } public getUrlWithFilters( urlPreview: string, variantName: string, filters?: string, variantType = 'cover', isProduction = true, ) { return ( urlPreview && filters && filters.includes('i') ) ? `${this._getPIGUrl(isProduction)}/render/4editor` + `/${variantName}/${variantType}/?image=${encodeURIComponent(urlPreview)}&` + `filters=i` : urlPreview; } public getRenderedImage( { border, backgroundColor = 'FFFFFF', debug = false, filters, fillMode = PIG_FILL_MODES.FIT, format = 'jpg', imageUrl, mirror = false, padding = 20, renderPrintImage = false, rotateDegrees = 0, scale = 1, size = { height: 628, width: 452, }, templateId, translateX = 0, translateY = 0, variantName, }: PIGRenderInputsInterface, isProduction = true, ) { const roundedDegrees = -Math.round(rotateDegrees); return this._getPIGUrl(isProduction) + '/render/?image=' + encodeURIComponent(imageUrl) + `&product_id=${templateId}` + `&variant=${variantName}` + `&format=${format}` + `&debug=${debug}` + `&background=${backgroundColor}` + `${size ? `&size=${size.width}x${size.height}` : ''}` + `&fill_mode=${fillMode}` + `&padding=${padding}` + `&scale=${scale}` + `&rotate=${roundedDegrees}` + `&mirror=${mirror}` + `&translate=${translateX},${translateY}` + (filters ? `&filters=${filters}` : '') + (border ? `&border=${border}` : '') + (renderPrintImage ? `&print_image=true` : ''); } public getUploadedImageMeta$( imageUrl: string, isProduction: boolean = true, ): Observable { return Observable.create( (observer: Observer) => { if (this.uploadedImagesCache[imageUrl]) { observer.next(this.uploadedImagesCache[imageUrl]); observer.complete(); } else { this._http.get( this._getPIGUrl(isProduction) + '/image_meta/?image=' + encodeURIComponent(imageUrl), ).subscribe( (metaData) => { if (metaData.error) { observer.error( 'Could not get image meta information for ' + `'${imageUrl}'`, ); observer.complete(); return undefined; } else { this.uploadedImagesCache[imageUrl] = metaData; observer.next(metaData); observer.complete(); } }, ); } }, ); } private _getPIGUrl( isProduction: boolean, ) { return process.env.PIG_URL || ( isProduction ? 'https://image.kite.ly' : 'https://piglet.kite.ly' ); } }