コード例 #1
0
func (this *Html2Imager) Do(req ufop.UfopRequest) (result interface{}, resultType int, contentType string, err error) {
	reqId := req.ReqId
	remoteSrcUrl, options, pErr := this.parse(req.Cmd)
	if pErr != nil {
		err = pErr
		return
	}

	//if not text format, error it
	if !strings.HasPrefix(req.Src.MimeType, "text/") {
		err = errors.New("unsupported file mime type, only text/* allowed")
		return
	}

	//if file size exceeds, error it
	if req.Src.Fsize > this.maxPageSize {
		err = errors.New("page file length exceeds the limit")
		return
	}

	jobPrefix := utils.Md5Hex(req.Src.Url)

	//prepare command
	cmdParams := make([]string, 0)
	cmdParams = append(cmdParams, "-q")

	if options.CropH > 0 {
		cmdParams = append(cmdParams, "--crop-h", fmt.Sprintf("%d", options.CropH))
	}

	if options.CropW > 0 {
		cmdParams = append(cmdParams, "--crop-w", fmt.Sprintf("%d", options.CropW))
	}

	if options.CropX > 0 {
		cmdParams = append(cmdParams, "--crop-x", fmt.Sprintf("%d", options.CropX))
	}

	if options.CropY > 0 {
		cmdParams = append(cmdParams, "--crop-y", fmt.Sprintf("%d", options.CropY))
	}

	if options.Format != "" {
		cmdParams = append(cmdParams, "--format", options.Format)
	}

	if options.Quality > 0 {
		cmdParams = append(cmdParams, "--quality", fmt.Sprintf("%d", options.Quality))
	}

	if options.Height > 0 {
		cmdParams = append(cmdParams, "--height", fmt.Sprintf("%d", options.Height))
	}

	if options.Width > 0 {
		cmdParams = append(cmdParams, "--width", fmt.Sprintf("%d", options.Width))
	}

	if options.Force {
		cmdParams = append(cmdParams, "--disable-smart-width")
	}

	//result tmp file
	resultTmpFname := fmt.Sprintf("%s%d.result.%s", jobPrefix, time.Now().UnixNano(), options.Format)
	resultTmpFpath := filepath.Join(os.TempDir(), resultTmpFname)

	cmdParams = append(cmdParams, remoteSrcUrl, resultTmpFpath)

	//cmd
	convertCmd := exec.Command("wkhtmltoimage", cmdParams...)
	log.Info(reqId, convertCmd.Path, convertCmd.Args)

	stdErrPipe, pipeErr := convertCmd.StderrPipe()
	if pipeErr != nil {
		err = errors.New(fmt.Sprintf("open exec stderr pipe error, %s", pipeErr.Error()))
		return
	}

	if startErr := convertCmd.Start(); startErr != nil {
		err = errors.New(fmt.Sprintf("start html2image command error, %s", startErr.Error()))
		return
	}

	stdErrData, readErr := ioutil.ReadAll(stdErrPipe)
	if readErr != nil {
		err = errors.New(fmt.Sprintf("read html2image command stderr error, %s", readErr.Error()))
		defer os.Remove(resultTmpFpath)
		return
	}

	//check stderr output & output file
	if string(stdErrData) != "" {
		log.Info(reqId, string(stdErrData))
	}

	if waitErr := convertCmd.Wait(); waitErr != nil {
		err = errors.New(fmt.Sprintf("wait html2image to exit error, %s", waitErr.Error()))
		defer os.Remove(resultTmpFpath)
		return
	}

	if oFileInfo, statErr := os.Stat(resultTmpFpath); statErr != nil || oFileInfo.Size() == 0 {
		err = errors.New("html2image with no valid output result")
		defer os.Remove(resultTmpFpath)
		return
	}

	//write result
	result = resultTmpFpath
	resultType = ufop.RESULT_TYPE_OCTECT_FILE
	if options.Format == "png" {
		contentType = "image/png"
	} else {
		contentType = "image/jpeg"
	}

	return
}
コード例 #2
0
func (this *Html2Pdfer) Do(req ufop.UfopRequest) (result interface{}, resultType int, contentType string, err error) {
	options, pErr := this.parse(req.Cmd)
	if pErr != nil {
		err = pErr
		return
	}

	//if not text format, error it
	if !strings.HasPrefix(req.Src.MimeType, "text/") {
		err = errors.New("unsupported file mime type, only text/* allowed")
		return
	}

	//if file size exceeds, error it
	if req.Src.Fsize > this.maxPageSize {
		err = errors.New("page file length exceeds the limit")
		return
	}

	if options.Copies > this.maxCopies {
		err = errors.New("pdf copies exceeds the limit")
		return
	}

	//get page file content save it into temp dir
	resp, respErr := http.Get(req.Src.Url)
	if respErr != nil || resp.StatusCode != 200 {
		if respErr != nil {
			err = errors.New(fmt.Sprintf("retrieve page file resource data failed, %s", respErr.Error()))
		} else {
			err = errors.New(fmt.Sprintf("retrieve page file resource data failed, %s", resp.Status))
			if resp.Body != nil {
				resp.Body.Close()
			}
		}
		return
	}

	jobPrefix := utils.Md5Hex(req.Src.Url)

	pageSuffix := "txt"
	if strings.HasPrefix(req.Src.MimeType, "text/html") {
		pageSuffix = "html"
	}

	localPageTmpFname := fmt.Sprintf("%s%d.page.%s", jobPrefix, time.Now().UnixNano(), pageSuffix)
	localPageTmpFpath := filepath.Join(os.TempDir(), localPageTmpFname)
	defer os.Remove(localPageTmpFpath)

	localPageTmpFp, openErr := os.OpenFile(localPageTmpFpath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0655)
	if openErr != nil {
		err = errors.New(fmt.Sprintf("open page file temp file failed, %s", openErr.Error()))
		return
	}
	_, cpErr := io.Copy(localPageTmpFp, resp.Body)
	if cpErr != nil {
		err = errors.New(fmt.Sprintf("save page file content to tmp file failed, %s", cpErr.Error()))
		return
	}

	localPageTmpFp.Close()
	resp.Body.Close()

	//prepare command
	cmdParams := make([]string, 0)
	cmdParams = append(cmdParams, "-q")

	if options.Gray {
		cmdParams = append(cmdParams, "--grayscale")
	}

	if options.LowQuality {
		cmdParams = append(cmdParams, "--lowquality")
	}

	if options.Orientation != "" {
		cmdParams = append(cmdParams, "--orientation", options.Orientation)
	}

	if options.Size != "" {
		cmdParams = append(cmdParams, "--page-size", options.Size)
	}

	if options.Title != "" {
		cmdParams = append(cmdParams, "--title", options.Title)
	}

	if options.Collate {
		cmdParams = append(cmdParams, "--collate")
	} else {
		cmdParams = append(cmdParams, "--no-collate")
	}

	cmdParams = append(cmdParams, "--copies", fmt.Sprintf("%d", options.Copies))

	//result tmp file
	resultTmpFname := fmt.Sprintf("%s%d.result.pdf", jobPrefix, time.Now().UnixNano())
	resultTmpFpath := filepath.Join(os.TempDir(), resultTmpFname)

	cmdParams = append(cmdParams, localPageTmpFpath, resultTmpFpath)

	//cmd
	convertCmd := exec.Command("wkhtmltopdf", cmdParams...)

	stdErrPipe, pipeErr := convertCmd.StderrPipe()
	if pipeErr != nil {
		err = errors.New(fmt.Sprintf("open exec stderr pipe error, %s", pipeErr.Error()))
		return
	}

	if startErr := convertCmd.Start(); startErr != nil {
		err = errors.New(fmt.Sprintf("start html2pdf command error, %s", startErr.Error()))
		return
	}

	stdErrData, readErr := ioutil.ReadAll(stdErrPipe)
	if readErr != nil {
		err = errors.New(fmt.Sprintf("read html2pdf command stderr error, %s", readErr.Error()))
		defer os.Remove(resultTmpFpath)
		return
	}

	//check stderr output & output file
	if string(stdErrData) != "" {
		log.Error(string(stdErrData))
	}

	if waitErr := convertCmd.Wait(); waitErr != nil {
		err = errors.New(fmt.Sprintf("wait html2pdf to exit error, %s", waitErr.Error()))
		defer os.Remove(resultTmpFpath)
		return
	}

	if oFileInfo, statErr := os.Stat(resultTmpFpath); statErr != nil || oFileInfo.Size() == 0 {
		err = errors.New("html2pdf with no valid output result")
		defer os.Remove(resultTmpFpath)
		return
	}

	//write result
	result = resultTmpFpath
	resultType = ufop.RESULT_TYPE_OCTECT_FILE
	contentType = "application/pdf"
	return
}
コード例 #3
0
func (this *Html2Pdfer) Do(req ufop.UfopRequest) (result interface{}, resultType int, contentType string, err error) {
	reqId := req.ReqId
	remoteSrcUrl, options, pErr := this.parse(req.Cmd)
	if pErr != nil {
		err = pErr
		return
	}

	//if not text format, error it
	if !strings.HasPrefix(req.Src.MimeType, "text/") {
		err = errors.New("unsupported file mime type, only text/* allowed")
		return
	}

	//if file size exceeds, error it
	if req.Src.Fsize > this.maxPageSize {
		err = errors.New("page file length exceeds the limit")
		return
	}

	if options.Copies > this.maxCopies {
		err = errors.New("pdf copies exceeds the limit")
		return
	}

	jobPrefix := utils.Md5Hex(req.Src.Url)

	//prepare command
	cmdParams := make([]string, 0)
	cmdParams = append(cmdParams, "-q")

	if options.Gray {
		cmdParams = append(cmdParams, "--grayscale")
	}

	if options.LowQuality {
		cmdParams = append(cmdParams, "--lowquality")
	}

	if options.Orientation != "" {
		cmdParams = append(cmdParams, "--orientation", options.Orientation)
	}

	if options.Size != "" {
		cmdParams = append(cmdParams, "--page-size", options.Size)
	}

	if options.Title != "" {
		cmdParams = append(cmdParams, "--title", options.Title)
	}

	if options.Collate {
		cmdParams = append(cmdParams, "--collate")
	} else {
		cmdParams = append(cmdParams, "--no-collate")
	}

	cmdParams = append(cmdParams, "--copies", fmt.Sprintf("%d", options.Copies))

	//result tmp file
	resultTmpFname := fmt.Sprintf("%s%d.result.pdf", jobPrefix, time.Now().UnixNano())
	resultTmpFpath := filepath.Join(os.TempDir(), resultTmpFname)

	cmdParams = append(cmdParams, remoteSrcUrl, resultTmpFpath)

	//cmd
	convertCmd := exec.Command("wkhtmltopdf", cmdParams...)
	log.Info(reqId, convertCmd.Path, convertCmd.Args)

	stdErrPipe, pipeErr := convertCmd.StderrPipe()
	if pipeErr != nil {
		err = errors.New(fmt.Sprintf("open exec stderr pipe error, %s", pipeErr.Error()))
		return
	}

	if startErr := convertCmd.Start(); startErr != nil {
		err = errors.New(fmt.Sprintf("start html2pdf command error, %s", startErr.Error()))
		return
	}

	stdErrData, readErr := ioutil.ReadAll(stdErrPipe)
	if readErr != nil {
		err = errors.New(fmt.Sprintf("read html2pdf command stderr error, %s", readErr.Error()))
		defer os.Remove(resultTmpFpath)
		return
	}

	//check stderr output & output file
	if string(stdErrData) != "" {
		log.Info(reqId, string(stdErrData))
	}

	if waitErr := convertCmd.Wait(); waitErr != nil {
		err = errors.New(fmt.Sprintf("wait html2pdf to exit error, %s", waitErr.Error()))
		defer os.Remove(resultTmpFpath)
		return
	}

	if oFileInfo, statErr := os.Stat(resultTmpFpath); statErr != nil || oFileInfo.Size() == 0 {
		err = errors.New("html2pdf with no valid output result")
		defer os.Remove(resultTmpFpath)
		return
	}

	//write result
	result = resultTmpFpath
	resultType = ufop.RESULT_TYPE_OCTECT_FILE
	contentType = "application/pdf"
	return
}
コード例 #4
0
func (this *Html2Imager) Do(req ufop.UfopRequest) (result interface{}, resultType int, contentType string, err error) {
	options, pErr := this.parse(req.Cmd)
	if pErr != nil {
		err = pErr
		return
	}

	//if not text format, error it
	if !strings.HasPrefix(req.Src.MimeType, "text/") {
		err = errors.New("unsupported file mime type, only text/* allowed")
		return
	}

	//if file size exceeds, error it
	if req.Src.Fsize > this.maxPageSize {
		err = errors.New("page file length exceeds the limit")
		return
	}

	//get page file content save it into temp dir
	resp, respErr := http.Get(req.Src.Url)
	if respErr != nil || resp.StatusCode != 200 {
		if respErr != nil {
			err = errors.New(fmt.Sprintf("retrieve page file resource data failed, %s", respErr.Error()))
		} else {
			err = errors.New(fmt.Sprintf("retrieve page file resource data failed, %s", resp.Status))
			if resp.Body != nil {
				resp.Body.Close()
			}
		}
		return
	}

	jobPrefix := utils.Md5Hex(req.Src.Url)

	pageSuffix := "txt"
	if strings.HasPrefix(req.Src.MimeType, "text/html") {
		pageSuffix = "html"
	}

	localPageTmpFname := fmt.Sprintf("%s%d.page.%s", jobPrefix, time.Now().UnixNano(), pageSuffix)
	localPageTmpFpath := filepath.Join(os.TempDir(), localPageTmpFname)
	defer os.Remove(localPageTmpFpath)

	localPageTmpFp, openErr := os.OpenFile(localPageTmpFpath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0655)
	if openErr != nil {
		err = errors.New(fmt.Sprintf("open page file temp file failed, %s", openErr.Error()))
		return
	}
	_, cpErr := io.Copy(localPageTmpFp, resp.Body)
	if cpErr != nil {
		err = errors.New(fmt.Sprintf("save page file content to tmp file failed, %s", cpErr.Error()))
		return
	}

	localPageTmpFp.Close()
	resp.Body.Close()

	//prepare command
	cmdParams := make([]string, 0)
	cmdParams = append(cmdParams, "-q")

	if options.CropH > 0 {
		cmdParams = append(cmdParams, "--crop-h", fmt.Sprintf("%d", options.CropH))
	}

	if options.CropW > 0 {
		cmdParams = append(cmdParams, "--crop-w", fmt.Sprintf("%d", options.CropW))
	}

	if options.CropX > 0 {
		cmdParams = append(cmdParams, "--crop-x", fmt.Sprintf("%d", options.CropX))
	}

	if options.CropY > 0 {
		cmdParams = append(cmdParams, "--crop-y", fmt.Sprintf("%d", options.CropY))
	}

	if options.Format != "" {
		cmdParams = append(cmdParams, "--format", options.Format)
	}

	if options.Quality > 0 {
		cmdParams = append(cmdParams, "--quality", fmt.Sprintf("%d", options.Quality))
	}

	if options.Height > 0 {
		cmdParams = append(cmdParams, "--height", fmt.Sprintf("%d", options.Height))
	}

	if options.Width > 0 {
		cmdParams = append(cmdParams, "--width", fmt.Sprintf("%d", options.Width))
	}

	if options.Force {
		cmdParams = append(cmdParams, "--disable-smart-width")
	}

	//result tmp file
	resultTmpFname := fmt.Sprintf("%s%d.result.%s", jobPrefix, time.Now().UnixNano(), options.Format)
	resultTmpFpath := filepath.Join(os.TempDir(), resultTmpFname)

	cmdParams = append(cmdParams, localPageTmpFpath, resultTmpFpath)

	//cmd
	convertCmd := exec.Command("wkhtmltoimage", cmdParams...)

	stdErrPipe, pipeErr := convertCmd.StderrPipe()
	if pipeErr != nil {
		err = errors.New(fmt.Sprintf("open exec stderr pipe error, %s", pipeErr.Error()))
		return
	}

	if startErr := convertCmd.Start(); startErr != nil {
		err = errors.New(fmt.Sprintf("start html2image command error, %s", startErr.Error()))
		return
	}

	stdErrData, readErr := ioutil.ReadAll(stdErrPipe)
	if readErr != nil {
		err = errors.New(fmt.Sprintf("read html2image command stderr error, %s", readErr.Error()))
		defer os.Remove(resultTmpFpath)
		return
	}

	//check stderr output & output file
	if string(stdErrData) != "" {
		log.Error(string(stdErrData))
	}

	if waitErr := convertCmd.Wait(); waitErr != nil {
		err = errors.New(fmt.Sprintf("wait html2image to exit error, %s", waitErr.Error()))
		defer os.Remove(resultTmpFpath)
		return
	}

	if oFileInfo, statErr := os.Stat(resultTmpFpath); statErr != nil || oFileInfo.Size() == 0 {
		err = errors.New("html2image with no valid output result")
		defer os.Remove(resultTmpFpath)
		return
	}

	//write result
	result = resultTmpFpath
	resultType = ufop.RESULT_TYPE_OCTECT_FILE
	if options.Format == "png" {
		contentType = "image/png"
	} else {
		contentType = "image/jpeg"
	}

	return
}
コード例 #5
0
func (this *ImageComposer) Do(req ufop.UfopRequest) (result interface{}, resultType int, contentType string, err error) {
	bucket, format, halign, valign, rows, cols, order, bgColor, margin, urls, pErr := this.parse(req.Cmd)
	if pErr != nil {
		err = pErr
		return
	}

	formatMimes := map[string]string{
		"png":  "image/png",
		"jpg":  "image/jpeg",
		"jpeg": "image/jpeg",
	}

	//check urls validity, all should in bucket
	statItems := make([]rs.EntryPath, 0)
	statUrls := make([]string, 0)
	for _, urlItem := range urls {
		iPath := urlItem["path"]
		iUrl := urlItem["url"]
		entryPath := rs.EntryPath{
			bucket, iPath,
		}
		statItems = append(statItems, entryPath)
		statUrls = append(statUrls, iUrl)
	}

	qclient := rs.New(this.mac)

	statRet, statErr := qclient.BatchStat(nil, statItems)

	if statErr != nil {
		if sErr, ok := statErr.(*rpc.ErrorInfo); !ok {
			err = errors.New(fmt.Sprintf("batch stat error, %s", statErr.Error()))
			return
		} else {
			if sErr.Err != "" {
				err = errors.New(fmt.Sprintf("batch stat error, %s", sErr.Err))
				return
			}
		}
	}

	for index := 0; index < len(statRet); index++ {
		ret := statRet[index]
		if ret.Code != 200 {
			if ret.Code == 612 {
				err = errors.New(fmt.Sprintf("batch stat '%s' error, no such file or directory", statUrls[index]))
			} else if ret.Code == 631 {
				err = errors.New(fmt.Sprintf("batch stat '%s' error, no such bucket", statUrls[index]))
			} else {
				err = errors.New(fmt.Sprintf("batch stat '%s' error, %d", statUrls[index], ret.Code))
			}
			return
		}
	}

	//download images by url
	localImgPathTypeMap := make(map[string]string)
	localImgPaths := make([]string, 0)
	remoteImgUrls := make(map[string]string)
	for _, urlItem := range urls {
		iUrl := urlItem["url"]
		iLocalName := fmt.Sprintf("imagecomp_tmp_%s_%d", utils.Md5Hex(iUrl), time.Now().UnixNano())
		iLocalPath := filepath.Join(os.TempDir(), iLocalName)
		dContentType, dErr := utils.Download(iUrl, iLocalPath)
		if dErr != nil {
			err = dErr
			return
		}

		if !(dContentType == "image/png" || dContentType == "image/jpeg") {
			err = errors.New(fmt.Sprintf("unsupported mimetype of '%s', '%s'", iUrl, dContentType))
			return
		}

		localImgPaths = append(localImgPaths, iLocalPath)
		localImgPathTypeMap[iLocalPath] = dContentType
		remoteImgUrls[iLocalPath] = iUrl
	}

	defer func() {
		for iPath, _ := range localImgPathTypeMap {
			os.Remove(iPath)
		}
	}()

	//layout the images
	localImgFps := make([]*os.File, 0)

	var localImgObjs [][]image.Image = make([][]image.Image, rows*cols)

	for index := 0; index < rows; index++ {
		localImgObjs[index] = make([]image.Image, cols)
	}

	var rowIndex int = 0
	var colIndex int = 0

	for _, iLocalPath := range localImgPaths {
		iContentType := localImgPathTypeMap[iLocalPath]
		imgFp, openErr := os.Open(iLocalPath)
		if openErr != nil {
			err = errors.New(fmt.Sprintf("open local image of remote '%s' failed, %s", remoteImgUrls[iLocalPath], openErr.Error()))
			return
		}
		localImgFps = append(localImgFps, imgFp)

		var imgObj image.Image
		var dErr error

		if iContentType == "image/png" {
			imgObj, dErr = png.Decode(imgFp)
			if dErr != nil {
				err = errors.New(fmt.Sprintf("decode png image of remote '%s' failed, %s", remoteImgUrls[iLocalPath], dErr.Error()))
				return
			}
		} else if iContentType == "image/jpeg" {
			imgObj, dErr = jpeg.Decode(imgFp)
			if dErr != nil {
				err = errors.New(fmt.Sprintf("decode jpeg image of remote '%s' failed, %s", remoteImgUrls[iLocalPath], dErr.Error()))
				return
			}
		} else {
			err = errors.New(fmt.Sprintf("unsupported src image format '%s' of url '%s'", iContentType, remoteImgUrls[iLocalPath]))
			return
		}

		localImgObjs[rowIndex][colIndex] = imgObj

		//update index
		switch order {
		case IMAGECOMP_ORDER_BY_ROW:
			if colIndex < cols-1 {
				colIndex += 1
			} else {
				colIndex = 0
				rowIndex += 1
			}

		case IMAGECOMP_ORDER_BY_COL:
			if rowIndex < rows-1 {
				rowIndex += 1
			} else {
				rowIndex = 0
				colIndex += 1
			}
		}
	}

	//close file handlers
	defer func() {
		for _, fp := range localImgFps {
			fp.Close()
		}
	}()

	//calc the dst image size
	dstImageWidth := 0
	dstImageHeight := 0

	rowImageMaxWidths := make([]int, 0)
	rowImageMaxHeights := make([]int, 0)

	for _, rowSlice := range localImgObjs {
		if len(rowSlice) == 0 {
			continue
		}

		rowImageColWidths := make([]int, 0)
		rowImageColHeights := make([]int, 0)

		for _, imgObj := range rowSlice {
			if imgObj != nil {
				bounds := imgObj.Bounds()
				rowImageColWidths = append(rowImageColWidths, bounds.Dx())
				rowImageColHeights = append(rowImageColHeights, bounds.Dy())
			}
		}

		rowImageColMaxWidth := utils.MaxInt(rowImageColWidths...)
		rowImageColMaxHeight := utils.MaxInt(rowImageColHeights...)

		rowImageMaxWidths = append(rowImageMaxWidths, rowImageColMaxWidth)
		rowImageMaxHeights = append(rowImageMaxHeights, rowImageColMaxHeight)
	}

	blockWidth := utils.MaxInt(rowImageMaxWidths...)
	blockHeight := utils.MaxInt(rowImageMaxHeights...)

	//dest image width & height with margin
	dstImageWidth = blockWidth*cols + (cols+1)*margin
	dstImageHeight = blockHeight*rows + (rows+1)*margin

	//compose the dst image
	dstRect := image.Rect(0, 0, dstImageWidth, dstImageHeight)
	dstImage := image.NewRGBA(dstRect)

	draw.Draw(dstImage, dstImage.Bounds(), image.NewUniform(bgColor), image.ZP, draw.Src)

	for rowIndex, rowSlice := range localImgObjs {
		for colIndex := 0; colIndex < len(rowSlice); colIndex++ {
			imgObj := rowSlice[colIndex]

			//check nil
			if imgObj == nil {
				continue
			}

			imgWidth := imgObj.Bounds().Max.X - imgObj.Bounds().Min.X
			imgHeight := imgObj.Bounds().Max.Y - imgObj.Bounds().Min.Y

			//calc the draw rect start point
			p1 := image.Point{
				colIndex*blockWidth + (colIndex+1)*margin,
				rowIndex*blockHeight + (rowIndex+1)*margin,
			}

			//check halign and valign
			//default is left and top
			switch halign {
			case H_ALIGN_CENTER:
				offset := (blockWidth - imgWidth) / 2
				p1.X += offset
			case H_ALIGN_RIGHT:
				offset := (blockWidth - imgWidth)
				p1.X += offset
			}

			switch valign {
			case V_ALIGN_MIDDLE:
				offset := (blockHeight - imgHeight) / 2
				p1.Y += offset
			case V_ALIGN_BOTTOM:
				offset := (blockHeight - imgHeight)
				p1.Y += offset
			}

			//calc the draw rect end point
			p2 := image.Point{}
			p2.X = p1.X + blockWidth
			p2.Y = p1.Y + blockHeight

			drawRect := image.Rect(p1.X, p1.Y, p2.X, p2.Y)

			//draw
			draw.Draw(dstImage, drawRect, imgObj, imgObj.Bounds().Min, draw.Src)
		}
	}

	//write result
	contentType = formatMimes[format]

	var buffer = bytes.NewBuffer(nil)
	switch contentType {
	case "image/png":
		eErr := png.Encode(buffer, dstImage)
		if eErr != nil {
			err = errors.New(fmt.Sprintf("create dst png image failed, %s", eErr))
			return
		}

	case "image/jpeg":
		eErr := jpeg.Encode(buffer, dstImage, &jpeg.Options{
			Quality: 100,
		})
		if eErr != nil {
			err = errors.New(fmt.Sprintf("create dst jpeg image failed, %s", eErr))
			return
		}
	}

	result = buffer.Bytes()
	resultType = ufop.RESULT_TYPE_OCTECT_BYTES
	return
}