Skip to content

Commit 2ccd00a

Browse files
authored
Merge pull request #2322 from youngyou/fix-drop-image-rotate
Fix drop image rotate wrong
2 parents 0123a99 + 2bbcb8c commit 2ccd00a

File tree

1 file changed

+142
-15
lines changed

1 file changed

+142
-15
lines changed

browser/main/lib/dataApi/attachmentManagement.js

Lines changed: 142 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,118 @@ import i18n from 'browser/lib/i18n'
1111
const STORAGE_FOLDER_PLACEHOLDER = ':storage'
1212
const DESTINATION_FOLDER = 'attachments'
1313
const PATH_SEPARATORS = escapeStringRegexp(path.posix.sep) + escapeStringRegexp(path.win32.sep)
14+
/**
15+
* @description
16+
* Create a Image element to get the real size of image.
17+
* @param {File} file the File object dropped.
18+
* @returns {Promise<Image>} Image element created
19+
*/
20+
function getImage (file) {
21+
return new Promise((resolve) => {
22+
const reader = new FileReader()
23+
const img = new Image()
24+
img.onload = () => resolve(img)
25+
reader.onload = e => {
26+
img.src = e.target.result
27+
}
28+
reader.readAsDataURL(file)
29+
})
30+
}
31+
32+
/**
33+
* @description
34+
* Get the orientation info from iamges's EXIF data.
35+
* case 1: The 0th row is at the visual top of the image, and the 0th column is the visual left-hand side.
36+
* case 2: The 0th row is at the visual top of the image, and the 0th column is the visual right-hand side.
37+
* case 3: The 0th row is at the visual bottom of the image, and the 0th column is the visual right-hand side.
38+
* case 4: The 0th row is at the visual bottom of the image, and the 0th column is the visual left-hand side.
39+
* case 5: The 0th row is the visual left-hand side of the image, and the 0th column is the visual top.
40+
* case 6: The 0th row is the visual right-hand side of the image, and the 0th column is the visual top.
41+
* case 7: The 0th row is the visual right-hand side of the image, and the 0th column is the visual bottom.
42+
* case 8: The 0th row is the visual left-hand side of the image, and the 0th column is the visual bottom.
43+
* Other: reserved
44+
* ref: http://sylvana.net/jpegcrop/exif_orientation.html
45+
* @param {File} file the File object dropped.
46+
* @returns {Promise<Number>} Orientation info
47+
*/
48+
function getOrientation (file) {
49+
const getData = arrayBuffer => {
50+
const view = new DataView(arrayBuffer)
51+
52+
// Not start with SOI(Start of image) Marker return fail value
53+
if (view.getUint16(0, false) !== 0xFFD8) return -2
54+
const length = view.byteLength
55+
let offset = 2
56+
while (offset < length) {
57+
const marker = view.getUint16(offset, false)
58+
offset += 2
59+
// Loop and seed for APP1 Marker
60+
if (marker === 0xFFE1) {
61+
// return fail value if it isn't EXIF data
62+
if (view.getUint32(offset += 2, false) !== 0x45786966) {
63+
return -1
64+
}
65+
// Read TIFF header,
66+
// First 2bytes defines byte align of TIFF data.
67+
// If it is 0x4949="II", it means "Intel" type byte align.
68+
// If it is 0x4d4d="MM", it means "Motorola" type byte align
69+
const little = view.getUint16(offset += 6, false) === 0x4949
70+
offset += view.getUint32(offset + 4, little)
71+
const tags = view.getUint16(offset, little) // Get TAG number
72+
offset += 2
73+
for (let i = 0; i < tags; i++) {
74+
// Loop to find Orientation TAG and return the value
75+
if (view.getUint16(offset + (i * 12), little) === 0x0112) {
76+
return view.getUint16(offset + (i * 12) + 8, little)
77+
}
78+
}
79+
} else if ((marker & 0xFF00) !== 0xFF00) { // If not start with 0xFF, not a Marker
80+
break
81+
} else {
82+
offset += view.getUint16(offset, false)
83+
}
84+
}
85+
return -1
86+
}
87+
return new Promise((resolve) => {
88+
const reader = new FileReader()
89+
reader.onload = event => resolve(getData(event.target.result))
90+
reader.readAsArrayBuffer(file.slice(0, 64 * 1024))
91+
})
92+
}
93+
/**
94+
* @description
95+
* Rotate image file to correct direction.
96+
* Create a canvas and draw the image with correct direction, then export to base64 format.
97+
* @param {*} file the File object dropped.
98+
* @return {String} Base64 encoded image.
99+
*/
100+
function fixRotate (file) {
101+
return Promise.all([getImage(file), getOrientation(file)])
102+
.then(([img, orientation]) => {
103+
const canvas = document.createElement('canvas')
104+
const ctx = canvas.getContext('2d')
105+
if (orientation > 4 && orientation < 9) {
106+
canvas.width = img.height
107+
canvas.height = img.width
108+
} else {
109+
canvas.width = img.width
110+
canvas.height = img.height
111+
}
112+
switch (orientation) {
113+
case 2: ctx.transform(-1, 0, 0, 1, img.width, 0); break
114+
case 3: ctx.transform(-1, 0, 0, -1, img.width, img.height); break
115+
case 4: ctx.transform(1, 0, 0, -1, 0, img.height); break
116+
case 5: ctx.transform(0, 1, 1, 0, 0, 0); break
117+
case 6: ctx.transform(0, 1, -1, 0, img.height, 0); break
118+
case 7: ctx.transform(0, -1, -1, 0, img.height, img.width); break
119+
case 8: ctx.transform(0, -1, 1, 0, 0, img.width); break
120+
default: break
121+
}
122+
ctx.drawImage(img, 0, 0)
123+
return canvas.toDataURL()
124+
})
125+
}
14126

15127
/**
16128
* @description
@@ -38,26 +150,34 @@ function copyAttachment (sourceFilePath, storageKey, noteKey, useRandomName = tr
38150
}
39151

40152
try {
41-
if (!fs.existsSync(sourceFilePath)) {
42-
reject('source file does not exist')
153+
const isBase64 = typeof sourceFilePath === 'object' && sourceFilePath.type === 'base64'
154+
if (!fs.existsSync(sourceFilePath) && !isBase64) {
155+
return reject('source file does not exist')
43156
}
44-
45157
const targetStorage = findStorage.findStorage(storageKey)
46-
47-
const inputFileStream = fs.createReadStream(sourceFilePath)
48158
let destinationName
49159
if (useRandomName) {
50-
destinationName = `${uniqueSlug()}${path.extname(sourceFilePath)}`
160+
destinationName = `${uniqueSlug()}${path.extname(sourceFilePath.sourceFilePath || sourceFilePath)}`
51161
} else {
52-
destinationName = path.basename(sourceFilePath)
162+
destinationName = path.basename(sourceFilePath.sourceFilePath || sourceFilePath)
53163
}
54164
const destinationDir = path.join(targetStorage.path, DESTINATION_FOLDER, noteKey)
55165
createAttachmentDestinationFolder(targetStorage.path, noteKey)
56166
const outputFile = fs.createWriteStream(path.join(destinationDir, destinationName))
57-
inputFileStream.pipe(outputFile)
58-
inputFileStream.on('end', () => {
59-
resolve(destinationName)
60-
})
167+
168+
if (isBase64) {
169+
const base64Data = sourceFilePath.data.replace(/^data:image\/\w+;base64,/, '')
170+
const dataBuffer = new Buffer(base64Data, 'base64')
171+
outputFile.write(dataBuffer, () => {
172+
resolve(destinationName)
173+
})
174+
} else {
175+
const inputFileStream = fs.createReadStream(sourceFilePath)
176+
inputFileStream.pipe(outputFile)
177+
inputFileStream.on('end', () => {
178+
resolve(destinationName)
179+
})
180+
}
61181
} catch (e) {
62182
return reject(e)
63183
}
@@ -137,10 +257,17 @@ function handleAttachmentDrop (codeEditor, storageKey, noteKey, dropEvent) {
137257
const filePath = file.path
138258
const originalFileName = path.basename(filePath)
139259
const fileType = file['type']
140-
141-
copyAttachment(filePath, storageKey, noteKey).then((fileName) => {
142-
const showPreview = fileType.startsWith('image')
143-
const imageMd = generateAttachmentMarkdown(originalFileName, path.join(STORAGE_FOLDER_PLACEHOLDER, noteKey, fileName), showPreview)
260+
const isImage = fileType.startsWith('image')
261+
let promise
262+
if (isImage) {
263+
promise = fixRotate(file).then(base64data => {
264+
return copyAttachment({type: 'base64', data: base64data, sourceFilePath: filePath}, storageKey, noteKey)
265+
})
266+
} else {
267+
promise = copyAttachment(filePath, storageKey, noteKey)
268+
}
269+
promise.then((fileName) => {
270+
const imageMd = generateAttachmentMarkdown(originalFileName, path.join(STORAGE_FOLDER_PLACEHOLDER, noteKey, fileName), isImage)
144271
codeEditor.insertAttachmentMd(imageMd)
145272
})
146273
}

0 commit comments

Comments
 (0)