build close to render standalone app..

This commit is contained in:
educative 2021-11-17 01:19:46 +01:00
parent f5aa0a6b50
commit 926d42fbcc
23 changed files with 594 additions and 8 deletions

View File

@ -3,7 +3,9 @@ package cmd
import (
"fmt"
// "accorder/pkg/calibre"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var buildCmd = &cobra.Command{
@ -19,15 +21,23 @@ the configuration file for the future use (therefore: 'accorder build PROFILE'
should be enough for the next successful build).`,
Args: OnlyProfileArgument,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(args)
fromFlag := viper.GetString("directory")
fromConfig := viper.GetString(fmt.Sprintf("%s.directory", args[0]))
if fromFlag != "" && fromFlag != fromConfig {
fmt.Printf("Would you like to write the new directory path:\n%s\ninto the configuration file?\n", viper.GetString("directory"))
fromConfig = fromFlag
}
fmt.Println("DIRECTORY:", fromConfig)
// calibre.RenderStandaloneApp()
},
}
func init() {
rootCmd.AddCommand(buildCmd)
buildCmd.PersistentFlags().StringP("directory", "d", "", "A local Calibre directory path.")
buildCmd.PersistentFlags().StringP("librarian", "l", "", "Librarian's name.")
buildCmd.PersistentFlags().StringP("jsonpath", "j", "", "Path where to render all metadata into JSON.")
buildCmd.PersistentFlags().StringP("bibtex", "b", "", "Import books from BibTex file into Calibre.")
CustomHelpOutput(buildCmd)
viper.BindPFlag("directory", buildCmd.PersistentFlags().Lookup("directory"))
rootCmd.AddCommand(buildCmd)
}

View File

@ -19,5 +19,4 @@ func OnlyProfileArgument(cmd *cobra.Command, args []string) error {
return fmt.Errorf("You can only have one argument and that should be PROFILE.")
}
return nil
}

View File

@ -2,9 +2,10 @@ package cmd
import (
"fmt"
"github.com/spf13/cobra"
"os"
"github.com/spf13/cobra"
homedir "github.com/mitchellh/go-homedir"
"github.com/spf13/viper"
)
@ -35,26 +36,33 @@ The configuration file will keep information about one or more PROFILE.
Under every PROFILE's configuration section there will be information
about the directory path of local Calibre's library, librarian's name,
credentials needed to upload the files to the destination server etc.`,
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
if err := initConfig(args); err != nil {
fmt.Println("ERROR:", err)
os.Exit(1)
}
return nil
},
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
fmt.Println("BYE!", err)
os.Exit(1)
}
}
func init() {
cobra.OnInitialize(initConfig)
// cobra.OnInitialize(initConfig)
rootCmd.SetHelpTemplate(RootHelpTemplate)
rootCmd.SetUsageTemplate(RootUsageTemplate)
}
// initConfig reads in config file and ENV variables if set.
func initConfig() {
func initConfig(args []string) error {
if cfgFile != "" {
// Use config file from the flag.
viper.SetConfigFile(cfgFile)
@ -65,7 +73,6 @@ func initConfig() {
fmt.Println(err)
os.Exit(1)
}
// Search config in home directory with name ".accorder" (without extension).
viper.AddConfigPath(home)
viper.SetConfigName(".accorder")
@ -77,4 +84,15 @@ func initConfig() {
if err := viper.ReadInConfig(); err == nil {
fmt.Println("Using config file:", viper.ConfigFileUsed())
}
hasProfile := false
for k := range viper.AllSettings() {
if k == args[0] {
hasProfile = true
}
}
if !hasProfile {
return fmt.Errorf("PROFILE %s should be added to the configuration file.\n", args[0])
}
return nil
}

3
go.mod
View File

@ -3,8 +3,11 @@ module accorder
go 1.16
require (
github.com/cespare/xxhash v1.1.0 // indirect
github.com/karrick/godirwalk v1.16.1 // indirect
github.com/mitchellh/go-homedir v1.1.0
github.com/spf13/cobra v1.2.1
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.9.0
nullprogram.com/x/uuid v1.2.1 // indirect
)

7
go.sum
View File

@ -54,6 +54,7 @@ github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgI
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
@ -196,6 +197,8 @@ github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw=
github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
@ -685,6 +688,10 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
nullprogram.com/x/isaac64 v1.0.0 h1:60wAUcmF8LI7bqmIR84aCviOFYsZ16uzVVQwEv0KMo0=
nullprogram.com/x/isaac64 v1.0.0/go.mod h1:50jMhq3WThwRpM4z6xTHwp94BMqha3WbuKWb2vRxr78=
nullprogram.com/x/uuid v1.2.1 h1:dBg/lObcs+vykLutnXWbZp++/mRvyCnZcsq+BRfoJY4=
nullprogram.com/x/uuid v1.2.1/go.mod h1:NmzZbQYLdE9DEeN3zar52XwVIehpxzbXHxsbV/08T2I=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

382
pkg/calibre/calibre.go Normal file
View File

@ -0,0 +1,382 @@
package calibre
import (
"embed"
"encoding/json"
"encoding/xml"
"fmt"
"io"
"io/fs"
"log"
"math"
"os"
"path/filepath"
"sort"
"strings"
"sync"
"github.com/cespare/xxhash"
// "github.com/google/uuid"
"github.com/karrick/godirwalk"
"nullprogram.com/x/uuid"
)
var (
//go:embed embResources
embResources embed.FS
)
func c(err error) {
if err != nil {
log.Fatal(err)
}
}
type DataJs struct {
Portable bool `json:"portable"`
Total int `json:"total,omitempty"`
Books []*BookJSON `json:"books"`
}
// type BooksJSON struct {
// Books []*BookJSON `json:"books"`
// }
type IdentifierJSON struct {
Scheme string `json:"scheme"`
Code string `json:"code"`
}
type FormatJSON struct {
Format string `json:"format"`
DirPath string `json:"dir_path"`
FileName string `json:"file_name"`
Size int64 `json:"size"`
XXHash uint64 `json:"xxhash"`
}
type BookJSON struct {
Id string `json:"_id"`
Librarian string `json:"librarian"`
LibraryUUID string `json:"library_uuid"`
Title string `json:"title"`
TitleSort string `json:"title_sort,omitempty"`
Authors []string `json:"authors"`
Abstract string `json:"abstract"`
Tags []string `json:"tags"`
Publisher string `json:"publisher"`
Pubdate string `json:"pubdate"`
LastModified string `json:"last_modified"`
Languages []string `json:"languages"`
Identifiers []*IdentifierJSON `json:"identifiers"`
Formats []*FormatJSON `json:"formats"`
CoverUrl string `json:"cover_url"`
}
type BookOpf struct {
XMLName xml.Name `xml:"package"`
Version string `xml:"version,attr"`
Xmlns string `xml:"xmlns,attr"`
UniqueIdentifier string `xml:"unique-identifier,attr"`
Metadata struct {
XMLName xml.Name `xml:"metadata"`
DC string `xml:"dc,attr"`
OPF string `xml:"opf,attr"`
Identifiers []struct {
XMLName xml.Name `xml:"identifier"`
Scheme string `xml:"scheme,attr"`
Id string `xml:"id,attr"` // Calibre has two internal Ids: calibre_id and uuid_id
Value string `xml:",chardata"`
} `xml:"identifier"`
Title string `xml:"title"`
Creators []struct {
XMLName xml.Name `xml:"creator"`
Role string `xml:"role,attr"`
Name string `xml:",chardata"`
} `xml:"creator"`
Published string `xml:"date"`
Description string `xml:"description"`
Publisher string `xml:"publisher"`
Languages []struct {
XMLName xml.Name `xml:"language"`
Language string `xml:",chardata"`
} `xml:"language"`
Tags []struct {
XMLName xml.Name `xml:"subject"`
Tag string `xml:",chardata"`
} `xml:"subject"`
Meta []struct {
XMLName xml.Name `xml:"meta"`
Content string `xml:"content,attr"`
Name string `xml:"name,attr"`
} `xml:"meta"`
}
}
// TitleSort returns if Calibre processed Title for sorting order
func (book BookOpf) TitleSort() string {
for _, meta := range book.Metadata.Meta {
if meta.Name == "calibre:title_sort" && meta.Content != book.Metadata.Title {
return meta.Content
}
}
return ""
}
// LastModified returns when Calibre last time touched the file
func (book BookOpf) LastModified() string {
lastModified := ""
for _, meta := range book.Metadata.Meta {
if meta.Name == "calibre:timestamp" {
lastModified = meta.Content
}
}
return lastModified
}
// Authors parses creators and give back authors
func (book BookOpf) Authors() []string {
authors := []string{}
if len(book.Metadata.Creators) > 0 {
for _, author := range book.Metadata.Creators {
if author.Role == "aut" {
authors = append(authors, author.Name)
}
}
}
return authors
}
// Tags gets a list of tags from Subject nodes in OPF
func (book BookOpf) Tags() []string {
tags := []string{}
for _, tag := range book.Metadata.Tags {
tags = append(tags, tag.Tag)
}
return tags
}
// Languages gets a list of languages
func (book BookOpf) Languages() []string {
languages := []string{}
for _, l := range book.Metadata.Languages {
languages = append(languages, l.Language)
}
return languages
}
// Identifiers returns list of identifiers like ISBN, DOI, Google, Amazon...
func (book BookOpf) Identifiers() []*IdentifierJSON {
identifiers := []*IdentifierJSON{}
for _, i := range book.Metadata.Identifiers {
if i.Id == "" {
identifiers = append(identifiers, &IdentifierJSON{
Scheme: strings.ToLower(i.Scheme),
Code: i.Value,
})
}
}
return identifiers
}
// Formats return list of files and info on them
func Formats(path string) []*FormatJSON {
formats := []*FormatJSON{}
files, err := os.ReadDir(path)
c(err)
for _, f := range files {
if f.Name() != "metadata.opf" && f.Name() != "cover.jpg" {
fi, err := f.Info()
c(err)
x, err := os.Open(filepath.Join(path, f.Name()))
c(err)
defer x.Close()
xxHash := xxhash.New()
_, err = io.Copy(xxHash, x)
c(err)
formats = append(formats, &FormatJSON{
Format: strings.ReplaceAll(filepath.Ext(f.Name()), ".", ""),
DirPath: path,
FileName: f.Name(),
Size: fi.Size(),
XXHash: xxHash.Sum64(),
})
}
}
return formats
}
func lsEmbResources() {
_ = fs.WalkDir(embResources, ".", func(path string, d fs.DirEntry, err error) error {
c(err)
// fmt.Println(" ", path)
_ = path
return nil
})
}
func copyEmbResources(destPath string) {
_ = fs.WalkDir(embResources, ".", func(fsPath string, fsEntry fs.DirEntry, err error) error {
destFsPath := strings.ReplaceAll(fsPath, "embResources/", "")
c(err)
if fsPath == "." {
return nil
} else if fsEntry.IsDir() {
err := os.MkdirAll(filepath.Join(destPath, destFsPath), 0755)
c(err)
} else {
newFile, err := os.Create(destFsPath)
c(err)
defer newFile.Close()
embFile, err := embResources.Open(fsPath)
c(err)
_, err = io.Copy(newFile, embFile)
c(err)
}
return nil
})
}
func writeDataJs(calibrePath string, books []*BookJSON) {
// j, _ := json.MarshalIndent(&BooksJSON{books})
dataJsFirst, err := os.Create(filepath.Join(calibrePath, "static", "data1.js"))
c(err)
defer dataJsFirst.Close()
j1, _ := json.Marshal(&DataJs{
Portable: true,
Total: len(books),
Books: books[0:24],
})
calibreBooks1 := []byte("CALIBRE_BOOKS1=")
_, _ = dataJsFirst.Write(append(calibreBooks1, j1...))
// [24:120], [120:216], [216:3288], [3288:6360], [6360:9432], [9432:12504]
block := 24
counter := 2
for i := range [2]int{} {
ii := make([]int, int(math.Pow(2, float64(i+1))))
for j := range ii {
_ = j
var jsn []byte
endBlock := block + int(math.Pow(32, float64(i+1)))*3
if endBlock > len(books) {
endBlock = len(books)
}
dataJs, err := os.Create(filepath.Join(calibrePath, "static", fmt.Sprintf("data%d.js", counter)))
c(err)
defer dataJs.Close()
if endBlock <= len(books) {
jsn, _ = json.Marshal(&DataJs{
Portable: true,
Books: books[block:endBlock],
})
} else {
jsn, _ = json.Marshal(&DataJs{
Portable: true,
Books: []*BookJSON{},
})
}
calibreBooks := []byte(fmt.Sprintf("CALIBRE_BOOKS%d=", counter))
_, _ = dataJs.Write(append(calibreBooks, jsn...))
block = endBlock
counter++
}
}
}
func PrintExePath() {
fmt.Println(os.Executable())
}
func RenderStandaloneApp(calibrePath string) {
// exePath, err := os.Executable()
// c(err)
// calibrePath := fmt.Sprintf("%s/", filepath.Dir(exePath))
// calibrePath := "/home/m/CalibreLibraries/MarcellMarsBooks/"
// calibrePath := "/media/m/My Book/motw/motw_calibre_libraries/MarcellMarsBooks/"
fmt.Println(calibrePath)
var outputs sync.Map
var wg sync.WaitGroup
count := 0
err := godirwalk.Walk(calibrePath, &godirwalk.Options{
Callback: func(path string, info *godirwalk.Dirent) error {
if info.Name() == "metadata.opf" {
bookOpf := &BookOpf{}
count++
wg.Add(1)
go func(bookOpf *BookOpf) {
// randSuffix := uuid.New()
randSuffix := uuid.NewGen()
relativeDirPath := strings.NewReplacer(calibrePath, "", "metadata.opf", "").Replace(path)
f, _ := os.ReadFile(path)
_ = xml.Unmarshal([]byte(f), &bookOpf)
book := &BookJSON{
// Id: randSuffix.String(),
Id: randSuffix.NewV4().String(),
Title: bookOpf.Metadata.Title,
Librarian: "Aaron Elbakyan",
// LibraryUUID: randSuffix.String(),
LibraryUUID: randSuffix.NewV4().String(),
TitleSort: bookOpf.TitleSort(),
Authors: bookOpf.Authors(),
Pubdate: bookOpf.Metadata.Published,
LastModified: bookOpf.LastModified(),
Tags: bookOpf.Tags(),
Publisher: bookOpf.Metadata.Publisher,
Abstract: bookOpf.Metadata.Description,
Languages: bookOpf.Languages(),
Identifiers: bookOpf.Identifiers(),
Formats: Formats(filepath.Join(calibrePath, relativeDirPath)),
CoverUrl: filepath.Join(relativeDirPath, "cover.jpg"),
}
// outputs.Store(fmt.Sprintf("%s_%s", book.LastModified, randSuffix.String()), book)
outputs.Store(fmt.Sprintf("%s_%s", book.LastModified, randSuffix.NewV4().String()), book)
wg.Done()
}(bookOpf)
}
if count > 100 {
wg.Wait()
count = 0
}
return nil
},
ErrorCallback: func(path string, err error) godirwalk.ErrorAction {
fmt.Fprintf(os.Stderr, "ERROR: %s\n", err)
return godirwalk.SkipNode
},
Unsorted: true})
wg.Wait()
c(err)
var keysByDate []string
outputs.Range(func(k, v interface{}) bool {
keysByDate = append(keysByDate, k.(string))
return true
})
sort.Sort(sort.Reverse(sort.StringSlice(keysByDate)))
var books []*BookJSON
for _, keyByDate := range keysByDate {
b, _ := outputs.Load(keyByDate)
books = append(books, b.(*BookJSON))
}
// j, _ := json.MarshalIndent(&BooksJSON{books}, "", " ")
// fmt.Println(string(j))
// _ = j
lsEmbResources()
copyEmbResources(calibrePath)
writeDataJs(calibrePath, books)
}

View File

@ -0,0 +1,24 @@
<!doctype html>
<html>
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width'>
<title>Memory of the World Library</title>
<link rel="apple-touch-icon" sizes="180x180" href="static/favicons/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="static/favicons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="static/favicons/favicon-16x16.png">
<link rel="mask-icon" href="static/favicons/safari-pinned-tab.svg" color="#5bbad5">
<meta name="msapplication-TileColor" content="#da532c">
<meta name="theme-color" content="#ffffff">
<link rel='stylesheet' href='static/css/bundle.css'>
<script rel="prefetch" src="static/data1.js"></script>
<script defer src='static/js/bundle.js'></script>
</head>
<body class="pl-2 pr-2 monocle:p-0 phone:p-0">
</body>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
CALIBRE_BOOKS1={"portable":false}

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 978 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

@ -0,0 +1,106 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="1374.000000pt" height="1374.000000pt" viewBox="0 0 1374.000000 1374.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.11, written by Peter Selinger 2001-2013
</metadata>
<g transform="translate(0.000000,1374.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M6869 11514 c-24 -18 -49 -33 -56 -33 -6 -1 -13 -4 -15 -8 -1 -5 -48
-37 -103 -73 -55 -36 -102 -68 -105 -71 -3 -3 -9 -8 -15 -10 -20 -8 -280 -184
-283 -191 -2 -5 -8 -8 -13 -8 -6 0 -15 -5 -22 -10 -14 -12 -155 -105 -203
-135 -19 -11 -42 -28 -53 -37 -11 -9 -22 -14 -25 -11 -3 3 -8 -2 -12 -11 -3
-9 -10 -16 -16 -16 -5 0 -35 -17 -66 -39 -31 -21 -61 -39 -66 -39 -5 0 -12 -4
-15 -9 -10 -14 -79 -63 -89 -63 -5 0 -17 -9 -27 -20 -10 -11 -22 -20 -26 -20
-10 0 -133 -83 -137 -92 -2 -5 -7 -8 -12 -8 -10 0 -134 -82 -138 -92 -2 -4 -8
-8 -13 -8 -11 0 -133 -81 -137 -92 -2 -5 -7 -8 -12 -8 -8 0 -374 -239 -397
-259 -7 -6 -16 -11 -20 -11 -5 0 -20 -11 -35 -25 -15 -14 -31 -25 -36 -25 -4
0 -17 -7 -27 -15 -11 -9 -101 -69 -200 -135 -99 -65 -184 -122 -190 -127 -5
-4 -22 -14 -37 -22 -16 -8 -28 -18 -28 -23 0 -4 -5 -8 -10 -8 -6 0 -33 -16
-60 -35 -27 -19 -54 -35 -59 -35 -5 0 -11 -3 -13 -7 -3 -8 -174 -124 -193
-132 -5 -2 -33 -21 -62 -43 -29 -21 -55 -38 -58 -38 -3 0 -27 -16 -54 -35 -26
-19 -53 -35 -59 -35 -6 0 -12 -4 -14 -8 -4 -11 -155 -112 -167 -112 -5 0 -11
-3 -13 -7 -5 -12 -95 -73 -107 -73 -6 0 -11 -4 -11 -8 0 -5 -12 -15 -27 -23
-16 -8 -30 -16 -33 -20 -6 -6 -389 -261 -405 -269 -5 -3 -51 -33 -102 -67 -51
-35 -97 -63 -103 -63 -5 0 -10 -4 -10 -8 0 -4 -22 -21 -50 -37 -27 -17 -50
-33 -50 -37 0 -5 -7 -8 -15 -8 -8 0 -15 -4 -15 -10 0 -5 -4 -10 -10 -10 -5 0
-32 -16 -59 -35 -26 -19 -53 -35 -59 -35 -6 0 -12 -3 -14 -8 -1 -4 -50 -38
-108 -77 -58 -38 -108 -74 -112 -80 -5 -6 -8 -6 -8 0 0 6 -6 3 -13 -6 -7 -9
-22 -19 -35 -23 -12 -4 -22 -11 -22 -15 0 -5 -13 -14 -30 -21 -16 -7 -30 -16
-30 -20 0 -4 -16 -15 -35 -24 -19 -9 -35 -21 -35 -27 0 -6 -5 -7 -10 -4 -6 3
-10 1 -10 -4 0 -6 -4 -11 -9 -11 -6 0 -31 -15 -56 -32 -26 -18 -54 -38 -63
-43 -37 -22 -77 -50 -82 -56 -3 -3 -16 -12 -30 -19 -14 -7 -42 -26 -62 -42
-21 -16 -38 -27 -38 -23 0 3 -7 0 -15 -7 -33 -30 -45 -38 -54 -38 -5 0 -16 -6
-23 -13 -17 -17 -488 -330 -510 -339 -9 -4 -37 -26 -61 -50 -74 -74 -100 -190
-98 -438 1 -118 4 -169 16 -285 4 -27 8 -69 10 -93 3 -24 7 -56 11 -70 5 -27
11 -64 18 -112 2 -14 7 -41 11 -60 4 -19 9 -45 11 -57 1 -11 5 -30 8 -41 4
-11 8 -29 10 -39 10 -48 39 -164 56 -223 11 -36 22 -76 24 -90 3 -14 5 -25 6
-25 1 0 3 -7 5 -15 4 -17 62 -187 72 -210 3 -8 7 -17 7 -20 1 -3 7 -15 14 -27
7 -13 10 -23 7 -23 -3 0 6 -20 19 -45 14 -25 23 -45 20 -45 -7 0 64 -136 98
-187 15 -23 28 -45 28 -49 0 -4 7 -14 15 -23 8 -9 29 -32 47 -52 18 -20 51
-49 73 -65 22 -16 49 -37 59 -46 11 -10 26 -18 33 -18 7 0 13 -4 13 -10 0 -5
7 -10 15 -10 8 0 15 -4 15 -9 0 -5 20 -21 44 -35 25 -14 51 -32 58 -39 7 -7
47 -36 88 -65 41 -28 98 -67 125 -87 28 -20 68 -48 90 -63 22 -15 41 -30 43
-34 2 -5 10 -8 18 -8 8 0 14 -3 14 -8 0 -4 18 -18 40 -32 22 -14 40 -28 40
-32 0 -5 6 -8 13 -8 8 0 22 -9 32 -20 10 -11 22 -20 27 -20 5 0 14 -5 21 -10
7 -6 48 -36 92 -67 44 -32 82 -60 85 -63 3 -3 12 -9 21 -15 10 -5 37 -24 60
-41 24 -16 76 -52 114 -79 39 -27 76 -55 83 -61 6 -7 21 -16 32 -19 11 -3 20
-10 20 -14 0 -4 18 -17 40 -30 22 -12 40 -26 40 -32 0 -5 5 -9 11 -9 6 0 28
-14 49 -30 21 -17 42 -30 48 -30 6 0 12 -3 14 -7 4 -9 174 -133 182 -133 3 0
14 -7 24 -16 9 -8 60 -45 112 -82 52 -37 97 -70 98 -74 2 -5 9 -8 15 -8 7 0
24 -11 39 -25 15 -14 31 -25 35 -25 4 0 19 -9 33 -20 63 -51 120 -90 129 -90
5 0 11 -3 13 -7 5 -11 147 -113 157 -113 5 0 11 -4 13 -8 1 -5 26 -23 53 -42
28 -18 52 -36 55 -39 12 -14 70 -51 80 -51 5 0 10 -3 10 -8 0 -4 18 -18 40
-32 22 -14 40 -28 40 -32 0 -5 7 -8 15 -8 9 0 18 -7 21 -15 4 -8 10 -15 15
-15 8 0 113 -71 144 -98 6 -5 24 -17 40 -27 17 -11 56 -39 87 -62 31 -24 59
-43 62 -43 3 0 14 -6 23 -14 39 -31 113 -86 118 -86 4 0 132 -90 180 -127 6
-4 33 -24 60 -43 28 -19 52 -38 53 -42 2 -4 8 -8 13 -8 9 0 67 -38 79 -51 3
-3 28 -21 55 -39 28 -19 52 -37 53 -42 2 -4 10 -8 18 -8 8 0 14 -4 14 -10 0
-5 6 -10 14 -10 8 0 16 -4 18 -8 2 -5 26 -23 53 -42 28 -18 52 -36 55 -39 12
-14 70 -51 80 -51 5 0 10 -3 10 -7 0 -5 25 -24 55 -43 30 -19 54 -38 55 -42 0
-5 5 -8 11 -8 11 0 92 -60 97 -72 2 -5 10 -8 18 -8 8 0 14 -4 14 -10 0 -5 7
-10 15 -10 8 0 15 -4 15 -10 0 -5 7 -10 15 -10 8 0 15 -4 15 -10 0 -5 6 -10
14 -10 8 0 16 -3 18 -8 4 -9 202 -150 223 -158 15 -6 52 13 84 44 9 9 31 28
49 42 18 14 45 36 61 50 57 52 91 80 96 80 3 0 16 10 28 23 12 12 47 43 77 67
67 55 95 79 124 105 12 11 41 35 64 53 22 19 50 41 60 50 11 10 34 28 52 42
17 13 47 38 65 55 18 16 50 44 70 60 21 17 47 39 59 50 12 11 45 38 72 60 27
22 65 54 84 72 19 18 41 33 48 33 6 0 12 7 12 15 0 8 4 15 8 15 5 0 18 8 30
17 41 35 108 91 187 158 26 22 58 50 71 63 13 12 25 22 28 22 6 0 44 33 91 78
13 12 27 22 30 22 3 0 13 8 23 17 10 10 40 36 67 58 26 22 59 49 71 60 12 11
39 34 60 50 20 17 44 37 53 46 9 8 39 33 66 55 28 21 52 42 55 45 3 3 34 30
70 60 36 29 70 58 76 64 6 5 30 26 54 45 25 19 62 51 84 70 23 19 45 38 51 43
5 4 21 17 35 30 14 12 45 38 69 57 24 19 56 46 71 60 15 14 43 37 62 52 19 15
45 37 59 50 14 13 45 39 69 58 24 19 61 51 83 70 22 19 62 52 89 72 28 21 46
38 42 38 -5 1 1 6 12 13 21 12 69 52 162 134 26 24 53 43 60 43 6 0 12 7 12
15 0 8 4 15 10 15 10 0 27 14 98 78 21 18 58 49 85 69 26 21 47 41 47 45 0 4
5 8 10 8 6 0 18 8 28 17 31 30 85 77 117 101 17 13 50 41 75 62 40 35 154 131
219 185 13 11 58 49 101 85 84 71 84 70 215 179 50 41 97 81 106 90 9 9 25 23
36 31 26 21 137 113 143 120 5 5 118 99 135 113 6 4 21 17 35 30 14 12 45 38
69 57 24 19 48 40 55 45 6 6 38 33 71 60 33 28 67 58 76 67 9 10 19 18 23 18
4 0 21 13 39 29 34 32 160 139 242 206 70 57 104 87 131 114 13 13 24 21 24
18 0 -4 15 8 33 26 19 18 49 43 67 57 18 14 43 34 55 45 12 11 41 36 66 55 24
19 53 44 64 55 11 11 36 32 55 47 19 15 47 37 61 50 15 13 46 39 69 58 132
108 133 110 128 165 -5 64 -34 105 -96 136 -26 13 -49 24 -52 24 -3 0 -12 5
-20 10 -8 5 -60 32 -115 60 -102 52 -117 60 -170 88 -29 15 -51 57 -61 117 -4
29 -8 44 -19 85 -5 16 -9 39 -10 50 -1 11 -5 36 -8 55 -4 19 -10 49 -13 65 -7
41 -11 68 -18 150 -4 39 -9 90 -12 115 -6 50 -6 368 0 415 2 17 7 62 11 100 8
80 9 87 20 138 4 20 10 37 14 37 4 0 20 12 38 28 40 35 59 51 108 87 22 16 47
36 57 45 9 8 31 26 49 40 18 14 58 45 89 70 31 25 69 54 85 65 69 49 85 72 85
120 0 65 -34 115 -102 148 -29 14 -93 44 -143 67 -49 23 -112 52 -140 65 -54
25 -137 64 -308 144 -59 28 -109 51 -112 51 -2 0 -50 22 -105 49 -55 28 -103
50 -105 51 -5 1 -84 37 -230 105 -171 80 -233 109 -295 138 -36 17 -67 31 -70
32 -44 15 -70 29 -70 38 0 6 -3 8 -6 4 -4 -3 -27 5 -53 18 -76 39 -146 69
-153 67 -5 -1 -8 3 -8 10 0 6 -3 9 -6 6 -3 -4 -31 6 -62 22 -70 34 -202 97
-302 142 -41 19 -102 48 -135 64 -33 16 -62 29 -65 29 -3 0 -40 18 -83 38
-106 51 -191 91 -197 92 -3 1 -43 19 -90 42 -47 22 -119 57 -161 76 -115 53
-173 80 -291 136 -59 28 -109 51 -111 51 -3 0 -65 29 -138 63 -155 74 -171 81
-181 79 -5 -1 -8 3 -8 8 0 6 -4 10 -9 10 -5 0 -51 20 -102 44 -157 73 -209 96
-219 96 -6 0 -10 5 -10 10 0 6 -5 10 -12 10 -6 0 -49 18 -94 40 -46 22 -88 40
-93 40 -6 0 -11 5 -11 12 0 6 -3 9 -6 6 -3 -4 -41 11 -85 33 -43 21 -81 39
-83 39 -3 0 -49 20 -103 45 -132 62 -147 69 -153 76 -9 10 -94 30 -121 27 -14
-1 -45 -17 -70 -34z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.2 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long