├── .DS_Store ├── logo.png ├── simple.jpg ├── .coding-ci.yml ├── brand ├── .DS_Store ├── apple.svg ├── dji.svg ├── unknow.svg ├── xiaomi.svg ├── canon.svg ├── sony.svg ├── fujifilm.svg ├── leica.svg └── nikon corporation.svg ├── .gitignore ├── src ├── utils.rs ├── main.rs └── lib.rs ├── .idea ├── vcs.xml ├── .gitignore ├── modules.xml └── gen-brand-photo-pictrue.iml ├── README.md ├── package.json ├── .appveyor.yml ├── Cargo.toml ├── sw.js ├── index.css ├── index.html ├── sw.reg.mgr.js ├── .travis.yml └── index.js /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzd/gen-brand-photo-pictrue/master/.DS_Store -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzd/gen-brand-photo-pictrue/master/logo.png -------------------------------------------------------------------------------- /simple.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzd/gen-brand-photo-pictrue/master/simple.jpg -------------------------------------------------------------------------------- /.coding-ci.yml: -------------------------------------------------------------------------------- 1 | $: 2 | api_trigger_vscode: 3 | clouddev: 4 | runner: 5 | cpus: 1 -------------------------------------------------------------------------------- /brand/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzd/gen-brand-photo-pictrue/master/brand/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | bin/ 5 | pkg/ 6 | wasm-pack.log 7 | /node_modules 8 | /build -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | pub fn set_panic_hook() { 2 | #[cfg(feature = "console_error_panic_hook")] 3 | console_error_panic_hook::set_once(); 4 | } 5 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | - 自动识别照片的信息,包含图中的 GPS、设备、相机参数、品牌等信息。 2 | - 并支持手动修正,和自定义。 3 | - 支持导出图片。 4 | 5 | 工具地址: [gen-brand-photo-pictrue](https://lecepin.github.io/gen-brand-photo-pictrue/) 6 | 7 | ![image](https://user-images.githubusercontent.com/11046969/179353093-0fbb9c33-f933-495b-a76d-eb9777d8f060.png) 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "build": "wasm-pack build --target web", 4 | "pages": "npm run build && cargo run && gh-pages --dist build --dest . --repo https://github.com/zzd/gen-brand-photo-pictrue" 5 | }, 6 | "devDependencies": { 7 | "gh-pages": "^4.1.8" 8 | } 9 | } -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.appveyor.yml: -------------------------------------------------------------------------------- 1 | install: 2 | - appveyor-retry appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe 3 | - if not defined RUSTFLAGS rustup-init.exe -y --default-host x86_64-pc-windows-msvc --default-toolchain nightly 4 | - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin 5 | - rustc -V 6 | - cargo -V 7 | 8 | build: false 9 | 10 | test_script: 11 | - cargo test --locked 12 | -------------------------------------------------------------------------------- /.idea/gen-brand-photo-pictrue.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gen-brand-photo-pictrue" 3 | version = "0.1.0" 4 | authors = ["lecepin"] 5 | edition = "2021" 6 | 7 | [lib] 8 | crate-type = ["cdylib", "rlib"] 9 | 10 | [features] 11 | default = ["console_error_panic_hook", "wee_alloc"] 12 | 13 | [dependencies] 14 | wasm-bindgen = { version = "0.2.81", features = ["serde-serialize"] } 15 | console_error_panic_hook = { version = "0.1.7", optional = true } 16 | wee_alloc = { version = "0.4.5", optional = true } 17 | web-sys = { version = "0.3.58", features = [] } 18 | kamadak-exif = "0.5.4" 19 | serde = { version = "1.0.138", features = ["derive"] } 20 | 21 | [profile.release] 22 | opt-level = "s" 23 | -------------------------------------------------------------------------------- /sw.js: -------------------------------------------------------------------------------- 1 | const CACHE_NAME = "gen-brand-photo-pictrue-res"; 2 | 3 | self.addEventListener("install", (event) => { 4 | self.skipWaiting(); 5 | 6 | event.waitUntil( 7 | caches 8 | .open(CACHE_NAME) 9 | .then((cache) => 10 | cache.addAll([ 11 | "./", 12 | "./index.html", 13 | "./index.js", 14 | "./index.css", 15 | "./simple.jpg", 16 | "./pkg/gen_brand_photo_pictrue.js", 17 | "./pkg/gen_brand_photo_pictrue_bg.wasm", 18 | ]) 19 | ) 20 | ); 21 | }); 22 | 23 | self.addEventListener("activate", (event) => { 24 | clients.claim(); 25 | }); 26 | 27 | self.addEventListener("fetch", (event) => { 28 | return event.respondWith( 29 | caches.match(event.request).then((cacheData) => { 30 | if (cacheData) { 31 | return cacheData; 32 | } 33 | 34 | return fetch(event.request); 35 | }) 36 | ); 37 | }); 38 | -------------------------------------------------------------------------------- /brand/apple.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 11 | 12 | -------------------------------------------------------------------------------- /brand/dji.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Layer 1 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /brand/unknow.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | use std::{fs, io, path::Path}; 3 | 4 | struct SourceFile { 5 | files: Vec<&'static str>, 6 | dirs: Vec<&'static str>, 7 | } 8 | 9 | fn main() -> Result<(), Box> { 10 | let build_dir_path = Path::new("build"); 11 | let source = SourceFile { 12 | files: vec![ 13 | "index.html", 14 | "logo.png", 15 | "simple.jpg", 16 | "index.js", 17 | "index.css", 18 | "sw.js", 19 | "sw.reg.mgr.js" 20 | ], 21 | dirs: vec!["pkg", "brand"], 22 | }; 23 | 24 | match fs::remove_dir_all(&build_dir_path) { _ => {} }; 25 | 26 | // 创建目标目录; 27 | fs::create_dir_all(&build_dir_path)?; 28 | 29 | // 遍历 fiLes 30 | for file in source.files.iter() { 31 | fs::copy(&file, build_dir_path.join(&file))?; 32 | } 33 | 34 | // 遍历 dirs 35 | for dir in source.dirs.iter() { 36 | copy_dir_content(Path::new(dir), &build_dir_path.join(dir))?; 37 | } 38 | 39 | println!("复制到 {} 完成", build_dir_path.display()); 40 | 41 | Ok(()) 42 | } 43 | 44 | fn copy_dir_content(dir: &Path, to_dir: &Path) -> io::Result<()> { 45 | // 创建目标目录; 46 | fs::create_dir_all(&to_dir)?; 47 | 48 | // 读取源目录,进行内容遍历 49 | for item in fs::read_dir(dir)? { 50 | // 获取 DirEntry 51 | let item_entry = item?; 52 | 53 | // 是否为目录 54 | if item_entry.file_type()?.is_dir() { 55 | copy_dir_content(&item_entry.path(), &to_dir.join(item_entry.file_name()))?; 56 | } else { 57 | fs::copy(item_entry.path(), to_dir.join(item_entry.file_name()))?; 58 | } 59 | } 60 | 61 | Ok(()) 62 | } 63 | -------------------------------------------------------------------------------- /brand/xiaomi.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /index.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Zilla+Slab&display=swap'); 2 | 3 | #app { 4 | width: 800px; 5 | margin: 0 auto; 6 | padding: 20px 0; 7 | } 8 | 9 | .preview-box { 10 | margin-bottom: 16px; 11 | } 12 | 13 | .preview { 14 | box-shadow: 7px 4px 15px #ccc; 15 | background: #fff; 16 | } 17 | 18 | .preview-picture { 19 | max-width: 100%; 20 | } 21 | 22 | .preview-info { 23 | font-family: 'Zilla Slab', serif; 24 | padding: 20px; 25 | display: flex; 26 | flex-direction: row; 27 | } 28 | 29 | .preview-info-left { 30 | flex-grow: 1; 31 | width: 0; 32 | position: relative; 33 | display: flex; 34 | flex-direction: column; 35 | align-items: flex-start; 36 | justify-content: center; 37 | } 38 | 39 | .preview-info-split { 40 | border-left: 2px solid #ddd; 41 | height: 39px; 42 | margin-top: 6px; 43 | margin-right: 12px; 44 | margin-left: 12px; 45 | } 46 | 47 | .preview-info-right { 48 | display: flex; 49 | flex-direction: column; 50 | align-items: flex-start; 51 | justify-content: center; 52 | } 53 | 54 | .preview-info-model, 55 | .preview-info-device { 56 | font-weight: bold; 57 | font-size: 18px; 58 | } 59 | 60 | .preview-info-date, 61 | .preview-info-gps { 62 | font-size: 14px; 63 | color: #aaa; 64 | } 65 | 66 | .preview-info-brand { 67 | position: absolute; 68 | right: 0; 69 | top: 0; 70 | height: 100%; 71 | display: flex; 72 | align-items: center; 73 | } 74 | 75 | .preview-info-brand img { 76 | height: 39px; 77 | } 78 | 79 | .props { 80 | margin-top: 20px; 81 | } 82 | 83 | .props .ant-form-inline .ant-form-item { 84 | margin-bottom: 10px; 85 | } 86 | 87 | .op { 88 | display: flex; 89 | justify-content: space-between; 90 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 生成徕卡水印照片 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | mod utils; 2 | 3 | use serde::Serialize; 4 | use wasm_bindgen::prelude::*; 5 | 6 | #[cfg(feature = "wee_alloc")] 7 | #[global_allocator] 8 | static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; 9 | 10 | #[derive(Debug, Serialize)] 11 | struct ExifData { 12 | tag: String, 13 | value: String, 14 | value_with_unit: String, 15 | } 16 | 17 | #[wasm_bindgen(start)] 18 | pub fn run() { 19 | utils::set_panic_hook(); 20 | } 21 | 22 | #[wasm_bindgen] 23 | pub fn get_exif(raw: Vec) -> JsValue { 24 | let mut exif_data: Vec = Vec::new(); 25 | let exifreader = exif::Reader::new(); 26 | 27 | // vec 转换为 io::BufRead + io::Seek 28 | let mut bufreader = std::io::Cursor::new(raw.as_slice()); 29 | let exif = exifreader.read_from_container(&mut bufreader).unwrap(); 30 | 31 | for field in exif.fields() { 32 | if let Some(_) = field.tag.to_string().find("Tag(Exif") { 33 | continue; 34 | } 35 | 36 | if ["Make", "Model"].contains(&field.tag.to_string().as_str()) { 37 | exif_data.push(ExifData { 38 | tag: field.tag.to_string(), 39 | value: field 40 | .display_value() 41 | .to_string() 42 | .replace( 43 | |item: char| ["\"", ","].contains(&item.to_string().as_str()), 44 | "", 45 | ) 46 | .trim() 47 | .to_string(), 48 | value_with_unit: field 49 | .display_value() 50 | .with_unit(&exif) 51 | .to_string() 52 | .replace('"', ""), 53 | }); 54 | continue; 55 | } 56 | 57 | exif_data.push(ExifData { 58 | tag: field.tag.to_string(), 59 | value: field.display_value().to_string(), 60 | value_with_unit: field.display_value().with_unit(&exif).to_string(), 61 | }); 62 | } 63 | 64 | JsValue::from_serde(&exif_data).unwrap() 65 | } 66 | -------------------------------------------------------------------------------- /sw.reg.mgr.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | register({ 3 | ver: 2, 4 | path: ".", 5 | name: "sw.js", 6 | onUpdate: () => {}, 7 | onSuccess: () => {}, 8 | }); 9 | 10 | function register(config) { 11 | if ("serviceWorker" in navigator) { 12 | var swUrl = config.path + "/" + config.name + "?v=" + config.ver; 13 | navigator.serviceWorker 14 | .register(swUrl) 15 | .then((swReg) => { 16 | config.log && console.log("Service Worker 注册成功。", swReg); 17 | if (config && config.onRegster) { 18 | config.onRegister(swReg); 19 | } 20 | 21 | swReg.onupdatefound = () => { 22 | var installingWorker = swReg.installing; 23 | if (installingWorker == null) { 24 | return; 25 | } 26 | installingWorker.onstatechange = () => { 27 | if (installingWorker.state === "installed") { 28 | if (navigator.serviceWorker.controller) { 29 | config.log && console.log("Service Worker 已安装更新。"); 30 | if (config && config.onUpdate) { 31 | config.onUpdate(swReg); 32 | } 33 | } else { 34 | config.log && console.log("Service Worker 已安装。"); 35 | if (config && config.onSuccess) { 36 | config.onSuccess(swReg); 37 | } 38 | } 39 | } 40 | }; 41 | }; 42 | }) 43 | .catch((err) => { 44 | if (config && config.onError) { 45 | config.onError(err); 46 | } 47 | console.error("serviceWorker注册期间发生错误:", error); 48 | }); 49 | } 50 | } 51 | 52 | function unregister() { 53 | if ("serviceWorker" in navigator) { 54 | navigator.serviceWorker.ready.then((swReg) => { 55 | swReg.unregister((result) => { 56 | result && console.log("Service Worker 注销成功"); 57 | }); 58 | }); 59 | } 60 | } 61 | })(); -------------------------------------------------------------------------------- /brand/canon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | Layer 1 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /brand/sony.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml 3 | 4 | 5 | Layer 1 6 | 7 | 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | sudo: false 3 | 4 | cache: cargo 5 | 6 | matrix: 7 | include: 8 | 9 | # Builds with wasm-pack. 10 | - rust: beta 11 | env: RUST_BACKTRACE=1 12 | addons: 13 | firefox: latest 14 | chrome: stable 15 | before_script: 16 | - (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update) 17 | - (test -x $HOME/.cargo/bin/cargo-generate || cargo install --vers "^0.2" cargo-generate) 18 | - cargo install-update -a 19 | - curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh -s -- -f 20 | script: 21 | - cargo generate --git . --name testing 22 | # Having a broken Cargo.toml (in that it has curlies in fields) anywhere 23 | # in any of our parent dirs is problematic. 24 | - mv Cargo.toml Cargo.toml.tmpl 25 | - cd testing 26 | - wasm-pack build 27 | - wasm-pack test --chrome --firefox --headless 28 | 29 | # Builds on nightly. 30 | - rust: nightly 31 | env: RUST_BACKTRACE=1 32 | before_script: 33 | - (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update) 34 | - (test -x $HOME/.cargo/bin/cargo-generate || cargo install --vers "^0.2" cargo-generate) 35 | - cargo install-update -a 36 | - rustup target add wasm32-unknown-unknown 37 | script: 38 | - cargo generate --git . --name testing 39 | - mv Cargo.toml Cargo.toml.tmpl 40 | - cd testing 41 | - cargo check 42 | - cargo check --target wasm32-unknown-unknown 43 | - cargo check --no-default-features 44 | - cargo check --target wasm32-unknown-unknown --no-default-features 45 | - cargo check --no-default-features --features console_error_panic_hook 46 | - cargo check --target wasm32-unknown-unknown --no-default-features --features console_error_panic_hook 47 | - cargo check --no-default-features --features "console_error_panic_hook wee_alloc" 48 | - cargo check --target wasm32-unknown-unknown --no-default-features --features "console_error_panic_hook wee_alloc" 49 | 50 | # Builds on beta. 51 | - rust: beta 52 | env: RUST_BACKTRACE=1 53 | before_script: 54 | - (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update) 55 | - (test -x $HOME/.cargo/bin/cargo-generate || cargo install --vers "^0.2" cargo-generate) 56 | - cargo install-update -a 57 | - rustup target add wasm32-unknown-unknown 58 | script: 59 | - cargo generate --git . --name testing 60 | - mv Cargo.toml Cargo.toml.tmpl 61 | - cd testing 62 | - cargo check 63 | - cargo check --target wasm32-unknown-unknown 64 | - cargo check --no-default-features 65 | - cargo check --target wasm32-unknown-unknown --no-default-features 66 | - cargo check --no-default-features --features console_error_panic_hook 67 | - cargo check --target wasm32-unknown-unknown --no-default-features --features console_error_panic_hook 68 | # Note: no enabling the `wee_alloc` feature here because it requires 69 | # nightly for now. 70 | -------------------------------------------------------------------------------- /brand/fujifilm.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | image/svg+xml 4 | 5 | Layer 1 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /brand/leica.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 13 | 16 | 21 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /brand/nikon corporation.svg: -------------------------------------------------------------------------------- 1 | 2 | Nikon 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import init, { get_exif } from "./pkg/gen_brand_photo_pictrue.js"; 2 | 3 | const { useState, useRef } = React; 4 | const { Typography, Divider, Button, Form, Input, Select, Upload, message } = 5 | antd; 6 | const { PlusOutlined, VerticalAlignBottomOutlined } = icons; 7 | 8 | init(); 9 | 10 | function App() { 11 | const [formValue, setFormValue] = useState({ 12 | model: "XIAOMI 12S ULTRA", 13 | date: "2022.06.20 22:51:12", 14 | gps: `51°30'00"N 0°10'00"E`, 15 | device: "23mm f/1.0 1/320 ISO1495", 16 | brand: "leica", 17 | brand_url: "./brand/leica.svg", 18 | }); 19 | const [imgUrl, setImgUrl] = useState("./simple.jpg"); 20 | const brandList = [ 21 | "Apple", 22 | "Canon", 23 | "Dji", 24 | "Fujifilm", 25 | "Huawei", 26 | "Leica", 27 | "Xiaomi", 28 | "Nikon Corporation", 29 | "Sony", 30 | "未收录", 31 | ]; 32 | const formRef = useRef(); 33 | 34 | const handleDownload = () => { 35 | const previewDom = document.getElementById("preview"); 36 | const zoomRatio = 4; 37 | 38 | domtoimage 39 | .toJpeg(previewDom, { 40 | quality: 0.8, 41 | width: previewDom.clientWidth * zoomRatio, 42 | height: previewDom.clientHeight * zoomRatio, 43 | style: { 44 | transform: "scale(" + zoomRatio + ")", 45 | "transform-origin": "top left", 46 | }, 47 | }) 48 | .then((data) => { 49 | const binaryString = window.atob(data.split(",")[1]); 50 | const length = binaryString.length; 51 | const binaryArray = new Uint8Array(length); 52 | 53 | for (let i = 0; i < length; i++) 54 | binaryArray[i] = binaryString.charCodeAt(i); 55 | 56 | return new Blob([binaryArray], { 57 | type: "image/jpeg", 58 | }); 59 | }) 60 | .then((data) => window.URL.createObjectURL(data)) 61 | .then((data) => { 62 | const link = document.createElement("a"); 63 | 64 | link.download = Date.now() + ".jpg"; 65 | link.href = data; 66 | document.body.appendChild(link); 67 | link.click(); 68 | link.remove(); 69 | }); 70 | }; 71 | 72 | const handleAdd = (file) => { 73 | const reader = new FileReader(); 74 | 75 | reader.onloadend = (e) => { 76 | try { 77 | const result = get_exif(new Uint8Array(e.target.result)); 78 | const resultObj = {}; 79 | 80 | result.map((item) => (resultObj[item.tag] = item)); 81 | 82 | let deviceObj = { 83 | focalLen: 84 | resultObj?.FocalLengthIn35mmFilm?.value_with_unit ?? 85 | resultObj?.FocalLength?.value_with_unit, 86 | fNum: resultObj?.FNumber?.value_with_unit 87 | ?.split("/") 88 | ?.map((item, index) => (index == 1 ? (+item).toFixed(1) : item)) 89 | ?.join("/"), 90 | exposureTime: resultObj?.ExposureTime?.value 91 | ?.split("/") 92 | ?.map((item) => ~~item) 93 | ?.join("/"), 94 | iso: resultObj?.PhotographicSensitivity?.value, 95 | }; 96 | let formValue = { 97 | model: resultObj?.Model?.value, 98 | date: resultObj?.DateTimeOriginal?.value, 99 | gps: `${formatGPS( 100 | resultObj?.GPSLatitude?.value_with_unit 101 | )} ${formatGPS(resultObj?.GPSLongitude?.value_with_unit)}`, 102 | device: `${deviceObj.focalLen ? deviceObj.focalLen + " " : ""}${ 103 | deviceObj.fNum ? deviceObj.fNum + " " : "" 104 | }${deviceObj.exposureTime ? deviceObj.exposureTime + " " : ""}${ 105 | deviceObj.iso ? "ISO " + deviceObj.iso + " " : "" 106 | }`, 107 | brand: resultObj?.Make?.value.toLowerCase(), 108 | }; 109 | formValue.brand_url = `./brand/${ 110 | formValue.brand === "未收录" 111 | ? "unknow.svg" 112 | : (formValue?.brand?.toLowerCase() ?? "unknow") + ".svg" 113 | }`; 114 | 115 | formRef.current.setFieldsValue(formValue); 116 | setFormValue(formValue); 117 | setImgUrl(URL.createObjectURL(new Blob([file], { type: file.type }))); 118 | } catch (error) { 119 | console.log({ error }); 120 | message.error("无法识别照片特定数据,请换一张照片"); 121 | } 122 | }; 123 | reader.readAsArrayBuffer(file); 124 | }; 125 | 126 | return ( 127 | <> 128 | 生成徕卡水印照片 129 |
130 | 预览 131 |
132 | 133 |
134 |
135 |
{formValue.model}
136 |
{formValue.date}
137 |
138 | 139 |
140 |
141 |
142 |
143 |
{formValue.device}
144 |
{formValue.gps}
145 |
146 |
147 |
148 |
149 | 150 |
151 | 参数 152 |
{ 157 | setFormValue({ 158 | ...value, 159 | brand_url: `./brand/${ 160 | value.brand === "未收录" 161 | ? "unknow.svg" 162 | : (value?.brand?.toLowerCase() ?? "unknow") + ".svg" 163 | }`, 164 | }); 165 | }} 166 | initialValues={formValue} 167 | > 168 | 169 | 170 | 171 | 172 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | {/* 185 | 186 | */} 187 | 188 | 189 | 190 |
191 |
192 | 193 | 194 |
195 | { 199 | handleAdd(file); 200 | return false; 201 | }} 202 | fileList={[]} 203 | > 204 | 207 | 208 | 215 |
216 | 217 | ); 218 | } 219 | 220 | ReactDOM.render(, document.getElementById("app")); 221 | 222 | function formatGPS(gps) { 223 | if (!gps) { 224 | return ""; 225 | } 226 | 227 | const [degrees, minutes, seconds, dir] = gps 228 | .match(/(\d+\.?\d*)|([NSWE]$)/gim) 229 | .map((item) => 230 | !Number.isNaN(Number(item)) ? (~~item < 10 ? "0" + ~~item : ~~item) : item 231 | ); 232 | 233 | return `${degrees}°${minutes}'${seconds}"${dir}`; 234 | } 235 | --------------------------------------------------------------------------------