获取图片的orientation
ref to http://stackoverflow.com/questions/7584794/accessing-jpeg-exif-rotation-data-in-javascript-on-the-client-side
779635
function getOrientation(buffer){
    var view = new DataView(buffer);
    if (view.getUint16(0, false) != 0xFFD8) return -2;
    var length = view.byteLength, offset = 2;
    while (offset < length) {
        var marker = view.getUint16(offset, false);
        offset += 2;
        if (marker == 0xFFE1) {
            if (view.getUint32(offset += 2, false) != 0x45786966) return -1;
            var little = view.getUint16(offset += 6, false) == 0x4949;
            offset += view.getUint32(offset + 4, little);
            var tags = view.getUint16(offset, little);
            offset += 2;
            for (var i = 0; i < tags; i++)
                if (view.getUint16(offset + (i * 12), little) == 0x0112)
                    return view.getUint16(offset + (i * 12) + 8, little);
        }
        else if ((marker & 0xFF00) != 0xFF00) break;
        else offset += view.getUint16(offset, false);
    }
    return -1;
}
694399
public function fixOrientation()
    {
        if (!in_array(exif_imagetype($this->source->getInfos()), array(IMAGETYPE_JPEG, IMAGETYPE_TIFF_II, IMAGETYPE_TIFF_MM))) {
            return $this;
        }

        if (!extension_loaded('exif')) {
            throw new \RuntimeException('You need to EXIF PHP Extension to use this function');
        }

        $exif = @exif_read_data($this->source->getInfos());

        if ($exif === false || !array_key_exists('Orientation', $exif)) {
            return $this;
        }

        switch ($exif['Orientation']) {
            case 1:
                break;

            case 2:
                $this->flip(false, true);
                break;

            case 3: // 180 rotate left
                $this->rotate(180);
                break;

            case 4: // vertical flip
                $this->flip(true, false);
                break;

            case 5: // vertical flip + 90 rotate right
                $this->flip(true, false);
                $this->rotate(-90);
                break;

            case 6: // 90 rotate right
                $this->rotate(-90);
                break;

            case 7: // horizontal flip + 90 rotate right
                $this->flip(false, true);
                $this->rotate(-90);
                break;

            case 8: // 90 rotate left
                $this->rotate(90);
                break;
        }

        return $this;
    }
1070994
function ensureImageLoaded(url) {
            var img = new Image();
            var destroy = function () {
                if (img) {
                    img.src = ""; // 読み込み停止
                    img = null;
                }
            };
            return new Promise(function (resolve, reject) {
                img.onload = function (event) {
                    destroy();
                    resolve(url);
                };
                img.onerror = function (event) {
                    destroy();
                    reject(CDP.makeErrorInfo(CDP.RESULT_CODE.ERROR_CDP_TOOLS_IMAGE_LOAD_FAILED, TAG, "image load failed. [url: " + url + "]"));
                };
                img.src = url;
            }, destroy);
        }
40724
function computeFinalBuffer(image, thumbnail, exifData, orientation) {
  exifData['0th'][piexif.ImageIFD.Orientation] = 1
  if (typeof exifData['Exif'][piexif.ExifIFD.PixelXDimension] !== 'undefined') {
    exifData['Exif'][piexif.ExifIFD.PixelXDimension] = image.width
  }
  if (typeof exifData['Exif'][piexif.ExifIFD.PixelYDimension] !== 'undefined') {
    exifData['Exif'][piexif.ExifIFD.PixelYDimension] = image.height
  }
  if (thumbnail.buffer) {
    exifData['thumbnail'] = thumbnail.buffer.toString('binary')
  }
  const exifBytes = piexif.dump(exifData)
  const updatedBuffer = Buffer.from(piexif.insert(exifBytes, image.buffer.toString('binary')), 'binary')
  const updatedDimensions = {
    height: image.height,
    width: image.width,
  }
  return Promise.resolve({updatedBuffer, orientation, updatedDimensions})
}
776286
function(dir, options) {
    let ueOpts = []
    if(typeof dir === 'object'){
        if(Array.isArray(dir)){
            ueOpts = dir
        }else{
            options = dir
            ueOpts.push('public')
        }
    }else{
        ueOpts.push(dir || 'public')
    }

    const publicDir = path.resolve(ueOpts[0])
    const conf = Object.assign({}, config, ueOpts[1] || {})
    const uploadType = {
        [conf.imageActionName]: 'image',
        [conf.scrawlActionName]: 'scrawl',
        [conf.catcherActionName]: 'catcher',
        [conf.videoActionName]: 'video',
        [conf.fileActionName]: 'file',
    }
    const listType = {
        [conf.imageManagerActionName]: 'image',
        [conf.fileManagerActionName]: 'file',
    }

    // Ueditor Controller
    return async (ctx, next) => {
        let result = {}
        let {action, start = 0} = ctx.query
        start = parseInt(start)

        // 上传文件
        if(Object.keys(uploadType).includes(action)){
            const actionName = uploadType[action]
            let pathFormat = setFullPath(conf[actionName + 'PathFormat']).split('/')
            let filename = pathFormat.pop()
            try {
                // 涂鸦类型图片
                if(action === conf.scrawlActionName){
                    let base64Data = ctx.request.body[conf[actionName + 'FieldName']]
                    let base64Length = base64Data.length
                    if(base64Length - (base64Length / 8) * 2 > conf[actionName + 'MaxSize']){
                        throw new Error('Picture too big')
                    }
                    ctx.req.file = upload.base64Image(base64Data, publicDir, {
                        destination: path.join(publicDir, ...pathFormat)
                    })

                    result = Object.assign({state: 'SUCCESS'}, upload.fileFormat(ctx.req.file))
                }
                // 抓取远程图片
                else if(action === conf.catcherActionName){
                    const sources = ctx.request.body[conf[actionName + 'FieldName']]
                    let list = []
                    let images = []
                    sources.forEach((url) => {
                        images.push(catchImage(url).then((image) => {
                            if(image.error){
                                list.push({state: 'ERROR', source: url})
                            }else{
                                let base64Data = image.base64Data
                                let base64Length = base64Data.length
                                if(base64Length - (base64Length / 8) * 2 > conf[actionName + 'MaxSize']){
                                    list.push({state: 'Picture too big', source: url})
                                }else{
                                    // 重新获取filename
                                    filename = setFullPath(conf[actionName + 'PathFormat']).split('/').pop()
                                    if(filename === '{filename}'){
                                        filename = image.originalname.replace(/\.\w+$/, '')
                                    }
                                    if(/^image\/(\w+)$/.test(image.contentType)){
                                        base64Data = 'data:'+ image.contentType +';base64,' + base64Data
                                    }
                                    list.push(Object.assign({state: 'SUCCESS', source: url}, upload.fileFormat(
                                        upload.base64Image(base64Data, publicDir, {
                                            destination: path.join(publicDir, ...pathFormat),
                                            filename
                                        })
                                    ), {original: image.originalname}))
                                }
                            }
                            return image
                        }))
                    })

                    await Promise.all(images)
                    result = {state: 'SUCCESS', list}
                }
                // 表单上传图片、文件
                else{
                    await upload(publicDir, {
                        storage: upload.diskStorage({
                            destination: path.join(publicDir, ...pathFormat),
                            filename (req, file, cb) {
                                if(filename === '{filename}'){
                                    filename = file.originalname
                                }else{
                                    filename += upload.getSuffix(file.originalname)
                                }
                                cb(null, filename)
                            }
                        }),
                        limits: {
                            fileSize: conf[actionName + 'MaxSize']
                        },
                        allowfiles: conf[actionName + 'AllowFiles']
                    }, options || {}).single(conf[actionName + 'FieldName'])(ctx, next)

                    result = Object.assign({state: 'SUCCESS'}, upload.fileFormat(ctx.req.file))
                }
            } catch (err) {
                result = {state: err.message}
            }
        }
        // 获取图片/文件列表
        else if(Object.keys(listType).includes(action)){
            const actionName = listType[action]
            let files = []
            eachFileSync(path.join(publicDir, conf[actionName + 'ManagerListPath']), (file, stat) => {
                if(conf[actionName + 'ManagerAllowFiles'].includes(upload.getSuffix(file))){
                    const url = file.replace(publicDir, '').replace(/\\/g, '\/')
                    const mtime = stat.mtimeMs
                    files.push({url, mtime})
                }
            })
            result = {
                list: files.slice(start, start + conf[actionName + 'ManagerListSize']),
                start: start,
                total: files.length,
                state: 'SUCCESS'
            }
        }
        // 返回Ueditor配置给前端
        else if(action === 'config'){
            result = conf
        }
        else{
            result = {state: 'FAIL'}
        }

        ctx.body = JSON.stringify(result)
    }
}
1178340
function parseAngle (string) {
	const value = parseNum(string);
	if (value) {
		if (value.num === 0 || !value.unit || value.unit === "deg") {
			// 没有角度单位，或数值为0，或角度单位为度，则无需换算
			value.deg = value.num;
		} else if (angleUnitMap[value.unit]) {
			// 不同角度单位间的换算
			value.deg = value.num * 360 / angleUnitMap[value.unit];
		} else {
			return;
		}
		// 修正为w3c语法的角度。0°为上方而非右方
		value.deg = require("normalize-range").wrap(0, 360, 90 - value.deg);
		return value;
	}
}
977591
private int getRotationAngle(final ExifInterface exifInterface) {
    return JfifUtil.getAutoRotateAngleFromOrientation(
        Integer.parseInt(exifInterface.getAttribute(ExifInterface.TAG_ORIENTATION)));
  }
806789
public static void slice(Image srcImage, File descDir, int destWidth, int destHeight) {
		if (destWidth <= 0) {
			destWidth = 200; // 切片宽度
		}
		if (destHeight <= 0) {
			destHeight = 150; // 切片高度
		}
		int srcWidth = srcImage.getHeight(null); // 源图宽度
		int srcHeight = srcImage.getWidth(null); // 源图高度

		try {
			if (srcWidth > destWidth && srcHeight > destHeight) {
				int cols = 0; // 切片横向数量
				int rows = 0; // 切片纵向数量
				// 计算切片的横向和纵向数量
				if (srcWidth % destWidth == 0) {
					cols = srcWidth / destWidth;
				} else {
					cols = (int) Math.floor(srcWidth / destWidth) + 1;
				}
				if (srcHeight % destHeight == 0) {
					rows = srcHeight / destHeight;
				} else {
					rows = (int) Math.floor(srcHeight / destHeight) + 1;
				}
				// 循环建立切片
				BufferedImage tag;
				for (int i = 0; i < rows; i++) {
					for (int j = 0; j < cols; j++) {
						// 四个参数分别为图像起点坐标和宽高
						// 即: CropImageFilter(int x,int y,int width,int height)
						tag = cut(srcImage, new Rectangle(j * destWidth, i * destHeight, destWidth, destHeight));
						// 输出为文件
						ImageIO.write(tag, IMAGE_TYPE_JPEG, new File(descDir, "_r" + i + "_c" + j + ".jpg"));
					}
				}
			}
		} catch (IOException e) {
			throw new IORuntimeException(e);
		}
	}
1372901
public function processImage($path, $option = [])
    {
        // 图片重力
        try {
            if (extension_loaded('exif')) {
                $image = imagecreatefromstring(Common::readFile($path));
                $exif = exif_read_data($path);
                if (! empty($exif['Orientation'])) {
                    switch ($exif['Orientation']) {
                        case 8:
                            $image = imagerotate($image, 90, 0);
                            break;
                        case 3:
                            $image = imagerotate($image, 180, 0);
                            break;
                        case 6:
                            $image = imagerotate($image, - 90, 0);
                            break;
                    }
                    imagejpeg($image, $path);
                }
            }
        } catch (\Exception $e) {}
        
        // 图片大小
        try {
            if (extension_loaded('gd')) {
                if (isset($option['width']) || isset($option['height'])) {
                    $image = Image::open($path);
                    if ($image) {
                        $width = isset($option['width']) && ! empty($option['width']) ? $option['width'] : $image->width();
                        $height = isset($option['height']) && ! empty($option['height']) ? $option['height'] : $image->height();
                        $image = $image->thumb($width, $height);
                        $image->save($path);
                    }
                }
            }
        } catch (\Exception $e) {}
    }
596125
def rotate(self, img):
        
        try:
            exif = image2exif.get_exif(img)
        except AttributeError:
            # image format doesn't support exif
            return img
        
        orientation = exif.get('Orientation', 1)

        landscape = img.height < img.width
        
        if orientation == 6 and landscape:
            print("ROTATING")
            return img.rotate(-90)

        return img
265960
def rotate_exif(self):
        
        exif = self.image._getexif() or {}
        rotation = exif.get(TAGS['Orientation'], 1)

        rotations = {
            6: -90,
            3: -180,
            8: -270,
        }
        if rotation not in rotations:
            return

        self.image = self.image.rotate(rotations[rotation])
498554
public function rotateExif()
    {
        if (!in_array($this->fileType, array(IMAGETYPE_JPEG, IMAGETYPE_TIFF_II, IMAGETYPE_TIFF_MM))) {
            $this->log("Autorotate ignored, EXIF not supported by this filetype.");
            return $this;
        }

        $exif = exif_read_data($this->pathToImage);

        if (!empty($exif['Orientation'])) {
            switch ($exif['Orientation']) {
                case 3:
                    $this->log("Autorotate 180.");
                    $this->rotate(180, $this->bgColor);
                    break;

                case 6:
                    $this->log("Autorotate -90.");
                    $this->rotate(-90, $this->bgColor);
                    break;

                case 8:
                    $this->log("Autorotate 90.");
                    $this->rotate(90, $this->bgColor);
                    break;

                default:
                    $this->log("Autorotate ignored, unknown value as orientation.");
            }
        } else {
            $this->log("Autorotate ignored, no orientation in EXIF.");
        }

        return $this;
    }
1136595
def rotate(self, deg, **kwargs):
        
        opts = Image._normalize_options(kwargs)

        if deg == "auto":
            if self._orig_format == "JPEG":
                try:
                    exif = self.img._getexif() or dict()
                    deg = _orientation_to_rotation.get(exif.get(274, 0), 0)
                except Exception:
                    logger.warn('unable to parse exif')
                    deg = 0
            else:
                deg = 0

        deg = 360 - (int(deg) % 360)
        if deg % 90 == 0:
            if deg == 90:
                self.img = self.img.transpose(PIL.Image.ROTATE_90)
            elif deg == 180:
                self.img = self.img.transpose(PIL.Image.ROTATE_180)
            elif deg == 270:
                self.img = self.img.transpose(PIL.Image.ROTATE_270)
        else:
            self.img = self.img.rotate(deg, expand=bool(int(opts["expand"])))

        return self
964659
def exif_orientation(im):
    
    try:
        exif = im._getexif()
    except Exception:
        # There are many ways that _getexif fails, we're just going to blanket
        # cover them all.
        exif = None
    if exif:
        orientation = exif.get(0x0112)
        if orientation == 2:
            im = im.transpose(Image.FLIP_LEFT_RIGHT)
        elif orientation == 3:
            im = im.transpose(Image.ROTATE_180)
        elif orientation == 4:
            im = im.transpose(Image.FLIP_TOP_BOTTOM)
        elif orientation == 5:
            im = im.transpose(Image.ROTATE_270).transpose(Image.FLIP_LEFT_RIGHT)
        elif orientation == 6:
            im = im.transpose(Image.ROTATE_270)
        elif orientation == 7:
            im = im.transpose(Image.ROTATE_90).transpose(Image.FLIP_LEFT_RIGHT)
        elif orientation == 8:
            im = im.transpose(Image.ROTATE_90)
    return im
617526
function fixOldLinearGradient (args) {
	let angle;
	// 从args[0]中获取起始点信息
	const start = normalizePos(args[0]);
	// 从args[1]中获取终止点信息
	const end = normalizePos(args[1]);

	// 根据起始点和终止点计算角度
	angle = 180 - Math.atan2(end.x - start.x, end.y - start.y) * (180 / Math.PI);
	// 将角度进行坐标系变换
	angle = require("normalize-range").wrap(0, 360, angle);
	// 如果角度不等于180°，将其转换为字符串后返回
	if (angle !== 180) {
		angle = angle2String({
			deg: angle,
		});
		return angle;
	}
}
1010244
function cleanSVG(svgDom, x, y) {
            function getTransformToElement(target, source) {
                var matrix;
                try {
                    matrix = source.getScreenCTM().inverse();
                } catch (e) {
                    throw new Error("Can not inverse source element' ctm.");
                }
                return matrix.multiply(target.getScreenCTM());
            }
            function dealWithPath(d, dealWithPattern) {
                if (!(dealWithPattern instanceof Function)) {
                    dealWithPattern = function() {};
                }
                var strArr = [], pattern = [], cache = [];
                for (var i = 0, l = d.length; i < l; i++) {
                    switch (d[i]) {
                      case "M":
                      case "L":
                      case "T":
                      case "S":
                      case "A":
                      case "C":
                      case "H":
                      case "V":
                      case "Q":
                        {
                            if (cache.length) {
                                pattern.push(cache.join(""));
                                cache = [];
                            }
                            // 脑图的path格式真奇怪...偶尔就给我蹦出来一个"..V123 C..", 那空格几个意思 - -
                            if (pattern[pattern.length - 1] === ",") {
                                pattern.pop();
                            }
                            if (pattern.length) {
                                dealWithPattern(pattern);
                                strArr.push(pattern.join(""));
                                pattern = [];
                            }
                            pattern.push(d[i]);
                            break;
                        }

                      case "Z":
                      case "z":
                        {
                            pattern.push(cache.join(""), d[i]);
                            dealWithPattern(pattern);
                            strArr.push(pattern.join(""));
                            cache = [];
                            pattern = [];
                            break;
                        }

                      case ".":
                      case "e":
                        {
                            cache.push(d[i]);
                            break;
                        }

                      case "-":
                        {
                            if (d[i - 1] !== "e") {
                                if (cache.length) {
                                    pattern.push(cache.join(""), ",");
                                }
                                cache = [];
                            }
                            cache.push("-");
                            break;
                        }

                      case " ":
                      case ",":
                        {
                            if (cache.length) {
                                pattern.push(cache.join(""), ",");
                                cache = [];
                            }
                            break;
                        }

                      default:
                        {
                            if (/\d/.test(d[i])) {
                                cache.push(d[i]);
                            } else {
                                // m a c s q h v l t z情况
                                if (cache.length) {
                                    pattern.push(cache.join(""), d[i]);
                                    cache = [];
                                } else {
                                    // 脑图的path格式真奇怪...偶尔就给我蹦出来一个"..V123 c..", 那空格几个意思 - -
                                    if (pattern[pattern.length - 1] === ",") {
                                        pattern.pop();
                                    }
                                    pattern.push(d[i]);
                                }
                            }
                            if (i + 1 === l) {
                                if (cache.length) {
                                    pattern.push(cache.join(""));
                                }
                                dealWithPattern(pattern);
                                strArr.push(pattern.join(""));
                                cache = null;
                                pattern = null;
                            }
                        }
                    }
                }
                return strArr.join("");
            }
            function replaceWithNode(svgNode, parentX, parentY) {
                if (!svgNode) {
                    return;
                }
                if (svgNode.tagName === "defs") {
                    return;
                }
                if (svgNode.getAttribute("fill") === "transparent") {
                    svgNode.setAttribute("fill", "none");
                }
                if (svgNode.getAttribute("marker-end")) {
                    svgNode.removeAttribute("marker-end");
                }
                parentX = parentX || 0;
                parentY = parentY || 0;
                if (svgNode.getAttribute("transform")) {
                    var ctm = getTransformToElement(svgNode, svgNode.parentElement);
                    parentX -= ctm.e;
                    parentY -= ctm.f;
                    svgNode.removeAttribute("transform");
                }
                switch (svgNode.tagName.toLowerCase()) {
                  case "g":
                    break;

                  case "path":
                    {
                        var d = svgNode.getAttribute("d");
                        if (d) {
                            d = dealWithPath(d, function(pattern) {
                                switch (pattern[0]) {
                                  case "V":
                                    {
                                        pattern[1] = +pattern[1] - parentY;
                                        break;
                                    }

                                  case "H":
                                    {
                                        pattern[1] = +pattern[1] - parentX;
                                        break;
                                    }

                                  case "M":
                                  case "L":
                                  case "T":
                                    {
                                        pattern[1] = +pattern[1] - parentX;
                                        pattern[3] = +pattern[3] - parentY;
                                        break;
                                    }

                                  case "Q":
                                  case "S":
                                    {
                                        pattern[1] = +pattern[1] - parentX;
                                        pattern[3] = +pattern[3] - parentY;
                                        pattern[5] = +pattern[5] - parentX;
                                        pattern[7] = +pattern[7] - parentY;
                                        break;
                                    }

                                  case "A":
                                    {
                                        pattern[11] = +pattern[11] - parentX;
                                        pattern[13] = +pattern[13] - parentY;
                                        break;
                                    }

                                  case "C":
                                    {
                                        pattern[1] = +pattern[1] - parentX;
                                        pattern[3] = +pattern[3] - parentY;
                                        pattern[5] = +pattern[5] - parentX;
                                        pattern[7] = +pattern[7] - parentY;
                                        pattern[9] = +pattern[9] - parentX;
                                        pattern[11] = +pattern[11] - parentY;
                                    }
                                }
                            });
                            svgNode.setAttribute("d", d);
                            svgNode.removeAttribute("transform");
                        }
                        return;
                    }

                  case "image":
                  case "text":
                    {
                        if (parentX && parentY) {
                            var x = +svgNode.getAttribute("x") || 0, y = +svgNode.getAttribute("y") || 0;
                            svgNode.setAttribute("x", x - parentX);
                            svgNode.setAttribute("y", y - parentY);
                        }
                        if (svgNode.getAttribute("dominant-baseline")) {
                            svgNode.removeAttribute("dominant-baseline");
                            svgNode.setAttribute("dy", ".8em");
                        }
                        svgNode.removeAttribute("transform");
                        return;
                    }
                }
                if (svgNode.children) {
                    for (var i = 0, l = svgNode.children.length; i < l; i++) {
                        replaceWithNode(svgNode.children[i], parentX, parentY);
                    }
                }
            }
            svgDom.style.visibility = "hidden";
            replaceWithNode(svgDom, x || 0, y || 0);
            svgDom.style.visibility = "visible";
        }
1100485
static function cropImage($image, $params, $original_size, $crop_size)
    {
        $qulitity = isset($params['qulitity']) ? $params['qulitity'] : 100;
        $dst_width = isset($params['width']) ? $params['width'] : 90;
        $dst_height = isset($params['height']) ? $params['height'] : 105;

        $image = WEBPATH . $image;
        if (!file_exists($image))
        {
            return '错误，图片不存在！';
        }

        $image_info = getimagesize($image);

        if ($image_info["mime"] == "image/jpeg" || $image_info["mime"] == "image/gif" || $image_info["mime"] == "image/png")
        {
            /**
             * 计算实际裁剪区域，图片是否被缩放，如果不是真实大小，需要计算
             */
            if (isset($params['abs_width']))
            {
                $tmp_rate = $params['abs_width'] / $params['width'];
                $crop_size['left'] = $crop_size['left'] * $tmp_rate;
                $crop_size['top'] = $crop_size['top'] * $tmp_rate;
                $crop_size['width'] = $crop_size['width'] * $tmp_rate;
                $crop_size['height'] = $crop_size['height'] * $tmp_rate;
            }

            //裁剪
            $image_new = imagecreatetruecolor($dst_width, $dst_height);
            switch ($image_info["mime"])
            {
                case "image/jpeg":
                    $bin_ori = imagecreatefromjpeg($image);
                    break;
                case "image/gif":
                    $bin_ori = imagecreatefromgif($image);
                    break;
                case "image/png":
                    $bin_ori = imagecreatefrompng($image);
                    break;
            }

            imagecopyresampled($image_new, $bin_ori, 0, 0, $crop_size['left'], $crop_size['top'], $dst_width,
                $dst_height, $crop_size['width'], $crop_size['height']);
            $file_new = WEBPATH . $params['newfile'];
            if (!file_exists(dirname($file_new)))
            {
                mkdir(dirname($file_new), 0777, true);
            }

            return imagejpeg($image_new, $file_new, $qulitity);
        }
    }