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 }
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 }
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 }
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 }
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 }