From 6dffcd3bf278349c9a50fcf1a8f4544f96cb997e Mon Sep 17 00:00:00 2001 From: Peter Marshall Date: Fri, 17 Nov 2023 00:40:04 +0000 Subject: [PATCH] TS-ify exif_parser and test Bug: b/289003444 Change-Id: I045c9a8516d41b2c3b1cf8725dbf277ae6af88a2 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5035499 Reviewed-by: Luciano Pacheco Commit-Queue: Peter Marshall Cr-Commit-Position: refs/heads/main@{#1225836} --- .../foreground/js/metadata/byte_reader.ts | 2 +- .../{exif_parser.js => exif_parser.ts} | 284 +++++++----------- ...er_unittest.js => exif_parser_unittest.ts} | 154 +++++----- .../foreground/js/metadata/metadata_item.ts | 13 +- ui/file_manager/file_names.gni | 4 +- 5 files changed, 201 insertions(+), 256 deletions(-) rename ui/file_manager/file_manager/foreground/js/metadata/{exif_parser.js => exif_parser.ts} (53%) rename ui/file_manager/file_manager/foreground/js/metadata/{exif_parser_unittest.js => exif_parser_unittest.ts} (55%) diff --git a/ui/file_manager/file_manager/foreground/js/metadata/byte_reader.ts b/ui/file_manager/file_manager/foreground/js/metadata/byte_reader.ts index c4f878b7fc33ff..653f8c76bb70bc 100644 --- a/ui/file_manager/file_manager/foreground/js/metadata/byte_reader.ts +++ b/ui/file_manager/file_manager/foreground/js/metadata/byte_reader.ts @@ -399,7 +399,7 @@ export class ByteReader { * @param end Maximum position to read from. * @return Image as a data url. */ - readImage(size: number, end: number|undefined): string { + readImage(size: number, end?: number): string { const rv = ByteReader.readImage(this.view_, this.pos_, size, end); this.pos_ += size; return rv; diff --git a/ui/file_manager/file_manager/foreground/js/metadata/exif_parser.js b/ui/file_manager/file_manager/foreground/js/metadata/exif_parser.ts similarity index 53% rename from ui/file_manager/file_manager/foreground/js/metadata/exif_parser.js rename to ui/file_manager/file_manager/foreground/js/metadata/exif_parser.ts index c0246c9e738ecd..df7241f53173aa 100644 --- a/ui/file_manager/file_manager/foreground/js/metadata/exif_parser.js +++ b/ui/file_manager/file_manager/foreground/js/metadata/exif_parser.ts @@ -7,39 +7,50 @@ import {MetadataParserLogger} from '../../../externs/metadata_worker_window.js'; import {ByteOrder, ByteReader, SeekOrigin} from './byte_reader.js'; import {ExifAlign, ExifMark, ExifTag} from './exif_constants.js'; +import type {ImageTransformation, ParserMetadata} from './metadata_item.js'; import {ImageParser} from './metadata_parser.js'; /** @final */ export class ExifParser extends ImageParser { /** - * @param {!MetadataParserLogger} parent Parent object. + * @param parent Parent object. */ - constructor(parent) { + constructor(parent: MetadataParserLogger) { super(parent, 'jpeg', /\.jpe?g$/i); } /** - * @param {File} file File object to parse. - * @param {!Object} metadata Metadata object for the file. - * @param {function(!Object):void} callback Callback to be called on success. - * @param {function((Event|string)):void} errorCallback Error callback. + * @param file File object to parse. + * @param metadata Metadata object for the file. + * @param callback Callback to be called on success. + * @param errorCallback Error callback. */ - parse(file, metadata, callback, errorCallback) { + parse( + file: File, + metadata: ParserMetadata, + callback: (metadata: ParserMetadata) => void, + errorCallback: (error: Event | string) => void) { this.requestSlice(file, callback, errorCallback, metadata, 0); } /** - * @param {File} file File object to parse. - * @param {function(!Object):void} callback Callback to be called on success. - * @param {function((Event|string)):void} errorCallback Error callback. - * @param {!Object} metadata Metadata object. - * @param {number} filePos Position to slice at. - * @param {number=} opt_length Number of bytes to slice. By default 1 KB. + * @param file File object to parse. + * @param callback Callback to be called on success. + * @param errorCallback Error callback. + * @param metadata Metadata object. + * @param filePos Position to slice at. + * @param length Number of bytes to slice. By default 1 KB. */ - requestSlice(file, callback, errorCallback, metadata, filePos, opt_length) { + requestSlice( + file: File, + callback: (metadata: ParserMetadata) => void, + errorCallback: (error: Event | string) => void, + metadata: ParserMetadata, + filePos: number, + length?: number) { // Read at least 1Kb so that we do not issue too many read requests. - opt_length = Math.max(1024, opt_length || 0); + length = Math.max(1024, length || 0); const self = this; const reader = new FileReader(); @@ -47,20 +58,26 @@ export class ExifParser extends ImageParser { reader.onload = () => { self.parseSlice( file, callback, errorCallback, metadata, filePos, - /** @type{ArrayBuffer} */ (reader.result)); + reader.result as ArrayBuffer); }; - reader.readAsArrayBuffer(file.slice(filePos, filePos + opt_length)); + reader.readAsArrayBuffer(file.slice(filePos, filePos + length)); } /** - * @param {File} file File object to parse. - * @param {function(!Object):void} callback Callback to be called on success. - * @param {function((Event|string)):void} errorCallback Error callback. - * @param {!Object} metadata Metadata object. - * @param {number} filePos Position to slice at. - * @param {ArrayBuffer} buf Buffer to be parsed. + * @param file File object to parse. + * @param callback Callback to be called on success. + * @param errorCallback Error callback. + * @param metadata Metadata object. + * @param filePos Position to slice at. + * @param buf Buffer to be parsed. */ - parseSlice(file, callback, errorCallback, metadata, filePos, buf) { + parseSlice( + file: File, + callback: (metadata: ParserMetadata) => void, + errorCallback: (error: Event | string) => void, + metadata: ParserMetadata, + filePos: number, + buf: ArrayBuffer) { try { const br = new ByteReader(buf); @@ -81,13 +98,11 @@ export class ExifParser extends ImageParser { const self = this; /** - * @param {number=} opt_offset - * @param {number=} opt_bytes */ - const reread = (opt_offset, opt_bytes) => { + const reread = (offset?: number, bytes?: number) => { self.requestSlice( file, callback, errorCallback, metadata, - filePos + br.tell() + (opt_offset || 0), opt_bytes); + filePos + br.tell() + (offset || 0), bytes); }; while (true) { @@ -117,7 +132,7 @@ export class ExifParser extends ImageParser { if (mark === ExifMark.EXIF) { this.parseExifSection(metadata, buf, br); - } else if (ExifParser.isSOF_(mark)) { + } else if (ExifParser.isSof_(mark)) { // The most reliable size information is encoded in the SOF section. br.seek(1, SeekOrigin.SEEK_CUR); // Skip the precision byte. const height = br.readScalar(2); @@ -130,17 +145,15 @@ export class ExifParser extends ImageParser { br.seek(nextSectionStart, SeekOrigin.SEEK_BEG); } } catch (e) { - // @ts-ignore: error TS18046: 'e' is of type 'unknown'. - errorCallback(e.toString()); + errorCallback(e!.toString()); } } /** - * @private - * @param {number} mark Mark to be checked. - * @return {boolean} True if the mark is SOF. + * @param mark Mark to be checked. + * @return True if the mark is SOF (Start of Frame). */ - static isSOF_(mark) { + private static isSof_(mark: number): boolean { // There are 13 variants of SOF fragment format distinguished by the last // hex digit of the mark, but the part we want is always the same. if ((mark & ~0xF) !== ExifMark.SOF) { @@ -153,11 +166,11 @@ export class ExifParser extends ImageParser { } /** - * @param {Object} metadata Metadata object. - * @param {ArrayBuffer} buf Buffer to be parsed. - * @param {ByteReader} br Byte reader to be used. + * @param metadata Metadata object. + * @param buf Buffer to be parsed. + * @param br Byte reader to be used. */ - parseExifSection(metadata, buf, br) { + parseExifSection(metadata: ParserMetadata, buf: ArrayBuffer, br: ByteReader) { const magic = br.readString(6); if (magic !== 'Exif\0\0') { // Some JPEG files may have sections marked with EXIF_MARK_EXIF @@ -185,91 +198,55 @@ export class ExifParser extends ImageParser { return; } - // @ts-ignore: error TS2339: Property 'littleEndian' does not exist on type - // 'Object'. metadata.littleEndian = (order === ExifAlign.LITTLE); - // @ts-ignore: error TS2339: Property 'ifd' does not exist on type 'Object'. metadata.ifd = { - image: {}, - thumbnail: {}, + image: {} as Record, + thumbnail: {} as Record, }; let directoryOffset = br.readScalar(4); // Image directory. this.vlog('Read image directory'); br.seek(directoryOffset); - // @ts-ignore: error TS2339: Property 'ifd' does not exist on type 'Object'. - directoryOffset = this.readDirectory(br, metadata.ifd.image); - // @ts-ignore: error TS2339: Property 'ifd' does not exist on type 'Object'. - metadata.imageTransform = this.parseOrientation(metadata.ifd.image); + directoryOffset = this.readDirectory(br, metadata.ifd.image!); + metadata.imageTransform = this.parseOrientation(metadata.ifd.image!); // Thumbnail Directory chained from the end of the image directory. if (directoryOffset) { this.vlog('Read thumbnail directory'); br.seek(directoryOffset); - // @ts-ignore: error TS2339: Property 'ifd' does not exist on type - // 'Object'. - this.readDirectory(br, metadata.ifd.thumbnail); + this.readDirectory(br, metadata.ifd.thumbnail!); // If no thumbnail orientation is encoded, assume same orientation as // the primary image. - // @ts-ignore: error TS2339: Property 'thumbnailTransform' does not exist - // on type 'Object'. metadata.thumbnailTransform = - // @ts-ignore: error TS2339: Property 'ifd' does not exist on type - // 'Object'. - this.parseOrientation(metadata.ifd.thumbnail) || - // @ts-ignore: error TS2339: Property 'imageTransform' does not exist - // on type 'Object'. + this.parseOrientation(metadata.ifd.thumbnail!) || metadata.imageTransform; } // EXIF Directory may be specified as a tag in the image directory. - // @ts-ignore: error TS2339: Property 'ifd' does not exist on type 'Object'. - if (ExifTag.EXIFDATA in metadata.ifd.image) { + if (ExifTag.EXIFDATA in metadata.ifd.image!) { this.vlog('Read EXIF directory'); - // @ts-ignore: error TS2339: Property 'ifd' does not exist on type - // 'Object'. directoryOffset = metadata.ifd.image[ExifTag.EXIFDATA].value; br.seek(directoryOffset); - // @ts-ignore: error TS2339: Property 'ifd' does not exist on type - // 'Object'. - metadata.ifd.exif = {}; - // @ts-ignore: error TS2339: Property 'ifd' does not exist on type - // 'Object'. + metadata.ifd.exif = {} as Record; this.readDirectory(br, metadata.ifd.exif); } // GPS Directory may also be linked from the image directory. - // @ts-ignore: error TS2339: Property 'ifd' does not exist on type 'Object'. - if (ExifTag.GPSDATA in metadata.ifd.image) { + if (ExifTag.GPSDATA in metadata.ifd.image!) { this.vlog('Read GPS directory'); - // @ts-ignore: error TS2339: Property 'ifd' does not exist on type - // 'Object'. directoryOffset = metadata.ifd.image[ExifTag.GPSDATA].value; br.seek(directoryOffset); - // @ts-ignore: error TS2339: Property 'ifd' does not exist on type - // 'Object'. - metadata.ifd.gps = {}; - // @ts-ignore: error TS2339: Property 'ifd' does not exist on type - // 'Object'. + metadata.ifd.gps = {} as Record; this.readDirectory(br, metadata.ifd.gps); } // Thumbnail may be linked from the image directory. - // @ts-ignore: error TS2339: Property 'ifd' does not exist on type 'Object'. - if (ExifTag.JPG_THUMB_OFFSET in metadata.ifd.thumbnail && - // @ts-ignore: error TS2339: Property 'ifd' does not exist on type - // 'Object'. + if (ExifTag.JPG_THUMB_OFFSET in metadata.ifd.thumbnail! && ExifTag.JPG_THUMB_LENGTH in metadata.ifd.thumbnail) { this.vlog('Read thumbnail image'); - // @ts-ignore: error TS2339: Property 'ifd' does not exist on type - // 'Object'. br.seek(metadata.ifd.thumbnail[ExifTag.JPG_THUMB_OFFSET].value); - // @ts-ignore: error TS2339: Property 'thumbnailURL' does not exist on - // type 'Object'. metadata.thumbnailURL = - // @ts-ignore: error TS2339: Property 'ifd' does not exist on type - // 'Object'. br.readImage(metadata.ifd.thumbnail[ExifTag.JPG_THUMB_LENGTH].value); } else { this.vlog('Image has EXIF data, but no JPG thumbnail'); @@ -277,66 +254,50 @@ export class ExifParser extends ImageParser { } /** - * @param {Object} metadata Metadata object. - * @param {number} width Width in pixels. - * @param {number} height Height in pixels. + * @param metadata Metadata object. + * @param width Width in pixels. + * @param height Height in pixels. */ - static setImageSize(metadata, width, height) { - // @ts-ignore: error TS2339: Property 'imageTransform' does not exist on - // type 'Object'. + static setImageSize(metadata: ParserMetadata, width: number, height: number) { if (metadata.imageTransform && metadata.imageTransform.rotate90) { - // @ts-ignore: error TS2339: Property 'width' does not exist on type - // 'Object'. metadata.width = height; - // @ts-ignore: error TS2339: Property 'height' does not exist on type - // 'Object'. metadata.height = width; } else { - // @ts-ignore: error TS2339: Property 'width' does not exist on type - // 'Object'. metadata.width = width; - // @ts-ignore: error TS2339: Property 'height' does not exist on type - // 'Object'. metadata.height = height; } } /** - * @param {ByteReader} br Byte reader to be used for reading. - * @return {number} Mark value. + * @param br Byte reader to be used for reading. + * @return Mark value. */ - readMark(br) { + readMark(br: ByteReader): number { return br.readScalar(2); } /** - * @param {ByteReader} br Bye reader to be used for reading. - * @return {number} Size of the mark at the current position. + * @param br Bye reader to be used for reading. + * @return Size of the mark at the current position. */ - readMarkLength(br) { + readMarkLength(br: ByteReader): number { // Length includes the 2 bytes used to store the length. return br.readScalar(2) - 2; } /** - * @param {ByteReader} br Byte reader to be used for reading. - * @param {Object} tags Map of tags to be written to. - * @return {number} Directory offset. + * @param br Byte reader to be used for reading. + * @param tags Map of tags to be written to. + * @return Directory offset. */ - readDirectory(br, tags) { + readDirectory(br: ByteReader, tags: Record): number { const entryCount = br.readScalar(2); for (let i = 0; i < entryCount; i++) { - // @ts-ignore: error TS2315: Type 'Tag' is not generic. - const tagId = /** @type {!ExifTag} */ (br.readScalar(2)); - const tag = tags[tagId] = {id: tagId}; - // @ts-ignore: error TS2339: Property 'format' does not exist on type '{ - // id: any; }'. + const tagId = br.readScalar(2) as ExifTag; + const tag: ExifEntry = tags[tagId] = + {id: tagId, format: 0, componentCount: 0, value: undefined}; tag.format = br.readScalar(2); - // @ts-ignore: error TS2339: Property 'componentCount' does not exist on - // type '{ id: any; }'. tag.componentCount = br.readScalar(4); - // @ts-ignore: error TS2345: Argument of type '{ id: any; }' is not - // assignable to parameter of type 'ExifEntry'. this.readTagValue(br, tag); } @@ -344,45 +305,37 @@ export class ExifParser extends ImageParser { } /** - * @param {ByteReader} br Byte reader to be used for reading. - * @param {ExifEntry} tag Tag object. + * @param br Byte reader to be used for reading. + * @param tag Tag object. */ - readTagValue(br, tag) { + readTagValue(br: ByteReader, tag: ExifEntry) { const self = this; - /** - * @param {number} size - * @param {function(number)=} opt_readFunction - * @param {boolean=} opt_signed - */ - function safeRead(size, opt_readFunction, opt_signed) { + function safeRead( + size: (1|2|4|8), + readFunction?: () => [number, number], + signed?: boolean) { try { - unsafeRead(size, opt_readFunction, opt_signed); + unsafeRead(size, readFunction, signed); } catch (ex) { self.log( 'Error reading tag 0x' + tag.id.toString(16) + '/' + tag.format + ', size ' + tag.componentCount + '*' + size + ' ' + - // @ts-ignore: error TS18046: 'ex' is of type 'unknown'. - (ex.stack || '') + ': ' + ex); + ((ex as {stack: string}).stack || '') + ': ' + ex); tag.value = null; } } - /** - * @param {number} size - * @param {function(number)=} opt_readFunction - * @param {boolean=} opt_signed - */ - function unsafeRead(size, opt_readFunction, opt_signed) { - const readFunction = opt_readFunction || (size => { + function unsafeRead( + size: (1|2|4|8), + readFunction?: () => [number, number], + signed?: boolean) { + const reader = readFunction || ((size: (1|2|4)) => { // Every time this function is called with `size` = - // 8, `opt_readFunction` is also passed, so + // 8, `readFunction` is also passed, so // readScalar is only ever called with `size` = 1,2 // or 4. - // @ts-ignore: error TS2345: Argument of type - // 'number' is not assignable to parameter of type - // '2 | 1 | 4' - return br.readScalar(size, opt_signed); + return br.readScalar(size, signed); }); const totalSize = tag.componentCount * size; @@ -400,12 +353,12 @@ export class ExifParser extends ImageParser { } if (tag.componentCount === 1) { - tag.value = readFunction(size); + tag.value = reader(size as (1|2|4)); } else { // Read multiple components into an array. tag.value = []; for (let i = 0; i < tag.componentCount; i++) { - tag.value[i] = readFunction(size); + tag.value[i] = reader(size as (1|2|4)); } } @@ -430,10 +383,9 @@ export class ExifParser extends ImageParser { if (tag.componentCount === 0) { tag.value = ''; } else if (tag.componentCount === 1) { - tag.value = String.fromCharCode(/** @type {number} */ (tag.value)); + tag.value = String.fromCharCode(tag.value); } else { - tag.value = String.fromCharCode.apply( - null, /** @type{Array} */ (tag.value)); + tag.value = String.fromCharCode.apply(null, tag.value); } this.validateAndFixStringTag_(tag); break; @@ -477,10 +429,9 @@ export class ExifParser extends ImageParser { /** * Validates string tag value, and fix it if necessary. - * @param {!ExifEntry} tag A tag to be validated and fixed. - * @private + * @param tag A tag to be validated and fixed. */ - validateAndFixStringTag_(tag) { + private validateAndFixStringTag_(tag: ExifEntry) { if (tag.format === 2) { // string // String should end with null character. if (tag.value.charAt(tag.value.length - 1) !== '\0') { @@ -497,41 +448,34 @@ export class ExifParser extends ImageParser { * Transform exif-encoded orientation into a set of parameters compatible with * CSS and canvas transforms (scaleX, scaleY, rotation). * - * @param {Object} ifd Exif property dictionary (image or thumbnail). - * @return {Object} Orientation object. + * @param ifd Exif property dictionary (image or thumbnail). + * @return Orientation object. */ - parseOrientation(ifd) { - // @ts-ignore: error TS7053: Element implicitly has an 'any' type because - // expression of type 'number' can't be used to index type 'Object'. + parseOrientation(ifd: Record) + : ImageTransformation|undefined { if (ifd[ExifTag.ORIENTATION]) { - // @ts-ignore: error TS7053: Element implicitly has an 'any' type because - // expression of type 'number' can't be used to index type 'Object'. const index = (ifd[ExifTag.ORIENTATION].value || 1) - 1; return { - scaleX: ExifParser.SCALEX[index], - scaleY: ExifParser.SCALEY[index], - rotate90: ExifParser.ROTATE90[index], + scaleX: SCALEX[index]!, + scaleY: SCALEY[index]!, + rotate90: ROTATE90[index]!, }; } - // @ts-ignore: error TS2322: Type 'null' is not assignable to type 'Object'. - return null; + return undefined; } } /** * Map from the exif orientation value to the horizontal scale value. - * @const @type {Array} */ -ExifParser.SCALEX = [1, -1, -1, 1, 1, 1, -1, -1]; +const SCALEX = [1, -1, -1, 1, 1, 1, -1, -1]; /** * Map from the exif orientation value to the vertical scale value. - * @const @type {Array} */ -ExifParser.SCALEY = [1, 1, -1, -1, -1, 1, 1, -1]; +const SCALEY = [1, 1, -1, -1, -1, 1, 1, -1]; /** - * Map from the exit orientation value to the rotation value. - * @const @type {Array} + * Map from the exif orientation value to the rotation value. */ -ExifParser.ROTATE90 = [0, 0, 0, 0, 1, 1, 1, 1]; +const ROTATE90 = [0, 0, 0, 0, 1, 1, 1, 1]; diff --git a/ui/file_manager/file_manager/foreground/js/metadata/exif_parser_unittest.js b/ui/file_manager/file_manager/foreground/js/metadata/exif_parser_unittest.ts similarity index 55% rename from ui/file_manager/file_manager/foreground/js/metadata/exif_parser_unittest.js rename to ui/file_manager/file_manager/foreground/js/metadata/exif_parser_unittest.ts index 597be89c47b884..40b33fa2c1c9b4 100644 --- a/ui/file_manager/file_manager/foreground/js/metadata/exif_parser_unittest.js +++ b/ui/file_manager/file_manager/foreground/js/metadata/exif_parser_unittest.ts @@ -4,33 +4,34 @@ import {assertEquals, assertTrue} from 'chrome://webui-test/chromeos/chai_assert.js'; -import {ExifEntry} from '../../../externs/exif_entry.js'; import {MetadataParserLogger} from '../../../externs/metadata_worker_window.js'; - +import {ExifEntry} from '../../../externs/exif_entry.js'; import {ByteOrder, ByteReader} from './byte_reader.js'; import {ExifTag} from './exif_constants.js'; import {ExifParser} from './exif_parser.js'; class ByteWriter { + private view_: DataView; + private littleEndian_ = false; + private pos_ = 0; + private forwards_ = {} as Record; + /** - * @param {!ArrayBuffer} arrayBuffer Underlying buffer to use. - * @param {number} offset Offset at which to start writing. - * @param {number=} opt_length Maximum length to use. + * @param arrayBuffer Underlying buffer to use. + * @param offset Offset at which to start writing. + * @param length Maximum length to use. */ - constructor(arrayBuffer, offset, opt_length) { - const length = opt_length || (arrayBuffer.byteLength - offset); - this.view_ = new DataView(arrayBuffer, offset, length); - this.littleEndian_ = false; - this.pos_ = 0; - this.forwards_ = {}; + constructor(arrayBuffer: ArrayBuffer, offset: number, length?: number) { + const calculatedLength = length || (arrayBuffer.byteLength - offset); + this.view_ = new DataView(arrayBuffer, offset, calculatedLength); } /** * If key is a number, format it in hex style. - * @param {!(string|ExifTag)} key A key. - * @return {string} Formatted representation. + * @param key A key. + * @return Formatted representation. */ - static prettyKeyFormat(key) { + static prettyKeyFormat(key: (string|ExifTag)): string { if (typeof key === 'number') { return '0x' + key.toString(16); } else { @@ -40,25 +41,25 @@ class ByteWriter { /** * Set the byte ordering for future writes. - * @param {ByteOrder} order ByteOrder to use + * @param order ByteOrder to use * {ByteOrder.LITTLE_ENDIAN} or {ByteOrder.BIG_ENDIAN}. */ - setByteOrder(order) { + setByteOrder(order: ByteOrder) { this.littleEndian_ = (order === ByteOrder.LITTLE_ENDIAN); } /** - * @return {number} the current write position. + * @return the current write position. */ - tell() { + tell(): number { return this.pos_; } /** * Skips desired amount of bytes in output stream. - * @param {number} count Byte count to skip. + * @param count Byte count to skip. */ - skip(count) { + skip(count: number) { this.validateWrite(count); this.pos_ += count; } @@ -66,9 +67,9 @@ class ByteWriter { /** * Check if the buffer has enough room to read 'width' bytes. Throws an error * if it has not. - * @param {number} width Amount of bytes to check. + * @param width Amount of bytes to check. */ - validateWrite(width) { + validateWrite(width: number) { if (this.pos_ + width > this.view_.byteLength) { throw new Error('Writing past the end of the buffer'); } @@ -76,48 +77,50 @@ class ByteWriter { /** * Writes scalar value to output stream. - * @param {number} value Value to write. - * @param {number} width Desired width of written value. - * @param {boolean=} opt_signed True if value represents signed number. + * @param value Value to write. + * @param width Desired width of written value. + * @param signed True if value represents signed number. */ - writeScalar(value, width, opt_signed) { - let method; - // The below switch is so verbose for two reasons: - // 1. V8 is faster on method names which are 'symbols'. - // 2. Method names are discoverable by full text search. + writeScalar(value: number, width: number, signed?: boolean) { + this.validateWrite(width); + switch (width) { case 1: - method = opt_signed ? 'setInt8' : 'setUint8'; + if (signed) { + this.view_.setInt8(this.pos_, value); + } else { + this.view_.setUint8(this.pos_, value); + } break; case 2: - method = opt_signed ? 'setInt16' : 'setUint16'; + if (signed) { + this.view_.setInt16(this.pos_, value, this.littleEndian_); + } else { + this.view_.setUint16(this.pos_, value, this.littleEndian_); + } break; case 4: - method = opt_signed ? 'setInt32' : 'setUint32'; - break; - - case 8: - method = opt_signed ? 'setInt64' : 'setUint64'; + if (signed) { + this.view_.setInt32(this.pos_, value, this.littleEndian_); + } else { + this.view_.setUint32(this.pos_, value, this.littleEndian_); + } break; default: throw new Error('Invalid width: ' + width); } - this.validateWrite(width); - // @ts-ignore: error TS7053: Element implicitly has an 'any' type because - // expression of type 'string' can't be used to index type 'DataView'. - this.view_[method](this.pos_, value, this.littleEndian_); this.pos_ += width; } /** * Writes string. - * @param {string} str String to write. + * @param str String to write. */ - writeString(str) { + writeString(str: string) { this.validateWrite(str.length); for (let i = 0; i != str.length; i++) { this.view_.setUint8(this.pos_++, str.charCodeAt(i)); @@ -127,16 +130,14 @@ class ByteWriter { /** * Allocate the space for 'width' bytes for the value that will be set later. * To be followed by a 'resolve' call with the same key. - * @param {(string|ExifTag)} key A key to identify the value. - * @param {number} width Width of the value in bytes. + * @param key A key to identify the value. + * @param width Width of the value in bytes. */ - forward(key, width) { + forward(key: ExifTag, width: number) { if (key in this.forwards_) { throw new Error('Duplicate forward key ' + key); } this.validateWrite(width); - // @ts-ignore: error TS7053: Element implicitly has an 'any' type because - // expression of type 'string | number' can't be used to index type '{}'. this.forwards_[key] = { pos: this.pos_, width: width, @@ -146,30 +147,26 @@ class ByteWriter { /** * Set the value previously allocated with a 'forward' call. - * @param {(string|ExifTag)} key A key to identify the value. - * @param {number} value value to write in pre-allocated space. + * @param key A key to identify the value. + * @param value value to write in pre-allocated space. */ - resolve(key, value) { + resolve(key: ExifTag, value: number) { if (!(key in this.forwards_)) { throw new Error('Undeclared forward key ' + key.toString(16)); } - // @ts-ignore: error TS7053: Element implicitly has an 'any' type because - // expression of type 'string | number' can't be used to index type '{}'. const forward = this.forwards_[key]; const curPos = this.pos_; this.pos_ = forward.pos; this.writeScalar(value, forward.width); this.pos_ = curPos; - // @ts-ignore: error TS7053: Element implicitly has an 'any' type because - // expression of type 'string | number' can't be used to index type '{}'. delete this.forwards_[key]; } /** * A shortcut to resolve the value to the current write position. - * @param {(string|ExifTag)} key A key to identify pre-allocated position. + * @param key A key to identify pre-allocated position. */ - resolveOffset(key) { + resolveOffset(key: ExifTag) { this.resolve(key, this.tell()); } @@ -187,10 +184,10 @@ class ByteWriter { /** * Creates a directory with specified tag. This method only supports string * format tag, which is longer than 4 characters. - * @param {!ArrayBufferView} bytes Bytes to be written. - * @param {!ExifEntry} tag An exif entry which will be written. + * @param bytes Bytes to be written. + * @param tag An exif entry which will be written. */ -function writeDirectory_(bytes, tag) { +function writeDirectory(bytes: ArrayBufferView, tag: ExifEntry) { assertEquals(2, tag.format); assertTrue(tag.componentCount > 4); @@ -205,49 +202,44 @@ function writeDirectory_(bytes, tag) { byteWriter.writeScalar(0, 4); // Offset to next IFD. byteWriter.resolveOffset(tag.id); - const string = /** @type {string} */ (tag.value); + const string = tag.value; byteWriter.writeString(string); byteWriter.checkResolved(); } /** - * @implements {MetadataParserLogger} * @final */ -class ConsoleLogger { +class ConsoleLogger extends MetadataParserLogger{ constructor() { + super(); this.verbose = true; } - // @ts-ignore: error TS7006: Parameter 'arg' implicitly has an 'any' type. - error(arg) { - console.error(arg); + override error(...args: unknown[]) { + console.error(...args); } - // @ts-ignore: error TS7006: Parameter 'arg' implicitly has an 'any' type. - log(arg) { - console.log(arg); + override log(...args: unknown[]) { + console.log(...args); } - // @ts-ignore: error TS7006: Parameter 'arg' implicitly has an 'any' type. - vlog(arg) { - console.log(arg); + override vlog(...args: unknown[]) { + console.log(...args); } } /** * Parses exif data bytes (with logging) and returns the parsed tags. - * @param {!ArrayBufferView} bytes Bytes to be read. - * @return {!Object} Tags. + * @param bytes Bytes to be read. + * @return Tags. */ -function parseExifData_(bytes) { +function parseExifData(bytes: ArrayBufferView): Record { const exifParser = new ExifParser(new ConsoleLogger()); - const tags = {}; + const tags = {} as Record; const byteReader = new ByteReader(bytes.buffer); - // @ts-ignore: error TS2345: Argument of type '{}' is not assignable to - // parameter of type '{ [x: number]: Object; }'. assertEquals(0, exifParser.readDirectory(byteReader, tags)); return tags; } @@ -258,15 +250,15 @@ function parseExifData_(bytes) { export function testWithoutNullCharacterTermination() { // Create exif with a value that does not end with null character. const data = new Uint8Array(0x10000); - writeDirectory_(data, /** @type {!ExifEntry} */ ({ + writeDirectory(data,{ id: ExifTag.MAKE, // Manufacturer Id. format: 2, // String format. componentCount: 8, // Length of value 'Manufact'. value: 'Manufact', - })); + }); // Parse the exif data. - const tags = parseExifData_(data); + const tags = parseExifData(data); // The parsed value should end in a null character. const parsedTag = tags[ExifTag.MAKE]; diff --git a/ui/file_manager/file_manager/foreground/js/metadata/metadata_item.ts b/ui/file_manager/file_manager/foreground/js/metadata/metadata_item.ts index 7d22a1f59eb771..ec4a218e056613 100644 --- a/ui/file_manager/file_manager/foreground/js/metadata/metadata_item.ts +++ b/ui/file_manager/file_manager/foreground/js/metadata/metadata_item.ts @@ -2,7 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -interface ImageTransformation { +import type {ExifEntry} from '../../../externs/exif_entry.js'; + +import type {ExifTag} from './exif_constants.js'; + +export interface ImageTransformation { scaleX: number; scaleY: number; rotate90: number; @@ -217,7 +221,12 @@ export class ParserMetadata { thumbnailTransform?: ImageTransformation; thumbnailURL?: string; littleEndian?: boolean; - ifd?: object; + ifd?: { + image?: Record, + thumbnail?: Record, + exif?: Record, + gps?: Record, + }; height?: number; width?: number; mimeType?: string; diff --git a/ui/file_manager/file_names.gni b/ui/file_manager/file_names.gni index 6db791ea3d48eb..60a0af8abc2ded 100644 --- a/ui/file_manager/file_names.gni +++ b/ui/file_manager/file_names.gni @@ -110,7 +110,6 @@ static_js_files = [ # Metadata: "file_manager/foreground/js/metadata/dlp_metadata_provider.js", - "file_manager/foreground/js/metadata/exif_parser.js", "file_manager/foreground/js/metadata/external_metadata_provider.js", "file_manager/foreground/js/metadata/function_parallel.js", "file_manager/foreground/js/metadata/function_sequence.js", @@ -366,6 +365,7 @@ ts_files = [ "file_manager/foreground/js/metadata/byte_reader.ts", "file_manager/foreground/js/metadata/content_metadata_provider.ts", "file_manager/foreground/js/metadata/exif_constants.ts", + "file_manager/foreground/js/metadata/exif_parser.ts", "file_manager/foreground/js/metadata/file_system_metadata_provider.ts", "file_manager/foreground/js/metadata/metadata_item.ts", "file_manager/foreground/js/metadata/metadata_model.ts", @@ -536,6 +536,7 @@ ts_test_files = [ # These lines will be removed at the end of the TS migration. # Metadata: "file_manager/foreground/js/metadata/content_metadata_provider_unittest.ts", + "file_manager/foreground/js/metadata/exif_parser_unittest.ts", "file_manager/foreground/js/metadata/file_system_metadata_provider_unittest.ts", ] @@ -616,7 +617,6 @@ unittest_files = [ # Foreground: "file_manager/foreground/elements/files_xf_elements_unittest.js", "file_manager/foreground/js/metadata/metadata_cache_item_unittest.js", - "file_manager/foreground/js/metadata/exif_parser_unittest.js", "file_manager/foreground/js/metadata/thumbnail_model_unittest.js", "file_manager/foreground/js/metadata/metadata_model_unittest.js", "file_manager/foreground/js/metadata/external_metadata_provider_unittest.js",