LuaTeX: použití základního rozhraní mezi TeXem a skripty

Pavel Tišnovský 26. 7. 2016

Ve druhé části seriálu o programovatelném sázecím systému LuaTeX si na několika demonstračních příkladech ukážeme kooperaci mezi jádrem LuaTeXu a interpretrem skriptovacího jazyka Lua. Taktéž si řekneme, jak lze volat Lua funkci s využitím uživatelsky definovaných příkazů.

Obsah

1. Programovatelný sázecí systém LuaTeX: použití základního rozhraní mezi TeXem a skripty

2. Vytvoření tabulky v dokumentu s využitím programové smyčky

3. Zdrojový kód čtvrtého demonstračního příkladu

4. Volání skriptu uloženého v externím souboru s využitím funkce dofile

5. Zdrojový kód pátého demonstračního příkladu

6. Rozdělení deklarace tabulky na statickou a dynamickou část

7. Zdrojový kód šestého demonstračního příkladu

8. Načítání externích dat (vypočtených v jiném nástroji)

9. Volání funkce deklarované v externím souboru

10. Zdrojový kód sedmého demonstračního příkladu

11. Vytvoření nového příkazu volajícího funkci deklarovanou v jazyku Lua

12. Zdrojový kód osmého demonstračního příkladu

13. Repositář s popsanými demonstračními příklady

14. Odkazy na Internetu

1. Programovatelný sázecí systém LuaTeX: použití základního rozhraní mezi TeXem a skripty

V první části seriálu o sázecím systému LuaTeX jsme se seznámili se základními vlastnostmi tohoto nástroje a taktéž s některými odlišnostmi LuaTeXu od klasického TeXu. Posléze jsme si ukázali nově přidaný příkaz \directlua, který slouží k zajištění základního rozhraní mezi přepsaným a upraveným jádrem TeXu a interpretrem skriptovacího jazyka Lua (tento příkaz mohou používat jak uživatelé plainTeXu, tak i LaTeXu). Taktéž jsme se zmínili o příkazu nazvaném luacode, jenž zjednodušuje volání skriptů. Tento příkaz se používá v LaTeXu resp. přesněji řečeno v LuaLaTeXu stylem \begin\{luacode}\end{luacode} (takže se vlastně jedná o nově definované prostředí, i když s některými omezeními). Dnes si ukážeme některé další možnosti obou zmíněných příkazů, popíšeme si způsob využití externích skriptů a nakonec se seznámíme s tím, jak je možné vhodnou kombinací příkazů \newcommand (popř. \def pro uživatele plainTeXu) a \directlua do LuaTeXu/LuaLaTeXu přidat vlastní příkazy, samozřejmě s možností předání případných parametrů těmto příkazům.

Obrázek 1: Dokument vygenerovaný demonstračním příkladem, s nímž jsme se seznámili minule.

2. Vytvoření tabulky v dokumentu s využitím programové smyčky

Demonstrační příklady popsané minule byly ve skutečnosti velmi jednoduché až primitivní, takže při pohledu na ně možná nemuselo být příliš jasné, v čem vlastně spočívá výhoda kombinace TeXu a skriptovacího jazyka Lua. Podívejme se nyní na poněkud složitější dokument, v němž je umístěna tabulka s druhými mocninami celých čísel od 1 do 16. Celá tabulka je vygenerována skriptem vytvořeným v programovacím jazyce Lua, v němž lze využít funkci nazvanou pow pocházející ze standardní knihovny math [1].

Tabulka je do vstupního bufferu TeXu „vypsána“ funkcí tex.print() (TeX vidí a následně zpracuje už vygenerovanou tabulku), což znamená, že jsme si museli dát pozor na problematický znak zpětného lomítka (o této problematice jsme se taktéž zmiňovali minule). TeXem se zpracuje přibližně tento kód:

\documentclass{article}
\usepackage{luacode}
 
\begin{document}
 
\section*{Mocniny dvou}
 
\subsection*{verze 1}
 
\begin{tabular}{|r|r|}
\hline
$n$ & $2^n$ \\
\hline
1 & 2 \\
2 & 4 \\
3 & 8 \\
4 & 16 \\
5 & 32 \\
6 & 64 \\
7 & 128 \\
8 & 256 \\
9 & 512 \\
10 & 1024 \\
11 & 2048 \\
12 & 4096 \\
13 & 8192 \\
14 & 16384 \\
15 & 32768 \\
16 & 65536 \\
\hline
\end{tabular}
 
\end{document}

V tomto demonstračním příkladu používáme upravené prostředí nazvané luacode* a nikoli pouze luacode. Je tomu tak především z toho důvodu, aby se zamezilo expanzi TeXovských maker ještě předtím, než bude kód v prostředí předán interpretru jazyka Lua.

3. Zdrojový kód čtvrtého demonstračního příkladu

Úplný zdrojový kód v celkovém pořadí již čtvrtého demonstračního příkladu vypadá následovně:

\documentclass{article}
\usepackage{luacode}
 
\begin{document}
 
\section*{Mocniny dvou}
 
\subsection*{verze 1}
 
\begin{luacode*}
tex.print("\\begin{tabular}{|r|r|}\\hline")
tex.print("$n$&$2^n$\\\\")
tex.print("\\hline")
for n = 1,16 do
    tex.print(n .. "&" .. math.pow(2, n))
    tex.print("\\\\")
end
tex.print("\\hline")
tex.print("\\end{tabular}")
\end{luacode*}
 
\end{document}

Základ pro vytvoření obsahu tabulky tvoří čtveřice řádků:

for n = 1,16 do
    tex.print(n .. "&" .. math.pow(2, n))
    tex.print("\\\\")
end

Ve skutečnosti se však do interpretru jazyka Lua předá jediný řádek, v němž jsou příkazy odděleny mezerami (to je v jazyku Lua zcela legitimní):

tex.print("\\begin{tabular}{|r|r|}\\hline") tex.print("$n$&$2^n$\\\\") tex.print("\\hline") for n = 1,16 do tex.print(n .. "&" .. math.pow(2, n)) tex.print("\\\\") end tex.print("\\hline") tex.print("\\end{tabular}")

Obrázek 2: Dokument vygenerovaný předchozím demonstračním příkladem.

4. Volání skriptu uloženého v externím souboru s využitím funkce dofile

Ve standardní knihovně programovacího jazyka Lua existuje funkce nazvaná dofile() [2], která dokáže do aktuálně běžícího skriptu načíst obsah jiného zdrojového souboru a následně ho zpracovat (interpretovat). Tato funkce je samozřejmě, podobně jako i další funkce ze základních knihoven, dostupná i v LuaTeXu, čehož můžeme využít pro oddělení dokumentů od skriptů. Dokument bude uložen v jednom souboru (v tomto příkladu se konkrétně bude jmenovat test5.tex), zatímco skript bude uložen ve druhém souboru nazvaném test5.lua. Načtení a okamžitá interpretace skriptu s programovou smyčkou určenou pro výpočet a vygenerování tabulky druhých mocnin pak v dokumentu zajistí jediný řádek:

\directlua{dofile("test5.lua")}

První předností tohoto způsobu oddělení skriptu od dokumentu je fakt, že nedojde ke spojení všech řádků skriptu před jeho předáním interpretru (to může v některých případech způsobit problémy, například při použití jednořádkových komentářů). Dále je toto oddělení výhodné i z praktického hlediska – programátorské textové editory nebudou mít problém ani se zvýrazněním syntaxe dokumentů (TeX/LaTeX) ani s Lua skripty, zatímco při použití jediného souboru je zvýraznění syntaxe s rozpoznáním rozdílných sekcí problematické (popravdě jsem zatím neviděl editor, který by tuto kombinaci bez problémů podporoval).

5. Zdrojový kód pátého demonstračního příkladu

Obsah souboru (přesněji řečeno dokumentu) test5.tex se díky oddělení skriptu do samostatného souboru zkrátil a současně i zpřehlednil:

\documentclass{article}
\usepackage{luacode}
 
\begin{document}
 
\section*{Mocniny dvou}
 
\subsection*{verze 2}
 
\directlua{dofile("test5.lua")}
 
\end{document}

Poznámka: řádek \usepackage{luacode} je vlastně v tomto demonstračním příkladu zbytečný, neboť příkaz \directlua je přímo zabudován do LuaTeXu (v předchozím příkladu bylo nutné tento řádek použít).

Samotný skript uložený v souboru nazvaném test5.lua potom vypadá následovně:

tex.print("\\begin{tabular}{|r|r|}\\hline")
tex.print("$n$&$2^n$\\\\")
tex.print("\\hline")
for n = 1,16 do
    tex.print(n .. "&" .. math.pow(2, n))
    tex.print("\\\\")
end
tex.print("\\hline")
tex.print("\\end{tabular}")

Zdvojení zpětných lomítek v tomto skriptu stále nalezneme, a to konkrétně z toho důvodu, že zpětné lomítko má v řetězcových literálech jazyka Lua speciální význam; viz též [3].

Obrázek 3: Dokument vygenerovaný předchozím demonstračním příkladem.

6. Rozdělení deklarace tabulky na statickou a dynamickou část

Při pohledu na demonstrační příklad zmíněný v předchozí kapitole jste si pravděpodobně uvědomili, že se v Lua skriptu zbytečně a k tomu navíc velmi pracně vytváří celá tabulka, včetně všech příkazů určených pro definici jejího začátku s deklarací sloupců, prvním řádkem s popisem obou sloupců apod. Celý skript je kvůli tomu dosti nepřehledný, navíc v něm snadno může dojít k chybě, která se těžko hledá (TeX totiž nahlásí chybu v kódu, který „vidí“ v průběhu tokenizace a expanze maker, tedy po zpracování interpretrem, bez přímé vazby na vstupní dokument).

Ovšem kooperace mezi TeXovským dokumentem a Lua skripty je ve skutečnosti velmi variabilní, takže je například možné dokument i k němu příslušející skript upravit takovým způsobem, že se tabulka deklaruje přímo v TeXovském dokumentu (jak je to obvyklé) a pouze ty řádky vytvářené tabulky, které se mají vypočítat, budou vytvářeny skriptem naprogramovaným v jazyku Lua. Příkaz \directlua{dofile(„jméno_skrip­tu.lua“)} se nám tedy posune dovnitř definice celé tabulky a samozřejmě díky tomu dojde ke značnému zjednodušení vlastního skriptu, z něhož je odstraněna většina otrocky napsaných příkazů tex.print(). Další výhoda tohoto přístupu spočívá v tom, že kód uložený ve skriptu bude možné využít na více místech (například tehdy, pokud by se data do tabulky načítala z datového souboru, což je téma, kterému se budeme věnovat o několik odstavců níže).

Poznámka: dejte si pozor na to, že tento způsob není možné použít ve chvíli, kdy se kombinuje prostředí tabular s prostředím luacode či luacode* (viz též http://tex.stackexchange.com/qu­estions/253706/is-it-possible-to-view-the-latex-code-after-lua-expansion-is-made).

7. Zdrojový kód šestého demonstračního příkladu

V dokumentu v pořadí již šestého demonstračního příkladu vidíme deklaraci běžné tabulky s jedním řádkem (nadpisem) a voláním příkazu \directlua:

\documentclass{article}
\usepackage{luacode}
 
\begin{document}
 
\section*{Mocniny dvou}
 
\subsection*{verze 3}
 
\begin{tabular}{|r|r|}
\hline
$n$ & $2^n$ \\
\hline
\directlua{dofile("test6.lua")}
\hline
\end{tabular}
 
\end{document}

Poznámka: řádek \usepackage{luacode} je v tomto demonstračním příkladu opět zbytečný a uváděn je pouze pro úplnost.

Skript se nám zjednoduší na pouhé čtyři řádky, které jsou již mnohem čitelnější, než tomu bylo v příkladu předchozím:

for n = 1,16 do
    tex.print(n .. "&" .. math.pow(2, n))
    tex.print("\\\\")
end

Obrázek 4: Dokument vygenerovaný předchozím demonstračním příkladem.

8. Načítání externích dat (vypočtených v jiném nástroji)

Samozřejmě není nutné, aby skript naprogramovaný v jazyku Lua prováděl pouze nějaké výpočty. Poměrně často se totiž ocitneme v situaci, kdy jsou již data vypočtena či naměřena s využitím jiného externího nástroje a my „pouze“potřebujeme tato data vhodným způsobem vložit do dokumentu. Jedním z poměrně flexibilních způsobů je právě použití skriptu naprogramovaného v jazyku Lua. Předpokládejme pro jednoduchost, že máme následující datový soubor, jehož obsah potřebujeme vložit do tabulky:

42
0
-100
NaN
6502
123456

Pomoci nám může následující skript, který data postupně načte a vloží do tabulky s očíslováním jednotlivých řádků (tento úkol zajišťuje počitadlo lineno):

local infile = io.open("test6.data")
local lineno = 1
 
if not infile then
    print("Chyba pri otevirani souboru")
else
    for line in infile:lines() do
        print(lineno, line)
        tex.print(lineno .. "&" .. line)
        tex.print("\\\\")
        lineno = lineno + 1
    end
    infile:close()
end

Poznámka: pokud není zapotřebí provádět alespoň základní detekci, zda soubor se vstupními daty existuje a je dostupný (čitelný), lze skript ještě více zjednodušit:

local lineno = 1
for line in io.lines("test6.data") do
    print(lineno, line)
    tex.print(lineno .. "&" .. line)
    tex.print("\\\\")
    lineno = lineno + 1
end

Tento skript lze samozřejmě vylepšit a rozšířit. Jedno z možných vylepšení spočívá v detekci speciálních znaků ve vstupním souboru. Tyto znaky, mezi než patří zejména dvojice zpětných lomítek, znak & apod., je nutné například funkcí subs nahradit za odpovídající ekvivalenty, které nebudou při vložení do tabulky způsobovat problémy. Možných rozšíření je celá řada; zmiňme například načítání souborů CSV s detekcí, zda na prvním řádku jsou umístěny popisy sloupců, filtrace dat na základě skriptu, použití externího modulu pro práci se soubory JSON a XML atd.

9. Volání funkce deklarované v externím souboru

V externích souborech s Lua skripty je možné deklarovat různé datové struktury (tj. tabulky fungující jako záznamy, pole i asociativní pole) a taktéž funkce. Pokud například budeme mít v souboru nazvaném test7.lua uloženu deklaraci funkce pro (rekurzivní) výpočet faktoriálu, bude možné takovou funkci nechat zpracovat interpretrem a následně je možné tuto funkci volat:

function factorial(n)
    if n <= 1 then
        return 1
    else
        return n * factorial(n-1)
    end
end

Poznámka: interpret při prvním čtení definice funkce provede základní kontrolu a překlad funkce do bajtkódu (viz též [4] a [5]).

10. Zdrojový kód sedmého demonstračního příkladu

Zkusme nyní použít funkci factorial(), jejíž definice je uložena v samostatném souboru nazvaném test7.lua. Tento soubor je nejdříve nutné načíst do interpretru, což se provede jednoduše:

\directlua{dofile("test7.lua")}

Následně se již funkce může volat, například opět při vytváření tabulky:

\begin{tabular}{|r|r|}
\hline
$n$ & $n!$ \\
\hline
1   & \directlua{tex.print(factorial(1))} \\
10  & \directlua{tex.print(factorial(10))} \\
100 & \directlua{tex.print(factorial(100))} \\
\hline
\end{tabular}

Úplný zdrojový kód dokumentu vypadá takto:

\documentclass{article}
\usepackage{luacode}
 
\directlua{dofile("test7.lua")}
 
\begin{document}
 
\section*{Faktorial}
 
\subsection*{verze 1}
 
\begin{tabular}{|r|r|}
\hline
$n$ & $n!$ \\
\hline
1   & \directlua{tex.print(factorial(1))} \\
10  & \directlua{tex.print(factorial(10))} \\
100 & \directlua{tex.print(factorial(100))} \\
\hline
\end{tabular}
 
\end{document}

Obrázek 5: Dokument vygenerovaný předchozím demonstračním příkladem.

11. Vytvoření nového příkazu volajícího funkci deklarovanou v jazyku Lua

V praxi může být výhodnější nevolat funkci naprogramovanou v jazyku Lua nepřímo:

\directlua{tex.print(factorial(42))}

ale použít namísto toho uživatelský příkaz deklarovaný na úrovni plainTeXu či LaTeXu, tedy takto:

\factorial{1}

Podívejme se nyní, jak je možné tohoto stavu dosáhnout. Nejdříve opět musíme předat interpretru jazyka Lua skript, v němž je deklarována funkce, která se má volat:

\directlua{dofile("test8.lua")}

Následně můžeme s využitím příkazu \newcommand (LaTeX) či \def (TeX) vytvořit nový uživatelský příkaz, který bude interně volat naši funkci, samozřejmě s předáním všech potřebných parametrů:

\newcommand*{\factorial}[1]{%
  \directlua{tex.print(factorial(#1))}%
}

V praxi se tento nový příkaz deklarovaný na úrovni (La)TeXu použije následujícím způsobem:

\begin{tabular}{|r|r|}
\hline
$n$ & $n!$ \\
\hline
1   & \factorial{1} \\
10  & \factorial{10} \\
100 & \factorial{100} \\
\hline
\end{tabular}

což vlastně odpovídá předchozímu kódu:

\begin{tabular}{|r|r|}
\hline
$n$ & $n!$ \\
\hline
1   & \directlua{tex.print(factorial(1))} \\
10  & \directlua{tex.print(factorial(10))} \\
100 & \directlua{tex.print(factorial(100))} \\
\hline
\end{tabular}

První varianta je evidentně mnohem čitelnější než varianta druhá.

12. Zdrojový kód osmého demonstračního příkladu

Nový příkaz deklarovaný na úrovni LaTeXu je použit v osmém demonstračním příkladu, jehož zdrojový kód je zobrazen pod tímto odstavcem:

\documentclass{article}
\usepackage{luacode}
 
\directlua{dofile("test8.lua")}
 
\newcommand*{\factorial}[1]{%
  \directlua{tex.print(factorial(#1))}%
}
 
\begin{document}
 
\section*{Faktorial}
 
\subsection*{verze 2}
 
\begin{tabular}{|r|r|}
\hline
$n$ & $n!$ \\
\hline
1   & \factorial{1} \\
10  & \factorial{10} \\
100 & \factorial{100} \\
\hline
\end{tabular}
 
\end{document}

Obsah souboru test8.lua:

widgety

function factorial(n)
    if n <= 1 then
        return 1
    else
        return n * factorial(n-1)
    end
end

Obrázek 6: Dokument vygenerovaný předchozím demonstračním příkladem.

13. Repositář s popsanými demonstračními příklady

Demonstrační příklady popsané v předchozím i v dnešním článku, byly uloženy do veřejného Git repositáře, z něhož si je můžete snadno stáhnout a otestovat:

# Soubor Stručný popis Odkaz
1 test1.tex příkaz \directlua https://github.com/tisnik/pre­sentations/blob/master/lu­atex/slides/test1.tex
2 test1.pdf dokument vysázený LuaTeXem https://github.com/tisnik/pre­sentations/blob/master/lu­atex/slides/test1.pdf
       
3 test2.tex prostředí luacode https://github.com/tisnik/pre­sentations/blob/master/lu­atex/slides/test2.tex
4 test2.pdf dokument vysázený LuaTeXem https://github.com/tisnik/pre­sentations/blob/master/lu­atex/slides/test2.pdf
       
5 test3.tex zpětné lomítko, expanze maker https://github.com/tisnik/pre­sentations/blob/master/lu­atex/slides/test3.tex
6 test3.pdf dokument vysázený LuaTeXem https://github.com/tisnik/pre­sentations/blob/master/lu­atex/slides/test3.pdf
       
7 test4.tex vygenerování tabulky https://github.com/tisnik/pre­sentations/blob/master/lu­atex/slides/test4.tex
8 test4.pdf dokument vysázený LuaTeXem https://github.com/tisnik/pre­sentations/blob/master/lu­atex/slides/test4.pdf
       
9 test5.tex použití funkce dofile() https://github.com/tisnik/pre­sentations/blob/master/lu­atex/slides/test5.tex
10 test5.lua skript pro vygenerování tabulky https://github.com/tisnik/pre­sentations/blob/master/lu­atex/slides/test5.lua
11 test5.pdf dokument vysázený LuaTeXem https://github.com/tisnik/pre­sentations/blob/master/lu­atex/slides/test5.pdf
       
12 test6.tex tvorba tabulek v Lua skriptech https://github.com/tisnik/pre­sentations/blob/master/lu­atex/slides/test6.tex
13 test6.lua zjednodušený skript vygenerování tabulky https://github.com/tisnik/pre­sentations/blob/master/lu­atex/slides/test6.lua
14 test6.pdf dokument vysázený LuaTeXem https://github.com/tisnik/pre­sentations/blob/master/lu­atex/slides/test6.pdf
       
15 test7.tex volání funkce pro výpočet faktoriálu https://github.com/tisnik/pre­sentations/blob/master/lu­atex/slides/test7.tex
16 test7.lua funkce pro výpočet faktoriálu https://github.com/tisnik/pre­sentations/blob/master/lu­atex/slides/test7.lua
17 test7.pdf dokument vysázený LuaTeXem https://github.com/tisnik/pre­sentations/blob/master/lu­atex/slides/test7.pdf
       
18 test8.tex vytvoření nového příkazu factorial https://github.com/tisnik/pre­sentations/blob/master/lu­atex/slides/test8.tex
19 test8.lua funkce pro výpočet faktoriálu https://github.com/tisnik/pre­sentations/blob/master/lu­atex/slides/test8.lua
20 test8.pdf dokument vysázený LuaTeXem https://github.com/tisnik/pre­sentations/blob/master/lu­atex/slides/test8.pdf

14. Odkazy na Internetu

  1. LuaTex
    http://www.luatex.org/
  2. LuaTex: dokumentace
    http://www.luatex.org/docu­mentation.html
  3. LuaTex Wiki
    http://wiki.luatex.org/in­dex.php/Main_Page
  4. LuaTeX (Wikipedia)
    https://en.wikipedia.org/wiki/LuaTeX
  5. Paper o LuaTeXu
    https://www.tug.org/TUGboat/tb28–3/tb90hoekwater-luatex.pdf
  6. TeX (Wikibooks)
    https://en.wikibooks.org/wiki/TeX
  7. LaTeX (Wikibooks)
    https://en.wikibooks.org/wiki/LaTeX
  8. The Latin Modern (LM) Family of Fonts
    http://www.gust.org.pl/projects/e-foundry/latin-modern
  9. Sázecí system TeX
    https://www.phil.muni.cz/~letty/tex/
  10. CSTeX – česká a slovenská podpora TeXu
    http://petr.olsak.net/cstex.html
  11. Proč nerad používám LaTeX
    http://petr.olsak.net/ftp/ol­sak/bulletin/nolatex.pdf
  12. εχTEX
    http://www.extex.org/index.html
  13. PlainTeX (Wikipedia)
    https://cs.wikipedia.org/wi­ki/PlainTeX
  14. What is the difference between \def and \newcommand? (SO)
    http://tex.stackexchange.com/qu­estions/655/what-is-the-difference-between-def-and-newcommand#658
  15. LaTeX/Macros
    https://en.wikibooks.org/wi­ki/LaTeX/Macros
  16. TeX (StackExchange)
    http://tex.stackexchange.com/
Našli jste v článku chybu?
Lupa.cz: Cimrman má hry na YouTube i vlastní doodle

Cimrman má hry na YouTube i vlastní doodle

Lupa.cz: Blíží se konec Wi-Fi sítí bez hesla?

Blíží se konec Wi-Fi sítí bez hesla?

Vitalia.cz: Voda z Vltavy před a po úpravě na pitnou

Voda z Vltavy před a po úpravě na pitnou

120na80.cz: Pálení žáhy: která jídla ne a co nás uzdraví?

Pálení žáhy: která jídla ne a co nás uzdraví?

Lupa.cz: Proč jsou firemní počítače pomalé?

Proč jsou firemní počítače pomalé?

DigiZone.cz: Wimbledon na Nova Sport až do 2019

Wimbledon na Nova Sport až do 2019

Vitalia.cz: Pryč se zastaralým stravováním ve školách

Pryč se zastaralým stravováním ve školách

Vitalia.cz: 5 chyb, které děláme při skladování potravin

5 chyb, které děláme při skladování potravin

DigiZone.cz: DVB-T2 ověřeno: seznam TV zveřejněn

DVB-T2 ověřeno: seznam TV zveřejněn

Vitalia.cz: Jak Ondra o astma přišel

Jak Ondra o astma přišel

DigiZone.cz: Světový pohár v přímém přenosu na ČT

Světový pohár v přímém přenosu na ČT

Podnikatel.cz: Letáky? Lidi zuří, ale ony stále fungují

Letáky? Lidi zuří, ale ony stále fungují

Vitalia.cz: Muž, který miluje příliš. Ženám neimponuje

Muž, který miluje příliš. Ženám neimponuje

Lupa.cz: Jak levné procesory změnily svět?

Jak levné procesory změnily svět?

DigiZone.cz: Parlamentní listy: kde končí PR...

Parlamentní listy: kde končí PR...

DigiZone.cz: Samsung EVO-S: novinka pro Skylink

Samsung EVO-S: novinka pro Skylink

Lupa.cz: Patička e-mailu závazná jako vlastnoruční podpis?

Patička e-mailu závazná jako vlastnoruční podpis?

Podnikatel.cz: Udělali jsme velkou chybu, napsal Čupr

Udělali jsme velkou chybu, napsal Čupr

Vitalia.cz: Tradiční čínská medicína a rakovina

Tradiční čínská medicína a rakovina

Lupa.cz: Jak se prodává firma za miliardu?

Jak se prodává firma za miliardu?