Skip to content

Commit f858f42

Browse files
committed
fix avatar-control for unfortunately rotation, file block empty while editing achievement in profile & add mock level of education
1 parent 24b460f commit f858f42

6 files changed

Lines changed: 174 additions & 5 deletions

File tree

projects/core/src/consts/lists/education-info-list.const.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,9 @@ export const educationUserLevel = [
4444
value: "Высшее образование – аспирантура",
4545
label: "высшее образование – аспирантура",
4646
},
47+
// {
48+
// id: 5,
49+
// value: "Дополнительное профессиональное образования",
50+
// label: "дополнительное профессиональное образования"
51+
// },
4752
];

projects/social_platform/src/app/office/profile/edit/edit.component.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -639,7 +639,6 @@ export class ProfileEditComponent implements OnInit, OnDestroy, AfterViewInit {
639639
title: achievementItem.title,
640640
status: achievementItem.status,
641641
year: achievementItem.year,
642-
files: achievementItem.files,
643642
});
644643
this.editIndex.set(index);
645644
}

projects/social_platform/src/app/office/program/services/program.service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ export class ProgramService {
128128
url += `?${params.toString()}`;
129129
}
130130

131-
return this.apiService.post(url, { filters: filters });
131+
return this.apiService.post(url, { filters });
132132
}
133133

134134
submitCompettetiveProject(relationId: number): Observable<Project> {

projects/social_platform/src/app/office/program/services/project-rating.service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export class ProjectRatingService {
5858
url += `?${params.toString()}`;
5959
}
6060

61-
return this.apiService.post(url, { filters: filters });
61+
return this.apiService.post(url, { filters });
6262
}
6363

6464
rate(projectId: number, scores: ProjectRatingCriterionOutput[]): Observable<void> {

projects/social_platform/src/app/ui/components/avatar-control/avatar-control.component.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
<div class="cancel__cropper-container">
6969
<image-cropper
7070
[imageChangedEvent]="imageChangedEvent"
71+
[imageBase64]="correctedImageBase64"
7172
[maintainAspectRatio]="false"
7273
[aspectRatio]="1"
7374
[resizeToWidth]="600"

projects/social_platform/src/app/ui/components/avatar-control/avatar-control.component.ts

Lines changed: 166 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,9 @@ export class AvatarControlComponent implements OnInit, ControlValueAccessor {
118118
/** Состояние загрузки файла */
119119
loading = false;
120120

121+
/** Исправленное изображение в формате base64 для кроппера */
122+
correctedImageBase64 = "";
123+
121124
/**
122125
* Обработчик выбора файла - открывает кроппер
123126
*/
@@ -128,8 +131,169 @@ export class AvatarControlComponent implements OnInit, ControlValueAccessor {
128131
return;
129132
}
130133

131-
this.imageChangedEvent = event;
132-
this.showCropperModal = true;
134+
// Обрабатываем EXIF ориентацию перед открытием кроппера
135+
this.fixImageOrientation(files[0], () => {
136+
// Используем исходное событие, но imageChangedEvent уже содержит исправленное изображение
137+
this.imageChangedEvent = event;
138+
this.showCropperModal = true;
139+
});
140+
}
141+
142+
/**
143+
* Исправляет EXIF ориентацию изображения
144+
* Решает проблему с повернутыми фотографиями со смартфонов
145+
*/
146+
private fixImageOrientation(file: File, onComplete: () => void) {
147+
const reader = new FileReader();
148+
149+
reader.onload = e => {
150+
const img = new Image();
151+
img.onload = () => {
152+
// Читаем EXIF данные для определения ориентации
153+
this.getImageOrientation(file, orientation => {
154+
// Если ориентация нормальная (1), просто используем исходное изображение
155+
if (orientation === 1) {
156+
this.correctedImageBase64 = "";
157+
onComplete();
158+
return;
159+
}
160+
161+
// Ротируем изображение на Canvas
162+
const canvas = this.rotateImage(img, orientation);
163+
this.correctedImageBase64 = canvas.toDataURL(file.type);
164+
onComplete();
165+
});
166+
};
167+
img.src = e.target?.result as string;
168+
};
169+
170+
reader.readAsDataURL(file);
171+
}
172+
173+
/**
174+
* Определяет EXIF ориентацию изображения
175+
*/
176+
private getImageOrientation(file: File, onOrientationDetected: (orientation: number) => void) {
177+
const reader = new FileReader();
178+
179+
reader.onload = event => {
180+
const view = new DataView(event.target?.result as ArrayBuffer);
181+
// Проверяем JPEG маркер
182+
if (view.byteLength < 2 || view.getUint16(0) !== 0xffd8) {
183+
onOrientationDetected(1); // Не JPEG, используем нормальную ориентацию
184+
return;
185+
}
186+
187+
let offset = 2;
188+
// Ищем EXIF данные
189+
while (offset < view.byteLength - 9) {
190+
if (view.getUint16(offset) === 0xffe1) {
191+
const length = view.getUint16(offset + 2) + 2;
192+
// Проверяем EXIF идентификатор
193+
if (view.getUint32(offset + 4) === 0x45786966 && view.getUint16(offset + 8) === 0x0000) {
194+
const orientation = this.getExifOrientation(view, offset + 10);
195+
onOrientationDetected(orientation);
196+
return;
197+
}
198+
offset += length;
199+
} else {
200+
offset += 2;
201+
}
202+
}
203+
onOrientationDetected(1); // EXIF не найден, используем нормальную ориентацию
204+
};
205+
206+
reader.readAsArrayBuffer(file);
207+
}
208+
209+
/**
210+
* Извлекает значение ориентации из EXIF данных
211+
*/
212+
private getExifOrientation(view: DataView, offset: number): number {
213+
try {
214+
const littleEndian = view.getUint16(offset) === 0x4949;
215+
const ifdOffset = view.getUint32(offset + 4, littleEndian);
216+
const entries = view.getUint16(offset + ifdOffset, littleEndian);
217+
218+
for (let i = 0; i < entries; i++) {
219+
const entryOffset = offset + ifdOffset + 2 + i * 12;
220+
const tag = view.getUint16(entryOffset, littleEndian);
221+
// 0x0112 это тег для ориентации (Orientation tag)
222+
if (tag === 0x0112) {
223+
const value = view.getUint32(entryOffset + 8, littleEndian);
224+
return value > 1 && value <= 8 ? value : 1;
225+
}
226+
}
227+
} catch (e) {
228+
console.warn("Ошибка при чтении EXIF ориентации:", e);
229+
}
230+
return 1;
231+
}
232+
233+
/**
234+
* Ротирует изображение на Canvas в зависимости от EXIF ориентации
235+
*/
236+
private rotateImage(img: HTMLImageElement, orientation: number): HTMLCanvasElement {
237+
const canvas = document.createElement("canvas");
238+
const ctx = canvas.getContext("2d");
239+
if (!ctx) {
240+
return canvas;
241+
}
242+
243+
const [newWidth, newHeight] = [img.width, img.height];
244+
245+
switch (orientation) {
246+
case 2:
247+
canvas.width = newWidth;
248+
canvas.height = newHeight;
249+
ctx.scale(-1, 1);
250+
ctx.drawImage(img, -newWidth, 0);
251+
break;
252+
case 3:
253+
canvas.width = newWidth;
254+
canvas.height = newHeight;
255+
ctx.rotate(Math.PI);
256+
ctx.drawImage(img, -newWidth, -newHeight);
257+
break;
258+
case 4:
259+
canvas.width = newWidth;
260+
canvas.height = newHeight;
261+
ctx.scale(1, -1);
262+
ctx.drawImage(img, 0, -newHeight);
263+
break;
264+
case 5:
265+
canvas.width = newHeight;
266+
canvas.height = newWidth;
267+
ctx.rotate(Math.PI / 2);
268+
ctx.scale(-1, 1);
269+
ctx.drawImage(img, -newHeight, 0);
270+
break;
271+
case 6:
272+
canvas.width = newHeight;
273+
canvas.height = newWidth;
274+
ctx.rotate(Math.PI / 2);
275+
ctx.drawImage(img, 0, -newWidth);
276+
break;
277+
case 7:
278+
canvas.width = newHeight;
279+
canvas.height = newWidth;
280+
ctx.rotate(-Math.PI / 2);
281+
ctx.scale(-1, 1);
282+
ctx.drawImage(img, -newHeight, -newWidth);
283+
break;
284+
case 8:
285+
canvas.width = newHeight;
286+
canvas.height = newWidth;
287+
ctx.rotate(-Math.PI / 2);
288+
ctx.drawImage(img, -newHeight, 0);
289+
break;
290+
default:
291+
canvas.width = newWidth;
292+
canvas.height = newHeight;
293+
ctx.drawImage(img, 0, 0);
294+
}
295+
296+
return canvas;
133297
}
134298

135299
/**

0 commit comments

Comments
 (0)