summaryrefslogtreecommitdiff
path: root/src/fontpack.c
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-10-17 20:33:14 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-10-17 20:33:14 +0300
commitb3ae7efcb9adb1de3d02f0753e2a79888bdb71ac (patch)
tree57fb9f82f2d19a1c8e6d1aa85865f9df210e2c4b /src/fontpack.c
parent2e2b823bfb5d34d42c6b6c1b289193c854459b45 (diff)
FontPack management via "about:fonts"
Diffstat (limited to 'src/fontpack.c')
-rw-r--r--src/fontpack.c197
1 files changed, 149 insertions, 48 deletions
diff --git a/src/fontpack.c b/src/fontpack.c
index 9603bded..9b165051 100644
--- a/src/fontpack.c
+++ b/src/fontpack.c
@@ -32,6 +32,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
32#include <the_Foundation/path.h> 32#include <the_Foundation/path.h>
33#include <the_Foundation/ptrarray.h> 33#include <the_Foundation/ptrarray.h>
34#include <the_Foundation/string.h> 34#include <the_Foundation/string.h>
35#include <the_Foundation/stringlist.h>
35#include <the_Foundation/toml.h> 36#include <the_Foundation/toml.h>
36 37
37const char *mimeType_FontPack = "application/lagrange-fontpack+zip"; 38const char *mimeType_FontPack = "application/lagrange-fontpack+zip";
@@ -236,6 +237,10 @@ iFontPackId id_FontPack(const iFontPack *d) {
236 return (iFontPackId){ &d->id, d->version }; 237 return (iFontPackId){ &d->id, d->version };
237} 238}
238 239
240const iString *loadPath_FontPack(const iFontPack *d) {
241 return d->loadPath;
242}
243
239const iPtrArray *listSpecs_FontPack(const iFontPack *d) { 244const iPtrArray *listSpecs_FontPack(const iFontPack *d) {
240 if (!d) return NULL; 245 if (!d) return NULL;
241 iPtrArray *list = collectNew_PtrArray(); 246 iPtrArray *list = collectNew_PtrArray();
@@ -441,6 +446,20 @@ void setLoadPath_FontPack(iFontPack *d, const iString *path) {
441 setRange_String(&d->id, withoutExtension_Path(&d->id)); 446 setRange_String(&d->id, withoutExtension_Path(&d->id));
442} 447}
443 448
449const iString *idFromUrl_FontPack(const iString *url) {
450 iString *id = new_String();
451 iUrl parts;
452 init_Url(&parts, url);
453 setRange_String(id, baseName_Path(collectNewRange_String(parts.path)));
454 setRange_String(id, withoutExtension_Path(id));
455 return collect_String(id);
456}
457
458void setUrl_FontPack(iFontPack *d, const iString *url) {
459 /* TODO: Should we remember the URL as well? */
460 set_String(&d->id, idFromUrl_FontPack(url));
461}
462
444void setStandalone_FontPack(iFontPack *d, iBool standalone) { 463void setStandalone_FontPack(iFontPack *d, iBool standalone) {
445 d->isStandalone = standalone; 464 d->isStandalone = standalone;
446} 465}
@@ -486,7 +505,7 @@ static void sortSpecs_Fonts_(iFonts *d) {
486 clear_PtrArray(&d->specOrder); 505 clear_PtrArray(&d->specOrder);
487 iConstForEach(PtrArray, p, &d->packs) { 506 iConstForEach(PtrArray, p, &d->packs) {
488 const iFontPack *pack = p.ptr; 507 const iFontPack *pack = p.ptr;
489 if (!contains_StringSet(prefs_App()->disabledFontPacks, &pack->id)) { 508 if (!isDisabled_FontPack(pack)) {
490 iConstForEach(Array, i, &pack->fonts) { 509 iConstForEach(Array, i, &pack->fonts) {
491 pushBack_PtrArray(&d->specOrder, i.value); 510 pushBack_PtrArray(&d->specOrder, i.value);
492 } 511 }
@@ -620,24 +639,131 @@ const iPtrArray *listSpecsByPriority_Fonts(void) {
620 return &fonts_.specOrder; 639 return &fonts_.specOrder;
621} 640}
622 641
642iString *infoText_FontPack(const iFontPack *d) {
643 const iFontPack *installed = pack_Fonts(cstr_String(&d->id));
644 const iBool isInstalled = (installed != NULL);
645 const int installedVersion = installed ? installed->version : 0;
646 const iBool isDisabled = isDisabled_FontPack(d);
647 iString *str = new_String();
648 size_t sizeInBytes = 0;
649 iPtrSet *uniqueFiles = new_PtrSet();
650 iStringList *names = new_StringList();
651 iConstForEach(PtrArray, i, listSpecs_FontPack(d)) {
652 const iFontSpec *spec = i.ptr;
653 pushBack_StringList(names, &spec->name);
654 iForIndices(j, spec->styles) {
655 insert_PtrSet(uniqueFiles, spec->styles[j]);
656 }
657 }
658 iConstForEach(PtrSet, j, uniqueFiles) {
659 sizeInBytes += size_Block(&((const iFontFile *) *j.value)->sourceData);
660 }
661 appendFormat_String(str, "%.1f ${mb} (%s)\n%s: %s\n",
662 sizeInBytes / 1.0e6,
663 formatCStrs_Lang("num.files.n", size_PtrSet(uniqueFiles)),
664 formatCStrs_Lang("num.fonts.n", size_StringList(names)),
665 cstrCollect_String(joinCStr_StringList(names, ", ")),
666 d->version);
667 if (isInstalled && installedVersion != d->version) {
668 appendCStr_String(str, format_Lang("${fontpack.meta.version}\n", d->version));
669 }
670 if (!isEmpty_String(&d->id)) {
671 appendFormat_String(str, "%s %s%s\n",
672 isInstalled ? ballotChecked_Icon : ballotUnchecked_Icon,
673 isInstalled ? (installedVersion == d->version ? "${fontpack.meta.installed}"
674 : format_CStr("${fontpack.meta.installed} (%s)",
675 format_Lang("${fontpack.meta.version}",
676 installedVersion)))
677 : "${fontpack.meta.notinstalled}",
678 isDisabled ? "${fontpack.meta.disabled}" : "");
679 }
680 iRelease(names);
681 delete_PtrSet(uniqueFiles);
682 return str;
683}
684
685const iArray *actions_FontPack(const iFontPack *d) {
686 iArray *items = new_Array(sizeof(iMenuItem));
687 const iFontPackId fp = id_FontPack(d);
688 const char *fpId = cstr_String(fp.id);
689 const iFontPack *installed = pack_Fonts(fpId);
690 const iBool isEnabled = !isDisabled_FontPack(d);
691 if (isInstalled_Fonts(fpId)) {
692 if (d->version > installed->version) {
693 pushBack_Array(items, &(iMenuItem){
694 format_Lang(add_Icon " ${fontpack.upgrade}", fpId, d->version),
695 SDLK_RETURN, 0, "fontpack.install"
696 });
697 }
698 pushBack_Array(items, &(iMenuItem){
699 format_Lang(isEnabled ? close_Icon " ${fontpack.disable}"
700 : leftArrowhead_Icon " ${fontpack.enable}", fpId), 0, 0,
701 format_CStr("fontpack.enable arg:%d id:%s", !isEnabled, fpId) });
702 if (!d->isReadOnly && installed->loadPath && d->loadPath &&
703 !cmpString_String(installed->loadPath, d->loadPath)) {
704 pushBack_Array(items, &(iMenuItem){
705 format_Lang(delete_Icon " ${fontpack.delete}", fpId), 0, 0,
706 format_CStr("fontpack.delete id:%s", fpId) });
707 }
708 }
709 else if (d->isStandalone) {
710 pushBack_Array(items, &(iMenuItem){
711 format_Lang(add_Icon " ${fontpack.install}", fpId),
712 SDLK_RETURN, 0, "fontpack.install"
713 });
714 }
715 return collect_Array(items);
716}
717
718iBool isDisabled_FontPack(const iFontPack *d) {
719 return contains_StringSet(prefs_App()->disabledFontPacks, &d->id);
720}
721
623const iString *infoPage_Fonts(void) { 722const iString *infoPage_Fonts(void) {
624 iFonts *d = &fonts_; 723 iFonts *d = &fonts_;
625 iString *str = collectNewCStr_String("# Fonts\n"); 724 iString *str = collectNewCStr_String("# ${heading.fontpack.meta}\n"
725 "=> gemini://skyjake.fi/fonts Download more fonts\n"
726 "=> about:command?!open%20newtab:1%20gotoheading:1%20url:about:help Using fonts in Lagrange\n"
727 "=> about:command?!open%20newtab:1%20gotoheading:1%20url:about:help How to create a fontpack\n");
626 iPtrArray *specsByPack = collectNew_PtrArray(); 728 iPtrArray *specsByPack = collectNew_PtrArray();
627 setCopy_PtrArray(specsByPack, &d->specOrder); 729 setCopy_PtrArray(specsByPack, &d->specOrder);
628 sort_Array(specsByPack, cmpSourceAndPriority_FontSpecPtr_); 730 sort_Array(specsByPack, cmpSourceAndPriority_FontSpecPtr_);
629 iString *currentSourcePath = collectNew_String(); 731 iString *currentSourcePath = collectNew_String();
630 iConstForEach(PtrArray, i, specsByPack) { 732 for (int group = 0; group < 2; group++) {
631 const iFontSpec *spec = i.ptr; 733 iBool isFirst = iTrue;
632 if (isEmpty_String(&spec->sourcePath)) { 734 iConstForEach(PtrArray, i, specsByPack) {
633 continue; /* built-in font */ 735 const iFontSpec *spec = i.ptr;
634 } 736 if (isEmpty_String(&spec->sourcePath)) {
635 if (!equal_String(&spec->sourcePath, currentSourcePath)) { 737 continue; /* built-in font */
636 appendFormat_String(str, "=> %s %s%s\n", 738 }
637 cstrCollect_String(makeFileUrl_String(&spec->sourcePath)), 739 if (!equal_String(&spec->sourcePath, currentSourcePath)) {
638 endsWithCase_String(&spec->sourcePath, ".fontpack") ? "\U0001f520 " : "", 740 set_String(currentSourcePath, &spec->sourcePath);
639 cstr_Rangecc(baseName_Path(&spec->sourcePath))); 741 /* Print some information about this page. */
640 set_String(currentSourcePath, &spec->sourcePath); 742 const iFontPack *pack = packByPath_Fonts(currentSourcePath);
743 if (pack) {
744 if (!isDisabled_FontPack(pack) ^ group) {
745 if (isFirst) {
746 appendCStr_String(str, "\n## ");
747 appendCStr_String(str, group == 0 ? "${heading.fontpack.meta.enabled}"
748 : "${heading.fontpack.meta.disabled}");
749 appendCStr_String(str, "\n\n");
750 isFirst = iFalse;
751 }
752 const iString *packId = id_FontPack(pack).id;
753 appendFormat_String(str, "### %s\n=> %s ${fontpack.meta.viewfile}\n",
754 isEmpty_String(packId) ? "fonts.ini" :
755 cstr_String(packId),
756 cstrCollect_String(makeFileUrl_String(&spec->sourcePath)));
757 append_String(str, collect_String(infoText_FontPack(pack)));
758 iConstForEach(Array, a, actions_FontPack(pack)) {
759 const iMenuItem *item = a.value;
760 appendFormat_String(str, "=> about:command?%s %s\n",
761 cstr_String(withSpacesEncoded_String(collectNewCStr_String(item->command))),
762 item->label);
763 }
764 }
765 }
766 }
641 } 767 }
642 } 768 }
643 return str; 769 return str;
@@ -645,6 +771,9 @@ const iString *infoPage_Fonts(void) {
645 771
646const iFontPack *pack_Fonts(const char *packId) { 772const iFontPack *pack_Fonts(const char *packId) {
647 iFonts *d = &fonts_; 773 iFonts *d = &fonts_;
774 if (!*packId) {
775 return NULL;
776 }
648 iConstForEach(PtrArray, i, &d->packs) { 777 iConstForEach(PtrArray, i, &d->packs) {
649 const iFontPack *pack = i.ptr; 778 const iFontPack *pack = i.ptr;
650 if (!cmp_String(&pack->id, packId)) { 779 if (!cmp_String(&pack->id, packId)) {
@@ -675,10 +804,15 @@ void reload_Fonts(void) {
675 delete_String(userDir); 804 delete_String(userDir);
676} 805}
677 806
678void install_Fonts(const iString *fontId, const iBlock *data) { 807void install_Fonts(const iString *packId, const iBlock *data) {
808 if (!detect_FontPack(data)) {
809 return;
810 }
811 /* Newly installed packs will never be disabled. */
812 remove_StringSet(prefs_App()->disabledFontPacks, packId);
679 iFonts *d = &fonts_; 813 iFonts *d = &fonts_;
680 iFile *f = new_File(collect_String(concatCStr_Path( 814 iFile *f = new_File(collect_String(concatCStr_Path(
681 userFontsDirectory_Fonts_(d), format_CStr("%s.fontpack", cstr_String(fontId))))); 815 userFontsDirectory_Fonts_(d), format_CStr("%s.fontpack", cstr_String(packId)))));
682 if (open_File(f, writeOnly_FileMode)) { 816 if (open_File(f, writeOnly_FileMode)) {
683 write_File(f, data); 817 write_File(f, data);
684 } 818 }
@@ -687,38 +821,5 @@ void install_Fonts(const iString *fontId, const iBlock *data) {
687 reload_Fonts(); 821 reload_Fonts();
688} 822}
689 823
690iBool preloadLocalFontpackForPreview_Fonts(iGmDocument *doc) {
691 iBool wasLoaded = iFalse;
692 for (size_t linkId = 1; ; linkId++) {
693 const iString *linkUrl = linkUrl_GmDocument(doc, linkId);
694 if (!linkUrl) {
695 break; /* ran out of links */
696 }
697 const int linkFlags = linkFlags_GmDocument(doc, linkId);
698 if (linkFlags & fontpackFileExtension_GmLinkFlag &&
699 scheme_GmLinkFlag(linkFlags) == file_GmLinkScheme) {
700 iMediaId linkMedia = findMediaForLink_Media(media_GmDocument(doc), linkId, fontpack_MediaType);
701 if (linkMedia.type) {
702 continue; /* got this one already */
703 }
704 iString *filePath = localFilePathFromUrl_String(linkUrl);
705 iFile *f = new_File(filePath);
706 if (open_File(f, readOnly_FileMode)) {
707 iBlock *fontPackArchiveData = readAll_File(f);
708 setUrl_Media(media_GmDocument(doc), linkId, fontpack_MediaType, linkUrl);
709 setData_Media(media_GmDocument(doc),
710 linkId,
711 collectNewCStr_String(mimeType_FontPack),
712 fontPackArchiveData,
713 0);
714 delete_Block(fontPackArchiveData);
715 wasLoaded = iTrue;
716 }
717 iRelease(f);
718 }
719 }
720 return wasLoaded;
721}
722
723iDefineClass(FontFile) 824iDefineClass(FontFile)
724 825