-- Aplikace SCANCSV.LUA slouží ke zpracování Excelovských tabulek ve formátu CSV (kódovaných UTF-8) (Lua)ConTeXtem. -- Použití: tisk mailmerge, vysvědčení, pozvánky, vizitky, atd. -- Základní myšlenka aplikace vychází z plainovského makra "scanbase.tex" pana P. Olšáka (ftp://math.feld.cvut.cz/pub/olsak/makra/scanbase.tex). -- Aplikace obsahuje několik Lua funkcí pro práci s CSV soubory a po načtení této "knihovny" jsou ConTeXtu zpřístupněna data v CSV tabulkách -- Data v jednotlivých řádcích CSV tabulky jsou prostřednictvím cyklu v Lua předávány ConTeXtu, kde mohou být dle libosti zpracovávána. -- Uživatel si může aplikaci do značné míry přizpůsobit a nastavit prostřednictvím několika globálních proměnných celou řadu vlastností. -- Lua vytvoří automaticky (+ navíc ze záhlaví CSV souboru) definice TeXovských maker, do nichž jsou následně uložena data z jednotlivých řádků CSV tabulky. -- TeXová makra vznikají v průběhu cyklu zcela automaticky bez jakéhokoliv přispění uživatele a uživatel je dokonce nemusí ani v ConTeXtu definovat, -- čímž nastává do jisté míry paradoxní situace, neboť se ve zdrojovém ConTeXtovém textu objevují makra např. \cA, \cB, ..., \Prijmeni, \Jmeno, \DatumNarozeni, atd., která nejsou nikde ve zdrojáku definovaná). -- Tento soubor scancsv.lua je plný komentářů a z nichž snad zájemce vyčte princip fungování. V závěru najdete i stručný "návod k použití". -- Na úložišti je ke stažení přiložen i ConTeXtový soubor scancsv.tex - jakýsi minimální ukázkový příklad v ConTeXtu. -- Pro ty, kteří nepočítají s tím, že by to využívali, ale chtěli by pouze vědět jak nakonec výsledek vypadá a jak to vlastně funguje jej zde uvádím: -- Použité globální proměnné (uživatel si je může předefinovat): -- -- Zde si může uživatel nastavit defaultní hodnoty, které v rámci své práce nejčastěji používá UserCSVSeparator=';' -- předdefinování uživatelem nejpoužívanějšího separátoru polí v CSV tabulkách UserCSVLeftDelimiter='' -- předdefinování uživatelem nejpoužívanějších vymezovačů polí v CSV tabulkách (samostatně levý) UserCSVRightDelimiter='' -- (a samostatně pravý) UserCSVHeader=false -- Načtený CSV soubor je defaultně posuzován jako CSV soubor bez hlavičky (údaje v hlavičce jsou brány jako názvy sloupcových maker) UserCountOfHor=3 -- pokud není v ConTeXtu nastavena hodnota \countofhor, nastaví se tato hodnota - tj. počet kartiček na šířku UserCountOfVert=5 -- pokud není v ConTeXtu nastavena hodnota \countofvert, nastaví se tato hodnota - tj. počet kartiček na výšku -- Ukázka defaultního záznamu: položka1;položka2;položka3;položka4 atd. -- často jsou používány záznamy v podobě: "položka1";"položka2";"položka3";"položka4" nebo "položka1","položka2","položka3","položka4" -- vygenerovaná TeXová makra odkazující na hodnoty ve sloupcích jsou číslována a`la EXCEL -- tj. cA, cB, ..., cAA, atd.. nebo a`la římské číslice tj. cI, cII, cIII, cIV, ..., cXVIII, atd.. UserColumnNumbering='XLS' -- Něco jiného než XLS nebo nedefinovaná hodnota proměnné (tj. např. zakomentováním tohoto řádku) se nastaví římské číslování ... -- Defaultně tedy budou názvy sloupců: cA, cB, cC, ..., cAA, atd. tj. c + doplněno o písmeno či dvojici písmen ala označení sloupce ala Excel -- File2Scan - používaný CSV soubor. Proměnná se může nastavit manuálně, nebo se nastaví při volání funkce, jejímž parametrem je název souboru. Její nastavení umožňuje volat funkce OpenCSVFile(), CSVReport(), lineaction() a filelineaction() bez parametrů -- Sep - Oddělovač sloupců v CSV souboru. Defaultní hodnota separátoru je v proměnné UserCSVSeparator. Některé znaky nelze použít jako separátor -- Ld - Sloupcové položky mohou být ohraničeny jinak zleva a jinak zprava. Toto je Ld - left delimiter. Defaultní hodnota je v proměnné UserCSVLeftDelimiter. Některé znaky nelze pro ohraničení použít. -- Rd - a toto je Rd - right delimiter. Defaultní hodnota je v proměnné UserCSVRightDelimiter. Nejčastěji však bývají jednotlivé položky ohraničeny jen jedním - stejným znakem " (tj. vloženy do uvozovek) -- CSVFile - pro interní potřebu -- CSV - pro interní potřebu -- CSVHeader - když je TRUE, znamená to, že 1. řádek OBSAHUJE hlavičku se jmény sloupců tj. první řádek není DATOVÝ. Pokud je false, předpokládá se že všechny řádky jsou datové. Defaultní hotnota je FALSE if CSVHeader == nil then CSVHeader = UserCSVHeader end -- Default hodnota uložena v glob. proměnné UserCSVHeader (default FALSE) if not context then -- Když není spuštěno v režimu ConTeXtu, pak je nutno ještě načíst knihovnu l-string require "l-string" end function ParseCSVdata(string2parse, separator, leftdelimiter, rightdelimiter) -- Funkce pro "rozparsování" jednotlivých záznamů (řádků) CSV tabulky -- Vstupní textový řetězec (načtený řádek) je v místech výskytu separátoru "rozsekán" na jednotlivá pole -- Jediný povinný vstupní parametr je 'string2parse'. Při nenastavení zbývajících tří parametrů se jako separátor a oddělovače použijí buď globální proměnné Sep, Ld a Rd nebo se použijí defaultní hodnoty -- Výstupem je pole oddělených řetězců -- Když nejsou globální proměnné Sep, Ld a Rd nastaveny uživatelem, nastaví se na defaultní hodnoty. Uživatel si může defaultní hodnoty libovolně změnit podle toho, jaké hodnoty používá ve svých aplikacích nejčastěji Sep = (Sep == nil) and UserCSVSeparator or Sep -- Pokud není glob. neznámá Sep nastavena, použije se defaultní hodnota ';' (středník). Ld = (Ld == nil) and UserCSVLeftDelimiter or Ld -- Pokud není glob. neznámá Ld (left delimiter) nastavena, použije se defaultní hodnota '' (prázdný řetězec) Rd = (Rd == nil) and UserCSVRightDelimiter or Rd -- Pokud není glob. neznámá Rd (right delimiter) nastavena, použije se defaultní hodnota '' (prázdný řetězec) -- Nastavení hodnot parametrů, které nejsou při volání funkce nastaveny tj. při volání funkce ParseCSVdata(string2parse) s pouze jedním parametrem local separator = (separator == nil) and Sep or separator -- při nenastavení separátoru se použije local leftdelimiter = (leftdelimiter == nil) and Ld or leftdelimiter local rightdelimiter = (rightdelimiter == nil) and Rd or rightdelimiter -- A jdeme na vlastní zpracování řetězce ... local result={} if leftdelimiter ~= '' and rightdelimiter ~= '' then -- Když jsou položky v řádku vymezeny levým i pravým delimiterem (tj. jakousi závorkou) např. "pole1";"pole2" nebo {pole1};{pole2} .. atd. string.gsub(string2parse, leftdelimiter.."(.-)"..rightdelimiter, function(a) table.insert(result,a) end ) -- Je vidět, že v tomto případě ani nezáleží na separátoru, jednotlivé uzávorkované řetězce se od sebe oddělí else -- Když je pouze separátor polí bez vymezujících znaků - delimiterů (tento případ bohužel neumožňuje použití separátoru jako samostatného znaku v nějakém poli ) result=string.split(string2parse,separator) -- pak stačí vstupní řetezec jen "vysplitovat" tj. rozsekat na samostatné části end return result -- Funkce vrátí pole result obsahující oddělená jednotlivá pole řádkového CSV záznamu end function OpenCSVFile(file) -- Otevře CSV tabulku, inicializuje proměnné local inpCSVfile=csvfilename(file) -- CSVFile, CSV ..... globální proměnné -- Na základě hodnoty globální proměnné UserColumnNumbering se po otevření souboru vytvoří seznam maker: -- buď \cA, \cB, \cC, ..., \cAA, \cAB, ... tj. ala Excel (UserColumnNumbering='XLS' - defaultní nastavení) -- nebo \cI, \cII, \cIII atd. tj. římskými číslicemi (UserColumnNumbering=něco jiného než 'XLS') -- Pomocí těchto TeXových maker lze přistupovat v ConTeXtu k řádkovým položkám. -- Následně se nabízí možnost definice vlastních maker pomocí konstrukce \let\mojemakro\cA atd. -- Navíc se po zavolání této funkce načte 1. řádek CSV souboru (tj. záhlaví CSV tabulky) a -- odtud se vezmou názvy polí, pomocí kterých se budeme dostávat v ConTeXtu k jednotlivým položkám CSV tabulky -- Př: Pokud je záhlaví CSV tabulky: Jmeno;Prijmeni;DatNar; ... atd. -- dostaneme se k údajům CSV tabulky v Lua pomoci CSV.Jmeno, CSV.Prijmeni, CSV DatNar atd. -- a v ConTeXtu pomocí \Jmeno, \Prijmeni, \DatNar atd. -- Pokud jsou v 1. řádku znaky, které nemohou být součástí názvů TeXových maker, tak se automaticky -- názvy maker upraví na korektní hodnoty (odstraní se diakritika, číslice atd...) tex.sprint([[\def\csvfilename{]]..tostring(inpCSVfile)..[[}]]) -- definice makra \csvfilename CSVFile=io.open(inpCSVfile,"r") local CSVLine=CSVFile:read() -- Načtení 1. řádku (je-li CSVHeader=TRUE, tak není 1. řádek datový numline=0 -- tato globální proměnná obsahuje číslo aktuálně čteného řádku tex.sprint([[\def\numline{]]..numline..[[}]]) -- vytvoř definici makra \numline. Toto makro obsahuje číslo aktuálně načteného řádku pro použití v sestavách ConTeXtu CSV=ParseCSVdata(CSVLine) -- rozparsovaním se do tabulky CSV se načte pole názvů sloupců tex.sprint([[\def\numcols{]]..#CSV..[[}]]) -- definice makra \numcols - počet sloupců CSV tabulky -- z těchto dat se vytvoří asociativní pole v němž "indexy" jsou názvy sloupců -- Po načtení 1. řádku CSV tabulky se pro každé pole definuje TeXové makro s názvem stejným jako název pole. -- Obsah makra se inicializuje stejnou hodnotou (kvůli otestování neprázdné hodnoty před zahájením cyklu). -- Např. pro záhlaví CSV souboru: -- Prijmeni;Jmeno;DatumNarozeni;MistoNarozeni;Pohlavi;Mesto;PSC;Ulice -- se vytvoří inicializační makra \def\Prijmeni{Prijmeni}, \def\Jmeno{Jmeno}, atd. -- je to kvůli tomu, aby byly názvy maker použitelné při vytváření bufferů, dalších maker atd. for i = 1, #CSV do -- pro všechna pole v záhlaví tex.sprint([[\def\c]]..(ar2colnum(i))..[[{]]..CSV[i]..[[}]]) -- vytvoř automatickou definici TeXovských maker \cI, \cII, atd. a zadej do nich příslušný obsah. tex.sprint([[\def\]]..TMN(CSV[i])..[[{\c]]..(ar2colnum(i))..[[}]]) -- vytvoř definice TeXovských maker pomocí údajů v záhlaví (při čtení údajů v záhlaví se ošetří název makra). Makro je uzavřeno do skupiny \bcng ... \ecng -- Pokud bychom chtěli využít následující definice (tj. sloupcové položky jsou "oháčkovány"), tak nepůjde snadno např. testovat hodnoty ve sloupcových makrech, proto to není raději použito -- navíc se to zřejmě nedá nějak zásadně prakticky využít (např. všechny položky by byly vypsány kurzívou atd.) -- tex.sprint([[\def\]]..TMN(CSV[i])..[[{\bch{\c]]..(ar2colnum(i))..[[}\ech}]]) -- Makro je uzavřeno do skupiny \bch ... \ech end -- for i=1, #CSV if CSVHeader then -- 1. řádek obsahuje hlavičku, takže začít číst až od 2. řádku CSVFile=io.open(inpCSVfile,"r") CSVLine=CSVFile:read() -- Načtení 1. řádku (obsahuje záhlaví se jmény sloupců - polí) FirstRowIsLoaded=true else CSVFile=io.open(inpCSVfile,"r") FirstRowIsLoaded=false end CSVField={} -- Experimental ... vytvoření pole pro ukládání akt. hodnot polí z aktuálně načteného řádku... tex.sprint([[\global\EOFfalse]]) tex.sprint([[\global\notEOFtrue]]) end -- of OpenCSVFile(file) function ReadLineFromCSV(deftexmacros) -- Parametr deftexmacros=TRUE zajistí aby se vytvořily v průběhu cyklu TeXovská makra obsahující řádková data -- z CSV tabulky. Pro deftexmacros=FALSE se sloupcová makra nedefinují -- Po volání této funkce zůstane v asociativním poli CSV obsah načteného řádku -- k údajům v CSV tabulce se dostaneme pomocí Lua proměnných CSV.Jmeno, CSV.Prijmeni, CSV DatNar atd. local CSVLine=CSVFile:read() -- přečti řádek numline=numline+1 tex.sprint([[\def\numline{]]..numline..[[}]]) -- změna definice makra s číslem akt. načteného řádku pro použití v ConTeXtu if CSVLine ~= nil -- když to není poslední řádek then CSVLine=ParseCSVdata(CSVLine) -- rozparsuj řádek for i = 1, #CSVLine do -- vytvoření asociativního pole: "indexy" jsou názvy sloupců -- Experimental row: CSVField[ar2xls(i)]=CSVLine[i] -- Naplnit pole sloupcovými údaji z akt. řádku --CSVField[i]=CSVLine[i] -- Naplnit pole sloupcovými údaji z akt. řádku CSV[CSV[i]]=CSVLine[i] -- hodnoty jsou údaje z řádku příslušného sloupce CSV tabulky if deftexmacros then -- nadefinování aktuálního obsahu maker v případě deftexmacros = TRUE --tex.sprint([[\def\c]]..(ar2colnum(i))..[[{\bch ]]..CSVLine[i]..[[\ech}]]) -- nadefinování automatických TeXových maker \cA, \cB, atd. resp. \cI, \cII, ... obsahujících obsahy polí daného řádku. Makra s názvy ze záhlaví se automaticky aktualizují tex.sprint([[\def\c]]..(ar2colnum(i))..[[{]]..CSVLine[i]..[[}]]) -- nadefinování automatických TeXových maker \cA, \cB, atd. resp. \cI, \cII, ... obsahujících obsahy polí daného řádku. Makra s názvy ze záhlaví se automaticky aktualizují end -- if deftexmacros end -- for tex.sprint([[\global\EOFfalse]]) tex.sprint([[\global\notEOFtrue]]) return(true) -- pokračuj else tex.sprint([[\global\EOFtrue]]) tex.sprint([[\global\notEOFfalse]]) return(false) -- konec cyklu end -- if CSVLine ~= nil FirstRowIsLoaded=true -- kvůli správnému zpracování hlavičkového řádku end function lineaction(recnumfrom, recnumto) -- od řádku č. do řádku č. POZOR, NENÍ OŠETŘEN ROZSAH HODNOT!!! -- Funkce lineaction() provádí v cyklu pro všechny řádky otevřeného CSV souboru TeXovské makro \lineaction -- Makro \lineaction může být definováno v ConTeXtovém zdrojáku např. takto: \def\lineaction{\printaction} -- Kde makro \printaction obsahuje vlastní tiskový materiál, který se má pro každý řádek vytisknout např.: -- \def\printaction{ -- \hlavicka -- \hrule -- \blank[big] -- \adresnistitek{{\CjPozvanka} }{31.3.2010} -- \podpisreditele -- \page -- }% -- Když není v LuaTeXu makro \lineaction definováno, pak se automaticky použije nastavení \def\lineaction{\printline}, -- které vytiskne celou CSV tabulku v podobě zhuštěné řádkové tabulky. if (not texmacroisdefined('lineaction')) then -- Když není v LuaTeXu definováno makro \lineaction tex.sprint([[\let\lineaction\printline]]) -- inicializuje se makro \lineaction hodnotou \printline - to se použije v případě, že uživatel makro \lineaction sám nenadefinuje. end numline=0 tex.sprint([[\bfilehook]]) -- "háček" - makro \bfilehook se provede před zahájením zpracování tabulky (nebo její vybrané části) if recnumfrom == nil then -- Když není zadán parametr, vypiš všechny záznamy while ReadLineFromCSV(true) do -- dokud není konec vstupního CSV souboru -- true zajistí, že se při každém načtení řádku zaktualizují makra tj. načtou se do nich hodnoty polí na daném řádku tex.sprint([[\blinehook\lineaction\elinehook]]) -- volej TeXovské makro \lineaction s "háčky" -- "háčky" - makra \blinehook a \elinehook se provedou před a po zpracování aktuálního řádku end -- of while else -- jinak vypiš pouze záznam s daným číslem if recnumfrom > 0 then for i = 1, recnumfrom do numline=0 ReadLineFromCSV(true) end -- for -- "odskákej" na potřebný řádek (nepočítej přitom řádky tj. neinkrementuj numline) tex.sprint([[\blinehook\lineaction\elinehook]]) -- volej TeXovské makro \lineaction s "háčky" if recnumto ~= nil then if recnumto > recnumfrom then for i = recnumfrom, recnumto - 1 do -- pokud je zadán rozsah řádků, tak potřebné řádky vytiskni ReadLineFromCSV(true) tex.sprint([[\blinehook\lineaction\elinehook]]) -- volej TeXovské makro \lineaction s "háčky" end -- for i = recnumfrom, recnumto end -- if recnumto > recnumfrom end -- recnumto ~= nil numline=numline+1 -- závěrečná úprava numline end -- if recnumfrom > 0 end -- if recnumfrom == nil tex.sprint([[\def\numrows{]]..(numline-1)..[[}]]) -- Definice počtu zpracovaných řádků CSV tabulky (po ukončení funkce lineaction je počet řádků v numline) tex.sprint([[\efilehook]]) -- "háček" - makro \efilehook se provede po zpracování celé tabulky (nebo její vybrané části) tex.print('') -- Bez tohoto řádku někdy aplikace dělá problémy (\endlinechar ???) end function filelineaction(file,recnumfrom, recnumto) -- otevře soubor "file" a v něm následně vytiskne řádky dle zadaného rozsahu -- Funkce filelineaction() - viz. předchozí lineaction(), ale funkce nejprve otevře vstupní CSV soubor local inpCSVfile=csvfilename(file) OpenCSVFile(inpCSVfile) lineaction(recnumfrom, recnumto) -- pro všechny řádky tabulky v cyklu provádí TeXovské makro \lineaction s "háčky" CSVFile:close() end function CSVReport(file) -- Výpis reportových informací o otevřeném CSV souboru local inpCSVfile=csvfilename(file) csep = (Sep == nil) and "" or Sep ldel = (Ld == nil) and "" or Ld rdel = (Rd == nil) and "" or Rd OpenCSVFile(inpCSVfile) infomakra='\\par ' for i = 1, #CSV do -- pro všechna pole v záhlaví --if CSVHeader then local makroname=[[{\bf$\backslash$]]..TMN(CSV[i])..[[}]] --else makroname="" --end --headercolnames = makroname..[[={\bf$\backslash$c]]..(ar2colnum(i))..[[}, ]] headercolnames = [[{\bf$\backslash$c]]..(ar2colnum(i))..[[}=]]..makroname..[[, ]] infomakra=infomakra..headercolnames -- generování seznamu end -- for i=1, #CSV numrows=0 while ReadLineFromCSV(false) do -- dokud není konec vstupního CSV souboru (false - netvořit definice maker viz. ReadLineFromCSV() ) numrows=numrows+1 -- spočítej počet řádků end -- of while -- Kvůli nastavení na zač. CSVFile=io.open(inpCSVfile,"r") CSVLine=CSVFile:read() -- Načtení 1. řádku (obsahuje záhlaví se jmény sloupců - polí) infomakra=infomakra..[[\par]] -- uzavření otevřené skupiny string2print=([[{\bf Informace o používaném CSV souboru}\par Vstupní CSV soubor: {\bf\csvfilename}\par Vymezovače a oddělovače sloupců viz. Lua proměnné {\tt Sep}, {\tt Ld} a {\tt Rd}\par Nastavení vymezovačů a oddělovačů sloupců: {\tt ]]..ldel..[[pole1]]..rdel..csep..ldel..[[pole2]]..rdel..csep..ldel..[[pole3]]..rdel..csep..[[} \dots\par Počet sloupců v tabulce: {\bf\numcols}\par Počet řádků v tabulce : {\bf ]]..numrows..[[}\par Makra zpřístupňující řádková data v tabulce: ]]..infomakra..[[ \par Další předdefinovaná makra: \par {\bf$\backslash$csvfilename } -- otevřený soubor s CSV tabulkou ({\bf\csvfilename})\par {\bf$\backslash$numcols } -- počet sloupců tabulky ({\bf\numcols})\par {\bf$\backslash$numrows } -- počet aktuálně zpracovaných (vypsaných) řádků ({\bf\numrows})\par {\bf$\backslash$numline } -- číslo aktuálně načteného řádku (pro použítí v tiskových sestavách) \par {\bf$\backslash$csvreport} -- vypíše se report o otevřeném souboru \par {\bf$\backslash$printline} -- vypíše aktuální řádek CSV tabulky ve zhuštěném tvaru \par {\bf$\backslash$printall} -- vypíše CSV tabulku ve zhuštěném tvaru \par {\bf$\backslash$setfiletoscan}\{{\it filename}\} -- nastaví jméno zpracovávaného CSV souboru \par {\bf$\backslash$opencsvfile}\{{\it filename}\} -- otevření CSV souboru\par {\bf$\backslash$openheadercsvfile}\{{\it filename}\} -- otevření CSV souboru s hlavičkou\par {\bf$\backslash$setheader} -- nastavení příznaku existence hlavičky\par {\bf$\backslash$resetheader} -- nastavení příznaku neexistence hlavičky\par {\bf$\backslash$readrow} -- načtení řádku (+ zůstane na něm)\par {\bf$\backslash$nextrow} -- načtení řádku (+ skok na další)\par {\bf$\backslash$setsep}\{{\it separator}\} -- nastavení oddělovače polí na příslušný znak\par {\bf$\backslash$resetsep} -- nastavení oddělovače polí na defaultní hodnotu\par {\bf$\backslash$setld}\{{\it delimiter}\} -- nastavení levého vymezovacího znaku\par {\bf$\backslash$resetld} -- nastavení levého vymezovače na defaultní hodnotu\par {\bf$\backslash$setrd}\{{\it delimiter}\} -- nastavení pravého vymezovacího znaku\par {\bf$\backslash$resetrd} -- nastavení pravého vymezovače na defaultní hodnotu\par {\bf$\backslash$blinehook} -- nastavení háčku (vykonaného před načtením řádku) \par {\bf$\backslash$elinehook} -- nastavení háčku (vykonaného po zpracování řádku) \par {\bf$\backslash$bfilehook} -- nastavení háčku (vykonaného před načtením CSV souboru)\par {\bf$\backslash$efilehook} -- nastavení háčku (vykonaného po zpracování CSV souboru)\par \vfill\break ]]) string2print=string.gsub(string2print, '\n', " ") tex.sprint(string2print) end -- CSVReport() -- Pomocná makra : function csvfilename(file) -- při neuvedení názvu souboru se použije globální proměnná File2Scan = (file ~= nil) and file or File2Scan File2Scan = (File2Scan == nil) and file or File2Scan local file = (file ~= nil) and file or File2Scan return file end function TMN(s) -- TeX Macro Name. Název TeX. makra nesmí obsahovat zakázané znaky if string.len(s) == 0 then s='nil' end -- Když je parametr 's' neobsahuje žádný znak tj. mezi oddělovači není znak, je třeba "vyrobit" název makra maxdelkamakra=20 -- kdyby byl řetězec v 1. řádku delší "než je zdrávo", tak asi postačí 20 znaků -- POZOR! V případě, že 1. řádek CSV tabulky tj. záhlaví má u různých sloupců obsah, u kterého se shoduje prvních 'maxdelkamakra' znaků, pak názvy maker u různých sloupců budou stejné (tj. makra nebudou dávat správný výsledek pro daný sloupec) diachar= {"á","ä","č","ď","é","ě","í","ň","ó","ř","š","ť","ú","ů","ý","ž","Á","Ä","Č","Ď","É","Ě","Í","Ň","Ó","Ř","Š","Ť","Ú","Ů","Ý","Ž"} asciichar={"a","a","c","d","e","e","i","n","o","r","s","t","u","u","y","z","A","A","C","D","E","E","I","N","O","R","S","T","U","U","Y","Z"} for i=1, 32 do s=string.gsub(s, diachar[i], asciichar[i]) -- záměna diakritických znaků end --s=string.gsub(s, "%d", "n") -- nahradí číslice v názvu -- Pro číslice 0-9 nahradit písmenem O nebo římskými číslicemi s=string.gsub(s, "0", "O") -- nahradí číslice v názvu s=string.gsub(s, "1", "I") -- nahradí číslice v názvu s=string.gsub(s, "2", "II") -- nahradí číslice v názvu s=string.gsub(s, "3", "III") -- nahradí číslice v názvu s=string.gsub(s, "4", "IV") -- nahradí číslice v názvu s=string.gsub(s, "5", "V") -- nahradí číslice v názvu s=string.gsub(s, "6", "VI") -- nahradí číslice v názvu s=string.gsub(s, "7", "VII") -- nahradí číslice v názvu s=string.gsub(s, "8", "VIII") -- nahradí číslice v názvu s=string.gsub(s, "9", "IX") -- nahradí číslice v názvu s=string.gsub(s, "%A", "x") -- na závěr ještě odstraní všechny nealfabetické znaky, které tam zbyly if string.len(s) > maxdelkamakra+1 then s=string.sub(s, 1, maxdelkamakra) end -- pro omezení max. délky makra return s end function ar2rom(arnum) -- Převod arabských čísel na římská. Použito pro "číslování" sloupců v TeXovských makrech local romans = {{1000, "M"}, {900, "CM"}, {500, "D"}, {400, "CD"}, {100, "C"}, {90, "XC"}, {50, "L"}, {40, "XL"}, {10, "X"}, {9, "IX"}, {5, "V"}, {4, "IV"}, {1, "I"} } local romnum='' for _, v in ipairs(romans) do -- "výroba" římského čísla val, let = unpack(v) while arnum >= val do arnum = arnum - val romnum=romnum..let end end return romnum end function ar2xls(arnum) -- převod čísla na název Excelovského sloupce -- Pro více než 703 sloupců tj. od sloupce A ... do sloupce ZZ je třeba funkci modifikovat -- Excel 2003 stejně zvládá pouze do sloupce IV! local podil=math.floor(arnum/26) local zbytek = math.mod(arnum,26) podil = podil - (math.mod(arnum,26)==0 and 1 or 0) zbytek = zbytek + (math.mod(arnum,26)==0 and 26 or 0) local ctl ='' if arnum < 703 then if podil > 0 then ctl=string.char(64+podil) end ctl = ctl .. string.char(64+zbytek) else ctl = 'overZZ' end return ctl end function ar2colnum(arnum) -- Podle nastavení glob. proměnné vrací označení sloupce TeXového makra -- vygenerovaná TeXová makra odkazující na hodnoty ve sloupcích jsou číslována buď a`la EXCEL tj. cA, cB, ..., cAA, atd.. -- nebo a`la římské číslice tj. cI, cII, cIII, cIV, ..., cXVIII, atd.. -- Pokud je nastaveno "romannumbers" tak se bude číslovat římskými, jinak klasicky Excelovsky ... if string.lower(UserColumnNumbering) == 'xls' then return ar2xls(arnum) -- označuj sloupce a la EXCEL else return ar2rom(arnum) -- označuj sloupce a la ROMAN end end function texmacroisdefined(macroname) -- kontrola, zda je nadefinované TeXovské makro "macroname" -- funkce se používá pro testování, zda si uživatel nadefinoval makro \lineaction. Pokud ne, je třeba jej nadefinovat na defaultní hodnotu if token.csname_name(token.create(macroname)) == macroname then return true else return false end end function printline() -- vypíše aktuální řádek CSV tabulky (potřebné pro definici makra \printline) for i=1,table.maxn(CSV) do tex.sprint([[\c]]..ar2colnum(i)..Sep..[[]]) end --tex.print('') -- ??? end function printall() -- vypíše celou CSV tabulku (potřebné pro definici makra \printall) OpenCSVFile() while ReadLineFromCSV(true) do -- dokud není konec vstupního CSV souboru for i=1,table.maxn(CSV) do tex.sprint([[\c]]..ar2colnum(i)..Sep..[[ ]]) end tex.sprint([[\hfill\break]]) end -- of while -- tex.print('') -- ??? end -- Nyní následuje seznam definic TeXových maker, které mohou být použity v ConTeXtovém zdrojáku tj. zejména v makru \printaction, které tiskne aktuální řádkový materiál -- A dále následuje seznam defaultních přiřazení a nastavení, které může uživatel dle potřeby libovolně změnit -- Následující dva řádky nejsou použity: -- \bch tj. begin column hook - makro které se provede před vypsáním každé zpracovávané sloupcové položky \cI, \cII, atd... tzn. např. makro \cI je definováno: \def\cI{\bch pole\ech} dále viz \ech -- \ech tj. end column hook - makro které se provede po vypsání každé zpracovávané sloupcové položky viz \bch -- Následující komentáře jsou samosatně a nejsou součástí řetězce TeXLetsViaLua, protože zakomentování pomocí % vyřadí z činnosti (v TeXu) zbývající řádky řetězce -- \blinehook tj. begin line hook - makro které se provede před vypsáním řádkových hodnot makrem \lineaction -- \elinehook tj. end line hook - makro které se provede po vypsání všech zadaných řádkových hodnot makrem \lineaction -- \bfilehook tj. begin file hook - makro které se provede před zahájením činnosti makra \lineaction (tj. před vypisováním požadovaných řádků tabulky) -- \efilehook tj. end file hook - makro které se provede po ukončení činnosti makra \lineaction (tj. po vypsání všech požadovaných řádků tabulky) initTeXenvironmentbyLua=[[ \def\printline{ \directlua{printline()} \par } \def\csvreport{ \directlua{CSVReport()} } \def\readrow{\ifnotEOF\directlua{ReadLineFromCSV(true)}\fi} \def\nextrow{\ifnotEOF\directlua{if not FirstRowIsLoaded then ReadLineFromCSV(true);ReadLineFromCSV(true) else ReadLineFromCSV(true);FirstRowIsLoaded=true; end}\fi} \def\setfiletoscan#1{\directlua{File2Scan="#1"}} \def\opencsvfile#1{\directlua{OpenCSVFile("#1");if CSVHeader then ReadLineFromCSV(true) end}} \def\openheadercsvfile#1{\directlua{CSVHeader=true}\opencsvfile{#1}} \def\setheader{\directlua{CSVHeader=true}} \def\resetheader{\directlua{CSVHeader=false}} \def\resetsep{\directlua{Sep=UserCSVSeparator}} \def\resetld{\directlua{Ld=UserCSVLeftDelimiter}} \def\resetrd{\directlua{Rd=UserCSVRightDelimiter}} \let\blinehook\relax \let\elinehook\relax \let\bfilehook\relax \let\efilehook\relax \let\numrows\relax \let\bch\relax \let\ech\relax \newif\ifEOF \newif\ifnotEOF ]] if context then -- Když je spuštěno v režimu ConTeXtu, pak je nutno řešit nekompatibilitu (La)TeXu a ConTeXtu initCompatTeXenvironmentbyLua=[[ \def\printall{ \directlua{printallcontext()} \par } \def\filelineaction{\dotriplegroupempty\dofilelineaction} \def\dofilelineaction#1#2#3{ \doifelsenothing{#1} {\directlua{filelineaction()} } {\doifelsenothing{#2} {\directlua{filelineaction("#1")} } {\doifelsenothing{#3} {\directlua{filelineaction("#1",1,#2)} } {\directlua{filelineaction("#1",#2,#3)} }}} } \unprotect \def\setsep#1{\directlua{Sep=\!!bs#1\!!es}} \def\setld#1{\directlua{Ld=\!!bs#1\!!es}} \def\setrd#1{\directlua{Rd=\!!bs#1\!!es}} \protect ]] else -- v LuaLaTeXu musí být některá makra definována jinak (a s jistým omezením - proměnlivý počet parametrů atd...): initCompatTeXenvironmentbyLua=[[ \def\printall{ \directlua{printall()} \par } \def\filelineaction#1{\directlua{filelineaction("#1")}} \def\setsep#1{\directlua{Sep='#1'}} \def\setld#1{\directlua{Ld='#1'}} \def\setrd#1{\directlua{Rd='#1'}} ]] end -- if context initTeXenvironmentbyLua=string.gsub(initTeXenvironmentbyLua, '\n', "") -- Kvůli nesprávné interpretaci CRLF v LuLaTeXem a LuaPlainem tex.sprint(initTeXenvironmentbyLua) -- "Nahrnout" definice do ConTeXtu (LuaTeXu atd...) initCompatTeXenvironmentbyLua=string.gsub(initCompatTeXenvironmentbyLua, '\n', "") -- Kvůli nesprávné interpretaci CRLF v LuLaTeXem a LuaPlainem tex.sprint(initCompatTeXenvironmentbyLua) -- "Nahrnout" definice do ConTeXtu (LuaTeXu atd...) ... function ConTeXtCSVReport(file) -- Výpis reportových informací o otevřeném CSV souboru local inpCSVfile=csvfilename(file) csep = (Sep == nil) and "" or Sep ldel = (Ld == nil) and "" or Ld rdel = (Rd == nil) and "" or Rd OpenCSVFile(inpCSVfile) infomakra=[[\startitemize[n,packed, joinedup,columns, three] ]] for i = 1, #CSV do -- pro všechna pole v záhlaví headercolnames = [[\item {\bf\backslash ]]..TMN(CSV[i])..[[} ({\bf\backslash c]]..(ar2colnum(i))..[[})]] infomakra=infomakra..headercolnames -- generování seznamu end -- for i=1, #CSV numrows=0 while ReadLineFromCSV(false) do -- dokud není konec vstupního CSV souboru (false - netvořit definice maker viz. ReadLineFromCSV() ) numrows=numrows+1 -- spočítej počet řádků end -- of while -- Kvůli nastavení na zač. CSVFile=io.open(inpCSVfile,"r") CSVLine=CSVFile:read() -- Načtení 1. řádku (obsahuje záhlaví se jmény sloupců - polí) infomakra=infomakra..[[\stopitemize ]] -- uzavření otevřené skupiny tex.sprint([[ \title{Informace o používaném CSV souboru} Vstupní CSV soubor: {\bf\csvfilename}\par Vymezovače a oddělovače sloupců viz. Lua proměnné \type{Sep}, \type{Ld} a \type{Rd}\par Nastavení vymezovačů a oddělovačů sloupců: \type{]]..ldel..[[pole1]]..rdel..csep..ldel..[[pole2]]..rdel..csep..ldel..[[pole3]]..rdel..csep..[[} \dots\par Počet sloupců v tabulce: {\bf\numcols}\par Počet řádků v tabulce: {\bf ]]..numrows..[[}\par\blank[medium] Makra zpřístupňující řádková data v tabulce: \par ]]..infomakra..[[ \par\blank[medium] Další předdefinovaná makra: \par {\bf\backslash csvfilename } -- název otevřeného souboru s CSV tabulkou ({\bf\csvfilename})\par {\bf\backslash numcols } -- počet sloupců tabulky ({\bf\numcols})\par {\bf\backslash numline } -- číslo aktuálně načteného řádku (pro použítí v tiskových sestavách) \par {\bf\backslash csvreport} -- vypíše se report o otevřeném souboru \par {\bf\backslash printline} -- vypíše aktuální řádek CSV tabulky ve zhuštěném tvaru \par {\bf\backslash printall} -- vypíše CSV tabulku ve zhuštěném tvaru \par \page ]]) end -- CSVReport()