84 Commits

Author SHA1 Message Date
Abdel
05d6580ba6 Merge pull request #56 from JDM170/fixes
Update
2025-06-18 13:47:15 -04:00
a60ae3c5a2 Update
* Updated DisplaySaveDialog
* Fixed a bug in SaveContactsHandler, when an exception was thrown when canceling file saving
* The window title displays "New file" instead of empty when creating a new file
* Commented out the code that saves a website with the "work" type (the program does not work with this type of website)
* Explicitly specified data types for some variables
* Minor code formatting

Signed-off-by: Lev Rusanov <30170278+JDM170@users.noreply.github.com>
2025-06-18 20:45:44 +07:00
abdelkader
9c66157599 fixed test 2025-06-12 13:28:37 -04:00
Abdel
a67e88bb22 Merge pull request #53 from JDM170/update_readme
Update README
2025-06-10 13:23:33 -04:00
7d029293b0 Update README.md
Signed-off-by: Lev Rusanov <30170278+JDM170@users.noreply.github.com>
2025-06-05 17:39:00 +07:00
abdelkader
c9b324a8c6 Merge branch 'master' of https://github.com/abdelkader/vCardEditor 2025-06-04 22:29:47 -04:00
Abdel
8457b57dae Merge pull request #52 from JDM170/fix_dialogs
Dialog fixes
2025-06-04 22:28:50 -04:00
Abdel
1b4d8373f2 Merge pull request #51 from JDM170/code_fixes
Corrections and additions in code
2025-06-04 22:27:54 -04:00
abdelkader
9c562b3f7e added test 2025-06-04 22:19:22 -04:00
a37ceee7f2 Update
* Aligning buttons on the About dialog and removing an extra line in tableLayoutPanel
* Removed unnecessary cancel button

Signed-off-by: Lev Rusanov <30170278+JDM170@users.noreply.github.com>
2025-06-04 18:41:58 +07:00
8392f0c273 Update
* Added question when deleting data from the Extras tab
* ReplaceControls function merged with RemoveControl in ExtendedPanel
* Fixed strange logic of working with AskMessage
* Removed extra lines and spaces (again:D)

Signed-off-by: Lev Rusanov <30170278+JDM170@users.noreply.github.com>
2025-06-04 17:16:26 +07:00
abdelkader
76398ae2c4 Added test and refactored the localization 2025-06-03 22:09:37 -04:00
abdelkader
dc47c59dc9 i18n draft 2025-06-02 21:37:39 -04:00
Abdel
1272d93ae9 Merge pull request #50 from JDM170/delete_bug_2
Fixed bug when deleting a number or site was not saved (separate signal)
2025-06-02 15:19:58 -04:00
892bf14a25 Fixed bug when deleting a number or site was not saved (separate signal)
Signed-off-by: Lev Rusanov <30170278+JDM170@users.noreply.github.com>
2025-05-31 20:26:43 +07:00
abdelkader
0f8a6387e9 Merge branch 'master' of https://github.com/abdelkader/vCardEditor 2025-05-30 14:24:15 -04:00
abdelkader
313d70ae05 fixed merge 2025-05-30 14:23:51 -04:00
Abdel
5d8fd20541 Merge pull request #46 from JDM170/issue_40
Added a dialog box with a question about deleting contacts
2025-05-30 14:17:58 -04:00
26dfe4c357 Update
* Fixed indents, removed extra lines and spaces
* Partially removed var statement declaration (proper variable declaration)
* Sorted and removed unnecessary usings
* Correction of typos

Signed-off-by: Lev Rusanov <30170278+JDM170@users.noreply.github.com>
2025-05-31 00:09:41 +07:00
75f6c55cbc Added a dialog box with a question about deleting contacts
Closes abdelkader/vCardEditor#40

Signed-off-by: Lev Rusanov <30170278+JDM170@users.noreply.github.com>
2025-05-30 12:15:40 +07:00
abdelkader
a7c66075a1 changed version number 2025-05-29 08:51:27 -04:00
Abdel
c4a29b678f Merge pull request #45 from JDM170/fix_indexoutofrange_exception
Fixed IndexOutOfRange exception when trying to search
2025-05-29 08:47:20 -04:00
14efe79576 Fixed IndexOutOfRange exception when trying to search
Signed-off-by: Lev Rusanov <30170278+JDM170@users.noreply.github.com>
2025-05-29 10:37:40 +07:00
Abdel
66a77f3b98 Merge pull request #42 from JDM170/fix_work_number
Fixed adding a work number via extended panel
2025-05-28 15:12:09 -04:00
5f33b3adaf fixed adding a work number via extended panel 2024-07-30 15:22:20 +07:00
abdelkader
90dce429bd bug fixed 2024-04-04 12:45:14 -04:00
abdelkader
b17fce8dde 2nd draft for loading folder 2024-04-04 12:42:08 -04:00
abdelkader
afe52e2b18 draft for opening multiple vcard files 2024-04-02 22:45:32 -04:00
abdelkader
c89d9a197c fixed bug 2024-04-02 13:14:58 -04:00
abdelkader
6b57814c5b added a test for SaveSplittedFile 2024-04-02 12:45:31 -04:00
abdelkader
2023043889 draft for saving split files 2024-04-02 12:31:33 -04:00
abdelkader
4401f13fb4 allow to create a empty vcard file 2024-03-30 19:12:35 -04:00
Abdel
c9b9dfb623 Update Releases.txt 2024-03-28 18:53:42 -04:00
Abdel
d79ea44306 Update README.md 2024-03-28 18:46:21 -04:00
abdelkader
724113e304 added image batch export 2024-03-28 18:39:06 -04:00
abdelkader
32bf064f93 added the clear image batch operation 2024-03-28 13:14:43 -04:00
abdelkader
250279cf7c fix when opening new file
feature: image count
2024-03-28 12:10:40 -04:00
abdelkader
62744daa8a fix image type retrieval 2024-03-27 17:22:17 -04:00
abdelkader
4dad2c4151 fix the filter 2024-03-21 08:57:48 -04:00
abdelkader
e89b85411f Fixed the image handlers 2024-03-21 08:47:22 -04:00
abdelkader
81ce797614 keep focus on text box filter 2024-03-21 08:41:38 -04:00
abdelkader
2eb633cfd7 remove unused warning
fix bug
2024-03-21 08:39:29 -04:00
abdelkader
7a575bf526 forget a declaration 2024-03-20 18:17:39 -04:00
abdelkader
ad990213be Fix a multiple firing event 2024-03-20 18:16:55 -04:00
abdelkader
d1296f66b7 show the last inserted address 2024-03-20 17:21:05 -04:00
abdelkader
6b133c27f5 Merge branch 'master' of https://github.com/abdelkader/vCardEditor 2024-03-20 16:04:50 -04:00
abdelkader
ce69d72d46 fix a bug in Add Address 2024-03-20 16:04:28 -04:00
abdelkader
b2edc48f66 Added Parcel, and Preferred 2024-03-20 15:04:18 -04:00
abdelkader
ae3961ec33 rename OverWrite
refactor some code
2024-03-20 09:54:14 -04:00
abdelkader
bad8fee66b arrange some code 2024-03-20 09:50:48 -04:00
abdelkader
4ab38ffea7 move some code around. 2024-03-20 09:48:28 -04:00
abdelkader
2faa7965cd Extra Tab show number of item.
Correct a designer pb
2024-03-20 09:34:17 -04:00
Abdel
3d558b0216 Update README.md
new image
2024-03-19 13:09:52 -04:00
Abdel
ad28a57df8 Update README.md
added a new image
2024-03-19 13:05:34 -04:00
abdelkader
ba9d26f981 some minor changes 2024-03-19 12:28:25 -04:00
abdelkader
abbb03dddf added a button to the extra tab 2024-03-19 12:04:55 -04:00
abdelkader
a989351889 remove the flowlayout panel, and replace it with a simple panel. 2024-03-19 11:48:18 -04:00
abdelkader
22f7f88018 move the declaration, so the autogenerate of vs won't remove them 2024-03-19 11:41:37 -04:00
abdelkader
85bb588f42 saving only if change occured 2024-03-19 08:41:19 -04:00
abdelkader
1bf467f81f catch error when parsing error.
optimize parsing of vcard
save last row
2024-03-18 17:26:20 -04:00
abdelkader
7e8c43e011 refactor 2024-03-18 13:02:42 -04:00
abdelkader
7d09a9ee3e Use Version Class to handle version, and added some error handling 2024-03-18 12:34:47 -04:00
abdelkader
77c1e45bfd the application check if un update is available. 2024-03-18 11:05:52 -04:00
abdelkader
1f234c61e9 #38 fix when changing phone, email, web 2024-03-17 22:25:12 -04:00
abdelkader
927d36a9a0 save phone when changed 2024-03-17 22:05:27 -04:00
abdelkader
653f21b9cc extended, po box were not saved 2024-03-15 14:19:11 -04:00
abdelkader
f509d5da84 Added svg support for qr code 2023-10-19 15:12:35 -04:00
Abdel
9012d355d3 Update README.md 2023-10-18 08:57:42 -04:00
abdelkader
88efefef5e #36 feature added 2023-10-18 08:55:44 -04:00
abdelkader
c244f09b92 Export/display of qr code 2023-10-18 08:52:13 -04:00
abdelkader
1df41c4714 QR Code Draft 2023-10-17 16:05:57 -04:00
abdelkader
634f82b3bb draft for adding/removing phones 2023-07-21 09:13:48 -04:00
abdelkader
016c23d4fc some refactoring 2023-07-07 17:00:52 -04:00
abdelkader
e3171db86d a working draft for adding extra properties 2023-07-07 16:45:05 -04:00
abdelkader
2bf9cd6064 Merge branch 'master' of https://github.com/abdelkader/vCardEditor 2023-07-07 14:39:50 -04:00
abdelkader
81a63e8281 remove unused event, and corrected event binding for text values 2023-07-07 14:39:27 -04:00
Abdel
8cda766954 Update README.md 2023-07-07 09:50:49 -04:00
abdelkader
194ee7e56c draft for extra field 2023-07-07 09:11:22 -04:00
Abdel
a0a6c4900c Update properties.md 2023-07-06 16:52:05 -04:00
Abdel
449c22f576 Update properties.md 2023-07-02 22:19:58 -04:00
Abdel
0020c3e6c8 Update properties.md 2023-07-02 21:57:21 -04:00
Abdel
397279be43 Update properties.md 2023-07-01 22:14:46 -04:00
Abdel
d6f5e2f316 Update properties.md 2023-07-01 12:01:03 -04:00
Abdel
5a87bc104f Create properties.md 2023-07-01 08:36:18 -04:00
142 changed files with 11150 additions and 2440 deletions

View File

@@ -1,35 +1,28 @@
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/B0B2KV8WP)
<a href="https://github.com/abdelkader/vCardEditor/releases/latest/download/vCardEditor.exe"><img src="https://camo.githubusercontent.com/d83fa798b621f1e112646fcc4aa74fff1ff6a8b22f5fc1da5ed8f79ddb4a51cb/68747470733a2f2f62616467656e2e6e65742f6769746875622f72656c656173652f4e61657265656e2f5374726170646f776e2e6a73" alt="Latest release" data-canonical-src="https://badgen.net/github/release/Naereen/Strapdown.js" style="max-width: 100%;"></a>
<a href="https://github.com/abdelkader/vCardEditor/releases/latest/download/vCardEditor.exe"><img src="https://badgen.net/github/release/Naereen/Strapdown.js" alt="Latest release" style="max-width: 100%;"></a>
## vCard Editor
A Simple vcf file Editor. You can export easily edit (modify, delete) entries of a vcf file with this simple tool.
The software is still in **early stage**.
<p align="center"><img src="https://user-images.githubusercontent.com/169070/236289228-106c1489-e01d-400c-968e-92d3e2be74ab.png" width="800"></p>
## ✅ Features
- [x] No need to install anything. Just head to the release section and download the last release version.
- [x] Add/Export images
## 📚 Tech Stack
- 🧰 [Wonderful library of parsing and generating vcf format](https://github.com/drlongnecker/Thought.vCards)
- 📖 [MVP pattern from this example](https://github.com/lennykean/NoteCards)
- 🧰 [SortableBindingList](http://timvw.be/2008/08/02/presenting-the-sortablebindinglistt-take-two/)
- 🧰 [Custom TabControl](https://github.com/r-aghaei/TabControlWithCloseButtonAndAddButton)
- 🧰 [Wonderful library of parsing and generating vcf format](https://github.com/drlongnecker/Thought.vCards)
- 📖 [MVP pattern from this example](https://github.com/lennykean/NoteCards)
- 🧰 [SortableBindingList](http://timvw.be/2008/08/02/presenting-the-sortablebindinglistt-take-two/)
- 🧰 [Custom TabControl](https://github.com/r-aghaei/TabControlWithCloseButtonAndAddButton)
- 🧰 [QRCoder](https://github.com/codebude/QRCoder)
## 📑 Release notes
Check release text file for history.
## 👷 Contributing
## 👷 Contributing and help
Contributions are always welcome! Check ths projet or ths issue page for ideas.
- 📝 [**Report a bug**](https://github.com/abdelkader/vCardEditor/issues)
- 🙋‍ [**Request a feature**](https://github.com/abdelkader/vCardEditor/discussions)

446
properties.md Normal file
View File

@@ -0,0 +1,446 @@
Some properties are not supported by the library **Tought.vCards**.
AGENT, ANNIVERSARY, CALADRURI, CALURI, CLIENTPIDMAP, FBURL, GENDER, KIND, LANG, LOGO, MEMBER, PROFILE, RELATED, SORT-STRING, SOUND.
<h3>Already implemented &#9989; in version 5.0 </h3>
<table>
<tr>
<td rowspan="2"><b>Name</b></td>
<td colspan="3"><b>Property presence</b></td>
<td rowspan="2"><b>Description</b></td>
<td rowspan="2"><b>Example</b></td>
</tr>
<tr>
<td><b>v. 2.1</b></td>
<td><b>v. 3.0</b></td>
<td><b>v. 4.0</b></td>
</tr>
<tr>
<td>ADR</td>
<td>Optional</td>
<td>Optional</td>
<td>Optional</td>
<td>A structured representation of the physical delivery address for the vCard object.</td>
<td>ADR;TYPE=home:;;123 Main St.;Springfield;IL;12345;USA</td>
</tr>
<tr>
<td>BEGIN</td>
<td>Required</td>
<td>Required</td>
<td>Required</td>
<td>All vCards must start with this property.</td>
<td>BEGIN:VCARD</td>
</tr>
<tr>
<td>EMAIL</td>
<td>Optional</td>
<td>Optional</td>
<td>Optional</td>
<td>The address for electronic mail communication with the vCard object.</td>
<td>EMAIL:johndoe@hotmail.com</td>
</tr>
<tr>
<td>END</td>
<td>Required</td>
<td>Required</td>
<td>Required</td>
<td>All vCards must end with this property.</td>
<td>END:VCARD</td>
</tr>
<tr>
<td>FN</td>
<td>Optional</td>
<td>Required</td>
<td>Required</td>
<td>The formatted name string associated with the vCard object.</td>
<td>FN:Dr. John Doe</td>
</tr>
<tr>
<td>N</td>
<td>Required</td>
<td>Required</td>
<td>Optional</td>
<td>A structured representation of the name of the person, place or thing associated with the vCard object. Structure recognizes, in order separated by semicolons: Family Name, Given Name, Additional/Middle Names, Honorific Prefixes, and Honorific Suffixes[3]</td>
<td>N:Doe;John;;Dr;</td>
</tr>
<tr>
<td>NAME</td>
<td>Undefined</td>
<td>Optional</td>
<td>Undefined</td>
<td>Provides a textual representation of the SOURCE property.</td>
<td></td>
</tr>
<tr>
<td>NICKNAME</td>
<td>Undefined</td>
<td>Optional</td>
<td>Optional</td>
<td>One or more descriptive/familiar names for the object represented by this vCard.</td>
<td>NICKNAME:Jon,Johnny</td>
</tr>
<tr>
<td rowspan="6">PHOTO</td>
<td rowspan="6">Optional</td>
<td rowspan="6">Optional</td>
<td rowspan="6">Optional</td>
<td rowspan="6">An image or photograph of the individual associated with the vCard. It may point to an external URL or may be embedded in the vCard as a Base64 encoded block of text.</td>
<td>2.1: PHOTO;JPEG:http://example.com/photo.jpg</td>
</tr>
<tr>
<td>2.1: PHOTO;JPEG;ENCODING=BASE64:[base64-data]</td>
</tr>
<tr><td>3.0: PHOTO;TYPE=JPEG;VALUE=URI:http://example.com/photo.jpg</td>
</tr>
<tr><td>3.0: PHOTO;TYPE=JPEG;ENCODING=b:[base64-data]</td>
</tr>
<tr><td>4.0: PHOTO;MEDIATYPE=image/jpeg:http://example.com/photo.jpg</td>
</tr>
<tr><td>4.0: PHOTO;ENCODING=BASE64;TYPE=JPEG:[base64-data]</td>
</tr>
<tr>
<td>TEL</td>
<td>Optional</td>
<td>Optional</td>
<td>Optional</td>
<td>The canonical number string for a telephone number for telephony communication with the vCard object.</td>
<td>TEL;TYPE=cell:(123) 555-5832</td>
</tr>
<tr>
<td>TITLE</td>
<td>Optional</td>
<td>Optional</td>
<td>Optional</td>
<td>Specifies the job title, functional position or function of the individual associated with the vCard object within an organization.</td>
<td>TITLE:V.P. Research and Development</td>
</tr>
<tr>
<td>VERSION</td>
<td>Required</td>
<td>Required</td>
<td>Required</td>
<td>The version of the vCard specification. In version 4.0, this must come right after the BEGIN property.</td>
<td>VERSION:3.0</td>
</tr>
</table>
<h3>Not implemented yet &#10060; </h3>
<table>
<tr>
<td rowspan="2"><b>Name</b></td>
<td colspan="3"><b>Property presence</b></td>
<td rowspan="2"><b>Description</b></td>
<td rowspan="2"><b>Example</b></td>
</tr>
<tr>
<td><b>v. 2.1</b></td>
<td><b>v. 3.0</b></td>
<td><b>v. 4.0</b></td>
</tr>
<tr>
<td>AGENT</td>
<td>Optional</td>
<td>Optional</td>
<td>Undefined</td>
<td>Information about another person who will act on behalf of the vCard object. Typically this would be an area administrator, assistant, or secretary for the individual. Can be either a URL or an embedded vCard.</td>
<td>AGENT:http://mi6.gov.uk/007</td>
</tr>
<tr>
<td>ANNIVERSARY</td>
<td>Undefined</td>
<td>Undefined</td>
<td>Optional</td>
<td>Defines the person&#39;s anniversary.</td>
<td>ANNIVERSARY:19901021</td>
</tr>
<tr>
<td>BDAY</td>
<td>Optional</td>
<td>Optional</td>
<td>Optional</td>
<td>Date of birth of the individual associated with the vCard.</td>
<td>BDAY:19700310</td>
</tr>
<tr>
<td>CALADRURI</td>
<td>Undefined</td>
<td>Undefined</td>
<td>Optional</td>
<td>A URL to use for sending a scheduling request to the person&#39;s calendar.</td>
<td>CALADRURI:http://example.com/calendar/jdoe</td>
</tr>
<tr>
<td>CALURI</td>
<td>Undefined</td>
<td>Undefined</td>
<td>Optional</td>
<td>A URL to the person&#39;s calendar.</td>
<td>CALURI:http://example.com/calendar/jdoe</td>
</tr>
<tr>
<td>CATEGORIES</td>
<td>Optional</td>
<td>Optional</td>
<td>Optional</td>
<td>A list of &quot;tags&quot; that can be used to describe the object represented by this vCard.</td>
<td>CATEGORIES:swimmer,biker</td>
</tr>
<tr>
<td>CLASS</td>
<td>Undefined</td>
<td>Optional</td>
<td>Undefined</td>
<td>Describes the sensitivity of the information in the vCard.</td>
<td>CLASS:public</td>
</tr>
<tr>
<td>CLIENTPIDMAP</td>
<td>Undefined</td>
<td>Undefined</td>
<td>Optional</td>
<td>Used for synchronizing different revisions of the same vCard.</td>
<td>CLIENTPIDMAP:1;urn:uuid:3df403f4-5924-4bb7-b077-3c711d9eb34b</td>
</tr>
<tr>
<td>FBURL</td>
<td>Undefined</td>
<td>Undefined</td>
<td>Optional</td>
<td>Defines a URL that shows when the person is &quot;free&quot; or &quot;busy&quot; on their calendar.</td>
<td>FBURL:http://example.com/fb/jdoe</td>
</tr>
<tr>
<td>GENDER</td>
<td>Undefined</td>
<td>Undefined</td>
<td>Optional</td>
<td>Defines the person&#39;s gender.</td>
<td>GENDER:F</td>
</tr>
<tr>
<td>GEO</td>
<td>Optional</td>
<td>Optional</td>
<td>Optional</td>
<td>Specifies a latitude and longitude.</td>
<td>2.1, 3.0: GEO:39.95;-75.1667 <br>4.0: GEO:geo:39.95,-75.1667</td>
</tr>
<tr>
<td>IMPP</td>
<td>Undefined</td>
<td>Maybe</td>
<td>Optional</td>
<td>&quot;Defines an instant messenger handle. <br>This property was introduced in a separate RFC when the latest vCard version was 3.0. Therefore, 3.0 vCards might use this property without otherwise declaring it.&quot;</td>
<td>IMPP:aim:johndoe@aol.com</td>
</tr>
<tr>
<td rowspan="6">KEY</td>
<td rowspan="6">Optional</td>
<td rowspan="6">Optional</td>
<td rowspan="6">Optional</td>
<td rowspan="6">The public encryption key associated with the vCard object. It may point to an external URL, may be plain text, or may be embedded in the vCard as a Base64 encoded block of text.</td>
<td>2.1: KEY;PGP:http://example.com/key.pgp</td>
</tr>
<tr>
<td>2.1: KEY;PGP;ENCODING=BASE64:[base64-data]</td>
</tr>
<tr>
<td>3.0: KEY;TYPE=PGP:http://example.com/key.pgp</td>
</tr>
<tr><td>3.0: KEY;TYPE=PGP;ENCODING=b:[base64-data]</td>
</tr>
<tr><td>4.0: KEY;MEDIATYPE=application/pgp-keys:http://example.com/key.pgp</td>
</tr>
<tr><td>4.0: KEY:data:application/pgp-keys;base64,[base64-data]</td>
</tr>
<tr>
<td>KIND</td>
<td>Undefined</td>
<td>Undefined</td>
<td>Optional</td>
<td>Defines the type of entity that this vCard represents: &#39;application&#39;, &#39;individual&#39;, &#39;group&#39;, &#39;location&#39; or &#39;organization&#39;; &#39;x-*&#39; values may be used for experimental purposes.[1][2]</td>
<td>KIND:individual</td>
</tr>
<tr>
<td>LABEL</td>
<td>Optional</td>
<td>Optional</td>
<td>Incorporated without</td>
<td>Represents the actual text that should be put on the mailing label when delivering a physical package to the person/object associated with the vCard (related to the ADR property).<br>Not supported in version 4.0. Instead, this information is stored in the LABEL parameter of the ADR property. Example: ADR;TYPE=home;LABEL=&quot;123 Main St\nNew York, NY 12345&quot;:;;123 Main St;New York;NY;12345;USA</td>
<td>LABEL;TYPE=HOME:123 Main St.\nSpringfield, IL 12345\nUSA</td>
</tr>
<tr>
<td>LANG</td>
<td>Undefined</td>
<td>Undefined</td>
<td>Optional</td>
<td>Defines a language that the person speaks.</td>
<td>LANG:fr-CA</td>
</tr>
<tr>
<td rowspan="6">LOGO</td>
<td rowspan="6">Optional</td>
<td rowspan="6">Optional</td>
<td rowspan="6">Optional</td>
<td rowspan="6">An image or graphic of the logo of the organization that is associated with the individual to which the vCard belongs. It may point to an external URL or may be embedded in the vCard as a Base64 encoded block of text.</td>
<td>2.1: LOGO;PNG:http://example.com/logo.png</td>
</tr>
<tr> <td>2.1: LOGO;PNG;ENCODING=BASE64:[base64-data]</td>
</tr>
<tr><td>3.0: LOGO;TYPE=PNG:http://example.com/logo.png</td>
</tr>
<tr><td>3.0: LOGO;TYPE=PNG;ENCODING=b:[base64-data]</td>
</tr>
<tr><td>4.0: LOGO;MEDIATYPE=image/png:http://example.com/logo.png</td>
</tr>
<tr><td>4.0: LOGO;ENCODING=BASE64;TYPE=PNG:[base64-data]</td>
</tr>
<tr>
<td>MAILER</td>
<td>Optional</td>
<td>Optional</td>
<td>Undefined</td>
<td>Type of email program used.</td>
<td>MAILER:Thunderbird</td>
</tr>
<tr>
<td>MEMBER</td>
<td>Undefined</td>
<td>Undefined</td>
<td>Optional</td>
<td>Defines a member that is part of the group that this vCard represents. Acceptable values include:
<ul><li>a &quot;mailto:&quot; URL containing an email address</li>
<li>a UID which references the member&#39;s own vCard</li>
</ul>
The KIND property must be set to &quot;group&quot; in order to use this property.
</td>
<td>MEMBER:urn:uuid:03a0e51f-d1aa-4385-8a53-e29025acd8af</td>
</tr><tr>
<td>NOTE</td>
<td>Optional</td>
<td>Optional</td>
<td>Optional</td>
<td>Specifies supplemental information or a comment that is associated with the vCard.</td>
<td>NOTE:I am proficient in Tiger-Crane Style,\nand I am more than proficient in the exquisite art of the Samurai sword.</td>
</tr>
<tr>
<td>ORG</td>
<td>Optional</td>
<td>Optional</td>
<td>Optional</td>
<td>The name and optionally the unit(s) of the organization associated with the vCard object. This property is based on the X.520 Organization Name attribute and the X.520 Organization Unit attribute.</td>
<td>ORG:Google;GMail Team;Spam Detection Squad</td>
</tr>
<tr>
<td>PRODID</td>
<td>Undefined</td>
<td>Optional</td>
<td>Optional</td>
<td>The identifier for the product that created the vCard object.</td>
<td>PRODID:-//ONLINE DIRECTORY//NONSGML Version 1//EN</td>
</tr>
<tr>
<td>PROFILE</td>
<td>Optional</td>
<td>Optional</td>
<td>Undefined</td>
<td>States that the vCard is a vCard.</td>
<td>PROFILE:VCARD</td>
</tr>
<tr>
<td>RELATED</td>
<td>Undefined</td>
<td>Undefined</td>
<td>Optional</td>
<td>Another entity that the person is related to. Acceptable values include:
<ul>
<li>a &quot;mailto:&quot; URL containing an email address</li>
<li>a UID which references the person&#39;s own vCard</li>
</ul>
</td>
<td>RELATED;TYPE=friend:urn:uuid:03a0e51f-d1aa-4385-8a53-e29025acd8af</td>
</tr>
<tr>
<td>REV</td>
<td>Optional</td>
<td>Optional</td>
<td>Optional</td>
<td>A timestamp for the last time the vCard was updated.</td>
<td>REV:20121201T134211Z</td>
</tr>
<tr>
<td>ROLE</td>
<td>Optional</td>
<td>Optional</td>
<td>Optional</td>
<td>The role, occupation, or business category of the vCard object within an organization.</td>
<td>ROLE:Executive</td>
</tr>
<tr>
<td>SORT-STRING</td>
<td>Undefined</td>
<td>Optional</td>
<td>Incorporated without</td>
<td>Defines a string that should be used when an application sorts this vCard in some way.<br/>Not supported in version 4.0. Instead, this information is stored in the SORT-AS parameter of the N and/or ORG properties.</td>
<td>SORT-STRING:Doe</td>
</tr>
<tr>
<td rowspan="6">SOUND</td>
<td rowspan="6">Optional</td>
<td rowspan="6">Optional</td>
<td rowspan="6">Optional</td>
<td rowspan="6">By default, if this property is not grouped with other properties it specifies the pronunciation of the FN property of the vCard object. It may point to an external URL or may be embedded in the vCard as a Base64 encoded block of text.</td>
<td>2.1: SOUND;OGG:http://example.com/sound.ogg</td>
</tr>
<tr><td>2.1: SOUND;OGG;ENCODING=BASE64:[base64-data]</td>
</tr>
<tr><td>3.0: SOUND;TYPE=OGG:http://example.com/sound.ogg</td>
</tr>
<tr><td>3.0: SOUND;TYPE=OGG;ENCODING=b:[base64-data]</td>
</tr>
<tr><td>4.0: SOUND;MEDIATYPE=audio/ogg:http://example.com/sound.ogg</td>
</tr>
<tr><td>4.0: SOUND:data:audio/ogg;base64,[base64-data]</td>
</tr>
<tr>
<td>SOURCE</td>
<td>Optional</td>
<td>Optional</td>
<td>Optional</td>
<td>A URL that can be used to get the latest version of this vCard.</td>
<td>SOURCE:http://johndoe.com/vcard.vcf</td>
</tr>
<tr>
<td>TZ</td>
<td>Optional</td>
<td>Optional</td>
<td>Optional</td>
<td>The time zone of the vCard object.</td>
<td>2.1, 3.0: TZ:-0500 <br/>4.0: TZ:America/New_York</td>
</tr>
<tr>
<td>UID</td>
<td>Optional</td>
<td>Optional</td>
<td>Optional</td>
<td>Specifies a value that represents a persistent, globally unique identifier associated with the object.</td>
<td>UID:urn:uuid:da418720-3754-4631-a169-db89a02b831b</td>
</tr>
<tr>
<td>URL</td>
<td>Optional</td>
<td>Optional</td>
<td>Optional</td>
<td>A URL pointing to a website that represents the person in some way.</td>
<td>URL:http://www.johndoe.com</td>
</tr>
<tr>
<td>XML</td>
<td>Undefined</td>
<td>Undefined</td>
<td>Optional</td>
<td>Any XML data that is attached to the vCard. This is used if the vCard was encoded in XML (xCard standard) and the XML document contained elements which are not part of the xCard standard.</td>
<td>XML:&lt;b&gt;Not an xCard XML element&lt;/b&gt;</td>
</tr>
</table>

View File

@@ -0,0 +1,22 @@
using System.Collections.Generic;
namespace vCardEditor
{
public interface ILocalizationProvider
{
string this[string key] { get; }
void SetLanguage(string langCode);
string CurrentLanguage { get; }
IReadOnlyDictionary<string, string> CurrentMessages { get; }
IEnumerable<string> AvailableLanguages { get; }
IEnumerable<string> AvailableLanguageNames { get; }
}
}

View File

@@ -0,0 +1,57 @@
using System.Collections.Generic;
using System.Linq;
using vCardEditor.Libs.TinyJson;
namespace vCardEditor
{
public class JsonLocalizationProvider : ILocalizationProvider
{
private readonly LocalizationFile _localization;
private string _currentLanguage;
public JsonLocalizationProvider(LocalizationFile localization, string defaultLanguage = "en")
{
_localization = localization;
_currentLanguage = defaultLanguage;
}
public void SetLanguage(string langCode)
{
if (_localization.languages.ContainsKey(langCode))
_currentLanguage = langCode;
}
public string this[string key]
{
get
{
if (_localization.languages.TryGetValue(_currentLanguage, out LanguageData lang))
{
if (lang.messages.TryGetValue(key, out string value))
return value;
}
if (_localization.languages.TryGetValue("en", out LanguageData fallbackLang))
{
if (fallbackLang.messages.TryGetValue(key, out string fallbackMsg))
return fallbackMsg;
}
return $"!{key}!";
}
}
public IReadOnlyDictionary<string, string> CurrentMessages =>
_localization.languages.TryGetValue(_currentLanguage, out LanguageData lang)
? lang.messages
: new Dictionary<string, string>();
public IEnumerable<string> AvailableLanguages => _localization.languages.Keys;
public IEnumerable<string> AvailableLanguageNames => _localization.languages?.Values != null
? _localization.languages.Values.Select(l => l?.name).Where(n => !string.IsNullOrEmpty(n))
: new List<string>();
public string CurrentLanguage => _currentLanguage;
}
}

View File

@@ -0,0 +1,78 @@
using System;
using System.Collections.Generic;
using System.Text;
using static QRCoder.QRCodeGenerator;
namespace QRCoder
{
public class AsciiQRCode : AbstractQRCode, IDisposable
{
/// <summary>
/// Constructor without params to be used in COM Objects connections
/// </summary>
public AsciiQRCode() { }
public AsciiQRCode(QRCodeData data) : base(data) { }
/// <summary>
/// Returns a strings that contains the resulting QR code as ASCII chars.
/// </summary>
/// <param name="repeatPerModule">Number of repeated darkColorString/whiteSpaceString per module.</param>
/// <param name="darkColorString">String for use as dark color modules. In case of string make sure whiteSpaceString has the same length.</param>
/// <param name="whiteSpaceString">String for use as white modules (whitespace). In case of string make sure darkColorString has the same length.</param>
/// <param name="endOfLine">End of line separator. (Default: \n)</param>
/// <returns></returns>
public string GetGraphic(int repeatPerModule, string darkColorString = "██", string whiteSpaceString = " ", bool drawQuietZones = true, string endOfLine = "\n")
{
return string.Join(endOfLine, GetLineByLineGraphic(repeatPerModule, darkColorString, whiteSpaceString, drawQuietZones));
}
/// <summary>
/// Returns an array of strings that contains each line of the resulting QR code as ASCII chars.
/// </summary>
/// <param name="repeatPerModule">Number of repeated darkColorString/whiteSpaceString per module.</param>
/// <param name="darkColorString">String for use as dark color modules. In case of string make sure whiteSpaceString has the same length.</param>
/// <param name="whiteSpaceString">String for use as white modules (whitespace). In case of string make sure darkColorString has the same length.</param>
/// <returns></returns>
public string[] GetLineByLineGraphic(int repeatPerModule, string darkColorString = "██", string whiteSpaceString = " ", bool drawQuietZones = true)
{
var qrCode = new List<string>();
//We need to adjust the repeatPerModule based on number of characters in darkColorString
//(we assume whiteSpaceString has the same number of characters)
//to keep the QR code as square as possible.
var quietZonesModifier = (drawQuietZones ? 0 : 8);
var quietZonesOffset = (int)(quietZonesModifier * 0.5);
var adjustmentValueForNumberOfCharacters = darkColorString.Length / 2 != 1 ? darkColorString.Length / 2 : 0;
var verticalNumberOfRepeats = repeatPerModule + adjustmentValueForNumberOfCharacters;
var sideLength = (QrCodeData.ModuleMatrix.Count - quietZonesModifier) * verticalNumberOfRepeats;
for (var y = 0; y < sideLength; y++)
{
var lineBuilder = new StringBuilder();
for (var x = 0; x < QrCodeData.ModuleMatrix.Count - quietZonesModifier; x++)
{
var module = QrCodeData.ModuleMatrix[x + quietZonesOffset][((y + verticalNumberOfRepeats) / verticalNumberOfRepeats - 1)+quietZonesOffset];
for (var i = 0; i < repeatPerModule; i++)
{
lineBuilder.Append(module ? darkColorString : whiteSpaceString);
}
}
qrCode.Add(lineBuilder.ToString());
}
return qrCode.ToArray();
}
}
public static class AsciiQRCodeHelper
{
public static string GetQRCode(string plainText, int pixelsPerModule, string darkColorString, string whiteSpaceString, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1, string endOfLine = "\n", bool drawQuietZones = true)
{
using (var qrGenerator = new QRCodeGenerator())
using (var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode, requestedVersion))
using (var qrCode = new AsciiQRCode(qrCodeData))
return qrCode.GetGraphic(pixelsPerModule, darkColorString, whiteSpaceString, drawQuietZones, endOfLine);
}
}
}

View File

@@ -0,0 +1,28 @@
namespace QRCoder
{
public abstract class AbstractQRCode
{
protected QRCodeData QrCodeData { get; set; }
protected AbstractQRCode() {
}
protected AbstractQRCode(QRCodeData data) {
this.QrCodeData = data;
}
/// <summary>
/// Set a QRCodeData object that will be used to generate QR code. Used in COM Objects connections
/// </summary>
/// <param name="data">Need a QRCodeData object generated by QRCodeGenerator.CreateQrCode()</param>
virtual public void SetQRCodeData(QRCodeData data) {
this.QrCodeData = data;
}
public void Dispose()
{
this.QrCodeData?.Dispose();
this.QrCodeData = null;
}
}
}

View File

@@ -0,0 +1,298 @@
#if NETFRAMEWORK || NETSTANDARD2_0 || NET5_0 || NET6_0_WINDOWS
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using static QRCoder.ArtQRCode;
using static QRCoder.QRCodeGenerator;
// pull request raised to extend library used.
namespace QRCoder
{
#if NET6_0_WINDOWS
[System.Runtime.Versioning.SupportedOSPlatform("windows")]
#endif
public class ArtQRCode : AbstractQRCode, IDisposable
{
/// <summary>
/// Constructor without params to be used in COM Objects connections
/// </summary>
public ArtQRCode() { }
/// <summary>
/// Creates new ArtQrCode object
/// </summary>
/// <param name="data">QRCodeData generated by the QRCodeGenerator</param>
public ArtQRCode(QRCodeData data) : base(data) { }
/// <summary>
/// Renders an art-style QR code with dots as modules. (With default settings: DarkColor=Black, LightColor=White, Background=Transparent, QuietZone=true)
/// </summary>
/// <param name="pixelsPerModule">Amount of px each dark/light module of the QR code shall take place in the final QR code image</param>
/// <returns>QRCode graphic as bitmap</returns>
public Bitmap GetGraphic(int pixelsPerModule)
{
return this.GetGraphic(pixelsPerModule, Color.Black, Color.White, Color.Transparent);
}
/// <summary>
/// Renders an art-style QR code with dots as modules and a background image (With default settings: DarkColor=Black, LightColor=White, Background=Transparent, QuietZone=true)
/// </summary>
/// <param name="backgroundImage">A bitmap object that will be used as background picture</param>
/// <returns>QRCode graphic as bitmap</returns>
public Bitmap GetGraphic(Bitmap backgroundImage = null)
{
return this.GetGraphic(10, Color.Black, Color.White, Color.Transparent, backgroundImage: backgroundImage);
}
/// <summary>
/// Renders an art-style QR code with dots as modules and various user settings
/// </summary>
/// <param name="pixelsPerModule">Amount of px each dark/light module of the QR code shall take place in the final QR code image</param>
/// <param name="darkColor">Color of the dark modules</param>
/// <param name="lightColor">Color of the light modules</param>
/// <param name="backgroundColor">Color of the background</param>
/// <param name="backgroundImage">A bitmap object that will be used as background picture</param>
/// <param name="pixelSizeFactor">Value between 0.0 to 1.0 that defines how big the module dots are. The bigger the value, the less round the dots will be.</param>
/// <param name="drawQuietZones">If true a white border is drawn around the whole QR Code</param>
/// <param name="quietZoneRenderingStyle">Style of the quiet zones</param>
/// <param name="backgroundImageStyle">Style of the background image (if set). Fill=spanning complete graphic; DataAreaOnly=Don't paint background into quietzone</param>
/// <param name="finderPatternImage">Optional image that should be used instead of the default finder patterns</param>
/// <returns>QRCode graphic as bitmap</returns>
public Bitmap GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, Color backgroundColor, Bitmap backgroundImage = null, double pixelSizeFactor = 0.8,
bool drawQuietZones = true, QuietZoneStyle quietZoneRenderingStyle = QuietZoneStyle.Dotted,
BackgroundImageStyle backgroundImageStyle = BackgroundImageStyle.DataAreaOnly, Bitmap finderPatternImage = null)
{
if (pixelSizeFactor > 1)
throw new Exception("The parameter pixelSize must be between 0 and 1. (0-100%)");
int pixelSize = (int)Math.Min(pixelsPerModule, Math.Floor(pixelsPerModule / pixelSizeFactor));
var numModules = QrCodeData.ModuleMatrix.Count - (drawQuietZones ? 0 : 8);
var offset = (drawQuietZones ? 0 : 4);
var size = numModules * pixelsPerModule;
var bitmap = new Bitmap(size, size);
using (var graphics = Graphics.FromImage(bitmap))
{
using (var lightBrush = new SolidBrush(lightColor))
{
using (var darkBrush = new SolidBrush(darkColor))
{
// make background transparent
using (var brush = new SolidBrush(backgroundColor))
graphics.FillRectangle(brush, new Rectangle(0, 0, size, size));
//Render background if set
if (backgroundImage != null)
{
if (backgroundImageStyle == BackgroundImageStyle.Fill)
graphics.DrawImage(Resize(backgroundImage, size), 0, 0);
else if (backgroundImageStyle == BackgroundImageStyle.DataAreaOnly)
{
var bgOffset = 4 - offset;
graphics.DrawImage(Resize(backgroundImage, size - (2 * bgOffset * pixelsPerModule)), 0 + (bgOffset * pixelsPerModule), (bgOffset * pixelsPerModule));
}
}
var darkModulePixel = MakeDotPixel(pixelsPerModule, pixelSize, darkBrush);
var lightModulePixel = MakeDotPixel(pixelsPerModule, pixelSize, lightBrush);
for (var x = 0; x < numModules; x += 1)
{
for (var y = 0; y < numModules; y += 1)
{
var rectangleF = new Rectangle(x * pixelsPerModule, y * pixelsPerModule, pixelsPerModule, pixelsPerModule);
var pixelIsDark = this.QrCodeData.ModuleMatrix[offset + y][offset + x];
var solidBrush = pixelIsDark ? darkBrush : lightBrush;
var pixelImage = pixelIsDark ? darkModulePixel : lightModulePixel;
if (!IsPartOfFinderPattern(x, y, numModules, offset))
if (drawQuietZones && quietZoneRenderingStyle == QuietZoneStyle.Flat && IsPartOfQuietZone(x, y, numModules))
graphics.FillRectangle(solidBrush, rectangleF);
else
graphics.DrawImage(pixelImage, rectangleF);
else if (finderPatternImage == null)
graphics.FillRectangle(solidBrush, rectangleF);
}
}
if (finderPatternImage != null)
{
var finderPatternSize = 7 * pixelsPerModule;
graphics.DrawImage(finderPatternImage, new Rectangle(0, 0, finderPatternSize, finderPatternSize));
graphics.DrawImage(finderPatternImage, new Rectangle(size - finderPatternSize, 0, finderPatternSize, finderPatternSize));
graphics.DrawImage(finderPatternImage, new Rectangle(0, size - finderPatternSize, finderPatternSize, finderPatternSize));
}
graphics.Save();
}
}
}
return bitmap;
}
/// <summary>
/// If the pixelSize is bigger than the pixelsPerModule or may end up filling the Module making a traditional QR code.
/// </summary>
/// <param name="pixelsPerModule">Pixels used per module rendered</param>
/// <param name="pixelSize">Size of the dots</param>
/// <param name="brush">Color of the pixels</param>
/// <returns></returns>
private Bitmap MakeDotPixel(int pixelsPerModule, int pixelSize, SolidBrush brush)
{
// draw a dot
var bitmap = new Bitmap(pixelSize, pixelSize);
using (var graphics = Graphics.FromImage(bitmap))
{
graphics.FillEllipse(brush, new Rectangle(0, 0, pixelSize, pixelSize));
graphics.Save();
}
var pixelWidth = Math.Min(pixelsPerModule, pixelSize);
var margin = Math.Max((pixelsPerModule - pixelWidth) / 2, 0);
// center the dot in the module and crop to stay the right size.
var cropped = new Bitmap(pixelsPerModule, pixelsPerModule);
using (var graphics = Graphics.FromImage(cropped))
{
graphics.DrawImage(bitmap, new Rectangle(margin, margin, pixelWidth, pixelWidth),
new RectangleF(((float)pixelSize - pixelWidth) / 2, ((float)pixelSize - pixelWidth) / 2, pixelWidth, pixelWidth),
GraphicsUnit.Pixel);
graphics.Save();
}
return cropped;
}
/// <summary>
/// Checks if a given module(-position) is part of the quietzone of a QR code
/// </summary>
/// <param name="x">X position</param>
/// <param name="y">Y position</param>
/// <param name="numModules">Total number of modules per row</param>
/// <returns>true, if position is part of quiet zone</returns>
private bool IsPartOfQuietZone(int x, int y, int numModules)
{
return
x < 4 || //left
y < 4 || //top
x > numModules - 5 || //right
y > numModules - 5; //bottom
}
/// <summary>
/// Checks if a given module(-position) is part of one of the three finder patterns of a QR code
/// </summary>
/// <param name="x">X position</param>
/// <param name="y">Y position</param>
/// <param name="numModules">Total number of modules per row</param>
/// <param name="offset">Offset in modules (usually depending on drawQuietZones parameter)</param>
/// <returns>true, if position is part of any finder pattern</returns>
private bool IsPartOfFinderPattern(int x, int y, int numModules, int offset)
{
var cornerSize = 11 - offset;
var outerLimitLow = (numModules - cornerSize - 1);
var outerLimitHigh = outerLimitLow + 8;
var invertedOffset = 4 - offset;
return
(x >= invertedOffset && x < cornerSize && y >= invertedOffset && y < cornerSize) || //Top-left finder pattern
(x > outerLimitLow && x < outerLimitHigh && y >= invertedOffset && y < cornerSize) || //Top-right finder pattern
(x >= invertedOffset && x < cornerSize && y > outerLimitLow && y < outerLimitHigh); //Bottom-left finder pattern
}
/// <summary>
/// Resize to a square bitmap, but maintain the aspect ratio by padding transparently.
/// </summary>
/// <param name="image"></param>
/// <param name="newSize"></param>
/// <returns>Resized image as bitmap</returns>
private Bitmap Resize(Bitmap image, int newSize)
{
if (image == null) return null;
float scale = Math.Min((float)newSize / image.Width, (float)newSize / image.Height);
var scaledWidth = (int)(image.Width * scale);
var scaledHeight = (int)(image.Height * scale);
var offsetX = (newSize - scaledWidth) / 2;
var offsetY = (newSize - scaledHeight) / 2;
var scaledImage = new Bitmap(image, new Size(scaledWidth, scaledHeight));
var bm = new Bitmap(newSize, newSize);
using (Graphics graphics = Graphics.FromImage(bm))
{
using (var brush = new SolidBrush(Color.Transparent))
{
graphics.FillRectangle(brush, new Rectangle(0, 0, newSize, newSize));
graphics.InterpolationMode = InterpolationMode.High;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.DrawImage(scaledImage, new Rectangle(offsetX, offsetY, scaledWidth, scaledHeight));
}
}
return bm;
}
/// <summary>
/// Defines how the quiet zones shall be rendered.
/// </summary>
public enum QuietZoneStyle
{
Dotted,
Flat
}
/// <summary>
/// Defines how the background image (if set) shall be rendered.
/// </summary>
public enum BackgroundImageStyle
{
Fill,
DataAreaOnly
}
}
#if NET6_0_WINDOWS
[System.Runtime.Versioning.SupportedOSPlatform("windows")]
#endif
public static class ArtQRCodeHelper
{
/// <summary>
/// Helper function to create an ArtQRCode graphic with a single function call
/// </summary>
/// <param name="plainText">Text/payload to be encoded inside the QR code</param>
/// <param name="pixelsPerModule">Amount of px each dark/light module of the QR code shall take place in the final QR code image</param>
/// <param name="darkColor">Color of the dark modules</param>
/// <param name="lightColor">Color of the light modules</param>
/// <param name="backgroundColor">Color of the background</param>
/// <param name="eccLevel">The level of error correction data</param>
/// <param name="forceUtf8">Shall the generator be forced to work in UTF-8 mode?</param>
/// <param name="utf8BOM">Should the byte-order-mark be used?</param>
/// <param name="eciMode">Which ECI mode shall be used?</param>
/// <param name="requestedVersion">Set fixed QR code target version.</param>
/// <param name="backgroundImage">A bitmap object that will be used as background picture</param>
/// <param name="pixelSizeFactor">Value between 0.0 to 1.0 that defines how big the module dots are. The bigger the value, the less round the dots will be.</param>
/// <param name="drawQuietZones">If true a white border is drawn around the whole QR Code</param>
/// <param name="quietZoneRenderingStyle">Style of the quiet zones</param>
/// <param name="backgroundImageStyle">Style of the background image (if set). Fill=spanning complete graphic; DataAreaOnly=Don't paint background into quietzone</param>
/// <param name="finderPatternImage">Optional image that should be used instead of the default finder patterns</param>
/// <returns>QRCode graphic as bitmap</returns>
public static Bitmap GetQRCode(string plainText, int pixelsPerModule, Color darkColor, Color lightColor, Color backgroundColor, ECCLevel eccLevel, bool forceUtf8 = false,
bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1, Bitmap backgroundImage = null, double pixelSizeFactor = 0.8,
bool drawQuietZones = true, QuietZoneStyle quietZoneRenderingStyle = QuietZoneStyle.Flat,
BackgroundImageStyle backgroundImageStyle = BackgroundImageStyle.DataAreaOnly, Bitmap finderPatternImage = null)
{
using (var qrGenerator = new QRCodeGenerator())
using (var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode, requestedVersion))
using (var qrCode = new ArtQRCode(qrCodeData))
return qrCode.GetGraphic(pixelsPerModule, darkColor, lightColor, backgroundColor, backgroundImage, pixelSizeFactor, drawQuietZones, quietZoneRenderingStyle, backgroundImageStyle, finderPatternImage);
}
}
}
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -0,0 +1,59 @@
## About
QRCoder is a simple library, written in C#.NET, which enables you to create QR codes. It hasn't any dependencies to other libraries and is available as .NET Framework and .NET Core PCL version on NuGet.
***
## Documentation
👉 *Your first place to go should be our wiki. Here you can find a detailed documentation of the QRCoder and its functions.*
* [**QRCode Wiki**](https://github.com/codebude/QRCoder/wiki)
* [Creator's blog (english)](http://en.code-bude.net/2013/10/17/qrcoder-an-open-source-qr-code-generator-implementation-in-csharp/)
* [Creator's blog (german)](http://code-bude.net/2013/10/17/qrcoder-eine-open-source-qr-code-implementierung-in-csharp/)
### Release Notes
The release notes for the current and all past releases can be read here: [📄 Release Notes](https://github.com/codebude/QRCoder/wiki/Release-notes)
## Usage / Quick start
You only need four lines of code, to generate and view your first QR code.
```csharp
using (QRCodeGenerator qrGenerator = new QRCodeGenerator())
using (QRCodeData qrCodeData = qrGenerator.CreateQrCode("The text which should be encoded.", QRCodeGenerator.ECCLevel.Q))
using (QRCode qrCode = new QRCode(qrCodeData))
{
Bitmap qrCodeImage = qrCode.GetGraphic(20);
}
```
### Optional parameters and overloads
The GetGraphics-method has some more overloads. The first two enable you to set the color of the QR code graphic. One uses Color-class-types, the other HTML hex color notation.
```csharp
//Set color by using Color-class types
Bitmap qrCodeImage = qrCode.GetGraphic(20, Color.DarkRed, Color.PaleGreen, true);
//Set color by using HTML hex color notation
Bitmap qrCodeImage = qrCode.GetGraphic(20, "#000ff0", "#0ff000");
```
The other overload enables you to render a logo/image in the center of the QR code.
```csharp
Bitmap qrCodeImage = qrCode.GetGraphic(20, Color.Black, Color.White, (Bitmap)Bitmap.FromFile("C:\\myimage.png"));
```
There are a plenty of other options. So feel free to read more on that in our wiki: [Wiki: How to use QRCoder](https://github.com/codebude/QRCoder/wiki/How-to-use-QRCoder)
## Help & Issues
If you think you have found a bug or have new ideas or feature requests, then feel free to open a new issue: https://github.com/codebude/QRCoder/issues
In case you have a question about using the library (and couldn't find an answer in our wiki), feel free to open a new question/discussion: https://github.com/codebude/QRCoder/discussions
## Legal information and credits
QRCoder is a project by [Raffael Herrmann](https://raffaelherrmann.de) and was first released in 10/2013. It's licensed under the [MIT license](https://github.com/codebude/QRCoder/blob/master/LICENSE.txt).

View File

@@ -0,0 +1,115 @@
#if NETFRAMEWORK || NETSTANDARD2_0 || NET5_0 || NET6_0_WINDOWS
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using static QRCoder.Base64QRCode;
using static QRCoder.QRCodeGenerator;
namespace QRCoder
{
#if NET6_0_WINDOWS
[System.Runtime.Versioning.SupportedOSPlatform("windows")]
#endif
public class Base64QRCode : AbstractQRCode, IDisposable
{
private QRCode qr;
/// <summary>
/// Constructor without params to be used in COM Objects connections
/// </summary>
public Base64QRCode() {
qr = new QRCode();
}
public Base64QRCode(QRCodeData data) : base(data) {
qr = new QRCode(data);
}
public override void SetQRCodeData(QRCodeData data) {
this.qr.SetQRCodeData(data);
}
public string GetGraphic(int pixelsPerModule)
{
return this.GetGraphic(pixelsPerModule, Color.Black, Color.White, true);
}
public string GetGraphic(int pixelsPerModule, string darkColorHtmlHex, string lightColorHtmlHex, bool drawQuietZones = true, ImageType imgType = ImageType.Png)
{
return this.GetGraphic(pixelsPerModule, ColorTranslator.FromHtml(darkColorHtmlHex), ColorTranslator.FromHtml(lightColorHtmlHex), drawQuietZones, imgType);
}
public string GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, bool drawQuietZones = true, ImageType imgType = ImageType.Png)
{
var base64 = string.Empty;
using (Bitmap bmp = qr.GetGraphic(pixelsPerModule, darkColor, lightColor, drawQuietZones))
{
base64 = BitmapToBase64(bmp, imgType);
}
return base64;
}
public string GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, Bitmap icon, int iconSizePercent = 15, int iconBorderWidth = 6, bool drawQuietZones = true, ImageType imgType = ImageType.Png)
{
var base64 = string.Empty;
using (Bitmap bmp = qr.GetGraphic(pixelsPerModule, darkColor, lightColor, icon, iconSizePercent, iconBorderWidth, drawQuietZones))
{
base64 = BitmapToBase64(bmp, imgType);
}
return base64;
}
private string BitmapToBase64(Bitmap bmp, ImageType imgType)
{
var base64 = string.Empty;
ImageFormat iFormat;
switch (imgType) {
case ImageType.Png:
iFormat = ImageFormat.Png;
break;
case ImageType.Jpeg:
iFormat = ImageFormat.Jpeg;
break;
case ImageType.Gif:
iFormat = ImageFormat.Gif;
break;
default:
iFormat = ImageFormat.Png;
break;
}
using (MemoryStream memoryStream = new MemoryStream())
{
bmp.Save(memoryStream, iFormat);
base64 = Convert.ToBase64String(memoryStream.ToArray(), Base64FormattingOptions.None);
}
return base64;
}
public enum ImageType
{
Gif,
Jpeg,
Png
}
}
#if NET6_0_WINDOWS
[System.Runtime.Versioning.SupportedOSPlatform("windows")]
#endif
public static class Base64QRCodeHelper
{
public static string GetQRCode(string plainText, int pixelsPerModule, string darkColorHtmlHex, string lightColorHtmlHex, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1, bool drawQuietZones = true, ImageType imgType = ImageType.Png)
{
using (var qrGenerator = new QRCodeGenerator())
using (var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode, requestedVersion))
using (var qrCode = new Base64QRCode(qrCodeData))
return qrCode.GetGraphic(pixelsPerModule, darkColorHtmlHex, lightColorHtmlHex, drawQuietZones, imgType);
}
}
}
#endif

View File

@@ -0,0 +1,125 @@
using System;
using System.Collections.Generic;
using System.Linq;
using static QRCoder.QRCodeGenerator;
namespace QRCoder
{
// ReSharper disable once InconsistentNaming
public class BitmapByteQRCode : AbstractQRCode, IDisposable
{
/// <summary>
/// Constructor without params to be used in COM Objects connections
/// </summary>
public BitmapByteQRCode() { }
public BitmapByteQRCode(QRCodeData data) : base(data) { }
public byte[] GetGraphic(int pixelsPerModule)
{
return GetGraphic(pixelsPerModule, new byte[] { 0x00, 0x00, 0x00 }, new byte[] { 0xFF, 0xFF, 0xFF });
}
public byte[] GetGraphic(int pixelsPerModule, string darkColorHtmlHex, string lightColorHtmlHex)
{
return GetGraphic(pixelsPerModule, HexColorToByteArray(darkColorHtmlHex), HexColorToByteArray(lightColorHtmlHex));
}
public byte[] GetGraphic(int pixelsPerModule, byte[] darkColorRgb, byte[] lightColorRgb)
{
var sideLength = this.QrCodeData.ModuleMatrix.Count * pixelsPerModule;
var moduleDark = darkColorRgb.Reverse();
var moduleLight = lightColorRgb.Reverse();
List<byte> bmp = new List<byte>();
//header
bmp.AddRange(new byte[] { 0x42, 0x4D, 0x4C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00 });
//width
bmp.AddRange(IntTo4Byte(sideLength));
//height
bmp.AddRange(IntTo4Byte(sideLength));
//header end
bmp.AddRange(new byte[] { 0x01, 0x00, 0x18, 0x00 });
//draw qr code
for (var x = sideLength-1; x >= 0; x = x - pixelsPerModule)
{
for (int pm = 0; pm < pixelsPerModule; pm++)
{
for (var y = 0; y < sideLength; y = y + pixelsPerModule)
{
var module =
this.QrCodeData.ModuleMatrix[(x + pixelsPerModule)/pixelsPerModule - 1][(y + pixelsPerModule)/pixelsPerModule - 1];
for (int i = 0; i < pixelsPerModule; i++)
{
bmp.AddRange(module ? moduleDark : moduleLight);
}
}
if (sideLength%4 != 0)
{
for (int i = 0; i < sideLength%4; i++)
{
bmp.Add(0x00);
}
}
}
}
//finalize with terminator
bmp.AddRange(new byte[] { 0x00, 0x00 });
return bmp.ToArray();
}
private byte[] HexColorToByteArray(string colorString)
{
if (colorString.StartsWith("#"))
colorString = colorString.Substring(1);
byte[] byteColor = new byte[colorString.Length / 2];
for (int i = 0; i < byteColor.Length; i++)
byteColor[i] = byte.Parse(colorString.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber, System.Globalization.CultureInfo.InvariantCulture);
return byteColor;
}
private byte[] IntTo4Byte(int inp)
{
byte[] bytes = new byte[2];
unchecked
{
bytes[1] = (byte)(inp >> 8);
bytes[0] = (byte)(inp);
}
return bytes;
}
}
public static class BitmapByteQRCodeHelper
{
public static byte[] GetQRCode(string plainText, int pixelsPerModule, string darkColorHtmlHex,
string lightColorHtmlHex, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false,
EciMode eciMode = EciMode.Default, int requestedVersion = -1)
{
using (var qrGenerator = new QRCodeGenerator())
using (
var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode,
requestedVersion))
using (var qrCode = new BitmapByteQRCode(qrCodeData))
return qrCode.GetGraphic(pixelsPerModule, darkColorHtmlHex, lightColorHtmlHex);
}
public static byte[] GetQRCode(string txt, QRCodeGenerator.ECCLevel eccLevel, int size)
{
using (var qrGen = new QRCodeGenerator())
using (var qrCode = qrGen.CreateQrCode(txt, eccLevel))
using (var qrBmp = new BitmapByteQRCode(qrCode))
return qrBmp.GetGraphic(size);
}
}
}

View File

@@ -0,0 +1,16 @@
using System;
namespace QRCoder.Exceptions
{
public class DataTooLongException : Exception
{
public DataTooLongException(string eccLevel, string encodingMode, int maxSizeByte) : base(
$"The given payload exceeds the maximum size of the QR code standard. The maximum size allowed for the choosen paramters (ECC level={eccLevel}, EncodingMode={encodingMode}) is {maxSizeByte} byte."
){}
public DataTooLongException(string eccLevel, string encodingMode, int version, int maxSizeByte) : base(
$"The given payload exceeds the maximum size of the QR code standard. The maximum size allowed for the choosen paramters (ECC level={eccLevel}, EncodingMode={encodingMode}, FixedVersion={version}) is {maxSizeByte} byte."
)
{ }
}
}

View File

@@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
namespace QRCoder.Extensions
{
/// <summary>
/// Used to represent a string value for a value in an enum
/// </summary>
public class StringValueAttribute : Attribute
{
#region Properties
/// <summary>
/// Holds the alue in an enum
/// </summary>
public string StringValue { get; protected set; }
#endregion
/// <summary>
/// Init a StringValue Attribute
/// </summary>
/// <param name="value"></param>
public StringValueAttribute(string value)
{
this.StringValue = value;
}
}
public static class CustomExtensions
{
/// <summary>
/// Will get the string value for a given enum's value
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static string GetStringValue(this Enum value)
{
#if NETSTANDARD1_3
var fieldInfo = value.GetType().GetRuntimeField(value.ToString());
#else
var fieldInfo = value.GetType().GetField(value.ToString());
#endif
var attr = fieldInfo.GetCustomAttributes(typeof(StringValueAttribute), false) as StringValueAttribute[];
return attr.Length > 0 ? attr[0].StringValue : null;
}
}
}

View File

@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace QRCoder.Framework4._0Methods
{
class Stream4Methods
{
public static void CopyTo(System.IO.Stream input, System.IO.Stream output)
{
byte[] buffer = new byte[16 * 1024];
int bytesRead;
while ((bytesRead = input.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, bytesRead);
}
}
}
}

View File

@@ -0,0 +1,48 @@
using System;
namespace QRCoder
{
internal static class String40Methods
{
/// <summary>
/// The IsNullOrWhiteSpace method from Framework4.0
/// </summary>
/// <returns>
/// <c>true</c> if the <paramref name="value"/> is null or white space; otherwise, <c>false</c>.
/// </returns>
public static bool IsNullOrWhiteSpace(String value)
{
if (value == null) return true;
for (int i = 0; i < value.Length; i++)
{
if (!Char.IsWhiteSpace(value[i])) return false;
}
return true;
}
public static string ReverseString(string str)
{
char[] chars = str.ToCharArray();
char[] result = new char[chars.Length];
for (int i = 0, j = str.Length - 1; i < str.Length; i++, j--)
{
result[i] = chars[j];
}
return new string(result);
}
public static bool IsAllDigit(string str)
{
foreach (var c in str)
{
if (!char.IsDigit(c))
{
return false;
}
}
return true;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,243 @@
#if NETFRAMEWORK || NETSTANDARD2_0 || NET5_0 || NET6_0_WINDOWS
using System;
using System.Collections.Generic;
using System.Drawing.Imaging;
using System.Globalization;
using System.IO;
using System.Linq;
using static QRCoder.QRCodeGenerator;
/* This renderer is inspired by RemusVasii: https://github.com/codebude/QRCoder/issues/223 */
namespace QRCoder
{
#if NET6_0_WINDOWS
[System.Runtime.Versioning.SupportedOSPlatform("windows")]
#endif
// ReSharper disable once InconsistentNaming
public class PdfByteQRCode : AbstractQRCode, IDisposable
{
private readonly byte[] pdfBinaryComment = new byte[] { 0x25, 0xe2, 0xe3, 0xcf, 0xd3 };
/// <summary>
/// Constructor without params to be used in COM Objects connections
/// </summary>
public PdfByteQRCode() { }
public PdfByteQRCode(QRCodeData data) : base(data) { }
/// <summary>
/// Creates a PDF document with a black & white QR code
/// </summary>
/// <param name="pixelsPerModule"></param>
/// <returns></returns>
public byte[] GetGraphic(int pixelsPerModule)
{
return GetGraphic(pixelsPerModule, "#000000", "#ffffff");
}
/// <summary>
/// Takes hexadecimal color string #000000 and returns byte[]{ 0, 0, 0 }
/// </summary>
/// <param name="colorString">Color in HEX format like #ffffff</param>
/// <returns></returns>
private byte[] HexColorToByteArray(string colorString)
{
if (colorString.StartsWith("#"))
colorString = colorString.Substring(1);
byte[] byteColor = new byte[colorString.Length / 2];
for (int i = 0; i < byteColor.Length; i++)
byteColor[i] = byte.Parse(colorString.Substring(i * 2, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
return byteColor;
}
/// <summary>
/// Creates a PDF document with given colors DPI and quality
/// </summary>
/// <param name="pixelsPerModule"></param>
/// <param name="darkColorHtmlHex"></param>
/// <param name="lightColorHtmlHex"></param>
/// <param name="dpi"></param>
/// <param name="jpgQuality"></param>
/// <returns></returns>
public byte[] GetGraphic(int pixelsPerModule, string darkColorHtmlHex, string lightColorHtmlHex, int dpi = 150, long jpgQuality = 85)
{
byte[] jpgArray = null, pngArray = null;
var imgSize = QrCodeData.ModuleMatrix.Count * pixelsPerModule;
var pdfMediaSize = (imgSize * 72 / dpi).ToString(CultureInfo.InvariantCulture);
//Get QR code image
using (var qrCode = new PngByteQRCode(QrCodeData))
{
pngArray = qrCode.GetGraphic(pixelsPerModule, HexColorToByteArray(darkColorHtmlHex), HexColorToByteArray(lightColorHtmlHex));
}
//Create image and transofrm to JPG
using (var msPng = new MemoryStream())
{
msPng.Write(pngArray, 0, pngArray.Length);
var img = System.Drawing.Image.FromStream(msPng);
using (var msJpeg = new MemoryStream())
{
// Create JPEG with specified quality
var jpgImageCodecInfo = ImageCodecInfo.GetImageEncoders().First(x => x.MimeType == "image/jpeg");
var jpgEncoderParameters = new EncoderParameters(1) {
Param = new EncoderParameter[]{ new EncoderParameter(Encoder.Quality, jpgQuality) }
};
img.Save(msJpeg, jpgImageCodecInfo, jpgEncoderParameters);
jpgArray = msJpeg.ToArray();
}
}
//Create PDF document
using (var stream = new MemoryStream())
{
var writer = new StreamWriter(stream, System.Text.Encoding.GetEncoding("ASCII"));
var xrefs = new List<long>();
writer.Write("%PDF-1.5\r\n");
writer.Flush();
stream.Write(pdfBinaryComment, 0, pdfBinaryComment.Length);
writer.WriteLine();
writer.Flush();
xrefs.Add(stream.Position);
writer.Write(
xrefs.Count.ToString() + " 0 obj\r\n" +
"<<\r\n" +
"/Type /Catalog\r\n" +
"/Pages 2 0 R\r\n" +
">>\r\n" +
"endobj\r\n"
);
writer.Flush();
xrefs.Add(stream.Position);
writer.Write(
xrefs.Count.ToString() + " 0 obj\r\n" +
"<<\r\n" +
"/Count 1\r\n" +
"/Kids [ <<\r\n" +
"/Type /Page\r\n" +
"/Parent 2 0 R\r\n" +
"/MediaBox [0 0 " + pdfMediaSize + " " + pdfMediaSize + "]\r\n" +
"/Resources << /ProcSet [ /PDF /ImageC ]\r\n" +
"/XObject << /Im1 4 0 R >> >>\r\n" +
"/Contents 3 0 R\r\n" +
">> ]\r\n" +
">>\r\n" +
"endobj\r\n"
);
var X = "q\r\n" +
pdfMediaSize + " 0 0 " + pdfMediaSize + " 0 0 cm\r\n" +
"/Im1 Do\r\n" +
"Q";
writer.Flush();
xrefs.Add(stream.Position);
writer.Write(
xrefs.Count.ToString() + " 0 obj\r\n" +
"<< /Length " + X.Length.ToString() + " >>\r\n" +
"stream\r\n" +
X + "endstream\r\n" +
"endobj\r\n"
);
writer.Flush();
xrefs.Add(stream.Position);
writer.Write(
xrefs.Count.ToString() + " 0 obj\r\n" +
"<<\r\n" +
"/Name /Im1\r\n" +
"/Type /XObject\r\n" +
"/Subtype /Image\r\n" +
"/Width " + imgSize.ToString() + "/Height " + imgSize.ToString() + "/Length 5 0 R\r\n" +
"/Filter /DCTDecode\r\n" +
"/ColorSpace /DeviceRGB\r\n" +
"/BitsPerComponent 8\r\n" +
">>\r\n" +
"stream\r\n"
);
writer.Flush();
stream.Write(jpgArray, 0, jpgArray.Length);
writer.Write(
"\r\n" +
"endstream\r\n" +
"endobj\r\n"
);
writer.Flush();
xrefs.Add(stream.Position);
writer.Write(
xrefs.Count.ToString() + " 0 obj\r\n" +
jpgArray.Length.ToString() + " endobj\r\n"
);
writer.Flush();
var startxref = stream.Position;
writer.Write(
"xref\r\n" +
"0 " + (xrefs.Count + 1).ToString() + "\r\n" +
"0000000000 65535 f\r\n"
);
foreach (var refValue in xrefs)
writer.Write(refValue.ToString("0000000000") + " 00000 n\r\n");
writer.Write(
"trailer\r\n" +
"<<\r\n" +
"/Size " + (xrefs.Count + 1).ToString() + "\r\n" +
"/Root 1 0 R\r\n" +
">>\r\n" +
"startxref\r\n" +
startxref.ToString() + "\r\n" +
"%%EOF"
);
writer.Flush();
stream.Position = 0;
return stream.ToArray();
}
}
}
#if NET6_0_WINDOWS
[System.Runtime.Versioning.SupportedOSPlatform("windows")]
#endif
public static class PdfByteQRCodeHelper
{
public static byte[] GetQRCode(string plainText, int pixelsPerModule, string darkColorHtmlHex,
string lightColorHtmlHex, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false,
EciMode eciMode = EciMode.Default, int requestedVersion = -1)
{
using (var qrGenerator = new QRCodeGenerator())
using (
var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode,
requestedVersion))
using (var qrCode = new PdfByteQRCode(qrCodeData))
return qrCode.GetGraphic(pixelsPerModule, darkColorHtmlHex, lightColorHtmlHex);
}
public static byte[] GetQRCode(string txt, ECCLevel eccLevel, int size)
{
using (var qrGen = new QRCodeGenerator())
using (var qrCode = qrGen.CreateQrCode(txt, eccLevel))
using (var qrBmp = new PdfByteQRCode(qrCode))
return qrBmp.GetGraphic(size);
}
}
}
#endif

View File

@@ -0,0 +1,341 @@
using System;
using System.IO;
using System.IO.Compression;
using static QRCoder.QRCodeGenerator;
namespace QRCoder
{
public sealed class PngByteQRCode : AbstractQRCode, IDisposable
{
/// <summary>
/// Constructor without params to be used in COM Objects connections
/// </summary>
public PngByteQRCode() { }
public PngByteQRCode(QRCodeData data) : base(data)
{
}
/// <summary>
/// Creates a black & white PNG of the QR code, using 1-bit grayscale.
/// </summary>
public byte[] GetGraphic(int pixelsPerModule, bool drawQuietZones = true)
{
using (var png = new PngBuilder())
{
var size = (this.QrCodeData.ModuleMatrix.Count - (drawQuietZones ? 0 : 8)) * pixelsPerModule;
png.WriteHeader(size, size, 1, PngBuilder.ColorType.Greyscale);
png.WriteScanlines(this.DrawScanlines(pixelsPerModule, drawQuietZones));
png.WriteEnd();
return png.GetBytes();
}
}
/// <summary>
/// Creates 2-color PNG of the QR code, using 1-bit indexed color. Accepts 3-byte RGB colors for normal images and 4-byte RGBA-colors for transparent images.
/// </summary>
public byte[] GetGraphic(int pixelsPerModule, byte[] darkColorRgba, byte[] lightColorRgba, bool drawQuietZones = true)
{
using (var png = new PngBuilder())
{
var size = (this.QrCodeData.ModuleMatrix.Count - (drawQuietZones ? 0 : 8)) * pixelsPerModule;
png.WriteHeader(size, size, 1, PngBuilder.ColorType.Indexed);
png.WritePalette(darkColorRgba, lightColorRgba);
png.WriteScanlines(this.DrawScanlines(pixelsPerModule, drawQuietZones));
png.WriteEnd();
return png.GetBytes();
}
}
/// <summary>
/// Creates a bitmap where each pixel is represented by a single bit, dark = 0 and light = 1.
/// </summary>
private byte[] DrawScanlines(int pixelsPerModule, bool drawQuietZones)
{
var moduleMatrix = this.QrCodeData.ModuleMatrix;
var matrixSize = moduleMatrix.Count - (drawQuietZones ? 0 : 8);
var quietZoneOffset = (drawQuietZones ? 0 : 4);
var bytesPerScanline = (matrixSize * pixelsPerModule + 7) / 8 + 1; // A monochrome scanline is one byte for filter type then one bit per pixel.
var scanlines = new byte[bytesPerScanline * matrixSize * pixelsPerModule];
for (var y = 0; y < matrixSize; y++)
{
var modules = moduleMatrix[y+quietZoneOffset];
var scanlineOffset = y * pixelsPerModule * bytesPerScanline;
// Draw a scanline with the modules from the QR code.
for (var x = 0; x < matrixSize; x++)
{
if (modules[x + quietZoneOffset])
{
continue;
}
var pixelIndex = x * pixelsPerModule;
var endIndex = pixelIndex + pixelsPerModule;
for (; pixelIndex < endIndex; pixelIndex++)
{
scanlines[scanlineOffset + 1 + pixelIndex / 8] |= (byte)(0x80 >> (pixelIndex % 8));
}
}
// Copy the scanline required number of times.
for (var copyCount = 1; copyCount < pixelsPerModule; copyCount++)
{
Array.Copy(scanlines, scanlineOffset, scanlines, scanlineOffset + copyCount * bytesPerScanline, bytesPerScanline);
}
}
return scanlines;
}
/// <summary>
/// Writes the chunks that make up a PNG file.
/// </summary>
/// <remarks>
/// See https://www.w3.org/TR/2003/REC-PNG-20031110 and https://www.ietf.org/rfc/rfc1950.txt.
/// </remarks>
private sealed class PngBuilder : IDisposable
{
private static readonly byte[] PngSignature = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A };
private static readonly uint[] CrcTable = {
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
};
// ReSharper disable InconsistentNaming
// Chunk types
private static readonly byte[] IHDR = { 73, 72, 68, 82 };
private static readonly byte[] IDAT = { 73, 68, 65, 84 };
private static readonly byte[] IEND = { 73, 69, 78, 68 };
private static readonly byte[] PLTE = { 80, 76, 84, 69 };
private static readonly byte[] tRNS = { 116, 82, 78, 83 };
// ReSharper enable InconsistentNaming
public enum ColorType : byte
{
Greyscale = 0,
Indexed = 3
}
private MemoryStream stream = new MemoryStream();
public void Dispose()
{
this.stream?.Dispose();
this.stream = null;
}
public byte[] GetBytes()
{
var bytes = this.stream.ToArray();
// Enumerate chunks in file and insert their CRC32 checksums.
var chunkOffset = PngSignature.Length;
while (chunkOffset < bytes.Length)
{
// Read length field.
var dataLength = (bytes[chunkOffset] << 24) | (bytes[chunkOffset + 1] << 16) | (bytes[chunkOffset + 2] << 8) | bytes[chunkOffset + 3];
// CRC is computed from type and data fields.
var crc = Crc32(bytes, chunkOffset + 4, dataLength + 4);
// Write CRC to end of chunk.
var crcOffset = chunkOffset + 8 + dataLength;
bytes[crcOffset + 0] = (byte)(crc >> 24);
bytes[crcOffset + 1] = (byte)(crc >> 16);
bytes[crcOffset + 2] = (byte)(crc >> 8);
bytes[crcOffset + 3] = (byte)crc;
// Seek to next chunk.
chunkOffset = crcOffset + 4;
}
return bytes;
}
/// <summary>
/// Writes the IHDR chunk. This must be the first chunk in the file.
/// </summary>
public void WriteHeader(int width, int height, byte bitDepth, ColorType colorType)
{
this.stream.Write(PngSignature, 0, PngSignature.Length);
this.WriteChunkStart(IHDR, 13);
// Size.
this.WriteIntBigEndian((uint)width);
this.WriteIntBigEndian((uint)height);
// Color.
this.stream.WriteByte(bitDepth);
this.stream.WriteByte((byte)colorType);
// Constants.
this.stream.WriteByte(0);
this.stream.WriteByte(0);
this.stream.WriteByte(0);
this.WriteChunkEnd();
}
/// <summary>
/// Writes the PLTE chunk, and also the tRNS chunk if necessary. Must come before the IDAT chunk.
/// </summary>
public void WritePalette(params byte[][] rgbaColors)
{
const int Red = 0, Green = 1, Blue = 2, Alpha = 3;
const byte Opaque = 255;
var hasAlpha = false;
this.WriteChunkStart(PLTE, 3 * rgbaColors.Length);
foreach (var color in rgbaColors)
{
hasAlpha |= color.Length > Alpha && color[Alpha] < Opaque;
this.stream.WriteByte(color[Red]);
this.stream.WriteByte(color[Green]);
this.stream.WriteByte(color[Blue]);
}
this.WriteChunkEnd();
if (!hasAlpha)
{
return;
}
this.WriteChunkStart(tRNS, rgbaColors.Length);
foreach (var color in rgbaColors)
{
this.stream.WriteByte(color.Length > Alpha ? color[Alpha] : Opaque);
}
this.WriteChunkEnd();
}
/// <summary>
/// Writes the IDAT chunk with the actual picture.
/// </summary>
public void WriteScanlines(byte[] scanlines)
{
using (var idatStream = new MemoryStream())
{
Deflate(idatStream, scanlines);
this.WriteChunkStart(IDAT, (int)(idatStream.Length + 6));
// Deflate header.
this.stream.WriteByte(0x78); // 8 Deflate algorithm, 7 max window size
this.stream.WriteByte(0x9C); // Check bits.
// Compressed data.
idatStream.Position = 0;
#if NET35
idatStream.WriteTo(this.stream);
#else
idatStream.CopyTo(this.stream);
#endif
// Deflate checksum.
var adler = Adler32(scanlines, 0, scanlines.Length);
this.WriteIntBigEndian(adler);
this.WriteChunkEnd();
}
}
/// <summary>
/// Writes the IEND chunk. This must be the last chunk in the file.
/// </summary>
public void WriteEnd()
{
this.WriteChunkStart(IEND, 0);
this.WriteChunkEnd();
}
private void WriteChunkStart(byte[] type, int length)
{
this.WriteIntBigEndian((uint)length);
this.stream.Write(type, 0, 4);
}
private void WriteChunkEnd()
{
// Reserves 4 bytes space for crc32 so GetBytes can add it later.
this.stream.SetLength(this.stream.Length + 4);
this.stream.Position += 4;
}
private void WriteIntBigEndian(uint value)
{
this.stream.WriteByte((byte)(value >> 24));
this.stream.WriteByte((byte)(value >> 16));
this.stream.WriteByte((byte)(value >> 8));
this.stream.WriteByte((byte)value);
}
private static void Deflate(Stream output, byte[] bytes)
{
using (var deflateStream = new DeflateStream(output, CompressionMode.Compress, leaveOpen: true))
{
deflateStream.Write(bytes, 0, bytes.Length);
}
}
// Reference implementation from RFC 1950. Not optimized.
private static uint Adler32(byte[] data, int index, int length)
{
const uint Base = 65521;
uint s1 = 1, s2 = 0;
var end = index + length;
for (var n = index; n < end; n++)
{
s1 = (s1 + data[n]) % Base;
s2 = (s2 + s1) % Base;
}
return (s2 << 16) + s1;
}
// Reference implementation from REC-PNG-20031110. Not optimized.
private static uint Crc32(byte[] data, int index, int length)
{
var c = 0xffffffff;
var end = index + length;
for (var n = index; n < end; n++)
{
c = CrcTable[(c ^ data[n]) & 0xff] ^ (c >> 8);
}
return c ^ 0xffffffff;
}
}
}
public static class PngByteQRCodeHelper
{
public static byte[] GetQRCode(string plainText, int pixelsPerModule, byte[] darkColorRgba, byte[] lightColorRgba, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1, bool drawQuietZones = true)
{
using (var qrGenerator = new QRCodeGenerator())
using (var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode, requestedVersion))
using (var qrCode = new PngByteQRCode(qrCodeData))
return qrCode.GetGraphic(pixelsPerModule, darkColorRgba, lightColorRgba, drawQuietZones);
}
public static byte[] GetQRCode(string txt, QRCodeGenerator.ECCLevel eccLevel, int size, bool drawQuietZones = true)
{
using (var qrGen = new QRCodeGenerator())
using (var qrCode = qrGen.CreateQrCode(txt, eccLevel))
using (var qrPng = new PngByteQRCode(qrCode))
return qrPng.GetGraphic(size, drawQuietZones);
}
}
}

View File

@@ -0,0 +1,161 @@
#if NETFRAMEWORK || NETSTANDARD2_0 || NET5_0 || NET6_0_WINDOWS
using System;
using System.Drawing;
using static QRCoder.QRCodeGenerator;
namespace QRCoder
{
#if NET6_0_WINDOWS
[System.Runtime.Versioning.SupportedOSPlatform("windows")]
#endif
public class PostscriptQRCode : AbstractQRCode, IDisposable
{
/// <summary>
/// Constructor without params to be used in COM Objects connections
/// </summary>
public PostscriptQRCode() { }
public PostscriptQRCode(QRCodeData data) : base(data) { }
public string GetGraphic(int pointsPerModule, bool epsFormat = false)
{
var viewBox = new Size(pointsPerModule * this.QrCodeData.ModuleMatrix.Count, pointsPerModule * this.QrCodeData.ModuleMatrix.Count);
return this.GetGraphic(viewBox, Color.Black, Color.White, true, epsFormat);
}
public string GetGraphic(int pointsPerModule, Color darkColor, Color lightColor, bool drawQuietZones = true, bool epsFormat = false)
{
var viewBox = new Size(pointsPerModule * this.QrCodeData.ModuleMatrix.Count, pointsPerModule * this.QrCodeData.ModuleMatrix.Count);
return this.GetGraphic(viewBox, darkColor, lightColor, drawQuietZones, epsFormat);
}
public string GetGraphic(int pointsPerModule, string darkColorHex, string lightColorHex, bool drawQuietZones = true, bool epsFormat = false)
{
var viewBox = new Size(pointsPerModule * this.QrCodeData.ModuleMatrix.Count, pointsPerModule * this.QrCodeData.ModuleMatrix.Count);
return this.GetGraphic(viewBox, darkColorHex, lightColorHex, drawQuietZones, epsFormat);
}
public string GetGraphic(Size viewBox, bool drawQuietZones = true, bool epsFormat = false)
{
return this.GetGraphic(viewBox, Color.Black, Color.White, drawQuietZones, epsFormat);
}
public string GetGraphic(Size viewBox, string darkColorHex, string lightColorHex, bool drawQuietZones = true, bool epsFormat = false)
{
return this.GetGraphic(viewBox, ColorTranslator.FromHtml(darkColorHex), ColorTranslator.FromHtml(lightColorHex), drawQuietZones, epsFormat);
}
public string GetGraphic(Size viewBox, Color darkColor, Color lightColor, bool drawQuietZones = true, bool epsFormat = false)
{
var offset = drawQuietZones ? 0 : 4;
var drawableModulesCount = this.QrCodeData.ModuleMatrix.Count - (drawQuietZones ? 0 : offset * 2);
var pointsPerModule = (double)Math.Min(viewBox.Width, viewBox.Height) / (double)drawableModulesCount;
string psFile = string.Format(psHeader, new object[] {
DateTime.Now.ToString("s"), CleanSvgVal(viewBox.Width), CleanSvgVal(pointsPerModule),
epsFormat ? "EPSF-3.0" : string.Empty
});
psFile += string.Format(psFunctions, new object[] {
CleanSvgVal(darkColor.R /255.0), CleanSvgVal(darkColor.G /255.0), CleanSvgVal(darkColor.B /255.0),
CleanSvgVal(lightColor.R /255.0), CleanSvgVal(lightColor.G /255.0), CleanSvgVal(lightColor.B /255.0),
drawableModulesCount
});
for (int xi = offset; xi < offset + drawableModulesCount; xi++)
{
if (xi > offset)
psFile += "nl\n";
for (int yi = offset; yi < offset + drawableModulesCount; yi++)
{
psFile += (this.QrCodeData.ModuleMatrix[xi][yi] ? "f " : "b ");
}
psFile += "\n";
}
return psFile + psFooter;
}
private string CleanSvgVal(double input)
{
//Clean double values for international use/formats
return input.ToString(System.Globalization.CultureInfo.InvariantCulture);
}
private const string psHeader = @"%!PS-Adobe-3.0 {3}
%%Creator: QRCoder.NET
%%Title: QRCode
%%CreationDate: {0}
%%DocumentData: Clean7Bit
%%Origin: 0
%%DocumentMedia: Default {1} {1} 0 () ()
%%BoundingBox: 0 0 {1} {1}
%%LanguageLevel: 2
%%Pages: 1
%%Page: 1 1
%%EndComments
%%BeginConstants
/sz {1} def
/sc {2} def
%%EndConstants
%%BeginFeature: *PageSize Default
<< /PageSize [ sz sz ] /ImagingBBox null >> setpagedevice
%%EndFeature
";
private const string psFunctions = @"%%BeginFunctions
/csquare {{
newpath
0 0 moveto
0 1 rlineto
1 0 rlineto
0 -1 rlineto
closepath
setrgbcolor
fill
}} def
/f {{
{0} {1} {2} csquare
1 0 translate
}} def
/b {{
1 0 translate
}} def
/background {{
{3} {4} {5} csquare
}} def
/nl {{
-{6} -1 translate
}} def
%%EndFunctions
%%BeginBody
0 0 moveto
gsave
sz sz scale
background
grestore
gsave
sc sc scale
0 {6} 1 sub translate
";
private const string psFooter = @"%%EndBody
grestore
showpage
%%EOF
";
}
#if NET6_0_WINDOWS
[System.Runtime.Versioning.SupportedOSPlatform("windows")]
#endif
public static class PostscriptQRCodeHelper
{
public static string GetQRCode(string plainText, int pointsPerModule, string darkColorHex, string lightColorHex, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1, bool drawQuietZones = true, bool epsFormat = false)
{
using (var qrGenerator = new QRCodeGenerator())
using (var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode, requestedVersion))
using (var qrCode = new PostscriptQRCode(qrCodeData))
return qrCode.GetGraphic(pointsPerModule, darkColorHex, lightColorHex, drawQuietZones, epsFormat);
}
}
}
#endif

View File

@@ -0,0 +1,138 @@
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using static QRCoder.QRCodeGenerator;
namespace QRCoder
{
public class QRCode : AbstractQRCode, IDisposable
{
/// <summary>
/// Constructor without params to be used in COM Objects connections
/// </summary>
public QRCode() { }
public QRCode(QRCodeData data) : base(data) {}
public Bitmap GetGraphic(int pixelsPerModule)
{
return this.GetGraphic(pixelsPerModule, Color.Black, Color.White, true);
}
public Bitmap GetGraphic(int pixelsPerModule, string darkColorHtmlHex, string lightColorHtmlHex, bool drawQuietZones = true)
{
return this.GetGraphic(pixelsPerModule, ColorTranslator.FromHtml(darkColorHtmlHex), ColorTranslator.FromHtml(lightColorHtmlHex), drawQuietZones);
}
public Bitmap GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, bool drawQuietZones = true)
{
var size = (this.QrCodeData.ModuleMatrix.Count - (drawQuietZones ? 0 : 8)) * pixelsPerModule;
var offset = drawQuietZones ? 0 : 4 * pixelsPerModule;
var bmp = new Bitmap(size, size);
using (var gfx = Graphics.FromImage(bmp))
using (var lightBrush = new SolidBrush(lightColor))
using (var darkBrush = new SolidBrush(darkColor))
{
for (var x = 0; x < size + offset; x = x + pixelsPerModule)
{
for (var y = 0; y < size + offset; y = y + pixelsPerModule)
{
var module = this.QrCodeData.ModuleMatrix[(y + pixelsPerModule) / pixelsPerModule - 1][(x + pixelsPerModule) / pixelsPerModule - 1];
if (module)
{
gfx.FillRectangle(darkBrush, new Rectangle(x - offset, y - offset, pixelsPerModule, pixelsPerModule));
}
else
{
gfx.FillRectangle(lightBrush, new Rectangle(x - offset, y - offset, pixelsPerModule, pixelsPerModule));
}
}
}
gfx.Save();
}
return bmp;
}
public Bitmap GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, Bitmap icon=null, int iconSizePercent=15, int iconBorderWidth = 0, bool drawQuietZones = true, Color? iconBackgroundColor = null)
{
var size = (this.QrCodeData.ModuleMatrix.Count - (drawQuietZones ? 0 : 8)) * pixelsPerModule;
var offset = drawQuietZones ? 0 : 4 * pixelsPerModule;
var bmp = new Bitmap(size, size, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
using (var gfx = Graphics.FromImage(bmp))
using (var lightBrush = new SolidBrush(lightColor))
using (var darkBrush = new SolidBrush(darkColor))
{
gfx.InterpolationMode = InterpolationMode.HighQualityBicubic;
gfx.CompositingQuality = CompositingQuality.HighQuality;
gfx.Clear(lightColor);
var drawIconFlag = icon != null && iconSizePercent > 0 && iconSizePercent <= 100;
for (var x = 0; x < size + offset; x = x + pixelsPerModule)
{
for (var y = 0; y < size + offset; y = y + pixelsPerModule)
{
var moduleBrush = this.QrCodeData.ModuleMatrix[(y + pixelsPerModule) / pixelsPerModule - 1][(x + pixelsPerModule) / pixelsPerModule - 1] ? darkBrush : lightBrush;
gfx.FillRectangle(moduleBrush , new Rectangle(x - offset, y - offset, pixelsPerModule, pixelsPerModule));
}
}
if (drawIconFlag)
{
float iconDestWidth = iconSizePercent * bmp.Width / 100f;
float iconDestHeight = drawIconFlag ? iconDestWidth * icon.Height / icon.Width : 0;
float iconX = (bmp.Width - iconDestWidth) / 2;
float iconY = (bmp.Height - iconDestHeight) / 2;
var centerDest = new RectangleF(iconX - iconBorderWidth, iconY - iconBorderWidth, iconDestWidth + iconBorderWidth * 2, iconDestHeight + iconBorderWidth * 2);
var iconDestRect = new RectangleF(iconX, iconY, iconDestWidth, iconDestHeight);
var iconBgBrush = iconBackgroundColor != null ? new SolidBrush((Color)iconBackgroundColor) : lightBrush;
//Only render icon/logo background, if iconBorderWith is set > 0
if (iconBorderWidth > 0)
{
using (GraphicsPath iconPath = CreateRoundedRectanglePath(centerDest, iconBorderWidth * 2))
{
gfx.FillPath(iconBgBrush, iconPath);
}
}
gfx.DrawImage(icon, iconDestRect, new RectangleF(0, 0, icon.Width, icon.Height), GraphicsUnit.Pixel);
}
gfx.Save();
}
return bmp;
}
internal GraphicsPath CreateRoundedRectanglePath(RectangleF rect, int cornerRadius)
{
var roundedRect = new GraphicsPath();
roundedRect.AddArc(rect.X, rect.Y, cornerRadius * 2, cornerRadius * 2, 180, 90);
roundedRect.AddLine(rect.X + cornerRadius, rect.Y, rect.Right - cornerRadius * 2, rect.Y);
roundedRect.AddArc(rect.X + rect.Width - cornerRadius * 2, rect.Y, cornerRadius * 2, cornerRadius * 2, 270, 90);
roundedRect.AddLine(rect.Right, rect.Y + cornerRadius * 2, rect.Right, rect.Y + rect.Height - cornerRadius * 2);
roundedRect.AddArc(rect.X + rect.Width - cornerRadius * 2, rect.Y + rect.Height - cornerRadius * 2, cornerRadius * 2, cornerRadius * 2, 0, 90);
roundedRect.AddLine(rect.Right - cornerRadius * 2, rect.Bottom, rect.X + cornerRadius * 2, rect.Bottom);
roundedRect.AddArc(rect.X, rect.Bottom - cornerRadius * 2, cornerRadius * 2, cornerRadius * 2, 90, 90);
roundedRect.AddLine(rect.X, rect.Bottom - cornerRadius * 2, rect.X, rect.Y + cornerRadius * 2);
roundedRect.CloseFigure();
return roundedRect;
}
}
public static class QRCodeHelper
{
public static Bitmap GetQRCode(string plainText, int pixelsPerModule, Color darkColor, Color lightColor, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1, Bitmap icon = null, int iconSizePercent = 15, int iconBorderWidth = 0, bool drawQuietZones = true)
{
using (var qrGenerator = new QRCodeGenerator())
using (var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode, requestedVersion))
using (var qrCode = new QRCode(qrCodeData))
return qrCode.GetGraphic(pixelsPerModule, darkColor, lightColor, icon, iconSizePercent, iconBorderWidth, drawQuietZones);
}
}
}

View File

@@ -0,0 +1,185 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace QRCoder
{
using QRCoder.Framework4._0Methods;
using System;
using System.IO;
using System.IO.Compression;
public class QRCodeData : IDisposable
{
public List<BitArray> ModuleMatrix { get; set; }
public QRCodeData(int version)
{
this.Version = version;
var size = ModulesPerSideFromVersion(version);
this.ModuleMatrix = new List<BitArray>();
for (var i = 0; i < size; i++)
this.ModuleMatrix.Add(new BitArray(size));
}
#if NETFRAMEWORK || NETSTANDARD2_0 || NET5_0
public QRCodeData(string pathToRawData, Compression compressMode) : this(File.ReadAllBytes(pathToRawData), compressMode)
{
}
#endif
public QRCodeData(byte[] rawData, Compression compressMode)
{
var bytes = new List<byte>(rawData);
//Decompress
if (compressMode == Compression.Deflate)
{
using (var input = new MemoryStream(bytes.ToArray()))
{
using (var output = new MemoryStream())
{
using (var dstream = new DeflateStream(input, CompressionMode.Decompress))
{
Stream4Methods.CopyTo(dstream, output);
}
bytes = new List<byte>(output.ToArray());
}
}
}
else if (compressMode == Compression.GZip)
{
using (var input = new MemoryStream(bytes.ToArray()))
{
using (var output = new MemoryStream())
{
using (var dstream = new GZipStream(input, CompressionMode.Decompress))
{
Stream4Methods.CopyTo(dstream, output);
}
bytes = new List<byte>(output.ToArray());
}
}
}
if (bytes[0] != 0x51 || bytes[1] != 0x52 || bytes[2] != 0x52)
throw new Exception("Invalid raw data file. Filetype doesn't match \"QRR\".");
//Set QR code version
var sideLen = (int)bytes[4];
bytes.RemoveRange(0, 5);
this.Version = (sideLen - 21 - 8) / 4 + 1;
//Unpack
var modules = new Queue<bool>(8 * bytes.Count);
foreach (var b in bytes)
{
var bArr = new BitArray(new byte[] { b });
for (int i = 7; i >= 0; i--)
{
modules.Enqueue((b & (1 << i)) != 0);
}
}
//Build module matrix
this.ModuleMatrix = new List<BitArray>(sideLen);
for (int y = 0; y < sideLen; y++)
{
this.ModuleMatrix.Add(new BitArray(sideLen));
for (int x = 0; x < sideLen; x++)
{
this.ModuleMatrix[y][x] = modules.Dequeue();
}
}
}
public byte[] GetRawData(Compression compressMode)
{
var bytes = new List<byte>();
//Add header - signature ("QRR")
bytes.AddRange(new byte[]{ 0x51, 0x52, 0x52, 0x00 });
//Add header - rowsize
bytes.Add((byte)ModuleMatrix.Count);
//Build data queue
var dataQueue = new Queue<int>();
foreach (var row in ModuleMatrix)
{
foreach (var module in row)
{
dataQueue.Enqueue((bool)module ? 1 : 0);
}
}
for (int i = 0; i < 8 - (ModuleMatrix.Count * ModuleMatrix.Count) % 8; i++)
{
dataQueue.Enqueue(0);
}
//Process queue
while (dataQueue.Count > 0)
{
byte b = 0;
for (int i = 7; i >= 0; i--)
{
b += (byte)(dataQueue.Dequeue() << i);
}
bytes.Add(b);
}
var rawData = bytes.ToArray();
//Compress stream (optional)
if (compressMode == Compression.Deflate)
{
using (var output = new MemoryStream())
{
using (var dstream = new DeflateStream(output, CompressionMode.Compress))
{
dstream.Write(rawData, 0, rawData.Length);
}
rawData = output.ToArray();
}
}
else if (compressMode == Compression.GZip)
{
using (var output = new MemoryStream())
{
using (GZipStream gzipStream = new GZipStream(output, CompressionMode.Compress, true))
{
gzipStream.Write(rawData, 0, rawData.Length);
}
rawData = output.ToArray();
}
}
return rawData;
}
#if NETFRAMEWORK || NETSTANDARD2_0 || NET5_0
public void SaveRawData(string filePath, Compression compressMode)
{
File.WriteAllBytes(filePath, GetRawData(compressMode));
}
#endif
public int Version { get; private set; }
private static int ModulesPerSideFromVersion(int version)
{
return 21 + (version - 1) * 4;
}
public void Dispose()
{
this.ModuleMatrix = null;
this.Version = 0;
}
public enum Compression
{
Uncompressed,
Deflate,
GZip
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,391 @@
using QRCoder.Extensions;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Text.RegularExpressions;
using static QRCoder.QRCodeGenerator;
using static QRCoder.SvgQRCode;
namespace QRCoder
{
public class SvgQRCode : AbstractQRCode, IDisposable
{
/// <summary>
/// Constructor without params to be used in COM Objects connections
/// </summary>
public SvgQRCode() { }
public SvgQRCode(QRCodeData data) : base(data) { }
/// <summary>
/// Returns a QR code as SVG string
/// </summary>
/// <param name="pixelsPerModule">The pixel size each b/w module is drawn</param>
/// <returns>SVG as string</returns>
public string GetGraphic(int pixelsPerModule)
{
var viewBox = new Size(pixelsPerModule*this.QrCodeData.ModuleMatrix.Count, pixelsPerModule * this.QrCodeData.ModuleMatrix.Count);
return this.GetGraphic(viewBox, Color.Black, Color.White);
}
/// <summary>
/// Returns a QR code as SVG string with custom colors, optional quietzone and logo
/// </summary>
/// <param name="pixelsPerModule">The pixel size each b/w module is drawn</param>
/// <param name="darkColor">Color of the dark modules</param>
/// <param name="lightColor">Color of the light modules</param>
/// <param name="drawQuietZones">If true a white border is drawn around the whole QR Code</param>
/// <param name="sizingMode">Defines if width/height or viewbox should be used for size definition</param>
/// <param name="logo">A (optional) logo to be rendered on the code (either Bitmap or SVG)</param>
/// <returns>SVG as string</returns>
public string GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, bool drawQuietZones = true, SizingMode sizingMode = SizingMode.WidthHeightAttribute, SvgLogo logo = null)
{
var offset = drawQuietZones ? 0 : 4;
var edgeSize = this.QrCodeData.ModuleMatrix.Count * pixelsPerModule - (offset * 2 * pixelsPerModule);
var viewBox = new Size(edgeSize, edgeSize);
return this.GetGraphic(viewBox, darkColor, lightColor, drawQuietZones, sizingMode, logo);
}
/// <summary>
/// Returns a QR code as SVG string with custom colors (in HEX syntax), optional quietzone and logo
/// </summary>
/// <param name="pixelsPerModule">The pixel size each b/w module is drawn</param>
/// <param name="darkColorHex">The color of the dark/black modules in hex (e.g. #000000) representation</param>
/// <param name="lightColorHex">The color of the light/white modules in hex (e.g. #ffffff) representation</param>
/// <param name="drawQuietZones">If true a white border is drawn around the whole QR Code</param>
/// <param name="sizingMode">Defines if width/height or viewbox should be used for size definition</param>
/// <param name="logo">A (optional) logo to be rendered on the code (either Bitmap or SVG)</param>
/// <returns>SVG as string</returns>
public string GetGraphic(int pixelsPerModule, string darkColorHex, string lightColorHex, bool drawQuietZones = true, SizingMode sizingMode = SizingMode.WidthHeightAttribute, SvgLogo logo = null)
{
var offset = drawQuietZones ? 0 : 4;
var edgeSize = this.QrCodeData.ModuleMatrix.Count * pixelsPerModule - (offset * 2 * pixelsPerModule);
var viewBox = new Size(edgeSize, edgeSize);
return this.GetGraphic(viewBox, darkColorHex, lightColorHex, drawQuietZones, sizingMode, logo);
}
/// <summary>
/// Returns a QR code as SVG string with optional quietzone and logo
/// </summary>
/// <param name="viewBox">The viewbox of the QR code graphic</param>
/// <param name="drawQuietZones">If true a white border is drawn around the whole QR Code</param>
/// <param name="sizingMode">Defines if width/height or viewbox should be used for size definition</param>
/// <param name="logo">A (optional) logo to be rendered on the code (either Bitmap or SVG)</param>
/// <returns>SVG as string</returns>
public string GetGraphic(Size viewBox, bool drawQuietZones = true, SizingMode sizingMode = SizingMode.WidthHeightAttribute, SvgLogo logo = null)
{
return this.GetGraphic(viewBox, Color.Black, Color.White, drawQuietZones, sizingMode, logo);
}
/// <summary>
/// Returns a QR code as SVG string with custom colors and optional quietzone and logo
/// </summary>
/// <param name="viewBox">The viewbox of the QR code graphic</param>
/// <param name="darkColor">Color of the dark modules</param>
/// <param name="lightColor">Color of the light modules</param>
/// <param name="drawQuietZones">If true a white border is drawn around the whole QR Code</param>
/// <param name="sizingMode">Defines if width/height or viewbox should be used for size definition</param>
/// <param name="logo">A (optional) logo to be rendered on the code (either Bitmap or SVG)</param>
/// <returns>SVG as string</returns>
public string GetGraphic(Size viewBox, Color darkColor, Color lightColor, bool drawQuietZones = true, SizingMode sizingMode = SizingMode.WidthHeightAttribute, SvgLogo logo = null)
{
return this.GetGraphic(viewBox, ColorTranslator.ToHtml(Color.FromArgb(darkColor.ToArgb())), ColorTranslator.ToHtml(Color.FromArgb(lightColor.ToArgb())), drawQuietZones, sizingMode, logo);
}
/// <summary>
/// Returns a QR code as SVG string with custom colors (in HEX syntax), optional quietzone and logo
/// </summary>
/// <param name="viewBox">The viewbox of the QR code graphic</param>
/// <param name="darkColorHex">The color of the dark/black modules in hex (e.g. #000000) representation</param>
/// <param name="lightColorHex">The color of the light/white modules in hex (e.g. #ffffff) representation</param>
/// <param name="drawQuietZones">If true a white border is drawn around the whole QR Code</param>
/// <param name="sizingMode">Defines if width/height or viewbox should be used for size definition</param>
/// <param name="logo">A (optional) logo to be rendered on the code (either Bitmap or SVG)</param>
/// <returns>SVG as string</returns>
public string GetGraphic(Size viewBox, string darkColorHex, string lightColorHex, bool drawQuietZones = true, SizingMode sizingMode = SizingMode.WidthHeightAttribute, SvgLogo logo = null)
{
int offset = drawQuietZones ? 0 : 4;
int drawableModulesCount = this.QrCodeData.ModuleMatrix.Count - (drawQuietZones ? 0 : offset * 2);
double pixelsPerModule = Math.Min(viewBox.Width, viewBox.Height) / (double)drawableModulesCount;
double qrSize = drawableModulesCount * pixelsPerModule;
string svgSizeAttributes = (sizingMode == SizingMode.WidthHeightAttribute) ? $@"width=""{viewBox.Width}"" height=""{viewBox.Height}""" : $@"viewBox=""0 0 {viewBox.Width} {viewBox.Height}""";
ImageAttributes? logoAttr = null;
if (logo != null)
logoAttr = GetLogoAttributes(logo, viewBox);
// Merge horizontal rectangles
int[,] matrix = new int[drawableModulesCount, drawableModulesCount];
for (int yi = 0; yi < drawableModulesCount; yi += 1)
{
BitArray bitArray = this.QrCodeData.ModuleMatrix[yi+offset];
int x0 = -1;
int xL = 0;
for (int xi = 0; xi < drawableModulesCount; xi += 1)
{
matrix[yi, xi] = 0;
if (bitArray[xi+offset] && (logo == null || !logo.FillLogoBackground() || !IsBlockedByLogo((xi+offset)*pixelsPerModule, (yi+offset) * pixelsPerModule, logoAttr, pixelsPerModule)))
{
if(x0 == -1)
{
x0 = xi;
}
xL += 1;
}
else
{
if(xL > 0)
{
matrix[yi, x0] = xL;
x0 = -1;
xL = 0;
}
}
}
if (xL > 0)
{
matrix[yi, x0] = xL;
}
}
StringBuilder svgFile = new StringBuilder($@"<svg version=""1.1"" baseProfile=""full"" shape-rendering=""crispEdges"" {svgSizeAttributes} xmlns=""http://www.w3.org/2000/svg"" xmlns:xlink=""http://www.w3.org/1999/xlink"">");
svgFile.AppendLine($@"<rect x=""0"" y=""0"" width=""{CleanSvgVal(qrSize)}"" height=""{CleanSvgVal(qrSize)}"" fill=""{lightColorHex}"" />");
for (int yi = 0; yi < drawableModulesCount; yi += 1)
{
double y = yi * pixelsPerModule;
for (int xi = 0; xi < drawableModulesCount; xi += 1)
{
int xL = matrix[yi, xi];
if(xL > 0)
{
// Merge vertical rectangles
int yL = 1;
for (int y2 = yi + 1; y2 < drawableModulesCount; y2 += 1)
{
if(matrix[y2, xi] == xL)
{
matrix[y2, xi] = 0;
yL += 1;
}
else
{
break;
}
}
// Output SVG rectangles
double x = xi * pixelsPerModule;
if (logo == null || !logo.FillLogoBackground() || !IsBlockedByLogo(x, y, logoAttr, pixelsPerModule))
svgFile.AppendLine($@"<rect x=""{CleanSvgVal(x)}"" y=""{CleanSvgVal(y)}"" width=""{CleanSvgVal(xL * pixelsPerModule)}"" height=""{CleanSvgVal(yL * pixelsPerModule)}"" fill=""{darkColorHex}"" />");
}
}
}
//Render logo, if set
if (logo != null)
{
if (!logo.IsEmbedded())
{
svgFile.AppendLine($@"<svg width=""100%"" height=""100%"" version=""1.1"" xmlns = ""http://www.w3.org/2000/svg"">");
svgFile.AppendLine($@"<image x=""{CleanSvgVal(logoAttr.Value.X)}"" y=""{CleanSvgVal(logoAttr.Value.Y)}"" width=""{CleanSvgVal(logoAttr.Value.Width)}"" height=""{CleanSvgVal(logoAttr.Value.Height)}"" xlink:href=""{logo.GetDataUri()}"" />");
svgFile.AppendLine(@"</svg>");
}
else
{
var rawLogo = (string)logo.GetRawLogo();
var svg = System.Xml.Linq.XDocument.Parse(rawLogo);
svg.Root.SetAttributeValue("x", CleanSvgVal(logoAttr.Value.X));
svg.Root.SetAttributeValue("y", CleanSvgVal(logoAttr.Value.Y));
svg.Root.SetAttributeValue("width", CleanSvgVal(logoAttr.Value.Width));
svg.Root.SetAttributeValue("height", CleanSvgVal(logoAttr.Value.Height));
svg.Root.SetAttributeValue("shape-rendering", "geometricPrecision");
svgFile.AppendLine(svg.ToString(System.Xml.Linq.SaveOptions.DisableFormatting).Replace("svg:", ""));
}
}
svgFile.Append(@"</svg>");
return svgFile.ToString();
}
private bool IsBlockedByLogo(double x, double y, ImageAttributes? attr, double pixelPerModule)
{
return x + pixelPerModule >= attr.Value.X && x <= attr.Value.X + attr.Value.Width && y + pixelPerModule >= attr.Value.Y && y <= attr.Value.Y + attr.Value.Height;
}
private ImageAttributes GetLogoAttributes(SvgLogo logo, Size viewBox)
{
var imgWidth = logo.GetIconSizePercent() / 100d * viewBox.Width;
var imgHeight = logo.GetIconSizePercent() / 100d * viewBox.Height;
var imgPosX = viewBox.Width / 2d - imgWidth / 2d;
var imgPosY = viewBox.Height / 2d - imgHeight / 2d;
return new ImageAttributes()
{
Width = imgWidth,
Height = imgHeight,
X = imgPosX,
Y = imgPosY
};
}
private struct ImageAttributes
{
public double Width;
public double Height;
public double X;
public double Y;
}
private string CleanSvgVal(double input)
{
//Clean double values for international use/formats
//We use explicitly "G15" to avoid differences between .NET full and Core platforms
//https://stackoverflow.com/questions/64898117/tostring-has-a-different-behavior-between-net-462-and-net-core-3-1
return input.ToString("G15", System.Globalization.CultureInfo.InvariantCulture);
}
/// <summary>
/// Mode of sizing attribution on svg root node
/// </summary>
public enum SizingMode
{
WidthHeightAttribute,
ViewBoxAttribute
}
/// <summary>
/// Represents a logo graphic that can be rendered on a SvgQRCode
/// </summary>
public class SvgLogo
{
private string _logoData;
private MediaType _mediaType;
private int _iconSizePercent;
private bool _fillLogoBackground;
private object _logoRaw;
private bool _isEmbedded;
/// <summary>
/// Create a logo object to be used in SvgQRCode renderer
/// </summary>
/// <param name="iconRasterized">Logo to be rendered as Bitmap/rasterized graphic</param>
/// <param name="iconSizePercent">Degree of percentage coverage of the QR code by the logo</param>
/// <param name="fillLogoBackground">If true, the background behind the logo will be cleaned</param>
public SvgLogo(Bitmap iconRasterized, int iconSizePercent = 15, bool fillLogoBackground = true)
{
_iconSizePercent = iconSizePercent;
using (var ms = new System.IO.MemoryStream())
{
using (var bitmap = new Bitmap(iconRasterized))
{
bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
_logoData = Convert.ToBase64String(ms.GetBuffer(), Base64FormattingOptions.None);
}
}
_mediaType = MediaType.PNG;
_fillLogoBackground = fillLogoBackground;
_logoRaw = iconRasterized;
_isEmbedded = false;
}
/// <summary>
/// Create a logo object to be used in SvgQRCode renderer
/// </summary>
/// <param name="iconVectorized">Logo to be rendered as SVG/vectorized graphic/string</param>
/// <param name="iconSizePercent">Degree of percentage coverage of the QR code by the logo</param>
/// <param name="fillLogoBackground">If true, the background behind the logo will be cleaned</param>
/// <param name="iconEmbedded">If true, the logo will embedded as native svg instead of embedding it as image-tag</param>
public SvgLogo(string iconVectorized, int iconSizePercent = 15, bool fillLogoBackground = true, bool iconEmbedded = true)
{
_iconSizePercent = iconSizePercent;
_logoData = Convert.ToBase64String(Encoding.UTF8.GetBytes(iconVectorized), Base64FormattingOptions.None);
_mediaType = MediaType.SVG;
_fillLogoBackground = fillLogoBackground;
_logoRaw = iconVectorized;
_isEmbedded = iconEmbedded;
}
/// <summary>
/// Returns the raw logo's data
/// </summary>
/// <returns></returns>
public object GetRawLogo()
{
return _logoRaw;
}
/// <summary>
/// Defines, if the logo shall be natively embedded.
/// true=native svg embedding, false=embedding via image-tag
/// </summary>
/// <returns></returns>
public bool IsEmbedded()
{
return _isEmbedded;
}
/// <summary>
/// Returns the media type of the logo
/// </summary>
/// <returns></returns>
public MediaType GetMediaType()
{
return _mediaType;
}
/// <summary>
/// Returns the logo as data-uri
/// </summary>
/// <returns></returns>
public string GetDataUri()
{
return $"data:{_mediaType.GetStringValue()};base64,{_logoData}";
}
/// <summary>
/// Returns how much of the QR code should be covered by the logo (in percent)
/// </summary>
/// <returns></returns>
public int GetIconSizePercent()
{
return _iconSizePercent;
}
/// <summary>
/// Returns if the background of the logo should be cleaned (no QR modules will be rendered behind the logo)
/// </summary>
/// <returns></returns>
public bool FillLogoBackground()
{
return _fillLogoBackground;
}
/// <summary>
/// Media types for SvgLogos
/// </summary>
public enum MediaType : int
{
[StringValue("image/png")]
PNG = 0,
[StringValue("image/svg+xml")]
SVG = 1
}
}
}
public static class SvgQRCodeHelper
{
public static string GetQRCode(string plainText, int pixelsPerModule, string darkColorHex, string lightColorHex, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1, bool drawQuietZones = true, SizingMode sizingMode = SizingMode.WidthHeightAttribute, SvgLogo logo = null)
{
using (var qrGenerator = new QRCodeGenerator())
using (var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode, requestedVersion))
using (var qrCode = new SvgQRCode(qrCodeData))
return qrCode.GetGraphic(pixelsPerModule, darkColorHex, lightColorHex, drawQuietZones, sizingMode, logo);
}
}
}

View File

@@ -21,7 +21,7 @@ namespace Thought.vCards
/// </remarks>
/// <seealso cref="vCardEmailAddressCollection"/>
/// <seealso cref="vCardEmailAddressType"/>
public class vCardEmailAddress
public class vCardEmailAddress : vCardRoot
{
private string address;
@@ -120,6 +120,16 @@ namespace Thought.vCards
}
public override void ChangeContent(string text)
{
this.address = text;
}
public override string GetNameType()
{
return EmailType.ToString();
}
/// <summary>
/// Builds a string that represents the email address.

View File

@@ -16,7 +16,7 @@ namespace Thought.vCards
/// <seealso cref="vCardPhoneCollection"/>
/// <seealso cref="vCardPhoneTypes"/>
[Serializable]
public class vCardPhone
public class vCardPhone : vCardRoot
{
private string fullNumber;
@@ -42,6 +42,10 @@ namespace Thought.vCards
this.fullNumber = fullNumber;
}
public override string ToString()
{
return this.fullNumber;
}
/// <summary>
/// Creates a new <see cref="vCardPhone"/> with the specified number and subtype.
@@ -452,6 +456,15 @@ namespace Thought.vCards
}
}
public override void ChangeContent(string text)
{
this.FullNumber = text;
}
public override string GetNameType()
{
return PhoneType.ToString();
}
}
}

View File

@@ -0,0 +1,8 @@
namespace Thought.vCards
{
abstract public class vCardRoot
{
abstract public void ChangeContent(string text);
abstract public string GetNameType();
}
}

View File

@@ -1906,13 +1906,12 @@ namespace Thought.vCards
/// </summary>
private void ReadInto_PHOTO(vCard card, vCardProperty property)
{
string[] Formats = { "GIF", "CGM", "WMF", "JPEG", "BMP", "MET", "PMB", "DIB", "PICT", "TIFF", "PS", "PDF" };
string imageType = property.Subproperties.GetValue("TYPE", Formats);
string imageType = property.Subproperties.GetValue("TYPE");
// The PHOTO property contains an embedded (encoded) image
// or a link to an image. A URL (linked) image is supposed
// to be indicated with the VALUE=URI subproperty.
string valueType = property.Subproperties.GetValue("VALUE");
//URI is the standard, but I've seen examples online of URL
@@ -2290,10 +2289,7 @@ namespace Thought.vCards
firstLine = firstLine.Trim();
if (firstLine.Length == 0)
{
Warnings.Add(Thought.vCards.WarningMessages.BlankLine);
continue;
}
// Get the index of the colon (:) in this
// property line. All vCard properties are
@@ -2301,10 +2297,7 @@ namespace Thought.vCards
int colonIndex = firstLine.IndexOf(':');
if (colonIndex == -1)
{
Warnings.Add(Thought.vCards.WarningMessages.ColonMissing);
continue;
}
// Get the name portion of the property. This
// portion contains the property name as well
@@ -2312,10 +2305,7 @@ namespace Thought.vCards
string namePart = firstLine.Substring(0, colonIndex).Trim();
if (string.IsNullOrEmpty(namePart))
{
Warnings.Add(Thought.vCards.WarningMessages.EmptyName);
continue;
}
// Split apart the name portion of the property.
// A property can have subproperties, separated
@@ -2323,17 +2313,14 @@ namespace Thought.vCards
string[] nameParts = namePart.Split(';');
for (int i = 0; i < nameParts.Length; i++)
nameParts[i] = nameParts[i].Trim();
nameParts[i] = nameParts[i].Trim();
// The name of the property is supposed to
// be first on the line. An empty name is not
// legal syntax.
if (nameParts[0].Length == 0)
{
Warnings.Add(Thought.vCards.WarningMessages.EmptyName);
continue;
}
// At this point there is sufficient text
// to define a vCard property. The only
@@ -2362,13 +2349,14 @@ namespace Thought.vCards
if (subNameValue.Length == 1)
{
// The Split function above returned a single
// array element. This means no equal (=) sign
// was present. The subproperty consists of
// a name only.
property.Subproperties.Add(
nameParts[index].Trim());
// The Split function above returned a single
// array element. This means no equal (=) sign
// was present. The subproperty consists of
// a name only.
if (!string.IsNullOrEmpty(subNameValue[0]))
{
property.Subproperties.Add(nameParts[index].Trim());
}
}
else
{

View File

@@ -311,8 +311,8 @@ namespace Thought.vCards
vCardValueCollection values = new vCardValueCollection(';');
values.Add(string.Empty);
values.Add(string.Empty);
values.Add(address.PostOfficeBox);
values.Add(address.ExtendedAddress);
values.Add(!string.IsNullOrEmpty(address.Street) ? address.Street.Replace("\r\n", "\n") : string.Empty);
values.Add(address.City);
values.Add(address.Region);

View File

@@ -15,7 +15,7 @@ namespace Thought.vCards
/// </summary>
/// <seealso cref="vCardWebsiteCollection"/>
/// <seealso cref="vCardWebsiteTypes"/>
public class vCardWebsite
public class vCardWebsite : vCardRoot
{
private string url;
@@ -154,6 +154,16 @@ namespace Thought.vCards
}
}
public override void ChangeContent(string text)
{
this.url = text;
}
public override string GetNameType()
{
return WebsiteType.ToString();
}
/// <summary>
/// Returns the string representation (URL) of the web site.

View File

@@ -0,0 +1,7 @@
namespace vCardEditor.Libs.TinyJson
{
public interface IParser
{
T Deserialize<T>(string json);
}
}

View File

@@ -0,0 +1,377 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
namespace TinyJson
{
// Really simple JSON parser in ~300 lines
// - Attempts to parse JSON files with minimal GC allocation
// - Nice and simple "[1,2,3]".FromJson<List<int>>() API
// - Classes and structs can be parsed too!
// class Foo { public int Value; }
// "{\"Value\":10}".FromJson<Foo>()
// - Can parse JSON without type information into Dictionary<string,object> and List<object> e.g.
// "[1,2,3]".FromJson<object>().GetType() == typeof(List<object>)
// "{\"Value\":10}".FromJson<object>().GetType() == typeof(Dictionary<string,object>)
// - No JIT Emit support to support AOT compilation on iOS
// - Attempts are made to NOT throw an exception if the JSON is corrupted or invalid: returns null instead.
// - Only public fields and property setters on classes/structs will be written to
//
// Limitations:
// - No JIT Emit support to parse structures quickly
// - Limited to parsing <2GB JSON files (due to int.MaxValue)
// - Parsing of abstract classes or interfaces is NOT supported and will throw an exception.
public static class JSONParser
{
[ThreadStatic] static Stack<List<string>> splitArrayPool;
[ThreadStatic] static StringBuilder stringBuilder;
[ThreadStatic] static Dictionary<Type, Dictionary<string, FieldInfo>> fieldInfoCache;
[ThreadStatic] static Dictionary<Type, Dictionary<string, PropertyInfo>> propertyInfoCache;
public static T FromJson<T>(this string json)
{
// Initialize, if needed, the ThreadStatic variables
if (propertyInfoCache == null) propertyInfoCache = new Dictionary<Type, Dictionary<string, PropertyInfo>>();
if (fieldInfoCache == null) fieldInfoCache = new Dictionary<Type, Dictionary<string, FieldInfo>>();
if (stringBuilder == null) stringBuilder = new StringBuilder();
if (splitArrayPool == null) splitArrayPool = new Stack<List<string>>();
//Remove all whitespace not within strings to make parsing simpler
stringBuilder.Length = 0;
for (int i = 0; i < json.Length; i++)
{
char c = json[i];
if (c == '"')
{
i = AppendUntilStringEnd(true, i, json);
continue;
}
if (char.IsWhiteSpace(c))
continue;
stringBuilder.Append(c);
}
//Parse the thing!
return (T)ParseValue(typeof(T), stringBuilder.ToString());
}
static int AppendUntilStringEnd(bool appendEscapeCharacter, int startIdx, string json)
{
stringBuilder.Append(json[startIdx]);
for (int i = startIdx + 1; i < json.Length; i++)
{
if (json[i] == '\\')
{
if (appendEscapeCharacter)
stringBuilder.Append(json[i]);
stringBuilder.Append(json[i + 1]);
i++;//Skip next character as it is escaped
}
else if (json[i] == '"')
{
stringBuilder.Append(json[i]);
return i;
}
else
stringBuilder.Append(json[i]);
}
return json.Length - 1;
}
//Splits { <value>:<value>, <value>:<value> } and [ <value>, <value> ] into a list of <value> strings
static List<string> Split(string json)
{
List<string> splitArray = splitArrayPool.Count > 0 ? splitArrayPool.Pop() : new List<string>();
splitArray.Clear();
if (json.Length == 2)
return splitArray;
int parseDepth = 0;
stringBuilder.Length = 0;
for (int i = 1; i < json.Length - 1; i++)
{
switch (json[i])
{
case '[':
case '{':
parseDepth++;
break;
case ']':
case '}':
parseDepth--;
break;
case '"':
i = AppendUntilStringEnd(true, i, json);
continue;
case ',':
case ':':
if (parseDepth == 0)
{
splitArray.Add(stringBuilder.ToString());
stringBuilder.Length = 0;
continue;
}
break;
}
stringBuilder.Append(json[i]);
}
splitArray.Add(stringBuilder.ToString());
return splitArray;
}
internal static object ParseValue(Type type, string json)
{
if (type == typeof(string))
{
if (json.Length <= 2)
return string.Empty;
StringBuilder parseStringBuilder = new StringBuilder(json.Length);
for (int i = 1; i < json.Length - 1; ++i)
{
if (json[i] == '\\' && i + 1 < json.Length - 1)
{
int j = "\"\\nrtbf/".IndexOf(json[i + 1]);
if (j >= 0)
{
parseStringBuilder.Append("\"\\\n\r\t\b\f/"[j]);
++i;
continue;
}
if (json[i + 1] == 'u' && i + 5 < json.Length - 1)
{
UInt32 c = 0;
if (UInt32.TryParse(json.Substring(i + 2, 4), System.Globalization.NumberStyles.AllowHexSpecifier, null, out c))
{
parseStringBuilder.Append((char)c);
i += 5;
continue;
}
}
}
parseStringBuilder.Append(json[i]);
}
return parseStringBuilder.ToString();
}
if (type.IsPrimitive)
{
var result = Convert.ChangeType(json, type, System.Globalization.CultureInfo.InvariantCulture);
return result;
}
if (type == typeof(decimal))
{
decimal result;
decimal.TryParse(json, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out result);
return result;
}
if (type == typeof(DateTime))
{
DateTime result;
DateTime.TryParse(json.Replace("\"",""), System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out result);
return result;
}
if (json == "null")
{
return null;
}
if (type.IsEnum)
{
if (json[0] == '"')
json = json.Substring(1, json.Length - 2);
try
{
return Enum.Parse(type, json, false);
}
catch
{
return 0;
}
}
if (type.IsArray)
{
Type arrayType = type.GetElementType();
if (json[0] != '[' || json[json.Length - 1] != ']')
return null;
List<string> elems = Split(json);
Array newArray = Array.CreateInstance(arrayType, elems.Count);
for (int i = 0; i < elems.Count; i++)
newArray.SetValue(ParseValue(arrayType, elems[i]), i);
splitArrayPool.Push(elems);
return newArray;
}
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>))
{
Type listType = type.GetGenericArguments()[0];
if (json[0] != '[' || json[json.Length - 1] != ']')
return null;
List<string> elems = Split(json);
var list = (IList)type.GetConstructor(new Type[] { typeof(int) }).Invoke(new object[] { elems.Count });
for (int i = 0; i < elems.Count; i++)
list.Add(ParseValue(listType, elems[i]));
splitArrayPool.Push(elems);
return list;
}
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>))
{
Type keyType, valueType;
{
Type[] args = type.GetGenericArguments();
keyType = args[0];
valueType = args[1];
}
//Refuse to parse dictionary keys that aren't of type string
if (keyType != typeof(string))
return null;
//Must be a valid dictionary element
if (json[0] != '{' || json[json.Length - 1] != '}')
return null;
//The list is split into key/value pairs only, this means the split must be divisible by 2 to be valid JSON
List<string> elems = Split(json);
if (elems.Count % 2 != 0)
return null;
var dictionary = (IDictionary)type.GetConstructor(new Type[] { typeof(int) }).Invoke(new object[] { elems.Count / 2 });
for (int i = 0; i < elems.Count; i += 2)
{
if (elems[i].Length <= 2)
continue;
string keyValue = elems[i].Substring(1, elems[i].Length - 2);
object val = ParseValue(valueType, elems[i + 1]);
dictionary[keyValue] = val;
}
return dictionary;
}
if (type == typeof(object))
{
return ParseAnonymousValue(json);
}
if (json[0] == '{' && json[json.Length - 1] == '}')
{
return ParseObject(type, json);
}
return null;
}
static object ParseAnonymousValue(string json)
{
if (json.Length == 0)
return null;
if (json[0] == '{' && json[json.Length - 1] == '}')
{
List<string> elems = Split(json);
if (elems.Count % 2 != 0)
return null;
var dict = new Dictionary<string, object>(elems.Count / 2);
for (int i = 0; i < elems.Count; i += 2)
dict[elems[i].Substring(1, elems[i].Length - 2)] = ParseAnonymousValue(elems[i + 1]);
return dict;
}
if (json[0] == '[' && json[json.Length - 1] == ']')
{
List<string> items = Split(json);
var finalList = new List<object>(items.Count);
for (int i = 0; i < items.Count; i++)
finalList.Add(ParseAnonymousValue(items[i]));
return finalList;
}
if (json[0] == '"' && json[json.Length - 1] == '"')
{
string str = json.Substring(1, json.Length - 2);
return str.Replace("\\", string.Empty);
}
if (char.IsDigit(json[0]) || json[0] == '-')
{
if (json.Contains("."))
{
double result;
double.TryParse(json, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out result);
return result;
}
else
{
int result;
int.TryParse(json, out result);
return result;
}
}
if (json == "true")
return true;
if (json == "false")
return false;
// handles json == "null" as well as invalid JSON
return null;
}
static Dictionary<string, T> CreateMemberNameDictionary<T>(T[] members) where T : MemberInfo
{
Dictionary<string, T> nameToMember = new Dictionary<string, T>(StringComparer.OrdinalIgnoreCase);
for (int i = 0; i < members.Length; i++)
{
T member = members[i];
if (member.IsDefined(typeof(IgnoreDataMemberAttribute), true))
continue;
string name = member.Name;
if (member.IsDefined(typeof(DataMemberAttribute), true))
{
DataMemberAttribute dataMemberAttribute = (DataMemberAttribute)Attribute.GetCustomAttribute(member, typeof(DataMemberAttribute), true);
if (!string.IsNullOrEmpty(dataMemberAttribute.Name))
name = dataMemberAttribute.Name;
}
nameToMember.Add(name, member);
}
return nameToMember;
}
static object ParseObject(Type type, string json)
{
object instance = FormatterServices.GetUninitializedObject(type);
//The list is split into key/value pairs only, this means the split must be divisible by 2 to be valid JSON
List<string> elems = Split(json);
if (elems.Count % 2 != 0)
return instance;
Dictionary<string, FieldInfo> nameToField;
Dictionary<string, PropertyInfo> nameToProperty;
if (!fieldInfoCache.TryGetValue(type, out nameToField))
{
nameToField = CreateMemberNameDictionary(type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy));
fieldInfoCache.Add(type, nameToField);
}
if (!propertyInfoCache.TryGetValue(type, out nameToProperty))
{
nameToProperty = CreateMemberNameDictionary(type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy));
propertyInfoCache.Add(type, nameToProperty);
}
for (int i = 0; i < elems.Count; i += 2)
{
if (elems[i].Length <= 2)
continue;
string key = elems[i].Substring(1, elems[i].Length - 2);
string value = elems[i + 1];
FieldInfo fieldInfo;
PropertyInfo propertyInfo;
if (nameToField.TryGetValue(key, out fieldInfo))
fieldInfo.SetValue(instance, ParseValue(fieldInfo.FieldType, value));
else if (nameToProperty.TryGetValue(key, out propertyInfo))
propertyInfo.SetValue(instance, ParseValue(propertyInfo.PropertyType, value), null);
}
return instance;
}
}
}

View File

@@ -0,0 +1,16 @@
using System.Collections.Generic;
namespace vCardEditor.Libs.TinyJson
{
public class LocalizationFile
{
public string version;
public Dictionary<string, LanguageData> languages = new Dictionary<string, LanguageData>();
}
public class LanguageData
{
public string name;
public Dictionary<string, string> messages = new Dictionary<string, string>();
}
}

View File

@@ -0,0 +1,30 @@
using vCardEditor.Repository;
namespace vCardEditor.Libs.TinyJson
{
public class LocalizationLoader
{
private readonly IParser _parser;
private readonly IFileHandler _fileHandler;
public LocalizationLoader(IParser parser, IFileHandler fileHandler)
{
_parser = parser;
_fileHandler = fileHandler;
}
public LocalizationFile LoadEmbedded(string EmbeddedResourceName = "vCardEditor.i18n.lang.json")
{
string json = _fileHandler.LoadJsonFromAssembly(EmbeddedResourceName);
return Deserialize(json);
}
private LocalizationFile Deserialize(string json)
{
var result = _parser.Deserialize<LocalizationFile>(json);
return result ?? new LocalizationFile();
}
}
}

View File

@@ -0,0 +1,12 @@
using TinyJson;
namespace vCardEditor.Libs.TinyJson
{
public class TinyJsonParser : IParser
{
public T Deserialize<T>(string json)
{
return JSONParser.FromJson<T>(json);
}
}
}

View File

@@ -15,6 +15,7 @@ namespace VCFEditor.Model
NotifyPropertyChanged("Name");
}
}
[DisplayName("F.Name")]
public string FamilyName
{
@@ -27,12 +28,10 @@ namespace VCFEditor.Model
get {
if (card.Phones.GetFirstChoice(vCardPhoneTypes.Cellular) != null)
return card.Phones.GetFirstChoice(vCardPhoneTypes.Cellular).FullNumber;
return string.Empty;
}
}
[Browsable(false)]
public vCard card { get; set; }
@@ -44,7 +43,9 @@ namespace VCFEditor.Model
[Browsable(false)]
public bool isDeleted { get; set; }
[Browsable(false)]
public string path { get; set; }
public Contact()
{
@@ -53,6 +54,18 @@ namespace VCFEditor.Model
isDirty = false;
}
public Contact(vCard card)
{
this.card = card;
isSelected = false;
isDirty = false;
}
public Contact(string path) : this()
{
this.path = path;
}
private void NotifyPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));

View File

@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace vCardEditor.Model
{
@@ -16,10 +15,9 @@ namespace vCardEditor.Model
get { return _size; }
set { _size = value; }
}
public FixedList() : this(5)
{
}
public FixedList() { }
public FixedList(int size)
{
this._size = size;

View File

@@ -0,0 +1,51 @@
namespace vCardEditor.Model
{
public enum vCardPropeties
{
ADR = 0,
AGENT,
ANNIVERSARY,
BDAY,
BEGIN,
CALADRURI,
CALURI,
CATEGORIES,
CLASS,
CLIENTPIDMAP,
EMAIL,
END,
FBURL,
FN,
GENDER,
GEO,
IMPP,
KEY,
KIND,
LABEL,
LANG,
LOGO,
MAILER,
MEMBER,
N,
NAME,
NICKNAME,
NOTE,
ORG,
PHOTO,
PRODID,
PROFILE,
RELATED,
REV,
ROLE,
SORTSTRING,
SOUND,
SOURCE,
TEL,
TITLE,
TZ,
UID,
URL,
VERSION,
XML
}
}

View File

@@ -1,13 +1,13 @@
using System;
using Thought.vCards;
using VCFEditor.View;
using vCardEditor.View;
using VCFEditor.Repository;
using vCardEditor.Repository;
using vCardEditor.Model;
using System.Linq;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using Thought.vCards;
using vCardEditor;
using vCardEditor.Model;
using vCardEditor.Repository;
using vCardEditor.View.Customs;
using VCFEditor.Repository;
using VCFEditor.View;
namespace VCFEditor.Presenter
{
@@ -15,16 +15,17 @@ namespace VCFEditor.Presenter
{
private readonly IMainView _view;
private readonly IContactRepository _repository;
private readonly ILocalizationProvider _localization;
public MainPresenter(IMainView view, IContactRepository repository)
public MainPresenter(IMainView view, IContactRepository repository, ILocalizationProvider localization )
{
_view = view;
_repository = repository;
_localization = localization;
_view.LoadForm += LoadFormHandler;
_view.AddContact += AddContactHandler;
_view.NewFileOpened += NewFileOpenedHandler;
_view.BeforeOpeningNewFile += BeforeOpeningNewFileHandler;
_view.NewFileOpened += OpenNewFileHandler;
_view.SaveContactsSelected += SaveContactsHandler;
_view.ChangeContactsSelected += ChangeContactSelectedHandler;
_view.DeleteContact += DeleteContactHandler;
@@ -34,11 +35,140 @@ namespace VCFEditor.Presenter
_view.CloseForm += CloseFormHandler;
_view.ModifyImage += ModifyImageHandler;
_view.ExportImage += ExportImageHandler;
_view.ExportQR += ExportQRHandler;
_view.AddressAdded += AddressAddedHandler;
_view.AddressModified += AddressModifiedHandler;
_view.AddressRemoved += AddressRemovedHandler;
_view.CopyTextToClipboardEvent += CopyTextToClipboardHandler;
_view.AddExtraField += _view_AddExtraField;
_view.CountImagesEvent += _view_CountImages;
_view.ClearImagesEvent += _view_ClearImages;
_view.BatchExportImagesEvent += _view_BatchExportImagesEvent;
_view.SplitFileEvent += SaveSplittedFileHandler;
_view.OpenFolderEvent += OpenNewFolderHandler;
_view.CardInfoRemoved += CardInfoRemovedHandler;
}
private void OpenNewFolderHandler(object sender, EventArg<string> e)
{
BeforeOpeningNewFileHandler();
string path = e.Data;
if (string.IsNullOrEmpty(path))
path = _view.DisplayOpenFolderDialog();
if (!string.IsNullOrEmpty(path))
{
bool Loaded =_repository.LoadMultipleFilesContact(path);
if (!Loaded)
{
_view.DisplayMessage("No file loaded!", "Error");
return;
}
AddPathToMostRecentUsedFiles(path);
_view.DisplayContacts(_repository.Contacts);
}
}
public void OpenNewFileHandler(object sender, EventArg<string> e)
{
BeforeOpeningNewFileHandler();
string path = e.Data;
if (string.IsNullOrEmpty(path))
path = _view.DisplayOpenFileDialog("vCard Files|*.vcf");
if (!string.IsNullOrEmpty(path))
{
string ext = _repository.GetExtension(path);
if (!string.Equals(ext, ".vcf", StringComparison.OrdinalIgnoreCase))
{
_view.DisplayMessage("Only vcf extension accepted!", "Error");
return;
}
if (!_repository.LoadContacts(path))
_view.DisplayMessage("File seems missing or corrupted!", "Error");
else
{
_view.DisplayContacts(_repository.Contacts);
AddPathToMostRecentUsedFiles(path);
}
}
}
private void AddPathToMostRecentUsedFiles(string path)
{
FixedList MostRecentUsedFiles = ConfigRepository.Instance.Paths;
if (!MostRecentUsedFiles.Contains(path))
{
MostRecentUsedFiles.Enqueue(path);
_view.UpdateMRUMenu(MostRecentUsedFiles);
}
}
private void _view_BatchExportImagesEvent(object sender, EventArgs e)
{
if (_repository.Contacts == null || _repository.Contacts.Count == 0)
return;
int count = 0;
for (int i = 0; i < _repository.Contacts.Count; i++)
{
if (_repository.Contacts[i].card.Photos.Count > 0)
{
count++;
SaveCardPhoto(_repository.Contacts[i].card, i);
}
}
if (count > 0)
_view.DisplayMessage($"{count} contact(s) processed!", "Photo Count");
else
_view.DisplayMessage($"No picture found!", "Photo Count");
}
private void _view_ClearImages(object sender, EventArgs e)
{
if (_repository.Contacts == null || _repository.Contacts.Count == 0)
return;
int count = 0;
for (int i = 0; i < _repository.Contacts.Count; i++)
{
if (_repository.Contacts[i].card.Photos.Count > 0)
{
count++;
_repository.ModifyImage(i, null);
//remove from the form the image displayed.
if (_view.SelectedContactIndex == i)
_view.ClearImageFromForm();
}
}
if (count > 0)
_view.DisplayMessage($"{count} contact(s) processed!", "Photo Count");
else
_view.DisplayMessage($"No picture found!", "Photo Count");
}
private void _view_CountImages(object sender, EventArgs e)
{
if (_repository.Contacts == null)
return;
int count = _repository.Contacts.Count(x => x.card.Photos.Count > 0);
if (count > 0)
_view.DisplayMessage($"{count} contact(s) containing a picture = ", "Photo Count");
else
_view.DisplayMessage($"No picture found!", "Photo Count");
}
private void _view_AddExtraField(object sender, EventArg<vCardPropeties> e)
{
_view.AddExtraTextGroup(e.Data, string.Empty);
}
private void CopyTextToClipboardHandler(object sender, EventArgs e)
@@ -53,18 +183,20 @@ namespace VCFEditor.Presenter
_view.SendTextToClipBoard(SerializedCard);
_view.DisplayMessage("vCard copied to clipboard!", "Information");
}
private void LoadFormHandler(object sender, EventArg<FormState> e)
{
e.Data = ConfigRepository.Instance.FormState;
var paths = Environment.GetCommandLineArgs();
_view.LoadIntialState(ConfigRepository.Instance.FormState);
_view.LoadAvailablesLangs(_localization.AvailableLanguages);
_view.LoadLocalizedUI(_localization.CurrentMessages);
string[] paths = Environment.GetCommandLineArgs();
if (paths.Length > 1)
{
var evt = new EventArg<string>(paths[1]);
NewFileOpenedHandler(sender, evt);
OpenNewFileHandler(sender, evt);
}
}
private void AddressRemovedHandler(object sender, EventArg<int> e)
{
var contact = _repository.Contacts[_view.SelectedContactIndex];
@@ -78,7 +210,7 @@ namespace VCFEditor.Presenter
var contact = _repository.Contacts[_view.SelectedContactIndex];
_repository.SetDirtyFlag(_view.SelectedContactIndex);
contact.card.DeliveryAddresses.Add(new vCardDeliveryAddress( e.Data));
contact.card.DeliveryAddresses.Add(new vCardDeliveryAddress(e.Data));
}
private void AddressModifiedHandler(object sender, EventArg<List<vCardDeliveryAddressTypes>> e)
@@ -89,51 +221,69 @@ namespace VCFEditor.Presenter
contact.card.DeliveryAddresses.Clear();
contact.card.DeliveryAddresses.Add(new vCardDeliveryAddress(e.Data));
}
private void ExportImageHandler(object sender, EventArgs e)
{
if (_view.SelectedContactIndex > -1)
{
//TODO: image can be url, or file location.
var card = _repository.Contacts[_view.SelectedContactIndex].card;
var image = card.Photos.FirstOrDefault();
vCard card = _repository.Contacts[_view.SelectedContactIndex].card;
SaveCardPhoto(card, _view.SelectedContactIndex, true);
}
}
if (image != null)
{
var newPath = _repository.ChangeExtension(_repository.fileName, image.Extension);
private void SaveCardPhoto(vCard card, int index, bool askUser = false)
{
//TODO: Save every image for a vCard.
vCardPhoto image = card.Photos.FirstOrDefault();
string imageFile = _view.DisplaySaveDialog(newPath);
_repository.SaveImageToDisk(imageFile, image);
}
if (image != null)
{
string newPath = _repository.GenerateFileName(_repository.fileName, index, image.Extension);
//string ImagePath = string.Empty;
//if (askUser)
// ImagePath = _view.DisplaySaveDialog(newPath);
_repository.SaveImageToDisk(newPath, image);
}
}
private void ExportQRHandler(object sender, EventArgs e)
{
if (_view.SelectedContactIndex > -1)
{
vCard card = _repository.Contacts[_view.SelectedContactIndex].card;
string content = _repository.GenerateStringFromVCard(card);
_view.DisplayQRCode(content);
}
}
private void ModifyImageHandler(object sender, EventArg<string> e)
{
if (!string.IsNullOrEmpty(e.Data) )
if (!string.IsNullOrEmpty(e.Data))
{
vCardPhoto photo = new vCardPhoto(e.Data);
_repository.ModifyImage(_view.SelectedContactIndex, photo);
}
else
_repository.ModifyImage(_view.SelectedContactIndex, null);
}
void CloseFormHandler(object sender, EventArg<bool> e)
{
if (_repository.dirty && _view.AskMessage("Exit without saving?", "Exit"))
if (_repository.dirty && !_view.AskMessage("Exit without saving?", "Exit"))
e.Data = true;
if (!e.Data)
{
var state = _view.GetFormState();
FormState state = _view.GetFormState();
ConfigRepository.Instance.FormState = state;
ConfigRepository.Instance.SaveConfig();
}
}
public void BeforeLeavingContactHandler(object sender, EventArg<vCard> e)
{
_repository.SaveDirtyVCard(_view.SelectedContactIndex, e.Data);
@@ -141,10 +291,14 @@ namespace VCFEditor.Presenter
public void TextBoxValueChangedHandler(object sender, EventArgs e)
{
var tb = sender as StateTextBox;
StateTextBox tb = sender as StateTextBox;
if (tb != null && tb.oldText != tb.Text)
_repository.SetDirtyFlag(_view.SelectedContactIndex);
}
public void CardInfoRemovedHandler(object sender, EventArgs e)
{
_repository.SetDirtyFlag(_view.SelectedContactIndex);
}
public void FilterTextChangedHandler(object sender, EventArg<string> e)
@@ -156,6 +310,7 @@ namespace VCFEditor.Presenter
private void AddContactHandler(object sender, EventArgs e)
{
_repository.AddEmptyContact();
_view.DisplayContacts(_repository.Contacts);
}
private void DeleteContactHandler(object sender, EventArgs e)
@@ -165,49 +320,34 @@ namespace VCFEditor.Presenter
private void SaveContactsHandler(object sender, EventArgs e)
{
if (!string.IsNullOrEmpty(_repository.fileName))
_repository.SaveContactsToFile(_repository.fileName);
string filename = _repository.fileName ?? _view.DisplaySaveDialog();
if (string.IsNullOrWhiteSpace(filename))
return;
_repository.SaveContactsToFile(filename);
}
private void BeforeOpeningNewFileHandler(object sender, EventArgs e)
private void SaveSplittedFileHandler(object sender, EventArgs e)
{
if (_repository.Contacts == null || _repository.Contacts.Count == 0)
return;
string Path = _view.DisplayOpenFolderDialog();
if (!string.IsNullOrEmpty(Path))
{
int count = _repository.SaveSplittedFiles(Path);
_view.DisplayMessage(string.Format("{0} contact(s) processed!", count), "Information");
}
}
private void BeforeOpeningNewFileHandler()
{
if (_repository.Contacts != null && _repository.dirty)
{
if (!_view.AskMessage("Save current file before?", "Load"))
_repository.SaveContactsToFile(_repository.fileName);
if (_view.AskMessage("Save current file before?", "Load"))
SaveContactsHandler(null, null);
//_repository.SaveContactsToFile(_repository.fileName);
}
}
public void NewFileOpenedHandler(object sender, EventArg<string> e)
{
BeforeOpeningNewFileHandler(sender, e);
string path = e.Data;
if (string.IsNullOrEmpty(path))
path = _view.DisplayOpenDialog("vCard Files|*.vcf");
if (!string.IsNullOrEmpty(path))
{
string ext = _repository.GetExtension(path);
if (!string.Equals(ext, ".vcf", StringComparison.OrdinalIgnoreCase))
{
_view.DisplayMessage("Only vcf extension accepted!", "Error");
return;
}
FixedList MostRecentUsedFiles = ConfigRepository.Instance.Paths;
if (!MostRecentUsedFiles.Contains(path))
{
MostRecentUsedFiles.Enqueue(path);
_view.UpdateMRUMenu(MostRecentUsedFiles);
}
_repository.LoadContacts(path);
_view.DisplayContacts(_repository.Contacts);
}
}
public void ChangeContactSelectedHandler(object sender, EventArgs e)
@@ -223,8 +363,6 @@ namespace VCFEditor.Presenter
}
else
_view.ClearContactDetail();
}
}
}

View File

@@ -1,12 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using vCardEditor.Libs.TinyJson;
using vCardEditor.Repository;
using vCardEditor.View;
using VCFEditor.Presenter;
using VCFEditor;
using VCFEditor.Repository;
using vCardEditor.Repository;
namespace vCardEditor
{
@@ -20,10 +18,14 @@ namespace vCardEditor
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
FileHandler fileHandler = new FileHandler();
var fileHandler = new FileHandler();
var mainForm = new MainForm();
new MainPresenter(mainForm, new ContactRepository(fileHandler));
var embeddedlang = new LocalizationLoader(new TinyJsonParser(), fileHandler).LoadEmbedded();
MainForm mainForm = new MainForm();
new MainPresenter(mainForm,
new ContactRepository(fileHandler),
new JsonLocalizationProvider(embeddedlang));
Application.Run(mainForm);
}

View File

@@ -32,5 +32,4 @@ using System.Runtime.InteropServices;
// Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut
// en utilisant '*', comme indiqué ci-dessous :
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.4.0.*")]
//[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyVersion("0.5.8")]

View File

@@ -79,5 +79,15 @@ namespace vCardEditor.Properties {
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Recherche une ressource localisée de type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap nuget_icon {
get {
object obj = ResourceManager.GetObject("nuget-icon", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
}
}

View File

@@ -124,4 +124,7 @@
<data name="Close" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\assests\Close.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="nuget-icon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Libs\QRCoder\Assets\nuget-icon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

View File

@@ -1,4 +1,32 @@
0.5
0.5.8
Merged PR #42, #45
0.5.7
added a feature to batch export/clear/count images.
Fix bug when opening files by menu.
some buttons click were not working properly.
0.5.6
Not released!
0.5.5
redisgn the extra tab
Fix some bugs
0.5.4
Fix a regression when saving Phones, Website, Email
added the update button in the about dialog to check the latest version.
0.5.3
Support of QR Code.
0.5.2
added an option to Right-click on a ".vcf"" file > Open With —> vCardEditor
0.5.1
Fixed clearing fields when changing contact
0.5
A reworked control for adding/modifying or removing addresses.
0.4
@@ -14,4 +42,4 @@
Replaced Moq with nsubstitute (Test mocking library).
0.1
Intial release
Intial release

View File

@@ -17,6 +17,7 @@ namespace vCardEditor.Repository
private const int MAX_RECENT_FILES = 5;
private static ConfigRepository instance = null;
[XmlIgnore]
public static ConfigRepository Instance
{
@@ -30,10 +31,14 @@ namespace vCardEditor.Repository
}
[Description("Overwrite the file when saving")]
public bool OverWrite { get; set; }
public bool Overwrite { get; set; }
[Description("Maximum entries for MRU ")]
public int Maximum { get; set; }
[Description("Url for checking application version")]
public string VersionUrl { get; set; }
[Browsable(false)]
public FixedList Paths { get; set; }
@@ -42,14 +47,12 @@ namespace vCardEditor.Repository
private ConfigRepository() { }
/// <summary>
/// save config file
/// </summary>
public void SaveConfig()
{
var ns = new XmlSerializerNamespaces();
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
XmlSerializer xsSubmit = new XmlSerializer(typeof(ConfigRepository));
@@ -79,21 +82,18 @@ namespace vCardEditor.Repository
configData = (ConfigRepository)deserializer.Deserialize(reader);
configData.Paths.Size = configData.Maximum;
}
}
catch (Exception)
{
configData = new ConfigRepository
{
Maximum = MAX_RECENT_FILES,
Paths = new FixedList(MAX_RECENT_FILES)
Paths = new FixedList(MAX_RECENT_FILES),
VersionUrl = "https://raw.githubusercontent.com/abdelkader/vCardEditor/master/vCardEditor/Releases.txt"
};
}
return configData;
}
}
}

View File

@@ -3,10 +3,9 @@ using System.IO;
using System.Linq;
using System.Text;
using Thought.vCards;
using VCFEditor.Model;
using System.ComponentModel;
using vCardEditor.Repository;
using vCardEditor.View;
using VCFEditor.Model;
namespace VCFEditor.Repository
{
@@ -14,16 +13,13 @@ namespace VCFEditor.Repository
{
public string fileName { get; set; }
private IFileHandler _fileHandler;
/// <summary>
/// Formatted name.
/// </summary>
public const string KeyName = "FN";
/// <summary>
/// Keep a copy of contact list when filtering
/// </summary>
private SortableBindingList<Contact> OriginalContactList = null;
private SortableBindingList<Contact> _contacts;
public SortableBindingList<Contact> Contacts
{
get
@@ -44,45 +40,84 @@ namespace VCFEditor.Repository
get { return (_contacts != null && _contacts.Any(x => x.isDirty)) || _dirty; }
set { _dirty = true; }
}
public ContactRepository(IFileHandler fileHandler)
{
_fileHandler = fileHandler;
}
public SortableBindingList<Contact> LoadContacts(string fileName)
public bool LoadMultipleFilesContact(string path)
{
Contacts.Clear();
string[] filePaths = _fileHandler.GetFiles(path, "*.vcf");
if (filePaths.Count() == 0)
return false;
foreach (string item in filePaths)
{
var result = LoadContactFromFile(item);
Contacts.AddRange(result);
OriginalContactList = Contacts;
}
return true;
}
public bool LoadContacts(string fileName)
{
Contacts.Clear();
this.fileName = fileName;
Contacts = LoadContactFromFile(fileName);
OriginalContactList = Contacts;
return true;
}
public SortableBindingList<Contact> LoadContactFromFile(string fileName)
{
if (!_fileHandler.FileExist(fileName))
return null;
SortableBindingList<Contact> ListOfContacts = new SortableBindingList<Contact>();
string[] lines = _fileHandler.ReadAllLines(fileName);
StringBuilder RawContent = new StringBuilder();
Contact contact = new Contact();
Contact contact;
for (int i = 0; i < lines.Length; i++)
{
RawContent.AppendLine(lines[i]);
if (string.Equals(lines[i].TrimEnd(), "END:VCARD", StringComparison.OrdinalIgnoreCase))
try
{
contact.card = ParseRawContent(RawContent);
Contacts.Add(contact);
contact = new Contact();
RawContent.Length = 0;
if (string.Equals(lines[i].TrimEnd(), "END:VCARD", StringComparison.OrdinalIgnoreCase))
{
contact = new Contact(ParseRawContent(RawContent));
ListOfContacts.Add(contact);
RawContent.Length = 0;
}
}
catch (Exception)
{
return null;
}
}
OriginalContactList = Contacts;
return Contacts;
return ListOfContacts;
}
private vCard ParseRawContent(StringBuilder rawContent)
{
vCard card = null;
using (StringReader reader = new StringReader(rawContent.ToString()))
card = new vCard(reader);
return card;
}
public void AddEmptyContact()
{
if (_contacts != null && _contacts.Count > 0)
{
Contact contact = new Contact();
Contacts.Add(contact);
}
Contacts.Add(new Contact() { isDirty = true });
}
public void SaveContactsToFile(string fileName)
@@ -92,7 +127,7 @@ namespace VCFEditor.Repository
fileName = this.fileName;
//Take a copy if specified in the config file
if (!ConfigRepository.Instance.OverWrite)
if (!ConfigRepository.Instance.Overwrite)
{
string backupName = GetBackupName();
_fileHandler.MoveFile(fileName, backupName);
@@ -100,7 +135,7 @@ namespace VCFEditor.Repository
StringBuilder sb = new StringBuilder();
foreach (var entry in Contacts)
foreach (Contact entry in Contacts)
{
//Do not save the deleted ones!
if (!entry.isDeleted)
@@ -112,8 +147,7 @@ namespace VCFEditor.Repository
//Clean the flag for every contact, even the deleted ones.
entry.isDirty = false;
}
_dirty = false;
_fileHandler.WriteAllText(fileName, sb.ToString());
}
@@ -143,33 +177,8 @@ namespace VCFEditor.Repository
_dirty = true;
_contacts.RemoveAt(i);
}
}
}
}
private vCard ParseRawContent(StringBuilder rawContent)
{
vCard card = null;
using (MemoryStream s = GenerateStreamFromString(rawContent.ToString()))
using (TextReader streamReader = new StreamReader(s, Encoding.UTF8))
{
card = new vCard(streamReader);
}
return card;
}
private MemoryStream GenerateStreamFromString(string s)
{
MemoryStream stream = new MemoryStream();
StreamWriter writer = new StreamWriter(stream);
writer.Write(s);
writer.Flush();
stream.Position = 0;
return stream;
}
public SortableBindingList<Contact> FilterContacts(string filter)
@@ -180,7 +189,6 @@ namespace VCFEditor.Repository
return Contacts;
}
public void SetDirtyFlag(int index)
{
if (index > -1)
@@ -203,14 +211,31 @@ namespace VCFEditor.Repository
SaveEmail(NewCard, card);
SaveWebUrl(NewCard, card);
SaveAddresses(NewCard, card);
SaveExtraField(NewCard, card);
SaveExtraPhones(NewCard, card);
}
}
private void SaveExtraPhones(vCard newCard, vCard card)
{
card.Phones.Clear();
foreach (vCardPhone item in newCard.Phones)
card.Phones.Add(new vCardPhone(item.FullNumber, item.PhoneType));
}
private void SaveExtraField(vCard newCard, vCard card)
{
card.Notes.Clear();
foreach (vCardNote item in newCard.Notes)
card.Notes.Add(new vCardNote(item.Text));
card.Organization = newCard.Organization;
}
private void SaveAddresses(vCard NewCard, vCard card)
{
foreach (var item in NewCard.DeliveryAddresses)
foreach (vCardDeliveryAddress item in NewCard.DeliveryAddresses)
{
var adr = card.DeliveryAddresses.Where(x => x.AddressType.FirstOrDefault() == item.AddressType.FirstOrDefault()).FirstOrDefault();
vCardDeliveryAddress adr = card.DeliveryAddresses.Where(x => x.AddressType.FirstOrDefault() == item.AddressType.FirstOrDefault()).FirstOrDefault();
if (adr != null)
{
adr.City = item.City;
@@ -218,11 +243,12 @@ namespace VCFEditor.Repository
adr.PostalCode = item.PostalCode;
adr.Region = item.Region;
adr.Street = item.Street;
adr.ExtendedAddress = item.ExtendedAddress;
adr.PostOfficeBox = item.PostOfficeBox;
}
else
card.DeliveryAddresses.Add(new vCardDeliveryAddress(item.Street, item.City, item.Region, item.Country,
item.PostalCode, item.AddressType.FirstOrDefault()));
}
}
@@ -240,10 +266,8 @@ namespace VCFEditor.Repository
{
if (card.Phones.GetFirstChoice(vCardPhoneTypes.Home) != null)
card.Phones.GetFirstChoice(vCardPhoneTypes.Home).FullNumber = string.Empty;
}
//Cellular
if (NewCard.Phones.GetFirstChoice(vCardPhoneTypes.Cellular) != null)
{
@@ -256,7 +280,6 @@ namespace VCFEditor.Repository
{
if (card.Phones.GetFirstChoice(vCardPhoneTypes.Cellular) != null)
card.Phones.GetFirstChoice(vCardPhoneTypes.Cellular).FullNumber = string.Empty;
}
//Work
@@ -271,7 +294,6 @@ namespace VCFEditor.Repository
{
if (card.Phones.GetFirstChoice(vCardPhoneTypes.Work) != null)
card.Phones.GetFirstChoice(vCardPhoneTypes.Work).FullNumber = string.Empty;
}
}
@@ -291,14 +313,12 @@ namespace VCFEditor.Repository
{
if (card.EmailAddresses.GetFirstChoice(vCardEmailAddressType.Internet) != null)
card.EmailAddresses.GetFirstChoice(vCardEmailAddressType.Internet).Address = string.Empty;
}
}
private void SaveWebUrl(vCard NewCard, vCard card)
{
//Personal
if (NewCard.Websites.GetFirstChoice(vCardWebsiteTypes.Personal) != null)
{
if (card.Websites.GetFirstChoice(vCardWebsiteTypes.Personal) != null)
@@ -310,32 +330,31 @@ namespace VCFEditor.Repository
{
if (card.Websites.GetFirstChoice(vCardWebsiteTypes.Personal) != null)
card.Websites.GetFirstChoice(vCardWebsiteTypes.Personal).Url = string.Empty;
}
if (NewCard.Websites.GetFirstChoice(vCardWebsiteTypes.Work) != null)
{
if (card.Websites.GetFirstChoice(vCardWebsiteTypes.Work) != null)
card.Websites.GetFirstChoice(vCardWebsiteTypes.Work).Url = NewCard.Websites.GetFirstChoice(vCardWebsiteTypes.Work).Url;
else
card.Websites.Add(new vCardWebsite(NewCard.Websites.GetFirstChoice(vCardWebsiteTypes.Work).Url, vCardWebsiteTypes.Work));
}
else
{
if (card.Websites.GetFirstChoice(vCardWebsiteTypes.Work) != null)
card.Websites.GetFirstChoice(vCardWebsiteTypes.Work).Url = string.Empty;
}
//Work
//if (NewCard.Websites.GetFirstChoice(vCardWebsiteTypes.Work) != null)
//{
// if (card.Websites.GetFirstChoice(vCardWebsiteTypes.Work) != null)
// card.Websites.GetFirstChoice(vCardWebsiteTypes.Work).Url = NewCard.Websites.GetFirstChoice(vCardWebsiteTypes.Work).Url;
// else
// card.Websites.Add(new vCardWebsite(NewCard.Websites.GetFirstChoice(vCardWebsiteTypes.Work).Url, vCardWebsiteTypes.Work));
//}
//else
//{
// if (card.Websites.GetFirstChoice(vCardWebsiteTypes.Work) != null)
// card.Websites.GetFirstChoice(vCardWebsiteTypes.Work).Url = string.Empty;
//}
}
public string GenerateStringFromVCard(vCard card)
{
vCardStandardWriter writer = new vCardStandardWriter();
TextWriter tw = new StringWriter();
writer.Write(card, tw);
return tw.ToString();
using (TextWriter tw = new StringWriter())
{
writer.Write(card, tw);
return tw.ToString();
}
}
public void ModifyImage(int index, vCardPhoto photo)
@@ -364,7 +383,47 @@ namespace VCFEditor.Repository
return _fileHandler.ChangeExtension(path, extension);
}
public string GenerateFileName(string fileName, int index, string extension)
{
return _fileHandler.GetFileNameWithExtension(fileName, index, extension);
}
public int SaveSplittedFiles(string FolderPath)
{
//Do not save the deleted ones!
var contactsToSave = Contacts.Where(x => !x.isDeleted).ToList();
int count;
for (count = 0; count < contactsToSave.Count(); count++)
{
Contact entry = contactsToSave[count];
string SerializedCard = GenerateStringFromVCard(entry.card);
//Check if filename for the card is empty, and generate one if empty
if (string.IsNullOrEmpty(entry.path))
entry.path = GenerateFileName(FolderPath, entry.FamilyName, count);
_fileHandler.WriteAllText(entry.path, SerializedCard);
//Clean the flag for every contact, even the deleted ones.
entry.isDirty = false;
}
//Clean the global flag for the entire vCard Catalog.
_dirty = false;
//return number of contacts processed!
return count;
}
private string GenerateFileName(string FolderPath, string familyName, int index)
{
string FinalPath;
if (string.IsNullOrEmpty(familyName))
FinalPath = _fileHandler.GetVcfFileName(FolderPath, index.ToString());
else
FinalPath = _fileHandler.GetVcfFileName(FolderPath, familyName);
return FinalPath;
}
}
}

View File

@@ -1,5 +1,5 @@
using System.IO;
using System.Reflection;
namespace vCardEditor.Repository
{
@@ -22,7 +22,8 @@ namespace vCardEditor.Repository
public void MoveFile(string newFilename, string oldFilename)
{
File.Move(newFilename, oldFilename);
if (File.Exists(newFilename))
File.Move(newFilename, oldFilename);
}
public string[] ReadAllLines(string filename)
@@ -37,11 +38,41 @@ namespace vCardEditor.Repository
public void WriteBytesToFile(string imageFile, byte[] image)
{
using (var ms = new MemoryStream(image))
using (MemoryStream ms = new MemoryStream(image))
{
using (var fs = new FileStream(imageFile, FileMode.Create))
using (FileStream fs = new FileStream(imageFile, FileMode.Create))
ms.WriteTo(fs);
}
}
public string GetVcfFileName(string folderPath, string filename)
{
return Path.Combine(folderPath, filename + ".vcf");
}
public string GetFileNameWithExtension(string fileName, int index, string extension)
{
return Path.Combine(Path.GetDirectoryName(fileName), index.ToString() + "." + extension);
}
public string[] GetFiles(string path, string ext)
{
return Directory.GetFiles(path, ext, SearchOption.TopDirectoryOnly);
}
public string LoadJsonFromAssembly(string EmbeddedResourceName)
{
string json;
Assembly assembly = Assembly.GetExecutingAssembly();
using (Stream stream = assembly.GetManifestResourceStream(EmbeddedResourceName))
{
if (stream == null)
throw new FileNotFoundException($"Embedded resource '{EmbeddedResourceName}' not found.");
using (StreamReader reader = new StreamReader(stream))
json = reader.ReadToEnd();
}
return json;
}
}
}

View File

@@ -5,7 +5,7 @@ namespace vCardEditor.Repository
public interface IConfigRepository
{
int Maximum { get; set; }
bool OverWrite { get; set; }
bool Overwrite { get; set; }
FixedList Paths { get; set; }
void SaveConfig();

View File

@@ -1,10 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Thought.vCards;
using VCFEditor.Model;
using System.ComponentModel;
using vCardEditor.View;
namespace VCFEditor.Repository
@@ -14,7 +10,8 @@ namespace VCFEditor.Repository
bool dirty { get; }
string fileName { get; set; }
SortableBindingList<Contact> Contacts { get; set; }
SortableBindingList<Contact> LoadContacts(string fileName);
bool LoadContacts(string fileName);
bool LoadMultipleFilesContact(string path);
SortableBindingList<Contact> FilterContacts(string p);
void SaveContactsToFile(string fileName);
void DeleteContact();
@@ -23,9 +20,12 @@ namespace VCFEditor.Repository
void AddEmptyContact();
void ModifyImage(int index, vCardPhoto photo);
string GetExtension(string path);
string ChangeExtension(string path, string extension);
//string ChangeExtension(string path, int index, string extension);
void SaveImageToDisk(string imageFile, vCardPhoto image);
string GenerateStringFromVCard(vCard card);
string GenerateFileName(string fileName, int index, string extension);
int SaveSplittedFiles(string Path);
}
}

View File

@@ -1,4 +1,6 @@
namespace vCardEditor.Repository
using System.IO;
namespace vCardEditor.Repository
{
public interface IFileHandler
{
@@ -9,5 +11,9 @@
string GetExtension(string path);
string ChangeExtension(string path, string extension);
void WriteBytesToFile(string imageFile, byte[] image);
string GetVcfFileName(string folderPath, string familyName);
string GetFileNameWithExtension(string fileName, int index, string extension);
string LoadJsonFromAssembly(string EmbeddedResourceName);
string[] GetFiles(string path, string ext);
}
}

View File

@@ -1,90 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// Ce code a été généré par un outil.
// Version du runtime :4.0.30319.34209
//
// Les modifications apportées à ce fichier peuvent provoquer un comportement incorrect et seront perdues si
// le code est régénéré.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Thought.vCards {
using System;
/// <summary>
/// Une classe de ressource fortement typée destinée, entre autres, à la consultation des chaînes localisées.
/// </summary>
// Cette classe a été générée automatiquement par la classe StronglyTypedResourceBuilder
// à l'aide d'un outil, tel que ResGen ou Visual Studio.
// Pour ajouter ou supprimer un membre, modifiez votre fichier .ResX, puis réexécutez ResGen
// avec l'option /str ou régénérez votre projet VS.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class WarningMessages {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal WarningMessages() {
}
/// <summary>
/// Retourne l'instance ResourceManager mise en cache utilisée par cette classe.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("vCardEditor.Thought.vCards.WarningMessages", typeof(WarningMessages).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Remplace la propriété CurrentUICulture du thread actuel pour toutes
/// les recherches de ressources à l'aide de cette classe de ressource fortement typée.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Recherche une chaîne localisée semblable à Line {0} A blank line was encountered. This is not allowed in the vCard specification..
/// </summary>
internal static string BlankLine {
get {
return ResourceManager.GetString("BlankLine", resourceCulture);
}
}
/// <summary>
/// Recherche une chaîne localisée semblable à Line {0}: A colon (:) is missing. All properties must be in NAME:VALUE format..
/// </summary>
internal static string ColonMissing {
get {
return ResourceManager.GetString("ColonMissing", resourceCulture);
}
}
/// <summary>
/// Recherche une chaîne localisée semblable à Line {0}: The name section of the property is empty..
/// </summary>
internal static string EmptyName {
get {
return ResourceManager.GetString("EmptyName", resourceCulture);
}
}
}
}

View File

@@ -36,137 +36,154 @@
this.labelCompanyName = new System.Windows.Forms.Label();
this.textBoxDescription = new System.Windows.Forms.TextBox();
this.okButton = new System.Windows.Forms.Button();
this.updateButton = new System.Windows.Forms.Button();
this.tableLayoutPanel.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.logoPictureBox)).BeginInit();
this.SuspendLayout();
//
// tableLayoutPanel
//
this.tableLayoutPanel.ColumnCount = 2;
this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 27.81775F));
this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 72.18225F));
this.tableLayoutPanel.ColumnCount = 3;
this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 32.31441F));
this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 67.68559F));
this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 90F));
this.tableLayoutPanel.Controls.Add(this.logoPictureBox, 0, 0);
this.tableLayoutPanel.Controls.Add(this.labelProductName, 1, 0);
this.tableLayoutPanel.Controls.Add(this.labelVersion, 1, 1);
this.tableLayoutPanel.Controls.Add(this.labelCopyright, 1, 2);
this.tableLayoutPanel.Controls.Add(this.labelCompanyName, 1, 3);
this.tableLayoutPanel.Controls.Add(this.textBoxDescription, 1, 4);
this.tableLayoutPanel.Controls.Add(this.okButton, 1, 5);
this.tableLayoutPanel.Controls.Add(this.okButton, 2, 5);
this.tableLayoutPanel.Controls.Add(this.updateButton, 1, 5);
this.tableLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel.Location = new System.Drawing.Point(12, 11);
this.tableLayoutPanel.Margin = new System.Windows.Forms.Padding(4);
this.tableLayoutPanel.Location = new System.Drawing.Point(9, 9);
this.tableLayoutPanel.Name = "tableLayoutPanel";
this.tableLayoutPanel.RowCount = 6;
this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 10F));
this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 10F));
this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 10F));
this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 10F));
this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 10F));
this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25F));
this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25F));
this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25F));
this.tableLayoutPanel.Size = new System.Drawing.Size(556, 326);
this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 47.93651F));
this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 12.69841F));
this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 16F));
this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 16F));
this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 16F));
this.tableLayoutPanel.Size = new System.Drawing.Size(417, 265);
this.tableLayoutPanel.TabIndex = 0;
//
// logoPictureBox
//
this.logoPictureBox.Image = ((System.Drawing.Image)(resources.GetObject("logoPictureBox.Image")));
this.logoPictureBox.Location = new System.Drawing.Point(4, 4);
this.logoPictureBox.Margin = new System.Windows.Forms.Padding(4);
this.logoPictureBox.Location = new System.Drawing.Point(3, 3);
this.logoPictureBox.Name = "logoPictureBox";
this.tableLayoutPanel.SetRowSpan(this.logoPictureBox, 6);
this.logoPictureBox.Size = new System.Drawing.Size(145, 121);
this.logoPictureBox.Size = new System.Drawing.Size(99, 98);
this.logoPictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
this.logoPictureBox.TabIndex = 12;
this.logoPictureBox.TabStop = false;
//
// labelProductName
//
this.tableLayoutPanel.SetColumnSpan(this.labelProductName, 2);
this.labelProductName.Dock = System.Windows.Forms.DockStyle.Fill;
this.labelProductName.Location = new System.Drawing.Point(162, 0);
this.labelProductName.Margin = new System.Windows.Forms.Padding(8, 0, 4, 0);
this.labelProductName.MaximumSize = new System.Drawing.Size(0, 21);
this.labelProductName.Location = new System.Drawing.Point(111, 0);
this.labelProductName.Margin = new System.Windows.Forms.Padding(6, 0, 3, 0);
this.labelProductName.MaximumSize = new System.Drawing.Size(0, 17);
this.labelProductName.Name = "labelProductName";
this.labelProductName.Size = new System.Drawing.Size(390, 21);
this.labelProductName.Size = new System.Drawing.Size(303, 17);
this.labelProductName.TabIndex = 19;
this.labelProductName.Text = "Nom du produit";
this.labelProductName.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// labelVersion
//
this.tableLayoutPanel.SetColumnSpan(this.labelVersion, 2);
this.labelVersion.Dock = System.Windows.Forms.DockStyle.Fill;
this.labelVersion.Location = new System.Drawing.Point(162, 32);
this.labelVersion.Margin = new System.Windows.Forms.Padding(8, 0, 4, 0);
this.labelVersion.MaximumSize = new System.Drawing.Size(0, 21);
this.labelVersion.Location = new System.Drawing.Point(111, 26);
this.labelVersion.Margin = new System.Windows.Forms.Padding(6, 0, 3, 0);
this.labelVersion.MaximumSize = new System.Drawing.Size(0, 17);
this.labelVersion.Name = "labelVersion";
this.labelVersion.Size = new System.Drawing.Size(390, 21);
this.labelVersion.Size = new System.Drawing.Size(303, 17);
this.labelVersion.TabIndex = 0;
this.labelVersion.Text = "Version";
this.labelVersion.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// labelCopyright
//
this.tableLayoutPanel.SetColumnSpan(this.labelCopyright, 2);
this.labelCopyright.Dock = System.Windows.Forms.DockStyle.Fill;
this.labelCopyright.Location = new System.Drawing.Point(162, 64);
this.labelCopyright.Margin = new System.Windows.Forms.Padding(8, 0, 4, 0);
this.labelCopyright.MaximumSize = new System.Drawing.Size(0, 21);
this.labelCopyright.Location = new System.Drawing.Point(111, 52);
this.labelCopyright.Margin = new System.Windows.Forms.Padding(6, 0, 3, 0);
this.labelCopyright.MaximumSize = new System.Drawing.Size(0, 17);
this.labelCopyright.Name = "labelCopyright";
this.labelCopyright.Size = new System.Drawing.Size(390, 21);
this.labelCopyright.Size = new System.Drawing.Size(303, 17);
this.labelCopyright.TabIndex = 21;
this.labelCopyright.Text = "Copyright";
this.labelCopyright.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// labelCompanyName
//
this.tableLayoutPanel.SetColumnSpan(this.labelCompanyName, 2);
this.labelCompanyName.Dock = System.Windows.Forms.DockStyle.Fill;
this.labelCompanyName.Location = new System.Drawing.Point(162, 96);
this.labelCompanyName.Margin = new System.Windows.Forms.Padding(8, 0, 4, 0);
this.labelCompanyName.MaximumSize = new System.Drawing.Size(0, 21);
this.labelCompanyName.Location = new System.Drawing.Point(111, 78);
this.labelCompanyName.Margin = new System.Windows.Forms.Padding(6, 0, 3, 0);
this.labelCompanyName.MaximumSize = new System.Drawing.Size(0, 17);
this.labelCompanyName.Name = "labelCompanyName";
this.labelCompanyName.Size = new System.Drawing.Size(390, 21);
this.labelCompanyName.Size = new System.Drawing.Size(303, 17);
this.labelCompanyName.TabIndex = 22;
this.labelCompanyName.Text = "Nom de la société";
this.labelCompanyName.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// textBoxDescription
//
this.tableLayoutPanel.SetColumnSpan(this.textBoxDescription, 2);
this.textBoxDescription.Dock = System.Windows.Forms.DockStyle.Fill;
this.textBoxDescription.Location = new System.Drawing.Point(162, 132);
this.textBoxDescription.Margin = new System.Windows.Forms.Padding(8, 4, 4, 4);
this.textBoxDescription.Location = new System.Drawing.Point(111, 107);
this.textBoxDescription.Margin = new System.Windows.Forms.Padding(6, 3, 3, 3);
this.textBoxDescription.Multiline = true;
this.textBoxDescription.Name = "textBoxDescription";
this.textBoxDescription.ReadOnly = true;
this.textBoxDescription.ScrollBars = System.Windows.Forms.ScrollBars.Both;
this.textBoxDescription.Size = new System.Drawing.Size(390, 155);
this.textBoxDescription.Size = new System.Drawing.Size(303, 120);
this.textBoxDescription.TabIndex = 23;
this.textBoxDescription.TabStop = false;
this.textBoxDescription.Text = "Description";
//
// okButton
//
this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.okButton.Anchor = System.Windows.Forms.AnchorStyles.Right;
this.okButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.okButton.Location = new System.Drawing.Point(452, 295);
this.okButton.Margin = new System.Windows.Forms.Padding(4);
this.okButton.Location = new System.Drawing.Point(332, 235);
this.okButton.Name = "okButton";
this.okButton.Size = new System.Drawing.Size(100, 27);
this.okButton.Size = new System.Drawing.Size(82, 24);
this.okButton.TabIndex = 24;
this.okButton.Text = "&OK";
//
// updateButton
//
this.updateButton.Anchor = System.Windows.Forms.AnchorStyles.Right;
this.updateButton.Location = new System.Drawing.Point(233, 235);
this.updateButton.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);
this.updateButton.Name = "updateButton";
this.updateButton.Size = new System.Drawing.Size(91, 24);
this.updateButton.TabIndex = 25;
this.updateButton.Text = "Check update...";
this.updateButton.Click += new System.EventHandler(this.updateButton_Click);
//
// AboutDialog
//
this.AcceptButton = this.okButton;
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(580, 348);
this.ClientSize = new System.Drawing.Size(435, 283);
this.Controls.Add(this.tableLayoutPanel);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.Margin = new System.Windows.Forms.Padding(4);
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "AboutDialog";
this.Padding = new System.Windows.Forms.Padding(12, 11, 12, 11);
this.Padding = new System.Windows.Forms.Padding(9, 9, 9, 9);
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
@@ -188,5 +205,6 @@
private System.Windows.Forms.Label labelCompanyName;
private System.Windows.Forms.TextBox textBoxDescription;
private System.Windows.Forms.Button okButton;
private System.Windows.Forms.Button updateButton;
}
}

View File

@@ -1,10 +1,9 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.IO;
using System.Net;
using System.Reflection;
using System.Windows.Forms;
using vCardEditor.Repository;
namespace vCardEditor.View
{
@@ -36,7 +35,7 @@ namespace vCardEditor.View
return titleAttribute.Title;
}
}
return System.IO.Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().CodeBase);
return Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().CodeBase);
}
}
@@ -100,5 +99,36 @@ namespace vCardEditor.View
}
}
#endregion
private async void updateButton_Click(object sender, EventArgs e)
{
try
{
using (WebClient client = new WebClient())
{
string result = await client.DownloadStringTaskAsync(ConfigRepository.Instance.VersionUrl);
using (StringReader reader = new StringReader(result))
{
string InternetVersion = reader.ReadLine();
string AssemblyVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString();
Version v1 = new Version(InternetVersion);
Version v2 = new Version(AssemblyVersion);
if (v1.CompareTo(v2) > 0)
MessageBox.Show(string.Format("New version {0} found!", result), "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
else
MessageBox.Show("You have the latest version!", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
}
catch (WebException)
{
MessageBox.Show("Could not download version information from GitHub.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
catch (Exception)
{
MessageBox.Show("Error processing version information.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
}

View File

@@ -30,48 +30,33 @@
{
this.btnClose = new System.Windows.Forms.Button();
this.pgConfig = new System.Windows.Forms.PropertyGrid();
this.btnCancel = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// btnClose
//
this.btnClose.DialogResult = System.Windows.Forms.DialogResult.OK;
this.btnClose.Location = new System.Drawing.Point(337, 427);
this.btnClose.Margin = new System.Windows.Forms.Padding(4);
this.btnClose.Location = new System.Drawing.Point(253, 347);
this.btnClose.Name = "btnClose";
this.btnClose.Size = new System.Drawing.Size(100, 28);
this.btnClose.Size = new System.Drawing.Size(75, 23);
this.btnClose.TabIndex = 0;
this.btnClose.Text = "Close";
this.btnClose.UseVisualStyleBackColor = true;
//
// pgConfig
//
this.pgConfig.Location = new System.Drawing.Point(16, 15);
this.pgConfig.Margin = new System.Windows.Forms.Padding(4);
this.pgConfig.Location = new System.Drawing.Point(12, 12);
this.pgConfig.Name = "pgConfig";
this.pgConfig.Size = new System.Drawing.Size(421, 405);
this.pgConfig.Size = new System.Drawing.Size(316, 329);
this.pgConfig.TabIndex = 1;
//
// btnCancel
//
this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.btnCancel.Location = new System.Drawing.Point(229, 427);
this.btnCancel.Margin = new System.Windows.Forms.Padding(4);
this.btnCancel.Name = "btnCancel";
this.btnCancel.Size = new System.Drawing.Size(100, 28);
this.btnCancel.TabIndex = 0;
this.btnCancel.Text = "Cancel";
this.btnCancel.UseVisualStyleBackColor = true;
//
// ConfigDialog
//
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(449, 470);
this.ClientSize = new System.Drawing.Size(337, 382);
this.Controls.Add(this.pgConfig);
this.Controls.Add(this.btnCancel);
this.Controls.Add(this.btnClose);
this.Margin = new System.Windows.Forms.Padding(4);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.Name = "ConfigDialog";
this.Text = "Configuration Dialog";
this.ResumeLayout(false);
@@ -82,6 +67,5 @@
private System.Windows.Forms.Button btnClose;
private System.Windows.Forms.PropertyGrid pgConfig;
private System.Windows.Forms.Button btnCancel;
}
}

View File

@@ -1,7 +1,5 @@
using System;
using System.Windows.Forms;
using System.Windows.Forms;
using vCardEditor.Repository;
using vCardEditor.Model;
namespace vCardEditor.View
{
@@ -10,7 +8,7 @@ namespace vCardEditor.View
public ConfigDialog()
{
InitializeComponent();
ConfigRepository conf = ConfigRepository.Instance;//
ConfigRepository conf = ConfigRepository.Instance;
pgConfig.SelectedObject = conf;
}
}

View File

@@ -36,8 +36,8 @@ namespace vCardEditor.View.Customs
this.btnOK = new System.Windows.Forms.Button();
this.btnCancel = new System.Windows.Forms.Button();
this.cbInternational = new System.Windows.Forms.CheckBox();
this.cbCustom = new System.Windows.Forms.CheckBox();
this.textBox1 = new System.Windows.Forms.TextBox();
this.cbParcel = new System.Windows.Forms.CheckBox();
this.cbPreferred = new System.Windows.Forms.CheckBox();
this.SuspendLayout();
//
// cbHome
@@ -73,7 +73,7 @@ namespace vCardEditor.View.Customs
// cbDomestic
//
this.cbDomestic.AutoSize = true;
this.cbDomestic.Location = new System.Drawing.Point(12, 94);
this.cbDomestic.Location = new System.Drawing.Point(143, 40);
this.cbDomestic.Name = "cbDomestic";
this.cbDomestic.Size = new System.Drawing.Size(88, 21);
this.cbDomestic.TabIndex = 3;
@@ -82,8 +82,9 @@ namespace vCardEditor.View.Customs
//
// btnOK
//
this.btnOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK;
this.btnOK.Location = new System.Drawing.Point(95, 187);
this.btnOK.Location = new System.Drawing.Point(95, 161);
this.btnOK.Name = "btnOK";
this.btnOK.Size = new System.Drawing.Size(75, 23);
this.btnOK.TabIndex = 7;
@@ -93,9 +94,10 @@ namespace vCardEditor.View.Customs
//
// btnCancel
//
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnCancel.CausesValidation = false;
this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.btnCancel.Location = new System.Drawing.Point(176, 187);
this.btnCancel.Location = new System.Drawing.Point(176, 161);
this.btnCancel.Name = "btnCancel";
this.btnCancel.Size = new System.Drawing.Size(75, 23);
this.btnCancel.TabIndex = 8;
@@ -105,37 +107,41 @@ namespace vCardEditor.View.Customs
// cbInternational
//
this.cbInternational.AutoSize = true;
this.cbInternational.Location = new System.Drawing.Point(12, 121);
this.cbInternational.Location = new System.Drawing.Point(143, 12);
this.cbInternational.Name = "cbInternational";
this.cbInternational.Size = new System.Drawing.Size(108, 21);
this.cbInternational.TabIndex = 9;
this.cbInternational.Text = "International";
this.cbInternational.UseVisualStyleBackColor = true;
//
// cbCustom
// cbParcel
//
this.cbCustom.AutoSize = true;
this.cbCustom.Location = new System.Drawing.Point(12, 148);
this.cbCustom.Name = "cbCustom";
this.cbCustom.Size = new System.Drawing.Size(81, 21);
this.cbCustom.TabIndex = 10;
this.cbCustom.Text = "Custom:";
this.cbCustom.UseVisualStyleBackColor = true;
this.cbParcel.AutoSize = true;
this.cbParcel.Location = new System.Drawing.Point(143, 67);
this.cbParcel.Name = "cbParcel";
this.cbParcel.Size = new System.Drawing.Size(70, 21);
this.cbParcel.TabIndex = 10;
this.cbParcel.Text = "Parcel";
this.cbParcel.UseVisualStyleBackColor = true;
//
// textBox1
// cbPreferred
//
this.textBox1.Location = new System.Drawing.Point(90, 149);
this.textBox1.Name = "textBox1";
this.textBox1.Size = new System.Drawing.Size(161, 22);
this.textBox1.TabIndex = 11;
this.cbPreferred.AutoSize = true;
this.cbPreferred.Font = new System.Drawing.Font("Microsoft Sans Serif", 7.8F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.cbPreferred.Location = new System.Drawing.Point(95, 113);
this.cbPreferred.Name = "cbPreferred";
this.cbPreferred.Size = new System.Drawing.Size(99, 21);
this.cbPreferred.TabIndex = 11;
this.cbPreferred.Text = "Preferred";
this.cbPreferred.UseVisualStyleBackColor = true;
//
// AddAddress
// AddAddressDialog
//
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(263, 223);
this.Controls.Add(this.textBox1);
this.Controls.Add(this.cbCustom);
this.ClientSize = new System.Drawing.Size(263, 196);
this.Controls.Add(this.cbPreferred);
this.Controls.Add(this.cbParcel);
this.Controls.Add(this.cbInternational);
this.Controls.Add(this.btnCancel);
this.Controls.Add(this.btnOK);
@@ -143,7 +149,7 @@ namespace vCardEditor.View.Customs
this.Controls.Add(this.cbPostal);
this.Controls.Add(this.cbWork);
this.Controls.Add(this.cbHome);
this.Name = "AddAddress";
this.Name = "AddAddressDialog";
this.Text = "Address Type";
this.ResumeLayout(false);
this.PerformLayout();
@@ -159,7 +165,7 @@ namespace vCardEditor.View.Customs
private System.Windows.Forms.Button btnOK;
private System.Windows.Forms.Button btnCancel;
private System.Windows.Forms.CheckBox cbInternational;
private System.Windows.Forms.CheckBox cbCustom;
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.CheckBox cbParcel;
private System.Windows.Forms.CheckBox cbPreferred;
}
}

View File

@@ -1,11 +1,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Thought.vCards;
@@ -30,57 +26,52 @@ namespace vCardEditor.View.Customs
Addresses = addressCollection;
_checkBoxes = Controls.OfType<CheckBox>().ToList();
foreach (var item in addressCollection)
foreach (vCardDeliveryAddressTypes item in addressCollection)
{
switch (item.ToString())
{
case "Home":
cbHome.Checked = true;
break;
break;
case "Work":
cbWork.Checked = true;
break;
case "Postal":
cbPostal.Checked = true;
break;
case "Parcel":
cbParcel.Checked = true;
break;
case "Preferred":
cbPreferred.Checked = true;
break;
case "Domestic":
cbDomestic.Checked = true;
break;
case "International":
cbInternational.Checked = true;
break;
case "Default":
cbCustom.Checked = true;
break;
}
}
}
private void btnOK_Click(object sender, EventArgs e)
{
var total = _checkBoxes
.Where(checkBox => checkBox.Checked);
var checkedItems = _checkBoxes.Where(checkBox => checkBox.Checked);
if (total.Count() == 0)
if (checkedItems.Count() == 0)
{
MessageBox.Show("One item must be checked!");
MessageBox.Show("At least, one address type must be checked!");
DialogResult = DialogResult.None;
return;
}
foreach (var item in total)
Addresses.Clear();
foreach (CheckBox item in checkedItems)
{
var enumType = (vCardDeliveryAddressTypes)Enum.Parse(typeof(vCardDeliveryAddressTypes), item.Text, true);
vCardDeliveryAddressTypes enumType = (vCardDeliveryAddressTypes)Enum.Parse(typeof(vCardDeliveryAddressTypes), item.Text, true);
Addresses.Add(enumType);
}
}
}
}

View File

@@ -29,43 +29,41 @@ namespace vCardEditor.View.Customs
/// </summary>
private void InitializeComponent()
{
this.ExtAddrValue = new vCardEditor.View.StateTextBox();
this.ExtAddrValue = new vCardEditor.View.Customs.StateTextBox();
this.ExtAdressLabel = new System.Windows.Forms.Label();
this.StreetLabel = new System.Windows.Forms.Label();
this.StreetValue = new vCardEditor.View.StateTextBox();
this.StreetValue = new vCardEditor.View.Customs.StateTextBox();
this.POBoxLabel = new System.Windows.Forms.Label();
this.CountryValue = new vCardEditor.View.StateTextBox();
this.CountryValue = new vCardEditor.View.Customs.StateTextBox();
this.Country = new System.Windows.Forms.Label();
this.POBoxValue = new vCardEditor.View.StateTextBox();
this.POBoxValue = new vCardEditor.View.Customs.StateTextBox();
this.CityLabel = new System.Windows.Forms.Label();
this.RegionValue = new vCardEditor.View.StateTextBox();
this.CityValue = new vCardEditor.View.StateTextBox();
this.RegionValue = new vCardEditor.View.Customs.StateTextBox();
this.CityValue = new vCardEditor.View.Customs.StateTextBox();
this.StateLabel = new System.Windows.Forms.Label();
this.ZipLabel = new System.Windows.Forms.Label();
this.ZipValue = new vCardEditor.View.StateTextBox();
this.ZipValue = new vCardEditor.View.Customs.StateTextBox();
this.SuspendLayout();
//
// ExtAddrValue
//
this.ExtAddrValue.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.ExtAddrValue.Location = new System.Drawing.Point(90, 45);
this.ExtAddrValue.Location = new System.Drawing.Point(68, 45);
this.ExtAddrValue.Margin = new System.Windows.Forms.Padding(4);
this.ExtAddrValue.Name = "ExtAddrValue";
this.ExtAddrValue.oldText = null;
this.ExtAddrValue.Size = new System.Drawing.Size(237, 22);
this.ExtAddrValue.Size = new System.Drawing.Size(190, 22);
this.ExtAddrValue.TabIndex = 27;
this.ExtAddrValue.LostFocus += new System.EventHandler(this.Value_TextChanged);
this.ExtAddrValue.Validated += new System.EventHandler(this.Value_TextChanged);
//
// ExtAdressLabel
//
this.ExtAdressLabel.Location = new System.Drawing.Point(1, 42);
this.ExtAdressLabel.Location = new System.Drawing.Point(5, 44);
this.ExtAdressLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.ExtAdressLabel.Name = "ExtAdressLabel";
this.ExtAdressLabel.Size = new System.Drawing.Size(87, 23);
this.ExtAdressLabel.Size = new System.Drawing.Size(40, 23);
this.ExtAdressLabel.TabIndex = 26;
this.ExtAdressLabel.Text = "Ext Address:";
this.ExtAdressLabel.Text = "Ext:";
this.ExtAdressLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// StreetLabel
@@ -73,7 +71,7 @@ namespace vCardEditor.View.Customs
this.StreetLabel.Location = new System.Drawing.Point(2, 14);
this.StreetLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.StreetLabel.Name = "StreetLabel";
this.StreetLabel.Size = new System.Drawing.Size(65, 23);
this.StreetLabel.Size = new System.Drawing.Size(64, 23);
this.StreetLabel.TabIndex = 14;
this.StreetLabel.Text = "Address:";
this.StreetLabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
@@ -82,42 +80,41 @@ namespace vCardEditor.View.Customs
//
this.StreetValue.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.StreetValue.Location = new System.Drawing.Point(90, 14);
this.StreetValue.Location = new System.Drawing.Point(68, 14);
this.StreetValue.Margin = new System.Windows.Forms.Padding(4);
this.StreetValue.Name = "StreetValue";
this.StreetValue.oldText = "";
this.StreetValue.Size = new System.Drawing.Size(613, 22);
this.StreetValue.Size = new System.Drawing.Size(632, 22);
this.StreetValue.TabIndex = 15;
this.StreetValue.LostFocus += new System.EventHandler(this.Value_TextChanged);
this.StreetValue.Validated += new System.EventHandler(this.Value_TextChanged);
//
// POBoxLabel
//
this.POBoxLabel.Location = new System.Drawing.Point(334, 75);
this.POBoxLabel.Location = new System.Drawing.Point(267, 76);
this.POBoxLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.POBoxLabel.Name = "POBoxLabel";
this.POBoxLabel.Size = new System.Drawing.Size(59, 23);
this.POBoxLabel.Size = new System.Drawing.Size(38, 23);
this.POBoxLabel.TabIndex = 16;
this.POBoxLabel.Text = "PO Box:";
this.POBoxLabel.Text = "PO :";
this.POBoxLabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// CountryValue
//
this.CountryValue.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.CountryValue.Location = new System.Drawing.Point(579, 76);
this.CountryValue.Location = new System.Drawing.Point(557, 76);
this.CountryValue.Margin = new System.Windows.Forms.Padding(4);
this.CountryValue.Name = "CountryValue";
this.CountryValue.oldText = null;
this.CountryValue.Size = new System.Drawing.Size(125, 22);
this.CountryValue.Size = new System.Drawing.Size(143, 22);
this.CountryValue.TabIndex = 25;
this.CountryValue.LostFocus += new System.EventHandler(this.Value_TextChanged);
this.CountryValue.Validated += new System.EventHandler(this.Value_TextChanged);
//
// Country
//
this.Country.Location = new System.Drawing.Point(505, 76);
this.Country.Location = new System.Drawing.Point(486, 74);
this.Country.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.Country.Name = "Country";
this.Country.Size = new System.Drawing.Size(65, 23);
@@ -127,20 +124,18 @@ namespace vCardEditor.View.Customs
//
// POBoxValue
//
this.POBoxValue.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.POBoxValue.Location = new System.Drawing.Point(397, 75);
this.POBoxValue.Location = new System.Drawing.Point(315, 77);
this.POBoxValue.Margin = new System.Windows.Forms.Padding(4);
this.POBoxValue.Name = "POBoxValue";
this.POBoxValue.oldText = null;
this.POBoxValue.Size = new System.Drawing.Size(100, 22);
this.POBoxValue.Size = new System.Drawing.Size(166, 22);
this.POBoxValue.TabIndex = 17;
this.POBoxValue.LostFocus += new System.EventHandler(this.Value_TextChanged);
this.POBoxValue.Validated += new System.EventHandler(this.Value_TextChanged);
//
// CityLabel
//
this.CityLabel.Location = new System.Drawing.Point(338, 45);
this.CityLabel.Location = new System.Drawing.Point(267, 44);
this.CityLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.CityLabel.Name = "CityLabel";
this.CityLabel.Size = new System.Drawing.Size(32, 23);
@@ -152,31 +147,29 @@ namespace vCardEditor.View.Customs
//
this.RegionValue.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.RegionValue.Location = new System.Drawing.Point(90, 75);
this.RegionValue.Location = new System.Drawing.Point(557, 44);
this.RegionValue.Margin = new System.Windows.Forms.Padding(4);
this.RegionValue.Name = "RegionValue";
this.RegionValue.oldText = null;
this.RegionValue.Size = new System.Drawing.Size(236, 22);
this.RegionValue.Size = new System.Drawing.Size(143, 22);
this.RegionValue.TabIndex = 23;
this.RegionValue.LostFocus += new System.EventHandler(this.Value_TextChanged);
this.RegionValue.Validated += new System.EventHandler(this.Value_TextChanged);
//
// CityValue
//
this.CityValue.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.CityValue.Location = new System.Drawing.Point(397, 45);
this.CityValue.Location = new System.Drawing.Point(316, 46);
this.CityValue.Margin = new System.Windows.Forms.Padding(4);
this.CityValue.Name = "CityValue";
this.CityValue.oldText = null;
this.CityValue.Size = new System.Drawing.Size(127, 22);
this.CityValue.Size = new System.Drawing.Size(166, 22);
this.CityValue.TabIndex = 19;
this.CityValue.LostFocus += new System.EventHandler(this.Value_TextChanged);
this.CityValue.Validated += new System.EventHandler(this.Value_TextChanged);
//
// StateLabel
//
this.StateLabel.Location = new System.Drawing.Point(17, 74);
this.StateLabel.Location = new System.Drawing.Point(486, 46);
this.StateLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.StateLabel.Name = "StateLabel";
this.StateLabel.Size = new System.Drawing.Size(61, 23);
@@ -186,31 +179,29 @@ namespace vCardEditor.View.Customs
//
// ZipLabel
//
this.ZipLabel.Location = new System.Drawing.Point(533, 45);
this.ZipLabel.Location = new System.Drawing.Point(5, 75);
this.ZipLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.ZipLabel.Name = "ZipLabel";
this.ZipLabel.Size = new System.Drawing.Size(37, 23);
this.ZipLabel.TabIndex = 20;
this.ZipLabel.TabIndex = 28;
this.ZipLabel.Text = "Zip:";
this.ZipLabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
//
// ZipValue
//
this.ZipValue.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.ZipValue.Location = new System.Drawing.Point(579, 46);
this.ZipValue.Location = new System.Drawing.Point(68, 77);
this.ZipValue.Margin = new System.Windows.Forms.Padding(4);
this.ZipValue.Name = "ZipValue";
this.ZipValue.oldText = null;
this.ZipValue.Size = new System.Drawing.Size(124, 22);
this.ZipValue.TabIndex = 21;
this.ZipValue.LostFocus += new System.EventHandler(this.Value_TextChanged);
this.ZipValue.Validated += new System.EventHandler(this.Value_TextChanged);
this.ZipValue.Size = new System.Drawing.Size(190, 22);
this.ZipValue.TabIndex = 29;
//
// AddressBox
//
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.ZipLabel);
this.Controls.Add(this.ZipValue);
this.Controls.Add(this.ExtAddrValue);
this.Controls.Add(this.ExtAdressLabel);
this.Controls.Add(this.StreetLabel);
@@ -223,8 +214,6 @@ namespace vCardEditor.View.Customs
this.Controls.Add(this.RegionValue);
this.Controls.Add(this.CityValue);
this.Controls.Add(this.StateLabel);
this.Controls.Add(this.ZipLabel);
this.Controls.Add(this.ZipValue);
this.Name = "AddressBox";
this.Size = new System.Drawing.Size(706, 104);
this.ResumeLayout(false);

View File

@@ -8,8 +8,8 @@ namespace vCardEditor.View.Customs
public partial class AddressBox : UserControl
{
public event EventHandler TextChangedEvent;
public List<vCardDeliveryAddressTypes> AddressType { get; set; }
public AddressBox(string street, string city, string region, string country, string postalCode,
string extendedAddress, string postOfficeBox, List<vCardDeliveryAddressTypes> addressType)
@@ -46,7 +46,7 @@ namespace vCardEditor.View.Customs
public vCardDeliveryAddress getDeliveryAddress()
{
var deliveryAddress = new vCardDeliveryAddress
vCardDeliveryAddress deliveryAddress = new vCardDeliveryAddress
{
City = CityValue.Text,
Country = CountryValue.Text,
@@ -57,9 +57,7 @@ namespace vCardEditor.View.Customs
PostOfficeBox = POBoxValue.Text,
AddressType = AddressType
};
return deliveryAddress;
}
}
}

View File

@@ -26,7 +26,6 @@ namespace vCardEditor.View.Customs
Selecting += tbcAddress_Selecting;
HandleCreated += tbcAddress_HandleCreated;
MouseDoubleClick += AddressTabControl_MouseDoubleClick;
}
public void getDeliveryAddress(vCard card)
@@ -51,9 +50,9 @@ namespace vCardEditor.View.Customs
{
if (GetTabRect(i).Contains(e.Location))
{
var AddressBox = TabPages[i].Controls[0] as AddressBox;
AddressBox AddressBox = TabPages[i].Controls[0] as AddressBox;
var diag = new AddAddressDialog(AddressBox.AddressType);
AddAddressDialog diag = new AddAddressDialog(AddressBox.AddressType);
if (diag.ShowDialog() == DialogResult.OK)
{
@@ -66,10 +65,8 @@ namespace vCardEditor.View.Customs
break;
}
}
}
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
private const int TCM_SETMINTABWIDTH = 0x1300 + 49;
@@ -78,38 +75,35 @@ namespace vCardEditor.View.Customs
SendMessage(Handle, TCM_SETMINTABWIDTH, IntPtr.Zero, (IntPtr)16);
}
private void tbcAddress_Selecting(object sender, TabControlCancelEventArgs e)
{
if (e.TabPageIndex == TabCount - 1)
e.Cancel = true;
}
private void tbcAddress_MouseDown(object sender, MouseEventArgs e)
{
var lastIndex = TabCount - 1;
int lastIndex = TabCount - 1;
if (GetTabRect(lastIndex).Contains(e.Location))
{
var diag = new AddAddressDialog();
AddAddressDialog diag = new AddAddressDialog();
if (diag.ShowDialog() == DialogResult.OK)
{
vCardDeliveryAddress da = new vCardDeliveryAddress();
da.AddressType = diag.Addresses;
AddtabForAddress(da);
AddTab?.Invoke(sender, new EventArg<List<vCardDeliveryAddressTypes>>(diag.Addresses));
SelectedIndex = TabCount - 1;
SelectedIndex = TabCount - 2;
}
}
else
{
for (var i = 0; i < TabPages.Count; i++)
for (int i = 0; i < TabPages.Count; i++)
{
var tabRect = GetTabRect(i);
Rectangle tabRect = GetTabRect(i);
tabRect.Inflate(-2, -2);
var closeImage = Properties.Resources.Close;
var imageRect = new Rectangle(
Bitmap closeImage = Properties.Resources.Close;
Rectangle imageRect = new Rectangle(
(tabRect.Right - closeImage.Width),
tabRect.Top + (tabRect.Height - closeImage.Height) / 2,
closeImage.Width, closeImage.Height);
@@ -122,12 +116,9 @@ namespace vCardEditor.View.Customs
SelectedIndex = 0;
RemoveTab?.Invoke(sender, new EventArg<int>(i));
}
return;
}
}
}
}
@@ -136,19 +127,19 @@ namespace vCardEditor.View.Customs
if (e.Index > TabCount - 1)
return;
var tabRect = GetTabRect(e.Index);
Rectangle tabRect = GetTabRect(e.Index);
tabRect.Inflate(-2, -2);
if (e.Index == TabCount - 1)
{
var addImage = Properties.Resources.Add;
Bitmap addImage = Properties.Resources.Add;
e.Graphics.DrawImage(addImage,
tabRect.Left + (tabRect.Width - addImage.Width) / 2,
tabRect.Top + (tabRect.Height - addImage.Height) / 2);
}
else
{
var closeImage = Properties.Resources.Close;
Bitmap closeImage = Properties.Resources.Close;
e.Graphics.DrawImage(closeImage,
(tabRect.Right - closeImage.Width),
tabRect.Top + (tabRect.Height - closeImage.Height) / 2);
@@ -172,8 +163,6 @@ namespace vCardEditor.View.Customs
TextBrush.Dispose();
}
}
public void SetAddresses(vCard card)
@@ -184,7 +173,7 @@ namespace vCardEditor.View.Customs
private void AddTabForEveryAddress(vCard card)
{
foreach (var item in card.DeliveryAddresses)
foreach (vCardDeliveryAddress item in card.DeliveryAddresses)
AddtabForAddress(item);
SelectedIndex = 0;
}
@@ -193,35 +182,32 @@ namespace vCardEditor.View.Customs
{
string title = GetTabTitle(da.AddressType);
var page = new TabPage($" {title} ");
TabPage page = new TabPage($" {title} ");
TabPages.Insert(TabCount - 1, page);
var ab = new AddressBox(da.Street, da.City, da.Region, da.Country,
AddressBox ab = new AddressBox(da.Street, da.City, da.Region, da.Country,
da.PostalCode, da.ExtendedAddress, da.PostOfficeBox, da.AddressType);
ab.TextChangedEvent += (s, e) => TextChangedEvent?.Invoke(s, e);
ab.Dock = DockStyle.Fill;
page.Controls.Add(ab);
page.ToolTipText = string.Join(",", da.AddressType.ConvertAll(f => f.ToString()));
}
private string GetTabTitle(List<vCardDeliveryAddressTypes> addressTypes)
{
var title = string.Empty;
string title = string.Empty;
if (addressTypes.Count > 0)
{
title = addressTypes[0].ToString();
if (addressTypes.Count > 1)
title += "...";
}
return title;
}
private void ClearTabs()
{
//Remove every tab (except "+"). We don't call Clear() as it doesn't free memory.
while (TabCount > 1)
TabPages[0].Dispose();

Some files were not shown because too many files have changed in this diff Show More