├── .DS_Store ├── .github └── workflows │ ├── dartdoc.yml │ └── test.yml ├── .gitignore ├── .vscode ├── launch.json └── settings.json ├── CODEOWNERS ├── CONTRIBUTING.md ├── Develop.md ├── LICENSE ├── README.md ├── example ├── .gitignore ├── CHANGELOG.md ├── README.md ├── analysis_options.yaml ├── asset │ ├── 87a.gif │ ├── IMG_20180908_080245.jpg │ ├── demo.bmp │ ├── demo.webp │ ├── demo_extended.webp │ ├── demo_lossless.webp │ ├── dialog.gif │ ├── have_orientation_exif_3.jpg │ ├── have_orientation_exif_6.jpg │ ├── ic_launcher.png │ ├── issue27 │ │ └── issue27-1.jpg │ └── test.MP.jpg ├── bin │ └── main.dart ├── melos_example.iml ├── pubspec.yaml ├── pubspec_overrides.yaml └── test │ └── issue_027_test.dart ├── melos.yaml ├── melos_image_size_getter_project_workspaces.iml ├── packages ├── image_size_getter │ ├── .gitignore │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── analysis_options.yaml │ ├── lib │ │ ├── file_input.dart │ │ ├── image_size_getter.dart │ │ └── src │ │ │ ├── core │ │ │ ├── input.dart │ │ │ └── memory_input.dart │ │ │ ├── decoder │ │ │ ├── decoder.dart │ │ │ └── impl │ │ │ │ ├── bmp_decoder.dart │ │ │ │ ├── gif_decoder.dart │ │ │ │ ├── jpeg_decoder.dart │ │ │ │ ├── png_decoder.dart │ │ │ │ └── webp_decoder.dart │ │ │ ├── entity │ │ │ ├── block_entity.dart │ │ │ └── size.dart │ │ │ ├── image_size_getter.dart │ │ │ └── utils │ │ │ └── file_utils.dart │ ├── melos_image_size_getter.iml │ ├── migrate.md │ ├── pubspec.yaml │ └── test │ │ ├── decoder_extension_test.dart │ │ └── image_size_getter_test.dart ├── image_size_getter_heic │ ├── .gitignore │ ├── .vscode │ │ └── launch.json │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── analysis_options.yaml │ ├── example │ │ ├── asset │ │ │ └── example.heic │ │ └── image_size_getter_heic_example.dart │ ├── lib │ │ ├── image_size_getter_heic.dart │ │ └── src │ │ │ ├── context.dart │ │ │ ├── image_size_getter_heic_base.dart │ │ │ └── types.dart │ ├── melos_image_size_getter_heic.iml │ ├── pubspec.yaml │ ├── pubspec_overrides.yaml │ └── test │ │ └── image_size_getter_heic_test.dart └── image_size_getter_http_input │ ├── .gitignore │ ├── .pubignore │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── analysis_options.yaml │ ├── assets │ └── flutter_photo2.png │ ├── lib │ ├── image_size_getter_http_input.dart │ └── src │ │ ├── image_size_getter_http_input_base.dart │ │ ├── io_delegate.dart │ │ ├── stub_impl.dart │ │ └── web_delegate.dart │ ├── melos_image_size_getter_http_input.iml │ ├── pubspec.yaml │ ├── pubspec_overrides.yaml │ └── test │ └── image_size_getter_http_input_test.dart ├── publish_http_input.sh ├── publish_library.sh ├── pubspec.lock ├── pubspec.yaml └── test.sh /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercandies/dart_image_size_getter/b2350448d0bda758bfe2407e05a729f1590bb300/.DS_Store -------------------------------------------------------------------------------- /.github/workflows/dartdoc.yml: -------------------------------------------------------------------------------- 1 | name: dartdoc 2 | on: 3 | push: 4 | branches: 5 | - master 6 | - main 7 | pull_request: 8 | branches: 9 | - master 10 | - main 11 | 12 | jobs: 13 | make-dartdoc-job: 14 | name: Make dartdoc for packages 15 | runs-on: ${{ matrix.os }} 16 | strategy: 17 | matrix: 18 | os: 19 | - ubuntu-latest 20 | # - windows-latest 21 | sdk: 22 | - stable 23 | steps: 24 | - uses: actions/checkout@v4 25 | - uses: dart-lang/setup-dart@v1 26 | with: 27 | sdk: ${{ matrix.sdk }} 28 | - name: Install dependencies 29 | run: dart pub global activate melos 30 | - name: Bootstrap 31 | run: melos bootstrap 32 | - name: Install dartdoc 33 | run: dart pub global activate dartdoc 34 | - name: Run dartdoc 35 | run: melos run dartdoc --no-select 36 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | on: 3 | push: 4 | branches: 5 | - master 6 | - main 7 | pull_request: 8 | branches: 9 | - master 10 | - main 11 | 12 | jobs: 13 | test-job: 14 | runs-on: ${{ matrix.os }} 15 | strategy: 16 | matrix: 17 | os: 18 | - ubuntu-latest 19 | # - windows-latest 20 | sdk: 21 | - stable 22 | steps: 23 | - uses: actions/checkout@v4 24 | - uses: dart-lang/setup-dart@v1 25 | with: 26 | sdk: ${{ matrix.sdk }} 27 | - name: Install dependencies 28 | run: dart pub global activate melos 29 | - name: Bootstrap 30 | run: melos bootstrap 31 | - name: Run test shell 32 | run: ./test.sh 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .dart_tool/ -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // 使用 IntelliSense 了解相关属性。 3 | // 悬停以查看现有属性的描述。 4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "example", 9 | "cwd": "example", 10 | "request": "launch", 11 | "type": "dart" 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "*.m3u8": "m3u", 4 | "*.bt": "c" 5 | } 6 | } -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @CaiJingLong -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Use melos to manage the repository 2 | 3 | ## Install melos 4 | 5 | ```bash 6 | dart pub global activate melos 7 | # or 8 | flutter pub global activate melos 9 | ``` 10 | 11 | ## Initialize melos 12 | 13 | ```bash 14 | melos bootstrap 15 | ``` 16 | 17 | ## Run melos commands 18 | 19 | ```bash 20 | melos run test 21 | ``` 22 | -------------------------------------------------------------------------------- /Develop.md: -------------------------------------------------------------------------------- 1 | # 元数据总结 2 | 3 | - [元数据总结](#元数据总结) 4 | - [关于 Big Endian 和 Little Endian](#关于-big-endian-和-little-endian) 5 | - [JPEG](#jpeg) 6 | - [PNG](#png) 7 | - [Webp](#webp) 8 | - [Gif](#gif) 9 | - [Bmp](#bmp) 10 | 11 | ## 关于 Big Endian 和 Little Endian 12 | 13 | 写在前面 14 | 15 | 计算机中储存数值有 2 种方式,一种是大端存储(Big Endian),一种是小端存储(Little Endian)。 16 | 17 | 以 uint32 数值的储存为例来举例: 18 | 19 | 所谓大端储存就是指存储的数值是从高位到低位的顺序,,就是数值占用 4 个字节,比如: 20 | 21 | 0x00 0x00 0x00 0x01 转为十进制,结果是 1 22 | 23 | 小端储存就不同了,同样的数,如果是小端储存,高位存储在低地址,低位存储在高地址,比如: 24 | 25 | 0x00 0x00 0x00 0x01 转为十进制,结果是 256^3。等于大端储存的 0x01 0x00 0x00 0x00 26 | 27 | ## JPEG 28 | 29 | 因为历史原因,也叫 JPG 30 | 31 | 储存方式有如下的规则,其中一般使用开头和结尾的各 2 个字节来判断是否为 JPEG 格式 32 | 33 | 开头: 0xFF 0xD8 34 | 结尾: 0xFF 0xD9 35 | 36 | 如下所示: 37 | 38 | ```tree 39 | | start: 2 bytes | block | block | ... | end: 2 bytes | 40 | ``` 41 | 42 | 然后,储存时是按数据块(block)来储存的,每个数据库块的开头都有一个 2 字节的标识符,为 0xFF,type,来标识块的类型。 43 | 其中 C0 类型为图片的宽高信息。 44 | 45 | 数据块分析: 46 | 47 | | 数值 | 偏移量 | 长度 | 说明 | 48 | | ---- | ----------- | ---- | ------------------------------------------ | 49 | | FF | +0 | 1 | 开始标识符 | 50 | | 任意 | +1 | 1 | 类型,查看 JPEG 规范 | 51 | | n | +2 ~ +3 | 2 | 块长度,包含这两位,但不包含 FF 和类型长度 | 52 | | 内容 | +4 ~ +(n+2) | n-2 | 内容 | 53 | 54 | --- 55 | 56 | 已知类型标识符 57 | 58 | | 数值 | 说明 | 信息 | 59 | | ---- | -------- | ------------------------- | 60 | | C0 | 宽度高度 | `+5~+6:高度 +7~+8:宽度` | 61 | 62 | ## PNG 63 | 64 | 开头固定为: 65 | 66 | `89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52` 67 | 68 | 宽度在 0x00000010 偏移量处, `0~3` 位是宽度 `4~7` 位是高度 69 | 70 | 按照 PNG 规范,其实表示宽度和高度的字符各占 4 个字节,但实际应用中 2 字节可以表示的无符号整数的最大数值为 256^2-1=65535,而当前实际应用中我没有发现有这么大的图片,所以代码中只解析了 2 字节来表示宽度和高度,如果后续有需要,我会改进。 71 | 72 | ## Webp 73 | 74 | little endian 75 | 76 | 开头: 12 字节标识 webp 格式 77 | 78 | ![20190920150556.png](https://raw.githubusercontent.com/kikt-blog/image/master/img/20190920150556.png) 79 | 80 | RIFF(4 字节) + 文件长度(4 字节) + WEBP(4 字节), 文件长度不包含 RIFF 标识头和长度信息, 但包含 WEBP 81 | 82 | 比如我的图片文件大小为 125,198 字节, 这个文件长度对应的十六进制为 06 E9 01 00 , 这里要注意, 和 png jpg 的长度表示方案不同, 这里是倒序的 0x0001e906 = 125190 83 | 84 | --- 85 | 86 | VP8 chunk: 找到 VP8 的标识 根据内容不同, 表示也不尽相同 87 | 往后偏移 `+14 ~ +15` 为宽度(倒序), `16~17`为高度(倒序)) 88 | 89 | ## Gif 90 | 91 | 开头: `0x47, 0x49, 0x46, 0x38, 0x39, 0x61` 92 | 结尾: `0x3B` 93 | 94 | 从偏移量 `+6~+7` 两位标识宽度 `+8~+9`标识高度, 这里也是倒序, 示例中图片的宽度对应的十六进制为: `[0xB0,0x02] (688)`, 高度为`[0x2E,0x05](1326)` 95 | 96 | ## Bmp 97 | 98 | 储存数字的方式是 little endian 99 | 100 | 本部分参考 [信息来源](https://blog.csdn.net/qq_41137110/article/details/119893817) 101 | 102 | bmp 是 windows 的位图格式,但这个格式其实有很多种,通过开头的 2 个字节进行标识,一般情况下,咱们就关注 BM 开头的即可 103 | 104 | ![sCtP6U](https://cdn.jsdelivr.net/gh/kikt-blog/image@branch-2/uPic/sCtP6U.jpg) 105 | 106 | 数据段总体分 4 个部分,其中调色板部分如果在颜色信息为 24 位或者 32 位以上是没有的 107 | 108 | ![qWJIy2](https://cdn.jsdelivr.net/gh/kikt-blog/image@branch-2/uPic/qWJIy2.jpg) 109 | 110 | 而本库中需要的宽高信息集中在第二段中 111 | 112 | 第二段是如下的表 113 | 114 | | 字节序号 | 数据结构 | 描述 | 115 | | ----------- | -------- | --------------------------------- | 116 | | 15,16,17,18 | uint | 当前结构体的大小,通常是 40 或 56 | 117 | | 19,20,21,22 | int | 图像宽度(像素) | 118 | | 23,24,25,26 | int | 图像高度(像素) | 119 | | 27,28 | word | 这个字的值永远是 1 | 120 | | 29,30 | word | 每像素占用的位数,即 bpp | 121 | | 31,32,33,34 | uint | 压缩方式 | 122 | | 35,36,37,38 | uint | 图像的尺寸(字节数) | 123 | | 39,40,41,42 | int | 水平分辨率,pixels-per-meter | 124 | | 43,44,45,46 | int | 垂直分辨率,pixels-per-meter | 125 | | 47,48,49,50 | uint | 引用色彩数 | 126 | | 51,52,53,54 | uint | 关键色彩数 | 127 | 128 | 所以,我们通常读取前 2 个字节来判断是否是 BM,19 开始的四个字节是宽度,23 开始的四个字节是高度 129 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [2019] [Caijinglong] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | packages/image_size_getter/README.md -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Files and directories created by pub 2 | .dart_tool/ 3 | .packages 4 | # Remove the following pattern if you wish to check in your lock file 5 | pubspec.lock 6 | 7 | # Conventional directory for build outputs 8 | build/ 9 | 10 | # Directory created by dartdoc 11 | doc/api/ 12 | -------------------------------------------------------------------------------- /example/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.0.0 2 | 3 | - Initial version, created by Stagehand 4 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | A sample command-line application. 2 | 3 | Created from templates made available by Stagehand under a BSD-style 4 | [license](https://github.com/dart-lang/stagehand/blob/master/LICENSE). 5 | -------------------------------------------------------------------------------- /example/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # Defines a default set of lint rules enforced for 2 | # projects at Google. For details and rationale, 3 | # see https://github.com/dart-lang/pedantic#enabled-lints. 4 | include: package:pedantic/analysis_options.yaml 5 | 6 | # For lint rules and documentation, see http://dart-lang.github.io/linter/lints. 7 | # Uncomment to specify additional rules. 8 | # linter: 9 | # rules: 10 | # - camel_case_types 11 | 12 | analyzer: 13 | # exclude: 14 | # - path/to/excluded/files/** 15 | -------------------------------------------------------------------------------- /example/asset/87a.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercandies/dart_image_size_getter/b2350448d0bda758bfe2407e05a729f1590bb300/example/asset/87a.gif -------------------------------------------------------------------------------- /example/asset/IMG_20180908_080245.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercandies/dart_image_size_getter/b2350448d0bda758bfe2407e05a729f1590bb300/example/asset/IMG_20180908_080245.jpg -------------------------------------------------------------------------------- /example/asset/demo.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercandies/dart_image_size_getter/b2350448d0bda758bfe2407e05a729f1590bb300/example/asset/demo.bmp -------------------------------------------------------------------------------- /example/asset/demo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercandies/dart_image_size_getter/b2350448d0bda758bfe2407e05a729f1590bb300/example/asset/demo.webp -------------------------------------------------------------------------------- /example/asset/demo_extended.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercandies/dart_image_size_getter/b2350448d0bda758bfe2407e05a729f1590bb300/example/asset/demo_extended.webp -------------------------------------------------------------------------------- /example/asset/demo_lossless.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercandies/dart_image_size_getter/b2350448d0bda758bfe2407e05a729f1590bb300/example/asset/demo_lossless.webp -------------------------------------------------------------------------------- /example/asset/dialog.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercandies/dart_image_size_getter/b2350448d0bda758bfe2407e05a729f1590bb300/example/asset/dialog.gif -------------------------------------------------------------------------------- /example/asset/have_orientation_exif_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercandies/dart_image_size_getter/b2350448d0bda758bfe2407e05a729f1590bb300/example/asset/have_orientation_exif_3.jpg -------------------------------------------------------------------------------- /example/asset/have_orientation_exif_6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercandies/dart_image_size_getter/b2350448d0bda758bfe2407e05a729f1590bb300/example/asset/have_orientation_exif_6.jpg -------------------------------------------------------------------------------- /example/asset/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercandies/dart_image_size_getter/b2350448d0bda758bfe2407e05a729f1590bb300/example/asset/ic_launcher.png -------------------------------------------------------------------------------- /example/asset/issue27/issue27-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercandies/dart_image_size_getter/b2350448d0bda758bfe2407e05a729f1590bb300/example/asset/issue27/issue27-1.jpg -------------------------------------------------------------------------------- /example/asset/test.MP.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercandies/dart_image_size_getter/b2350448d0bda758bfe2407e05a729f1590bb300/example/asset/test.MP.jpg -------------------------------------------------------------------------------- /example/bin/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:image_size_getter/image_size_getter.dart'; 4 | import 'package:image_size_getter/file_input.dart'; 5 | 6 | void main(List arguments) async { 7 | final file = File('asset/IMG_20180908_080245.jpg'); 8 | final jpgResult = ImageSizeGetter.getSizeResult(FileInput(file)); 9 | print( 10 | 'jpg = ${jpgResult.size} (decoded by ${jpgResult.decoder.decoderName})'); 11 | 12 | final pngFile = File('asset/ic_launcher.png'); 13 | final pngResult = ImageSizeGetter.getSizeResult(FileInput(pngFile)); 14 | print( 15 | 'png = ${pngResult.size} (decoded by ${pngResult.decoder.decoderName})'); 16 | 17 | final webpFile = File('asset/demo.webp'); 18 | final webpResult = ImageSizeGetter.getSizeResult(FileInput(webpFile)); 19 | print( 20 | 'webp = ${webpResult.size} (decoded by ${webpResult.decoder.decoderName})'); 21 | 22 | final gifFile = File('asset/dialog.gif'); 23 | final gifResult = ImageSizeGetter.getSizeResult(FileInput(gifFile)); 24 | print( 25 | 'gif = ${gifResult.size} (decoded by ${gifResult.decoder.decoderName})'); 26 | 27 | // errorExample(); 28 | } 29 | 30 | void errorExample() { 31 | final input = FileInput(File( 32 | '/Users/jinglongcai/Desktop/96068243-1d25cb00-0ece-11eb-9f2c-6b958756c769.jpg')); 33 | 34 | final result = ImageSizeGetter.getSizeResult(input); 35 | print('${result.size} (decoded by ${result.decoder.decoderName})'); 36 | } 37 | -------------------------------------------------------------------------------- /example/melos_example.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: example 2 | description: A sample command-line application. 3 | # version: 1.0.0 4 | # homepage: https://www.example.com 5 | # author: cjl_macbook 6 | 7 | environment: 8 | sdk: ">=2.12.0 <3.0.0" 9 | 10 | dependencies: 11 | image_size_getter: any 12 | 13 | dev_dependencies: 14 | pedantic: ^1.7.0 15 | test: ^1.5.0 16 | -------------------------------------------------------------------------------- /example/pubspec_overrides.yaml: -------------------------------------------------------------------------------- 1 | # melos_managed_dependency_overrides: image_size_getter 2 | dependency_overrides: 3 | image_size_getter: 4 | path: ../packages/image_size_getter 5 | -------------------------------------------------------------------------------- /example/test/issue_027_test.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:image_size_getter/file_input.dart'; 4 | import 'package:image_size_getter/image_size_getter.dart'; 5 | import 'package:test/test.dart'; 6 | 7 | class MyJpegDecoder extends JpegDecoder { 8 | @override 9 | bool isValid(ImageInput input) { 10 | final header = input.getRange(0, 2); 11 | return _checkHeader(header); 12 | } 13 | 14 | @override 15 | Future isValidAsync(AsyncImageInput input) async { 16 | final header = await input.getRange(0, 2); 17 | return _checkHeader(header); 18 | } 19 | 20 | bool _checkHeader(List bytes) { 21 | return bytes[0] == 0xFF && bytes[1] == 0xD8; 22 | } 23 | 24 | @override 25 | List get supportedExtensions => JpegDecoder().supportedExtensions; 26 | } 27 | 28 | void main() { 29 | ImageSizeGetter.registerDecoder(MyJpegDecoder()); 30 | 31 | test('valid jpeg test', () { 32 | final dir = Directory('asset/issue27'); 33 | final files = dir.listSync(); 34 | for (final file in files) { 35 | if (file.path.endsWith('jpg')) { 36 | var fileInput = FileInput(File(file.path)); 37 | final isJpg = MyJpegDecoder().isValid(fileInput); 38 | expect(isJpg, true); 39 | 40 | final result = ImageSizeGetter.getSizeResult(fileInput); 41 | print( 42 | 'jpg size: ${result.size} (decoded by ${result.decoder.decoderName})'); 43 | } 44 | } 45 | }); 46 | } 47 | -------------------------------------------------------------------------------- /melos.yaml: -------------------------------------------------------------------------------- 1 | name: image_size_getter_project_workspaces 2 | 3 | packages: 4 | - example/ 5 | - packages/** 6 | 7 | scripts: 8 | test: 9 | exec: | 10 | dart test 11 | dartdoc: 12 | exec: | 13 | dartdoc 2>&1 14 | packageFilters: 15 | scope: 16 | - image_size_getter 17 | - image_size_getter_heic 18 | - image_size_getter_http_input 19 | -------------------------------------------------------------------------------- /melos_image_size_getter_project_workspaces.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /packages/image_size_getter/.gitignore: -------------------------------------------------------------------------------- 1 | # Files and directories created by pub 2 | .dart_tool/ 3 | .packages 4 | # Remove the following pattern if you wish to check in your lock file 5 | pubspec.lock 6 | 7 | # Conventional directory for build outputs 8 | build/ 9 | 10 | # Directory created by dartdoc 11 | doc/api/ 12 | -------------------------------------------------------------------------------- /packages/image_size_getter/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | - [CHANGELOG](#changelog) 4 | - [2.4.0](#240) 5 | - [2.3.0+1](#2301) 6 | - [2.3.0](#230) 7 | - [2.2.0](#220) 8 | - [2.1.3](#213) 9 | - [2.1.2](#212) 10 | - [2.1.1](#211) 11 | - [2.1.0](#210) 12 | - [2.0.0](#200) 13 | - [1.1.0](#110) 14 | - [1.0.0](#100) 15 | - [0.3.0](#030) 16 | - [0.2.1](#021) 17 | - [0.2.0](#020) 18 | - [0.1.1](#011) 19 | - [0.1.0](#010) 20 | 21 | ## 2.4.0 22 | 23 | - Add `isExtensionSupported` to `BaseDecoder` 24 | 25 | ## 2.3.0+1 26 | 27 | - Docs: Add docs for dartdoc. 28 | 29 | ## 2.3.0 30 | 31 | Feature: 32 | 33 | - Add `getSizeResult` for `SizeResult` 34 | 35 | ## 2.2.0 36 | 37 | Feature: 38 | 39 | - Add non-standard jpeg decoder 40 | 41 | ## 2.1.3 42 | 43 | Fix: 44 | 45 | - Support lossless WebP ([#38](https://github.com/CaiJingLong/dart_image_size_getter/issues/38)) 46 | 47 | ## 2.1.2 48 | 49 | Fix: 50 | 51 | - Support gif 87a type [#23] [#24]. 52 | 53 | ## 2.1.1 54 | 55 | - fix: remove debug value print calls from `jpeg_decoder` by @ZaLiTHkA in 56 | - @ZaLiTHkA made their first contribution in 57 | 58 | **Full Changelog**: 59 | 60 | ## 2.1.0 61 | 62 | Feature: 63 | 64 | - Add `needRotate` for `Size` 65 | 66 | **What's Changed:** - Add needRotate for Size by @CaiJingLong in 67 | 68 | **Full Changelog**: 69 | 70 | ## 2.0.0 71 | 72 | New feature: 73 | 74 | - Refactored the code and removed the `AsyncImageSizeGetter` class. 75 | - We can customize the decoder. 76 | 77 | ## 1.1.0 78 | 79 | - Support webp extended format by @nkming2 in 80 | - Http input by @CaiJingLong in 81 | 82 | **New Contributors:** - @nkming2 made their first contribution in 83 | 84 | **Full Changelog:** 85 | 86 | ## 1.0.0 87 | 88 | Null safety version. 89 | 90 | ## 0.3.0 91 | 92 | Support get size of async input of image. 93 | 94 | ## 0.2.1 95 | 96 | Update dependency. 97 | Fix readme. 98 | 99 | ## 0.2.0 100 | 101 | Support memory type. 102 | 103 | Breaking change: 104 | Fix: type of `ImageSizeGetter` 105 | 106 | ## 0.1.1 107 | 108 | FIx jpeg format. 109 | 110 | ## 0.1.0 111 | 112 | Initial version. 113 | -------------------------------------------------------------------------------- /packages/image_size_getter/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [2019] [Caijinglong] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /packages/image_size_getter/README.md: -------------------------------------------------------------------------------- 1 | # image_size_getter 2 | 3 | [![pub package](https://img.shields.io/pub/v/image_size_getter.svg)](https://pub.dev/packages/image_size_getter) 4 | [![likes](https://img.shields.io/pub/likes/image_size_getter)](https://pub.dev/packages/image_size_getter/score) 5 | [![popularity](https://img.shields.io/pub/popularity/image_size_getter)](https://pub.dev/packages/image_size_getter/score) 6 | [![pub points](https://img.shields.io/pub/points/image_size_getter)](https://pub.dev/packages/image_size_getter/score) 7 | 8 | [![GitHub stars](https://img.shields.io/github/stars/CaiJingLong/dart_image_size_getter.svg)](https://github.com/CaiJingLong/dart_image_size_getter) 9 | [![GitHub forks](https://img.shields.io/github/forks/CaiJingLong/dart_image_size_getter.svg)](https://github.com/CaiJingLong/dart_image_size_getter) 10 | [![GitHub license](https://img.shields.io/github/license/CaiJingLong/dart_image_size_getter.svg)](https://github.com/CaiJingLong/dart_image_size_getter/blob/master/LICENSE) 11 | 12 | Do not completely decode the image file, just read the metadata to get the image width and height. 13 | 14 | Just support jpeg, gif, png, webp, bmp. 15 | 16 | ## Install 17 | 18 | ```yaml 19 | dependencies: 20 | image_size_getter: ^2.3.0 21 | ``` 22 | 23 | ## Usage 24 | 25 | ### Image of File 26 | 27 | ```dart 28 | import 'dart:io'; 29 | 30 | import 'package:image_size_getter/image_size_getter.dart'; 31 | import 'package:image_size_getter/file_input.dart'; 32 | 33 | void main(List arguments) async { 34 | final file = File('asset/IMG_20180908_080245.jpg'); 35 | final jpgResult = ImageSizeGetter.getSizeResult(FileInput(file)); 36 | print( 37 | 'jpg = ${jpgResult.size} (decoded by ${jpgResult.decoder.decoderName})'); 38 | 39 | final pngFile = File('asset/ic_launcher.png'); 40 | final pngResult = ImageSizeGetter.getSizeResult(FileInput(pngFile)); 41 | print( 42 | 'png = ${pngResult.size} (decoded by ${pngResult.decoder.decoderName})'); 43 | 44 | final webpFile = File('asset/demo.webp'); 45 | final webpResult = ImageSizeGetter.getSizeResult(FileInput(webpFile)); 46 | print( 47 | 'webp = ${webpResult.size} (decoded by ${webpResult.decoder.decoderName})'); 48 | 49 | final gifFile = File('asset/dialog.gif'); 50 | final gifResult = ImageSizeGetter.getSizeResult(FileInput(gifFile)); 51 | print( 52 | 'gif = ${gifResult.size} (decoded by ${gifResult.decoder.decoderName})'); 53 | 54 | // errorExample(); 55 | } 56 | 57 | ``` 58 | 59 | ### Image of Memory 60 | 61 | ```dart 62 | import 'package:image_size_getter/image_size_getter.dart'; 63 | 64 | void foo(Uint8List image){ 65 | final memoryImageSizeResult = ImageSizeGetter.getSizeResult(MemoryInput(image)); 66 | final size = memoryImageSizeResult.size; 67 | final decoder = memoryImageSizeResult.decoder; 68 | print('size = $size, decoder = ${decoder.decoderName}'); 69 | } 70 | ``` 71 | 72 | ### Image of Http 73 | 74 | See [HttpInput][]. 75 | 76 | ## About Size 77 | 78 | The size contains width and height. 79 | 80 | But some image have orientation. 81 | Such as jpeg, when the orientation of exif is 5, 6, 7 or 8, the width and height will be swapped. 82 | 83 | We can use next code to get width and height. 84 | 85 | ```dart 86 | void foo(File file) { 87 | final sizeResult = ImageSizeGetter.getSizeResult(FileInput(file)); 88 | final size = sizeResult.size; 89 | if (size.needRotate) { 90 | final width = size.height; 91 | final height = size.width; 92 | print('width = $width, height = $height'); 93 | } else { 94 | print('width = ${size.width}, height = ${size.height}'); 95 | } 96 | print('decoder = ${sizeResult.decoder.decoderName}'); 97 | } 98 | ``` 99 | 100 | ## AsyncImageInput 101 | 102 | If your data source is read asynchronously, consider using `AsyncImageInput`. 103 | 104 | A typical use case is [http_input][HttpInput]. 105 | 106 | ## Check the extension for decoder 107 | 108 | > Since 2.4.0 109 | 110 | ```dart 111 | void checkExtension(String ext, BaseDecoder decoder) { 112 | final isSupport = decoder.isExtensionSupported(ext); 113 | print('The decoder ${decoder.decoderName} support $ext: $isSupport'); 114 | } 115 | ``` 116 | 117 | ## Custom 118 | 119 | We can implement our own input or decoder. 120 | 121 | In addition to several built-in implementations, subsequent implementations will also be added to the project through plugin. 122 | 123 | ### Custom Input 124 | 125 | Such as: [http_input](https://github.com/CaiJingLong/dart_image_size_getter/tree/master/image_size_getter_http_input). 126 | 127 | In addition, if your picture has verification, for example, you need to use the request header to access it, or you need a post request to get it, you need to customize input. 128 | 129 | ### Custom Decoder 130 | 131 | Such as bmp decoder 132 | 133 | Check the file type: 134 | 135 | ![VPMMfA](https://cdn.jsdelivr.net/gh/kikt-blog/image@branch-2/uPic/VPMMfA.png) 136 | 137 | The width and height: 138 | 139 | ![AZnx9I](https://cdn.jsdelivr.net/gh/kikt-blog/image@branch-2/uPic/AZnx9I.png) 140 | 141 | So, we can write code with: 142 | 143 | ```dart 144 | import 'package:image_size_getter/image_size_getter.dart'; 145 | 146 | class BmpDecoder extends BaseDecoder { 147 | const BmpDecoder(); 148 | 149 | @override 150 | String get decoderName => 'bmp'; 151 | 152 | @override 153 | Size getSize(ImageInput input) { 154 | final widthList = input.getRange(0x12, 0x16); 155 | final heightList = input.getRange(0x16, 0x1a); 156 | 157 | final width = convertRadix16ToInt(widthList, reverse: true); 158 | final height = convertRadix16ToInt(heightList, reverse: true); 159 | return Size(width, height); 160 | } 161 | 162 | @override 163 | Future getSizeAsync(AsyncImageInput input) async { 164 | final widthList = await input.getRange(0x12, 0x16); 165 | final heightList = await input.getRange(0x16, 0x1a); 166 | 167 | final width = convertRadix16ToInt(widthList, reverse: true); 168 | final height = convertRadix16ToInt(heightList, reverse: true); 169 | return Size(width, height); 170 | } 171 | 172 | @override 173 | bool isValid(ImageInput input) { 174 | final list = input.getRange(0, 2); 175 | return _isBmp(list); 176 | } 177 | 178 | @override 179 | Future isValidAsync(AsyncImageInput input) async { 180 | final list = await input.getRange(0, 2); 181 | return _isBmp(list); 182 | } 183 | 184 | bool _isBmp(List startList) { 185 | return startList[0] == 66 && startList[1] == 77; 186 | } 187 | } 188 | 189 | ``` 190 | 191 | Use it: 192 | 193 | ```dart 194 | final bmp = File('../../example/asset/demo.bmp'); 195 | 196 | const BmpDecoder decoder = BmpDecoder(); 197 | final input = FileInput(bmp); 198 | 199 | assert(decoder.isValid(input)); 200 | expect(decoder.getSizeResult(input).size, Size(256, 256)); 201 | ``` 202 | 203 | #### Register custom decoder to image size getter 204 | 205 | ```dart 206 | ImageSizeGetter.registerDecoder(const BmpDecoder()); 207 | ``` 208 | 209 | The method can also be used to replace the default decoder. 210 | 211 | For example, you think the existing JPEG format is not rigorous enough. 212 | 213 | ```dart 214 | ImageSizeGetter.registerDecoder(const MyJpegDecoder()); 215 | ``` 216 | 217 | #### Use decoder alone 218 | 219 | Each decoder can be used alone. 220 | 221 | ```dart 222 | void decodeWithImageInput(ImageInput input) { 223 | BaseDecoder decoder = const GifDecoder(); 224 | final isGif = decoder.isValid(input); 225 | print('isGif: $isGif'); 226 | 227 | if (isGif) { 228 | final sizeResult = decoder.getSizeResult(input); 229 | final size = sizeResult.size; 230 | print('size: $size'); 231 | } 232 | } 233 | 234 | void decodeWithAsyncImageInput(AsyncImageInput input) async { 235 | BaseDecoder decoder = const PngDecoder(); 236 | final isPng = await decoder.isValidAsync(input); 237 | print('isPng: $isPng'); 238 | 239 | if (isPng) { 240 | final sizeResult = await decoder.getSizeResultAsync(input); 241 | final size = sizeResult.size; 242 | print('size: $size'); 243 | } 244 | } 245 | ``` 246 | 247 | ## migrate 248 | 249 | See [migrate](https://github.com/CaiJingLong/dart_image_size_getter/blob/master/library/migrate.md) 250 | 251 | ## Other question 252 | 253 | The package is dart package, no just flutter package. 254 | So, if you want to get flutter asset image size, you must convert it to memory(Uint8List). 255 | 256 | ```dart 257 | final buffer = await rootBundle.load('assets/logo.png'); // get the byte buffer 258 | final memoryImageSizeResult = ImageSizeGetter.getSizeResult(MemoryInput.byteBuffer(buffer)); 259 | final size = memoryImageSizeResult.size; 260 | print('size = $size'); 261 | ``` 262 | 263 | ## LICENSE 264 | 265 | Apache 2.0 266 | 267 | [HttpInput]: https://pub.dev/packages/image_size_getter_http_input 268 | -------------------------------------------------------------------------------- /packages/image_size_getter/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # Defines a default set of lint rules enforced for 2 | # projects at Google. For details and rationale, 3 | # see https://github.com/dart-lang/pedantic#enabled-lints. 4 | # include: package:pedantic/analysis_options.yaml 5 | 6 | # For lint rules and documentation, see http://dart-lang.github.io/linter/lints. 7 | # Uncomment to specify additional rules. 8 | # linter: 9 | # rules: 10 | # - camel_case_types 11 | 12 | analyzer: 13 | # exclude: 14 | # - path/to/excluded/files/** 15 | -------------------------------------------------------------------------------- /packages/image_size_getter/lib/file_input.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:image_size_getter/src/core/input.dart'; 4 | import 'package:image_size_getter/src/utils/file_utils.dart'; 5 | 6 | /// 7 | /// {@template image_size_getter.file_input} 8 | /// 9 | /// [ImageInput] using file as input source. 10 | /// 11 | /// {@endtemplate} 12 | /// 13 | class FileInput extends ImageInput { 14 | /// {@macro image_size_getter.file_input} 15 | const FileInput(this.file); 16 | 17 | final File file; 18 | 19 | @override 20 | List getRange(int start, int end) { 21 | final utils = FileUtils(file); 22 | return utils.getRangeSync(start, end); 23 | } 24 | 25 | @override 26 | int get length => file.lengthSync(); 27 | 28 | @override 29 | bool exists() { 30 | return file.existsSync(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/image_size_getter/lib/image_size_getter.dart: -------------------------------------------------------------------------------- 1 | export 'src/entity/size.dart'; 2 | export 'src/core/memory_input.dart'; 3 | export 'src/image_size_getter.dart'; 4 | export 'src/decoder/decoder.dart'; 5 | export 'src/decoder/impl/gif_decoder.dart'; 6 | export 'src/decoder/impl/jpeg_decoder.dart'; 7 | export 'src/decoder/impl/png_decoder.dart'; 8 | export 'src/decoder/impl/webp_decoder.dart'; 9 | export 'src/decoder/impl/bmp_decoder.dart'; 10 | -------------------------------------------------------------------------------- /packages/image_size_getter/lib/src/core/input.dart: -------------------------------------------------------------------------------- 1 | import 'package:image_size_getter/src/image_size_getter.dart'; 2 | 3 | /// 4 | /// {@template image_size_getter.image_input} 5 | /// 6 | /// Provide a data source for [ImageSizeGetter] to get image size. 7 | /// 8 | /// {@endtemplate} 9 | /// 10 | abstract class ImageInput { 11 | /// {@macro image_size_getter.image_input} 12 | const ImageInput(); 13 | 14 | /// The length of the input data. 15 | int get length; 16 | 17 | /// Get a range of bytes from the input data. 18 | /// 19 | /// [start] and [end] are the start and end index of the range. 20 | /// 21 | /// Such as: [start] = 0, [end] = 2, then the result is `[0, 1]`. 22 | List getRange(int start, int end); 23 | 24 | /// Check if the input data exists. 25 | bool exists(); 26 | } 27 | 28 | /// {@template image_size_getter.HaveResourceImageInput} 29 | /// 30 | /// There are resources in these classes that need to be released. 31 | /// 32 | /// This class is a wrapper class that will automatically release resources after use. 33 | /// Once released, many resources are no longer effective. 34 | /// 35 | /// {@endtemplate} 36 | class HaveResourceImageInput extends ImageInput { 37 | /// {@macro image_size_getter.HaveResourceImageInput} 38 | /// 39 | /// [innerInput] is the input data of [ImageInput]. 40 | /// [onRelease] is the function to release the resources. 41 | /// 42 | const HaveResourceImageInput({ 43 | required this.innerInput, 44 | this.onRelease, 45 | }); 46 | 47 | /// The input data of [ImageInput]. 48 | final ImageInput innerInput; 49 | 50 | /// The function to release the resources. 51 | final Future Function()? onRelease; 52 | 53 | /// Release the resources. 54 | Future release() async { 55 | await onRelease?.call(); 56 | } 57 | 58 | @override 59 | bool exists() { 60 | return innerInput.exists(); 61 | } 62 | 63 | @override 64 | List getRange(int start, int end) { 65 | return innerInput.getRange(start, end); 66 | } 67 | 68 | @override 69 | int get length => innerInput.length; 70 | } 71 | 72 | /// {@template image_size_getter.AsyncImageInput} 73 | /// 74 | /// {@macro image_size_getter.image_input} 75 | /// 76 | /// Unlike [ImageInput], the methods of this class are asynchronous. 77 | /// 78 | /// {@endtemplate} 79 | abstract class AsyncImageInput { 80 | /// {@macro image_size_getter.image_input} 81 | /// 82 | /// {@macro image_size_getter.AsyncImageInput} 83 | const AsyncImageInput(); 84 | 85 | /// {@macro image_size_getter.image_input} 86 | /// 87 | /// {@macro image_size_getter.AsyncImageInput} 88 | /// 89 | /// This method is the [ImageInput] of agent synchronization. 90 | factory AsyncImageInput.input(ImageInput input) = _SyncInputWrapper; 91 | 92 | /// Whether partial loading is supported. 93 | /// 94 | /// Many asynchronous sources do not allow partial reading. 95 | Future supportRangeLoad(); 96 | 97 | /// When asynchronous reading is not supported, 98 | /// an input for real reading will be cached in memory(web) or file(dart.io). 99 | Future delegateInput(); 100 | 101 | /// Get a range of bytes from the input data. 102 | Future get length; 103 | 104 | /// Get a range of bytes from the input data. 105 | /// 106 | /// [start] and [end] are the start and end index of the range. 107 | /// 108 | /// Such as: `[start]` = 0, `[end]` = 2, then the result is `[0, 1]`. 109 | Future> getRange(int start, int end); 110 | 111 | /// Check if the input data exists. 112 | Future exists(); 113 | } 114 | 115 | /// {@template image_size_getter.SyncImageInput} 116 | /// 117 | /// Just wrap [ImageInput] to make it asynchronous. 118 | /// 119 | /// {@endtemplate} 120 | class _SyncInputWrapper extends AsyncImageInput { 121 | /// {@macro image_size_getter.SyncImageInput} 122 | const _SyncInputWrapper(this._input); 123 | 124 | /// The input data of [ImageInput]. 125 | final ImageInput _input; 126 | 127 | @override 128 | Future supportRangeLoad() async { 129 | return true; 130 | } 131 | 132 | @override 133 | Future exists() async { 134 | return _input.exists(); 135 | } 136 | 137 | @override 138 | Future> getRange(int start, int end) async { 139 | return _input.getRange(start, end); 140 | } 141 | 142 | @override 143 | Future get length async => _input.length; 144 | 145 | @override 146 | Future delegateInput() async { 147 | return HaveResourceImageInput(innerInput: _input); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /packages/image_size_getter/lib/src/core/memory_input.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:image_size_getter/src/core/input.dart'; 4 | 5 | /// {@template image_size_getter.memory_input} 6 | /// A memory-based implementation of [ImageInput] that reads image data from bytes in memory. 7 | /// 8 | /// This class provides functionality to read image data from a [Uint8List] or [ByteBuffer] 9 | /// stored in memory, making it useful for processing images that are already loaded into 10 | /// memory or received from network requests. 11 | /// {@endtemplate} 12 | class MemoryInput extends ImageInput { 13 | /// The underlying bytes containing the image data. 14 | final Uint8List bytes; 15 | 16 | /// {@macro image_size_getter.image_input} 17 | const MemoryInput(this.bytes); 18 | 19 | /// Creates a [MemoryInput] instance from a [ByteBuffer]. 20 | /// 21 | /// This factory constructor converts the provided [buffer] into a [Uint8List] 22 | /// and creates a new [MemoryInput] instance. 23 | /// 24 | /// [buffer] is the [ByteBuffer] containing the image data. 25 | factory MemoryInput.byteBuffer(ByteBuffer buffer) { 26 | return MemoryInput(buffer.asUint8List()); 27 | } 28 | 29 | /// Get a range of bytes from the input data. 30 | /// 31 | /// [start] and [end] are the start and end index of the range. 32 | /// 33 | /// Such as: [start] = 0, [end] = 2, then the result is `[0, 1]`. 34 | @override 35 | List getRange(int start, int end) { 36 | return bytes.sublist(start, end); 37 | } 38 | 39 | /// The length of the input data. 40 | @override 41 | int get length => bytes.length; 42 | 43 | /// Check if the input data exists. 44 | @override 45 | bool exists() { 46 | return bytes.isNotEmpty; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /packages/image_size_getter/lib/src/decoder/decoder.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import 'package:image_size_getter/image_size_getter.dart'; 3 | import 'package:meta/meta.dart'; 4 | 5 | /// {@template image_size_getter.BaseDecoder} 6 | /// Base of the decoder. 7 | /// 8 | /// Implement this class to create a new decoder. 9 | /// {@endtemplate} 10 | abstract class BaseDecoder { 11 | /// {@macro image_size_getter.BaseDecoder} 12 | const BaseDecoder(); 13 | 14 | /// The name of the decoder. 15 | String get decoderName; 16 | 17 | /// How many file extensions are supported with the decoder. 18 | /// 19 | /// The method must be overridden for each implementation. 20 | /// 21 | /// See also: 22 | /// - https://developer.mozilla.org/docs/Web/Media/Formats/Image_types 23 | /// - [isExtensionSupported] which helps to determine if an input extension 24 | /// is supported by the decoder. 25 | @mustBeOverridden 26 | List get supportedExtensions => List.empty(); 27 | 28 | /// {@template image_size_getter.BaseDecoder.isExtensionSupported} 29 | /// Returns the [extension] is support or not. 30 | /// Ignore the case of letters. 31 | /// {@endtemplate} 32 | bool isExtensionSupported(String extension) { 33 | extension = extension.toLowerCase(); 34 | for (final ext in supportedExtensions) { 35 | // Always using lower-cased extension for the comparison. 36 | if (ext.toLowerCase() == extension) { 37 | return true; 38 | } 39 | } 40 | return false; 41 | } 42 | 43 | /// {@template image_size_getter.BaseDecoder.isValid} 44 | /// 45 | /// Returns the [input] is support or not. 46 | /// 47 | /// {@endtemplate} 48 | bool isValid(ImageInput input); 49 | 50 | /// {@macro image_size_getter.BaseDecoder.isValid} 51 | Future isValidAsync(AsyncImageInput input); 52 | 53 | /// {@template image_size_getter.BaseDecoder.getSize} 54 | /// 55 | /// Returns the size of the [input]. 56 | /// 57 | /// {@endtemplate} 58 | Size getSize(ImageInput input); 59 | 60 | /// {@macro image_size_getter.BaseDecoder.getSize} 61 | Future getSizeAsync(AsyncImageInput input); 62 | 63 | /// Convert hex a decimal list to int type. 64 | /// 65 | /// If the number is stored in big endian, pass [reverse] as false. 66 | /// 67 | /// If the number is stored in little endian, pass [reverse] as true. 68 | int convertRadix16ToInt(List list, {bool reverse = false}) { 69 | final sb = StringBuffer(); 70 | if (reverse) { 71 | list = list.toList().reversed.toList(); 72 | } 73 | 74 | for (final i in list) { 75 | sb.write(i.toRadixString(16).padLeft(2, '0')); 76 | } 77 | final numString = sb.toString(); 78 | return int.tryParse(numString, radix: 16) ?? 0; 79 | } 80 | 81 | /// compare two list. 82 | bool compareTwoList(List list1, List list2) { 83 | final listEquals = ListEquality(); 84 | return listEquals.equals(list1, list2); 85 | } 86 | } 87 | 88 | /// {@template image_size_getter.SimpleTypeValidator} 89 | /// 90 | /// Simple type validator. 91 | /// 92 | /// {@endtemplate} 93 | mixin SimpleTypeValidator on BaseDecoder { 94 | /// {@macro image_size_getter.SimpleFileHeaderAndFooter} 95 | SimpleFileHeaderAndFooter get simpleFileHeaderAndFooter; 96 | 97 | @override 98 | Future isValidAsync(AsyncImageInput input) async { 99 | final length = await input.length; 100 | final header = await input.getRange( 101 | 0, 102 | simpleFileHeaderAndFooter.startBytes.length, 103 | ); 104 | final footer = await input.getRange( 105 | length - simpleFileHeaderAndFooter.endBytes.length, 106 | length, 107 | ); 108 | 109 | final headerEquals = compareTwoList( 110 | header, 111 | simpleFileHeaderAndFooter.startBytes, 112 | ); 113 | final footerEquals = compareTwoList( 114 | footer, 115 | simpleFileHeaderAndFooter.endBytes, 116 | ); 117 | return headerEquals && footerEquals; 118 | } 119 | 120 | @override 121 | bool isValid(ImageInput input) { 122 | final length = input.length; 123 | final header = input.getRange( 124 | 0, 125 | simpleFileHeaderAndFooter.startBytes.length, 126 | ); 127 | final footer = input.getRange( 128 | length - simpleFileHeaderAndFooter.endBytes.length, 129 | length, 130 | ); 131 | 132 | final headerEquals = compareTwoList( 133 | header, 134 | simpleFileHeaderAndFooter.startBytes, 135 | ); 136 | final footerEquals = compareTwoList( 137 | footer, 138 | simpleFileHeaderAndFooter.endBytes, 139 | ); 140 | return headerEquals && footerEquals; 141 | } 142 | } 143 | 144 | /// {@template image_size_getter.SimpleFileHeaderAndFooter} 145 | /// 146 | /// Provides the header and footer of the file. 147 | /// 148 | /// {@endtemplate} 149 | mixin SimpleFileHeaderAndFooter { 150 | /// The start bytes of the file. 151 | List get startBytes; 152 | 153 | /// The end bytes of the file. 154 | List get endBytes; 155 | } 156 | 157 | /// The content have multiple headers or footers. 158 | mixin MutilFileHeaderAndFooter { 159 | /// When there are multiple start bytes, this is the list of possible start bytes of the file. 160 | List> get mutipleStartBytesList; 161 | 162 | /// When there are multiple end bytes, this is the list of possible end bytes of the file. 163 | List> get mutipleEndBytesList; 164 | } 165 | 166 | /// Validate the content. 167 | mixin MutilFileHeaderAndFooterValidator on BaseDecoder { 168 | /// {@macro image_size_getter.SimpleFileHeaderAndFooter} 169 | MutilFileHeaderAndFooter get headerAndFooter; 170 | 171 | @override 172 | Future isValidAsync(AsyncImageInput input) async { 173 | final length = await input.length; 174 | 175 | for (final header in headerAndFooter.mutipleStartBytesList) { 176 | for (final footer in headerAndFooter.mutipleEndBytesList) { 177 | final fileHeader = await input.getRange( 178 | 0, 179 | header.length, 180 | ); 181 | final fileFooter = await input.getRange( 182 | length - footer.length, 183 | length, 184 | ); 185 | 186 | final headerEquals = compareTwoList( 187 | header, 188 | fileHeader, 189 | ); 190 | final footerEquals = compareTwoList( 191 | footer, 192 | fileFooter, 193 | ); 194 | if (headerEquals && footerEquals) { 195 | return true; 196 | } 197 | } 198 | } 199 | 200 | return false; 201 | } 202 | 203 | @override 204 | bool isValid(ImageInput input) { 205 | final length = input.length; 206 | 207 | for (final header in headerAndFooter.mutipleStartBytesList) { 208 | for (final footer in headerAndFooter.mutipleEndBytesList) { 209 | final fileHeader = input.getRange( 210 | 0, 211 | header.length, 212 | ); 213 | final fileFooter = input.getRange( 214 | length - footer.length, 215 | length, 216 | ); 217 | 218 | final headerEquals = compareTwoList( 219 | header, 220 | fileHeader, 221 | ); 222 | final footerEquals = compareTwoList( 223 | footer, 224 | fileFooter, 225 | ); 226 | if (headerEquals && footerEquals) { 227 | return true; 228 | } 229 | } 230 | } 231 | 232 | return false; 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /packages/image_size_getter/lib/src/decoder/impl/bmp_decoder.dart: -------------------------------------------------------------------------------- 1 | import 'package:image_size_getter/image_size_getter.dart'; 2 | 3 | /// {@template image_size_getter.BmpDecoder} 4 | /// 5 | /// [BmpDecoder] is a class for decoding BMP file. 6 | /// 7 | /// {@endtemplate} 8 | class BmpDecoder extends BaseDecoder { 9 | /// {@macro image_size_getter.BmpDecoder} 10 | const BmpDecoder(); 11 | 12 | @override 13 | String get decoderName => 'bmp'; 14 | 15 | @override 16 | List get supportedExtensions => List.unmodifiable(['bmp']); 17 | 18 | @override 19 | Size getSize(ImageInput input) { 20 | final widthList = input.getRange(0x12, 0x16); 21 | final heightList = input.getRange(0x16, 0x1a); 22 | 23 | final width = convertRadix16ToInt(widthList, reverse: true); 24 | final height = convertRadix16ToInt(heightList, reverse: true); 25 | return Size(width, height); 26 | } 27 | 28 | @override 29 | Future getSizeAsync(AsyncImageInput input) async { 30 | final widthList = await input.getRange(0x12, 0x16); 31 | final heightList = await input.getRange(0x16, 0x1a); 32 | 33 | final width = convertRadix16ToInt(widthList, reverse: true); 34 | final height = convertRadix16ToInt(heightList, reverse: true); 35 | return Size(width, height); 36 | } 37 | 38 | @override 39 | bool isValid(ImageInput input) { 40 | final list = input.getRange(0, 2); 41 | return _isBmp(list); 42 | } 43 | 44 | @override 45 | Future isValidAsync(AsyncImageInput input) async { 46 | final list = await input.getRange(0, 2); 47 | return _isBmp(list); 48 | } 49 | 50 | bool _isBmp(List startList) { 51 | return startList[0] == 66 && startList[1] == 77; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /packages/image_size_getter/lib/src/decoder/impl/gif_decoder.dart: -------------------------------------------------------------------------------- 1 | import 'package:image_size_getter/image_size_getter.dart'; 2 | 3 | /// {@template image_size_getter.GifDecoder} 4 | /// 5 | /// [GifDecoder] is a class for decoding gif image. 6 | /// 7 | /// {@endtemplate} 8 | class GifDecoder extends BaseDecoder with MutilFileHeaderAndFooterValidator { 9 | /// {@macro image_size_getter.GifDecoder} 10 | const GifDecoder(); 11 | 12 | String get decoderName => 'gif'; 13 | 14 | @override 15 | List get supportedExtensions => List.unmodifiable(['gif']); 16 | 17 | Size _getSize(List widthList, List heightList) { 18 | final width = convertRadix16ToInt(widthList, reverse: true); 19 | final height = convertRadix16ToInt(heightList, reverse: true); 20 | 21 | return Size(width, height); 22 | } 23 | 24 | @override 25 | Size getSize(ImageInput input) { 26 | final widthList = input.getRange(6, 8); 27 | final heightList = input.getRange(8, 10); 28 | 29 | return _getSize(widthList, heightList); 30 | } 31 | 32 | @override 33 | Future getSizeAsync(AsyncImageInput input) async { 34 | final widthList = await input.getRange(6, 8); 35 | final heightList = await input.getRange(8, 10); 36 | 37 | return _getSize(widthList, heightList); 38 | } 39 | 40 | @override 41 | MutilFileHeaderAndFooter get headerAndFooter => _GifInfo(); 42 | } 43 | 44 | class _GifInfo with MutilFileHeaderAndFooter { 45 | static const start89a = [ 46 | 0x47, 47 | 0x49, 48 | 0x46, 49 | 0x38, 50 | 0x37, 51 | 0x61, 52 | ]; 53 | static const start87a = [ 54 | 0x47, 55 | 0x49, 56 | 0x46, 57 | 0x38, 58 | 0x39, 59 | 0x61, 60 | ]; 61 | 62 | static const end = [0x3B]; 63 | 64 | @override 65 | List> get mutipleEndBytesList => [end]; 66 | 67 | @override 68 | List> get mutipleStartBytesList => [ 69 | start87a, 70 | start89a, 71 | ]; 72 | } 73 | -------------------------------------------------------------------------------- /packages/image_size_getter/lib/src/decoder/impl/jpeg_decoder.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import 'package:image_size_getter/image_size_getter.dart'; 3 | import 'package:image_size_getter/src/entity/block_entity.dart'; 4 | 5 | /// {@template image_size_getter.JpegDecoder} 6 | /// 7 | /// [JpegDecoder] is a class for decoding JPEG image. 8 | /// 9 | /// {@endtemplate} 10 | class JpegDecoder extends BaseDecoder with SimpleTypeValidator { 11 | /// {@macro image_size_getter.JpegDecoder} 12 | const JpegDecoder({ 13 | this.isStandardJpeg = true, 14 | }); 15 | 16 | final bool isStandardJpeg; 17 | 18 | @override 19 | String get decoderName => isStandardJpeg ? 'jpeg' : 'non-standard-jpeg'; 20 | 21 | @override 22 | List get supportedExtensions => 23 | List.unmodifiable(['jpg', 'jpeg', 'jfif', 'pjpeg', 'pjp']); 24 | 25 | @override 26 | Size getSize(ImageInput input) { 27 | int start = 2; 28 | BlockEntity? block; 29 | var orientation = 1; 30 | 31 | while (true) { 32 | block = _getBlockSync(input, start); 33 | 34 | if (block == null) { 35 | throw Exception('Invalid jpeg file'); 36 | } 37 | 38 | // Check for App1 block 39 | if (block.type == 0xE1) { 40 | final app1BlockData = input.getRange( 41 | start, 42 | block.start + block.length, 43 | ); 44 | final exifOrientation = _getOrientation(app1BlockData); 45 | if (exifOrientation != null) { 46 | orientation = exifOrientation; 47 | } 48 | } 49 | 50 | if (block.type == 0xC0 || block.type == 0xC2) { 51 | final widthList = input.getRange(start + 7, start + 9); 52 | final heightList = input.getRange(start + 5, start + 7); 53 | return _getSize(widthList, heightList, orientation); 54 | } else { 55 | start += block.length; 56 | } 57 | } 58 | } 59 | 60 | Size _getSize(List widthList, List heightList, int orientation) { 61 | final width = convertRadix16ToInt(widthList); 62 | final height = convertRadix16ToInt(heightList); 63 | final needRotate = [5, 6, 7, 8].contains(orientation); 64 | return Size(width, height, needRotate: needRotate); 65 | } 66 | 67 | @override 68 | Future getSizeAsync(AsyncImageInput input) async { 69 | int start = 2; 70 | BlockEntity? block; 71 | var orientation = 1; 72 | 73 | while (true) { 74 | block = await _getBlockAsync(input, start); 75 | 76 | if (block == null) { 77 | throw Exception('Invalid jpeg file'); 78 | } 79 | 80 | if (block.type == 0xE1) { 81 | final app1BlockData = await input.getRange( 82 | start, 83 | block.start + block.length, 84 | ); 85 | final exifOrientation = _getOrientation(app1BlockData); 86 | if (exifOrientation != null) { 87 | orientation = exifOrientation; 88 | } 89 | } 90 | 91 | if (block.type == 0xC0 || block.type == 0xC2) { 92 | final widthList = await input.getRange(start + 7, start + 9); 93 | final heightList = await input.getRange(start + 5, start + 7); 94 | orientation = (await input.getRange(start + 9, start + 10))[0]; 95 | return _getSize(widthList, heightList, orientation); 96 | } else { 97 | start += block.length; 98 | } 99 | } 100 | } 101 | 102 | BlockEntity? _getBlockSync(ImageInput input, int blockStart) { 103 | try { 104 | final blockInfoList = input.getRange(blockStart, blockStart + 4); 105 | 106 | if (blockInfoList[0] != 0xFF) { 107 | return null; 108 | } 109 | 110 | final blockSizeList = input.getRange(blockStart + 2, blockStart + 4); 111 | 112 | return _createBlock(blockSizeList, blockStart, blockInfoList); 113 | } catch (e) { 114 | return null; 115 | } 116 | } 117 | 118 | Future _getBlockAsync( 119 | AsyncImageInput input, int blockStart) async { 120 | try { 121 | final blockInfoList = await input.getRange(blockStart, blockStart + 4); 122 | 123 | if (blockInfoList[0] != 0xFF) { 124 | return null; 125 | } 126 | 127 | final blockSizeList = 128 | await input.getRange(blockStart + 2, blockStart + 4); 129 | 130 | return _createBlock(blockSizeList, blockStart, blockInfoList); 131 | } catch (e) { 132 | return null; 133 | } 134 | } 135 | 136 | BlockEntity _createBlock( 137 | List sizeList, 138 | int blockStart, 139 | List blockInfoList, 140 | ) { 141 | final blockLength = 142 | convertRadix16ToInt(sizeList) + 2; // +2 for 0xFF and TYPE 143 | final typeInt = blockInfoList[1]; 144 | 145 | return BlockEntity(typeInt, blockLength, blockStart); 146 | } 147 | 148 | @override 149 | SimpleFileHeaderAndFooter get simpleFileHeaderAndFooter => 150 | isStandardJpeg ? _JpegInfo() : _NonStandardJpegInfo(); 151 | 152 | int? _getOrientation(List app1blockData) { 153 | // About EXIF, See: https://www.media.mit.edu/pia/Research/deepview/exif.html#orientation 154 | 155 | // app1 block buffer: 156 | // header (2 bytes) 157 | // length (2 bytes) 158 | // exif header (6 bytes) 159 | // exif for little endian (2 bytes), 0x4d4d is for big endian, 0x4949 is for little endian 160 | // tag mark (2 bytes) 161 | // offset first IFD (4 bytes) 162 | // IFD data : 163 | // number of entries (2 bytes) 164 | // for each entry: 165 | // exif tag (2 bytes) 166 | // data format (2 bytes), 1 = unsigned byte, 2 = ascii, 3 = unsigned short, 4 = unsigned long, 5 = unsigned rational, 6 = signed byte, 7 = undefined, 8 = signed short, 9 = signed long, 10 = signed rational 167 | // number of components (4 bytes) 168 | // value (4 bytes) 169 | // padding (0 ~ 3 bytes, depends on data format) 170 | // So, the IFD data starts at offset 14. 171 | 172 | // Check app1 block exif info is valid 173 | if (app1blockData.length < 14) { 174 | return null; 175 | } 176 | 177 | // Check app1 block exif info is valid 178 | final exifIdentifier = app1blockData.sublist(4, 10); 179 | 180 | final listEquality = ListEquality(); 181 | 182 | if (!listEquality 183 | .equals(exifIdentifier, [0x45, 0x78, 0x69, 0x66, 0x00, 0x00])) { 184 | return null; 185 | } 186 | 187 | final littleEndian = app1blockData[10] == 0x49; 188 | 189 | int getNumber(int start, int end) { 190 | final numberList = app1blockData.sublist(start, end); 191 | return convertRadix16ToInt(numberList, reverse: littleEndian); 192 | } 193 | 194 | // Get idf byte 195 | var idf0Start = 18; 196 | final tagEntryCount = getNumber(idf0Start, idf0Start + 2); 197 | 198 | var currentIndex = idf0Start + 2; 199 | 200 | for (var i = 0; i < tagEntryCount; i++) { 201 | final tagType = getNumber(currentIndex, currentIndex + 2); 202 | 203 | if (tagType == 0x0112) { 204 | return getNumber(currentIndex + 8, currentIndex + 10); 205 | } 206 | 207 | // every tag length is 0xC bytes 208 | currentIndex += 0xC; 209 | } 210 | 211 | return null; 212 | } 213 | } 214 | 215 | class _JpegInfo with SimpleFileHeaderAndFooter { 216 | static const start = [0xFF, 0xD8]; 217 | static const end = [0xFF, 0xD9]; 218 | 219 | @override 220 | List get endBytes => end; 221 | 222 | @override 223 | List get startBytes => start; 224 | } 225 | 226 | /// Non-standard JPEG Info 227 | /// 228 | /// Some mvimg files have a non-standard header. 229 | /// 230 | /// The file is not end with 0xFFD9, so we need to use this to get the size. 231 | class _NonStandardJpegInfo with SimpleFileHeaderAndFooter { 232 | static const start = [0xFF, 0xD8]; 233 | static const end = []; 234 | 235 | @override 236 | List get endBytes => end; 237 | 238 | @override 239 | List get startBytes => start; 240 | } 241 | -------------------------------------------------------------------------------- /packages/image_size_getter/lib/src/decoder/impl/png_decoder.dart: -------------------------------------------------------------------------------- 1 | import 'package:image_size_getter/image_size_getter.dart'; 2 | 3 | /// {@template image_size_getter.PngDecoder} 4 | /// 5 | /// [PngDecoder] is a class for decoding PNG image. 6 | /// 7 | /// {@endtemplate} 8 | class PngDecoder extends BaseDecoder with SimpleTypeValidator { 9 | /// {@macro image_size_getter.PngDecoder} 10 | const PngDecoder(); 11 | 12 | @override 13 | String get decoderName => 'png'; 14 | 15 | @override 16 | List get supportedExtensions => List.unmodifiable(['png']); 17 | 18 | @override 19 | Size getSize(ImageInput input) { 20 | final widthList = input.getRange(0x10, 0x14); 21 | final heightList = input.getRange(0x14, 0x18); 22 | 23 | final width = convertRadix16ToInt(widthList); 24 | final height = convertRadix16ToInt(heightList); 25 | 26 | return Size(width, height); 27 | } 28 | 29 | @override 30 | Future getSizeAsync(AsyncImageInput input) async { 31 | final widthList = await input.getRange(0x10, 0x14); 32 | final heightList = await input.getRange(0x14, 0x18); 33 | final width = convertRadix16ToInt(widthList); 34 | final height = convertRadix16ToInt(heightList); 35 | return Size(width, height); 36 | } 37 | 38 | @override 39 | SimpleFileHeaderAndFooter get simpleFileHeaderAndFooter => _PngHeaders(); 40 | } 41 | 42 | class _PngHeaders with SimpleFileHeaderAndFooter { 43 | static const sig = [ 44 | 0x89, 45 | 0x50, 46 | 0x4E, 47 | 0x47, 48 | 0x0D, 49 | 0x0A, 50 | 0x1A, 51 | 0x0A, 52 | ]; 53 | 54 | static const iend = [ 55 | 0x00, 56 | 0x00, 57 | 0x00, 58 | 0x00, 59 | 0x49, 60 | 0x45, 61 | 0x4E, 62 | 0x44, 63 | 0xAE, 64 | 0x42, 65 | 0x60, 66 | 0x82 67 | ]; 68 | 69 | @override 70 | List get endBytes => iend; 71 | 72 | @override 73 | List get startBytes => sig; 74 | } 75 | -------------------------------------------------------------------------------- /packages/image_size_getter/lib/src/decoder/impl/webp_decoder.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import 'package:image_size_getter/image_size_getter.dart'; 3 | 4 | /// {@template image_size_getter.WebpDecoder} 5 | /// 6 | /// [WebpDecoder] is a class for decoding webp image. 7 | /// 8 | /// {@endtemplate} 9 | class WebpDecoder extends BaseDecoder { 10 | /// {@macro image_size_getter.WebpDecoder} 11 | const WebpDecoder(); 12 | 13 | @override 14 | String get decoderName => 'webp'; 15 | 16 | @override 17 | List get supportedExtensions => List.unmodifiable(['webp']); 18 | 19 | @override 20 | Size getSize(ImageInput input) { 21 | final chunkHeader = input.getRange(12, 16); 22 | if (_isExtendedFormat(chunkHeader)) { 23 | final widthList = input.getRange(0x18, 0x1b); 24 | final heightList = input.getRange(0x1b, 0x1d); 25 | return _createExtendedFormatSize(widthList, heightList); 26 | } else if (_isLosslessFormat(chunkHeader)) { 27 | final sizeList = input.getRange(0x15, 0x19); 28 | return _createLosslessFormatSize(sizeList); 29 | } else { 30 | final widthList = input.getRange(0x1a, 0x1c); 31 | final heightList = input.getRange(0x1c, 0x1e); 32 | return _createNormalSize(widthList, heightList); 33 | } 34 | } 35 | 36 | Size _createNormalSize(List widthList, List heightList) { 37 | final width = convertRadix16ToInt(widthList, reverse: true); 38 | final height = convertRadix16ToInt(heightList, reverse: true); 39 | return Size(width, height); 40 | } 41 | 42 | Size _createExtendedFormatSize(List widthList, List heightList) { 43 | final width = convertRadix16ToInt(widthList, reverse: true) + 1; 44 | final height = convertRadix16ToInt(heightList, reverse: true) + 1; 45 | return Size(width, height); 46 | } 47 | 48 | Size _createLosslessFormatSize(List sizeList) { 49 | final bits = sizeList 50 | .map( 51 | (i) => i.toRadixString(2).split('').reversed.join().padRight(8, '0'), 52 | ) 53 | .join() 54 | .split(''); 55 | final width = 56 | (int.tryParse(bits.sublist(0, 14).reversed.join(), radix: 2) ?? 0) + 1; 57 | final height = 58 | (int.tryParse(bits.sublist(14, 28).reversed.join(), radix: 2) ?? 0) + 1; 59 | return Size(width, height); 60 | } 61 | 62 | bool _isExtendedFormat(List chunkHeader) { 63 | return ListEquality().equals(chunkHeader, "VP8X".codeUnits); 64 | } 65 | 66 | bool _isLosslessFormat(List chunkHeader) { 67 | return ListEquality().equals(chunkHeader, "VP8L".codeUnits); 68 | } 69 | 70 | @override 71 | Future getSizeAsync(AsyncImageInput input) async { 72 | final chunkHeader = await input.getRange(12, 16); 73 | if (_isExtendedFormat(chunkHeader)) { 74 | final widthList = await input.getRange(0x18, 0x1b); 75 | final heightList = await input.getRange(0x1b, 0x1d); 76 | return _createExtendedFormatSize(widthList, heightList); 77 | } else if (_isLosslessFormat(chunkHeader)) { 78 | final sizeList = await input.getRange(0x15, 0x19); 79 | return _createLosslessFormatSize(sizeList); 80 | } else { 81 | final widthList = await input.getRange(0x1a, 0x1c); 82 | final heightList = await input.getRange(0x1c, 0x1e); 83 | return _createNormalSize(widthList, heightList); 84 | } 85 | } 86 | 87 | @override 88 | bool isValid(ImageInput input) { 89 | final sizeStart = input.getRange(0, 4); 90 | final sizeEnd = input.getRange(8, 12); 91 | 92 | const eq = ListEquality(); 93 | 94 | if (eq.equals(sizeStart, _WebpHeaders.fileSizeStart) && 95 | eq.equals(sizeEnd, _WebpHeaders.fileSizeEnd)) { 96 | return true; 97 | } 98 | return false; 99 | } 100 | 101 | @override 102 | Future isValidAsync(AsyncImageInput input) async { 103 | final sizeStart = await input.getRange(0, 4); 104 | final sizeEnd = await input.getRange(8, 12); 105 | 106 | const eq = ListEquality(); 107 | 108 | if (eq.equals(sizeStart, _WebpHeaders.fileSizeStart) && 109 | eq.equals(sizeEnd, _WebpHeaders.fileSizeEnd)) { 110 | return true; 111 | } 112 | return false; 113 | } 114 | } 115 | 116 | class _WebpHeaders with SimpleFileHeaderAndFooter { 117 | static const fileSizeStart = [ 118 | 0x52, 119 | 0x49, 120 | 0x46, 121 | 0x46, 122 | ]; 123 | 124 | static const fileSizeEnd = [ 125 | 0x57, 126 | 0x45, 127 | 0x42, 128 | 0x50, 129 | ]; 130 | 131 | @override 132 | List get endBytes => fileSizeEnd; 133 | 134 | @override 135 | List get startBytes => fileSizeStart; 136 | } 137 | -------------------------------------------------------------------------------- /packages/image_size_getter/lib/src/entity/block_entity.dart: -------------------------------------------------------------------------------- 1 | /// The block of jpeg format. 2 | class BlockEntity { 3 | /// The block of jpeg format. 4 | BlockEntity(this.type, this.length, this.start); 5 | 6 | /// The type of the block. 7 | int type; 8 | 9 | /// The length of the block. 10 | int length; 11 | 12 | /// Start of offset 13 | int start; 14 | 15 | /// Error block. 16 | static BlockEntity error = BlockEntity(-1, -1, -1); 17 | 18 | @override 19 | String toString() { 20 | return "BlockEntity (type:$type, length:$length)"; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/image_size_getter/lib/src/entity/size.dart: -------------------------------------------------------------------------------- 1 | import 'package:hashcodes/hashcodes.dart'; 2 | 3 | import '../../image_size_getter.dart'; 4 | 5 | /// {@template image_size_getter.Size.class} 6 | /// 7 | /// [Size] is a class for image size. 8 | /// 9 | /// The size contains [width] and [height]. 10 | /// 11 | /// {@endtemplate} 12 | /// 13 | /// --- 14 | /// 15 | /// {@macro image_size_getter.Size.needToRotate} 16 | class Size { 17 | /// {@macro image_size_getter.Size.class} 18 | /// 19 | /// --- 20 | /// 21 | /// {@macro image_size_getter.Size.needToRotate} 22 | const Size( 23 | this.width, 24 | this.height, { 25 | this.needRotate = false, 26 | }); 27 | 28 | /// The width of the media. 29 | final int width; 30 | 31 | /// The height of the media. 32 | final int height; 33 | 34 | /// {@template image_size_getter.Size.needToRotate} 35 | /// 36 | /// If the [needRotate] is true, 37 | /// the [width] and [height] need to be swapped when using. 38 | /// 39 | /// Such as, orientation value of the jpeg format is `[5, 6, 7, 8]`. 40 | /// 41 | /// {@endtemplate} 42 | final bool needRotate; 43 | 44 | /// The [width] is zero and [height] is zero. 45 | static Size zero = Size(0, 0); 46 | 47 | @override 48 | String toString() { 49 | return "Size( $width, $height, needRotate: $needRotate )"; 50 | } 51 | 52 | @override 53 | bool operator ==(Object obj) { 54 | if (identical(obj, this)) { 55 | return true; 56 | } 57 | 58 | if (obj is Size) { 59 | return width == obj.width && 60 | height == obj.height && 61 | needRotate == obj.needRotate; 62 | } 63 | 64 | return false; 65 | } 66 | 67 | @override 68 | int get hashCode => hashValues(width, height); 69 | } 70 | 71 | /// {@template image_size_getter.SizeResult} 72 | /// 73 | /// [SizeResult] is a class for image size result. 74 | /// 75 | /// The result contains [size] and [decoder]. 76 | /// 77 | /// {@endtemplate} 78 | class SizeResult { 79 | /// {@macro image_size_getter.SizeResult} 80 | const SizeResult({ 81 | required this.size, 82 | required this.decoder, 83 | }); 84 | 85 | /// The size of the media. 86 | /// 87 | /// See [Size]. 88 | final Size size; 89 | 90 | /// The decoder of the media. 91 | /// 92 | /// See [BaseDecoder]. 93 | final BaseDecoder decoder; 94 | } 95 | -------------------------------------------------------------------------------- /packages/image_size_getter/lib/src/image_size_getter.dart: -------------------------------------------------------------------------------- 1 | import 'dart:collection'; 2 | 3 | import 'package:image_size_getter/image_size_getter.dart'; 4 | 5 | export 'core/input.dart'; 6 | 7 | /// {@template image_size_getter._DecoderContainer} 8 | /// 9 | /// [_DecoderContainer] is a container for [BaseDecoder]s. 10 | /// 11 | /// {@endtemplate} 12 | class _DecoderContainer extends IterableBase { 13 | /// {@macro image_size_getter._DecoderContainer} 14 | _DecoderContainer(List decoders) { 15 | for (final decoder in decoders) { 16 | _decoders[decoder.decoderName] = decoder; 17 | } 18 | } 19 | 20 | /// The [BaseDecoder]s. 21 | final Map _decoders = {}; 22 | 23 | /// {@template image_size_getter._DecoderContainer.register} 24 | /// 25 | /// Registers a [BaseDecoder] to the container. 26 | /// 27 | /// If the [BaseDecoder] is already registered, it will be replaced. 28 | /// 29 | /// {@endtemplate} 30 | void registerDecoder(BaseDecoder decoder) { 31 | _decoders[decoder.decoderName] = decoder; 32 | } 33 | 34 | @override 35 | Iterator get iterator => _decoders.values.iterator; 36 | } 37 | 38 | /// The instance of [_DecoderContainer]. 39 | /// 40 | /// This instance is used to register [BaseDecoder]s, it will be used by [ImageSizeGetter]. 41 | final _decoders = _DecoderContainer([ 42 | const GifDecoder(), 43 | const JpegDecoder(), 44 | const JpegDecoder(isStandardJpeg: false), 45 | const WebpDecoder(), 46 | const PngDecoder(), 47 | const BmpDecoder(), 48 | ]); 49 | 50 | /// {@template image_size_getter.ImageSizeGetter} 51 | /// 52 | /// The main class of [ImageSizeGetter]. 53 | /// 54 | /// Simple example: 55 | /// 56 | /// ```dart 57 | /// import 'dart:io'; 58 | /// 59 | /// import 'package:image_size_getter/image_size_getter.dart'; 60 | /// import 'package:image_size_getter/file_input.dart'; // For compatibility with flutter web. 61 | /// 62 | /// void main(List arguments) async { 63 | /// final file = File('asset/IMG_20180908_080245.jpg'); 64 | /// final size = ImageSizeGetter.getSize(FileInput(file)); 65 | /// print('jpg size: $size'); 66 | /// } 67 | /// ``` 68 | /// 69 | /// {@endtemplate} 70 | class ImageSizeGetter { 71 | /// {@macro image_size_getter._DecoderContainer.register} 72 | static void registerDecoder(BaseDecoder decoder) { 73 | _decoders.registerDecoder(decoder); 74 | } 75 | 76 | /// Returns the [input] is png format or not. 77 | /// 78 | /// See also: [PngDecoder.isValid] or [PngDecoder.isValidAsync]. 79 | static bool isPng(ImageInput input) { 80 | return PngDecoder().isValid(input); 81 | } 82 | 83 | /// Returns the [input] is webp format or not. 84 | /// 85 | /// See also: [WebpDecoder.isValid] or [WebpDecoder.isValidAsync]. 86 | static bool isWebp(ImageInput input) { 87 | return WebpDecoder().isValid(input); 88 | } 89 | 90 | /// Returns the [input] is gif format or not. 91 | /// 92 | /// See also: [GifDecoder.isValid] or [GifDecoder.isValidAsync]. 93 | static bool isGif(ImageInput input) { 94 | return GifDecoder().isValid(input); 95 | } 96 | 97 | /// Returns the [input] is jpeg format or not. 98 | /// 99 | /// See also: [JpegDecoder.isValid] or [JpegDecoder.isValidAsync]. 100 | static bool isJpg(ImageInput input) { 101 | return JpegDecoder().isValid(input); 102 | } 103 | 104 | /// {@template image_size_getter.getSize} 105 | /// 106 | /// Get the size of the [input]. 107 | /// 108 | /// If the [input] not exists, it will throw [StateError]. 109 | /// 110 | /// If the [input] is not a valid image format, it will throw [UnsupportedError]. 111 | /// 112 | /// {@endtemplate} 113 | @Deprecated( 114 | 'Use getSizeResult instead. This method will be removed in the next major version.') 115 | static Size getSize(ImageInput input) { 116 | if (!input.exists()) { 117 | throw StateError('The input is not exists.'); 118 | } 119 | 120 | for (var value in _decoders) { 121 | if (value.isValid(input)) { 122 | return value.getSize(input); 123 | } 124 | } 125 | 126 | throw UnsupportedError('The input is not supported.'); 127 | } 128 | 129 | /// {@macro image_size_getter.getSize} 130 | /// 131 | /// The method is async. 132 | @Deprecated( 133 | 'Use getSizeResultAsync instead. This method will be removed in the next major version.') 134 | static Future getSizeAsync(AsyncImageInput input) async { 135 | if (!await input.exists()) { 136 | throw StateError('The input is not exists.'); 137 | } 138 | 139 | if (!(await input.supportRangeLoad())) { 140 | final delegateInput = await input.delegateInput(); 141 | try { 142 | return ImageSizeGetter.getSize(delegateInput); 143 | } finally { 144 | delegateInput.release(); 145 | } 146 | } 147 | 148 | for (var value in _decoders) { 149 | if (await value.isValidAsync(input)) { 150 | return value.getSizeAsync(input); 151 | } 152 | } 153 | 154 | throw UnsupportedError('The input is not supported.'); 155 | } 156 | 157 | /// {@template image_size_getter.getSizeResult} 158 | /// Get the size of the [input] and the [BaseDecoder] that decodes the [input]. 159 | /// {@endtemplate} 160 | static SizeResult getSizeResult(ImageInput input) { 161 | if (!input.exists()) { 162 | throw StateError('The input is not exists.'); 163 | } 164 | 165 | for (var value in _decoders) { 166 | if (value.isValid(input)) { 167 | return SizeResult(size: value.getSize(input), decoder: value); 168 | } 169 | } 170 | 171 | throw UnsupportedError('The input is not supported.'); 172 | } 173 | 174 | /// {@macro image_size_getter.getSizeResult} 175 | /// 176 | /// The method is async version for [getSizeResult]. 177 | static Future getSizeResultAsync(AsyncImageInput input) async { 178 | if (!await input.exists()) { 179 | throw StateError('The input is not exists.'); 180 | } 181 | 182 | if (!(await input.supportRangeLoad())) { 183 | final delegateInput = await input.delegateInput(); 184 | try { 185 | return ImageSizeGetter.getSizeResult(delegateInput); 186 | } finally { 187 | delegateInput.release(); 188 | } 189 | } 190 | 191 | for (var value in _decoders) { 192 | if (await value.isValidAsync(input)) { 193 | return SizeResult( 194 | size: await value.getSizeAsync(input), 195 | decoder: value, 196 | ); 197 | } 198 | } 199 | 200 | throw UnsupportedError('The input is not supported.'); 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /packages/image_size_getter/lib/src/utils/file_utils.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | /// {@template image_size_getter.FileUtils} 4 | /// 5 | /// [FileUtils] is a class for file operations. 6 | /// 7 | /// {@endtemplate} 8 | class FileUtils { 9 | /// {@macro image_size_getter.FileUtils} 10 | FileUtils(this.file); 11 | 12 | /// The file. 13 | File file; 14 | 15 | /// {@macro image_size_getter.FileUtils.getRangeSync} 16 | Future> getRange(int start, int end) async { 17 | return getRangeSync(start, end); 18 | } 19 | 20 | /// {@template image_size_getter.FileUtils.getRangeSync} 21 | /// 22 | /// Get the range of bytes from [start] to [end]. 23 | /// 24 | /// {@endtemplate} 25 | List getRangeSync(int start, int end) { 26 | final accessFile = file.openSync(); 27 | try { 28 | accessFile.setPositionSync(start); 29 | return accessFile.readSync(end - start).toList(); 30 | } finally { 31 | accessFile.closeSync(); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/image_size_getter/melos_image_size_getter.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /packages/image_size_getter/migrate.md: -------------------------------------------------------------------------------- 1 | # Migrate 2 | 3 | ## 1.x.x To 2.0.0 4 | 5 | Remove `AsyncImageSizeGetter`. 6 | 7 | Now, We use `ImageSizeGetter.getSizeAsync(AsyncImageInput)` to get image size. 8 | 9 | ## 1.0.x To 1.1.0 10 | 11 | Usually no change. 12 | 13 | However, if you use `AsyncImageSizeGetter` or `AsyncImageInput`, you need to implement 2 new methods. 14 | 15 | ## 0.x To 1.x 16 | 17 | The version is null-safety version. 18 | 19 | The `ImageSizGetter` typo will fixed, please use `ImageSizeGetter` or `AsyncImageSizeGetter`. 20 | 21 | ## 0.2.x To 0.3.x 22 | 23 | This version only completes the part that was not completed last time, that is. 24 | 25 | Use `AsyncImageInput` and `AsyncImageSizeGetter` to get image size of async image. 26 | 27 | ## 0.1.x To 0.2.x 28 | 29 | Replace `FileInput` to `File` 30 | 31 | old: 32 | 33 | ```dart 34 | File file = File("asset/IMG_20180908_080245.jpg"); 35 | final size = ImageSizGetter.getSize(file); 36 | print('jpg = $size'); 37 | ``` 38 | 39 | new: 40 | 41 | ```dart 42 | final file = File('asset/IMG_20180908_080245.jpg'); 43 | final size = ImageSizGetter.getSize(FileInput(file)); 44 | print('jpg = $size'); 45 | ``` 46 | -------------------------------------------------------------------------------- /packages/image_size_getter/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: image_size_getter 2 | description: Get image width and height, the library does not completely decode the image file, just read the metadata to get the image width and height. 3 | version: 2.4.0 4 | homepage: https://github.com/CaiJingLong/dart_image_size_getter 5 | 6 | topics: 7 | - image 8 | - metadata 9 | - size 10 | - performance 11 | 12 | environment: 13 | sdk: ">=2.12.0 <4.0.0" 14 | 15 | dependencies: 16 | collection: ^1.15.0 17 | hashcodes: ^2.0.0 18 | meta: ^1.9.0 19 | 20 | dev_dependencies: 21 | pedantic: ^1.11.0 22 | test: ^1.16.8 23 | -------------------------------------------------------------------------------- /packages/image_size_getter/test/decoder_extension_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:image_size_getter/image_size_getter.dart'; 2 | import 'package:test/test.dart'; 3 | 4 | void main() { 5 | group('Test decoder extension', () { 6 | test('Test gif decoder', () { 7 | final GifDecoder decoder = GifDecoder(); 8 | expect(decoder.isExtensionSupported('gif'), true); 9 | expect(decoder.isExtensionSupported('GIF'), true); 10 | 11 | expect(decoder.isExtensionSupported('jpg'), false); 12 | expect(decoder.isExtensionSupported('JPG'), false); 13 | }); 14 | 15 | test('Test jpeg decoder', () { 16 | final JpegDecoder decoder = JpegDecoder(); 17 | expect(decoder.isExtensionSupported('jpg'), true); 18 | expect(decoder.isExtensionSupported('JPG'), true); 19 | expect(decoder.isExtensionSupported('jpeg'), true); 20 | expect(decoder.isExtensionSupported('JPEG'), true); 21 | 22 | expect(decoder.isExtensionSupported('gif'), false); 23 | expect(decoder.isExtensionSupported('GIF'), false); 24 | }); 25 | 26 | test('Test png decoder', () { 27 | final PngDecoder decoder = PngDecoder(); 28 | expect(decoder.isExtensionSupported('png'), true); 29 | expect(decoder.isExtensionSupported('PNG'), true); 30 | 31 | expect(decoder.isExtensionSupported('jpg'), false); 32 | expect(decoder.isExtensionSupported('JPG'), false); 33 | }); 34 | 35 | test('Test webp decoder', () { 36 | final WebpDecoder decoder = WebpDecoder(); 37 | expect(decoder.isExtensionSupported('webp'), true); 38 | expect(decoder.isExtensionSupported('WEBP'), true); 39 | 40 | expect(decoder.isExtensionSupported('jpg'), false); 41 | expect(decoder.isExtensionSupported('JPG'), false); 42 | }); 43 | 44 | test('Test bmp decoder', () { 45 | final BmpDecoder decoder = BmpDecoder(); 46 | expect(decoder.isExtensionSupported('bmp'), true); 47 | expect(decoder.isExtensionSupported('BMP'), true); 48 | 49 | expect(decoder.isExtensionSupported('jpg'), false); 50 | expect(decoder.isExtensionSupported('JPG'), false); 51 | }); 52 | }); 53 | } 54 | -------------------------------------------------------------------------------- /packages/image_size_getter/test/image_size_getter_test.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: deprecated_member_use_from_same_package 2 | 3 | import 'dart:io'; 4 | 5 | import 'package:image_size_getter/file_input.dart'; 6 | import 'package:image_size_getter/image_size_getter.dart'; 7 | import 'package:test/test.dart'; 8 | 9 | void main() { 10 | group('Test decoders', () { 11 | test('Test gif decoder', () { 12 | final gif = File('../../example/asset/dialog.gif'); 13 | 14 | const GifDecoder decoder = GifDecoder(); 15 | final input = FileInput(gif); 16 | 17 | assert(decoder.isValid(input)); 18 | expect(decoder.getSize(input), Size(688, 1326)); 19 | }); 20 | 21 | test('Test jpeg decoder', () { 22 | final jpeg = File('../../example/asset/IMG_20180908_080245.jpg'); 23 | 24 | const JpegDecoder decoder = JpegDecoder(); 25 | final input = FileInput(jpeg); 26 | 27 | assert(decoder.isValid(input)); 28 | expect(decoder.getSize(input), Size(4032, 3024)); 29 | }); 30 | 31 | test('Test non-standard jpeg decoder', () { 32 | final jpeg = File('../../example/asset/test.MP.jpg'); 33 | 34 | const JpegDecoder decoder = JpegDecoder(isStandardJpeg: false); 35 | final input = FileInput(jpeg); 36 | 37 | assert(decoder.isValid(input)); 38 | expect(decoder.getSize(input), Size(3840, 2160, needRotate: true)); 39 | }); 40 | 41 | test('Test png decoder', () { 42 | final png = File('../../example/asset/ic_launcher.png'); 43 | 44 | const PngDecoder decoder = PngDecoder(); 45 | final input = FileInput(png); 46 | 47 | assert(decoder.isValid(input)); 48 | expect(decoder.getSize(input), Size(96, 96)); 49 | }); 50 | 51 | test('Test webp decoder', () { 52 | final webp = File('../../example/asset/demo.webp'); 53 | 54 | const WebpDecoder decoder = WebpDecoder(); 55 | final input = FileInput(webp); 56 | 57 | assert(decoder.isValid(input)); 58 | expect(decoder.getSize(input), Size(988, 466)); 59 | }); 60 | 61 | test('Test bmp decoder', () { 62 | final bmp = File('../../example/asset/demo.bmp'); 63 | 64 | const BmpDecoder decoder = BmpDecoder(); 65 | final input = FileInput(bmp); 66 | 67 | assert(decoder.isValid(input)); 68 | expect(decoder.getSize(input), Size(256, 256)); 69 | }); 70 | 71 | test('Test have orientation jpeg', () { 72 | final orientation3 = 73 | File('../../example/asset/have_orientation_exif_3.jpg'); 74 | 75 | const JpegDecoder decoder = JpegDecoder(); 76 | final input = FileInput(orientation3); 77 | 78 | assert(decoder.isValid(input)); 79 | expect(decoder.getSize(input), Size(533, 799)); 80 | 81 | final orientation6 = 82 | File('../../example/asset/have_orientation_exif_6.jpg'); 83 | final input2 = FileInput(orientation6); 84 | 85 | assert(decoder.isValid(input2)); 86 | final size = decoder.getSize(input2); 87 | expect(size, Size(3264, 2448, needRotate: true)); 88 | }); 89 | }); 90 | 91 | group('Test get size.', () { 92 | test('Test webp size', () async { 93 | final file = File('../../example/asset/demo.webp'); 94 | final size = ImageSizeGetter.getSize(FileInput(file)); 95 | print('size = $size'); 96 | await expectLater(size, Size(988, 466)); 97 | 98 | final result = ImageSizeGetter.getSizeResult(FileInput(file)); 99 | print('size = ${result.size} (decoded by ${result.decoder.decoderName})'); 100 | }); 101 | 102 | test('Test webp extended format size', () async { 103 | final file = File('../../example/asset/demo_extended.webp'); 104 | final size = ImageSizeGetter.getSize(FileInput(file)); 105 | print('size = $size'); 106 | await expectLater(size, Size(988, 466)); 107 | 108 | final result = ImageSizeGetter.getSizeResult(FileInput(file)); 109 | print('size = ${result.size} (decoded by ${result.decoder.decoderName})'); 110 | }); 111 | 112 | test('Test webp lossless format size', () async { 113 | final file = File('../../example/asset/demo_lossless.webp'); 114 | final size = ImageSizeGetter.getSize(FileInput(file)); 115 | print('size = $size'); 116 | await expectLater(size, Size(988, 466)); 117 | 118 | final result = ImageSizeGetter.getSizeResult(FileInput(file)); 119 | print('size = ${result.size} (decoded by ${result.decoder.decoderName})'); 120 | }); 121 | 122 | test('Test jpeg size', () async { 123 | final file = File('../../example/asset/IMG_20180908_080245.jpg'); 124 | final size = ImageSizeGetter.getSize(FileInput(file)); 125 | print('size = $size'); 126 | await expectLater(size, Size(4032, 3024)); 127 | 128 | final result = ImageSizeGetter.getSizeResult(FileInput(file)); 129 | print('size = ${result.size} (decoded by ${result.decoder.decoderName})'); 130 | }); 131 | 132 | test('Test non-standard jpeg size', () async { 133 | final file = File('../../example/asset/test.MP.jpg'); 134 | final size = ImageSizeGetter.getSize(FileInput(file)); 135 | print('size = $size'); 136 | await expectLater(size, Size(3840, 2160, needRotate: true)); 137 | 138 | final result = ImageSizeGetter.getSizeResult(FileInput(file)); 139 | print('size = ${result.size} (decoded by ${result.decoder.decoderName})'); 140 | }); 141 | 142 | group('Test gif size', () { 143 | test('89a', () async { 144 | final file = File('../../example/asset/dialog.gif'); 145 | final size = ImageSizeGetter.getSize(FileInput(file)); 146 | print('size = $size'); 147 | await expectLater(size, Size(688, 1326)); 148 | 149 | final result = ImageSizeGetter.getSizeResult(FileInput(file)); 150 | print( 151 | 'size = ${result.size} (decoded by ${result.decoder.decoderName})'); 152 | }); 153 | 154 | test('87a', () async { 155 | final file = File('../../example/asset/87a.gif'); 156 | final size = ImageSizeGetter.getSize(FileInput(file)); 157 | print('size = $size'); 158 | await expectLater(size, Size(200, 150)); 159 | 160 | final result = ImageSizeGetter.getSizeResult(FileInput(file)); 161 | print( 162 | 'size = ${result.size} (decoded by ${result.decoder.decoderName})'); 163 | }); 164 | }); 165 | 166 | test('Test png size', () async { 167 | final file = File('../../example/asset/ic_launcher.png'); 168 | final size = ImageSizeGetter.getSize(FileInput(file)); 169 | print('size = $size'); 170 | await expectLater(size, Size(96, 96)); 171 | 172 | final result = ImageSizeGetter.getSizeResult(FileInput(file)); 173 | print('size = ${result.size} (decoded by ${result.decoder.decoderName})'); 174 | }); 175 | 176 | test('Test png size with memory', () async { 177 | final file = File('../../example/asset/ic_launcher.png'); 178 | final bytes = file.readAsBytesSync(); 179 | final size = ImageSizeGetter.getSize(MemoryInput(bytes)); 180 | print('size = $size'); 181 | await expectLater(size, Size(96, 96)); 182 | 183 | final result = ImageSizeGetter.getSizeResult(MemoryInput(bytes)); 184 | print('size = ${result.size} (decoded by ${result.decoder.decoderName})'); 185 | }); 186 | }); 187 | } 188 | -------------------------------------------------------------------------------- /packages/image_size_getter_heic/.gitignore: -------------------------------------------------------------------------------- 1 | # Files and directories created by pub. 2 | .dart_tool/ 3 | .packages 4 | 5 | # Conventional directory for build outputs. 6 | build/ 7 | 8 | # Omit committing pubspec.lock for library packages; see 9 | # https://dart.dev/guides/libraries/private-files#pubspeclock. 10 | pubspec.lock 11 | 12 | doc/ -------------------------------------------------------------------------------- /packages/image_size_getter_heic/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // 使用 IntelliSense 了解相关属性。 3 | // 悬停以查看现有属性的描述。 4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "image_size_getter_heic", 9 | "request": "launch", 10 | "type": "dart" 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /packages/image_size_getter_heic/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## 1.1.0 4 | 5 | - Update dart version to `>=2.12.0 <4.0.0`. 6 | 7 | ## 1.0.1+1 8 | 9 | - Docs: Add docs for dartdoc. 10 | 11 | ## 1.0.1 12 | 13 | - Downgrade dart version to 2.12.0 14 | 15 | ## 1.0.0 16 | 17 | - Initial version. 18 | -------------------------------------------------------------------------------- /packages/image_size_getter_heic/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [2023] [Caijinglong] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /packages/image_size_getter_heic/README.md: -------------------------------------------------------------------------------- 1 | # Heic support 2 | 3 | heic support for [image_size_getter](https://pub.dev/packages/image_size_getter) 4 | 5 | ## Installation 6 | 7 | Add this to your package's pubspec.yaml file: 8 | 9 | ```yaml 10 | dependencies: 11 | image_size_getter_heic: ^1.0.0 12 | ``` 13 | 14 | ## Usage 15 | 16 | ```dart 17 | import 'dart:io'; 18 | 19 | import 'package:image_size_getter/file_input.dart'; 20 | import 'package:image_size_getter_heic/image_size_getter_heic.dart'; 21 | 22 | void main() { 23 | final decoder = HeicDecoder(); 24 | ImageSizeGetter.registerDecoder(decoder); 25 | 26 | final input = FileInput(File('example/asset/example.heic')); 27 | final size = ImageSizeGetter.getSize(input); 28 | 29 | print(size); 30 | } 31 | 32 | ``` 33 | 34 | ## License 35 | 36 | [APACHE LICENSE, VERSION 2.0](https://github.com/CaiJingLong/dart_image_size_getter/blob/main/packages/image_size_getter_heic/LICENSE) 37 | -------------------------------------------------------------------------------- /packages/image_size_getter_heic/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the static analysis results for your project (errors, 2 | # warnings, and lints). 3 | # 4 | # This enables the 'recommended' set of lints from `package:lints`. 5 | # This set helps identify many issues that may lead to problems when running 6 | # or consuming Dart code, and enforces writing Dart using a single, idiomatic 7 | # style and format. 8 | # 9 | # If you want a smaller set of lints you can change this to specify 10 | # 'package:lints/core.yaml'. These are just the most critical lints 11 | # (the recommended set includes the core lints). 12 | # The core lints are also what is used by pub.dev for scoring packages. 13 | 14 | include: package:lints/recommended.yaml 15 | 16 | # Uncomment the following section to specify additional rules. 17 | 18 | # linter: 19 | # rules: 20 | # - camel_case_types 21 | 22 | # analyzer: 23 | # exclude: 24 | # - path/to/excluded/files/** 25 | 26 | # For more information about the core and recommended set of lints, see 27 | # https://dart.dev/go/core-lints 28 | 29 | # For additional information about configuring this file, see 30 | # https://dart.dev/guides/language/analysis-options 31 | -------------------------------------------------------------------------------- /packages/image_size_getter_heic/example/asset/example.heic: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercandies/dart_image_size_getter/b2350448d0bda758bfe2407e05a729f1590bb300/packages/image_size_getter_heic/example/asset/example.heic -------------------------------------------------------------------------------- /packages/image_size_getter_heic/example/image_size_getter_heic_example.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:image_size_getter/file_input.dart'; 4 | import 'package:image_size_getter_heic/image_size_getter_heic.dart'; 5 | 6 | void main() { 7 | final decoder = HeicDecoder(); 8 | ImageSizeGetter.registerDecoder(decoder); 9 | 10 | final input = FileInput(File('example/asset/example.heic')); 11 | final size = ImageSizeGetter.getSizeResult(input); 12 | 13 | print('heic size: ${size.size} (decoded by ${size.decoder.decoderName})'); 14 | } 15 | -------------------------------------------------------------------------------- /packages/image_size_getter_heic/lib/image_size_getter_heic.dart: -------------------------------------------------------------------------------- 1 | /// Support for doing something awesome. 2 | /// 3 | /// More dartdocs go here. 4 | library image_size_getter_heic; 5 | 6 | export 'src/image_size_getter_heic_base.dart'; 7 | export 'package:image_size_getter/image_size_getter.dart'; 8 | -------------------------------------------------------------------------------- /packages/image_size_getter_heic/lib/src/context.dart: -------------------------------------------------------------------------------- 1 | import 'package:bmff/bmff.dart'; 2 | import 'package:image_size_getter/image_size_getter.dart'; 3 | import 'package:image_size_getter_heic/src/types.dart'; 4 | 5 | /// A context implementation for BMFF (ISO Base Media File Format) parsing 6 | /// that works with [ImageInput]. 7 | /// 8 | /// This class provides the necessary context for parsing HEIC images by 9 | /// implementing the [BmffContext] interface and using an [ImageInput] 10 | /// as the data source. 11 | class BmffImageContext extends BmffContext { 12 | /// The input source containing the image data. 13 | final ImageInput input; 14 | 15 | /// Creates a new [BmffImageContext] instance. 16 | /// 17 | /// [input] is the source containing the image data. 18 | /// [fullBoxTypes] is an optional list of box types that should be treated 19 | /// as full boxes when parsing. Defaults to [defaultFullBoxTypes]. 20 | BmffImageContext( 21 | this.input, { 22 | List fullBoxTypes = defaultFullBoxTypes, 23 | }) : super(fullBoxTypes: fullBoxTypes); 24 | 25 | /// No-op implementation as [ImageInput] doesn't require explicit closing. 26 | @override 27 | void close() {} 28 | 29 | /// Gets a range of bytes from the input data. 30 | /// 31 | /// [start] is the starting index (inclusive). 32 | /// [end] is the ending index (exclusive). 33 | /// 34 | /// Returns a list of integers representing the bytes in the specified range. 35 | @override 36 | List getRangeData(int start, int end) { 37 | return input.getRange(start, end); 38 | } 39 | 40 | /// Gets the total length of the input data. 41 | @override 42 | int get length => input.length; 43 | } 44 | -------------------------------------------------------------------------------- /packages/image_size_getter_heic/lib/src/image_size_getter_heic_base.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:image_size_getter_heic/image_size_getter_heic.dart'; 4 | import 'package:bmff/bmff.dart'; 5 | 6 | import 'context.dart'; 7 | import 'types.dart'; 8 | 9 | /// {@template image_size_getter_heic.HeicDecoder} 10 | /// A decoder for HEIC (High Efficiency Image Format) images. 11 | /// 12 | /// This decoder implements the [BaseDecoder] to extract size information from HEIC 13 | /// image files by parsing their BMFF (ISO Base Media File Format) structure. 14 | /// 15 | /// Example: 16 | /// ```dart 17 | /// final decoder = HeicDecoder(); 18 | /// final size = decoder.getSize(input); 19 | /// print('Width: ${size.width}, Height: ${size.height}'); 20 | /// ``` 21 | /// {@endtemplate} 22 | class HeicDecoder extends BaseDecoder { 23 | /// Creates a new [HeicDecoder] instance. 24 | /// 25 | /// [fullTypeBox] is an optional list of box types that should be treated as full boxes 26 | /// when parsing the BMFF structure. If not provided, [defaultFullBoxTypes] will be used. 27 | HeicDecoder({this.fullTypeBox = defaultFullBoxTypes}); 28 | 29 | /// List of box types that should be treated as full boxes when parsing the BMFF structure. 30 | final List fullTypeBox; 31 | 32 | /// The name identifier for this decoder. 33 | /// 34 | /// Always returns 'heic'. 35 | @override 36 | String get decoderName => 'heic'; 37 | 38 | @override 39 | List get supportedExtensions => 40 | List.unmodifiable(['heic', 'heics', 'heif', 'heifs']); 41 | 42 | /// Extracts the size information from a HEIC image synchronously. 43 | /// 44 | /// This method parses the BMFF structure of the HEIC file to find the 'ispe' box 45 | /// which contains the image size information. 46 | /// 47 | /// Returns a [Size] object containing the width and height of the image. 48 | /// 49 | /// Throws a [FormatException] if the input is not a valid HEIC image. 50 | @override 51 | Size getSize(ImageInput input) { 52 | final bmff = BmffImageContext(input, fullBoxTypes: fullTypeBox).bmff; 53 | final buffer = bmff['meta']['iprp']['ipco']['ispe'].getByteBuffer(); 54 | 55 | final width = buffer.getUint32(0, Endian.big); 56 | final height = buffer.getUint32(1, Endian.big); 57 | 58 | return Size(width, height); 59 | } 60 | 61 | /// Extracts the size information from a HEIC image asynchronously. 62 | /// 63 | /// This method creates an async BMFF context and parses the structure to find 64 | /// the 'ispe' box which contains the image size information. 65 | /// 66 | /// Returns a Future that completes with a [Size] object containing the width 67 | /// and height of the image. 68 | /// 69 | /// Throws a [FormatException] if the input is not a valid HEIC image. 70 | @override 71 | Future getSizeAsync(AsyncImageInput input) async { 72 | final context = AsyncBmffContext.common( 73 | () { 74 | return input.length; 75 | }, 76 | (start, end) => input.getRange(start, end), 77 | fullBoxTypes: fullTypeBox, 78 | ); 79 | 80 | final bmff = await Bmff.asyncContext(context); 81 | final ispe = bmff['meta']['iprp']['ipco']['ispe']; 82 | 83 | final buffer = await ispe.getByteBuffer(); 84 | 85 | final width = buffer.getUint32(0, Endian.big); 86 | final height = buffer.getUint32(1, Endian.big); 87 | 88 | return Size(width, height); 89 | } 90 | 91 | /// Checks if the input is a valid HEIC image synchronously. 92 | /// 93 | /// Returns `true` if the input is a valid HEIC image, `false` otherwise. 94 | @override 95 | bool isValid(ImageInput input) { 96 | final bmff = BmffImageContext(input, fullBoxTypes: fullTypeBox).bmff; 97 | return _checkHeic(bmff); 98 | } 99 | 100 | /// Checks if the input is a valid HEIC image asynchronously. 101 | /// 102 | /// Returns a Future that completes with `true` if the input is a valid HEIC image, 103 | /// `false` otherwise. 104 | @override 105 | Future isValidAsync(AsyncImageInput input) async { 106 | final lengthBytes = await input.getRange(0, 4); 107 | final length = lengthBytes.toBigEndian(); 108 | final typeBoxBytes = await input.getRange(0, length); 109 | 110 | final bmff = Bmff.memory(typeBoxBytes); 111 | return _checkHeic(bmff); 112 | } 113 | 114 | /// Checks if the BMFF structure is a valid HEIC image. 115 | /// 116 | /// Returns `true` if the BMFF structure is a valid HEIC image, `false` otherwise. 117 | bool _checkHeic(Bmff bmff) { 118 | final typeBox = bmff.typeBox; 119 | final compatibleBrands = typeBox.compatibleBrands; 120 | return compatibleBrands.contains('heic'); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /packages/image_size_getter_heic/lib/src/types.dart: -------------------------------------------------------------------------------- 1 | /// Default list of box types that should be treated as full boxes when parsing 2 | /// HEIC files. 3 | /// 4 | /// A full box is a box that contains a version and flags field in addition to 5 | /// its normal content. This list includes: 6 | /// - 'meta': The metadata container box 7 | /// - 'ispe': The image spatial extents box, containing width and height 8 | const List defaultFullBoxTypes = [ 9 | 'meta', 10 | // 'hdlr', 11 | // 'pitm', 12 | // 'iloc', 13 | // 'iinf', 14 | // 'infe', 15 | // 'iref', 16 | 'ispe', 17 | ]; 18 | -------------------------------------------------------------------------------- /packages/image_size_getter_heic/melos_image_size_getter_heic.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /packages/image_size_getter_heic/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: image_size_getter_heic 2 | description: A decoder for HEIC images with image_size_getter. 3 | version: 1.1.0 4 | homepage: https://github.com/CaiJingLong/dart_image_size_getter 5 | 6 | environment: 7 | sdk: ">=2.12.0 <4.0.0" 8 | 9 | dependencies: 10 | image_size_getter: ^2.0.0 11 | bmff: ^1.4.2 12 | 13 | dev_dependencies: 14 | lints: ^1.0.0 15 | test: ^1.16.0 16 | -------------------------------------------------------------------------------- /packages/image_size_getter_heic/pubspec_overrides.yaml: -------------------------------------------------------------------------------- 1 | # melos_managed_dependency_overrides: image_size_getter 2 | dependency_overrides: 3 | image_size_getter: 4 | path: ../image_size_getter 5 | -------------------------------------------------------------------------------- /packages/image_size_getter_heic/test/image_size_getter_heic_test.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:image_size_getter/file_input.dart'; 4 | import 'package:image_size_getter_heic/image_size_getter_heic.dart'; 5 | import 'package:test/test.dart'; 6 | 7 | void main() { 8 | group('Test heic decoder', () { 9 | final decoder = HeicDecoder(); 10 | final input = FileInput(File('example/asset/example.heic')); 11 | test('sync', () { 12 | expect(decoder.isValid(input), equals(true)); 13 | }); 14 | 15 | test('async', () { 16 | final asyncInput = AsyncImageInput.input(input); 17 | expect(decoder.isValidAsync(asyncInput), completion(equals(true))); 18 | }); 19 | 20 | test('extension', () { 21 | expect(decoder.isExtensionSupported('heic'), equals(true)); 22 | expect(decoder.isExtensionSupported('HEIC'), equals(true)); 23 | expect(decoder.isExtensionSupported('heif'), equals(true)); 24 | expect(decoder.isExtensionSupported('HEIF'), equals(true)); 25 | 26 | expect(decoder.isExtensionSupported('jpg'), equals(false)); 27 | expect(decoder.isExtensionSupported('JPG'), equals(false)); 28 | }); 29 | }); 30 | 31 | group('Test get heic size', () { 32 | final decoder = HeicDecoder(); 33 | 34 | ImageSizeGetter.registerDecoder(decoder); 35 | 36 | final input = FileInput(File('example/asset/example.heic')); 37 | test('sync', () async { 38 | expect(decoder.getSize(input), equals(Size(1440, 960))); 39 | }); 40 | 41 | test('async', () async { 42 | final asyncInput = AsyncImageInput.input(input); 43 | final size = await decoder.getSizeAsync(asyncInput); 44 | expect(size, equals(Size(1440, 960))); 45 | }); 46 | }); 47 | } 48 | -------------------------------------------------------------------------------- /packages/image_size_getter_http_input/.gitignore: -------------------------------------------------------------------------------- 1 | # Files and directories created by pub. 2 | .dart_tool/ 3 | .packages 4 | 5 | # Conventional directory for build outputs. 6 | build/ 7 | 8 | # Omit committing pubspec.lock for library packages; see 9 | # https://dart.dev/guides/libraries/private-files#pubspeclock. 10 | pubspec.lock 11 | 12 | doc -------------------------------------------------------------------------------- /packages/image_size_getter_http_input/.pubignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercandies/dart_image_size_getter/b2350448d0bda758bfe2407e05a729f1590bb300/packages/image_size_getter_http_input/.pubignore -------------------------------------------------------------------------------- /packages/image_size_getter_http_input/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## 2.1.0 4 | 5 | - Support library `2.4.0`. 6 | 7 | ## 2.0.1+1 8 | 9 | - Docs: Add docs for dartdoc. 10 | 11 | ## 2.0.1 12 | 13 | - Bump `http` version. 14 | 15 | ## 2.0.0 16 | 17 | - Support library 2.0.0. 18 | 19 | ## 1.0.0 20 | 21 | - Initial version. 22 | -------------------------------------------------------------------------------- /packages/image_size_getter_http_input/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [2022] [Caijinglong] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /packages/image_size_getter_http_input/README.md: -------------------------------------------------------------------------------- 1 | # http_input 2 | 3 | HttpInput for [image_size_getter](https://pub.dev/packages/image_size_getter) 4 | 5 | ## Example 6 | 7 | Tips: 8 | 9 | 1. normally, you should use the api provided by your application server or image server to get the image size instead of using this method. 10 | 11 | 1. If you don't have an application server or if the server can't provide the information, then there is another suggestion. 12 | - The server support range header and provide content-length header. 13 | - Image files larger than 5m or even 10m, as range fetching may require 5 ~ 20 interactions with the server to get the image size. 14 | 15 | Example: 16 | 17 | ```dart 18 | import 'package:image_size_getter/image_size_getter.dart'; 19 | import 'package:image_size_getter_http_input/image_size_getter_http_input.dart'; 20 | 21 | Future foo() async{ 22 | final testUrl = 23 | 'https://cdn.jsdelivr.net/gh/CaiJingLong/some_asset@master/flutter_photo2.png'; 24 | final httpInput = await HttpInput.createHttpInput(testUrl); 25 | 26 | final size = await ImageSizeGetter.getSizeAsync(httpInput); 27 | print('size: $size'); 28 | } 29 | 30 | ``` 31 | 32 | ### issues 33 | 34 | If you are using a non-web environment, then when you request a server that does not support range load, if you are worried about using too much memory, then you can use file as a cache, use `httpCachePath` to set it, or if not, it will use the memory cache. The cache file will be deleted automatically when the fetch size is done. 35 | 36 | ## LICENSE 37 | 38 | Apache License 2.0 39 | -------------------------------------------------------------------------------- /packages/image_size_getter_http_input/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the static analysis results for your project (errors, 2 | # warnings, and lints). 3 | # 4 | # This enables the 'recommended' set of lints from `package:lints`. 5 | # This set helps identify many issues that may lead to problems when running 6 | # or consuming Dart code, and enforces writing Dart using a single, idiomatic 7 | # style and format. 8 | # 9 | # If you want a smaller set of lints you can change this to specify 10 | # 'package:lints/core.yaml'. These are just the most critical lints 11 | # (the recommended set includes the core lints). 12 | # The core lints are also what is used by pub.dev for scoring packages. 13 | 14 | include: package:lints/recommended.yaml 15 | 16 | # Uncomment the following section to specify additional rules. 17 | 18 | # linter: 19 | # rules: 20 | # - camel_case_types 21 | 22 | # analyzer: 23 | # exclude: 24 | # - path/to/excluded/files/** 25 | 26 | # For more information about the core and recommended set of lints, see 27 | # https://dart.dev/go/core-lints 28 | 29 | # For additional information about configuring this file, see 30 | # https://dart.dev/guides/language/analysis-options 31 | -------------------------------------------------------------------------------- /packages/image_size_getter_http_input/assets/flutter_photo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercandies/dart_image_size_getter/b2350448d0bda758bfe2407e05a729f1590bb300/packages/image_size_getter_http_input/assets/flutter_photo2.png -------------------------------------------------------------------------------- /packages/image_size_getter_http_input/lib/image_size_getter_http_input.dart: -------------------------------------------------------------------------------- 1 | /// Support for doing something awesome. 2 | /// 3 | /// More dartdocs go here. 4 | library image_size_getter_http_input; 5 | 6 | export 'src/image_size_getter_http_input_base.dart'; 7 | 8 | // TODO: Export any libraries intended for clients of this package. 9 | -------------------------------------------------------------------------------- /packages/image_size_getter_http_input/lib/src/image_size_getter_http_input_base.dart: -------------------------------------------------------------------------------- 1 | import 'package:image_size_getter/image_size_getter.dart'; 2 | import 'stub_impl.dart' 3 | if (dart.library.html) 'web_delegate.dart' 4 | if (dart.library.io) 'io_delegate.dart'; 5 | 6 | import 'package:http/http.dart' as http; 7 | 8 | /// The path where HTTP responses will be cached. 9 | /// This can be set to customize the cache location. 10 | String httpCachePath = ''; 11 | 12 | /// An implementation of [AsyncImageInput] that reads image data from HTTP URLs. 13 | /// 14 | /// This class provides functionality to fetch and read image data from remote 15 | /// URLs using HTTP requests. It supports both web and IO platforms through 16 | /// conditional imports. 17 | class HttpInput extends AsyncImageInput { 18 | /// Creates a new [HttpInput] instance. 19 | /// 20 | /// This constructor is private. Use [createHttpInput] to create instances. 21 | HttpInput._(this.uri, this.headResponse); 22 | 23 | /// Creates a new [HttpInput] instance for the given URL. 24 | /// 25 | /// This factory method performs a HEAD request to the URL to get initial 26 | /// metadata about the resource. 27 | /// 28 | /// [url] is the URL of the image to fetch. 29 | /// 30 | /// Returns a Future that completes with a new [HttpInput] instance. 31 | /// Throws [FormatException] if the URL is invalid. 32 | static Future createHttpInput(String url) async { 33 | final uri = Uri.parse(url); 34 | final headResponse = await http.head(uri); 35 | return HttpInput._(uri, headResponse); 36 | } 37 | 38 | /// The URI of the image resource. 39 | final Uri uri; 40 | 41 | /// The response from the HEAD request to the image URL. 42 | final http.Response headResponse; 43 | 44 | /// Gets the HTTP headers from the HEAD response. 45 | /// 46 | /// Returns a map of HTTP headers. 47 | Map get headers => headResponse.headers; 48 | 49 | /// Delegates the input to a [HaveResourceImageInput] instance. 50 | /// 51 | /// This method creates a new [HaveResourceImageInput] instance and returns it. 52 | @override 53 | Future delegateInput() async { 54 | final input = await createDelegateInput(uri); 55 | final delegate = HaveResourceImageInput( 56 | innerInput: input.input, 57 | onRelease: () async { 58 | await input.onRelease(); 59 | }, 60 | ); 61 | return delegate; 62 | } 63 | 64 | /// Checks if the HTTP resource exists and is accessible. 65 | /// 66 | /// Returns true if the HEAD request was successful (status code 200). 67 | @override 68 | Future exists() async { 69 | return headResponse.statusCode == 200; 70 | } 71 | 72 | /// Gets a range of bytes from the image data. 73 | /// 74 | /// This method will fetch the specified range of bytes from the remote 75 | /// resource using a range request. 76 | /// 77 | /// [start] is the starting byte position. 78 | /// [end] is the ending byte position. 79 | /// 80 | /// Returns a Future that completes with the requested bytes. 81 | @override 82 | Future> getRange(int start, int end) { 83 | final partRequestHeaders = { 84 | 'Range': 'bytes=$start-${end - 1}', 85 | }; 86 | 87 | return http.get(uri, headers: partRequestHeaders).then((response) { 88 | return response.bodyBytes; 89 | }); 90 | } 91 | 92 | /// Gets the total length of the image data. 93 | /// 94 | /// This value is obtained from the Content-Length header of the HEAD response. 95 | @override 96 | Future get length async { 97 | try { 98 | return int.parse(headers['content-length'].toString()); 99 | } catch (e) { 100 | return 0; 101 | } 102 | } 103 | 104 | /// Checks if the HTTP resource supports range requests. 105 | /// 106 | /// Returns true if the Accept-Ranges header is 'bytes'. 107 | @override 108 | Future supportRangeLoad() async { 109 | return headers['accept-ranges'] == 'bytes'; 110 | } 111 | } 112 | 113 | /// Determines if the current platform is web. 114 | /// 115 | /// This is used internally to choose the appropriate implementation 116 | /// for HTTP requests. 117 | bool get kIsWeb => 0 == 0.0; 118 | 119 | /// A wrapper class that implements [ImageInput] using an [ImageInput]. 120 | /// 121 | /// This class is used internally to adapt async image inputs for synchronous 122 | /// operations when necessary. 123 | class ImageInputWrapper { 124 | /// The wrapped [ImageInput] instance. 125 | final ImageInput input; 126 | 127 | /// A callback to release resources when the wrapper is no longer needed. 128 | final Future Function() onRelease; 129 | 130 | /// Creates a new [ImageInputWrapper] instance. 131 | /// 132 | /// [input] is the [AsyncImageInput] instance to wrap. 133 | /// [onRelease] is the callback to release resources. 134 | ImageInputWrapper(this.input, this.onRelease); 135 | } 136 | -------------------------------------------------------------------------------- /packages/image_size_getter_http_input/lib/src/io_delegate.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:http/http.dart'; 4 | import 'package:image_size_getter/file_input.dart'; 5 | 6 | import 'image_size_getter_http_input_base.dart'; 7 | 8 | Future createDelegateInput(Uri uri) async { 9 | if (httpCachePath.isEmpty) { 10 | throw UnsupportedError( 11 | 'You must set httpCachePath before using http input.'); 12 | } 13 | 14 | String pathSplitter = '/'; 15 | 16 | if (Platform.isWindows) { 17 | pathSplitter = '\\'; 18 | } 19 | 20 | final response = await get(uri); 21 | final bodyBytes = response.bodyBytes; 22 | final file = File('$httpCachePath$pathSplitter${uri.pathSegments.last}'); 23 | await file.writeAsBytes(bodyBytes); 24 | 25 | return ImageInputWrapper( 26 | FileInput(file), 27 | () async { 28 | file.deleteSync(); 29 | }, 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /packages/image_size_getter_http_input/lib/src/stub_impl.dart: -------------------------------------------------------------------------------- 1 | import 'image_size_getter_http_input_base.dart'; 2 | 3 | Future createDelegateInput(Uri uri) => 4 | throw UnsupportedError( 5 | 'Cannot create a client without dart:html or dart:io.'); 6 | -------------------------------------------------------------------------------- /packages/image_size_getter_http_input/lib/src/web_delegate.dart: -------------------------------------------------------------------------------- 1 | import 'package:http/http.dart'; 2 | import 'package:image_size_getter/image_size_getter.dart'; 3 | 4 | import 'image_size_getter_http_input_base.dart'; 5 | 6 | Future createDelegateInput(Uri uri) async { 7 | final response = await get(uri); 8 | final bodyBytes = response.bodyBytes; 9 | return ImageInputWrapper( 10 | MemoryInput(bodyBytes), 11 | () async {}, 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /packages/image_size_getter_http_input/melos_image_size_getter_http_input.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /packages/image_size_getter_http_input/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: image_size_getter_http_input 2 | description: A http input for image_size_getter. 3 | version: 2.1.0 4 | homepage: https://github.com/CaiJingLong/dart_image_size_getter/tree/master/image_size_getter_http_input 5 | # publish_to: https://pub.dev/ 6 | 7 | environment: 8 | sdk: ">=2.12.0 <4.0.0" 9 | 10 | dependencies: 11 | http: ^1.0.0 12 | # image_size_getter: 13 | # path: ../library 14 | image_size_getter: ^2.4.0 15 | # path: ^1.8.0 16 | 17 | dev_dependencies: 18 | lints: ^1.0.0 19 | test: ^1.16.0 20 | -------------------------------------------------------------------------------- /packages/image_size_getter_http_input/pubspec_overrides.yaml: -------------------------------------------------------------------------------- 1 | # melos_managed_dependency_overrides: image_size_getter 2 | dependency_overrides: 3 | image_size_getter: 4 | path: ../image_size_getter 5 | -------------------------------------------------------------------------------- /packages/image_size_getter_http_input/test/image_size_getter_http_input_test.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: deprecated_member_use 2 | 3 | import 'dart:io'; 4 | import 'dart:typed_data'; 5 | import 'package:http/http.dart' as http; 6 | import 'package:image_size_getter/file_input.dart'; 7 | import 'package:image_size_getter/image_size_getter.dart'; 8 | import 'package:image_size_getter_http_input/image_size_getter_http_input.dart'; 9 | import 'package:test/test.dart'; 10 | 11 | Future createSupportRangeLoadHttpInput() async { 12 | // 2554x824 13 | final testUrl = 14 | 'https://cdn.jsdelivr.net/gh/CaiJingLong/some_asset@master/flutter_photo2.png'; 15 | return HttpInput.createHttpInput(testUrl); 16 | } 17 | 18 | Future createNoSupportRangeLoadHttpInput() async { 19 | final imgUrl = 'http://localhost:7777/assets/flutter_photo2.png'; 20 | return HttpInput.createHttpInput(imgUrl); 21 | } 22 | 23 | Future main() async { 24 | final input = await createSupportRangeLoadHttpInput(); 25 | 26 | group('Test http input properties:', () { 27 | setUp(() async {}); 28 | 29 | test('Uri exists.', () async { 30 | // expect(await url.exists(), isTrue); 31 | 32 | expect(await input.exists(), true); 33 | }); 34 | 35 | test('Support supportRangeLoad.', () async { 36 | expect(await input.supportRangeLoad(), true); 37 | }); 38 | 39 | test('Get length.', () async { 40 | expect(await input.length, isNonZero); 41 | }); 42 | }); 43 | 44 | group('Test support range load image size:', () { 45 | test('Test get size', () async { 46 | final width = 2554; 47 | final height = 824; 48 | final size = await ImageSizeGetter.getSizeAsync(input); 49 | 50 | expect(size.width, width); 51 | expect(size.height, height); 52 | }); 53 | 54 | test('Test get size result', () async { 55 | final width = 2554; 56 | final height = 824; 57 | final sizeResult = await ImageSizeGetter.getSizeResultAsync(input); 58 | final size = sizeResult.size; 59 | 60 | expect(size.width, width); 61 | expect(size.height, height); 62 | 63 | final decoder = sizeResult.decoder; 64 | expect(decoder.decoderName, 'png'); 65 | }); 66 | }); 67 | 68 | group('Test no support range load resource:', () { 69 | test('Start image server', () async { 70 | await startTestServer( 71 | callAfterStart: testFetch, 72 | supportRangeLoad: true, 73 | ); 74 | }); 75 | 76 | test('Test release resource.', () async { 77 | await startTestServer( 78 | supportRangeLoad: false, 79 | callAfterStart: () async { 80 | final width = 2554; 81 | final height = 824; 82 | 83 | final time = DateTime.now().microsecondsSinceEpoch; 84 | 85 | httpCachePath = [Directory.systemTemp.path, 'img', '$time'] 86 | .join(Platform.pathSeparator); 87 | 88 | final dir = Directory(httpCachePath); 89 | 90 | if (!dir.existsSync()) { 91 | dir.createSync(recursive: true); 92 | } 93 | 94 | final input2 = await createNoSupportRangeLoadHttpInput(); 95 | 96 | final delegateInput = await input2.delegateInput(); 97 | final delegateInnerInput = delegateInput.innerInput; 98 | 99 | if (delegateInnerInput is FileInput) { 100 | assert(delegateInnerInput.file.existsSync() == true, 101 | 'File not exists.'); 102 | } 103 | final sizeResult = ImageSizeGetter.getSizeResult(delegateInput); 104 | 105 | await delegateInput.release(); 106 | 107 | final size = sizeResult.size; 108 | 109 | expect(size.width, width); 110 | expect(size.height, height); 111 | 112 | if (delegateInnerInput is FileInput) { 113 | final tempFile = delegateInnerInput.file; 114 | try { 115 | assert(tempFile.existsSync() == false, 'File not exists.'); 116 | } catch (e, st) { 117 | print('tempFile.existsSync(): ${tempFile.existsSync()}'); 118 | print( 119 | '${tempFile.path} is exists, length: ${tempFile.lengthSync()}'); 120 | print(e); 121 | print(st); 122 | rethrow; 123 | } 124 | } 125 | }); 126 | }); 127 | }); 128 | } 129 | 130 | Future startTestServer({ 131 | int waitSeconds = 3, 132 | required void Function() callAfterStart, 133 | required bool supportRangeLoad, 134 | }) async { 135 | var path = 'assets/flutter_photo2.png'; 136 | final fileSrc = File(path); 137 | 138 | final server = await HttpServer.bind( 139 | InternetAddress.loopbackIPv4, 140 | 7777, 141 | shared: true, 142 | ); 143 | 144 | server.listen((request) { 145 | final response = request.response; 146 | if (request.method == "GET" || request.method == "HEAD") { 147 | final uri = request.uri; 148 | if (!uri.toString().startsWith('/$path')) { 149 | response.statusCode = 404; 150 | response.close(); 151 | return; 152 | } 153 | 154 | response.headers.contentType = ContentType.binary; 155 | response.headers.contentLength = fileSrc.lengthSync(); 156 | response.headers.set('Access-Control-Allow-Origin', '*'); 157 | response.headers.set('Access-Control-Allow-Methods', 'GET, HEAD'); 158 | 159 | if (supportRangeLoad) { 160 | response.headers.set(HttpHeaders.acceptRangesHeader, 'bytes'); 161 | } 162 | 163 | if (request.method == "GET") { 164 | if (supportRangeLoad) { 165 | final ranges = request.headers['range']; 166 | if (ranges != null && ranges.isNotEmpty) { 167 | final range = ranges[0]; 168 | final start = range.split('=')[1].split('-')[0]; 169 | final end = range.split('=')[1].split('-')[1]; 170 | 171 | response.statusCode = HttpStatus.partialContent; 172 | response.headers.set(HttpHeaders.contentRangeHeader, 173 | 'bytes $start-$end/${fileSrc.lengthSync()}'); 174 | 175 | final reader = fileSrc.openSync(); 176 | reader.setPositionSync(int.parse(start)); 177 | final length = int.parse(end) - int.parse(start) + 1; 178 | final buffer = Uint8List(length); 179 | reader.readIntoSync(buffer, 0, length); 180 | response.add(buffer); 181 | reader.close(); 182 | } else { 183 | response.add(fileSrc.readAsBytesSync()); 184 | } 185 | } else { 186 | response.add(fileSrc.readAsBytesSync()); 187 | } 188 | } 189 | 190 | response.close(); 191 | } else { 192 | response.statusCode = 403; 193 | response.close(); 194 | } 195 | }); 196 | 197 | // request http 198 | print('Server is running at http://${InternetAddress.loopbackIPv4}:7777'); 199 | print('Press Ctrl-C to stop server or wait $waitSeconds seconds to stop.'); 200 | callAfterStart(); 201 | await Future.delayed(Duration(seconds: waitSeconds)); 202 | server.close(); 203 | } 204 | 205 | Future testFetch() async { 206 | final url = 'http://localhost:7777/assets/flutter_photo2.png'; 207 | final url2 = 'http://localhost:7777/assets/flutter_photo4.png'; 208 | 209 | testServer(url, 200); 210 | testServer(url2, 404); 211 | } 212 | 213 | Future testServer(String url, int statusCode) async { 214 | final resp = await http.get(Uri.parse(url)); 215 | expect(resp.statusCode, statusCode); 216 | } 217 | -------------------------------------------------------------------------------- /publish_http_input.sh: -------------------------------------------------------------------------------- 1 | unset PUB_HOSTED_URL 2 | cd packages/image_size_getter_http_input 3 | dart pub publish -------------------------------------------------------------------------------- /publish_library.sh: -------------------------------------------------------------------------------- 1 | unset PUB_HOSTED_URL 2 | cd packages/image_size_getter 3 | dart pub publish -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | ansi_styles: 5 | dependency: transitive 6 | description: 7 | name: ansi_styles 8 | sha256: "9c656cc12b3c27b17dd982b2cc5c0cfdfbdabd7bc8f3ae5e8542d9867b47ce8a" 9 | url: "https://pub.flutter-io.cn" 10 | source: hosted 11 | version: "0.3.2+1" 12 | args: 13 | dependency: transitive 14 | description: 15 | name: args 16 | sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 17 | url: "https://pub.flutter-io.cn" 18 | source: hosted 19 | version: "2.4.2" 20 | async: 21 | dependency: transitive 22 | description: 23 | name: async 24 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" 25 | url: "https://pub.flutter-io.cn" 26 | source: hosted 27 | version: "2.11.0" 28 | boolean_selector: 29 | dependency: transitive 30 | description: 31 | name: boolean_selector 32 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" 33 | url: "https://pub.flutter-io.cn" 34 | source: hosted 35 | version: "2.1.1" 36 | charcode: 37 | dependency: transitive 38 | description: 39 | name: charcode 40 | sha256: fb98c0f6d12c920a02ee2d998da788bca066ca5f148492b7085ee23372b12306 41 | url: "https://pub.flutter-io.cn" 42 | source: hosted 43 | version: "1.3.1" 44 | cli_launcher: 45 | dependency: transitive 46 | description: 47 | name: cli_launcher 48 | sha256: "5e7e0282b79e8642edd6510ee468ae2976d847a0a29b3916e85f5fa1bfe24005" 49 | url: "https://pub.flutter-io.cn" 50 | source: hosted 51 | version: "0.3.1" 52 | cli_util: 53 | dependency: transitive 54 | description: 55 | name: cli_util 56 | sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19 57 | url: "https://pub.flutter-io.cn" 58 | source: hosted 59 | version: "0.4.1" 60 | collection: 61 | dependency: transitive 62 | description: 63 | name: collection 64 | sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a 65 | url: "https://pub.flutter-io.cn" 66 | source: hosted 67 | version: "1.18.0" 68 | conventional_commit: 69 | dependency: transitive 70 | description: 71 | name: conventional_commit 72 | sha256: dec15ad1118f029c618651a4359eb9135d8b88f761aa24e4016d061cd45948f2 73 | url: "https://pub.flutter-io.cn" 74 | source: hosted 75 | version: "0.6.0+1" 76 | file: 77 | dependency: transitive 78 | description: 79 | name: file 80 | sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" 81 | url: "https://pub.flutter-io.cn" 82 | source: hosted 83 | version: "7.0.0" 84 | glob: 85 | dependency: transitive 86 | description: 87 | name: glob 88 | sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" 89 | url: "https://pub.flutter-io.cn" 90 | source: hosted 91 | version: "2.1.2" 92 | graphs: 93 | dependency: transitive 94 | description: 95 | name: graphs 96 | sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19 97 | url: "https://pub.flutter-io.cn" 98 | source: hosted 99 | version: "2.3.1" 100 | http: 101 | dependency: transitive 102 | description: 103 | name: http 104 | sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" 105 | url: "https://pub.flutter-io.cn" 106 | source: hosted 107 | version: "1.2.1" 108 | http_parser: 109 | dependency: transitive 110 | description: 111 | name: http_parser 112 | sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" 113 | url: "https://pub.flutter-io.cn" 114 | source: hosted 115 | version: "4.0.2" 116 | io: 117 | dependency: transitive 118 | description: 119 | name: io 120 | sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" 121 | url: "https://pub.flutter-io.cn" 122 | source: hosted 123 | version: "1.0.4" 124 | json_annotation: 125 | dependency: transitive 126 | description: 127 | name: json_annotation 128 | sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 129 | url: "https://pub.flutter-io.cn" 130 | source: hosted 131 | version: "4.8.1" 132 | matcher: 133 | dependency: transitive 134 | description: 135 | name: matcher 136 | sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb 137 | url: "https://pub.flutter-io.cn" 138 | source: hosted 139 | version: "0.12.16+1" 140 | melos: 141 | dependency: "direct dev" 142 | description: 143 | name: melos 144 | sha256: a0cb264096a315e4acdb66ae75ee594a76c97fe15ce9ae469f6c58c6c4b2be87 145 | url: "https://pub.flutter-io.cn" 146 | source: hosted 147 | version: "5.3.0" 148 | meta: 149 | dependency: transitive 150 | description: 151 | name: meta 152 | sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" 153 | url: "https://pub.flutter-io.cn" 154 | source: hosted 155 | version: "1.12.0" 156 | mustache_template: 157 | dependency: transitive 158 | description: 159 | name: mustache_template 160 | sha256: a46e26f91445bfb0b60519be280555b06792460b27b19e2b19ad5b9740df5d1c 161 | url: "https://pub.flutter-io.cn" 162 | source: hosted 163 | version: "2.0.0" 164 | path: 165 | dependency: transitive 166 | description: 167 | name: path 168 | sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" 169 | url: "https://pub.flutter-io.cn" 170 | source: hosted 171 | version: "1.9.0" 172 | platform: 173 | dependency: transitive 174 | description: 175 | name: platform 176 | sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" 177 | url: "https://pub.flutter-io.cn" 178 | source: hosted 179 | version: "3.1.4" 180 | pool: 181 | dependency: transitive 182 | description: 183 | name: pool 184 | sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" 185 | url: "https://pub.flutter-io.cn" 186 | source: hosted 187 | version: "1.5.1" 188 | process: 189 | dependency: transitive 190 | description: 191 | name: process 192 | sha256: "21e54fd2faf1b5bdd5102afd25012184a6793927648ea81eea80552ac9405b32" 193 | url: "https://pub.flutter-io.cn" 194 | source: hosted 195 | version: "5.0.2" 196 | prompts: 197 | dependency: transitive 198 | description: 199 | name: prompts 200 | sha256: "3773b845e85a849f01e793c4fc18a45d52d7783b4cb6c0569fad19f9d0a774a1" 201 | url: "https://pub.flutter-io.cn" 202 | source: hosted 203 | version: "2.0.0" 204 | pub_semver: 205 | dependency: transitive 206 | description: 207 | name: pub_semver 208 | sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" 209 | url: "https://pub.flutter-io.cn" 210 | source: hosted 211 | version: "2.1.4" 212 | pub_updater: 213 | dependency: transitive 214 | description: 215 | name: pub_updater 216 | sha256: "54e8dc865349059ebe7f163d6acce7c89eb958b8047e6d6e80ce93b13d7c9e60" 217 | url: "https://pub.flutter-io.cn" 218 | source: hosted 219 | version: "0.4.0" 220 | pubspec: 221 | dependency: transitive 222 | description: 223 | name: pubspec 224 | sha256: f534a50a2b4d48dc3bc0ec147c8bd7c304280fff23b153f3f11803c4d49d927e 225 | url: "https://pub.flutter-io.cn" 226 | source: hosted 227 | version: "2.3.0" 228 | quiver: 229 | dependency: transitive 230 | description: 231 | name: quiver 232 | sha256: b1c1ac5ce6688d77f65f3375a9abb9319b3cb32486bdc7a1e0fdf004d7ba4e47 233 | url: "https://pub.flutter-io.cn" 234 | source: hosted 235 | version: "3.2.1" 236 | source_span: 237 | dependency: transitive 238 | description: 239 | name: source_span 240 | sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" 241 | url: "https://pub.flutter-io.cn" 242 | source: hosted 243 | version: "1.10.0" 244 | stack_trace: 245 | dependency: transitive 246 | description: 247 | name: stack_trace 248 | sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" 249 | url: "https://pub.flutter-io.cn" 250 | source: hosted 251 | version: "1.11.1" 252 | stream_channel: 253 | dependency: transitive 254 | description: 255 | name: stream_channel 256 | sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 257 | url: "https://pub.flutter-io.cn" 258 | source: hosted 259 | version: "2.1.2" 260 | string_scanner: 261 | dependency: transitive 262 | description: 263 | name: string_scanner 264 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" 265 | url: "https://pub.flutter-io.cn" 266 | source: hosted 267 | version: "1.2.0" 268 | term_glyph: 269 | dependency: transitive 270 | description: 271 | name: term_glyph 272 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 273 | url: "https://pub.flutter-io.cn" 274 | source: hosted 275 | version: "1.2.1" 276 | test_api: 277 | dependency: transitive 278 | description: 279 | name: test_api 280 | sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" 281 | url: "https://pub.flutter-io.cn" 282 | source: hosted 283 | version: "0.7.0" 284 | typed_data: 285 | dependency: transitive 286 | description: 287 | name: typed_data 288 | sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c 289 | url: "https://pub.flutter-io.cn" 290 | source: hosted 291 | version: "1.3.2" 292 | uri: 293 | dependency: transitive 294 | description: 295 | name: uri 296 | sha256: "889eea21e953187c6099802b7b4cf5219ba8f3518f604a1033064d45b1b8268a" 297 | url: "https://pub.flutter-io.cn" 298 | source: hosted 299 | version: "1.0.0" 300 | web: 301 | dependency: transitive 302 | description: 303 | name: web 304 | sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" 305 | url: "https://pub.flutter-io.cn" 306 | source: hosted 307 | version: "0.5.1" 308 | yaml: 309 | dependency: transitive 310 | description: 311 | name: yaml 312 | sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" 313 | url: "https://pub.flutter-io.cn" 314 | source: hosted 315 | version: "3.1.2" 316 | yaml_edit: 317 | dependency: transitive 318 | description: 319 | name: yaml_edit 320 | sha256: c566f4f804215d84a7a2c377667f546c6033d5b34b4f9e60dfb09d17c4e97826 321 | url: "https://pub.flutter-io.cn" 322 | source: hosted 323 | version: "2.2.0" 324 | sdks: 325 | dart: ">=3.3.0 <4.0.0" 326 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: image_size_getter_project_workspaces 2 | 3 | environment: 4 | sdk: ">=3.0.0 <4.0.0" 5 | 6 | dev_dependencies: 7 | melos: ^5.3.0 8 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | workdir="$( cd "$( dirname "$0" )" && pwd )" 2 | 3 | melos run test --------------------------------------------------------------------------------